1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/richtext/richtextbuffer.cpp
3 // Purpose: Buffer for wxRichTextCtrl
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // For compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
21 #include "wx/richtext/richtextbuffer.h"
27 #include "wx/dataobj.h"
28 #include "wx/module.h"
31 #include "wx/settings.h"
32 #include "wx/filename.h"
33 #include "wx/clipbrd.h"
34 #include "wx/wfstream.h"
35 #include "wx/mstream.h"
36 #include "wx/sstream.h"
37 #include "wx/textfile.h"
38 #include "wx/hashmap.h"
39 #include "wx/dynarray.h"
41 #include "wx/richtext/richtextctrl.h"
42 #include "wx/richtext/richtextstyles.h"
43 #include "wx/richtext/richtextimagedlg.h"
44 #include "wx/richtext/richtextsizepage.h"
45 #include "wx/richtext/richtextxml.h"
47 #include "wx/listimpl.cpp"
48 #include "wx/arrimpl.cpp"
50 WX_DEFINE_LIST(wxRichTextObjectList
)
51 WX_DEFINE_LIST(wxRichTextLineList
)
53 // Switch off if the platform doesn't like it for some reason
54 #define wxRICHTEXT_USE_OPTIMIZED_DRAWING 1
56 // Use GetPartialTextExtents for platforms that support it natively
57 #define wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS 1
59 const wxChar wxRichTextLineBreakChar
= (wxChar
) 29;
61 // Helper classes for floating layout
62 struct wxRichTextFloatRectMap
64 wxRichTextFloatRectMap(int sY
, int eY
, int w
, wxRichTextObject
* obj
)
74 wxRichTextObject
* anchor
;
77 WX_DEFINE_SORTED_ARRAY(wxRichTextFloatRectMap
*, wxRichTextFloatRectMapArray
);
79 int wxRichTextFloatRectMapCmp(wxRichTextFloatRectMap
* r1
, wxRichTextFloatRectMap
* r2
)
81 return r1
->startY
- r2
->startY
;
84 class wxRichTextFloatCollector
87 wxRichTextFloatCollector(const wxRect
& availableRect
);
88 ~wxRichTextFloatCollector();
90 // Collect the floating objects info in the given paragraph
91 void CollectFloat(wxRichTextParagraph
* para
);
92 void CollectFloat(wxRichTextParagraph
* para
, wxRichTextObject
* floating
);
94 // Return the last paragraph we collected
95 wxRichTextParagraph
* LastParagraph();
97 // Given the start y position and the height of the line,
98 // find out how wide the line can be
99 wxRect
GetAvailableRect(int startY
, int endY
);
101 // Given a floating box, find its fit position
102 int GetFitPosition(int direction
, int start
, int height
) const;
103 int GetFitPosition(const wxRichTextFloatRectMapArray
& array
, int start
, int height
) const;
105 // Find the last y position
106 int GetLastRectBottom();
108 // Draw the floats inside a rect
109 void Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
);
111 // HitTest the floats
112 int HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, int flags
);
114 // Get floating object count
115 int GetFloatingObjectCount() const { return m_left
.GetCount() + m_right
.GetCount(); }
117 // Get floating objects
118 bool GetFloatingObjects(wxRichTextObjectList
& objects
) const;
121 bool DeleteFloat(wxRichTextObject
* obj
);
123 // Do we have this float already?
124 bool HasFloat(wxRichTextObject
* obj
);
126 bool HasFloats() const { return m_left
.GetCount() >0 || m_right
.GetCount() > 0; }
128 static int SearchAdjacentRect(const wxRichTextFloatRectMapArray
& array
, int point
);
130 static int GetWidthFromFloatRect(const wxRichTextFloatRectMapArray
& array
, int index
, int startY
, int endY
);
132 static void FreeFloatRectMapArray(wxRichTextFloatRectMapArray
& array
);
134 static void DrawFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
);
136 static int HitTestFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, int flags
);
139 wxRichTextFloatRectMapArray m_left
;
140 wxRichTextFloatRectMapArray m_right
;
142 wxRect m_availableRect
;
143 wxRichTextParagraph
* m_para
;
147 bool wxRichTextFloatCollector::DeleteFloat(wxRichTextObject
* obj
)
150 for (i
= 0; i
< m_left
.GetCount(); i
++)
152 if (m_left
[i
]->anchor
== obj
)
158 for (i
= 0; i
< m_right
.GetCount(); i
++)
160 if (m_right
[i
]->anchor
== obj
)
169 // Do we have this float already?
170 bool wxRichTextFloatCollector::HasFloat(wxRichTextObject
* obj
)
173 for (i
= 0; i
< m_left
.GetCount(); i
++)
175 if (m_left
[i
]->anchor
== obj
)
180 for (i
= 0; i
< m_right
.GetCount(); i
++)
182 if (m_right
[i
]->anchor
== obj
)
190 // Get floating objects
191 bool wxRichTextFloatCollector::GetFloatingObjects(wxRichTextObjectList
& objects
) const
194 for (i
= 0; i
< m_left
.GetCount(); i
++)
195 objects
.Append(m_left
[i
]->anchor
);
196 for (i
= 0; i
< m_right
.GetCount(); i
++)
197 objects
.Append(m_right
[i
]->anchor
);
203 * Binary search helper function
204 * The argument point is the Y coordinate, and this fuction
205 * always return the floating rect that contain this coordinate
206 * or under this coordinate.
208 int wxRichTextFloatCollector::SearchAdjacentRect(const wxRichTextFloatRectMapArray
& array
, int point
)
210 int end
= array
.GetCount() - 1;
223 int mid
= (start
+ end
) / 2;
224 if (array
[mid
]->startY
<= point
&& array
[mid
]->endY
>= point
)
226 else if (array
[mid
]->startY
> point
)
231 else if (array
[mid
]->endY
< point
)
241 int wxRichTextFloatCollector::GetWidthFromFloatRect(const wxRichTextFloatRectMapArray
& array
, int index
, int startY
, int endY
)
244 int len
= array
.GetCount();
246 wxASSERT(index
>= 0 && index
< len
);
248 if (array
[index
]->startY
< startY
&& array
[index
]->endY
> startY
)
249 ret
= ret
< array
[index
]->width
? array
[index
]->width
: ret
;
250 while (index
< len
&& array
[index
]->startY
<= endY
)
252 ret
= ret
< array
[index
]->width
? array
[index
]->width
: ret
;
259 wxRichTextFloatCollector::wxRichTextFloatCollector(const wxRect
& rect
) : m_left(wxRichTextFloatRectMapCmp
), m_right(wxRichTextFloatRectMapCmp
)
261 m_availableRect
= rect
;
265 void wxRichTextFloatCollector::FreeFloatRectMapArray(wxRichTextFloatRectMapArray
& array
)
267 int len
= array
.GetCount();
268 for (int i
= 0; i
< len
; i
++)
272 wxRichTextFloatCollector::~wxRichTextFloatCollector()
274 FreeFloatRectMapArray(m_left
);
275 FreeFloatRectMapArray(m_right
);
278 int wxRichTextFloatCollector::GetFitPosition(const wxRichTextFloatRectMapArray
& array
, int start
, int height
) const
280 if (array
.GetCount() == 0)
283 int i
= SearchAdjacentRect(array
, start
);
285 while (i
< (int) array
.GetCount())
287 if (array
[i
]->startY
- last
>= height
)
289 last
= array
[i
]->endY
;
296 int wxRichTextFloatCollector::GetFitPosition(int direction
, int start
, int height
) const
298 if (direction
== wxTEXT_BOX_ATTR_FLOAT_LEFT
)
299 return GetFitPosition(m_left
, start
, height
);
300 else if (direction
== wxTEXT_BOX_ATTR_FLOAT_RIGHT
)
301 return GetFitPosition(m_right
, start
, height
);
304 wxASSERT("Never should be here");
309 // Adds a floating image to the float collector.
310 // The actual positioning is done by wxRichTextParagraph::LayoutFloat.
311 void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph
* para
, wxRichTextObject
* floating
)
313 int direction
= floating
->GetFloatDirection();
315 wxPoint pos
= floating
->GetPosition();
316 wxSize size
= floating
->GetCachedSize();
317 wxRichTextFloatRectMap
*map
= new wxRichTextFloatRectMap(pos
.y
, pos
.y
+ size
.y
, size
.x
, floating
);
320 case wxTEXT_BOX_ATTR_FLOAT_NONE
:
323 case wxTEXT_BOX_ATTR_FLOAT_LEFT
:
324 // Just a not-enough simple assertion
325 wxASSERT (m_left
.Index(map
) == wxNOT_FOUND
);
328 case wxTEXT_BOX_ATTR_FLOAT_RIGHT
:
329 wxASSERT (m_right
.Index(map
) == wxNOT_FOUND
);
334 wxASSERT("Unrecognised float attribute.");
340 void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph
* para
)
342 wxRichTextObjectList::compatibility_iterator node
= para
->GetChildren().GetFirst();
345 wxRichTextObject
* floating
= node
->GetData();
347 if (floating
->IsFloating())
349 CollectFloat(para
, floating
);
352 node
= node
->GetNext();
358 wxRichTextParagraph
* wxRichTextFloatCollector::LastParagraph()
363 wxRect
wxRichTextFloatCollector::GetAvailableRect(int startY
, int endY
)
365 int widthLeft
= 0, widthRight
= 0;
366 if (m_left
.GetCount() != 0)
368 int i
= SearchAdjacentRect(m_left
, startY
);
369 if (i
< (int) m_left
.GetCount())
370 widthLeft
= GetWidthFromFloatRect(m_left
, i
, startY
, endY
);
372 if (m_right
.GetCount() != 0)
374 int j
= SearchAdjacentRect(m_right
, startY
);
375 if (j
< (int) m_right
.GetCount())
376 widthRight
= GetWidthFromFloatRect(m_right
, j
, startY
, endY
);
379 // TODO: actually we want to use the actual image positions to find the
380 // available remaining space, since the image might not be right up against
381 // the left or right edge of the container.
382 return wxRect(widthLeft
+ m_availableRect
.x
, 0, m_availableRect
.width
- widthLeft
- widthRight
, 0);
385 int wxRichTextFloatCollector::GetLastRectBottom()
388 int len
= m_left
.GetCount();
390 ret
= ret
> m_left
[len
-1]->endY
? ret
: m_left
[len
-1]->endY
;
392 len
= m_right
.GetCount();
394 ret
= ret
> m_right
[len
-1]->endY
? ret
: m_right
[len
-1]->endY
;
400 void wxRichTextFloatCollector::DrawFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& WXUNUSED(range
), const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
403 int end
= rect
.y
+ rect
.height
;
405 i
= SearchAdjacentRect(array
, start
);
406 if (i
< 0 || i
>= (int) array
.GetCount())
408 j
= SearchAdjacentRect(array
, end
);
409 if (j
< 0 || j
>= (int) array
.GetCount())
410 j
= array
.GetCount() - 1;
413 wxRichTextObject
* obj
= array
[i
]->anchor
;
414 wxRichTextRange r
= obj
->GetRange();
415 obj
->Draw(dc
, context
, r
, selection
, wxRect(obj
->GetPosition(), obj
->GetCachedSize()), descent
, style
);
420 void wxRichTextFloatCollector::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
422 if (m_left
.GetCount() > 0)
423 DrawFloat(m_left
, dc
, context
, range
, selection
, rect
, descent
, style
);
424 if (m_right
.GetCount() > 0)
425 DrawFloat(m_right
, dc
, context
, range
, selection
, rect
, descent
, style
);
428 int wxRichTextFloatCollector::HitTestFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& WXUNUSED(dc
), wxRichTextDrawingContext
& WXUNUSED(context
), const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, int WXUNUSED(flags
))
431 if (array
.GetCount() == 0)
432 return wxRICHTEXT_HITTEST_NONE
;
433 i
= SearchAdjacentRect(array
, pt
.y
);
434 if (i
< 0 || i
>= (int) array
.GetCount())
435 return wxRICHTEXT_HITTEST_NONE
;
436 if (!array
[i
]->anchor
->IsShown())
437 return wxRICHTEXT_HITTEST_NONE
;
439 wxPoint point
= array
[i
]->anchor
->GetPosition();
440 wxSize size
= array
[i
]->anchor
->GetCachedSize();
441 if (point
.x
<= pt
.x
&& point
.x
+ size
.x
>= pt
.x
442 && point
.y
<= pt
.y
&& point
.y
+ size
.y
>= pt
.y
)
444 textPosition
= array
[i
]->anchor
->GetRange().GetStart();
445 * obj
= array
[i
]->anchor
;
446 if (pt
.x
> (pt
.x
+ pt
.x
+ size
.x
) / 2)
447 return wxRICHTEXT_HITTEST_BEFORE
;
449 return wxRICHTEXT_HITTEST_AFTER
;
452 return wxRICHTEXT_HITTEST_NONE
;
455 int wxRichTextFloatCollector::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, int flags
)
457 int ret
= HitTestFloat(m_left
, dc
, context
, pt
, textPosition
, obj
, flags
);
458 if (ret
== wxRICHTEXT_HITTEST_NONE
)
460 ret
= HitTestFloat(m_right
, dc
, context
, pt
, textPosition
, obj
, flags
);
465 // Helpers for efficiency
466 inline void wxCheckSetFont(wxDC
& dc
, const wxFont
& font
)
471 inline void wxCheckSetPen(wxDC
& dc
, const wxPen
& pen
)
473 const wxPen
& pen1
= dc
.GetPen();
474 if (pen1
.IsOk() && pen
.IsOk())
476 if (pen1
.GetWidth() == pen
.GetWidth() &&
477 pen1
.GetStyle() == pen
.GetStyle() &&
478 pen1
.GetColour() == pen
.GetColour())
484 inline void wxCheckSetBrush(wxDC
& dc
, const wxBrush
& brush
)
486 const wxBrush
& brush1
= dc
.GetBrush();
487 if (brush1
.IsOk() && brush
.IsOk())
489 if (brush1
.GetStyle() == brush
.GetStyle() &&
490 brush1
.GetColour() == brush
.GetColour())
498 * This is the base for drawable objects.
501 IMPLEMENT_CLASS(wxRichTextObject
, wxObject
)
503 wxRichTextObject::wxRichTextObject(wxRichTextObject
* parent
)
511 wxRichTextObject::~wxRichTextObject()
515 void wxRichTextObject::Dereference()
523 void wxRichTextObject::Copy(const wxRichTextObject
& obj
)
526 m_maxSize
= obj
.m_maxSize
;
527 m_minSize
= obj
.m_minSize
;
529 m_range
= obj
.m_range
;
530 m_ownRange
= obj
.m_ownRange
;
531 m_attributes
= obj
.m_attributes
;
532 m_properties
= obj
.m_properties
;
533 m_descent
= obj
.m_descent
;
537 // Get/set the top-level container of this object.
538 wxRichTextParagraphLayoutBox
* wxRichTextObject::GetContainer() const
540 const wxRichTextObject
* p
= this;
545 return wxDynamicCast(p
, wxRichTextParagraphLayoutBox
);
552 void wxRichTextObject::SetMargins(int margin
)
554 SetMargins(margin
, margin
, margin
, margin
);
557 void wxRichTextObject::SetMargins(int leftMargin
, int rightMargin
, int topMargin
, int bottomMargin
)
559 GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().SetValue(leftMargin
, wxTEXT_ATTR_UNITS_PIXELS
);
560 GetAttributes().GetTextBoxAttr().GetMargins().GetRight().SetValue(rightMargin
, wxTEXT_ATTR_UNITS_PIXELS
);
561 GetAttributes().GetTextBoxAttr().GetMargins().GetTop().SetValue(topMargin
, wxTEXT_ATTR_UNITS_PIXELS
);
562 GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().SetValue(bottomMargin
, wxTEXT_ATTR_UNITS_PIXELS
);
565 int wxRichTextObject::GetLeftMargin() const
567 return GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().GetValue();
570 int wxRichTextObject::GetRightMargin() const
572 return GetAttributes().GetTextBoxAttr().GetMargins().GetRight().GetValue();
575 int wxRichTextObject::GetTopMargin() const
577 return GetAttributes().GetTextBoxAttr().GetMargins().GetTop().GetValue();
580 int wxRichTextObject::GetBottomMargin() const
582 return GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().GetValue();
585 // Calculate the available content space in the given rectangle, given the
586 // margins, border and padding specified in the object's attributes.
587 wxRect
wxRichTextObject::GetAvailableContentArea(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& outerRect
) const
589 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
590 marginRect
= outerRect
;
591 wxRichTextAttr
attr(GetAttributes());
592 context
.ApplyVirtualAttributes(attr
, (wxRichTextObject
*) this);
593 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
597 // Invalidate the buffer. With no argument, invalidates whole buffer.
598 void wxRichTextObject::Invalidate(const wxRichTextRange
& invalidRange
)
600 if (invalidRange
!= wxRICHTEXT_NONE
)
602 // If this is a floating object, size may not be recalculated
603 // after floats have been collected in an early stage of Layout.
604 // So avoid resetting the cache for floating objects during layout.
605 if (!IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode())
606 SetCachedSize(wxDefaultSize
);
607 SetMaxSize(wxDefaultSize
);
608 SetMinSize(wxDefaultSize
);
612 // Convert units in tenths of a millimetre to device units
613 int wxRichTextObject::ConvertTenthsMMToPixels(wxDC
& dc
, int units
) const
618 scale
= GetBuffer()->GetScale() / GetBuffer()->GetDimensionScale();
619 int p
= ConvertTenthsMMToPixels(dc
.GetPPI().x
, units
, scale
);
624 // Convert units in tenths of a millimetre to device units
625 int wxRichTextObject::ConvertTenthsMMToPixels(int ppi
, int units
, double scale
)
627 // There are ppi pixels in 254.1 "1/10 mm"
629 double pixels
= ((double) units
* (double)ppi
) / 254.1;
633 // If the result is very small, make it at least one pixel in size.
634 if (pixels
== 0 && units
> 0)
640 // Convert units in pixels to tenths of a millimetre
641 int wxRichTextObject::ConvertPixelsToTenthsMM(wxDC
& dc
, int pixels
) const
646 scale
= GetBuffer()->GetScale();
648 return ConvertPixelsToTenthsMM(dc
.GetPPI().x
, p
, scale
);
651 int wxRichTextObject::ConvertPixelsToTenthsMM(int ppi
, int pixels
, double scale
)
653 // There are ppi pixels in 254.1 "1/10 mm"
655 double p
= double(pixels
);
660 int units
= int( p
* 254.1 / (double) ppi
);
664 // Draw the borders and background for the given rectangle and attributes.
665 // Width and height are taken to be the outer margin size, not the content.
666 bool wxRichTextObject::DrawBoxAttributes(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& attr
, const wxRect
& boxRect
, int flags
)
668 // Assume boxRect is the area around the content
669 wxRect marginRect
= boxRect
;
670 wxRect contentRect
, borderRect
, paddingRect
, outlineRect
;
672 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
674 // Margin is transparent. Draw background from margin.
675 if (attr
.HasBackgroundColour() || (flags
& wxRICHTEXT_DRAW_SELECTED
))
678 if (flags
& wxRICHTEXT_DRAW_SELECTED
)
680 // TODO: get selection colour from control?
681 colour
= wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
);
684 colour
= attr
.GetBackgroundColour();
687 wxBrush
brush(colour
);
691 dc
.DrawRectangle(borderRect
);
694 if (flags
& wxRICHTEXT_DRAW_GUIDELINES
)
696 wxRichTextAttr editBorderAttr
= attr
;
697 // TODO: make guideline colour configurable
698 editBorderAttr
.GetTextBoxAttr().GetBorder().SetColour(*wxLIGHT_GREY
);
699 editBorderAttr
.GetTextBoxAttr().GetBorder().SetWidth(1, wxTEXT_ATTR_UNITS_PIXELS
);
700 editBorderAttr
.GetTextBoxAttr().GetBorder().SetStyle(wxTEXT_BOX_ATTR_BORDER_SOLID
);
702 DrawBorder(dc
, buffer
, editBorderAttr
.GetTextBoxAttr().GetBorder(), borderRect
, flags
);
705 if (attr
.GetTextBoxAttr().GetBorder().IsValid())
706 DrawBorder(dc
, buffer
, attr
.GetTextBoxAttr().GetBorder(), borderRect
);
708 if (attr
.GetTextBoxAttr().GetOutline().IsValid())
709 DrawBorder(dc
, buffer
, attr
.GetTextBoxAttr().GetOutline(), outlineRect
);
715 bool wxRichTextObject::DrawBorder(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxTextAttrBorders
& attr
, const wxRect
& rect
, int WXUNUSED(flags
))
717 int borderLeft
= 0, borderRight
= 0, borderTop
= 0, borderBottom
= 0;
718 wxTextAttrDimensionConverter
converter(dc
, buffer
? buffer
->GetScale() : 1.0);
720 if (attr
.GetLeft().IsValid() && attr
.GetLeft().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
722 borderLeft
= converter
.GetPixels(attr
.GetLeft().GetWidth());
723 wxColour
col(attr
.GetLeft().GetColour());
725 // If pen width is > 1, resorts to a solid rectangle.
728 int penStyle
= wxSOLID
;
729 if (attr
.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
731 else if (attr
.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
732 penStyle
= wxLONG_DASH
;
733 wxPen
pen(col
, 1, penStyle
);
735 dc
.DrawLine(rect
.x
, rect
.y
, rect
.x
, rect
.y
+ rect
.height
);
738 else if (borderLeft
> 1)
744 dc
.DrawRectangle(rect
.x
, rect
.y
, borderLeft
, rect
.height
);
748 if (attr
.GetRight().IsValid() && attr
.GetRight().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
750 borderRight
= converter
.GetPixels(attr
.GetRight().GetWidth());
752 wxColour
col(attr
.GetRight().GetColour());
754 // If pen width is > 1, resorts to a solid rectangle.
755 if (borderRight
== 1)
757 int penStyle
= wxSOLID
;
758 if (attr
.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
760 else if (attr
.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
761 penStyle
= wxLONG_DASH
;
762 wxPen
pen(col
, 1, penStyle
);
764 dc
.DrawLine(rect
.x
+ rect
.width
, rect
.y
, rect
.x
+ rect
.width
, rect
.y
+ rect
.height
+ 1);
767 else if (borderRight
> 1)
773 dc
.DrawRectangle(rect
.x
+ rect
.width
- borderRight
, rect
.y
, borderRight
, rect
.height
);
777 if (attr
.GetTop().IsValid() && attr
.GetTop().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
779 borderTop
= converter
.GetPixels(attr
.GetTop().GetWidth());
781 wxColour
col(attr
.GetTop().GetColour());
783 // If pen width is > 1, resorts to a solid rectangle.
786 int penStyle
= wxSOLID
;
787 if (attr
.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
789 else if (attr
.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
790 penStyle
= wxLONG_DASH
;
791 wxPen
pen(col
, 1, penStyle
);
793 dc
.DrawLine(rect
.x
, rect
.y
, rect
.x
+ rect
.width
, rect
.y
);
796 else if (borderTop
> 1)
802 dc
.DrawRectangle(rect
.x
, rect
.y
, rect
.width
, borderTop
);
806 if (attr
.GetBottom().IsValid() && attr
.GetBottom().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
808 borderBottom
= converter
.GetPixels(attr
.GetBottom().GetWidth());
809 wxColour
col(attr
.GetBottom().GetColour());
811 // If pen width is > 1, resorts to a solid rectangle.
812 if (borderBottom
== 1)
814 int penStyle
= wxSOLID
;
815 if (attr
.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
817 else if (attr
.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
818 penStyle
= wxLONG_DASH
;
819 wxPen
pen(col
, 1, penStyle
);
821 dc
.DrawLine(rect
.x
, rect
.y
+ rect
.height
, rect
.x
+ rect
.width
, rect
.y
+ rect
.height
);
824 else if (borderBottom
> 1)
830 dc
.DrawRectangle(rect
.x
, rect
.y
+ rect
.height
- borderBottom
, rect
.width
, borderBottom
);
837 // Get the various rectangles of the box model in pixels. You can either specify contentRect (inner)
838 // or marginRect (outer), and the other must be the default rectangle (no width or height).
839 // Note that the outline doesn't affect the position of the rectangle, it's drawn in whatever space
842 // | Margin | Border | Padding | CONTENT | Padding | Border | Margin |
844 bool wxRichTextObject::GetBoxRects(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& attr
, wxRect
& marginRect
, wxRect
& borderRect
, wxRect
& contentRect
, wxRect
& paddingRect
, wxRect
& outlineRect
)
846 int borderLeft
= 0, borderRight
= 0, borderTop
= 0, borderBottom
= 0;
847 int outlineLeft
= 0, outlineRight
= 0, outlineTop
= 0, outlineBottom
= 0;
848 int paddingLeft
= 0, paddingRight
= 0, paddingTop
= 0, paddingBottom
= 0;
849 int marginLeft
= 0, marginRight
= 0, marginTop
= 0, marginBottom
= 0;
851 wxTextAttrDimensionConverter
converter(dc
, buffer
? buffer
->GetScale() : 1.0);
853 if (attr
.GetTextBoxAttr().GetMargins().GetLeft().IsValid())
854 marginLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetLeft());
855 if (attr
.GetTextBoxAttr().GetMargins().GetRight().IsValid())
856 marginRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetRight());
857 if (attr
.GetTextBoxAttr().GetMargins().GetTop().IsValid())
858 marginTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetTop());
859 if (attr
.GetTextBoxAttr().GetMargins().GetBottom().IsValid())
860 marginBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetBottom());
862 if (attr
.GetTextBoxAttr().GetBorder().GetLeft().GetWidth().IsValid())
863 borderLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetLeft().GetWidth());
864 if (attr
.GetTextBoxAttr().GetBorder().GetRight().GetWidth().IsValid())
865 borderRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetRight().GetWidth());
866 if (attr
.GetTextBoxAttr().GetBorder().GetTop().GetWidth().IsValid())
867 borderTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetTop().GetWidth());
868 if (attr
.GetTextBoxAttr().GetBorder().GetBottom().GetWidth().IsValid())
869 borderBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetBottom().GetWidth());
871 if (attr
.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
872 paddingLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetLeft());
873 if (attr
.GetTextBoxAttr().GetPadding().GetRight().IsValid())
874 paddingRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetRight());
875 if (attr
.GetTextBoxAttr().GetPadding().GetTop().IsValid())
876 paddingTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetTop());
877 if (attr
.GetTextBoxAttr().GetPadding().GetBottom().IsValid())
878 paddingBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetBottom());
880 if (attr
.GetTextBoxAttr().GetOutline().GetLeft().GetWidth().IsValid())
881 outlineLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetLeft().GetWidth());
882 if (attr
.GetTextBoxAttr().GetOutline().GetRight().GetWidth().IsValid())
883 outlineRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetRight().GetWidth());
884 if (attr
.GetTextBoxAttr().GetOutline().GetTop().GetWidth().IsValid())
885 outlineTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetTop().GetWidth());
886 if (attr
.GetTextBoxAttr().GetOutline().GetBottom().GetWidth().IsValid())
887 outlineBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetBottom().GetWidth());
889 int leftTotal
= marginLeft
+ borderLeft
+ paddingLeft
;
890 int rightTotal
= marginRight
+ borderRight
+ paddingRight
;
891 int topTotal
= marginTop
+ borderTop
+ paddingTop
;
892 int bottomTotal
= marginBottom
+ borderBottom
+ paddingBottom
;
894 if (marginRect
!= wxRect())
896 contentRect
.x
= marginRect
.x
+ leftTotal
;
897 contentRect
.y
= marginRect
.y
+ topTotal
;
898 contentRect
.width
= marginRect
.width
- (leftTotal
+ rightTotal
);
899 contentRect
.height
= marginRect
.height
- (topTotal
+ bottomTotal
);
903 marginRect
.x
= contentRect
.x
- leftTotal
;
904 marginRect
.y
= contentRect
.y
- topTotal
;
905 marginRect
.width
= contentRect
.width
+ (leftTotal
+ rightTotal
);
906 marginRect
.height
= contentRect
.height
+ (topTotal
+ bottomTotal
);
909 borderRect
.x
= marginRect
.x
+ marginLeft
;
910 borderRect
.y
= marginRect
.y
+ marginTop
;
911 borderRect
.width
= marginRect
.width
- (marginLeft
+ marginRight
);
912 borderRect
.height
= marginRect
.height
- (marginTop
+ marginBottom
);
914 paddingRect
.x
= marginRect
.x
+ marginLeft
+ borderLeft
;
915 paddingRect
.y
= marginRect
.y
+ marginTop
+ borderTop
;
916 paddingRect
.width
= marginRect
.width
- (marginLeft
+ marginRight
+ borderLeft
+ borderRight
);
917 paddingRect
.height
= marginRect
.height
- (marginTop
+ marginBottom
+ borderTop
+ borderBottom
);
919 // The outline is outside the margin and doesn't influence the overall box position or content size.
920 outlineRect
.x
= marginRect
.x
- outlineLeft
;
921 outlineRect
.y
= marginRect
.y
- outlineTop
;
922 outlineRect
.width
= marginRect
.width
+ (outlineLeft
+ outlineRight
);
923 outlineRect
.height
= marginRect
.height
+ (outlineTop
+ outlineBottom
);
928 // Get the total margin for the object in pixels, taking into account margin, padding and border size
929 bool wxRichTextObject::GetTotalMargin(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& attr
, int& leftMargin
, int& rightMargin
,
930 int& topMargin
, int& bottomMargin
)
932 // Assume boxRect is the area around the content
933 wxRect contentRect
, marginRect
, borderRect
, paddingRect
, outlineRect
;
934 marginRect
= wxRect(0, 0, 1000, 1000);
936 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
938 leftMargin
= contentRect
.GetLeft() - marginRect
.GetLeft();
939 rightMargin
= marginRect
.GetRight() - contentRect
.GetRight();
940 topMargin
= contentRect
.GetTop() - marginRect
.GetTop();
941 bottomMargin
= marginRect
.GetBottom() - contentRect
.GetBottom();
946 // Returns the rectangle which the child has available to it given restrictions specified in the
947 // child attribute, e.g. 50% width of the parent, 400 pixels, x position 20% of the parent, etc.
948 // availableContainerSpace might be a parent that the cell has to compute its width relative to.
949 // E.g. a cell that's 50% of its parent.
950 wxRect
wxRichTextObject::AdjustAvailableSpace(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& WXUNUSED(parentAttr
), const wxRichTextAttr
& childAttr
, const wxRect
& availableParentSpace
, const wxRect
& availableContainerSpace
)
952 wxRect rect
= availableParentSpace
;
955 scale
= buffer
->GetScale();
957 wxTextAttrDimensionConverter
converter(dc
, scale
, availableContainerSpace
.GetSize());
959 if (childAttr
.GetTextBoxAttr().GetWidth().IsValid())
960 rect
.width
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetWidth());
962 if (childAttr
.GetTextBoxAttr().GetHeight().IsValid())
963 rect
.height
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetHeight());
965 // Can specify either left or right for the position (we're assuming we can't
966 // set the left and right edges to effectively set the size. Would we want to do that?)
967 if (childAttr
.GetTextBoxAttr().GetPosition().GetLeft().IsValid())
969 rect
.x
= rect
.x
+ converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetLeft());
971 else if (childAttr
.GetTextBoxAttr().GetPosition().GetRight().IsValid())
973 int x
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetRight());
974 if (childAttr
.GetTextBoxAttr().GetPosition().GetRight().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE
)
975 rect
.x
= availableContainerSpace
.x
+ availableContainerSpace
.width
- rect
.width
;
980 if (childAttr
.GetTextBoxAttr().GetPosition().GetTop().IsValid())
982 rect
.y
= rect
.y
+ converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetTop());
984 else if (childAttr
.GetTextBoxAttr().GetPosition().GetBottom().IsValid())
986 int y
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetBottom());
987 if (childAttr
.GetTextBoxAttr().GetPosition().GetBottom().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE
)
988 rect
.y
= availableContainerSpace
.y
+ availableContainerSpace
.height
- rect
.height
;
993 if (rect
.GetWidth() > availableParentSpace
.GetWidth())
994 rect
.SetWidth(availableParentSpace
.GetWidth());
999 // Dump to output stream for debugging
1000 void wxRichTextObject::Dump(wxTextOutputStream
& stream
)
1002 stream
<< GetClassInfo()->GetClassName() << wxT("\n");
1003 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");
1004 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");
1007 // Gets the containing buffer
1008 wxRichTextBuffer
* wxRichTextObject::GetBuffer() const
1010 const wxRichTextObject
* obj
= this;
1011 while (obj
&& !wxDynamicCast(obj
, wxRichTextBuffer
))
1012 obj
= obj
->GetParent();
1013 return wxDynamicCast(obj
, wxRichTextBuffer
);
1016 // Get the absolute object position, by traversing up the child/parent hierarchy
1017 wxPoint
wxRichTextObject::GetAbsolutePosition() const
1019 wxPoint pt
= GetPosition();
1021 wxRichTextObject
* p
= GetParent();
1024 pt
= pt
+ p
->GetPosition();
1031 // Hit-testing: returns a flag indicating hit test details, plus
1032 // information about position
1033 int wxRichTextObject::HitTest(wxDC
& WXUNUSED(dc
), wxRichTextDrawingContext
& WXUNUSED(context
), const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int WXUNUSED(flags
))
1036 return wxRICHTEXT_HITTEST_NONE
;
1038 wxRect rect
= GetRect();
1039 if (pt
.x
>= rect
.x
&& pt
.x
< rect
.x
+ rect
.width
&&
1040 pt
.y
>= rect
.y
&& pt
.y
< rect
.y
+ rect
.height
)
1043 *contextObj
= GetParentContainer();
1044 textPosition
= GetRange().GetStart();
1045 return wxRICHTEXT_HITTEST_ON
;
1048 return wxRICHTEXT_HITTEST_NONE
;
1051 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1052 // lays out the object again using the maximum ('best') size
1053 bool wxRichTextObject::LayoutToBestSize(wxDC
& dc
, wxRichTextDrawingContext
& context
, wxRichTextBuffer
* buffer
,
1054 const wxRichTextAttr
& parentAttr
, const wxRichTextAttr
& attr
,
1055 const wxRect
& availableParentSpace
, const wxRect
& availableContainerSpace
,
1058 wxRect availableChildRect
= AdjustAvailableSpace(dc
, buffer
, parentAttr
, attr
, availableParentSpace
, availableContainerSpace
);
1059 wxRect originalAvailableRect
= availableChildRect
;
1060 Layout(dc
, context
, availableChildRect
, availableContainerSpace
, style
);
1062 wxSize maxSize
= GetMaxSize();
1064 // Don't ignore if maxSize.x is zero, since we need to redo the paragraph's lines
1066 if (!attr
.GetTextBoxAttr().GetWidth().IsValid() && maxSize
.x
< availableChildRect
.width
)
1068 // Redo the layout with a fixed, minimum size this time.
1069 Invalidate(wxRICHTEXT_ALL
);
1070 wxRichTextAttr
newAttr(attr
);
1071 newAttr
.GetTextBoxAttr().GetWidth().SetValue(maxSize
.x
, wxTEXT_ATTR_UNITS_PIXELS
);
1072 newAttr
.GetTextBoxAttr().GetWidth().SetPosition(wxTEXT_BOX_ATTR_POSITION_ABSOLUTE
);
1074 availableChildRect
= AdjustAvailableSpace(dc
, buffer
, parentAttr
, newAttr
, availableParentSpace
, availableContainerSpace
);
1076 // If a paragraph, align the whole paragraph.
1077 // Problem with this: if we're limited by a floating object, a line may be centered
1078 // w.r.t. the smaller resulting box rather than the actual available width.
1079 // FIXME: aligning whole paragraph not compatible with floating objects
1080 if (attr
.HasAlignment() && (!wxRichTextBuffer::GetFloatingLayoutMode() || (GetContainer()->GetFloatCollector() && !GetContainer()->GetFloatCollector()->HasFloats())))
1082 // centering, right-justification
1083 if (attr
.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE
)
1085 availableChildRect
.x
= (originalAvailableRect
.GetWidth() - availableChildRect
.GetWidth())/2 + availableChildRect
.x
;
1087 else if (attr
.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT
)
1089 availableChildRect
.x
= availableChildRect
.x
+ originalAvailableRect
.GetWidth() - availableChildRect
.GetWidth();
1093 Layout(dc
, context
, availableChildRect
, availableContainerSpace
, style
);
1107 // Move the object recursively, by adding the offset from old to new
1108 void wxRichTextObject::Move(const wxPoint
& pt
)
1115 * wxRichTextCompositeObject
1116 * This is the base for drawable objects.
1119 IMPLEMENT_CLASS(wxRichTextCompositeObject
, wxRichTextObject
)
1121 wxRichTextCompositeObject::wxRichTextCompositeObject(wxRichTextObject
* parent
):
1122 wxRichTextObject(parent
)
1126 wxRichTextCompositeObject::~wxRichTextCompositeObject()
1131 /// Get the nth child
1132 wxRichTextObject
* wxRichTextCompositeObject::GetChild(size_t n
) const
1134 wxASSERT ( n
< m_children
.GetCount() );
1136 return m_children
.Item(n
)->GetData();
1139 /// Append a child, returning the position
1140 size_t wxRichTextCompositeObject::AppendChild(wxRichTextObject
* child
)
1142 m_children
.Append(child
);
1143 child
->SetParent(this);
1144 return m_children
.GetCount() - 1;
1147 /// Insert the child in front of the given object, or at the beginning
1148 bool wxRichTextCompositeObject::InsertChild(wxRichTextObject
* child
, wxRichTextObject
* inFrontOf
)
1152 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(inFrontOf
);
1153 m_children
.Insert(node
, child
);
1156 m_children
.Insert(child
);
1157 child
->SetParent(this);
1162 /// Delete the child
1163 bool wxRichTextCompositeObject::RemoveChild(wxRichTextObject
* child
, bool deleteChild
)
1165 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(child
);
1168 wxRichTextObject
* obj
= node
->GetData();
1169 m_children
.Erase(node
);
1178 /// Delete all children
1179 bool wxRichTextCompositeObject::DeleteChildren()
1181 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1184 wxRichTextObjectList::compatibility_iterator oldNode
= node
;
1186 wxRichTextObject
* child
= node
->GetData();
1187 child
->Dereference(); // Only delete if reference count is zero
1189 node
= node
->GetNext();
1190 m_children
.Erase(oldNode
);
1196 /// Get the child count
1197 size_t wxRichTextCompositeObject::GetChildCount() const
1199 return m_children
.GetCount();
1203 void wxRichTextCompositeObject::Copy(const wxRichTextCompositeObject
& obj
)
1205 wxRichTextObject::Copy(obj
);
1209 wxRichTextObjectList::compatibility_iterator node
= obj
.m_children
.GetFirst();
1212 wxRichTextObject
* child
= node
->GetData();
1213 wxRichTextObject
* newChild
= child
->Clone();
1214 newChild
->SetParent(this);
1215 m_children
.Append(newChild
);
1217 node
= node
->GetNext();
1221 /// Hit-testing: returns a flag indicating hit test details, plus
1222 /// information about position
1223 int wxRichTextCompositeObject::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
1226 return wxRICHTEXT_HITTEST_NONE
;
1228 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1231 wxRichTextObject
* child
= node
->GetData();
1233 if (child
->IsShown() && child
->IsTopLevel() && (flags
& wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS
))
1235 // Just check if we hit the overall object
1236 int ret
= child
->wxRichTextObject::HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
1237 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
1240 else if (child
->IsShown())
1242 int ret
= child
->HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
1243 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
1247 node
= node
->GetNext();
1250 return wxRICHTEXT_HITTEST_NONE
;
1253 /// Finds the absolute position and row height for the given character position
1254 bool wxRichTextCompositeObject::FindPosition(wxDC
& dc
, wxRichTextDrawingContext
& context
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
1256 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1259 wxRichTextObject
* child
= node
->GetData();
1261 // Don't recurse if the child is a top-level object,
1262 // such as a text box, because the character position will no longer
1263 // apply. By definition, a top-level object has its own range of
1264 // character positions.
1265 if (!child
->IsTopLevel() && child
->FindPosition(dc
, context
, index
, pt
, height
, forceLineStart
))
1268 node
= node
->GetNext();
1275 void wxRichTextCompositeObject::CalculateRange(long start
, long& end
)
1277 long current
= start
;
1278 long lastEnd
= current
;
1286 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1289 wxRichTextObject
* child
= node
->GetData();
1292 child
->CalculateRange(current
, childEnd
);
1295 current
= childEnd
+ 1;
1297 node
= node
->GetNext();
1302 // A top-level object always has a range of size 1,
1303 // because its children don't count at this level.
1305 m_range
.SetRange(start
, start
);
1307 // An object with no children has zero length
1308 if (m_children
.GetCount() == 0)
1310 m_ownRange
.SetRange(0, lastEnd
);
1316 // An object with no children has zero length
1317 if (m_children
.GetCount() == 0)
1320 m_range
.SetRange(start
, end
);
1324 /// Delete range from layout.
1325 bool wxRichTextCompositeObject::DeleteRange(const wxRichTextRange
& range
)
1327 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1331 wxRichTextObject
* obj
= (wxRichTextObject
*) node
->GetData();
1332 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
1334 // Delete the range in each paragraph
1336 // When a chunk has been deleted, internally the content does not
1337 // now match the ranges.
1338 // However, so long as deletion is not done on the same object twice this is OK.
1339 // If you may delete content from the same object twice, recalculate
1340 // the ranges inbetween DeleteRange calls by calling CalculateRanges, and
1341 // adjust the range you're deleting accordingly.
1343 if (!obj
->GetRange().IsOutside(range
))
1345 // No need to delete within a top-level object; just removing this object will do fine
1346 if (!obj
->IsTopLevel())
1347 obj
->DeleteRange(range
);
1349 // Delete an empty object, or paragraph within this range.
1350 if (obj
->IsEmpty() ||
1351 (range
.GetStart() <= obj
->GetRange().GetStart() && range
.GetEnd() >= obj
->GetRange().GetEnd()))
1353 // An empty paragraph has length 1, so won't be deleted unless the
1354 // whole range is deleted.
1355 RemoveChild(obj
, true);
1365 /// Get any text in this object for the given range
1366 wxString
wxRichTextCompositeObject::GetTextForRange(const wxRichTextRange
& range
) const
1369 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1372 wxRichTextObject
* child
= node
->GetData();
1373 wxRichTextRange childRange
= range
;
1374 if (!child
->GetRange().IsOutside(range
))
1376 childRange
.LimitTo(child
->GetRange());
1378 wxString childText
= child
->GetTextForRange(childRange
);
1382 node
= node
->GetNext();
1388 /// Get the child object at the given character position
1389 wxRichTextObject
* wxRichTextCompositeObject::GetChildAtPosition(long pos
) const
1391 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1394 wxRichTextObject
* child
= node
->GetData();
1395 if (child
->GetRange().GetStart() == pos
)
1397 node
= node
->GetNext();
1402 /// Recursively merge all pieces that can be merged.
1403 bool wxRichTextCompositeObject::Defragment(wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
)
1405 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1408 wxRichTextObject
* child
= node
->GetData();
1409 if (range
== wxRICHTEXT_ALL
|| !child
->GetRange().IsOutside(range
))
1411 wxRichTextCompositeObject
* composite
= wxDynamicCast(child
, wxRichTextCompositeObject
);
1413 composite
->Defragment(context
);
1415 // Optimization: if there are no virtual attributes, we won't need to
1416 // to split objects in order to paint individually attributed chunks.
1417 // So only merge in this case.
1418 if (!context
.GetVirtualAttributesEnabled())
1420 if (node
->GetNext())
1422 wxRichTextObject
* nextChild
= node
->GetNext()->GetData();
1423 if (child
->CanMerge(nextChild
, context
) && child
->Merge(nextChild
, context
))
1425 nextChild
->Dereference();
1426 m_children
.Erase(node
->GetNext());
1429 node
= node
->GetNext();
1432 node
= node
->GetNext();
1436 // If we might have virtual attributes, we first see if we have to split
1437 // objects so that they may be painted with potential virtual attributes,
1438 // since text objects can only draw or measure with a single attributes object
1440 wxRichTextObject
* childAfterSplit
= child
;
1441 if (child
->CanSplit(context
))
1443 childAfterSplit
= child
->Split(context
);
1444 node
= m_children
.Find(childAfterSplit
);
1447 if (node
->GetNext())
1449 wxRichTextObject
* nextChild
= node
->GetNext()->GetData();
1451 // First split child and nextChild so we have smaller fragments to merge.
1452 // Then Merge only has to test per-object virtual attributes
1453 // because for an object with all the same sub-object attributes,
1454 // then any general virtual attributes should be merged with sub-objects by
1455 // the implementation.
1457 wxRichTextObject
* nextChildAfterSplit
= nextChild
;
1459 if (nextChildAfterSplit
->CanSplit(context
))
1460 nextChildAfterSplit
= nextChild
->Split(context
);
1462 bool splitNextChild
= nextChild
!= nextChildAfterSplit
;
1464 // See if we can merge this new fragment with (perhaps the first part of) the next object.
1465 // Note that we use nextChild because if we had split nextChild, the first object always
1466 // remains (and further parts are appended). However we must use childAfterSplit since
1467 // it's the last part of a possibly split child.
1469 if (childAfterSplit
->CanMerge(nextChild
, context
) && childAfterSplit
->Merge(nextChild
, context
))
1471 nextChild
->Dereference();
1472 m_children
.Erase(node
->GetNext());
1474 // Don't set node -- we'll see if we can merge again with the next
1475 // child. UNLESS we split this or the next child, in which case we know we have to
1476 // move on to the end of the next child.
1478 node
= m_children
.Find(nextChildAfterSplit
);
1483 node
= m_children
.Find(nextChildAfterSplit
); // start from the last object in the split
1485 node
= node
->GetNext();
1489 node
= node
->GetNext();
1493 node
= node
->GetNext();
1496 // Delete any remaining empty objects, but leave at least one empty object per composite object.
1497 if (GetChildCount() > 1)
1499 node
= m_children
.GetFirst();
1502 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
1503 wxRichTextObject
* child
= node
->GetData();
1504 if (range
== wxRICHTEXT_ALL
|| !child
->GetRange().IsOutside(range
))
1506 if (child
->IsEmpty())
1508 child
->Dereference();
1509 m_children
.Erase(node
);
1514 node
= node
->GetNext();
1521 /// Dump to output stream for debugging
1522 void wxRichTextCompositeObject::Dump(wxTextOutputStream
& stream
)
1524 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1527 wxRichTextObject
* child
= node
->GetData();
1528 child
->Dump(stream
);
1529 node
= node
->GetNext();
1533 /// Get/set the object size for the given range. Returns false if the range
1534 /// is invalid for this object.
1535 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
1537 if (!range
.IsWithin(GetRange()))
1542 wxArrayInt childExtents
;
1549 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1552 wxRichTextObject
* child
= node
->GetData();
1553 if (!child
->GetRange().IsOutside(range
))
1555 // Floating objects have a zero size within the paragraph.
1556 if (child
->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode())
1561 if (partialExtents
->GetCount() > 0)
1562 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
1566 partialExtents
->Add(0 /* zero size */ + lastSize
);
1573 wxRichTextRange rangeToUse
= range
;
1574 rangeToUse
.LimitTo(child
->GetRange());
1575 if (child
->IsTopLevel())
1576 rangeToUse
= child
->GetOwnRange();
1578 int childDescent
= 0;
1580 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we're already cached the size,
1581 // but it's only going to be used after caching has taken place.
1582 if ((flags
& wxRICHTEXT_HEIGHT_ONLY
) && child
->GetCachedSize().y
!= 0)
1584 childDescent
= child
->GetDescent();
1585 childSize
= child
->GetCachedSize();
1587 sz
.y
= wxMax(sz
.y
, childSize
.y
);
1588 sz
.x
+= childSize
.x
;
1589 descent
= wxMax(descent
, childDescent
);
1591 else if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, context
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
), parentSize
, p
))
1593 sz
.y
= wxMax(sz
.y
, childSize
.y
);
1594 sz
.x
+= childSize
.x
;
1595 descent
= wxMax(descent
, childDescent
);
1597 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange() || child
->IsTopLevel()))
1599 child
->SetCachedSize(childSize
);
1600 child
->SetDescent(childDescent
);
1606 if (partialExtents
->GetCount() > 0)
1607 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
1612 for (i
= 0; i
< childExtents
.GetCount(); i
++)
1614 partialExtents
->Add(childExtents
[i
] + lastSize
);
1624 node
= node
->GetNext();
1630 // Invalidate the buffer. With no argument, invalidates whole buffer.
1631 void wxRichTextCompositeObject::Invalidate(const wxRichTextRange
& invalidRange
)
1633 wxRichTextObject::Invalidate(invalidRange
);
1635 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1638 wxRichTextObject
* child
= node
->GetData();
1639 if (invalidRange
!= wxRICHTEXT_ALL
&& invalidRange
!= wxRICHTEXT_NONE
&& child
->GetRange().IsOutside(invalidRange
))
1643 else if (child
->IsTopLevel())
1645 if (wxRichTextBuffer::GetFloatingLayoutMode() && child
->IsFloating() && GetBuffer()->GetFloatCollector() && GetBuffer()->GetFloatCollector()->HasFloat(child
))
1647 // Don't invalidate subhierarchy if we've already been laid out
1651 if (invalidRange
== wxRICHTEXT_NONE
)
1652 child
->Invalidate(wxRICHTEXT_NONE
);
1654 child
->Invalidate(wxRICHTEXT_ALL
); // All children must be invalidated if within parent range
1658 child
->Invalidate(invalidRange
);
1659 node
= node
->GetNext();
1663 // Move the object recursively, by adding the offset from old to new
1664 void wxRichTextCompositeObject::Move(const wxPoint
& pt
)
1666 wxPoint oldPos
= GetPosition();
1668 wxPoint offset
= pt
- oldPos
;
1670 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1673 wxRichTextObject
* child
= node
->GetData();
1674 wxPoint childPos
= child
->GetPosition() + offset
;
1675 child
->Move(childPos
);
1676 node
= node
->GetNext();
1682 * wxRichTextParagraphLayoutBox
1683 * This box knows how to lay out paragraphs.
1686 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraphLayoutBox
, wxRichTextCompositeObject
)
1688 wxRichTextParagraphLayoutBox::wxRichTextParagraphLayoutBox(wxRichTextObject
* parent
):
1689 wxRichTextCompositeObject(parent
)
1694 wxRichTextParagraphLayoutBox::~wxRichTextParagraphLayoutBox()
1696 if (m_floatCollector
)
1698 delete m_floatCollector
;
1699 m_floatCollector
= NULL
;
1703 /// Initialize the object.
1704 void wxRichTextParagraphLayoutBox::Init()
1708 // For now, assume is the only box and has no initial size.
1709 m_range
= wxRichTextRange(0, -1);
1710 m_ownRange
= wxRichTextRange(0, -1);
1712 m_invalidRange
= wxRICHTEXT_ALL
;
1714 m_partialParagraph
= false;
1715 m_floatCollector
= NULL
;
1718 void wxRichTextParagraphLayoutBox::Clear()
1722 if (m_floatCollector
)
1723 delete m_floatCollector
;
1724 m_floatCollector
= NULL
;
1725 m_partialParagraph
= false;
1729 void wxRichTextParagraphLayoutBox::Copy(const wxRichTextParagraphLayoutBox
& obj
)
1733 wxRichTextCompositeObject::Copy(obj
);
1735 m_partialParagraph
= obj
.m_partialParagraph
;
1736 m_defaultAttributes
= obj
.m_defaultAttributes
;
1739 // Gather information about floating objects; only gather floats for those paragraphs that
1740 // will not be formatted again due to optimization, after which floats will be gathered per-paragraph
1742 bool wxRichTextParagraphLayoutBox::UpdateFloatingObjects(const wxRect
& availableRect
, wxRichTextObject
* untilObj
)
1744 if (m_floatCollector
!= NULL
)
1745 delete m_floatCollector
;
1746 m_floatCollector
= new wxRichTextFloatCollector(availableRect
);
1747 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1748 // Only gather floats up to the point we'll start formatting paragraphs.
1749 while (untilObj
&& node
&& node
->GetData() != untilObj
)
1751 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
1752 wxASSERT (child
!= NULL
);
1754 m_floatCollector
->CollectFloat(child
);
1755 node
= node
->GetNext();
1761 // Returns the style sheet associated with the overall buffer.
1762 wxRichTextStyleSheet
* wxRichTextParagraphLayoutBox::GetStyleSheet() const
1764 return GetBuffer() ? GetBuffer()->GetStyleSheet() : (wxRichTextStyleSheet
*) NULL
;
1767 // Get the number of floating objects at this level
1768 int wxRichTextParagraphLayoutBox::GetFloatingObjectCount() const
1770 if (m_floatCollector
)
1771 return m_floatCollector
->GetFloatingObjectCount();
1776 // Get a list of floating objects
1777 bool wxRichTextParagraphLayoutBox::GetFloatingObjects(wxRichTextObjectList
& objects
) const
1779 if (m_floatCollector
)
1781 return m_floatCollector
->GetFloatingObjects(objects
);
1788 void wxRichTextParagraphLayoutBox::UpdateRanges()
1792 start
= GetRange().GetStart();
1794 CalculateRange(start
, end
);
1798 int wxRichTextParagraphLayoutBox::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
1801 return wxRICHTEXT_HITTEST_NONE
;
1803 int ret
= wxRICHTEXT_HITTEST_NONE
;
1804 if (wxRichTextBuffer::GetFloatingLayoutMode() && m_floatCollector
&& (flags
& wxRICHTEXT_HITTEST_NO_FLOATING_OBJECTS
) == 0)
1805 ret
= m_floatCollector
->HitTest(dc
, context
, pt
, textPosition
, obj
, flags
);
1807 if (ret
== wxRICHTEXT_HITTEST_NONE
)
1808 return wxRichTextCompositeObject::HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
1816 /// Draw the floating objects
1817 void wxRichTextParagraphLayoutBox::DrawFloats(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
1819 if (wxRichTextBuffer::GetFloatingLayoutMode() && m_floatCollector
)
1820 m_floatCollector
->Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
1823 void wxRichTextParagraphLayoutBox::MoveAnchoredObjectToParagraph(wxRichTextParagraph
* from
, wxRichTextParagraph
* to
, wxRichTextObject
* obj
)
1828 from
->RemoveChild(obj
);
1829 to
->AppendChild(obj
);
1833 bool wxRichTextParagraphLayoutBox::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
1838 wxRect
thisRect(GetPosition(), GetCachedSize());
1840 wxRichTextAttr
attr(GetAttributes());
1841 context
.ApplyVirtualAttributes(attr
, this);
1844 if (selection
.IsValid() && GetParentContainer() != this && selection
.WithinSelection(GetRange().GetStart(), GetParentContainer()))
1845 flags
|= wxRICHTEXT_DRAW_SELECTED
;
1847 // Don't draw guidelines if at top level
1848 int theseFlags
= flags
;
1850 theseFlags
&= ~wxRICHTEXT_DRAW_GUIDELINES
;
1851 DrawBoxAttributes(dc
, GetBuffer(), attr
, thisRect
, theseFlags
);
1853 if (wxRichTextBuffer::GetFloatingLayoutMode())
1854 DrawFloats(dc
, context
, range
, selection
, rect
, descent
, style
);
1856 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1859 wxRichTextObject
* child
= node
->GetData();
1861 if (child
&& !child
->GetRange().IsOutside(range
))
1863 wxRect
childRect(child
->GetPosition(), child
->GetCachedSize());
1864 wxRichTextRange childRange
= range
;
1865 if (child
->IsTopLevel())
1867 childRange
= child
->GetOwnRange();
1870 if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) == 0) && childRect
.GetTop() > rect
.GetBottom())
1875 else if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) == 0) && childRect
.GetBottom() < rect
.GetTop())
1880 child
->Draw(dc
, context
, childRange
, selection
, rect
, descent
, style
);
1883 node
= node
->GetNext();
1888 /// Lay the item out
1889 bool wxRichTextParagraphLayoutBox::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
)
1891 SetPosition(rect
.GetPosition());
1896 wxRect availableSpace
;
1897 bool formatRect
= (style
& wxRICHTEXT_LAYOUT_SPECIFIED_RECT
) == wxRICHTEXT_LAYOUT_SPECIFIED_RECT
;
1899 wxRichTextAttr
attr(GetAttributes());
1900 context
.ApplyVirtualAttributes(attr
, this);
1902 // If only laying out a specific area, the passed rect has a different meaning:
1903 // the visible part of the buffer. This is used in wxRichTextCtrl::OnSize,
1904 // so that during a size, only the visible part will be relaid out, or
1905 // it would take too long causing flicker. As an approximation, we assume that
1906 // everything up to the start of the visible area is laid out correctly.
1909 wxRect
rect2(0, 0, rect
.width
, rect
.height
);
1910 availableSpace
= GetAvailableContentArea(dc
, context
, rect2
);
1912 // Invalidate the part of the buffer from the first visible line
1913 // to the end. If other parts of the buffer are currently invalid,
1914 // then they too will be taken into account if they are above
1915 // the visible point.
1917 wxRichTextLine
* line
= GetLineAtYPosition(rect
.y
);
1919 startPos
= line
->GetAbsoluteRange().GetStart();
1921 Invalidate(wxRichTextRange(startPos
, GetOwnRange().GetEnd()));
1925 availableSpace
= GetAvailableContentArea(dc
, context
, rect
);
1928 // Fix the width if we're at the top level
1930 attr
.GetTextBoxAttr().GetWidth().SetValue(rect
.GetWidth(), wxTEXT_ATTR_UNITS_PIXELS
);
1932 int leftMargin
, rightMargin
, topMargin
, bottomMargin
;
1933 wxRichTextObject::GetTotalMargin(dc
, GetBuffer(), attr
, leftMargin
, rightMargin
,
1934 topMargin
, bottomMargin
);
1939 // The maximum paragraph maximum width, so we can set the overall maximum width for this object
1940 int maxMaxWidth
= 0;
1942 // The maximum paragraph minimum width, so we can set the overall minimum width for this object
1943 int maxMinWidth
= 0;
1945 // If we have vertical alignment, we must recalculate everything.
1946 bool hasVerticalAlignment
= (attr
.GetTextBoxAttr().HasVerticalAlignment() &&
1947 (attr
.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP
));
1949 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1951 bool layoutAll
= true;
1953 // Get invalid range, rounding to paragraph start/end.
1954 wxRichTextRange invalidRange
= GetInvalidRange(true);
1956 if (invalidRange
== wxRICHTEXT_NONE
&& !formatRect
)
1959 if (invalidRange
== wxRICHTEXT_ALL
|| hasVerticalAlignment
)
1961 else // If we know what range is affected, start laying out from that point on.
1962 if (invalidRange
.GetStart() >= GetOwnRange().GetStart())
1964 wxRichTextParagraph
* firstParagraph
= GetParagraphAtPosition(invalidRange
.GetStart());
1967 wxRichTextObjectList::compatibility_iterator firstNode
= m_children
.Find(firstParagraph
);
1968 wxRichTextObjectList::compatibility_iterator previousNode
;
1970 previousNode
= firstNode
->GetPrevious();
1975 wxRichTextParagraph
* previousParagraph
= wxDynamicCast(previousNode
->GetData(), wxRichTextParagraph
);
1976 availableSpace
.y
= previousParagraph
->GetPosition().y
+ previousParagraph
->GetCachedSize().y
;
1979 // Now we're going to start iterating from the first affected paragraph.
1987 // Gather information about only those floating objects that will not be formatted,
1988 // after which floats will be gathered per-paragraph during layout.
1989 if (wxRichTextBuffer::GetFloatingLayoutMode())
1990 UpdateFloatingObjects(availableSpace
, node
? node
->GetData() : (wxRichTextObject
*) NULL
);
1992 // A way to force speedy rest-of-buffer layout (the 'else' below)
1993 bool forceQuickLayout
= false;
1995 // First get the size of the paragraphs we won't be laying out
1996 wxRichTextObjectList::compatibility_iterator n
= m_children
.GetFirst();
1997 while (n
&& n
!= node
)
1999 wxRichTextParagraph
* child
= wxDynamicCast(n
->GetData(), wxRichTextParagraph
);
2002 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
2003 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
2004 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
2011 // Assume this box only contains paragraphs
2013 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2014 // Unsure if this is needed
2015 // wxCHECK_MSG( child, false, wxT("Unknown object in layout") );
2017 if (child
&& child
->IsShown())
2019 // TODO: what if the child hasn't been laid out (e.g. involved in Undo) but still has 'old' lines
2020 if ( !forceQuickLayout
&&
2022 child
->GetLines().IsEmpty() ||
2023 !child
->GetRange().IsOutside(invalidRange
)) )
2025 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
2026 // lays out the object again using the minimum size
2027 child
->LayoutToBestSize(dc
, context
, GetBuffer(),
2028 attr
, child
->GetAttributes(), availableSpace
, rect
, style
&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT
);
2030 // Layout must set the cached size
2031 availableSpace
.y
+= child
->GetCachedSize().y
;
2032 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
2033 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
2034 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
2036 // If we're just formatting the visible part of the buffer,
2037 // and we're now past the bottom of the window, and we don't have any
2038 // floating objects (since they may cause wrapping to change for the rest of the
2039 // the buffer), start quick layout.
2040 if (!hasVerticalAlignment
&& formatRect
&& child
->GetPosition().y
> rect
.GetBottom() && GetFloatingObjectCount() == 0)
2041 forceQuickLayout
= true;
2045 // We're outside the immediately affected range, so now let's just
2046 // move everything up or down. This assumes that all the children have previously
2047 // been laid out and have wrapped line lists associated with them.
2048 // TODO: check all paragraphs before the affected range.
2050 int inc
= availableSpace
.y
- child
->GetPosition().y
;
2054 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2057 if (child
->GetLines().GetCount() == 0)
2059 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
2060 // lays out the object again using the minimum size
2061 child
->LayoutToBestSize(dc
, context
, GetBuffer(),
2062 attr
, child
->GetAttributes(), availableSpace
, rect
, style
&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT
);
2064 //child->Layout(dc, availableChildRect, style);
2067 child
->Move(wxPoint(child
->GetPosition().x
, child
->GetPosition().y
+ inc
));
2069 availableSpace
.y
+= child
->GetCachedSize().y
;
2070 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
2071 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
2072 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
2075 node
= node
->GetNext();
2081 node
= node
->GetNext();
2084 node
= m_children
.GetLast();
2085 if (node
&& node
->GetData()->IsShown())
2087 wxRichTextObject
* child
= node
->GetData();
2088 maxHeight
= child
->GetPosition().y
- (GetPosition().y
+ topMargin
) + child
->GetCachedSize().y
;
2091 maxHeight
= 0; // topMargin + bottomMargin;
2093 // Check the bottom edge of any floating object
2094 if (wxRichTextBuffer::GetFloatingLayoutMode() && GetFloatCollector() && GetFloatCollector()->HasFloats())
2096 int bottom
= GetFloatCollector()->GetLastRectBottom();
2097 if (bottom
> maxHeight
)
2101 if (attr
.GetTextBoxAttr().GetSize().GetWidth().IsValid())
2103 wxRect r
= AdjustAvailableSpace(dc
, GetBuffer(), wxRichTextAttr() /* not used */, attr
, parentRect
, parentRect
);
2104 int w
= r
.GetWidth();
2106 // Convert external to content rect
2107 w
= w
- leftMargin
- rightMargin
;
2108 maxWidth
= wxMax(maxWidth
, w
);
2109 maxMaxWidth
= wxMax(maxMaxWidth
, w
);
2113 // TODO: Make sure the layout box's position reflects
2114 // the position of the children, but without
2115 // breaking layout of a box within a paragraph.
2118 // TODO: (also in para layout) should set the
2119 // object's size to an absolute one if specified,
2120 // but if not specified, calculate it from content.
2122 // We need to add back the margins etc.
2124 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2125 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxWidth
, maxHeight
));
2126 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2127 SetCachedSize(marginRect
.GetSize());
2130 // The maximum size is the greatest of all maximum widths for all paragraphs.
2132 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2133 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxMaxWidth
, maxHeight
)); // Actually max height is a lie, we can't know it
2134 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2135 SetMaxSize(marginRect
.GetSize());
2138 // The minimum size is the greatest of all minimum widths for all paragraphs.
2140 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2141 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxMinWidth
, maxHeight
)); // Actually max height is a lie, we can't know it
2142 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2143 SetMinSize(marginRect
.GetSize());
2146 if (attr
.GetTextBoxAttr().HasVerticalAlignment() &&
2147 (attr
.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP
))
2150 int leftOverSpace
= availableSpace
.height
- topMargin
- bottomMargin
- maxHeight
;
2151 if (leftOverSpace
> 0)
2153 if (attr
.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_CENTRE
)
2155 yOffset
= (leftOverSpace
/2);
2157 else if (attr
.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_BOTTOM
)
2159 yOffset
= leftOverSpace
;
2163 // Move all the children to vertically align the content
2164 // This doesn't take into account floating objects, unfortunately.
2167 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2170 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2172 child
->Move(wxPoint(child
->GetPosition().x
, child
->GetPosition().y
+ yOffset
));
2174 node
= node
->GetNext();
2179 m_invalidRange
= wxRICHTEXT_NONE
;
2184 /// Get/set the size for the given range.
2185 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
2189 wxRichTextObjectList::compatibility_iterator startPara
= wxRichTextObjectList::compatibility_iterator();
2190 wxRichTextObjectList::compatibility_iterator endPara
= wxRichTextObjectList::compatibility_iterator();
2192 // First find the first paragraph whose starting position is within the range.
2193 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2196 // child is a paragraph
2197 wxRichTextObject
* child
= node
->GetData();
2198 const wxRichTextRange
& r
= child
->GetRange();
2200 if (r
.GetStart() <= range
.GetStart() && r
.GetEnd() >= range
.GetStart())
2206 node
= node
->GetNext();
2209 // Next find the last paragraph containing part of the range
2210 node
= m_children
.GetFirst();
2213 // child is a paragraph
2214 wxRichTextObject
* child
= node
->GetData();
2215 const wxRichTextRange
& r
= child
->GetRange();
2217 if (r
.GetStart() <= range
.GetEnd() && r
.GetEnd() >= range
.GetEnd())
2223 node
= node
->GetNext();
2226 if (!startPara
|| !endPara
)
2229 // Now we can add up the sizes
2230 for (node
= startPara
; node
; node
= node
->GetNext())
2232 // child is a paragraph
2233 wxRichTextObject
* child
= node
->GetData();
2234 const wxRichTextRange
& childRange
= child
->GetRange();
2235 wxRichTextRange rangeToFind
= range
;
2236 rangeToFind
.LimitTo(childRange
);
2238 if (child
->IsTopLevel())
2239 rangeToFind
= child
->GetOwnRange();
2243 int childDescent
= 0;
2244 child
->GetRangeSize(rangeToFind
, childSize
, childDescent
, dc
, context
, flags
, position
, parentSize
);
2246 descent
= wxMax(childDescent
, descent
);
2248 sz
.x
= wxMax(sz
.x
, childSize
.x
);
2249 sz
.y
+= childSize
.y
;
2251 if (node
== endPara
)
2260 /// Get the paragraph at the given position
2261 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphAtPosition(long pos
, bool caretPosition
) const
2266 // First find the first paragraph whose starting position is within the range.
2267 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2270 // child is a paragraph
2271 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2272 // wxASSERT (child != NULL);
2276 // Return first child in buffer if position is -1
2280 if (child
->GetRange().Contains(pos
))
2284 node
= node
->GetNext();
2289 /// Get the line at the given position
2290 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineAtPosition(long pos
, bool caretPosition
) const
2295 // First find the first paragraph whose starting position is within the range.
2296 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2299 wxRichTextObject
* obj
= (wxRichTextObject
*) node
->GetData();
2300 if (obj
->GetRange().Contains(pos
))
2302 // child is a paragraph
2303 wxRichTextParagraph
* child
= wxDynamicCast(obj
, wxRichTextParagraph
);
2304 // wxASSERT (child != NULL);
2308 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2311 wxRichTextLine
* line
= node2
->GetData();
2313 wxRichTextRange range
= line
->GetAbsoluteRange();
2315 if (range
.Contains(pos
) ||
2317 // If the position is end-of-paragraph, then return the last line of
2318 // of the paragraph.
2319 ((range
.GetEnd() == child
->GetRange().GetEnd()-1) && (pos
== child
->GetRange().GetEnd())))
2322 node2
= node2
->GetNext();
2327 node
= node
->GetNext();
2330 int lineCount
= GetLineCount();
2332 return GetLineForVisibleLineNumber(lineCount
-1);
2337 /// Get the line at the given y pixel position, or the last line.
2338 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineAtYPosition(int y
) const
2340 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2343 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2344 // wxASSERT (child != NULL);
2348 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2351 wxRichTextLine
* line
= node2
->GetData();
2353 wxRect
rect(line
->GetRect());
2355 if (y
<= rect
.GetBottom())
2358 node2
= node2
->GetNext();
2362 node
= node
->GetNext();
2366 int lineCount
= GetLineCount();
2368 return GetLineForVisibleLineNumber(lineCount
-1);
2373 /// Get the number of visible lines
2374 int wxRichTextParagraphLayoutBox::GetLineCount() const
2378 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2381 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2382 // wxASSERT (child != NULL);
2385 count
+= child
->GetLines().GetCount();
2387 node
= node
->GetNext();
2393 /// Get the paragraph for a given line
2394 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphForLine(wxRichTextLine
* line
) const
2396 return GetParagraphAtPosition(line
->GetAbsoluteRange().GetStart());
2399 /// Get the line size at the given position
2400 wxSize
wxRichTextParagraphLayoutBox::GetLineSizeAtPosition(long pos
, bool caretPosition
) const
2402 wxRichTextLine
* line
= GetLineAtPosition(pos
, caretPosition
);
2405 return line
->GetSize();
2408 return wxSize(0, 0);
2412 /// Convenience function to add a paragraph of text
2413 wxRichTextRange
wxRichTextParagraphLayoutBox::AddParagraph(const wxString
& text
, wxRichTextAttr
* paraStyle
)
2415 // Don't use the base style, just the default style, and the base style will
2416 // be combined at display time.
2417 // Divide into paragraph and character styles.
2419 wxRichTextAttr defaultCharStyle
;
2420 wxRichTextAttr defaultParaStyle
;
2422 // If the default style is a named paragraph style, don't apply any character formatting
2423 // to the initial text string.
2424 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2426 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2428 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2431 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2433 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2434 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2436 wxRichTextParagraph
* para
= new wxRichTextParagraph(text
, this, pStyle
, cStyle
);
2437 para
->GetAttributes().GetTextBoxAttr().Reset();
2443 return para
->GetRange();
2446 /// Adds multiple paragraphs, based on newlines.
2447 wxRichTextRange
wxRichTextParagraphLayoutBox::AddParagraphs(const wxString
& text
, wxRichTextAttr
* paraStyle
)
2449 // Don't use the base style, just the default style, and the base style will
2450 // be combined at display time.
2451 // Divide into paragraph and character styles.
2453 wxRichTextAttr defaultCharStyle
;
2454 wxRichTextAttr defaultParaStyle
;
2456 // If the default style is a named paragraph style, don't apply any character formatting
2457 // to the initial text string.
2458 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2460 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2462 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2465 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2467 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2468 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2470 wxRichTextParagraph
* firstPara
= NULL
;
2471 wxRichTextParagraph
* lastPara
= NULL
;
2473 wxRichTextRange
range(-1, -1);
2476 size_t len
= text
.length();
2478 wxRichTextParagraph
* para
= new wxRichTextParagraph(wxEmptyString
, this, pStyle
, cStyle
);
2479 para
->GetAttributes().GetTextBoxAttr().Reset();
2488 wxChar ch
= text
[i
];
2489 if (ch
== wxT('\n') || ch
== wxT('\r'))
2493 wxRichTextPlainText
* plainText
= (wxRichTextPlainText
*) para
->GetChildren().GetFirst()->GetData();
2494 plainText
->SetText(line
);
2496 para
= new wxRichTextParagraph(wxEmptyString
, this, pStyle
, cStyle
);
2497 para
->GetAttributes().GetTextBoxAttr().Reset();
2502 line
= wxEmptyString
;
2513 wxRichTextPlainText
* plainText
= (wxRichTextPlainText
*) para
->GetChildren().GetFirst()->GetData();
2514 plainText
->SetText(line
);
2519 return wxRichTextRange(firstPara
->GetRange().GetStart(), lastPara
->GetRange().GetEnd());
2522 /// Convenience function to add an image
2523 wxRichTextRange
wxRichTextParagraphLayoutBox::AddImage(const wxImage
& image
, wxRichTextAttr
* paraStyle
)
2525 // Don't use the base style, just the default style, and the base style will
2526 // be combined at display time.
2527 // Divide into paragraph and character styles.
2529 wxRichTextAttr defaultCharStyle
;
2530 wxRichTextAttr defaultParaStyle
;
2532 // If the default style is a named paragraph style, don't apply any character formatting
2533 // to the initial text string.
2534 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2536 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2538 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2541 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2543 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2544 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2546 wxRichTextParagraph
* para
= new wxRichTextParagraph(this, pStyle
);
2547 para
->GetAttributes().GetTextBoxAttr().Reset();
2549 para
->AppendChild(new wxRichTextImage(image
, this, cStyle
));
2553 return para
->GetRange();
2557 /// Insert fragment into this box at the given position. If partialParagraph is true,
2558 /// it is assumed that the last (or only) paragraph is just a piece of data with no paragraph
2561 bool wxRichTextParagraphLayoutBox::InsertFragment(long position
, wxRichTextParagraphLayoutBox
& fragment
)
2563 // First, find the first paragraph whose starting position is within the range.
2564 wxRichTextParagraph
* para
= GetParagraphAtPosition(position
);
2567 wxRichTextAttr originalAttr
= para
->GetAttributes();
2569 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(para
);
2571 // Now split at this position, returning the object to insert the new
2572 // ones in front of.
2573 wxRichTextObject
* nextObject
= para
->SplitAt(position
);
2575 // Special case: partial paragraph, just one paragraph. Might be a small amount of
2576 // text, for example, so let's optimize.
2578 if (fragment
.GetPartialParagraph() && fragment
.GetChildren().GetCount() == 1)
2580 // Add the first para to this para...
2581 wxRichTextObjectList::compatibility_iterator firstParaNode
= fragment
.GetChildren().GetFirst();
2585 // Iterate through the fragment paragraph inserting the content into this paragraph.
2586 wxRichTextParagraph
* firstPara
= wxDynamicCast(firstParaNode
->GetData(), wxRichTextParagraph
);
2587 wxASSERT (firstPara
!= NULL
);
2589 wxRichTextObjectList::compatibility_iterator objectNode
= firstPara
->GetChildren().GetFirst();
2592 wxRichTextObject
* newObj
= objectNode
->GetData()->Clone();
2597 para
->AppendChild(newObj
);
2601 // Insert before nextObject
2602 para
->InsertChild(newObj
, nextObject
);
2605 objectNode
= objectNode
->GetNext();
2612 // Procedure for inserting a fragment consisting of a number of
2615 // 1. Remove and save the content that's after the insertion point, for adding
2616 // back once we've added the fragment.
2617 // 2. Add the content from the first fragment paragraph to the current
2619 // 3. Add remaining fragment paragraphs after the current paragraph.
2620 // 4. Add back the saved content from the first paragraph. If partialParagraph
2621 // is true, add it to the last paragraph added and not a new one.
2623 // 1. Remove and save objects after split point.
2624 wxList savedObjects
;
2626 para
->MoveToList(nextObject
, savedObjects
);
2628 // 2. Add the content from the 1st fragment paragraph.
2629 wxRichTextObjectList::compatibility_iterator firstParaNode
= fragment
.GetChildren().GetFirst();
2633 wxRichTextParagraph
* firstPara
= wxDynamicCast(firstParaNode
->GetData(), wxRichTextParagraph
);
2634 wxASSERT(firstPara
!= NULL
);
2636 if (!(fragment
.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
))
2637 para
->SetAttributes(firstPara
->GetAttributes());
2639 // Save empty paragraph attributes for appending later
2640 // These are character attributes deliberately set for a new paragraph. Without this,
2641 // we couldn't pass default attributes when appending a new paragraph.
2642 wxRichTextAttr emptyParagraphAttributes
;
2644 wxRichTextObjectList::compatibility_iterator objectNode
= firstPara
->GetChildren().GetFirst();
2646 if (objectNode
&& firstPara
->GetChildren().GetCount() == 1 && objectNode
->GetData()->IsEmpty())
2647 emptyParagraphAttributes
= objectNode
->GetData()->GetAttributes();
2651 wxRichTextObject
* newObj
= objectNode
->GetData()->Clone();
2654 para
->AppendChild(newObj
);
2656 objectNode
= objectNode
->GetNext();
2659 // 3. Add remaining fragment paragraphs after the current paragraph.
2660 wxRichTextObjectList::compatibility_iterator nextParagraphNode
= node
->GetNext();
2661 wxRichTextObject
* nextParagraph
= NULL
;
2662 if (nextParagraphNode
)
2663 nextParagraph
= nextParagraphNode
->GetData();
2665 wxRichTextObjectList::compatibility_iterator i
= fragment
.GetChildren().GetFirst()->GetNext();
2666 wxRichTextParagraph
* finalPara
= para
;
2668 bool needExtraPara
= (!i
|| !fragment
.GetPartialParagraph());
2670 // If there was only one paragraph, we need to insert a new one.
2673 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2674 wxASSERT( para
!= NULL
);
2676 finalPara
= (wxRichTextParagraph
*) para
->Clone();
2679 InsertChild(finalPara
, nextParagraph
);
2681 AppendChild(finalPara
);
2686 // If there was only one paragraph, or we have full paragraphs in our fragment,
2687 // we need to insert a new one.
2690 finalPara
= new wxRichTextParagraph
;
2693 InsertChild(finalPara
, nextParagraph
);
2695 AppendChild(finalPara
);
2698 // 4. Add back the remaining content.
2702 finalPara
->MoveFromList(savedObjects
);
2704 // Ensure there's at least one object
2705 if (finalPara
->GetChildCount() == 0)
2707 wxRichTextPlainText
* text
= new wxRichTextPlainText(wxEmptyString
);
2708 text
->SetAttributes(emptyParagraphAttributes
);
2710 finalPara
->AppendChild(text
);
2714 if ((fragment
.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
) && firstPara
)
2715 finalPara
->SetAttributes(firstPara
->GetAttributes());
2716 else if (finalPara
&& finalPara
!= para
)
2717 finalPara
->SetAttributes(originalAttr
);
2725 wxRichTextObjectList::compatibility_iterator i
= fragment
.GetChildren().GetFirst();
2728 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2729 wxASSERT( para
!= NULL
);
2731 AppendChild(para
->Clone());
2740 /// Make a copy of the fragment corresponding to the given range, putting it in 'fragment'.
2741 /// If there was an incomplete paragraph at the end, partialParagraph is set to true.
2742 bool wxRichTextParagraphLayoutBox::CopyFragment(const wxRichTextRange
& range
, wxRichTextParagraphLayoutBox
& fragment
)
2744 wxRichTextObjectList::compatibility_iterator i
= GetChildren().GetFirst();
2747 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2748 wxASSERT( para
!= NULL
);
2750 if (!para
->GetRange().IsOutside(range
))
2752 fragment
.AppendChild(para
->Clone());
2757 // Now top and tail the first and last paragraphs in our new fragment (which might be the same).
2758 if (!fragment
.IsEmpty())
2760 wxRichTextParagraph
* firstPara
= wxDynamicCast(fragment
.GetChildren().GetFirst()->GetData(), wxRichTextParagraph
);
2761 wxASSERT( firstPara
!= NULL
);
2763 wxRichTextParagraph
* lastPara
= wxDynamicCast(fragment
.GetChildren().GetLast()->GetData(), wxRichTextParagraph
);
2764 wxASSERT( lastPara
!= NULL
);
2766 if (!firstPara
|| !lastPara
)
2769 bool isFragment
= (range
.GetEnd() < lastPara
->GetRange().GetEnd());
2771 long firstPos
= firstPara
->GetRange().GetStart();
2773 // Adjust for renumbering from zero
2774 wxRichTextRange
topTailRange(range
.GetStart() - firstPos
, range
.GetEnd() - firstPos
);
2777 fragment
.CalculateRange(0, end
);
2779 // Chop off the start of the paragraph
2780 if (topTailRange
.GetStart() > 0)
2782 wxRichTextRange
r(0, topTailRange
.GetStart()-1);
2783 firstPara
->DeleteRange(r
);
2785 // Make sure the numbering is correct
2786 fragment
.CalculateRange(0, end
);
2788 // Now, we've deleted some positions, so adjust the range
2790 topTailRange
.SetStart(range
.GetLength());
2791 topTailRange
.SetEnd(fragment
.GetOwnRange().GetEnd());
2795 topTailRange
.SetStart(range
.GetLength());
2796 topTailRange
.SetEnd(fragment
.GetOwnRange().GetEnd());
2799 if (topTailRange
.GetStart() < lastPara
->GetRange().GetEnd())
2801 lastPara
->DeleteRange(topTailRange
);
2803 // Make sure the numbering is correct
2805 fragment
.CalculateRange(0, end
);
2807 // We only have part of a paragraph at the end
2808 fragment
.SetPartialParagraph(true);
2812 // We have a partial paragraph (don't save last new paragraph marker)
2813 // or complete paragraph
2814 fragment
.SetPartialParagraph(isFragment
);
2821 /// Given a position, get the number of the visible line (potentially many to a paragraph),
2822 /// starting from zero at the start of the buffer.
2823 long wxRichTextParagraphLayoutBox::GetVisibleLineNumber(long pos
, bool caretPosition
, bool startOfLine
) const
2830 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2833 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2834 // wxASSERT( child != NULL );
2838 if (child
->GetRange().Contains(pos
))
2840 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2843 wxRichTextLine
* line
= node2
->GetData();
2844 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
2846 if (lineRange
.Contains(pos
) || pos
== lineRange
.GetStart())
2848 // If the caret is displayed at the end of the previous wrapped line,
2849 // we want to return the line it's _displayed_ at (not the actual line
2850 // containing the position).
2851 if (lineRange
.GetStart() == pos
&& !startOfLine
&& child
->GetRange().GetStart() != pos
)
2852 return lineCount
- 1;
2859 node2
= node2
->GetNext();
2861 // If we didn't find it in the lines, it must be
2862 // the last position of the paragraph. So return the last line.
2866 lineCount
+= child
->GetLines().GetCount();
2869 node
= node
->GetNext();
2876 /// Given a line number, get the corresponding wxRichTextLine object.
2877 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineForVisibleLineNumber(long lineNumber
) const
2881 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2884 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2885 // wxASSERT(child != NULL);
2889 if (lineNumber
< (int) (child
->GetLines().GetCount() + lineCount
))
2891 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2894 wxRichTextLine
* line
= node2
->GetData();
2896 if (lineCount
== lineNumber
)
2901 node2
= node2
->GetNext();
2905 lineCount
+= child
->GetLines().GetCount();
2908 node
= node
->GetNext();
2915 /// Delete range from layout.
2916 bool wxRichTextParagraphLayoutBox::DeleteRange(const wxRichTextRange
& range
)
2918 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2920 wxRichTextParagraph
* firstPara
= NULL
;
2923 wxRichTextParagraph
* obj
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2924 // wxASSERT (obj != NULL);
2926 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
2930 // Delete the range in each paragraph
2932 if (!obj
->GetRange().IsOutside(range
))
2934 // Deletes the content of this object within the given range
2935 obj
->DeleteRange(range
);
2937 wxRichTextRange thisRange
= obj
->GetRange();
2938 wxRichTextAttr thisAttr
= obj
->GetAttributes();
2940 // If the whole paragraph is within the range to delete,
2941 // delete the whole thing.
2942 if (range
.GetStart() <= thisRange
.GetStart() && range
.GetEnd() >= thisRange
.GetEnd())
2944 // Delete the whole object
2945 RemoveChild(obj
, true);
2948 else if (!firstPara
)
2951 // If the range includes the paragraph end, we need to join this
2952 // and the next paragraph.
2953 if (range
.GetEnd() <= thisRange
.GetEnd())
2955 // We need to move the objects from the next paragraph
2956 // to this paragraph
2958 wxRichTextParagraph
* nextParagraph
= NULL
;
2959 if ((range
.GetEnd() < thisRange
.GetEnd()) && obj
)
2960 nextParagraph
= obj
;
2963 // We're ending at the end of the paragraph, so merge the _next_ paragraph.
2965 nextParagraph
= wxDynamicCast(next
->GetData(), wxRichTextParagraph
);
2968 bool applyFinalParagraphStyle
= firstPara
&& nextParagraph
&& nextParagraph
!= firstPara
;
2970 wxRichTextAttr nextParaAttr
;
2971 if (applyFinalParagraphStyle
)
2973 // Special case when deleting the end of a paragraph - use _this_ paragraph's style,
2974 // not the next one.
2975 if (range
.GetStart() == range
.GetEnd() && range
.GetStart() == thisRange
.GetEnd())
2976 nextParaAttr
= thisAttr
;
2978 nextParaAttr
= nextParagraph
->GetAttributes();
2981 if (firstPara
&& nextParagraph
&& firstPara
!= nextParagraph
)
2983 // Move the objects to the previous para
2984 wxRichTextObjectList::compatibility_iterator node1
= nextParagraph
->GetChildren().GetFirst();
2988 wxRichTextObject
* obj1
= node1
->GetData();
2990 firstPara
->AppendChild(obj1
);
2992 wxRichTextObjectList::compatibility_iterator next1
= node1
->GetNext();
2993 nextParagraph
->GetChildren().Erase(node1
);
2998 // Delete the paragraph
2999 RemoveChild(nextParagraph
, true);
3002 // Avoid empty paragraphs
3003 if (firstPara
&& firstPara
->GetChildren().GetCount() == 0)
3005 wxRichTextPlainText
* text
= new wxRichTextPlainText(wxEmptyString
);
3006 firstPara
->AppendChild(text
);
3009 if (applyFinalParagraphStyle
)
3010 firstPara
->SetAttributes(nextParaAttr
);
3023 /// Get any text in this object for the given range
3024 wxString
wxRichTextParagraphLayoutBox::GetTextForRange(const wxRichTextRange
& range
) const
3028 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3031 wxRichTextObject
* child
= node
->GetData();
3032 if (!child
->GetRange().IsOutside(range
))
3034 wxRichTextRange childRange
= range
;
3035 childRange
.LimitTo(child
->GetRange());
3037 wxString childText
= child
->GetTextForRange(childRange
);
3041 if ((childRange
.GetEnd() == child
->GetRange().GetEnd()) && node
->GetNext())
3046 node
= node
->GetNext();
3052 /// Get all the text
3053 wxString
wxRichTextParagraphLayoutBox::GetText() const
3055 return GetTextForRange(GetOwnRange());
3058 /// Get the paragraph by number
3059 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphAtLine(long paragraphNumber
) const
3061 if ((size_t) paragraphNumber
>= GetChildCount())
3064 return (wxRichTextParagraph
*) GetChild((size_t) paragraphNumber
);
3067 /// Get the length of the paragraph
3068 int wxRichTextParagraphLayoutBox::GetParagraphLength(long paragraphNumber
) const
3070 wxRichTextParagraph
* para
= GetParagraphAtLine(paragraphNumber
);
3072 return para
->GetRange().GetLength() - 1; // don't include newline
3077 /// Get the text of the paragraph
3078 wxString
wxRichTextParagraphLayoutBox::GetParagraphText(long paragraphNumber
) const
3080 wxRichTextParagraph
* para
= GetParagraphAtLine(paragraphNumber
);
3082 return para
->GetTextForRange(para
->GetRange());
3084 return wxEmptyString
;
3087 /// Convert zero-based line column and paragraph number to a position.
3088 long wxRichTextParagraphLayoutBox::XYToPosition(long x
, long y
) const
3090 wxRichTextParagraph
* para
= GetParagraphAtLine(y
);
3093 return para
->GetRange().GetStart() + x
;
3099 /// Convert zero-based position to line column and paragraph number
3100 bool wxRichTextParagraphLayoutBox::PositionToXY(long pos
, long* x
, long* y
) const
3102 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
);
3106 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3109 wxRichTextObject
* child
= node
->GetData();
3113 node
= node
->GetNext();
3117 *x
= pos
- para
->GetRange().GetStart();
3125 /// Get the leaf object in a paragraph at this position.
3126 /// Given a line number, get the corresponding wxRichTextLine object.
3127 wxRichTextObject
* wxRichTextParagraphLayoutBox::GetLeafObjectAtPosition(long position
) const
3129 wxRichTextParagraph
* para
= GetParagraphAtPosition(position
);
3132 wxRichTextObjectList::compatibility_iterator node
= para
->GetChildren().GetFirst();
3136 wxRichTextObject
* child
= node
->GetData();
3137 if (child
->GetRange().Contains(position
))
3140 node
= node
->GetNext();
3142 if (position
== para
->GetRange().GetEnd() && para
->GetChildCount() > 0)
3143 return para
->GetChildren().GetLast()->GetData();
3148 /// Set character or paragraph text attributes: apply character styles only to immediate text nodes
3149 bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange
& range
, const wxRichTextAttr
& style
, int flags
)
3151 bool characterStyle
= false;
3152 bool paragraphStyle
= false;
3154 if (style
.IsCharacterStyle())
3155 characterStyle
= true;
3156 if (style
.IsParagraphStyle())
3157 paragraphStyle
= true;
3159 wxRichTextBuffer
* buffer
= GetBuffer();
3161 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
3162 bool applyMinimal
= ((flags
& wxRICHTEXT_SETSTYLE_OPTIMIZE
) != 0);
3163 bool parasOnly
= ((flags
& wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY
) != 0);
3164 bool charactersOnly
= ((flags
& wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY
) != 0);
3165 bool resetExistingStyle
= ((flags
& wxRICHTEXT_SETSTYLE_RESET
) != 0);
3166 bool removeStyle
= ((flags
& wxRICHTEXT_SETSTYLE_REMOVE
) != 0);
3168 // Apply paragraph style first, if any
3169 wxRichTextAttr
wholeStyle(style
);
3171 if (!removeStyle
&& wholeStyle
.HasParagraphStyleName() && buffer
->GetStyleSheet())
3173 wxRichTextParagraphStyleDefinition
* def
= buffer
->GetStyleSheet()->FindParagraphStyle(wholeStyle
.GetParagraphStyleName());
3175 wxRichTextApplyStyle(wholeStyle
, def
->GetStyleMergedWithBase(buffer
->GetStyleSheet()));
3178 // Limit the attributes to be set to the content to only character attributes.
3179 wxRichTextAttr
characterAttributes(wholeStyle
);
3180 characterAttributes
.SetFlags(characterAttributes
.GetFlags() & (wxTEXT_ATTR_CHARACTER
));
3182 if (!removeStyle
&& characterAttributes
.HasCharacterStyleName() && buffer
->GetStyleSheet())
3184 wxRichTextCharacterStyleDefinition
* def
= buffer
->GetStyleSheet()->FindCharacterStyle(characterAttributes
.GetCharacterStyleName());
3186 wxRichTextApplyStyle(characterAttributes
, def
->GetStyleMergedWithBase(buffer
->GetStyleSheet()));
3189 // If we are associated with a control, make undoable; otherwise, apply immediately
3192 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3194 wxRichTextAction
* action
= NULL
;
3196 if (haveControl
&& withUndo
)
3198 action
= new wxRichTextAction(NULL
, _("Change Style"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
3199 action
->SetRange(range
);
3200 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3203 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3206 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3207 // wxASSERT (para != NULL);
3209 if (para
&& para
->GetChildCount() > 0)
3211 // Stop searching if we're beyond the range of interest
3212 if (para
->GetRange().GetStart() > range
.GetEnd())
3215 if (!para
->GetRange().IsOutside(range
))
3217 // We'll be using a copy of the paragraph to make style changes,
3218 // not updating the buffer directly.
3219 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3221 if (haveControl
&& withUndo
)
3223 newPara
= new wxRichTextParagraph(*para
);
3224 action
->GetNewParagraphs().AppendChild(newPara
);
3226 // Also store the old ones for Undo
3227 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3232 // If we're specifying paragraphs only, then we really mean character formatting
3233 // to be included in the paragraph style
3234 if ((paragraphStyle
|| parasOnly
) && !charactersOnly
)
3238 // Removes the given style from the paragraph
3239 wxRichTextRemoveStyle(newPara
->GetAttributes(), style
);
3241 else if (resetExistingStyle
)
3242 newPara
->GetAttributes() = wholeStyle
;
3247 // Only apply attributes that will make a difference to the combined
3248 // style as seen on the display
3249 wxRichTextAttr
combinedAttr(para
->GetCombinedAttributes(true));
3250 wxRichTextApplyStyle(newPara
->GetAttributes(), wholeStyle
, & combinedAttr
);
3253 wxRichTextApplyStyle(newPara
->GetAttributes(), wholeStyle
);
3257 // When applying paragraph styles dynamically, don't change the text objects' attributes
3258 // since they will computed as needed. Only apply the character styling if it's _only_
3259 // character styling. This policy is subject to change and might be put under user control.
3261 // Hm. we might well be applying a mix of paragraph and character styles, in which
3262 // case we _do_ want to apply character styles regardless of what para styles are set.
3263 // But if we're applying a paragraph style, which has some character attributes, but
3264 // we only want the paragraphs to hold this character style, then we _don't_ want to
3265 // apply the character style. So we need to be able to choose.
3267 if (!parasOnly
&& (characterStyle
|charactersOnly
) && range
.GetStart() != newPara
->GetRange().GetEnd())
3269 wxRichTextRange
childRange(range
);
3270 childRange
.LimitTo(newPara
->GetRange());
3272 // Find the starting position and if necessary split it so
3273 // we can start applying a different style.
3274 // TODO: check that the style actually changes or is different
3275 // from style outside of range
3276 wxRichTextObject
* firstObject
wxDUMMY_INITIALIZE(NULL
);
3277 wxRichTextObject
* lastObject
wxDUMMY_INITIALIZE(NULL
);
3279 if (childRange
.GetStart() == newPara
->GetRange().GetStart())
3280 firstObject
= newPara
->GetChildren().GetFirst()->GetData();
3282 firstObject
= newPara
->SplitAt(range
.GetStart());
3284 // Increment by 1 because we're apply the style one _after_ the split point
3285 long splitPoint
= childRange
.GetEnd();
3286 if (splitPoint
!= newPara
->GetRange().GetEnd())
3290 if (splitPoint
== newPara
->GetRange().GetEnd())
3291 lastObject
= newPara
->GetChildren().GetLast()->GetData();
3293 // lastObject is set as a side-effect of splitting. It's
3294 // returned as the object before the new object.
3295 (void) newPara
->SplitAt(splitPoint
, & lastObject
);
3297 wxASSERT(firstObject
!= NULL
);
3298 wxASSERT(lastObject
!= NULL
);
3300 if (!firstObject
|| !lastObject
)
3303 wxRichTextObjectList::compatibility_iterator firstNode
= newPara
->GetChildren().Find(firstObject
);
3304 wxRichTextObjectList::compatibility_iterator lastNode
= newPara
->GetChildren().Find(lastObject
);
3306 wxASSERT(firstNode
);
3309 wxRichTextObjectList::compatibility_iterator node2
= firstNode
;
3313 wxRichTextObject
* child
= node2
->GetData();
3317 // Removes the given style from the paragraph
3318 wxRichTextRemoveStyle(child
->GetAttributes(), style
);
3320 else if (resetExistingStyle
)
3321 child
->GetAttributes() = characterAttributes
;
3326 // Only apply attributes that will make a difference to the combined
3327 // style as seen on the display
3328 wxRichTextAttr
combinedAttr(newPara
->GetCombinedAttributes(child
->GetAttributes(), true));
3329 wxRichTextApplyStyle(child
->GetAttributes(), characterAttributes
, & combinedAttr
);
3332 wxRichTextApplyStyle(child
->GetAttributes(), characterAttributes
);
3335 if (node2
== lastNode
)
3338 node2
= node2
->GetNext();
3344 node
= node
->GetNext();
3347 // Do action, or delay it until end of batch.
3348 if (haveControl
&& withUndo
)
3349 buffer
->SubmitAction(action
);
3354 // Just change the attributes for this single object.
3355 void wxRichTextParagraphLayoutBox::SetStyle(wxRichTextObject
* obj
, const wxRichTextAttr
& textAttr
, int flags
)
3357 wxRichTextBuffer
* buffer
= GetBuffer();
3358 bool withUndo
= flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
;
3359 bool resetExistingStyle
= ((flags
& wxRICHTEXT_SETSTYLE_RESET
) != 0);
3360 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3362 wxRichTextAction
*action
= NULL
;
3363 wxRichTextAttr newAttr
= obj
->GetAttributes();
3364 if (resetExistingStyle
)
3367 newAttr
.Apply(textAttr
);
3369 if (haveControl
&& withUndo
)
3371 action
= new wxRichTextAction(NULL
, _("Change Object Style"), wxRICHTEXT_CHANGE_ATTRIBUTES
, buffer
, obj
->GetContainer(), buffer
->GetRichTextCtrl());
3372 action
->SetRange(obj
->GetRange().FromInternal());
3373 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3374 action
->MakeObject(obj
);
3376 action
->GetAttributes() = newAttr
;
3379 obj
->GetAttributes() = newAttr
;
3381 if (haveControl
&& withUndo
)
3382 buffer
->SubmitAction(action
);
3385 /// Get the text attributes for this position.
3386 bool wxRichTextParagraphLayoutBox::GetStyle(long position
, wxRichTextAttr
& style
)
3388 return DoGetStyle(position
, style
, true);
3391 bool wxRichTextParagraphLayoutBox::GetUncombinedStyle(long position
, wxRichTextAttr
& style
)
3393 return DoGetStyle(position
, style
, false);
3396 /// Implementation helper for GetStyle. If combineStyles is true, combine base, paragraph and
3397 /// context attributes.
3398 bool wxRichTextParagraphLayoutBox::DoGetStyle(long position
, wxRichTextAttr
& style
, bool combineStyles
)
3400 wxRichTextObject
* obj
wxDUMMY_INITIALIZE(NULL
);
3402 if (style
.IsParagraphStyle())
3404 obj
= GetParagraphAtPosition(position
);
3409 // Start with the base style
3410 style
= GetAttributes();
3411 style
.GetTextBoxAttr().Reset();
3413 // Apply the paragraph style
3414 wxRichTextApplyStyle(style
, obj
->GetAttributes());
3417 style
= obj
->GetAttributes();
3424 obj
= GetLeafObjectAtPosition(position
);
3429 wxRichTextParagraph
* para
= wxDynamicCast(obj
->GetParent(), wxRichTextParagraph
);
3430 style
= para
? para
->GetCombinedAttributes(obj
->GetAttributes()) : obj
->GetAttributes();
3433 style
= obj
->GetAttributes();
3441 static bool wxHasStyle(long flags
, long style
)
3443 return (flags
& style
) != 0;
3446 /// Combines 'style' with 'currentStyle' for the purpose of summarising the attributes of a range of
3448 bool wxRichTextParagraphLayoutBox::CollectStyle(wxRichTextAttr
& currentStyle
, const wxRichTextAttr
& style
, wxRichTextAttr
& clashingAttr
, wxRichTextAttr
& absentAttr
)
3450 currentStyle
.CollectCommonAttributes(style
, clashingAttr
, absentAttr
);
3455 /// Get the combined style for a range - if any attribute is different within the range,
3456 /// that attribute is not present within the flags.
3457 /// *** Note that this is not recursive, and so assumes that content inside a paragraph is not itself
3459 bool wxRichTextParagraphLayoutBox::GetStyleForRange(const wxRichTextRange
& range
, wxRichTextAttr
& style
)
3461 style
= wxRichTextAttr();
3463 wxRichTextAttr clashingAttrPara
, clashingAttrChar
;
3464 wxRichTextAttr absentAttrPara
, absentAttrChar
;
3466 wxRichTextObjectList::compatibility_iterator node
= GetChildren().GetFirst();
3469 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3470 if (para
&& !(para
->GetRange().GetStart() > range
.GetEnd() || para
->GetRange().GetEnd() < range
.GetStart()))
3472 if (para
->GetChildren().GetCount() == 0)
3474 wxRichTextAttr paraStyle
= para
->GetCombinedAttributes(true /* use box attributes */);
3476 CollectStyle(style
, paraStyle
, clashingAttrPara
, absentAttrPara
);
3480 wxRichTextRange
paraRange(para
->GetRange());
3481 paraRange
.LimitTo(range
);
3483 // First collect paragraph attributes only
3484 wxRichTextAttr paraStyle
= para
->GetCombinedAttributes();
3485 paraStyle
.SetFlags(paraStyle
.GetFlags() & wxTEXT_ATTR_PARAGRAPH
);
3486 CollectStyle(style
, paraStyle
, clashingAttrPara
, absentAttrPara
);
3488 wxRichTextObjectList::compatibility_iterator childNode
= para
->GetChildren().GetFirst();
3492 wxRichTextObject
* child
= childNode
->GetData();
3493 if (!(child
->GetRange().GetStart() > range
.GetEnd() || child
->GetRange().GetEnd() < range
.GetStart()))
3495 wxRichTextAttr childStyle
= para
->GetCombinedAttributes(child
->GetAttributes(), true /* include box attributes */);
3497 // Now collect character attributes only
3498 childStyle
.SetFlags(childStyle
.GetFlags() & wxTEXT_ATTR_CHARACTER
);
3500 CollectStyle(style
, childStyle
, clashingAttrChar
, absentAttrChar
);
3503 childNode
= childNode
->GetNext();
3507 node
= node
->GetNext();
3512 /// Set default style
3513 bool wxRichTextParagraphLayoutBox::SetDefaultStyle(const wxRichTextAttr
& style
)
3515 m_defaultAttributes
= style
;
3519 /// Test if this whole range has character attributes of the specified kind. If any
3520 /// of the attributes are different within the range, the test fails. You
3521 /// can use this to implement, for example, bold button updating. style must have
3522 /// flags indicating which attributes are of interest.
3523 bool wxRichTextParagraphLayoutBox::HasCharacterAttributes(const wxRichTextRange
& range
, const wxRichTextAttr
& style
) const
3526 int matchingCount
= 0;
3528 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3531 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3532 // wxASSERT (para != NULL);
3536 // Stop searching if we're beyond the range of interest
3537 if (para
->GetRange().GetStart() > range
.GetEnd())
3538 return foundCount
== matchingCount
&& foundCount
!= 0;
3540 if (!para
->GetRange().IsOutside(range
))
3542 wxRichTextObjectList::compatibility_iterator node2
= para
->GetChildren().GetFirst();
3546 wxRichTextObject
* child
= node2
->GetData();
3547 // Allow for empty string if no buffer
3548 wxRichTextRange childRange
= child
->GetRange();
3549 if (childRange
.GetLength() == 0 && GetRange().GetLength() == 1)
3550 childRange
.SetEnd(childRange
.GetEnd()+1);
3552 if (!childRange
.IsOutside(range
) && wxDynamicCast(child
, wxRichTextPlainText
))
3555 wxRichTextAttr textAttr
= para
->GetCombinedAttributes(child
->GetAttributes());
3557 if (textAttr
.EqPartial(style
, false /* strong test - attributes must be valid in both objects */))
3561 node2
= node2
->GetNext();
3566 node
= node
->GetNext();
3569 return foundCount
== matchingCount
&& foundCount
!= 0;
3572 /// Test if this whole range has paragraph attributes of the specified kind. If any
3573 /// of the attributes are different within the range, the test fails. You
3574 /// can use this to implement, for example, centering button updating. style must have
3575 /// flags indicating which attributes are of interest.
3576 bool wxRichTextParagraphLayoutBox::HasParagraphAttributes(const wxRichTextRange
& range
, const wxRichTextAttr
& style
) const
3579 int matchingCount
= 0;
3581 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3584 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3585 // wxASSERT (para != NULL);
3589 // Stop searching if we're beyond the range of interest
3590 if (para
->GetRange().GetStart() > range
.GetEnd())
3591 return foundCount
== matchingCount
&& foundCount
!= 0;
3593 if (!para
->GetRange().IsOutside(range
))
3595 wxRichTextAttr textAttr
= GetAttributes();
3596 // Apply the paragraph style
3597 wxRichTextApplyStyle(textAttr
, para
->GetAttributes());
3600 if (textAttr
.EqPartial(style
, false /* strong test */))
3605 node
= node
->GetNext();
3607 return foundCount
== matchingCount
&& foundCount
!= 0;
3610 void wxRichTextParagraphLayoutBox::PrepareContent(wxRichTextParagraphLayoutBox
& container
)
3612 wxRichTextBuffer
* buffer
= GetBuffer();
3613 if (buffer
&& buffer
->GetRichTextCtrl())
3614 buffer
->GetRichTextCtrl()->PrepareContent(container
);
3617 /// Set character or paragraph properties
3618 bool wxRichTextParagraphLayoutBox::SetProperties(const wxRichTextRange
& range
, const wxRichTextProperties
& properties
, int flags
)
3620 wxRichTextBuffer
* buffer
= GetBuffer();
3622 bool withUndo
= ((flags
& wxRICHTEXT_SETPROPERTIES_WITH_UNDO
) != 0);
3623 bool parasOnly
= ((flags
& wxRICHTEXT_SETPROPERTIES_PARAGRAPHS_ONLY
) != 0);
3624 bool charactersOnly
= ((flags
& wxRICHTEXT_SETPROPERTIES_CHARACTERS_ONLY
) != 0);
3625 bool resetExistingProperties
= ((flags
& wxRICHTEXT_SETPROPERTIES_RESET
) != 0);
3626 bool removeProperties
= ((flags
& wxRICHTEXT_SETPROPERTIES_REMOVE
) != 0);
3628 // If we are associated with a control, make undoable; otherwise, apply immediately
3631 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3633 wxRichTextAction
* action
= NULL
;
3635 if (haveControl
&& withUndo
)
3637 action
= new wxRichTextAction(NULL
, _("Change Properties"), wxRICHTEXT_CHANGE_PROPERTIES
, buffer
, this, buffer
->GetRichTextCtrl());
3638 action
->SetRange(range
);
3639 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3642 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3645 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3646 // wxASSERT (para != NULL);
3648 if (para
&& para
->GetChildCount() > 0)
3650 // Stop searching if we're beyond the range of interest
3651 if (para
->GetRange().GetStart() > range
.GetEnd())
3654 if (!para
->GetRange().IsOutside(range
))
3656 // We'll be using a copy of the paragraph to make style changes,
3657 // not updating the buffer directly.
3658 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3660 if (haveControl
&& withUndo
)
3662 newPara
= new wxRichTextParagraph(*para
);
3663 action
->GetNewParagraphs().AppendChild(newPara
);
3665 // Also store the old ones for Undo
3666 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3673 if (removeProperties
)
3675 // Removes the given style from the paragraph
3677 newPara
->GetProperties().RemoveProperties(properties
);
3679 else if (resetExistingProperties
)
3680 newPara
->GetProperties() = properties
;
3682 newPara
->GetProperties().MergeProperties(properties
);
3685 // When applying paragraph styles dynamically, don't change the text objects' attributes
3686 // since they will computed as needed. Only apply the character styling if it's _only_
3687 // character styling. This policy is subject to change and might be put under user control.
3689 // Hm. we might well be applying a mix of paragraph and character styles, in which
3690 // case we _do_ want to apply character styles regardless of what para styles are set.
3691 // But if we're applying a paragraph style, which has some character attributes, but
3692 // we only want the paragraphs to hold this character style, then we _don't_ want to
3693 // apply the character style. So we need to be able to choose.
3695 if (!parasOnly
&& charactersOnly
&& range
.GetStart() != newPara
->GetRange().GetEnd())
3697 wxRichTextRange
childRange(range
);
3698 childRange
.LimitTo(newPara
->GetRange());
3700 // Find the starting position and if necessary split it so
3701 // we can start applying different properties.
3702 // TODO: check that the properties actually change or are different
3703 // from properties outside of range
3704 wxRichTextObject
* firstObject
wxDUMMY_INITIALIZE(NULL
);
3705 wxRichTextObject
* lastObject
wxDUMMY_INITIALIZE(NULL
);
3707 if (childRange
.GetStart() == newPara
->GetRange().GetStart())
3708 firstObject
= newPara
->GetChildren().GetFirst()->GetData();
3710 firstObject
= newPara
->SplitAt(range
.GetStart());
3712 // Increment by 1 because we're apply the style one _after_ the split point
3713 long splitPoint
= childRange
.GetEnd();
3714 if (splitPoint
!= newPara
->GetRange().GetEnd())
3718 if (splitPoint
== newPara
->GetRange().GetEnd())
3719 lastObject
= newPara
->GetChildren().GetLast()->GetData();
3721 // lastObject is set as a side-effect of splitting. It's
3722 // returned as the object before the new object.
3723 (void) newPara
->SplitAt(splitPoint
, & lastObject
);
3725 wxASSERT(firstObject
!= NULL
);
3726 wxASSERT(lastObject
!= NULL
);
3728 if (!firstObject
|| !lastObject
)
3731 wxRichTextObjectList::compatibility_iterator firstNode
= newPara
->GetChildren().Find(firstObject
);
3732 wxRichTextObjectList::compatibility_iterator lastNode
= newPara
->GetChildren().Find(lastObject
);
3734 wxASSERT(firstNode
);
3737 wxRichTextObjectList::compatibility_iterator node2
= firstNode
;
3741 wxRichTextObject
* child
= node2
->GetData();
3743 if (removeProperties
)
3745 // Removes the given properties from the paragraph
3746 child
->GetProperties().RemoveProperties(properties
);
3748 else if (resetExistingProperties
)
3749 child
->GetProperties() = properties
;
3752 child
->GetProperties().MergeProperties(properties
);
3755 if (node2
== lastNode
)
3758 node2
= node2
->GetNext();
3764 node
= node
->GetNext();
3767 // Do action, or delay it until end of batch.
3768 if (haveControl
&& withUndo
)
3769 buffer
->SubmitAction(action
);
3774 void wxRichTextParagraphLayoutBox::Reset()
3778 wxRichTextBuffer
* buffer
= GetBuffer();
3779 if (buffer
&& buffer
->GetRichTextCtrl())
3781 wxRichTextEvent
event(wxEVT_COMMAND_RICHTEXT_BUFFER_RESET
, buffer
->GetRichTextCtrl()->GetId());
3782 event
.SetEventObject(buffer
->GetRichTextCtrl());
3783 event
.SetContainer(this);
3785 buffer
->SendEvent(event
, true);
3788 AddParagraph(wxEmptyString
);
3790 PrepareContent(*this);
3792 InvalidateHierarchy(wxRICHTEXT_ALL
);
3795 /// Invalidate the buffer. With no argument, invalidates whole buffer.
3796 void wxRichTextParagraphLayoutBox::Invalidate(const wxRichTextRange
& invalidRange
)
3798 wxRichTextCompositeObject::Invalidate(invalidRange
);
3800 DoInvalidate(invalidRange
);
3803 // Do the (in)validation for this object only
3804 void wxRichTextParagraphLayoutBox::DoInvalidate(const wxRichTextRange
& invalidRange
)
3806 if (invalidRange
== wxRICHTEXT_ALL
)
3808 m_invalidRange
= wxRICHTEXT_ALL
;
3810 // Already invalidating everything
3811 else if (m_invalidRange
== wxRICHTEXT_ALL
)
3816 if ((invalidRange
.GetStart() < m_invalidRange
.GetStart()) || m_invalidRange
.GetStart() == -1)
3817 m_invalidRange
.SetStart(invalidRange
.GetStart());
3818 if (invalidRange
.GetEnd() > m_invalidRange
.GetEnd())
3819 m_invalidRange
.SetEnd(invalidRange
.GetEnd());
3823 // Do the (in)validation both up and down the hierarchy
3824 void wxRichTextParagraphLayoutBox::InvalidateHierarchy(const wxRichTextRange
& invalidRange
)
3826 Invalidate(invalidRange
);
3828 if (invalidRange
!= wxRICHTEXT_NONE
)
3830 // Now go up the hierarchy
3831 wxRichTextObject
* thisObj
= this;
3832 wxRichTextObject
* p
= GetParent();
3835 wxRichTextParagraphLayoutBox
* l
= wxDynamicCast(p
, wxRichTextParagraphLayoutBox
);
3837 l
->DoInvalidate(thisObj
->GetRange());
3845 /// Get invalid range, rounding to entire paragraphs if argument is true.
3846 wxRichTextRange
wxRichTextParagraphLayoutBox::GetInvalidRange(bool wholeParagraphs
) const
3848 if (m_invalidRange
== wxRICHTEXT_ALL
|| m_invalidRange
== wxRICHTEXT_NONE
)
3849 return m_invalidRange
;
3851 wxRichTextRange range
= m_invalidRange
;
3853 if (wholeParagraphs
)
3855 wxRichTextParagraph
* para1
= GetParagraphAtPosition(range
.GetStart());
3857 range
.SetStart(para1
->GetRange().GetStart());
3859 // FIXME: be more intelligent about this. Check if we have floating objects
3860 // before the end of the range. But it's not clear how we can in general
3861 // tell where it's safe to stop laying out.
3862 // Anyway, this code is central to efficiency when laying in floating mode.
3863 if (!wxRichTextBuffer::GetFloatingLayoutMode())
3865 wxRichTextParagraph
* para2
= GetParagraphAtPosition(range
.GetEnd());
3867 range
.SetEnd(para2
->GetRange().GetEnd());
3870 // Floating layout means that all children should be laid out,
3871 // because we can't tell how the whole buffer will be affected.
3872 range
.SetEnd(GetOwnRange().GetEnd());
3877 /// Apply the style sheet to the buffer, for example if the styles have changed.
3878 bool wxRichTextParagraphLayoutBox::ApplyStyleSheet(wxRichTextStyleSheet
* styleSheet
)
3880 wxASSERT(styleSheet
!= NULL
);
3886 wxRichTextAttr
attr(GetBasicStyle());
3887 if (GetBasicStyle().HasParagraphStyleName())
3889 wxRichTextParagraphStyleDefinition
* paraDef
= styleSheet
->FindParagraphStyle(GetBasicStyle().GetParagraphStyleName());
3892 attr
.Apply(paraDef
->GetStyleMergedWithBase(styleSheet
));
3893 SetBasicStyle(attr
);
3898 if (GetBasicStyle().HasCharacterStyleName())
3900 wxRichTextCharacterStyleDefinition
* charDef
= styleSheet
->FindCharacterStyle(GetBasicStyle().GetCharacterStyleName());
3903 attr
.Apply(charDef
->GetStyleMergedWithBase(styleSheet
));
3904 SetBasicStyle(attr
);
3909 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3912 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3913 // wxASSERT (para != NULL);
3917 // Combine paragraph and list styles. If there is a list style in the original attributes,
3918 // the current indentation overrides anything else and is used to find the item indentation.
3919 // Also, for applying paragraph styles, consider having 2 modes: (1) we merge with what we have,
3920 // thereby taking into account all user changes, (2) reset the style completely (except for indentation/list
3921 // exception as above).
3922 // Problem: when changing from one list style to another, there's a danger that the level info will get lost.
3923 // So when changing a list style interactively, could retrieve level based on current style, then
3924 // set appropriate indent and apply new style.
3928 if (para
->GetAttributes().HasOutlineLevel())
3929 outline
= para
->GetAttributes().GetOutlineLevel();
3930 if (para
->GetAttributes().HasBulletNumber())
3931 num
= para
->GetAttributes().GetBulletNumber();
3933 if (!para
->GetAttributes().GetParagraphStyleName().IsEmpty() && !para
->GetAttributes().GetListStyleName().IsEmpty())
3935 int currentIndent
= para
->GetAttributes().GetLeftIndent();
3937 wxRichTextParagraphStyleDefinition
* paraDef
= styleSheet
->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
3938 wxRichTextListStyleDefinition
* listDef
= styleSheet
->FindListStyle(para
->GetAttributes().GetListStyleName());
3939 if (paraDef
&& !listDef
)
3941 para
->GetAttributes() = paraDef
->GetStyleMergedWithBase(styleSheet
);
3944 else if (listDef
&& !paraDef
)
3946 // Set overall style defined for the list style definition
3947 para
->GetAttributes() = listDef
->GetStyleMergedWithBase(styleSheet
);
3949 // Apply the style for this level
3950 wxRichTextApplyStyle(para
->GetAttributes(), * listDef
->GetLevelAttributes(listDef
->FindLevelForIndent(currentIndent
)));
3953 else if (listDef
&& paraDef
)
3955 // Combines overall list style, style for level, and paragraph style
3956 para
->GetAttributes() = listDef
->CombineWithParagraphStyle(currentIndent
, paraDef
->GetStyleMergedWithBase(styleSheet
));
3960 else if (para
->GetAttributes().GetParagraphStyleName().IsEmpty() && !para
->GetAttributes().GetListStyleName().IsEmpty())
3962 int currentIndent
= para
->GetAttributes().GetLeftIndent();
3964 wxRichTextListStyleDefinition
* listDef
= styleSheet
->FindListStyle(para
->GetAttributes().GetListStyleName());
3966 // Overall list definition style
3967 para
->GetAttributes() = listDef
->GetStyleMergedWithBase(styleSheet
);
3969 // Style for this level
3970 wxRichTextApplyStyle(para
->GetAttributes(), * listDef
->GetLevelAttributes(listDef
->FindLevelForIndent(currentIndent
)));
3974 else if (!para
->GetAttributes().GetParagraphStyleName().IsEmpty() && para
->GetAttributes().GetListStyleName().IsEmpty())
3976 wxRichTextParagraphStyleDefinition
* def
= styleSheet
->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
3979 para
->GetAttributes() = def
->GetStyleMergedWithBase(styleSheet
);
3985 para
->GetAttributes().SetOutlineLevel(outline
);
3987 para
->GetAttributes().SetBulletNumber(num
);
3990 node
= node
->GetNext();
3992 return foundCount
!= 0;
3996 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
3998 wxRichTextBuffer
* buffer
= GetBuffer();
3999 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
4001 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
4002 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
4003 bool specifyLevel
= ((flags
& wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL
) != 0);
4004 bool renumber
= ((flags
& wxRICHTEXT_SETSTYLE_RENUMBER
) != 0);
4006 // Current number, if numbering
4009 wxASSERT (!specifyLevel
|| (specifyLevel
&& (specifiedLevel
>= 0)));
4011 // If we are associated with a control, make undoable; otherwise, apply immediately
4014 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
4016 wxRichTextAction
* action
= NULL
;
4018 if (haveControl
&& withUndo
)
4020 action
= new wxRichTextAction(NULL
, _("Change List Style"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
4021 action
->SetRange(range
);
4022 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
4025 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
4028 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
4029 // wxASSERT (para != NULL);
4031 if (para
&& para
->GetChildCount() > 0)
4033 // Stop searching if we're beyond the range of interest
4034 if (para
->GetRange().GetStart() > range
.GetEnd())
4037 if (!para
->GetRange().IsOutside(range
))
4039 // We'll be using a copy of the paragraph to make style changes,
4040 // not updating the buffer directly.
4041 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
4043 if (haveControl
&& withUndo
)
4045 newPara
= new wxRichTextParagraph(*para
);
4046 action
->GetNewParagraphs().AppendChild(newPara
);
4048 // Also store the old ones for Undo
4049 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
4056 int thisIndent
= newPara
->GetAttributes().GetLeftIndent();
4057 int thisLevel
= specifyLevel
? specifiedLevel
: def
->FindLevelForIndent(thisIndent
);
4059 // How is numbering going to work?
4060 // If we are renumbering, or numbering for the first time, we need to keep
4061 // track of the number for each level. But we might be simply applying a different
4063 // In Word, applying a style to several paragraphs, even if at different levels,
4064 // reverts the level back to the same one. So we could do the same here.
4065 // Renumbering will need to be done when we promote/demote a paragraph.
4067 // Apply the overall list style, and item style for this level
4068 wxRichTextAttr
listStyle(def
->GetCombinedStyleForLevel(thisLevel
, styleSheet
));
4069 wxRichTextApplyStyle(newPara
->GetAttributes(), listStyle
);
4071 // Now we need to do numbering
4072 // Preserve the existing list item continuation bullet style, if any
4073 if (para
->GetAttributes().HasBulletStyle() && (para
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
))
4074 newPara
->GetAttributes().SetBulletStyle(newPara
->GetAttributes().GetBulletStyle()|wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
);
4079 newPara
->GetAttributes().SetBulletNumber(n
);
4085 else if (!newPara
->GetAttributes().GetListStyleName().IsEmpty())
4087 // if def is NULL, remove list style, applying any associated paragraph style
4088 // to restore the attributes
4090 newPara
->GetAttributes().SetListStyleName(wxEmptyString
);
4091 newPara
->GetAttributes().SetLeftIndent(0, 0);
4092 newPara
->GetAttributes().SetBulletText(wxEmptyString
);
4093 newPara
->GetAttributes().SetBulletStyle(0);
4095 // Eliminate the main list-related attributes
4096 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
);
4098 if (styleSheet
&& !newPara
->GetAttributes().GetParagraphStyleName().IsEmpty())
4100 wxRichTextParagraphStyleDefinition
* def
= styleSheet
->FindParagraphStyle(newPara
->GetAttributes().GetParagraphStyleName());
4103 newPara
->GetAttributes() = def
->GetStyleMergedWithBase(styleSheet
);
4110 node
= node
->GetNext();
4113 // Do action, or delay it until end of batch.
4114 if (haveControl
&& withUndo
)
4115 buffer
->SubmitAction(action
);
4120 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
)
4122 wxRichTextBuffer
* buffer
= GetBuffer();
4123 if (buffer
&& buffer
->GetStyleSheet())
4125 wxRichTextListStyleDefinition
* def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4127 return SetListStyle(range
, def
, flags
, startFrom
, specifiedLevel
);
4132 /// Clear list for given range
4133 bool wxRichTextParagraphLayoutBox::ClearListStyle(const wxRichTextRange
& range
, int flags
)
4135 return SetListStyle(range
, NULL
, flags
);
4138 /// Number/renumber any list elements in the given range
4139 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
4141 return DoNumberList(range
, range
, 0, def
, flags
, startFrom
, specifiedLevel
);
4144 /// Number/renumber any list elements in the given range. Also do promotion or demotion of items, if specified
4145 bool wxRichTextParagraphLayoutBox::DoNumberList(const wxRichTextRange
& range
, const wxRichTextRange
& promotionRange
, int promoteBy
,
4146 wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
4148 wxRichTextBuffer
* buffer
= GetBuffer();
4149 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
4151 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
4152 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
4154 bool specifyLevel
= ((flags
& wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL
) != 0);
4157 bool renumber
= ((flags
& wxRICHTEXT_SETSTYLE_RENUMBER
) != 0);
4159 // Max number of levels
4160 const int maxLevels
= 10;
4162 // The level we're looking at now
4163 int currentLevel
= -1;
4165 // The item number for each level
4166 int levels
[maxLevels
];
4169 // Reset all numbering
4170 for (i
= 0; i
< maxLevels
; i
++)
4172 if (startFrom
!= -1)
4173 levels
[i
] = startFrom
-1;
4174 else if (renumber
) // start again
4177 levels
[i
] = -1; // start from the number we found, if any
4181 wxASSERT(!specifyLevel
|| (specifyLevel
&& (specifiedLevel
>= 0)));
4184 // If we are associated with a control, make undoable; otherwise, apply immediately
4187 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
4189 wxRichTextAction
* action
= NULL
;
4191 if (haveControl
&& withUndo
)
4193 action
= new wxRichTextAction(NULL
, _("Renumber List"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
4194 action
->SetRange(range
);
4195 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
4198 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
4201 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
4202 // wxASSERT (para != NULL);
4204 if (para
&& para
->GetChildCount() > 0)
4206 // Stop searching if we're beyond the range of interest
4207 if (para
->GetRange().GetStart() > range
.GetEnd())
4210 if (!para
->GetRange().IsOutside(range
))
4212 // We'll be using a copy of the paragraph to make style changes,
4213 // not updating the buffer directly.
4214 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
4216 if (haveControl
&& withUndo
)
4218 newPara
= new wxRichTextParagraph(*para
);
4219 action
->GetNewParagraphs().AppendChild(newPara
);
4221 // Also store the old ones for Undo
4222 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
4227 wxRichTextListStyleDefinition
* defToUse
= def
;
4230 if (styleSheet
&& !newPara
->GetAttributes().GetListStyleName().IsEmpty())
4231 defToUse
= styleSheet
->FindListStyle(newPara
->GetAttributes().GetListStyleName());
4236 int thisIndent
= newPara
->GetAttributes().GetLeftIndent();
4237 int thisLevel
= defToUse
->FindLevelForIndent(thisIndent
);
4239 // If we've specified a level to apply to all, change the level.
4240 if (specifiedLevel
!= -1)
4241 thisLevel
= specifiedLevel
;
4243 // Do promotion if specified
4244 if ((promoteBy
!= 0) && !para
->GetRange().IsOutside(promotionRange
))
4246 thisLevel
= thisLevel
- promoteBy
;
4253 // Apply the overall list style, and item style for this level
4254 wxRichTextAttr
listStyle(defToUse
->GetCombinedStyleForLevel(thisLevel
, styleSheet
));
4255 wxRichTextApplyStyle(newPara
->GetAttributes(), listStyle
);
4257 // Preserve the existing list item continuation bullet style, if any
4258 if (para
->GetAttributes().HasBulletStyle() && (para
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
))
4259 newPara
->GetAttributes().SetBulletStyle(newPara
->GetAttributes().GetBulletStyle()|wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
);
4261 // OK, we've (re)applied the style, now let's get the numbering right.
4263 if (currentLevel
== -1)
4264 currentLevel
= thisLevel
;
4266 // Same level as before, do nothing except increment level's number afterwards
4267 if (currentLevel
== thisLevel
)
4270 // A deeper level: start renumbering all levels after current level
4271 else if (thisLevel
> currentLevel
)
4273 for (i
= currentLevel
+1; i
<= thisLevel
; i
++)
4277 currentLevel
= thisLevel
;
4279 else if (thisLevel
< currentLevel
)
4281 currentLevel
= thisLevel
;
4284 // Use the current numbering if -1 and we have a bullet number already
4285 if (levels
[currentLevel
] == -1)
4287 if (newPara
->GetAttributes().HasBulletNumber())
4288 levels
[currentLevel
] = newPara
->GetAttributes().GetBulletNumber();
4290 levels
[currentLevel
] = 1;
4294 if (!(para
->GetAttributes().HasBulletStyle() && (para
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
)))
4295 levels
[currentLevel
] ++;
4298 newPara
->GetAttributes().SetBulletNumber(levels
[currentLevel
]);
4300 // Create the bullet text if an outline list
4301 if (listStyle
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
)
4304 for (i
= 0; i
<= currentLevel
; i
++)
4306 if (!text
.IsEmpty())
4308 text
+= wxString::Format(wxT("%d"), levels
[i
]);
4310 newPara
->GetAttributes().SetBulletText(text
);
4316 node
= node
->GetNext();
4319 // Do action, or delay it until end of batch.
4320 if (haveControl
&& withUndo
)
4321 buffer
->SubmitAction(action
);
4326 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
)
4328 wxRichTextBuffer
* buffer
= GetBuffer();
4329 if (buffer
->GetStyleSheet())
4331 wxRichTextListStyleDefinition
* def
= NULL
;
4332 if (!defName
.IsEmpty())
4333 def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4334 return NumberList(range
, def
, flags
, startFrom
, specifiedLevel
);
4339 /// Promote the list items within the given range. promoteBy can be a positive or negative number, e.g. 1 or -1
4340 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy
, const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int specifiedLevel
)
4343 // One strategy is to first work out the range within which renumbering must occur. Then could pass these two ranges
4344 // to NumberList with a flag indicating promotion is required within one of the ranges.
4345 // Find first and last paragraphs in range. Then for first, calculate new indentation and look back until we find
4346 // a paragraph that either has no list style, or has one that is different or whose indentation is less.
4347 // We start renumbering from the para after that different para we found. We specify that the numbering of that
4348 // list position will start from 1.
4349 // Similarly, we look after the last para in the promote range for an indentation that is less (or no list style).
4350 // We can end the renumbering at this point.
4352 // For now, only renumber within the promotion range.
4354 return DoNumberList(range
, range
, promoteBy
, def
, flags
, 1, specifiedLevel
);
4357 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy
, const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int specifiedLevel
)
4359 wxRichTextBuffer
* buffer
= GetBuffer();
4360 if (buffer
->GetStyleSheet())
4362 wxRichTextListStyleDefinition
* def
= NULL
;
4363 if (!defName
.IsEmpty())
4364 def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4365 return PromoteList(promoteBy
, range
, def
, flags
, specifiedLevel
);
4370 /// Fills in the attributes for numbering a paragraph after previousParagraph. It also finds the
4371 /// position of the paragraph that it had to start looking from.
4372 bool wxRichTextParagraphLayoutBox::FindNextParagraphNumber(wxRichTextParagraph
* previousParagraph
, wxRichTextAttr
& attr
) const
4374 // TODO: add GetNextChild/GetPreviousChild to composite
4375 // Search for a paragraph that isn't a continuation paragraph (no bullet)
4376 while (previousParagraph
&& previousParagraph
->GetAttributes().HasBulletStyle() && previousParagraph
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
)
4378 wxRichTextObjectList::compatibility_iterator node
= ((wxRichTextCompositeObject
*) previousParagraph
->GetParent())->GetChildren().Find(previousParagraph
);
4381 node
= node
->GetPrevious();
4383 previousParagraph
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
4385 previousParagraph
= NULL
;
4388 previousParagraph
= NULL
;
4391 if (!previousParagraph
|| !previousParagraph
->GetAttributes().HasFlag(wxTEXT_ATTR_BULLET_STYLE
) || previousParagraph
->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
)
4394 wxRichTextBuffer
* buffer
= GetBuffer();
4395 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
4396 if (styleSheet
&& !previousParagraph
->GetAttributes().GetListStyleName().IsEmpty())
4398 wxRichTextListStyleDefinition
* def
= styleSheet
->FindListStyle(previousParagraph
->GetAttributes().GetListStyleName());
4401 // int thisIndent = previousParagraph->GetAttributes().GetLeftIndent();
4402 // int thisLevel = def->FindLevelForIndent(thisIndent);
4404 bool isOutline
= (previousParagraph
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
) != 0;
4406 attr
.SetFlags(previousParagraph
->GetAttributes().GetFlags() & (wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_BULLET_NUMBER
|wxTEXT_ATTR_BULLET_TEXT
|wxTEXT_ATTR_BULLET_NAME
));
4407 if (previousParagraph
->GetAttributes().HasBulletName())
4408 attr
.SetBulletName(previousParagraph
->GetAttributes().GetBulletName());
4409 attr
.SetBulletStyle(previousParagraph
->GetAttributes().GetBulletStyle());
4410 attr
.SetListStyleName(previousParagraph
->GetAttributes().GetListStyleName());
4412 int nextNumber
= previousParagraph
->GetAttributes().GetBulletNumber() + 1;
4413 attr
.SetBulletNumber(nextNumber
);
4417 wxString text
= previousParagraph
->GetAttributes().GetBulletText();
4418 if (!text
.IsEmpty())
4420 int pos
= text
.Find(wxT('.'), true);
4421 if (pos
!= wxNOT_FOUND
)
4423 text
= text
.Mid(0, text
.Length() - pos
- 1);
4426 text
= wxEmptyString
;
4427 if (!text
.IsEmpty())
4429 text
+= wxString::Format(wxT("%d"), nextNumber
);
4430 attr
.SetBulletText(text
);
4444 * wxRichTextParagraph
4445 * This object represents a single paragraph (or in a straight text editor, a line).
4448 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraph
, wxRichTextCompositeObject
)
4450 wxArrayInt
wxRichTextParagraph::sm_defaultTabs
;
4452 wxRichTextParagraph::wxRichTextParagraph(wxRichTextObject
* parent
, wxRichTextAttr
* style
):
4453 wxRichTextCompositeObject(parent
)
4456 SetAttributes(*style
);
4459 wxRichTextParagraph::wxRichTextParagraph(const wxString
& text
, wxRichTextObject
* parent
, wxRichTextAttr
* paraStyle
, wxRichTextAttr
* charStyle
):
4460 wxRichTextCompositeObject(parent
)
4463 SetAttributes(*paraStyle
);
4465 AppendChild(new wxRichTextPlainText(text
, this, charStyle
));
4468 wxRichTextParagraph::~wxRichTextParagraph()
4474 bool wxRichTextParagraph::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int WXUNUSED(descent
), int style
)
4479 // Currently we don't merge these attributes with the parent, but we
4480 // should consider whether we should (e.g. if we set a border colour
4481 // for all paragraphs). But generally box attributes are likely to be
4482 // different for different objects.
4483 wxRect paraRect
= GetRect();
4484 wxRichTextAttr attr
= GetCombinedAttributes();
4485 context
.ApplyVirtualAttributes(attr
, this);
4487 DrawBoxAttributes(dc
, GetBuffer(), attr
, paraRect
);
4489 // Draw the bullet, if any
4490 if ((attr
.GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
) == 0 && (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
) == 0)
4492 if (attr
.GetLeftSubIndent() != 0)
4494 int spaceBeforePara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingBefore());
4495 int leftIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftIndent());
4497 wxRichTextAttr
bulletAttr(attr
);
4499 // Combine with the font of the first piece of content, if one is specified
4500 if (GetChildren().GetCount() > 0)
4502 wxRichTextObject
* firstObj
= (wxRichTextObject
*) GetChildren().GetFirst()->GetData();
4503 if (!firstObj
->IsFloatable() && firstObj
->GetAttributes().HasFont())
4505 wxRichTextApplyStyle(bulletAttr
, firstObj
->GetAttributes());
4509 // Get line height from first line, if any
4510 wxRichTextLine
* line
= m_cachedLines
.GetFirst() ? (wxRichTextLine
* ) m_cachedLines
.GetFirst()->GetData() : NULL
;
4513 int lineHeight
wxDUMMY_INITIALIZE(0);
4516 lineHeight
= line
->GetSize().y
;
4517 linePos
= line
->GetPosition() + GetPosition();
4522 if (bulletAttr
.HasFont() && GetBuffer())
4523 font
= GetBuffer()->GetFontTable().FindFont(bulletAttr
);
4525 font
= (*wxNORMAL_FONT
);
4527 wxCheckSetFont(dc
, font
);
4529 lineHeight
= dc
.GetCharHeight();
4530 linePos
= GetPosition();
4531 linePos
.y
+= spaceBeforePara
;
4534 wxRect
bulletRect(GetPosition().x
+ leftIndent
, linePos
.y
, linePos
.x
- (GetPosition().x
+ leftIndent
), lineHeight
);
4536 if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
)
4538 if (wxRichTextBuffer::GetRenderer())
4539 wxRichTextBuffer::GetRenderer()->DrawBitmapBullet(this, dc
, bulletAttr
, bulletRect
);
4541 else if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_STANDARD
)
4543 if (wxRichTextBuffer::GetRenderer())
4544 wxRichTextBuffer::GetRenderer()->DrawStandardBullet(this, dc
, bulletAttr
, bulletRect
);
4548 wxString bulletText
= GetBulletText();
4550 if (!bulletText
.empty() && wxRichTextBuffer::GetRenderer())
4551 wxRichTextBuffer::GetRenderer()->DrawTextBullet(this, dc
, bulletAttr
, bulletRect
, bulletText
);
4556 // Draw the range for each line, one object at a time.
4558 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
4561 wxRichTextLine
* line
= node
->GetData();
4562 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
4564 // Lines are specified relative to the paragraph
4566 wxPoint linePosition
= line
->GetPosition() + GetPosition();
4568 // Don't draw if off the screen
4569 if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) != 0) || ((linePosition
.y
+ line
->GetSize().y
) >= rect
.y
&& linePosition
.y
<= rect
.y
+ rect
.height
))
4571 wxPoint objectPosition
= linePosition
;
4572 int maxDescent
= line
->GetDescent();
4574 // Loop through objects until we get to the one within range
4575 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
4580 wxRichTextObject
* child
= node2
->GetData();
4582 if ((!child
->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && child
->GetRange().GetLength() > 0 && !child
->GetRange().IsOutside(lineRange
) && !lineRange
.IsOutside(range
))
4584 // Draw this part of the line at the correct position
4585 wxRichTextRange
objectRange(child
->GetRange());
4586 objectRange
.LimitTo(lineRange
);
4589 if (child
->IsTopLevel())
4591 objectSize
= child
->GetCachedSize();
4592 objectRange
= child
->GetOwnRange();
4596 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING && wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4597 if (i
< (int) line
->GetObjectSizes().GetCount())
4599 objectSize
.x
= line
->GetObjectSizes()[(size_t) i
];
4605 child
->GetRangeSize(objectRange
, objectSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, objectPosition
);
4609 // Use the child object's width, but the whole line's height
4610 wxRect
childRect(objectPosition
, wxSize(objectSize
.x
, line
->GetSize().y
));
4611 child
->Draw(dc
, context
, objectRange
, selection
, childRect
, maxDescent
, style
);
4613 objectPosition
.x
+= objectSize
.x
;
4616 else if (child
->GetRange().GetStart() > lineRange
.GetEnd())
4617 // Can break out of inner loop now since we've passed this line's range
4620 node2
= node2
->GetNext();
4624 node
= node
->GetNext();
4630 // Get the range width using partial extents calculated for the whole paragraph.
4631 static int wxRichTextGetRangeWidth(const wxRichTextParagraph
& para
, const wxRichTextRange
& range
, const wxArrayInt
& partialExtents
)
4633 wxASSERT(partialExtents
.GetCount() >= (size_t) range
.GetLength());
4635 if (partialExtents
.GetCount() < (size_t) range
.GetLength())
4638 int leftMostPos
= 0;
4639 if (range
.GetStart() - para
.GetRange().GetStart() > 0)
4640 leftMostPos
= partialExtents
[range
.GetStart() - para
.GetRange().GetStart() - 1];
4642 int rightMostPos
= partialExtents
[range
.GetEnd() - para
.GetRange().GetStart()];
4644 int w
= rightMostPos
- leftMostPos
;
4649 /// Lay the item out
4650 bool wxRichTextParagraph::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
)
4652 // Deal with floating objects firstly before the normal layout
4653 wxRichTextBuffer
* buffer
= GetBuffer();
4656 wxRichTextFloatCollector
* collector
= GetContainer()->GetFloatCollector();
4658 if (wxRichTextBuffer::GetFloatingLayoutMode())
4660 wxASSERT(collector
!= NULL
);
4662 LayoutFloat(dc
, context
, rect
, parentRect
, style
, collector
);
4665 wxRichTextAttr attr
= GetCombinedAttributes();
4666 context
.ApplyVirtualAttributes(attr
, this);
4670 // Increase the size of the paragraph due to spacing
4671 int spaceBeforePara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingBefore());
4672 int spaceAfterPara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingAfter());
4673 int leftIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftIndent());
4674 int leftSubIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftSubIndent());
4675 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
4677 int lineSpacing
= 0;
4679 // Let's assume line spacing of 10 is normal, 15 is 1.5, 20 is 2, etc.
4680 if (attr
.HasLineSpacing() && attr
.GetLineSpacing() > 0 && attr
.HasFont())
4682 wxFont
font(buffer
->GetFontTable().FindFont(attr
));
4685 wxCheckSetFont(dc
, font
);
4686 lineSpacing
= (int) (double(dc
.GetCharHeight()) * (double(attr
.GetLineSpacing())/10.0 - 1.0));
4690 // Start position for each line relative to the paragraph
4691 int startPositionFirstLine
= leftIndent
;
4692 int startPositionSubsequentLines
= leftIndent
+ leftSubIndent
;
4694 // If we have a bullet in this paragraph, the start position for the first line's text
4695 // is actually leftIndent + leftSubIndent.
4696 if (attr
.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE
)
4697 startPositionFirstLine
= startPositionSubsequentLines
;
4699 long lastEndPos
= GetRange().GetStart()-1;
4700 long lastCompletedEndPos
= lastEndPos
;
4702 int currentWidth
= 0;
4703 SetPosition(rect
.GetPosition());
4705 wxPoint
currentPosition(0, spaceBeforePara
); // We will calculate lines relative to paragraph
4708 int maxHeight
= currentPosition
.y
;
4713 int lineDescent
= 0;
4715 wxRichTextObjectList::compatibility_iterator node
;
4717 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4719 wxArrayInt partialExtents
;
4722 int paraDescent
= 0;
4724 // This calculates the partial text extents
4725 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, rect
.GetPosition(), parentRect
.GetSize(), & partialExtents
);
4727 node
= m_children
.GetFirst();
4730 wxRichTextObject
* child
= node
->GetData();
4732 //child->SetCachedSize(wxDefaultSize);
4733 child
->Layout(dc
, context
, rect
, style
);
4735 node
= node
->GetNext();
4741 // We may need to go back to a previous child, in which case create the new line,
4742 // find the child corresponding to the start position of the string, and
4745 wxRect availableRect
;
4747 node
= m_children
.GetFirst();
4750 wxRichTextObject
* child
= node
->GetData();
4752 // If floating, ignore. We already laid out floats.
4753 // Also ignore if empty object, except if we haven't got any
4755 if ((child
->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode())
4756 || !child
->IsShown() || (child
->GetRange().GetLength() == 0 && maxHeight
> spaceBeforePara
)
4759 node
= node
->GetNext();
4763 // If this is e.g. a composite text box, it will need to be laid out itself.
4764 // But if just a text fragment or image, for example, this will
4765 // do nothing. NB: won't we need to set the position after layout?
4766 // since for example if position is dependent on vertical line size, we
4767 // can't tell the position until the size is determined. So possibly introduce
4768 // another layout phase.
4770 // We may only be looking at part of a child, if we searched back for wrapping
4771 // and found a suitable point some way into the child. So get the size for the fragment
4774 long nextBreakPos
= GetFirstLineBreakPosition(lastEndPos
+1);
4775 long lastPosToUse
= child
->GetRange().GetEnd();
4776 bool lineBreakInThisObject
= (nextBreakPos
> -1 && nextBreakPos
<= child
->GetRange().GetEnd());
4778 if (lineBreakInThisObject
)
4779 lastPosToUse
= nextBreakPos
;
4782 int childDescent
= 0;
4784 int startOffset
= (lineCount
== 0 ? startPositionFirstLine
: startPositionSubsequentLines
);
4785 availableRect
= wxRect(rect
.x
+ startOffset
, rect
.y
+ currentPosition
.y
,
4786 rect
.width
- startOffset
- rightIndent
, rect
.height
);
4788 if (child
->IsTopLevel())
4790 wxSize oldSize
= child
->GetCachedSize();
4792 child
->Invalidate(wxRICHTEXT_ALL
);
4793 child
->SetPosition(wxPoint(0, 0));
4795 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4796 // lays out the object again using the minimum size
4797 // The position will be determined by its location in its line,
4798 // and not by the child's actual position.
4799 child
->LayoutToBestSize(dc
, context
, buffer
,
4800 attr
, child
->GetAttributes(), availableRect
, parentRect
, style
);
4802 if (oldSize
!= child
->GetCachedSize())
4804 partialExtents
.Clear();
4806 // Recalculate the partial text extents since the child object changed size
4807 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), parentRect
.GetSize(), & partialExtents
);
4811 // Problem: we need to layout composites here for which we need the available width,
4812 // but we can't get the available width without using the float collector which
4813 // needs to know the object height.
4815 if ((nextBreakPos
== -1) && (lastEndPos
== child
->GetRange().GetStart() - 1)) // i.e. we want to get the whole thing
4817 childSize
= child
->GetCachedSize();
4818 childDescent
= child
->GetDescent();
4822 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4823 // Get height only, then the width using the partial extents
4824 GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
, wxPoint(0,0), parentRect
.GetSize());
4825 childSize
.x
= wxRichTextGetRangeWidth(*this, wxRichTextRange(lastEndPos
+1, lastPosToUse
), partialExtents
);
4827 GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, rect
.GetPosition(), parentRect
.GetSize());
4832 int loopIterations
= 0;
4834 // If there are nested objects that need to lay themselves out, we have to do this in a
4835 // loop because the height of the object may well depend on the available width.
4836 // And because of floating object positioning, the available width depends on the
4837 // height of the object and whether it will clash with the floating objects.
4838 // So, we see whether the available width changes due to the presence of floating images.
4839 // If it does, then we'll use the new restricted width to find the object height again.
4840 // If this causes another restriction in the available width, we'll try again, until
4841 // either we lose patience or the available width settles down.
4846 wxRect oldAvailableRect
= availableRect
;
4848 // Available width depends on the floating objects and the line height.
4849 // Note: the floating objects may be placed vertically along the two sides of
4850 // buffer, so we may have different available line widths with different
4851 // [startY, endY]. So, we can't determine how wide the available
4852 // space is until we know the exact line height.
4853 if (childDescent
== 0)
4855 lineHeight
= wxMax(lineHeight
, childSize
.y
);
4856 lineDescent
= maxDescent
;
4857 lineAscent
= maxAscent
;
4861 lineDescent
= wxMax(childDescent
, maxDescent
);
4862 lineAscent
= wxMax(childSize
.y
-childDescent
, maxAscent
);
4864 lineHeight
= wxMax(lineHeight
, (lineDescent
+ lineAscent
));
4866 if (wxRichTextBuffer::GetFloatingLayoutMode() && collector
)
4868 wxRect floatAvailableRect
= collector
->GetAvailableRect(rect
.y
+ currentPosition
.y
, rect
.y
+ currentPosition
.y
+ lineHeight
);
4870 // Adjust availableRect to the space that is available when taking floating objects into account.
4872 if (floatAvailableRect
.x
+ startOffset
> availableRect
.x
)
4874 int newX
= floatAvailableRect
.x
+ startOffset
;
4875 int newW
= availableRect
.width
- (newX
- availableRect
.x
);
4876 availableRect
.x
= newX
;
4877 availableRect
.width
= newW
;
4880 if (floatAvailableRect
.width
< availableRect
.width
)
4881 availableRect
.width
= floatAvailableRect
.width
;
4884 currentPosition
.x
= availableRect
.x
- rect
.x
;
4886 if (child
->IsTopLevel() && loopIterations
<= 20)
4888 if (availableRect
!= oldAvailableRect
)
4890 wxSize oldSize
= child
->GetCachedSize();
4892 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4893 // lays out the object again using the minimum size
4894 child
->Invalidate(wxRICHTEXT_ALL
);
4895 child
->LayoutToBestSize(dc
, context
, buffer
,
4896 attr
, child
->GetAttributes(), availableRect
, parentRect
.GetSize(), style
);
4897 childSize
= child
->GetCachedSize();
4898 childDescent
= child
->GetDescent();
4900 if (oldSize
!= child
->GetCachedSize())
4902 partialExtents
.Clear();
4904 // Recalculate the partial text extents since the child object changed size
4905 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), parentRect
.GetSize(), & partialExtents
);
4908 // Go around the loop finding the available rect for the given floating objects
4918 if (child
->IsTopLevel())
4920 // We can move it to the correct position at this point
4921 // TODO: probably need to add margin
4922 child
->Move(GetPosition() + wxPoint(currentWidth
+ (wxMax(leftIndent
, leftIndent
+ leftSubIndent
)), currentPosition
.y
));
4926 // 1) There was a line break BEFORE the natural break
4927 // 2) There was a line break AFTER the natural break
4928 // 3) It's the last line
4929 // 4) The child still fits (carry on) - 'else' clause
4931 if ((lineBreakInThisObject
&& (childSize
.x
+ currentWidth
<= availableRect
.width
))
4933 (childSize
.x
+ currentWidth
> availableRect
.width
)
4936 ((childSize
.x
+ currentWidth
<= availableRect
.width
) && !node
->GetNext())
4940 long wrapPosition
= 0;
4941 if ((childSize
.x
+ currentWidth
<= availableRect
.width
) && !node
->GetNext() && !lineBreakInThisObject
)
4942 wrapPosition
= child
->GetRange().GetEnd();
4945 // Find a place to wrap. This may walk back to previous children,
4946 // for example if a word spans several objects.
4947 // Note: one object must contains only one wxTextAtrr, so the line height will not
4948 // change inside one object. Thus, we can pass the remain line width to the
4949 // FindWrapPosition function.
4950 if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos
+1, child
->GetRange().GetEnd()), dc
, context
, availableRect
.width
, wrapPosition
, & partialExtents
))
4952 // If the function failed, just cut it off at the end of this child.
4953 wrapPosition
= child
->GetRange().GetEnd();
4956 // FindWrapPosition can still return a value that will put us in an endless wrapping loop
4957 if (wrapPosition
<= lastCompletedEndPos
)
4958 wrapPosition
= wxMax(lastCompletedEndPos
+1,child
->GetRange().GetEnd());
4960 // Line end position shouldn't be the same as the end, or greater.
4961 if (wrapPosition
>= GetRange().GetEnd())
4962 wrapPosition
= wxMax(0, GetRange().GetEnd()-1);
4964 // wxLogDebug(wxT("Split at %ld"), wrapPosition);
4966 // Let's find the actual size of the current line now
4968 wxRichTextRange
actualRange(lastCompletedEndPos
+1, wrapPosition
);
4972 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4973 if (!child
->IsEmpty())
4975 // Get height only, then the width using the partial extents
4976 GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
, wxPoint(0,0), parentRect
.GetSize());
4977 actualSize
.x
= wxRichTextGetRangeWidth(*this, actualRange
, partialExtents
);
4981 GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, wxPoint(0,0), parentRect
.GetSize());
4983 currentWidth
= actualSize
.x
;
4985 // The descent for the whole line at this point, is the correct max descent
4986 maxDescent
= childDescent
;
4988 maxAscent
= actualSize
.y
-childDescent
;
4990 // lineHeight is given by the height for the whole line, since it will
4991 // take into account ascend/descend.
4992 lineHeight
= actualSize
.y
;
4994 if (lineHeight
== 0 && buffer
)
4996 wxFont
font(buffer
->GetFontTable().FindFont(attr
));
4997 wxCheckSetFont(dc
, font
);
4998 lineHeight
= dc
.GetCharHeight();
5001 if (maxDescent
== 0)
5004 dc
.GetTextExtent(wxT("X"), & w
, &h
, & maxDescent
);
5008 wxRichTextLine
* line
= AllocateLine(lineCount
);
5010 // Set relative range so we won't have to change line ranges when paragraphs are moved
5011 line
->SetRange(wxRichTextRange(actualRange
.GetStart() - GetRange().GetStart(), actualRange
.GetEnd() - GetRange().GetStart()));
5012 line
->SetPosition(currentPosition
);
5013 line
->SetSize(wxSize(currentWidth
, lineHeight
));
5014 line
->SetDescent(maxDescent
);
5016 maxHeight
= currentPosition
.y
+ lineHeight
;
5018 // Now move down a line. TODO: add margins, spacing
5019 currentPosition
.y
+= lineHeight
;
5020 currentPosition
.y
+= lineSpacing
;
5023 maxWidth
= wxMax(maxWidth
, currentWidth
+startOffset
);
5028 // TODO: account for zero-length objects
5029 // wxASSERT(wrapPosition > lastCompletedEndPos);
5031 lastEndPos
= wrapPosition
;
5032 lastCompletedEndPos
= lastEndPos
;
5036 if (wrapPosition
< GetRange().GetEnd()-1)
5038 // May need to set the node back to a previous one, due to searching back in wrapping
5039 wxRichTextObject
* childAfterWrapPosition
= FindObjectAtPosition(wrapPosition
+1);
5040 if (childAfterWrapPosition
)
5041 node
= m_children
.Find(childAfterWrapPosition
);
5043 node
= node
->GetNext();
5046 node
= node
->GetNext();
5048 // Apply paragraph styles such as alignment to the wrapped line
5049 ApplyParagraphStyle(line
, attr
, availableRect
, dc
);
5053 // We still fit, so don't add a line, and keep going
5054 currentWidth
+= childSize
.x
;
5056 if (childDescent
== 0)
5058 // An object with a zero descend value wants to take up the whole
5059 // height regardless of baseline
5060 lineHeight
= wxMax(lineHeight
, childSize
.y
);
5064 maxDescent
= wxMax(childDescent
, maxDescent
);
5065 maxAscent
= wxMax(childSize
.y
-childDescent
, maxAscent
);
5068 lineHeight
= wxMax(lineHeight
, (maxDescent
+ maxAscent
));
5070 maxWidth
= wxMax(maxWidth
, currentWidth
+startOffset
);
5071 lastEndPos
= child
->GetRange().GetEnd();
5073 node
= node
->GetNext();
5077 //wxASSERT(!(lastCompletedEndPos != -1 && lastCompletedEndPos < GetRange().GetEnd()-1));
5079 // Add the last line - it's the current pos -> last para pos
5080 // Substract -1 because the last position is always the end-paragraph position.
5081 if (lastCompletedEndPos
<= GetRange().GetEnd()-1)
5083 currentPosition
.x
= (lineCount
== 0 ? startPositionFirstLine
: startPositionSubsequentLines
);
5085 wxRichTextLine
* line
= AllocateLine(lineCount
);
5087 wxRichTextRange
actualRange(lastCompletedEndPos
+1, GetRange().GetEnd()-1);
5089 // Set relative range so we won't have to change line ranges when paragraphs are moved
5090 line
->SetRange(wxRichTextRange(actualRange
.GetStart() - GetRange().GetStart(), actualRange
.GetEnd() - GetRange().GetStart()));
5092 line
->SetPosition(currentPosition
);
5094 if (lineHeight
== 0 && buffer
)
5096 wxFont
font(buffer
->GetFontTable().FindFont(attr
));
5097 wxCheckSetFont(dc
, font
);
5098 lineHeight
= dc
.GetCharHeight();
5101 if (maxDescent
== 0)
5104 dc
.GetTextExtent(wxT("X"), & w
, &h
, & maxDescent
);
5107 line
->SetSize(wxSize(currentWidth
, lineHeight
));
5108 line
->SetDescent(maxDescent
);
5109 currentPosition
.y
+= lineHeight
;
5110 currentPosition
.y
+= lineSpacing
;
5113 // Apply paragraph styles such as alignment to the wrapped line
5114 ApplyParagraphStyle(line
, attr
, availableRect
, dc
);
5117 // Remove remaining unused line objects, if any
5118 ClearUnusedLines(lineCount
);
5120 // We need to add back the margins etc.
5122 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
5123 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxWidth
, currentPosition
.y
+ spaceAfterPara
));
5124 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
5125 SetCachedSize(marginRect
.GetSize());
5128 // The maximum size is the length of the paragraph stretched out into a line.
5129 // So if there were a single word, or an image, or a fixed-size text box, the object could be shrunk around
5130 // this size. TODO: take into account line breaks.
5132 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
5133 contentRect
= wxRect(wxPoint(0, 0), wxSize(paraSize
.x
+ wxMax(leftIndent
, leftIndent
+ leftSubIndent
) + rightIndent
, currentPosition
.y
+ spaceAfterPara
));
5134 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
5135 SetMaxSize(marginRect
.GetSize());
5138 // Find the greatest minimum size. Currently we only look at non-text objects,
5139 // which isn't ideal but it would be slow to find the maximum word width to
5140 // use as the minimum.
5143 node
= m_children
.GetFirst();
5146 wxRichTextObject
* child
= node
->GetData();
5148 // If floating, ignore. We already laid out floats.
5149 // Also ignore if empty object, except if we haven't got any
5151 if ((!child
->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && child
->GetRange().GetLength() != 0 && !wxDynamicCast(child
, wxRichTextPlainText
))
5153 if (child
->GetCachedSize().x
> minWidth
)
5154 minWidth
= child
->GetMinSize().x
;
5156 node
= node
->GetNext();
5159 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
5160 contentRect
= wxRect(wxPoint(0, 0), wxSize(minWidth
, currentPosition
.y
+ spaceAfterPara
));
5161 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
5162 SetMinSize(marginRect
.GetSize());
5165 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5166 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
5167 // Use the text extents to calculate the size of each fragment in each line
5168 wxRichTextLineList::compatibility_iterator lineNode
= m_cachedLines
.GetFirst();
5171 wxRichTextLine
* line
= lineNode
->GetData();
5172 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5174 // Loop through objects until we get to the one within range
5175 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
5179 wxRichTextObject
* child
= node2
->GetData();
5181 if (child
->GetRange().GetLength() > 0 && !child
->GetRange().IsOutside(lineRange
))
5183 wxRichTextRange rangeToUse
= lineRange
;
5184 rangeToUse
.LimitTo(child
->GetRange());
5186 // Find the size of the child from the text extents, and store in an array
5187 // for drawing later
5189 if (rangeToUse
.GetStart() > GetRange().GetStart())
5190 left
= partialExtents
[(rangeToUse
.GetStart()-1) - GetRange().GetStart()];
5191 int right
= partialExtents
[rangeToUse
.GetEnd() - GetRange().GetStart()];
5192 int sz
= right
- left
;
5193 line
->GetObjectSizes().Add(sz
);
5195 else if (child
->GetRange().GetStart() > lineRange
.GetEnd())
5196 // Can break out of inner loop now since we've passed this line's range
5199 node2
= node2
->GetNext();
5202 lineNode
= lineNode
->GetNext();
5210 /// Apply paragraph styles, such as centering, to wrapped lines
5211 /// TODO: take into account box attributes, possibly
5212 void wxRichTextParagraph::ApplyParagraphStyle(wxRichTextLine
* line
, const wxRichTextAttr
& attr
, const wxRect
& rect
, wxDC
& dc
)
5214 if (!attr
.HasAlignment())
5217 wxPoint pos
= line
->GetPosition();
5218 wxPoint originalPos
= pos
;
5219 wxSize size
= line
->GetSize();
5221 // centering, right-justification
5222 if (attr
.HasAlignment() && attr
.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE
)
5224 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
5225 pos
.x
= (rect
.GetWidth() - rightIndent
- size
.x
)/2 + pos
.x
;
5226 line
->SetPosition(pos
);
5228 else if (attr
.HasAlignment() && attr
.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT
)
5230 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
5231 pos
.x
= pos
.x
+ rect
.GetWidth() - size
.x
- rightIndent
;
5232 line
->SetPosition(pos
);
5235 if (pos
!= originalPos
)
5237 wxPoint inc
= pos
- originalPos
;
5239 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5243 wxRichTextObject
* child
= node
->GetData();
5244 if (child
->IsTopLevel() && !child
->GetRange().IsOutside(line
->GetAbsoluteRange()))
5245 child
->Move(child
->GetPosition() + inc
);
5247 node
= node
->GetNext();
5252 /// Insert text at the given position
5253 bool wxRichTextParagraph::InsertText(long pos
, const wxString
& text
)
5255 wxRichTextObject
* childToUse
= NULL
;
5256 wxRichTextObjectList::compatibility_iterator nodeToUse
= wxRichTextObjectList::compatibility_iterator();
5258 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5261 wxRichTextObject
* child
= node
->GetData();
5262 if (child
->GetRange().Contains(pos
) && child
->GetRange().GetLength() > 0)
5269 node
= node
->GetNext();
5274 wxRichTextPlainText
* textObject
= wxDynamicCast(childToUse
, wxRichTextPlainText
);
5277 int posInString
= pos
- textObject
->GetRange().GetStart();
5279 wxString newText
= textObject
->GetText().Mid(0, posInString
) +
5280 text
+ textObject
->GetText().Mid(posInString
);
5281 textObject
->SetText(newText
);
5283 int textLength
= text
.length();
5285 textObject
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart(),
5286 textObject
->GetRange().GetEnd() + textLength
));
5288 // Increment the end range of subsequent fragments in this paragraph.
5289 // We'll set the paragraph range itself at a higher level.
5291 wxRichTextObjectList::compatibility_iterator node
= nodeToUse
->GetNext();
5294 wxRichTextObject
* child
= node
->GetData();
5295 child
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart() + textLength
,
5296 textObject
->GetRange().GetEnd() + textLength
));
5298 node
= node
->GetNext();
5305 // TODO: if not a text object, insert at closest position, e.g. in front of it
5311 // Don't pass parent initially to suppress auto-setting of parent range.
5312 // We'll do that at a higher level.
5313 wxRichTextPlainText
* textObject
= new wxRichTextPlainText(text
, this);
5315 AppendChild(textObject
);
5322 void wxRichTextParagraph::Copy(const wxRichTextParagraph
& obj
)
5324 wxRichTextCompositeObject::Copy(obj
);
5327 /// Clear the cached lines
5328 void wxRichTextParagraph::ClearLines()
5330 WX_CLEAR_LIST(wxRichTextLineList
, m_cachedLines
);
5333 /// Get/set the object size for the given range. Returns false if the range
5334 /// is invalid for this object.
5335 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
5337 if (!range
.IsWithin(GetRange()))
5340 if (flags
& wxRICHTEXT_UNFORMATTED
)
5342 // Just use unformatted data, assume no line breaks
5345 wxArrayInt childExtents
;
5354 int maxLineHeight
= 0;
5356 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5359 wxRichTextObject
* child
= node
->GetData();
5360 if (!child
->GetRange().IsOutside(range
))
5362 // Floating objects have a zero size within the paragraph.
5363 if (child
->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode())
5368 if (partialExtents
->GetCount() > 0)
5369 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5373 partialExtents
->Add(0 /* zero size */ + lastSize
);
5380 wxRichTextRange rangeToUse
= range
;
5381 rangeToUse
.LimitTo(child
->GetRange());
5382 int childDescent
= 0;
5384 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we've already cached the size,
5385 // but it's only going to be used after caching has taken place.
5386 if ((flags
& wxRICHTEXT_HEIGHT_ONLY
) && child
->GetCachedSize().y
!= 0)
5388 childDescent
= child
->GetDescent();
5389 childSize
= child
->GetCachedSize();
5391 if (childDescent
== 0)
5393 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5397 maxDescent
= wxMax(maxDescent
, childDescent
);
5398 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5401 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5403 sz
.y
= wxMax(sz
.y
, maxLineHeight
);
5404 sz
.x
+= childSize
.x
;
5405 descent
= maxDescent
;
5407 else if (child
->IsTopLevel())
5409 childDescent
= child
->GetDescent();
5410 childSize
= child
->GetCachedSize();
5412 if (childDescent
== 0)
5414 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5418 maxDescent
= wxMax(maxDescent
, childDescent
);
5419 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5422 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5424 sz
.y
= wxMax(sz
.y
, maxLineHeight
);
5425 sz
.x
+= childSize
.x
;
5426 descent
= maxDescent
;
5428 // FIXME: this won't change the original values.
5429 // Should we be calling GetRangeSize above instead of using cached values?
5431 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange()))
5433 child
->SetCachedSize(childSize
);
5434 child
->SetDescent(childDescent
);
5441 if (partialExtents
->GetCount() > 0)
5442 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5446 partialExtents
->Add(childSize
.x
+ lastSize
);
5449 else if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, context
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
), parentSize
, p
))
5451 if (childDescent
== 0)
5453 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5457 maxDescent
= wxMax(maxDescent
, childDescent
);
5458 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5461 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5463 sz
.y
= wxMax(sz
.y
, maxLineHeight
);
5464 sz
.x
+= childSize
.x
;
5465 descent
= maxDescent
;
5467 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange()))
5469 child
->SetCachedSize(childSize
);
5470 child
->SetDescent(childDescent
);
5476 if (partialExtents
->GetCount() > 0)
5477 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5482 for (i
= 0; i
< childExtents
.GetCount(); i
++)
5484 partialExtents
->Add(childExtents
[i
] + lastSize
);
5494 node
= node
->GetNext();
5500 // Use formatted data, with line breaks
5503 // We're going to loop through each line, and then for each line,
5504 // call GetRangeSize for the fragment that comprises that line.
5505 // Only we have to do that multiple times within the line, because
5506 // the line may be broken into pieces. For now ignore line break commands
5507 // (so we can assume that getting the unformatted size for a fragment
5508 // within a line is the actual size)
5510 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5513 wxRichTextLine
* line
= node
->GetData();
5514 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5515 if (!lineRange
.IsOutside(range
))
5519 int maxLineHeight
= 0;
5520 int maxLineWidth
= 0;
5522 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
5525 wxRichTextObject
* child
= node2
->GetData();
5527 if ((!child
->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && !child
->GetRange().IsOutside(lineRange
))
5529 wxRichTextRange rangeToUse
= lineRange
;
5530 rangeToUse
.LimitTo(child
->GetRange());
5531 if (child
->IsTopLevel())
5532 rangeToUse
= child
->GetOwnRange();
5535 int childDescent
= 0;
5536 if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, context
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
), parentSize
))
5538 if (childDescent
== 0)
5540 // Assume that if descent is zero, this child can occupy the full line height
5541 // and does not need space for the line's maximum descent. So we influence
5542 // the overall max line height only.
5543 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5547 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5548 maxDescent
= wxMax(maxAscent
, childDescent
);
5550 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5551 maxLineWidth
+= childSize
.x
;
5555 node2
= node2
->GetNext();
5558 descent
= wxMax(descent
, maxDescent
);
5560 // Increase size by a line (TODO: paragraph spacing)
5561 sz
.y
+= maxLineHeight
;
5562 sz
.x
= wxMax(sz
.x
, maxLineWidth
);
5564 node
= node
->GetNext();
5571 /// Finds the absolute position and row height for the given character position
5572 bool wxRichTextParagraph::FindPosition(wxDC
& dc
, wxRichTextDrawingContext
& context
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
5576 wxRichTextLine
* line
= ((wxRichTextParagraphLayoutBox
*)GetParent())->GetLineAtPosition(0);
5578 *height
= line
->GetSize().y
;
5580 *height
= dc
.GetCharHeight();
5582 // -1 means 'the start of the buffer'.
5585 pt
= pt
+ line
->GetPosition();
5590 // The final position in a paragraph is taken to mean the position
5591 // at the start of the next paragraph.
5592 if (index
== GetRange().GetEnd())
5594 wxRichTextParagraphLayoutBox
* parent
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
5595 wxASSERT( parent
!= NULL
);
5597 // Find the height at the next paragraph, if any
5598 wxRichTextLine
* line
= parent
->GetLineAtPosition(index
+ 1);
5601 *height
= line
->GetSize().y
;
5602 pt
= line
->GetAbsolutePosition();
5606 *height
= dc
.GetCharHeight();
5607 int indent
= ConvertTenthsMMToPixels(dc
, m_attributes
.GetLeftIndent());
5608 pt
= wxPoint(indent
, GetCachedSize().y
);
5614 if (index
< GetRange().GetStart() || index
> GetRange().GetEnd())
5617 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5620 wxRichTextLine
* line
= node
->GetData();
5621 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5622 if (index
>= lineRange
.GetStart() && index
<= lineRange
.GetEnd())
5624 // If this is the last point in the line, and we're forcing the
5625 // returned value to be the start of the next line, do the required
5627 if (index
== lineRange
.GetEnd() && forceLineStart
)
5629 if (node
->GetNext())
5631 wxRichTextLine
* nextLine
= node
->GetNext()->GetData();
5632 *height
= nextLine
->GetSize().y
;
5633 pt
= nextLine
->GetAbsolutePosition();
5638 pt
.y
= line
->GetPosition().y
+ GetPosition().y
;
5640 wxRichTextRange
r(lineRange
.GetStart(), index
);
5644 // We find the size of the line up to this point,
5645 // then we can add this size to the line start position and
5646 // paragraph start position to find the actual position.
5648 if (GetRangeSize(r
, rangeSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, line
->GetPosition()+ GetPosition()))
5650 pt
.x
= line
->GetPosition().x
+ GetPosition().x
+ rangeSize
.x
;
5651 *height
= line
->GetSize().y
;
5658 node
= node
->GetNext();
5664 /// Hit-testing: returns a flag indicating hit test details, plus
5665 /// information about position
5666 int wxRichTextParagraph::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
5669 return wxRICHTEXT_HITTEST_NONE
;
5671 // If we're in the top-level container, then we can return
5672 // a suitable hit test code even if the point is outside the container area,
5673 // so that we can position the caret sensibly even if we don't
5674 // click on valid content. If we're not at the top-level, and the point
5675 // is not within this paragraph object, then we don't want to stop more
5676 // precise hit-testing from working prematurely, so return immediately.
5677 // NEW STRATEGY: use the parent boundary to test whether we're in the
5678 // right region, not the paragraph, since the paragraph may be positioned
5679 // some way in from where the user clicks.
5682 wxRichTextObject
* tempObj
, *tempContextObj
;
5683 if (GetParent() && GetParent()->wxRichTextObject::HitTest(dc
, context
, pt
, tmpPos
, & tempObj
, & tempContextObj
, flags
) == wxRICHTEXT_HITTEST_NONE
)
5684 return wxRICHTEXT_HITTEST_NONE
;
5687 wxRichTextObjectList::compatibility_iterator objNode
= m_children
.GetFirst();
5690 wxRichTextObject
* child
= objNode
->GetData();
5691 // Don't recurse if we have wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS,
5692 // and also, if this seems composite but actually is marked as atomic,
5694 if (child
->IsTopLevel() && ((flags
& wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS
) == 0) &&
5695 (! (((flags
& wxRICHTEXT_HITTEST_HONOUR_ATOMIC
) != 0) && child
->IsAtomic())))
5698 int hitTest
= child
->HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
);
5699 if (hitTest
!= wxRICHTEXT_HITTEST_NONE
)
5704 objNode
= objNode
->GetNext();
5707 wxPoint paraPos
= GetPosition();
5709 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5712 wxRichTextLine
* line
= node
->GetData();
5713 wxPoint linePos
= paraPos
+ line
->GetPosition();
5714 wxSize lineSize
= line
->GetSize();
5715 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5717 if (pt
.y
<= linePos
.y
+ lineSize
.y
)
5719 if (pt
.x
< linePos
.x
)
5721 textPosition
= lineRange
.GetStart();
5722 *obj
= FindObjectAtPosition(textPosition
);
5723 *contextObj
= GetContainer();
5724 return wxRICHTEXT_HITTEST_BEFORE
|wxRICHTEXT_HITTEST_OUTSIDE
;
5726 else if (pt
.x
>= (linePos
.x
+ lineSize
.x
))
5728 textPosition
= lineRange
.GetEnd();
5729 *obj
= FindObjectAtPosition(textPosition
);
5730 *contextObj
= GetContainer();
5731 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
;
5735 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5736 wxArrayInt partialExtents
;
5741 // This calculates the partial text extents
5742 GetRangeSize(lineRange
, paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, linePos
, wxDefaultSize
, & partialExtents
);
5744 int lastX
= linePos
.x
;
5746 for (i
= 0; i
< partialExtents
.GetCount(); i
++)
5748 int nextX
= partialExtents
[i
] + linePos
.x
;
5750 if (pt
.x
>= lastX
&& pt
.x
<= nextX
)
5752 textPosition
= i
+ lineRange
.GetStart(); // minus 1?
5754 *obj
= FindObjectAtPosition(textPosition
);
5755 *contextObj
= GetContainer();
5757 // So now we know it's between i-1 and i.
5758 // Let's see if we can be more precise about
5759 // which side of the position it's on.
5761 int midPoint
= (nextX
+ lastX
)/2;
5762 if (pt
.x
>= midPoint
)
5763 return wxRICHTEXT_HITTEST_AFTER
;
5765 return wxRICHTEXT_HITTEST_BEFORE
;
5772 int lastX
= linePos
.x
;
5773 for (i
= lineRange
.GetStart(); i
<= lineRange
.GetEnd(); i
++)
5778 wxRichTextRange
rangeToUse(lineRange
.GetStart(), i
);
5780 GetRangeSize(rangeToUse
, childSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, linePos
);
5782 int nextX
= childSize
.x
+ linePos
.x
;
5784 if (pt
.x
>= lastX
&& pt
.x
<= nextX
)
5788 *obj
= FindObjectAtPosition(textPosition
);
5789 *contextObj
= GetContainer();
5791 // So now we know it's between i-1 and i.
5792 // Let's see if we can be more precise about
5793 // which side of the position it's on.
5795 int midPoint
= (nextX
+ lastX
)/2;
5796 if (pt
.x
>= midPoint
)
5797 return wxRICHTEXT_HITTEST_AFTER
;
5799 return wxRICHTEXT_HITTEST_BEFORE
;
5810 node
= node
->GetNext();
5813 return wxRICHTEXT_HITTEST_NONE
;
5816 /// Split an object at this position if necessary, and return
5817 /// the previous object, or NULL if inserting at beginning.
5818 wxRichTextObject
* wxRichTextParagraph::SplitAt(long pos
, wxRichTextObject
** previousObject
)
5820 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5823 wxRichTextObject
* child
= node
->GetData();
5825 if (pos
== child
->GetRange().GetStart())
5829 if (node
->GetPrevious())
5830 *previousObject
= node
->GetPrevious()->GetData();
5832 *previousObject
= NULL
;
5838 if (child
->GetRange().Contains(pos
))
5840 // This should create a new object, transferring part of
5841 // the content to the old object and the rest to the new object.
5842 wxRichTextObject
* newObject
= child
->DoSplit(pos
);
5844 // If we couldn't split this object, just insert in front of it.
5847 // Maybe this is an empty string, try the next one
5852 // Insert the new object after 'child'
5853 if (node
->GetNext())
5854 m_children
.Insert(node
->GetNext(), newObject
);
5856 m_children
.Append(newObject
);
5857 newObject
->SetParent(this);
5860 *previousObject
= child
;
5866 node
= node
->GetNext();
5869 *previousObject
= NULL
;
5873 /// Move content to a list from obj on
5874 void wxRichTextParagraph::MoveToList(wxRichTextObject
* obj
, wxList
& list
)
5876 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(obj
);
5879 wxRichTextObject
* child
= node
->GetData();
5882 wxRichTextObjectList::compatibility_iterator oldNode
= node
;
5884 node
= node
->GetNext();
5886 m_children
.DeleteNode(oldNode
);
5890 /// Add content back from list
5891 void wxRichTextParagraph::MoveFromList(wxList
& list
)
5893 for (wxList::compatibility_iterator node
= list
.GetFirst(); node
; node
= node
->GetNext())
5895 AppendChild((wxRichTextObject
*) node
->GetData());
5900 void wxRichTextParagraph::CalculateRange(long start
, long& end
)
5902 wxRichTextCompositeObject::CalculateRange(start
, end
);
5904 // Add one for end of paragraph
5907 m_range
.SetRange(start
, end
);
5910 /// Find the object at the given position
5911 wxRichTextObject
* wxRichTextParagraph::FindObjectAtPosition(long position
)
5913 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5916 wxRichTextObject
* obj
= node
->GetData();
5917 if (obj
->GetRange().Contains(position
) ||
5918 obj
->GetRange().GetStart() == position
||
5919 obj
->GetRange().GetEnd() == position
)
5922 node
= node
->GetNext();
5927 /// Get the plain text searching from the start or end of the range.
5928 /// The resulting string may be shorter than the range given.
5929 bool wxRichTextParagraph::GetContiguousPlainText(wxString
& text
, const wxRichTextRange
& range
, bool fromStart
)
5931 text
= wxEmptyString
;
5935 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5938 wxRichTextObject
* obj
= node
->GetData();
5939 if (!obj
->GetRange().IsOutside(range
))
5941 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5944 text
+= textObj
->GetTextForRange(range
);
5952 node
= node
->GetNext();
5957 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetLast();
5960 wxRichTextObject
* obj
= node
->GetData();
5961 if (!obj
->GetRange().IsOutside(range
))
5963 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5966 text
= textObj
->GetTextForRange(range
) + text
;
5970 text
= wxT(" ") + text
;
5974 node
= node
->GetPrevious();
5981 /// Find a suitable wrap position.
5982 bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange
& range
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int availableSpace
, long& wrapPosition
, wxArrayInt
* partialExtents
)
5984 if (range
.GetLength() <= 0)
5987 // Find the first position where the line exceeds the available space.
5989 long breakPosition
= range
.GetEnd();
5991 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5992 if (partialExtents
&& partialExtents
->GetCount() >= (size_t) (GetRange().GetLength()-1)) // the final position in a paragraph is the newline
5996 if (range
.GetStart() > GetRange().GetStart())
5997 widthBefore
= (*partialExtents
)[range
.GetStart() - GetRange().GetStart() - 1];
6002 for (i
= (size_t) range
.GetStart(); i
<= (size_t) range
.GetEnd(); i
++)
6004 int widthFromStartOfThisRange
= (*partialExtents
)[i
- GetRange().GetStart()] - widthBefore
;
6006 if (widthFromStartOfThisRange
> availableSpace
)
6008 breakPosition
= i
-1;
6016 // Binary chop for speed
6017 long minPos
= range
.GetStart();
6018 long maxPos
= range
.GetEnd();
6021 if (minPos
== maxPos
)
6024 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
6026 if (sz
.x
> availableSpace
)
6027 breakPosition
= minPos
- 1;
6030 else if ((maxPos
- minPos
) == 1)
6033 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
6035 if (sz
.x
> availableSpace
)
6036 breakPosition
= minPos
- 1;
6039 GetRangeSize(wxRichTextRange(range
.GetStart(), maxPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
6040 if (sz
.x
> availableSpace
)
6041 breakPosition
= maxPos
-1;
6047 long nextPos
= minPos
+ ((maxPos
- minPos
) / 2);
6050 GetRangeSize(wxRichTextRange(range
.GetStart(), nextPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
6052 if (sz
.x
> availableSpace
)
6064 // Now we know the last position on the line.
6065 // Let's try to find a word break.
6068 if (GetContiguousPlainText(plainText
, wxRichTextRange(range
.GetStart(), breakPosition
), false))
6070 int newLinePos
= plainText
.Find(wxRichTextLineBreakChar
);
6071 if (newLinePos
!= wxNOT_FOUND
)
6073 breakPosition
= wxMax(0, range
.GetStart() + newLinePos
);
6077 int spacePos
= plainText
.Find(wxT(' '), true);
6078 int tabPos
= plainText
.Find(wxT('\t'), true);
6079 int pos
= wxMax(spacePos
, tabPos
);
6080 if (pos
!= wxNOT_FOUND
)
6082 int positionsFromEndOfString
= plainText
.length() - pos
- 1;
6083 breakPosition
= breakPosition
- positionsFromEndOfString
;
6088 wrapPosition
= breakPosition
;
6093 /// Get the bullet text for this paragraph.
6094 wxString
wxRichTextParagraph::GetBulletText()
6096 if (GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
||
6097 (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
))
6098 return wxEmptyString
;
6100 int number
= GetAttributes().GetBulletNumber();
6103 if ((GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ARABIC
) || (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
))
6105 text
.Printf(wxT("%d"), number
);
6107 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_UPPER
)
6109 // TODO: Unicode, and also check if number > 26
6110 text
.Printf(wxT("%c"), (wxChar
) (number
+64));
6112 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_LOWER
)
6114 // TODO: Unicode, and also check if number > 26
6115 text
.Printf(wxT("%c"), (wxChar
) (number
+96));
6117 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER
)
6119 text
= wxRichTextDecimalToRoman(number
);
6121 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER
)
6123 text
= wxRichTextDecimalToRoman(number
);
6126 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
)
6128 text
= GetAttributes().GetBulletText();
6131 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
)
6133 // The outline style relies on the text being computed statically,
6134 // since it depends on other levels points (e.g. 1.2.1.1). So normally the bullet text
6135 // should be stored in the attributes; if not, just use the number for this
6136 // level, as previously computed.
6137 if (!GetAttributes().GetBulletText().IsEmpty())
6138 text
= GetAttributes().GetBulletText();
6141 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PARENTHESES
)
6143 text
= wxT("(") + text
+ wxT(")");
6145 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_RIGHT_PARENTHESIS
)
6147 text
= text
+ wxT(")");
6150 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PERIOD
)
6158 /// Allocate or reuse a line object
6159 wxRichTextLine
* wxRichTextParagraph::AllocateLine(int pos
)
6161 if (pos
< (int) m_cachedLines
.GetCount())
6163 wxRichTextLine
* line
= m_cachedLines
.Item(pos
)->GetData();
6169 wxRichTextLine
* line
= new wxRichTextLine(this);
6170 m_cachedLines
.Append(line
);
6175 /// Clear remaining unused line objects, if any
6176 bool wxRichTextParagraph::ClearUnusedLines(int lineCount
)
6178 int cachedLineCount
= m_cachedLines
.GetCount();
6179 if ((int) cachedLineCount
> lineCount
)
6181 for (int i
= 0; i
< (int) (cachedLineCount
- lineCount
); i
++)
6183 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetLast();
6184 wxRichTextLine
* line
= node
->GetData();
6185 m_cachedLines
.Erase(node
);
6192 /// Get combined attributes of the base style, paragraph style and character style. We use this to dynamically
6193 /// retrieve the actual style.
6194 wxRichTextAttr
wxRichTextParagraph::GetCombinedAttributes(const wxRichTextAttr
& contentStyle
, bool includingBoxAttr
) const
6196 wxRichTextAttr attr
;
6197 wxRichTextParagraphLayoutBox
* buf
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
6200 attr
= buf
->GetBasicStyle();
6201 if (!includingBoxAttr
)
6203 attr
.GetTextBoxAttr().Reset();
6204 // The background colour will be painted by the container, and we don't
6205 // want to unnecessarily overwrite the background when we're drawing text
6206 // because this may erase the guideline (which appears just under the text
6207 // if there's no padding).
6208 attr
.SetFlags(attr
.GetFlags() & ~wxTEXT_ATTR_BACKGROUND_COLOUR
);
6210 wxRichTextApplyStyle(attr
, GetAttributes());
6213 attr
= GetAttributes();
6215 wxRichTextApplyStyle(attr
, contentStyle
);
6219 /// Get combined attributes of the base style and paragraph style.
6220 wxRichTextAttr
wxRichTextParagraph::GetCombinedAttributes(bool includingBoxAttr
) const
6222 wxRichTextAttr attr
;
6223 wxRichTextParagraphLayoutBox
* buf
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
6226 attr
= buf
->GetBasicStyle();
6227 if (!includingBoxAttr
)
6228 attr
.GetTextBoxAttr().Reset();
6229 wxRichTextApplyStyle(attr
, GetAttributes());
6232 attr
= GetAttributes();
6237 // Create default tabstop array
6238 void wxRichTextParagraph::InitDefaultTabs()
6240 // create a default tab list at 10 mm each.
6241 for (int i
= 0; i
< 20; ++i
)
6243 sm_defaultTabs
.Add(i
*100);
6247 // Clear default tabstop array
6248 void wxRichTextParagraph::ClearDefaultTabs()
6250 sm_defaultTabs
.Clear();
6253 void wxRichTextParagraph::LayoutFloat(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
, wxRichTextFloatCollector
* floatCollector
)
6255 wxRichTextObjectList::compatibility_iterator node
= GetChildren().GetFirst();
6258 wxRichTextObject
* anchored
= node
->GetData();
6259 if (anchored
&& anchored
->IsFloating() && !floatCollector
->HasFloat(anchored
))
6262 wxRichTextAttr
parentAttr(GetAttributes());
6263 context
.ApplyVirtualAttributes(parentAttr
, this);
6266 wxRect availableSpace
= GetParent()->GetAvailableContentArea(dc
, context
, rect
);
6268 anchored
->LayoutToBestSize(dc
, context
, GetBuffer(),
6269 parentAttr
, anchored
->GetAttributes(),
6270 parentRect
, availableSpace
,
6272 wxSize size
= anchored
->GetCachedSize();
6276 anchored
->GetRangeSize(anchored
->GetRange(), size
, descent
, dc
, context
, style
);
6280 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().IsValid())
6282 offsetY
= anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetValue();
6283 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
6285 offsetY
= ConvertTenthsMMToPixels(dc
, offsetY
);
6289 int pos
= floatCollector
->GetFitPosition(anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode(), rect
.y
+ offsetY
, size
.y
);
6291 /* Update the offset */
6292 int newOffsetY
= pos
- rect
.y
;
6293 if (newOffsetY
!= offsetY
)
6295 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
6296 newOffsetY
= ConvertPixelsToTenthsMM(dc
, newOffsetY
);
6297 anchored
->GetAttributes().GetTextBoxAttr().GetTop().SetValue(newOffsetY
);
6300 if (anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT
)
6302 else if (anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT
)
6303 x
= rect
.x
+ rect
.width
- size
.x
;
6305 //anchored->SetPosition(wxPoint(x, pos));
6306 anchored
->Move(wxPoint(x
, pos
)); // should move children
6307 anchored
->SetCachedSize(size
);
6308 floatCollector
->CollectFloat(this, anchored
);
6311 node
= node
->GetNext();
6315 // Get the first position from pos that has a line break character.
6316 long wxRichTextParagraph::GetFirstLineBreakPosition(long pos
)
6318 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
6321 wxRichTextObject
* obj
= node
->GetData();
6322 if (pos
>= obj
->GetRange().GetStart() && pos
<= obj
->GetRange().GetEnd())
6324 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
6327 long breakPos
= textObj
->GetFirstLineBreakPosition(pos
);
6332 node
= node
->GetNext();
6339 * This object represents a line in a paragraph, and stores
6340 * offsets from the start of the paragraph representing the
6341 * start and end positions of the line.
6344 wxRichTextLine::wxRichTextLine(wxRichTextParagraph
* parent
)
6350 void wxRichTextLine::Init(wxRichTextParagraph
* parent
)
6353 m_range
.SetRange(-1, -1);
6354 m_pos
= wxPoint(0, 0);
6355 m_size
= wxSize(0, 0);
6357 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6358 m_objectSizes
.Clear();
6363 void wxRichTextLine::Copy(const wxRichTextLine
& obj
)
6365 m_range
= obj
.m_range
;
6366 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6367 m_objectSizes
= obj
.m_objectSizes
;
6371 /// Get the absolute object position
6372 wxPoint
wxRichTextLine::GetAbsolutePosition() const
6374 return m_parent
->GetPosition() + m_pos
;
6377 /// Get the absolute range
6378 wxRichTextRange
wxRichTextLine::GetAbsoluteRange() const
6380 wxRichTextRange
range(m_range
.GetStart() + m_parent
->GetRange().GetStart(), 0);
6381 range
.SetEnd(range
.GetStart() + m_range
.GetLength()-1);
6386 * wxRichTextPlainText
6387 * This object represents a single piece of text.
6390 IMPLEMENT_DYNAMIC_CLASS(wxRichTextPlainText
, wxRichTextObject
)
6392 wxRichTextPlainText::wxRichTextPlainText(const wxString
& text
, wxRichTextObject
* parent
, wxRichTextAttr
* style
):
6393 wxRichTextObject(parent
)
6396 SetAttributes(*style
);
6401 #define USE_KERNING_FIX 1
6403 // If insufficient tabs are defined, this is the tab width used
6404 #define WIDTH_FOR_DEFAULT_TABS 50
6407 bool wxRichTextPlainText::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int WXUNUSED(style
))
6409 wxRichTextParagraph
* para
= wxDynamicCast(GetParent(), wxRichTextParagraph
);
6410 wxASSERT (para
!= NULL
);
6412 wxRichTextAttr
textAttr(para
? para
->GetCombinedAttributes(GetAttributes(), false /* no box attributes */) : GetAttributes());
6413 context
.ApplyVirtualAttributes(textAttr
, this);
6415 // Let's make the assumption for now that for content in a paragraph, including
6416 // text, we never have a discontinuous selection. So we only deal with a
6418 wxRichTextRange selectionRange
;
6419 if (selection
.IsValid())
6421 wxRichTextRangeArray selectionRanges
= selection
.GetSelectionForObject(this);
6422 if (selectionRanges
.GetCount() > 0)
6423 selectionRange
= selectionRanges
[0];
6425 selectionRange
= wxRICHTEXT_NO_SELECTION
;
6428 selectionRange
= wxRICHTEXT_NO_SELECTION
;
6430 int offset
= GetRange().GetStart();
6432 wxString str
= m_text
;
6433 if (context
.HasVirtualText(this))
6435 if (!context
.GetVirtualText(this, str
) || str
.Length() != m_text
.Length())
6439 // Replace line break characters with spaces
6440 wxString toRemove
= wxRichTextLineBreakChar
;
6441 str
.Replace(toRemove
, wxT(" "));
6442 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS
|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS
)))
6445 long len
= range
.GetLength();
6446 wxString stringChunk
= str
.Mid(range
.GetStart() - offset
, (size_t) len
);
6448 // Test for the optimized situations where all is selected, or none
6451 wxFont
textFont(GetBuffer()->GetFontTable().FindFont(textAttr
));
6452 wxCheckSetFont(dc
, textFont
);
6453 int charHeight
= dc
.GetCharHeight();
6456 if ( textFont
.IsOk() )
6458 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SMALL_CAPITALS
))
6460 textFont
.SetPointSize((int) (textFont
.GetPointSize()*0.75));
6461 wxCheckSetFont(dc
, textFont
);
6462 charHeight
= dc
.GetCharHeight();
6465 if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
) )
6467 if (textFont
.IsUsingSizeInPixels())
6469 double size
= static_cast<double>(textFont
.GetPixelSize().y
) / wxSCRIPT_MUL_FACTOR
;
6470 textFont
.SetPixelSize(wxSize(0, static_cast<int>(size
)));
6476 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6477 textFont
.SetPointSize(static_cast<int>(size
));
6481 wxCheckSetFont(dc
, textFont
);
6483 else if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) )
6485 if (textFont
.IsUsingSizeInPixels())
6487 double size
= static_cast<double>(textFont
.GetPixelSize().y
) / wxSCRIPT_MUL_FACTOR
;
6488 textFont
.SetPixelSize(wxSize(0, static_cast<int>(size
)));
6490 int sub_height
= static_cast<int>(static_cast<double>(charHeight
) / wxSCRIPT_MUL_FACTOR
);
6491 y
= rect
.y
+ (rect
.height
- sub_height
+ (descent
- m_descent
));
6495 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6496 textFont
.SetPointSize(static_cast<int>(size
));
6498 int sub_height
= static_cast<int>(static_cast<double>(charHeight
) / wxSCRIPT_MUL_FACTOR
);
6499 y
= rect
.y
+ (rect
.height
- sub_height
+ (descent
- m_descent
));
6501 wxCheckSetFont(dc
, textFont
);
6506 y
= rect
.y
+ (rect
.height
- charHeight
- (descent
- m_descent
));
6512 y
= rect
.y
+ (rect
.height
- charHeight
- (descent
- m_descent
));
6515 // TODO: new selection code
6517 // (a) All selected.
6518 if (selectionRange
.GetStart() <= range
.GetStart() && selectionRange
.GetEnd() >= range
.GetEnd())
6520 DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, true);
6522 // (b) None selected.
6523 else if (selectionRange
.GetEnd() < range
.GetStart() || selectionRange
.GetStart() > range
.GetEnd())
6525 // Draw all unselected
6526 DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, false);
6530 // (c) Part selected, part not
6531 // Let's draw unselected chunk, selected chunk, then unselected chunk.
6533 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6535 // 1. Initial unselected chunk, if any, up until start of selection.
6536 if (selectionRange
.GetStart() > range
.GetStart() && selectionRange
.GetStart() <= range
.GetEnd())
6538 int r1
= range
.GetStart();
6539 int s1
= selectionRange
.GetStart()-1;
6540 int fragmentLen
= s1
- r1
+ 1;
6541 if (fragmentLen
< 0)
6543 wxLogDebug(wxT("Mid(%d, %d"), (int)(r1
- offset
), (int)fragmentLen
);
6545 wxString stringFragment
= str
.Mid(r1
- offset
, fragmentLen
);
6547 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false);
6550 if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
)
6552 // Compensate for kerning difference
6553 wxString
stringFragment2(str
.Mid(r1
- offset
, fragmentLen
+1));
6554 wxString
stringFragment3(str
.Mid(r1
- offset
+ fragmentLen
, 1));
6556 wxCoord w1
, h1
, w2
, h2
, w3
, h3
;
6557 dc
.GetTextExtent(stringFragment
, & w1
, & h1
);
6558 dc
.GetTextExtent(stringFragment2
, & w2
, & h2
);
6559 dc
.GetTextExtent(stringFragment3
, & w3
, & h3
);
6561 int kerningDiff
= (w1
+ w3
) - w2
;
6562 x
= x
- kerningDiff
;
6567 // 2. Selected chunk, if any.
6568 if (selectionRange
.GetEnd() >= range
.GetStart())
6570 int s1
= wxMax(selectionRange
.GetStart(), range
.GetStart());
6571 int s2
= wxMin(selectionRange
.GetEnd(), range
.GetEnd());
6573 int fragmentLen
= s2
- s1
+ 1;
6574 if (fragmentLen
< 0)
6576 wxLogDebug(wxT("Mid(%d, %d"), (int)(s1
- offset
), (int)fragmentLen
);
6578 wxString stringFragment
= str
.Mid(s1
- offset
, fragmentLen
);
6580 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, true);
6583 if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
)
6585 // Compensate for kerning difference
6586 wxString
stringFragment2(str
.Mid(s1
- offset
, fragmentLen
+1));
6587 wxString
stringFragment3(str
.Mid(s1
- offset
+ fragmentLen
, 1));
6589 wxCoord w1
, h1
, w2
, h2
, w3
, h3
;
6590 dc
.GetTextExtent(stringFragment
, & w1
, & h1
);
6591 dc
.GetTextExtent(stringFragment2
, & w2
, & h2
);
6592 dc
.GetTextExtent(stringFragment3
, & w3
, & h3
);
6594 int kerningDiff
= (w1
+ w3
) - w2
;
6595 x
= x
- kerningDiff
;
6600 // 3. Remaining unselected chunk, if any
6601 if (selectionRange
.GetEnd() < range
.GetEnd())
6603 int s2
= wxMin(selectionRange
.GetEnd()+1, range
.GetEnd());
6604 int r2
= range
.GetEnd();
6606 int fragmentLen
= r2
- s2
+ 1;
6607 if (fragmentLen
< 0)
6609 wxLogDebug(wxT("Mid(%d, %d"), (int)(s2
- offset
), (int)fragmentLen
);
6611 wxString stringFragment
= str
.Mid(s2
- offset
, fragmentLen
);
6613 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false);
6620 bool wxRichTextPlainText::DrawTabbedString(wxDC
& dc
, const wxRichTextAttr
& attr
, const wxRect
& rect
,wxString
& str
, wxCoord
& x
, wxCoord
& y
, bool selected
)
6622 bool hasTabs
= (str
.Find(wxT('\t')) != wxNOT_FOUND
);
6624 wxArrayInt tabArray
;
6628 if (attr
.GetTabs().IsEmpty())
6629 tabArray
= wxRichTextParagraph::GetDefaultTabs();
6631 tabArray
= attr
.GetTabs();
6632 tabCount
= tabArray
.GetCount();
6634 for (int i
= 0; i
< tabCount
; ++i
)
6636 int pos
= tabArray
[i
];
6637 pos
= ConvertTenthsMMToPixels(dc
, pos
);
6644 int nextTabPos
= -1;
6650 wxColour
highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
));
6651 wxColour
highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
));
6653 wxCheckSetBrush(dc
, wxBrush(highlightColour
));
6654 wxCheckSetPen(dc
, wxPen(highlightColour
));
6655 dc
.SetTextForeground(highlightTextColour
);
6656 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6660 dc
.SetTextForeground(attr
.GetTextColour());
6662 if (attr
.HasFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
) && attr
.GetBackgroundColour().IsOk())
6664 dc
.SetBackgroundMode(wxBRUSHSTYLE_SOLID
);
6665 dc
.SetTextBackground(attr
.GetBackgroundColour());
6668 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6671 wxCoord x_orig
= GetParent()->GetPosition().x
;
6674 // the string has a tab
6675 // break up the string at the Tab
6676 wxString stringChunk
= str
.BeforeFirst(wxT('\t'));
6677 str
= str
.AfterFirst(wxT('\t'));
6678 dc
.GetTextExtent(stringChunk
, & w
, & h
);
6680 bool not_found
= true;
6681 for (int i
= 0; i
< tabCount
&& not_found
; ++i
)
6683 nextTabPos
= tabArray
.Item(i
) + x_orig
;
6685 // Find the next tab position.
6686 // Even if we're at the end of the tab array, we must still draw the chunk.
6688 if (nextTabPos
> tabPos
|| (i
== (tabCount
- 1)))
6690 if (nextTabPos
<= tabPos
)
6692 int defaultTabWidth
= ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
);
6693 nextTabPos
= tabPos
+ defaultTabWidth
;
6700 wxRect
selRect(x
, rect
.y
, w
, rect
.GetHeight());
6701 dc
.DrawRectangle(selRect
);
6703 dc
.DrawText(stringChunk
, x
, y
);
6705 if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
))
6707 wxPen oldPen
= dc
.GetPen();
6708 wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1));
6709 dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5));
6710 wxCheckSetPen(dc
, oldPen
);
6716 hasTabs
= (str
.Find(wxT('\t')) != wxNOT_FOUND
);
6721 dc
.GetTextExtent(str
, & w
, & h
);
6724 wxRect
selRect(x
, rect
.y
, w
, rect
.GetHeight());
6725 dc
.DrawRectangle(selRect
);
6727 dc
.DrawText(str
, x
, y
);
6729 if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
))
6731 wxPen oldPen
= dc
.GetPen();
6732 wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1));
6733 dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5));
6734 wxCheckSetPen(dc
, oldPen
);
6743 /// Lay the item out
6744 bool wxRichTextPlainText::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& WXUNUSED(rect
), const wxRect
& WXUNUSED(parentRect
), int WXUNUSED(style
))
6746 // Only lay out if we haven't already cached the size
6748 GetRangeSize(GetRange(), m_size
, m_descent
, dc
, context
, 0, wxPoint(0, 0));
6750 // Eventually we want to have a reasonable estimate of minimum size.
6751 m_minSize
= wxSize(0, 0);
6756 void wxRichTextPlainText::Copy(const wxRichTextPlainText
& obj
)
6758 wxRichTextObject::Copy(obj
);
6760 m_text
= obj
.m_text
;
6763 /// Get/set the object size for the given range. Returns false if the range
6764 /// is invalid for this object.
6765 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
6767 if (!range
.IsWithin(GetRange()))
6770 wxRichTextParagraph
* para
= wxDynamicCast(GetParent(), wxRichTextParagraph
);
6771 wxASSERT (para
!= NULL
);
6773 int relativeX
= position
.x
- GetParent()->GetPosition().x
;
6775 wxRichTextAttr
textAttr(para
? para
->GetCombinedAttributes(GetAttributes()) : GetAttributes());
6776 context
.ApplyVirtualAttributes(textAttr
, (wxRichTextObject
*) this);
6778 // Always assume unformatted text, since at this level we have no knowledge
6779 // of line breaks - and we don't need it, since we'll calculate size within
6780 // formatted text by doing it in chunks according to the line ranges
6782 bool bScript(false);
6783 wxFont
font(GetBuffer()->GetFontTable().FindFont(textAttr
));
6786 if ( textAttr
.HasTextEffects() && ( (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
)
6787 || (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) ) )
6789 wxFont textFont
= font
;
6790 if (textFont
.IsUsingSizeInPixels())
6792 double size
= static_cast<double>(textFont
.GetPixelSize().y
) / wxSCRIPT_MUL_FACTOR
;
6793 textFont
.SetPixelSize(wxSize(0, static_cast<int>(size
)));
6797 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6798 textFont
.SetPointSize(static_cast<int>(size
));
6800 wxCheckSetFont(dc
, textFont
);
6803 else if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SMALL_CAPITALS
))
6805 wxFont textFont
= font
;
6806 textFont
.SetPointSize((int) (textFont
.GetPointSize()*0.75));
6807 wxCheckSetFont(dc
, textFont
);
6812 wxCheckSetFont(dc
, font
);
6816 bool haveDescent
= false;
6817 int startPos
= range
.GetStart() - GetRange().GetStart();
6818 long len
= range
.GetLength();
6820 wxString
str(m_text
);
6821 if (context
.HasVirtualText(this))
6823 if (!context
.GetVirtualText(this, str
) || str
.Length() != m_text
.Length())
6827 wxString toReplace
= wxRichTextLineBreakChar
;
6828 str
.Replace(toReplace
, wxT(" "));
6830 wxString stringChunk
= str
.Mid(startPos
, (size_t) len
);
6832 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS
|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS
)))
6833 stringChunk
.MakeUpper();
6837 if (stringChunk
.Find(wxT('\t')) != wxNOT_FOUND
)
6839 // the string has a tab
6840 wxArrayInt tabArray
;
6841 if (textAttr
.GetTabs().IsEmpty())
6842 tabArray
= wxRichTextParagraph::GetDefaultTabs();
6844 tabArray
= textAttr
.GetTabs();
6846 int tabCount
= tabArray
.GetCount();
6848 for (int i
= 0; i
< tabCount
; ++i
)
6850 int pos
= tabArray
[i
];
6851 pos
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, pos
);
6855 int nextTabPos
= -1;
6857 while (stringChunk
.Find(wxT('\t')) >= 0)
6859 int absoluteWidth
= 0;
6861 // the string has a tab
6862 // break up the string at the Tab
6863 wxString stringFragment
= stringChunk
.BeforeFirst(wxT('\t'));
6864 stringChunk
= stringChunk
.AfterFirst(wxT('\t'));
6869 if (partialExtents
->GetCount() > 0)
6870 oldWidth
= (*partialExtents
)[partialExtents
->GetCount()-1];
6874 // Add these partial extents
6876 dc
.GetPartialTextExtents(stringFragment
, p
);
6878 for (j
= 0; j
< p
.GetCount(); j
++)
6879 partialExtents
->Add(oldWidth
+ p
[j
]);
6881 if (partialExtents
->GetCount() > 0)
6882 absoluteWidth
= (*partialExtents
)[(*partialExtents
).GetCount()-1] + relativeX
;
6884 absoluteWidth
= relativeX
;
6888 dc
.GetTextExtent(stringFragment
, & w
, & h
);
6890 absoluteWidth
= width
+ relativeX
;
6894 bool notFound
= true;
6895 for (int i
= 0; i
< tabCount
&& notFound
; ++i
)
6897 nextTabPos
= tabArray
.Item(i
);
6899 // Find the next tab position.
6900 // Even if we're at the end of the tab array, we must still process the chunk.
6902 if (nextTabPos
> absoluteWidth
|| (i
== (tabCount
- 1)))
6904 if (nextTabPos
<= absoluteWidth
)
6906 int defaultTabWidth
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
);
6907 nextTabPos
= absoluteWidth
+ defaultTabWidth
;
6911 width
= nextTabPos
- relativeX
;
6914 partialExtents
->Add(width
);
6920 if (!stringChunk
.IsEmpty())
6925 if (partialExtents
->GetCount() > 0)
6926 oldWidth
= (*partialExtents
)[partialExtents
->GetCount()-1];
6930 // Add these partial extents
6932 dc
.GetPartialTextExtents(stringChunk
, p
);
6934 for (j
= 0; j
< p
.GetCount(); j
++)
6935 partialExtents
->Add(oldWidth
+ p
[j
]);
6939 dc
.GetTextExtent(stringChunk
, & w
, & h
, & descent
);
6947 int charHeight
= dc
.GetCharHeight();
6948 if ((*partialExtents
).GetCount() > 0)
6949 w
= (*partialExtents
)[partialExtents
->GetCount()-1];
6952 size
= wxSize(w
, charHeight
);
6956 size
= wxSize(width
, dc
.GetCharHeight());
6960 dc
.GetTextExtent(wxT("X"), & w
, & h
, & descent
);
6968 /// Do a split, returning an object containing the second part, and setting
6969 /// the first part in 'this'.
6970 wxRichTextObject
* wxRichTextPlainText::DoSplit(long pos
)
6972 long index
= pos
- GetRange().GetStart();
6974 if (index
< 0 || index
>= (int) m_text
.length())
6977 wxString firstPart
= m_text
.Mid(0, index
);
6978 wxString secondPart
= m_text
.Mid(index
);
6982 wxRichTextPlainText
* newObject
= new wxRichTextPlainText(secondPart
);
6983 newObject
->SetAttributes(GetAttributes());
6984 newObject
->SetProperties(GetProperties());
6986 newObject
->SetRange(wxRichTextRange(pos
, GetRange().GetEnd()));
6987 GetRange().SetEnd(pos
-1);
6993 void wxRichTextPlainText::CalculateRange(long start
, long& end
)
6995 end
= start
+ m_text
.length() - 1;
6996 m_range
.SetRange(start
, end
);
7000 bool wxRichTextPlainText::DeleteRange(const wxRichTextRange
& range
)
7002 wxRichTextRange r
= range
;
7004 r
.LimitTo(GetRange());
7006 if (r
.GetStart() == GetRange().GetStart() && r
.GetEnd() == GetRange().GetEnd())
7012 long startIndex
= r
.GetStart() - GetRange().GetStart();
7013 long len
= r
.GetLength();
7015 m_text
= m_text
.Mid(0, startIndex
) + m_text
.Mid(startIndex
+len
);
7019 /// Get text for the given range.
7020 wxString
wxRichTextPlainText::GetTextForRange(const wxRichTextRange
& range
) const
7022 wxRichTextRange r
= range
;
7024 r
.LimitTo(GetRange());
7026 long startIndex
= r
.GetStart() - GetRange().GetStart();
7027 long len
= r
.GetLength();
7029 return m_text
.Mid(startIndex
, len
);
7032 /// Returns true if this object can merge itself with the given one.
7033 bool wxRichTextPlainText::CanMerge(wxRichTextObject
* object
, wxRichTextDrawingContext
& context
) const
7036 if (!context
.GetVirtualAttributesEnabled())
7038 return object
->GetClassInfo() == wxCLASSINFO(wxRichTextPlainText
) &&
7039 (m_text
.empty() || (wxTextAttrEq(GetAttributes(), object
->GetAttributes()) && m_properties
== object
->GetProperties()));
7043 wxRichTextPlainText
* otherObj
= wxDynamicCast(object
, wxRichTextPlainText
);
7044 if (!otherObj
|| m_text
.empty())
7047 if (!wxTextAttrEq(GetAttributes(), object
->GetAttributes()) || !(m_properties
== object
->GetProperties()))
7050 // Check if differing virtual attributes makes it impossible to merge
7053 bool hasVirtualAttr1
= context
.HasVirtualAttributes((wxRichTextObject
*) this);
7054 bool hasVirtualAttr2
= context
.HasVirtualAttributes((wxRichTextObject
*) object
);
7055 if (!hasVirtualAttr1
&& !hasVirtualAttr2
)
7057 else if (hasVirtualAttr1
!= hasVirtualAttr2
)
7061 wxRichTextAttr virtualAttr1
= context
.GetVirtualAttributes((wxRichTextObject
*) this);
7062 wxRichTextAttr virtualAttr2
= context
.GetVirtualAttributes((wxRichTextObject
*) object
);
7063 return virtualAttr1
== virtualAttr2
;
7068 /// Returns true if this object merged itself with the given one.
7069 /// The calling code will then delete the given object.
7070 bool wxRichTextPlainText::Merge(wxRichTextObject
* object
, wxRichTextDrawingContext
& WXUNUSED(context
))
7072 wxRichTextPlainText
* textObject
= wxDynamicCast(object
, wxRichTextPlainText
);
7073 wxASSERT( textObject
!= NULL
);
7077 m_text
+= textObject
->GetText();
7078 wxRichTextApplyStyle(m_attributes
, textObject
->GetAttributes());
7085 bool wxRichTextPlainText::CanSplit(wxRichTextDrawingContext
& context
) const
7087 // If this object has any virtual attributes at all, whether for the whole object
7088 // or individual ones, we should try splitting it by calling Split.
7089 // Must be more than one character in order to be able to split.
7090 return m_text
.Length() > 1 && context
.HasVirtualAttributes((wxRichTextObject
*) this);
7093 wxRichTextObject
* wxRichTextPlainText::Split(wxRichTextDrawingContext
& context
)
7095 int count
= context
.GetVirtualSubobjectAttributesCount(this);
7096 if (count
> 0 && GetParent())
7098 wxRichTextCompositeObject
* parent
= wxDynamicCast(GetParent(), wxRichTextCompositeObject
);
7099 wxRichTextObjectList::compatibility_iterator node
= parent
->GetChildren().Find(this);
7102 const wxRichTextAttr emptyAttr
;
7103 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
7105 wxArrayInt positions
;
7106 wxRichTextAttrArray attributes
;
7107 if (context
.GetVirtualSubobjectAttributes(this, positions
, attributes
) && positions
.GetCount() > 0)
7109 wxASSERT(positions
.GetCount() == attributes
.GetCount());
7111 // We will gather up runs of text with the same virtual attributes
7113 int len
= m_text
.Length();
7116 // runStart and runEnd represent the accumulated run with a consistent attribute
7117 // that hasn't yet been appended
7120 wxRichTextAttr currentAttr
;
7121 wxString text
= m_text
;
7122 wxRichTextPlainText
* lastPlainText
= this;
7124 for (i
= 0; i
< (int) positions
.GetCount(); i
++)
7126 int pos
= positions
[i
];
7127 wxASSERT(pos
>= 0 && pos
< len
);
7128 if (pos
>= 0 && pos
< len
)
7130 const wxRichTextAttr
& attr
= attributes
[i
];
7137 // Check if there was a gap from the last known attribute and this.
7138 // In that case, we need to do something with the span of non-attributed text.
7139 else if ((pos
-1) > runEnd
)
7143 // We hadn't processed anything previously, so the previous run is from the text start
7144 // to just before this position. The current attribute remains empty.
7150 // If the previous attribute matches the gap's attribute (i.e., no attributes)
7151 // then just extend the run.
7152 if (currentAttr
.IsDefault())
7158 // We need to add an object, or reuse the existing one.
7161 lastPlainText
= this;
7162 SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7166 wxRichTextPlainText
* obj
= new wxRichTextPlainText
;
7167 lastPlainText
= obj
;
7168 obj
->SetAttributes(GetAttributes());
7169 obj
->SetProperties(GetProperties());
7170 obj
->SetParent(parent
);
7172 obj
->SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7174 parent
->GetChildren().Insert(next
, obj
);
7176 parent
->GetChildren().Append(obj
);
7179 runStart
= runEnd
+1;
7182 currentAttr
= emptyAttr
;
7187 wxASSERT(runEnd
== pos
-1);
7189 // Now we only have to deal with the previous run
7190 if (currentAttr
== attr
)
7192 // If we still have the same attributes, then we
7193 // simply increase the run size.
7200 // We need to add an object, or reuse the existing one.
7203 lastPlainText
= this;
7204 SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7208 wxRichTextPlainText
* obj
= new wxRichTextPlainText
;
7209 lastPlainText
= obj
;
7210 obj
->SetAttributes(GetAttributes());
7211 obj
->SetProperties(GetProperties());
7212 obj
->SetParent(parent
);
7214 obj
->SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7216 parent
->GetChildren().Insert(next
, obj
);
7218 parent
->GetChildren().Append(obj
);
7230 // We may still have a run to add, and possibly a no-attribute text fragment after that.
7231 // If the whole string was already a single attribute (the run covers the whole string), don't split.
7232 if ((runStart
!= -1) && !(runStart
== 0 && runEnd
== (len
-1)))
7234 // If the current attribute is empty, merge the run with the next fragment
7235 // which by definition (because it's not specified) has empty attributes.
7236 if (currentAttr
.IsDefault())
7239 if (runEnd
< (len
-1))
7241 // We need to add an object, or reuse the existing one.
7244 lastPlainText
= this;
7245 SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7249 wxRichTextPlainText
* obj
= new wxRichTextPlainText
;
7250 lastPlainText
= obj
;
7251 obj
->SetAttributes(GetAttributes());
7252 obj
->SetProperties(GetProperties());
7253 obj
->SetParent(parent
);
7255 obj
->SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7257 parent
->GetChildren().Insert(next
, obj
);
7259 parent
->GetChildren().Append(obj
);
7262 runStart
= runEnd
+1;
7266 // Now the last, non-attributed fragment at the end, if any
7267 if ((runStart
< len
) && !(runStart
== 0 && runEnd
== (len
-1)))
7269 wxASSERT(runStart
!= 0);
7271 wxRichTextPlainText
* obj
= new wxRichTextPlainText
;
7272 obj
->SetAttributes(GetAttributes());
7273 obj
->SetProperties(GetProperties());
7274 obj
->SetParent(parent
);
7276 obj
->SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7278 parent
->GetChildren().Insert(next
, obj
);
7280 parent
->GetChildren().Append(obj
);
7282 lastPlainText
= obj
;
7286 return lastPlainText
;
7293 /// Dump to output stream for debugging
7294 void wxRichTextPlainText::Dump(wxTextOutputStream
& stream
)
7296 wxRichTextObject::Dump(stream
);
7297 stream
<< m_text
<< wxT("\n");
7300 /// Get the first position from pos that has a line break character.
7301 long wxRichTextPlainText::GetFirstLineBreakPosition(long pos
)
7304 int len
= m_text
.length();
7305 int startPos
= pos
- m_range
.GetStart();
7306 for (i
= startPos
; i
< len
; i
++)
7308 wxChar ch
= m_text
[i
];
7309 if (ch
== wxRichTextLineBreakChar
)
7311 return i
+ m_range
.GetStart();
7319 * This is a kind of box, used to represent the whole buffer
7322 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBuffer
, wxRichTextParagraphLayoutBox
)
7324 wxList
wxRichTextBuffer::sm_handlers
;
7325 wxList
wxRichTextBuffer::sm_drawingHandlers
;
7326 wxRichTextFieldTypeHashMap
wxRichTextBuffer::sm_fieldTypes
;
7327 wxRichTextRenderer
* wxRichTextBuffer::sm_renderer
= NULL
;
7328 int wxRichTextBuffer::sm_bulletRightMargin
= 20;
7329 float wxRichTextBuffer::sm_bulletProportion
= (float) 0.3;
7330 bool wxRichTextBuffer::sm_floatingLayoutMode
= true;
7333 void wxRichTextBuffer::Init()
7335 m_commandProcessor
= new wxCommandProcessor
;
7336 m_styleSheet
= NULL
;
7338 m_batchedCommandDepth
= 0;
7339 m_batchedCommand
= NULL
;
7343 m_dimensionScale
= 1.0;
7349 wxRichTextBuffer::~wxRichTextBuffer()
7351 delete m_commandProcessor
;
7352 delete m_batchedCommand
;
7355 ClearEventHandlers();
7358 void wxRichTextBuffer::ResetAndClearCommands()
7362 GetCommandProcessor()->ClearCommands();
7365 Invalidate(wxRICHTEXT_ALL
);
7368 void wxRichTextBuffer::Copy(const wxRichTextBuffer
& obj
)
7370 wxRichTextParagraphLayoutBox::Copy(obj
);
7372 m_styleSheet
= obj
.m_styleSheet
;
7373 m_modified
= obj
.m_modified
;
7374 m_batchedCommandDepth
= 0;
7375 if (m_batchedCommand
)
7376 delete m_batchedCommand
;
7377 m_batchedCommand
= NULL
;
7378 m_suppressUndo
= obj
.m_suppressUndo
;
7379 m_invalidRange
= obj
.m_invalidRange
;
7380 m_dimensionScale
= obj
.m_dimensionScale
;
7381 m_fontScale
= obj
.m_fontScale
;
7384 /// Push style sheet to top of stack
7385 bool wxRichTextBuffer::PushStyleSheet(wxRichTextStyleSheet
* styleSheet
)
7388 styleSheet
->InsertSheet(m_styleSheet
);
7390 SetStyleSheet(styleSheet
);
7395 /// Pop style sheet from top of stack
7396 wxRichTextStyleSheet
* wxRichTextBuffer::PopStyleSheet()
7400 wxRichTextStyleSheet
* oldSheet
= m_styleSheet
;
7401 m_styleSheet
= oldSheet
->GetNextSheet();
7410 /// Submit command to insert paragraphs
7411 bool wxRichTextBuffer::InsertParagraphsWithUndo(long pos
, const wxRichTextParagraphLayoutBox
& paragraphs
, wxRichTextCtrl
* ctrl
, int flags
)
7413 return ctrl
->GetFocusObject()->InsertParagraphsWithUndo(this, pos
, paragraphs
, ctrl
, flags
);
7416 /// Submit command to insert paragraphs
7417 bool wxRichTextParagraphLayoutBox::InsertParagraphsWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxRichTextParagraphLayoutBox
& paragraphs
, wxRichTextCtrl
* ctrl
, int WXUNUSED(flags
))
7419 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7421 action
->GetNewParagraphs() = paragraphs
;
7423 action
->SetPosition(pos
);
7425 wxRichTextRange range
= wxRichTextRange(pos
, pos
+ paragraphs
.GetOwnRange().GetEnd() - 1);
7426 if (!paragraphs
.GetPartialParagraph())
7427 range
.SetEnd(range
.GetEnd()+1);
7429 // Set the range we'll need to delete in Undo
7430 action
->SetRange(range
);
7432 buffer
->SubmitAction(action
);
7437 /// Submit command to insert the given text
7438 bool wxRichTextBuffer::InsertTextWithUndo(long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
, int flags
)
7440 return ctrl
->GetFocusObject()->InsertTextWithUndo(this, pos
, text
, ctrl
, flags
);
7443 /// Submit command to insert the given text
7444 bool wxRichTextParagraphLayoutBox::InsertTextWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
, int flags
)
7446 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7448 wxRichTextAttr
* p
= NULL
;
7449 wxRichTextAttr paraAttr
;
7450 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7452 // Get appropriate paragraph style
7453 paraAttr
= GetStyleForNewParagraph(buffer
, pos
, false, false);
7454 if (!paraAttr
.IsDefault())
7458 action
->GetNewParagraphs().AddParagraphs(text
, p
);
7460 int length
= action
->GetNewParagraphs().GetOwnRange().GetLength();
7462 if (!text
.empty() && text
.Last() != wxT('\n'))
7464 // Don't count the newline when undoing
7466 action
->GetNewParagraphs().SetPartialParagraph(true);
7468 else if (!text
.empty() && text
.Last() == wxT('\n'))
7471 action
->SetPosition(pos
);
7473 // Set the range we'll need to delete in Undo
7474 action
->SetRange(wxRichTextRange(pos
, pos
+ length
- 1));
7476 buffer
->SubmitAction(action
);
7481 /// Submit command to insert the given text
7482 bool wxRichTextBuffer::InsertNewlineWithUndo(long pos
, wxRichTextCtrl
* ctrl
, int flags
)
7484 return ctrl
->GetFocusObject()->InsertNewlineWithUndo(this, pos
, ctrl
, flags
);
7487 /// Submit command to insert the given text
7488 bool wxRichTextParagraphLayoutBox::InsertNewlineWithUndo(wxRichTextBuffer
* buffer
, long pos
, wxRichTextCtrl
* ctrl
, int flags
)
7490 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7492 wxRichTextAttr
* p
= NULL
;
7493 wxRichTextAttr paraAttr
;
7494 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7496 paraAttr
= GetStyleForNewParagraph(buffer
, pos
, false, true /* look for next paragraph style */);
7497 if (!paraAttr
.IsDefault())
7501 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7502 // Don't include box attributes such as margins
7503 attr
.GetTextBoxAttr().Reset();
7505 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(wxEmptyString
, this, & attr
);
7506 action
->GetNewParagraphs().AppendChild(newPara
);
7507 action
->GetNewParagraphs().UpdateRanges();
7508 action
->GetNewParagraphs().SetPartialParagraph(false);
7509 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
, false);
7513 newPara
->SetAttributes(*p
);
7515 if (flags
& wxRICHTEXT_INSERT_INTERACTIVE
)
7517 if (para
&& para
->GetRange().GetEnd() == pos
)
7520 // Now see if we need to number the paragraph.
7521 if (newPara
->GetAttributes().HasBulletNumber())
7523 wxRichTextAttr numberingAttr
;
7524 if (FindNextParagraphNumber(para
, numberingAttr
))
7525 wxRichTextApplyStyle(newPara
->GetAttributes(), (const wxRichTextAttr
&) numberingAttr
);
7529 action
->SetPosition(pos
);
7531 // Use the default character style
7532 if (!buffer
->GetDefaultStyle().IsDefault() && newPara
->GetChildren().GetFirst())
7534 // Check whether the default style merely reflects the paragraph/basic style,
7535 // in which case don't apply it.
7536 wxRichTextAttr
defaultStyle(buffer
->GetDefaultStyle());
7537 defaultStyle
.GetTextBoxAttr().Reset();
7538 wxRichTextAttr toApply
;
7541 wxRichTextAttr combinedAttr
= para
->GetCombinedAttributes(true /* include box attributes */);
7542 wxRichTextAttr newAttr
;
7543 // This filters out attributes that are accounted for by the current
7544 // paragraph/basic style
7545 wxRichTextApplyStyle(toApply
, defaultStyle
, & combinedAttr
);
7548 toApply
= defaultStyle
;
7550 if (!toApply
.IsDefault())
7551 newPara
->GetChildren().GetFirst()->GetData()->SetAttributes(toApply
);
7554 // Set the range we'll need to delete in Undo
7555 action
->SetRange(wxRichTextRange(pos1
, pos1
));
7557 buffer
->SubmitAction(action
);
7562 /// Submit command to insert the given image
7563 bool wxRichTextBuffer::InsertImageWithUndo(long pos
, const wxRichTextImageBlock
& imageBlock
, wxRichTextCtrl
* ctrl
, int flags
,
7564 const wxRichTextAttr
& textAttr
)
7566 return ctrl
->GetFocusObject()->InsertImageWithUndo(this, pos
, imageBlock
, ctrl
, flags
, textAttr
);
7569 /// Submit command to insert the given image
7570 bool wxRichTextParagraphLayoutBox::InsertImageWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxRichTextImageBlock
& imageBlock
,
7571 wxRichTextCtrl
* ctrl
, int flags
,
7572 const wxRichTextAttr
& textAttr
)
7574 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7576 wxRichTextAttr
* p
= NULL
;
7577 wxRichTextAttr paraAttr
;
7578 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7580 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
7581 if (!paraAttr
.IsDefault())
7585 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7587 // Don't include box attributes such as margins
7588 attr
.GetTextBoxAttr().Reset();
7590 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7592 newPara
->SetAttributes(*p
);
7594 wxRichTextImage
* imageObject
= new wxRichTextImage(imageBlock
, newPara
);
7595 newPara
->AppendChild(imageObject
);
7596 imageObject
->SetAttributes(textAttr
);
7597 action
->GetNewParagraphs().AppendChild(newPara
);
7598 action
->GetNewParagraphs().UpdateRanges();
7600 action
->GetNewParagraphs().SetPartialParagraph(true);
7602 action
->SetPosition(pos
);
7604 // Set the range we'll need to delete in Undo
7605 action
->SetRange(wxRichTextRange(pos
, pos
));
7607 buffer
->SubmitAction(action
);
7612 // Insert an object with no change of it
7613 wxRichTextObject
* wxRichTextBuffer::InsertObjectWithUndo(long pos
, wxRichTextObject
*object
, wxRichTextCtrl
* ctrl
, int flags
)
7615 return ctrl
->GetFocusObject()->InsertObjectWithUndo(this, pos
, object
, ctrl
, flags
);
7618 // Insert an object with no change of it
7619 wxRichTextObject
* wxRichTextParagraphLayoutBox::InsertObjectWithUndo(wxRichTextBuffer
* buffer
, long pos
, wxRichTextObject
*object
, wxRichTextCtrl
* ctrl
, int flags
)
7621 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Object"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7623 wxRichTextAttr
* p
= NULL
;
7624 wxRichTextAttr paraAttr
;
7625 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7627 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
7628 if (!paraAttr
.IsDefault())
7632 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7634 // Don't include box attributes such as margins
7635 attr
.GetTextBoxAttr().Reset();
7637 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7639 newPara
->SetAttributes(*p
);
7641 newPara
->AppendChild(object
);
7642 action
->GetNewParagraphs().AppendChild(newPara
);
7643 action
->GetNewParagraphs().UpdateRanges();
7645 action
->GetNewParagraphs().SetPartialParagraph(true);
7647 action
->SetPosition(pos
);
7649 // Set the range we'll need to delete in Undo
7650 action
->SetRange(wxRichTextRange(pos
, pos
));
7652 buffer
->SubmitAction(action
);
7654 wxRichTextObject
* obj
= GetLeafObjectAtPosition(pos
);
7658 wxRichTextField
* wxRichTextParagraphLayoutBox::InsertFieldWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxString
& fieldType
,
7659 const wxRichTextProperties
& properties
,
7660 wxRichTextCtrl
* ctrl
, int flags
,
7661 const wxRichTextAttr
& textAttr
)
7663 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Field"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7665 wxRichTextAttr
* p
= NULL
;
7666 wxRichTextAttr paraAttr
;
7667 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7669 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
7670 if (!paraAttr
.IsDefault())
7674 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7676 // Don't include box attributes such as margins
7677 attr
.GetTextBoxAttr().Reset();
7679 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7681 newPara
->SetAttributes(*p
);
7683 wxRichTextField
* fieldObject
= new wxRichTextField();
7684 fieldObject
->wxRichTextObject::SetProperties(properties
);
7685 fieldObject
->SetFieldType(fieldType
);
7686 fieldObject
->SetAttributes(textAttr
);
7687 newPara
->AppendChild(fieldObject
);
7688 action
->GetNewParagraphs().AppendChild(newPara
);
7689 action
->GetNewParagraphs().UpdateRanges();
7690 action
->GetNewParagraphs().SetPartialParagraph(true);
7691 action
->SetPosition(pos
);
7693 // Set the range we'll need to delete in Undo
7694 action
->SetRange(wxRichTextRange(pos
, pos
));
7696 buffer
->SubmitAction(action
);
7698 wxRichTextField
* obj
= wxDynamicCast(GetLeafObjectAtPosition(pos
), wxRichTextField
);
7702 /// Get the style that is appropriate for a new paragraph at this position.
7703 /// If the previous paragraph has a paragraph style name, look up the next-paragraph
7705 wxRichTextAttr
wxRichTextParagraphLayoutBox::GetStyleForNewParagraph(wxRichTextBuffer
* buffer
, long pos
, bool caretPosition
, bool lookUpNewParaStyle
) const
7707 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
, caretPosition
);
7710 wxRichTextAttr attr
;
7711 bool foundAttributes
= false;
7713 // Look for a matching paragraph style
7714 if (lookUpNewParaStyle
&& !para
->GetAttributes().GetParagraphStyleName().IsEmpty() && buffer
->GetStyleSheet())
7716 wxRichTextParagraphStyleDefinition
* paraDef
= buffer
->GetStyleSheet()->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
7719 // If we're not at the end of the paragraph, then we apply THIS style, and not the designated next style.
7720 if (para
->GetRange().GetEnd() == pos
&& !paraDef
->GetNextStyle().IsEmpty())
7722 wxRichTextParagraphStyleDefinition
* nextParaDef
= buffer
->GetStyleSheet()->FindParagraphStyle(paraDef
->GetNextStyle());
7725 foundAttributes
= true;
7726 attr
= nextParaDef
->GetStyleMergedWithBase(buffer
->GetStyleSheet());
7730 // If we didn't find the 'next style', use this style instead.
7731 if (!foundAttributes
)
7733 foundAttributes
= true;
7734 attr
= paraDef
->GetStyleMergedWithBase(buffer
->GetStyleSheet());
7739 // Also apply list style if present
7740 if (lookUpNewParaStyle
&& !para
->GetAttributes().GetListStyleName().IsEmpty() && buffer
->GetStyleSheet())
7742 wxRichTextListStyleDefinition
* listDef
= buffer
->GetStyleSheet()->FindListStyle(para
->GetAttributes().GetListStyleName());
7745 int thisIndent
= para
->GetAttributes().GetLeftIndent();
7746 int thisLevel
= para
->GetAttributes().HasOutlineLevel() ? para
->GetAttributes().GetOutlineLevel() : listDef
->FindLevelForIndent(thisIndent
);
7748 // Apply the overall list style, and item style for this level
7749 wxRichTextAttr
listStyle(listDef
->GetCombinedStyleForLevel(thisLevel
, buffer
->GetStyleSheet()));
7750 wxRichTextApplyStyle(attr
, listStyle
);
7751 attr
.SetOutlineLevel(thisLevel
);
7752 if (para
->GetAttributes().HasBulletNumber())
7753 attr
.SetBulletNumber(para
->GetAttributes().GetBulletNumber());
7757 if (!foundAttributes
)
7759 attr
= para
->GetAttributes();
7760 int flags
= attr
.GetFlags();
7762 // Eliminate character styles
7763 flags
&= ( (~ wxTEXT_ATTR_FONT
) |
7764 (~ wxTEXT_ATTR_TEXT_COLOUR
) |
7765 (~ wxTEXT_ATTR_BACKGROUND_COLOUR
) );
7766 attr
.SetFlags(flags
);
7772 return wxRichTextAttr();
7775 /// Submit command to delete this range
7776 bool wxRichTextBuffer::DeleteRangeWithUndo(const wxRichTextRange
& range
, wxRichTextCtrl
* ctrl
)
7778 return ctrl
->GetFocusObject()->DeleteRangeWithUndo(range
, ctrl
, this);
7781 /// Submit command to delete this range
7782 bool wxRichTextParagraphLayoutBox::DeleteRangeWithUndo(const wxRichTextRange
& range
, wxRichTextCtrl
* ctrl
, wxRichTextBuffer
* buffer
)
7784 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Delete"), wxRICHTEXT_DELETE
, buffer
, this, ctrl
);
7786 action
->SetPosition(ctrl
->GetCaretPosition());
7788 // Set the range to delete
7789 action
->SetRange(range
);
7791 // Copy the fragment that we'll need to restore in Undo
7792 CopyFragment(range
, action
->GetOldParagraphs());
7794 // See if we're deleting a paragraph marker, in which case we need to
7795 // make a note not to copy the attributes from the 2nd paragraph to the 1st.
7796 if (range
.GetStart() == range
.GetEnd())
7798 wxRichTextParagraph
* para
= GetParagraphAtPosition(range
.GetStart());
7799 if (para
&& para
->GetRange().GetEnd() == range
.GetEnd())
7801 wxRichTextParagraph
* nextPara
= GetParagraphAtPosition(range
.GetStart()+1);
7802 if (nextPara
&& nextPara
!= para
)
7804 action
->GetOldParagraphs().GetChildren().GetFirst()->GetData()->SetAttributes(nextPara
->GetAttributes());
7805 action
->GetOldParagraphs().GetAttributes().SetFlags(action
->GetOldParagraphs().GetAttributes().GetFlags() | wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
);
7810 buffer
->SubmitAction(action
);
7815 /// Collapse undo/redo commands
7816 bool wxRichTextBuffer::BeginBatchUndo(const wxString
& cmdName
)
7818 if (m_batchedCommandDepth
== 0)
7820 wxASSERT(m_batchedCommand
== NULL
);
7821 if (m_batchedCommand
)
7823 GetCommandProcessor()->Store(m_batchedCommand
);
7825 m_batchedCommand
= new wxRichTextCommand(cmdName
);
7828 m_batchedCommandDepth
++;
7833 /// Collapse undo/redo commands
7834 bool wxRichTextBuffer::EndBatchUndo()
7836 m_batchedCommandDepth
--;
7838 wxASSERT(m_batchedCommandDepth
>= 0);
7839 wxASSERT(m_batchedCommand
!= NULL
);
7841 if (m_batchedCommandDepth
== 0)
7843 GetCommandProcessor()->Store(m_batchedCommand
);
7844 m_batchedCommand
= NULL
;
7850 /// Submit immediately, or delay according to whether collapsing is on
7851 bool wxRichTextBuffer::SubmitAction(wxRichTextAction
* action
)
7853 if (action
&& !action
->GetNewParagraphs().IsEmpty())
7854 PrepareContent(action
->GetNewParagraphs());
7856 if (BatchingUndo() && m_batchedCommand
&& !SuppressingUndo())
7858 wxRichTextCommand
* cmd
= new wxRichTextCommand(action
->GetName());
7859 cmd
->AddAction(action
);
7861 cmd
->GetActions().Clear();
7864 m_batchedCommand
->AddAction(action
);
7868 wxRichTextCommand
* cmd
= new wxRichTextCommand(action
->GetName());
7869 cmd
->AddAction(action
);
7871 // Only store it if we're not suppressing undo.
7872 return GetCommandProcessor()->Submit(cmd
, !SuppressingUndo());
7878 /// Begin suppressing undo/redo commands.
7879 bool wxRichTextBuffer::BeginSuppressUndo()
7886 /// End suppressing undo/redo commands.
7887 bool wxRichTextBuffer::EndSuppressUndo()
7894 /// Begin using a style
7895 bool wxRichTextBuffer::BeginStyle(const wxRichTextAttr
& style
)
7897 wxRichTextAttr
newStyle(GetDefaultStyle());
7898 newStyle
.GetTextBoxAttr().Reset();
7900 // Save the old default style
7901 m_attributeStack
.Append((wxObject
*) new wxRichTextAttr(newStyle
));
7903 wxRichTextApplyStyle(newStyle
, style
);
7904 newStyle
.SetFlags(style
.GetFlags()|newStyle
.GetFlags());
7906 SetDefaultStyle(newStyle
);
7912 bool wxRichTextBuffer::EndStyle()
7914 if (!m_attributeStack
.GetFirst())
7916 wxLogDebug(_("Too many EndStyle calls!"));
7920 wxList::compatibility_iterator node
= m_attributeStack
.GetLast();
7921 wxRichTextAttr
* attr
= (wxRichTextAttr
*)node
->GetData();
7922 m_attributeStack
.Erase(node
);
7924 SetDefaultStyle(*attr
);
7931 bool wxRichTextBuffer::EndAllStyles()
7933 while (m_attributeStack
.GetCount() != 0)
7938 /// Clear the style stack
7939 void wxRichTextBuffer::ClearStyleStack()
7941 for (wxList::compatibility_iterator node
= m_attributeStack
.GetFirst(); node
; node
= node
->GetNext())
7942 delete (wxRichTextAttr
*) node
->GetData();
7943 m_attributeStack
.Clear();
7946 /// Begin using bold
7947 bool wxRichTextBuffer::BeginBold()
7949 wxRichTextAttr attr
;
7950 attr
.SetFontWeight(wxFONTWEIGHT_BOLD
);
7952 return BeginStyle(attr
);
7955 /// Begin using italic
7956 bool wxRichTextBuffer::BeginItalic()
7958 wxRichTextAttr attr
;
7959 attr
.SetFontStyle(wxFONTSTYLE_ITALIC
);
7961 return BeginStyle(attr
);
7964 /// Begin using underline
7965 bool wxRichTextBuffer::BeginUnderline()
7967 wxRichTextAttr attr
;
7968 attr
.SetFontUnderlined(true);
7970 return BeginStyle(attr
);
7973 /// Begin using point size
7974 bool wxRichTextBuffer::BeginFontSize(int pointSize
)
7976 wxRichTextAttr attr
;
7977 attr
.SetFontSize(pointSize
);
7979 return BeginStyle(attr
);
7982 /// Begin using this font
7983 bool wxRichTextBuffer::BeginFont(const wxFont
& font
)
7985 wxRichTextAttr attr
;
7988 return BeginStyle(attr
);
7991 /// Begin using this colour
7992 bool wxRichTextBuffer::BeginTextColour(const wxColour
& colour
)
7994 wxRichTextAttr attr
;
7995 attr
.SetFlags(wxTEXT_ATTR_TEXT_COLOUR
);
7996 attr
.SetTextColour(colour
);
7998 return BeginStyle(attr
);
8001 /// Begin using alignment
8002 bool wxRichTextBuffer::BeginAlignment(wxTextAttrAlignment alignment
)
8004 wxRichTextAttr attr
;
8005 attr
.SetFlags(wxTEXT_ATTR_ALIGNMENT
);
8006 attr
.SetAlignment(alignment
);
8008 return BeginStyle(attr
);
8011 /// Begin left indent
8012 bool wxRichTextBuffer::BeginLeftIndent(int leftIndent
, int leftSubIndent
)
8014 wxRichTextAttr attr
;
8015 attr
.SetFlags(wxTEXT_ATTR_LEFT_INDENT
);
8016 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
8018 return BeginStyle(attr
);
8021 /// Begin right indent
8022 bool wxRichTextBuffer::BeginRightIndent(int rightIndent
)
8024 wxRichTextAttr attr
;
8025 attr
.SetFlags(wxTEXT_ATTR_RIGHT_INDENT
);
8026 attr
.SetRightIndent(rightIndent
);
8028 return BeginStyle(attr
);
8031 /// Begin paragraph spacing
8032 bool wxRichTextBuffer::BeginParagraphSpacing(int before
, int after
)
8036 flags
|= wxTEXT_ATTR_PARA_SPACING_BEFORE
;
8038 flags
|= wxTEXT_ATTR_PARA_SPACING_AFTER
;
8040 wxRichTextAttr attr
;
8041 attr
.SetFlags(flags
);
8042 attr
.SetParagraphSpacingBefore(before
);
8043 attr
.SetParagraphSpacingAfter(after
);
8045 return BeginStyle(attr
);
8048 /// Begin line spacing
8049 bool wxRichTextBuffer::BeginLineSpacing(int lineSpacing
)
8051 wxRichTextAttr attr
;
8052 attr
.SetFlags(wxTEXT_ATTR_LINE_SPACING
);
8053 attr
.SetLineSpacing(lineSpacing
);
8055 return BeginStyle(attr
);
8058 /// Begin numbered bullet
8059 bool wxRichTextBuffer::BeginNumberedBullet(int bulletNumber
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
8061 wxRichTextAttr attr
;
8062 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
8063 attr
.SetBulletStyle(bulletStyle
);
8064 attr
.SetBulletNumber(bulletNumber
);
8065 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
8067 return BeginStyle(attr
);
8070 /// Begin symbol bullet
8071 bool wxRichTextBuffer::BeginSymbolBullet(const wxString
& symbol
, 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
.SetLeftIndent(leftIndent
, leftSubIndent
);
8077 attr
.SetBulletText(symbol
);
8079 return BeginStyle(attr
);
8082 /// Begin standard bullet
8083 bool wxRichTextBuffer::BeginStandardBullet(const wxString
& bulletName
, 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
.SetBulletName(bulletName
);
8091 return BeginStyle(attr
);
8094 /// Begin named character style
8095 bool wxRichTextBuffer::BeginCharacterStyle(const wxString
& characterStyle
)
8097 if (GetStyleSheet())
8099 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->FindCharacterStyle(characterStyle
);
8102 wxRichTextAttr attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
8103 return BeginStyle(attr
);
8109 /// Begin named paragraph style
8110 bool wxRichTextBuffer::BeginParagraphStyle(const wxString
& paragraphStyle
)
8112 if (GetStyleSheet())
8114 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(paragraphStyle
);
8117 wxRichTextAttr attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
8118 return BeginStyle(attr
);
8124 /// Begin named list style
8125 bool wxRichTextBuffer::BeginListStyle(const wxString
& listStyle
, int level
, int number
)
8127 if (GetStyleSheet())
8129 wxRichTextListStyleDefinition
* def
= GetStyleSheet()->FindListStyle(listStyle
);
8132 wxRichTextAttr
attr(def
->GetCombinedStyleForLevel(level
));
8134 attr
.SetBulletNumber(number
);
8136 return BeginStyle(attr
);
8143 bool wxRichTextBuffer::BeginURL(const wxString
& url
, const wxString
& characterStyle
)
8145 wxRichTextAttr attr
;
8147 if (!characterStyle
.IsEmpty() && GetStyleSheet())
8149 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->FindCharacterStyle(characterStyle
);
8152 attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
8157 return BeginStyle(attr
);
8160 /// Adds a handler to the end
8161 void wxRichTextBuffer::AddHandler(wxRichTextFileHandler
*handler
)
8163 sm_handlers
.Append(handler
);
8166 /// Inserts a handler at the front
8167 void wxRichTextBuffer::InsertHandler(wxRichTextFileHandler
*handler
)
8169 sm_handlers
.Insert( handler
);
8172 /// Removes a handler
8173 bool wxRichTextBuffer::RemoveHandler(const wxString
& name
)
8175 wxRichTextFileHandler
*handler
= FindHandler(name
);
8178 sm_handlers
.DeleteObject(handler
);
8186 /// Finds a handler by filename or, if supplied, type
8187 wxRichTextFileHandler
*wxRichTextBuffer::FindHandlerFilenameOrType(const wxString
& filename
,
8188 wxRichTextFileType imageType
)
8190 if (imageType
!= wxRICHTEXT_TYPE_ANY
)
8191 return FindHandler(imageType
);
8192 else if (!filename
.IsEmpty())
8194 wxString path
, file
, ext
;
8195 wxFileName::SplitPath(filename
, & path
, & file
, & ext
);
8196 return FindHandler(ext
, imageType
);
8203 /// Finds a handler by name
8204 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(const wxString
& name
)
8206 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
8209 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
8210 if (handler
->GetName().Lower() == name
.Lower()) return handler
;
8212 node
= node
->GetNext();
8217 /// Finds a handler by extension and type
8218 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(const wxString
& extension
, wxRichTextFileType type
)
8220 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
8223 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
8224 if ( handler
->GetExtension().Lower() == extension
.Lower() &&
8225 (type
== wxRICHTEXT_TYPE_ANY
|| handler
->GetType() == type
) )
8227 node
= node
->GetNext();
8232 /// Finds a handler by type
8233 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(wxRichTextFileType type
)
8235 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
8238 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
8239 if (handler
->GetType() == type
) return handler
;
8240 node
= node
->GetNext();
8245 void wxRichTextBuffer::InitStandardHandlers()
8247 if (!FindHandler(wxRICHTEXT_TYPE_TEXT
))
8248 AddHandler(new wxRichTextPlainTextHandler
);
8251 void wxRichTextBuffer::CleanUpHandlers()
8253 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
8256 wxRichTextFileHandler
* handler
= (wxRichTextFileHandler
*)node
->GetData();
8257 wxList::compatibility_iterator next
= node
->GetNext();
8262 sm_handlers
.Clear();
8265 wxString
wxRichTextBuffer::GetExtWildcard(bool combine
, bool save
, wxArrayInt
* types
)
8272 wxList::compatibility_iterator node
= GetHandlers().GetFirst();
8276 wxRichTextFileHandler
* handler
= (wxRichTextFileHandler
*) node
->GetData();
8277 if (handler
->IsVisible() && ((save
&& handler
->CanSave()) || (!save
&& handler
->CanLoad())))
8282 wildcard
+= wxT(";");
8283 wildcard
+= wxT("*.") + handler
->GetExtension();
8288 wildcard
+= wxT("|");
8289 wildcard
+= handler
->GetName();
8290 wildcard
+= wxT(" ");
8291 wildcard
+= _("files");
8292 wildcard
+= wxT(" (*.");
8293 wildcard
+= handler
->GetExtension();
8294 wildcard
+= wxT(")|*.");
8295 wildcard
+= handler
->GetExtension();
8297 types
->Add(handler
->GetType());
8302 node
= node
->GetNext();
8306 wildcard
= wxT("(") + wildcard
+ wxT(")|") + wildcard
;
8311 bool wxRichTextBuffer::LoadFile(const wxString
& filename
, wxRichTextFileType type
)
8313 wxRichTextFileHandler
* handler
= FindHandlerFilenameOrType(filename
, type
);
8316 SetDefaultStyle(wxRichTextAttr());
8317 handler
->SetFlags(GetHandlerFlags());
8318 bool success
= handler
->LoadFile(this, filename
);
8319 Invalidate(wxRICHTEXT_ALL
);
8327 bool wxRichTextBuffer::SaveFile(const wxString
& filename
, wxRichTextFileType type
)
8329 wxRichTextFileHandler
* handler
= FindHandlerFilenameOrType(filename
, type
);
8332 handler
->SetFlags(GetHandlerFlags());
8333 return handler
->SaveFile(this, filename
);
8339 /// Load from a stream
8340 bool wxRichTextBuffer::LoadFile(wxInputStream
& stream
, wxRichTextFileType type
)
8342 wxRichTextFileHandler
* handler
= FindHandler(type
);
8345 SetDefaultStyle(wxRichTextAttr());
8346 handler
->SetFlags(GetHandlerFlags());
8347 bool success
= handler
->LoadFile(this, stream
);
8348 Invalidate(wxRICHTEXT_ALL
);
8355 /// Save to a stream
8356 bool wxRichTextBuffer::SaveFile(wxOutputStream
& stream
, wxRichTextFileType type
)
8358 wxRichTextFileHandler
* handler
= FindHandler(type
);
8361 handler
->SetFlags(GetHandlerFlags());
8362 return handler
->SaveFile(this, stream
);
8368 /// Copy the range to the clipboard
8369 bool wxRichTextBuffer::CopyToClipboard(const wxRichTextRange
& range
)
8371 bool success
= false;
8372 wxRichTextParagraphLayoutBox
* container
= this;
8373 if (GetRichTextCtrl())
8374 container
= GetRichTextCtrl()->GetFocusObject();
8376 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
8378 if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open())
8380 wxTheClipboard
->Clear();
8382 // Add composite object
8384 wxDataObjectComposite
* compositeObject
= new wxDataObjectComposite();
8387 wxString text
= container
->GetTextForRange(range
);
8390 text
= wxTextFile::Translate(text
, wxTextFileType_Dos
);
8393 compositeObject
->Add(new wxTextDataObject(text
), false /* not preferred */);
8396 // Add rich text buffer data object. This needs the XML handler to be present.
8398 if (FindHandler(wxRICHTEXT_TYPE_XML
))
8400 wxRichTextBuffer
* richTextBuf
= new wxRichTextBuffer
;
8401 container
->CopyFragment(range
, *richTextBuf
);
8403 compositeObject
->Add(new wxRichTextBufferDataObject(richTextBuf
), true /* preferred */);
8406 if (wxTheClipboard
->SetData(compositeObject
))
8409 wxTheClipboard
->Close();
8418 /// Paste the clipboard content to the buffer
8419 bool wxRichTextBuffer::PasteFromClipboard(long position
)
8421 bool success
= false;
8422 wxRichTextParagraphLayoutBox
* container
= this;
8423 if (GetRichTextCtrl())
8424 container
= GetRichTextCtrl()->GetFocusObject();
8426 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
8427 if (CanPasteFromClipboard())
8429 if (wxTheClipboard
->Open())
8431 if (wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())))
8433 wxRichTextBufferDataObject data
;
8434 wxTheClipboard
->GetData(data
);
8435 wxRichTextBuffer
* richTextBuffer
= data
.GetRichTextBuffer();
8438 container
->InsertParagraphsWithUndo(this, position
+1, *richTextBuffer
, GetRichTextCtrl(), 0);
8439 if (GetRichTextCtrl())
8440 GetRichTextCtrl()->ShowPosition(position
+ richTextBuffer
->GetOwnRange().GetEnd());
8441 delete richTextBuffer
;
8444 else if (wxTheClipboard
->IsSupported(wxDF_TEXT
)
8446 || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
)
8450 wxTextDataObject data
;
8451 wxTheClipboard
->GetData(data
);
8452 wxString
text(data
.GetText());
8455 text2
.Alloc(text
.Length()+1);
8457 for (i
= 0; i
< text
.Length(); i
++)
8459 wxChar ch
= text
[i
];
8460 if (ch
!= wxT('\r'))
8464 wxString text2
= text
;
8466 container
->InsertTextWithUndo(this, position
+1, text2
, GetRichTextCtrl(), wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
);
8468 if (GetRichTextCtrl())
8469 GetRichTextCtrl()->ShowPosition(position
+ text2
.Length());
8473 else if (wxTheClipboard
->IsSupported(wxDF_BITMAP
))
8475 wxBitmapDataObject data
;
8476 wxTheClipboard
->GetData(data
);
8477 wxBitmap
bitmap(data
.GetBitmap());
8478 wxImage
image(bitmap
.ConvertToImage());
8480 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, this, container
, GetRichTextCtrl(), false);
8482 action
->GetNewParagraphs().AddImage(image
);
8484 if (action
->GetNewParagraphs().GetChildCount() == 1)
8485 action
->GetNewParagraphs().SetPartialParagraph(true);
8487 action
->SetPosition(position
+1);
8489 // Set the range we'll need to delete in Undo
8490 action
->SetRange(wxRichTextRange(position
+1, position
+1));
8492 SubmitAction(action
);
8496 wxTheClipboard
->Close();
8500 wxUnusedVar(position
);
8505 /// Can we paste from the clipboard?
8506 bool wxRichTextBuffer::CanPasteFromClipboard() const
8508 bool canPaste
= false;
8509 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
8510 if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open())
8512 if (wxTheClipboard
->IsSupported(wxDF_TEXT
)
8514 || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
)
8516 || wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())) ||
8517 wxTheClipboard
->IsSupported(wxDF_BITMAP
))
8521 wxTheClipboard
->Close();
8527 /// Dumps contents of buffer for debugging purposes
8528 void wxRichTextBuffer::Dump()
8532 wxStringOutputStream
stream(& text
);
8533 wxTextOutputStream
textStream(stream
);
8540 /// Add an event handler
8541 bool wxRichTextBuffer::AddEventHandler(wxEvtHandler
* handler
)
8543 m_eventHandlers
.Append(handler
);
8547 /// Remove an event handler
8548 bool wxRichTextBuffer::RemoveEventHandler(wxEvtHandler
* handler
, bool deleteHandler
)
8550 wxList::compatibility_iterator node
= m_eventHandlers
.Find(handler
);
8553 m_eventHandlers
.Erase(node
);
8563 /// Clear event handlers
8564 void wxRichTextBuffer::ClearEventHandlers()
8566 m_eventHandlers
.Clear();
8569 /// Send event to event handlers. If sendToAll is true, will send to all event handlers,
8570 /// otherwise will stop at the first successful one.
8571 bool wxRichTextBuffer::SendEvent(wxEvent
& event
, bool sendToAll
)
8573 bool success
= false;
8574 for (wxList::compatibility_iterator node
= m_eventHandlers
.GetFirst(); node
; node
= node
->GetNext())
8576 wxEvtHandler
* handler
= (wxEvtHandler
*) node
->GetData();
8577 if (handler
->ProcessEvent(event
))
8587 /// Set style sheet and notify of the change
8588 bool wxRichTextBuffer::SetStyleSheetAndNotify(wxRichTextStyleSheet
* sheet
)
8590 wxRichTextStyleSheet
* oldSheet
= GetStyleSheet();
8592 wxWindowID winid
= wxID_ANY
;
8593 if (GetRichTextCtrl())
8594 winid
= GetRichTextCtrl()->GetId();
8596 wxRichTextEvent
event(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACING
, winid
);
8597 event
.SetEventObject(GetRichTextCtrl());
8598 event
.SetContainer(GetRichTextCtrl()->GetFocusObject());
8599 event
.SetOldStyleSheet(oldSheet
);
8600 event
.SetNewStyleSheet(sheet
);
8603 if (SendEvent(event
) && !event
.IsAllowed())
8605 if (sheet
!= oldSheet
)
8611 if (oldSheet
&& oldSheet
!= sheet
)
8614 SetStyleSheet(sheet
);
8616 event
.SetEventType(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACED
);
8617 event
.SetOldStyleSheet(NULL
);
8620 return SendEvent(event
);
8623 /// Set renderer, deleting old one
8624 void wxRichTextBuffer::SetRenderer(wxRichTextRenderer
* renderer
)
8628 sm_renderer
= renderer
;
8631 /// Hit-testing: returns a flag indicating hit test details, plus
8632 /// information about position
8633 int wxRichTextBuffer::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
8635 int ret
= wxRichTextParagraphLayoutBox::HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
8636 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
8642 textPosition
= m_ownRange
.GetEnd()-1;
8645 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
;
8649 void wxRichTextBuffer::SetFontScale(double fontScale
)
8651 m_fontScale
= fontScale
;
8652 m_fontTable
.SetFontScale(fontScale
);
8655 void wxRichTextBuffer::SetDimensionScale(double dimScale
)
8657 m_dimensionScale
= dimScale
;
8660 bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxRichTextAttr
& bulletAttr
, const wxRect
& rect
)
8662 if (bulletAttr
.GetTextColour().IsOk())
8664 wxCheckSetPen(dc
, wxPen(bulletAttr
.GetTextColour()));
8665 wxCheckSetBrush(dc
, wxBrush(bulletAttr
.GetTextColour()));
8669 wxCheckSetPen(dc
, *wxBLACK_PEN
);
8670 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
8674 if (bulletAttr
.HasFont())
8676 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(bulletAttr
);
8679 font
= (*wxNORMAL_FONT
);
8681 wxCheckSetFont(dc
, font
);
8683 int charHeight
= dc
.GetCharHeight();
8685 int bulletWidth
= (int) (((float) charHeight
) * wxRichTextBuffer::GetBulletProportion());
8686 int bulletHeight
= bulletWidth
;
8690 // Calculate the top position of the character (as opposed to the whole line height)
8691 int y
= rect
.y
+ (rect
.height
- charHeight
);
8693 // Calculate where the bullet should be positioned
8694 y
= y
+ (charHeight
+1)/2 - (bulletHeight
+1)/2;
8696 // The margin between a bullet and text.
8697 int margin
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer::GetBulletRightMargin());
8699 if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
)
8700 x
= rect
.x
+ rect
.width
- bulletWidth
- margin
;
8701 else if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
)
8702 x
= x
+ (rect
.width
)/2 - bulletWidth
/2;
8704 if (bulletAttr
.GetBulletName() == wxT("standard/square"))
8706 dc
.DrawRectangle(x
, y
, bulletWidth
, bulletHeight
);
8708 else if (bulletAttr
.GetBulletName() == wxT("standard/diamond"))
8711 pts
[0].x
= x
; pts
[0].y
= y
+ bulletHeight
/2;
8712 pts
[1].x
= x
+ bulletWidth
/2; pts
[1].y
= y
;
8713 pts
[2].x
= x
+ bulletWidth
; pts
[2].y
= y
+ bulletHeight
/2;
8714 pts
[3].x
= x
+ bulletWidth
/2; pts
[3].y
= y
+ bulletHeight
;
8716 dc
.DrawPolygon(4, pts
);
8718 else if (bulletAttr
.GetBulletName() == wxT("standard/triangle"))
8721 pts
[0].x
= x
; pts
[0].y
= y
;
8722 pts
[1].x
= x
+ bulletWidth
; pts
[1].y
= y
+ bulletHeight
/2;
8723 pts
[2].x
= x
; pts
[2].y
= y
+ bulletHeight
;
8725 dc
.DrawPolygon(3, pts
);
8727 else if (bulletAttr
.GetBulletName() == wxT("standard/circle-outline"))
8729 wxCheckSetBrush(dc
, *wxTRANSPARENT_BRUSH
);
8730 dc
.DrawEllipse(x
, y
, bulletWidth
, bulletHeight
);
8732 else // "standard/circle", and catch-all
8734 dc
.DrawEllipse(x
, y
, bulletWidth
, bulletHeight
);
8740 bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxRichTextAttr
& attr
, const wxRect
& rect
, const wxString
& text
)
8745 if ((attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
) && !attr
.GetBulletFont().IsEmpty() && attr
.HasFont())
8747 wxRichTextAttr fontAttr
;
8748 if (attr
.HasFontPixelSize())
8749 fontAttr
.SetFontPixelSize(attr
.GetFontSize());
8751 fontAttr
.SetFontPointSize(attr
.GetFontSize());
8752 fontAttr
.SetFontStyle(attr
.GetFontStyle());
8753 fontAttr
.SetFontWeight(attr
.GetFontWeight());
8754 fontAttr
.SetFontUnderlined(attr
.GetFontUnderlined());
8755 fontAttr
.SetFontFaceName(attr
.GetBulletFont());
8756 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(fontAttr
);
8758 else if (attr
.HasFont())
8759 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(attr
);
8761 font
= (*wxNORMAL_FONT
);
8763 wxCheckSetFont(dc
, font
);
8765 if (attr
.GetTextColour().IsOk())
8766 dc
.SetTextForeground(attr
.GetTextColour());
8768 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
8770 int charHeight
= dc
.GetCharHeight();
8772 dc
.GetTextExtent(text
, & tw
, & th
);
8776 // Calculate the top position of the character (as opposed to the whole line height)
8777 int y
= rect
.y
+ (rect
.height
- charHeight
);
8779 // The margin between a bullet and text.
8780 int margin
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer::GetBulletRightMargin());
8782 if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
)
8783 x
= (rect
.x
+ rect
.width
) - tw
- margin
;
8784 else if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
)
8785 x
= x
+ (rect
.width
)/2 - tw
/2;
8787 dc
.DrawText(text
, x
, y
);
8795 bool wxRichTextStdRenderer::DrawBitmapBullet(wxRichTextParagraph
* WXUNUSED(paragraph
), wxDC
& WXUNUSED(dc
), const wxRichTextAttr
& WXUNUSED(attr
), const wxRect
& WXUNUSED(rect
))
8797 // Currently unimplemented. The intention is to store bitmaps by name in a media store associated
8798 // with the buffer. The store will allow retrieval from memory, disk or other means.
8802 /// Enumerate the standard bullet names currently supported
8803 bool wxRichTextStdRenderer::EnumerateStandardBulletNames(wxArrayString
& bulletNames
)
8805 bulletNames
.Add(wxTRANSLATE("standard/circle"));
8806 bulletNames
.Add(wxTRANSLATE("standard/circle-outline"));
8807 bulletNames
.Add(wxTRANSLATE("standard/square"));
8808 bulletNames
.Add(wxTRANSLATE("standard/diamond"));
8809 bulletNames
.Add(wxTRANSLATE("standard/triangle"));
8818 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBox
, wxRichTextParagraphLayoutBox
)
8820 wxRichTextBox::wxRichTextBox(wxRichTextObject
* parent
):
8821 wxRichTextParagraphLayoutBox(parent
)
8826 bool wxRichTextBox::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8831 // TODO: if the active object in the control, draw an indication.
8832 // We need to add the concept of active object, and not just focus object,
8833 // so we can apply commands (properties, delete, ...) to objects such as text boxes and images.
8834 // Ultimately we would like to be able to interactively resize an active object
8835 // using drag handles.
8836 return wxRichTextParagraphLayoutBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8840 void wxRichTextBox::Copy(const wxRichTextBox
& obj
)
8842 wxRichTextParagraphLayoutBox::Copy(obj
);
8845 // Edit properties via a GUI
8846 bool wxRichTextBox::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8848 wxRichTextObjectPropertiesDialog
boxDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Box Properties"));
8849 boxDlg
.SetAttributes(GetAttributes());
8851 if (boxDlg
.ShowModal() == wxID_OK
)
8853 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
8854 // indeterminate in the object.
8855 boxDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
8866 IMPLEMENT_DYNAMIC_CLASS(wxRichTextField
, wxRichTextParagraphLayoutBox
)
8868 wxRichTextField::wxRichTextField(const wxString
& fieldType
, wxRichTextObject
* parent
):
8869 wxRichTextParagraphLayoutBox(parent
)
8871 SetFieldType(fieldType
);
8875 bool wxRichTextField::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8880 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8881 if (fieldType
&& fieldType
->Draw(this, dc
, context
, range
, selection
, rect
, descent
, style
))
8884 // Fallback; but don't draw guidelines.
8885 style
&= ~wxRICHTEXT_DRAW_GUIDELINES
;
8886 return wxRichTextParagraphLayoutBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8889 bool wxRichTextField::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
)
8891 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8892 if (fieldType
&& fieldType
->Layout(this, dc
, context
, rect
, parentRect
, style
))
8896 return wxRichTextParagraphLayoutBox::Layout(dc
, context
, rect
, parentRect
, style
);
8899 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
8901 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8903 return fieldType
->GetRangeSize((wxRichTextField
*) this, range
, size
, descent
, dc
, context
, flags
, position
, parentSize
, partialExtents
);
8905 return wxRichTextParagraphLayoutBox::GetRangeSize(range
, size
, descent
, dc
, context
, flags
, position
, parentSize
, partialExtents
);
8909 void wxRichTextField::CalculateRange(long start
, long& end
)
8912 wxRichTextParagraphLayoutBox::CalculateRange(start
, end
);
8914 wxRichTextObject::CalculateRange(start
, end
);
8918 void wxRichTextField::Copy(const wxRichTextField
& obj
)
8920 wxRichTextParagraphLayoutBox::Copy(obj
);
8922 UpdateField(GetBuffer());
8925 // Edit properties via a GUI
8926 bool wxRichTextField::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8928 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8930 return fieldType
->EditProperties(this, parent
, buffer
);
8935 bool wxRichTextField::CanEditProperties() const
8937 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8939 return fieldType
->CanEditProperties((wxRichTextField
*) this);
8944 wxString
wxRichTextField::GetPropertiesMenuLabel() const
8946 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8948 return fieldType
->GetPropertiesMenuLabel((wxRichTextField
*) this);
8950 return wxEmptyString
;
8953 bool wxRichTextField::UpdateField(wxRichTextBuffer
* buffer
)
8955 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8957 return fieldType
->UpdateField(buffer
, (wxRichTextField
*) this);
8962 bool wxRichTextField::IsTopLevel() const
8964 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8966 return fieldType
->IsTopLevel((wxRichTextField
*) this);
8971 IMPLEMENT_CLASS(wxRichTextFieldType
, wxObject
)
8973 IMPLEMENT_CLASS(wxRichTextFieldTypeStandard
, wxRichTextFieldType
)
8975 wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString
& name
, const wxString
& label
, int displayStyle
)
8981 SetDisplayStyle(displayStyle
);
8984 wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString
& name
, const wxBitmap
& bitmap
, int displayStyle
)
8990 SetDisplayStyle(displayStyle
);
8993 void wxRichTextFieldTypeStandard::Init()
8995 m_displayStyle
= wxRICHTEXT_FIELD_STYLE_RECTANGLE
;
8996 m_font
= wxFont(6, wxFONTFAMILY_SWISS
, wxFONTSTYLE_NORMAL
, wxFONTWEIGHT_NORMAL
);
8997 m_textColour
= *wxWHITE
;
8998 m_borderColour
= *wxBLACK
;
8999 m_backgroundColour
= *wxBLACK
;
9000 m_verticalPadding
= 1;
9001 m_horizontalPadding
= 3;
9002 m_horizontalMargin
= 2;
9003 m_verticalMargin
= 0;
9006 void wxRichTextFieldTypeStandard::Copy(const wxRichTextFieldTypeStandard
& field
)
9008 wxRichTextFieldType::Copy(field
);
9010 m_label
= field
.m_label
;
9011 m_displayStyle
= field
.m_displayStyle
;
9012 m_font
= field
.m_font
;
9013 m_textColour
= field
.m_textColour
;
9014 m_borderColour
= field
.m_borderColour
;
9015 m_backgroundColour
= field
.m_backgroundColour
;
9016 m_verticalPadding
= field
.m_verticalPadding
;
9017 m_horizontalPadding
= field
.m_horizontalPadding
;
9018 m_horizontalMargin
= field
.m_horizontalMargin
;
9019 m_bitmap
= field
.m_bitmap
;
9022 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
))
9024 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_COMPOSITE
)
9025 return false; // USe default composite drawing
9026 else // if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_RECTANGLE || m_displayStyle == wxRICHTEXT_FIELD_STYLE_NOBORDER)
9030 wxPen
borderPen(m_borderColour
, 1, wxSOLID
);
9031 wxBrush
backgroundBrush(m_backgroundColour
);
9032 wxColour
textColour(m_textColour
);
9034 if (selection
.WithinSelection(obj
->GetRange().GetStart(), obj
))
9036 wxColour
highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
));
9037 wxColour
highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
));
9039 borderPen
= wxPen(highlightTextColour
, 1, wxSOLID
);
9040 backgroundBrush
= wxBrush(highlightColour
);
9042 wxCheckSetBrush(dc
, backgroundBrush
);
9043 wxCheckSetPen(dc
, wxPen(highlightColour
, 1, wxSOLID
));
9044 dc
.DrawRectangle(rect
);
9047 if (m_displayStyle
!= wxRICHTEXT_FIELD_STYLE_NO_BORDER
)
9050 // objectRect is the area where the content is drawn, after margins around it have been taken into account
9051 wxRect objectRect
= wxRect(wxPoint(rect
.x
+ m_horizontalMargin
, rect
.y
+ wxMax(0, rect
.height
- descent
- obj
->GetCachedSize().y
)),
9052 wxSize(obj
->GetCachedSize().x
- 2*m_horizontalMargin
- borderSize
, obj
->GetCachedSize().y
));
9054 // clientArea is where the text is actually written
9055 wxRect clientArea
= objectRect
;
9057 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_RECTANGLE
)
9059 dc
.SetPen(borderPen
);
9060 dc
.SetBrush(backgroundBrush
);
9061 dc
.DrawRoundedRectangle(objectRect
, 4.0);
9063 else if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_START_TAG
)
9065 int arrowLength
= objectRect
.height
/2;
9066 clientArea
.width
-= (arrowLength
- m_horizontalPadding
);
9069 pts
[0].x
= objectRect
.x
; pts
[0].y
= objectRect
.y
;
9070 pts
[1].x
= objectRect
.x
+ objectRect
.width
- arrowLength
; pts
[1].y
= objectRect
.y
;
9071 pts
[2].x
= objectRect
.x
+ objectRect
.width
; pts
[2].y
= objectRect
.y
+ (objectRect
.height
/2);
9072 pts
[3].x
= objectRect
.x
+ objectRect
.width
- arrowLength
; pts
[3].y
= objectRect
.y
+ objectRect
.height
;
9073 pts
[4].x
= objectRect
.x
; pts
[4].y
= objectRect
.y
+ objectRect
.height
;
9074 dc
.SetPen(borderPen
);
9075 dc
.SetBrush(backgroundBrush
);
9076 dc
.DrawPolygon(5, pts
);
9078 else if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_END_TAG
)
9080 int arrowLength
= objectRect
.height
/2;
9081 clientArea
.width
-= (arrowLength
- m_horizontalPadding
);
9082 clientArea
.x
+= (arrowLength
- m_horizontalPadding
);
9085 pts
[0].x
= objectRect
.x
+ objectRect
.width
; pts
[0].y
= objectRect
.y
;
9086 pts
[1].x
= objectRect
.x
+ arrowLength
; pts
[1].y
= objectRect
.y
;
9087 pts
[2].x
= objectRect
.x
; pts
[2].y
= objectRect
.y
+ (objectRect
.height
/2);
9088 pts
[3].x
= objectRect
.x
+ arrowLength
; pts
[3].y
= objectRect
.y
+ objectRect
.height
;
9089 pts
[4].x
= objectRect
.x
+ objectRect
.width
; pts
[4].y
= objectRect
.y
+ objectRect
.height
;
9090 dc
.SetPen(borderPen
);
9091 dc
.SetBrush(backgroundBrush
);
9092 dc
.DrawPolygon(5, pts
);
9095 if (m_bitmap
.IsOk())
9097 int x
= clientArea
.x
+ (clientArea
.width
- m_bitmap
.GetWidth())/2;
9098 int y
= clientArea
.y
+ m_verticalPadding
;
9099 dc
.DrawBitmap(m_bitmap
, x
, y
, true);
9101 if (selection
.WithinSelection(obj
->GetRange().GetStart(), obj
))
9103 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
9104 wxCheckSetPen(dc
, *wxBLACK_PEN
);
9105 dc
.SetLogicalFunction(wxINVERT
);
9106 dc
.DrawRectangle(wxRect(x
, y
, m_bitmap
.GetWidth(), m_bitmap
.GetHeight()));
9107 dc
.SetLogicalFunction(wxCOPY
);
9112 wxString
label(m_label
);
9113 if (label
.IsEmpty())
9115 int w
, h
, maxDescent
;
9117 dc
.GetTextExtent(m_label
, & w
, &h
, & maxDescent
);
9118 dc
.SetTextForeground(textColour
);
9120 int x
= clientArea
.x
+ (clientArea
.width
- w
)/2;
9121 int y
= clientArea
.y
+ (clientArea
.height
- (h
- maxDescent
))/2;
9122 dc
.DrawText(m_label
, x
, y
);
9129 bool wxRichTextFieldTypeStandard::Layout(wxRichTextField
* obj
, wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& WXUNUSED(rect
), const wxRect
& WXUNUSED(parentRect
), int style
)
9131 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_COMPOSITE
)
9132 return false; // USe default composite layout
9134 wxSize size
= GetSize(obj
, dc
, context
, style
);
9135 obj
->SetCachedSize(size
);
9136 obj
->SetMinSize(size
);
9137 obj
->SetMaxSize(size
);
9141 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
9143 if (IsTopLevel(obj
))
9144 return obj
->wxRichTextParagraphLayoutBox::GetRangeSize(range
, size
, descent
, dc
, context
, flags
, position
, parentSize
);
9147 wxSize sz
= GetSize(obj
, dc
, context
, 0);
9151 if (partialExtents
->GetCount() > 0)
9152 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
9155 partialExtents
->Add(lastSize
+ sz
.x
);
9162 wxSize
wxRichTextFieldTypeStandard::GetSize(wxRichTextField
* WXUNUSED(obj
), wxDC
& dc
, wxRichTextDrawingContext
& WXUNUSED(context
), int WXUNUSED(style
)) const
9165 int w
= 0, h
= 0, maxDescent
= 0;
9168 if (m_bitmap
.IsOk())
9170 w
= m_bitmap
.GetWidth();
9171 h
= m_bitmap
.GetHeight();
9173 sz
= wxSize(w
+ m_horizontalMargin
*2, h
+ m_verticalMargin
*2);
9177 wxString
label(m_label
);
9178 if (label
.IsEmpty())
9181 dc
.GetTextExtent(label
, & w
, &h
, & maxDescent
);
9183 sz
= wxSize(w
+ m_horizontalPadding
*2 + m_horizontalMargin
*2, h
+ m_verticalPadding
*2 + m_verticalMargin
*2);
9186 if (m_displayStyle
!= wxRICHTEXT_FIELD_STYLE_NO_BORDER
)
9188 sz
.x
+= borderSize
*2;
9189 sz
.y
+= borderSize
*2;
9192 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_START_TAG
|| m_displayStyle
== wxRICHTEXT_FIELD_STYLE_END_TAG
)
9194 // Add space for the arrow
9195 sz
.x
+= (sz
.y
/2 - m_horizontalPadding
);
9201 IMPLEMENT_DYNAMIC_CLASS(wxRichTextCell
, wxRichTextBox
)
9203 wxRichTextCell::wxRichTextCell(wxRichTextObject
* parent
):
9204 wxRichTextBox(parent
)
9209 bool wxRichTextCell::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
9211 return wxRichTextBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
9215 void wxRichTextCell::Copy(const wxRichTextCell
& obj
)
9217 wxRichTextBox::Copy(obj
);
9220 // Edit properties via a GUI
9221 bool wxRichTextCell::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
9223 // We need to gather common attributes for all selected cells.
9225 wxRichTextTable
* table
= wxDynamicCast(GetParent(), wxRichTextTable
);
9226 bool multipleCells
= false;
9227 wxRichTextAttr attr
;
9229 if (table
&& buffer
&& buffer
->GetRichTextCtrl() && buffer
->GetRichTextCtrl()->GetSelection().IsValid() &&
9230 buffer
->GetRichTextCtrl()->GetSelection().GetContainer() == GetParent())
9232 wxRichTextAttr clashingAttr
, absentAttr
;
9233 const wxRichTextSelection
& sel
= buffer
->GetRichTextCtrl()->GetSelection();
9235 int selectedCellCount
= 0;
9236 for (i
= 0; i
< sel
.GetCount(); i
++)
9238 const wxRichTextRange
& range
= sel
[i
];
9239 wxRichTextCell
* cell
= table
->GetCell(range
.GetStart());
9242 wxRichTextAttr cellStyle
= cell
->GetAttributes();
9244 CollectStyle(attr
, cellStyle
, clashingAttr
, absentAttr
);
9246 selectedCellCount
++;
9249 multipleCells
= selectedCellCount
> 1;
9253 attr
= GetAttributes();
9258 caption
= _("Multiple Cell Properties");
9260 caption
= _("Cell Properties");
9262 wxRichTextObjectPropertiesDialog
cellDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, caption
);
9263 cellDlg
.SetAttributes(attr
);
9265 wxRichTextSizePage
* sizePage
= wxDynamicCast(cellDlg
.FindPage(wxCLASSINFO(wxRichTextSizePage
)), wxRichTextSizePage
);
9268 // We don't want position and floating controls for a cell.
9269 sizePage
->ShowPositionControls(false);
9270 sizePage
->ShowFloatingControls(false);
9273 if (cellDlg
.ShowModal() == wxID_OK
)
9277 const wxRichTextSelection
& sel
= buffer
->GetRichTextCtrl()->GetSelection();
9278 // Apply the style; we interpret indeterminate attributes as 'don't touch this attribute'
9279 // since it may represent clashing attributes across multiple objects.
9280 table
->SetCellStyle(sel
, attr
);
9283 // For a single object, indeterminate attributes set by the user should be reflected in the
9284 // actual object style, so pass the wxRICHTEXT_SETSTYLE_RESET flag to assign
9285 // the style directly instead of applying (which ignores indeterminate attributes,
9286 // leaving them as they were).
9287 cellDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
9294 WX_DEFINE_OBJARRAY(wxRichTextObjectPtrArrayArray
)
9296 IMPLEMENT_DYNAMIC_CLASS(wxRichTextTable
, wxRichTextBox
)
9298 wxRichTextTable::wxRichTextTable(wxRichTextObject
* parent
): wxRichTextBox(parent
)
9304 // Draws the object.
9305 bool wxRichTextTable::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
9307 return wxRichTextBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
9310 WX_DECLARE_OBJARRAY(wxRect
, wxRichTextRectArray
);
9311 WX_DEFINE_OBJARRAY(wxRichTextRectArray
);
9313 // Lays the object out. rect is the space available for layout. Often it will
9314 // be the specified overall space for this object, if trying to constrain
9315 // layout to a particular size, or it could be the total space available in the
9316 // parent. rect is the overall size, so we must subtract margins and padding.
9317 // to get the actual available space.
9318 bool wxRichTextTable::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& WXUNUSED(parentRect
), int style
)
9320 SetPosition(rect
.GetPosition());
9322 // TODO: the meaty bit. Calculate sizes of all cells and rows. Try to use
9323 // minimum size if within alloted size, then divide up remaining size
9324 // between rows/cols.
9327 wxRichTextBuffer
* buffer
= GetBuffer();
9328 if (buffer
) scale
= buffer
->GetScale();
9330 wxRect availableSpace
= GetAvailableContentArea(dc
, context
, rect
);
9331 wxTextAttrDimensionConverter
converter(dc
, scale
, availableSpace
.GetSize());
9333 wxRichTextAttr
attr(GetAttributes());
9334 context
.ApplyVirtualAttributes(attr
, this);
9336 // If we have no fixed table size, and assuming we're not pushed for
9337 // space, then we don't have to try to stretch the table to fit the contents.
9338 bool stretchToFitTableWidth
= false;
9340 int tableWidth
= rect
.width
;
9341 if (attr
.GetTextBoxAttr().GetWidth().IsValid())
9343 tableWidth
= converter
.GetPixels(attr
.GetTextBoxAttr().GetWidth());
9345 // Fixed table width, so we do want to stretch columns out if necessary.
9346 stretchToFitTableWidth
= true;
9348 // Shouldn't be able to exceed the size passed to this function
9349 tableWidth
= wxMin(rect
.width
, tableWidth
);
9352 // Get internal padding
9353 int paddingLeft
= 0, paddingTop
= 0;
9354 if (attr
.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
9355 paddingLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetLeft());
9356 if (attr
.GetTextBoxAttr().GetPadding().GetTop().IsValid())
9357 paddingTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetTop());
9359 // Assume that left and top padding are also used for inter-cell padding.
9360 int paddingX
= paddingLeft
;
9361 int paddingY
= paddingTop
;
9363 int totalLeftMargin
= 0, totalRightMargin
= 0, totalTopMargin
= 0, totalBottomMargin
= 0;
9364 GetTotalMargin(dc
, buffer
, attr
, totalLeftMargin
, totalRightMargin
, totalTopMargin
, totalBottomMargin
);
9366 // Internal table width - the area for content
9367 int internalTableWidth
= tableWidth
- totalLeftMargin
- totalRightMargin
;
9369 int rowCount
= m_cells
.GetCount();
9370 if (m_colCount
== 0 || rowCount
== 0)
9372 wxRect
overallRect(rect
.x
, rect
.y
, totalLeftMargin
+ totalRightMargin
, totalTopMargin
+ totalBottomMargin
);
9373 SetCachedSize(overallRect
.GetSize());
9375 // Zero content size
9376 SetMinSize(overallRect
.GetSize());
9377 SetMaxSize(GetMinSize());
9381 // The final calculated widths
9382 wxArrayInt colWidths
;
9383 colWidths
.Add(0, m_colCount
);
9385 wxArrayInt absoluteColWidths
;
9386 absoluteColWidths
.Add(0, m_colCount
);
9388 wxArrayInt percentageColWidths
;
9389 percentageColWidths
.Add(0, m_colCount
);
9390 // wxArrayInt percentageColWidthsSpanning(m_colCount);
9391 // These are only relevant when the first column contains spanning information.
9392 // wxArrayInt columnSpans(m_colCount); // Each contains 1 for non-spanning cell, > 1 for spanning cell.
9393 wxArrayInt maxColWidths
;
9394 maxColWidths
.Add(0, m_colCount
);
9395 wxArrayInt minColWidths
;
9396 minColWidths
.Add(0, m_colCount
);
9398 wxSize
tableSize(tableWidth
, 0);
9402 for (i
= 0; i
< m_colCount
; i
++)
9404 absoluteColWidths
[i
] = 0;
9405 // absoluteColWidthsSpanning[i] = 0;
9406 percentageColWidths
[i
] = -1;
9407 // percentageColWidthsSpanning[i] = -1;
9409 maxColWidths
[i
] = 0;
9410 minColWidths
[i
] = 0;
9411 // columnSpans[i] = 1;
9414 // (0) Determine which cells are visible according to spans
9416 // __________________
9421 // |------------------|
9422 // |__________________| 4
9424 // To calculate cell visibility:
9425 // First find all spanning cells. Build an array of span records with start x, y and end x, y.
9426 // Then for each cell, test whether we're within one of those cells, and unless we're at the start of
9427 // that cell, hide the cell.
9429 // We can also use this array to match the size of spanning cells to the grid. Or just do
9430 // this when we iterate through all cells.
9432 // 0.1: add spanning cells to an array
9433 wxRichTextRectArray rectArray
;
9434 for (j
= 0; j
< m_rowCount
; j
++)
9436 for (i
= 0; i
< m_colCount
; i
++)
9438 wxRichTextBox
* cell
= GetCell(j
, i
);
9439 int colSpan
= 1, rowSpan
= 1;
9440 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9441 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9442 if (cell
->GetProperties().HasProperty(wxT("rowspan")))
9443 rowSpan
= cell
->GetProperties().GetPropertyLong(wxT("rowspan"));
9444 if (colSpan
> 1 || rowSpan
> 1)
9446 rectArray
.Add(wxRect(i
, j
, colSpan
, rowSpan
));
9450 // 0.2: find which cells are subsumed by a spanning cell
9451 for (j
= 0; j
< m_rowCount
; j
++)
9453 for (i
= 0; i
< m_colCount
; i
++)
9455 wxRichTextBox
* cell
= GetCell(j
, i
);
9456 if (rectArray
.GetCount() == 0)
9462 int colSpan
= 1, rowSpan
= 1;
9463 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9464 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9465 if (cell
->GetProperties().HasProperty(wxT("rowspan")))
9466 rowSpan
= cell
->GetProperties().GetPropertyLong(wxT("rowspan"));
9467 if (colSpan
> 1 || rowSpan
> 1)
9469 // Assume all spanning cells are shown
9475 for (k
= 0; k
< (int) rectArray
.GetCount(); k
++)
9477 if (rectArray
[k
].Contains(wxPoint(i
, j
)))
9489 // TODO: find the first spanned cell in each row that spans the most columns and doesn't
9490 // overlap with a spanned cell starting at a previous column position.
9491 // This means we need to keep an array of rects so we can check. However
9492 // it does also mean that some spans simply may not be taken into account
9493 // where there are different spans happening on different rows. In these cases,
9494 // they will simply be as wide as their constituent columns.
9496 // (1) Do an initial layout for all cells to get minimum and maximum size, and get
9497 // the absolute or percentage width of each column.
9499 for (j
= 0; j
< m_rowCount
; j
++)
9501 // First get the overall margins so we can calculate percentage widths based on
9502 // the available content space for all cells on the row
9504 int overallRowContentMargin
= 0;
9505 int visibleCellCount
= 0;
9507 for (i
= 0; i
< m_colCount
; i
++)
9509 wxRichTextBox
* cell
= GetCell(j
, i
);
9510 if (cell
->IsShown())
9512 int cellTotalLeftMargin
= 0, cellTotalRightMargin
= 0, cellTotalTopMargin
= 0, cellTotalBottomMargin
= 0;
9513 GetTotalMargin(dc
, buffer
, cell
->GetAttributes(), cellTotalLeftMargin
, cellTotalRightMargin
, cellTotalTopMargin
, cellTotalBottomMargin
);
9515 overallRowContentMargin
+= (cellTotalLeftMargin
+ cellTotalRightMargin
);
9516 visibleCellCount
++;
9520 // Add in inter-cell padding
9521 overallRowContentMargin
+= ((visibleCellCount
-1) * paddingX
);
9523 int rowContentWidth
= internalTableWidth
- overallRowContentMargin
;
9524 wxSize
rowTableSize(rowContentWidth
, 0);
9525 wxTextAttrDimensionConverter
converter(dc
, scale
, rowTableSize
);
9527 for (i
= 0; i
< m_colCount
; i
++)
9529 wxRichTextBox
* cell
= GetCell(j
, i
);
9530 if (cell
->IsShown())
9533 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9534 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9536 // Lay out cell to find min/max widths
9537 cell
->Invalidate(wxRICHTEXT_ALL
);
9538 cell
->Layout(dc
, context
, availableSpace
, availableSpace
, style
);
9542 int absoluteCellWidth
= -1;
9543 int percentageCellWidth
= -1;
9545 // I think we need to calculate percentages from the internal table size,
9546 // minus the padding between cells which we'll need to calculate from the
9547 // (number of VISIBLE cells - 1)*paddingX. Then percentages that add up to 100%
9548 // will add up to 100%. In CSS, the width specifies the cell's content rect width,
9549 // so if we want to conform to that we'll need to add in the overall cell margins.
9550 // However, this will make it difficult to specify percentages that add up to
9551 // 100% and still fit within the table width.
9552 // Let's say two cells have 50% width. They have 10 pixels of overall margin each.
9553 // The table content rect is 500 pixels and the inter-cell padding is 20 pixels.
9554 // If we're using internal content size for the width, we would calculate the
9555 // the overall cell width for n cells as:
9556 // (500 - 20*(n-1) - overallCellMargin1 - overallCellMargin2 - ...) * percentage / 100
9557 // + thisOverallCellMargin
9558 // = 500 - 20 - 10 - 10) * 0.5 + 10 = 240 pixels overall cell width.
9559 // Adding this back, we get 240 + 240 + 20 = 500 pixels.
9561 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9563 int w
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetWidth());
9564 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
9566 percentageCellWidth
= w
;
9570 absoluteCellWidth
= w
;
9572 // Override absolute width with minimum width if necessary
9573 if (cell
->GetMinSize().x
> 0 && absoluteCellWidth
!=1 && cell
->GetMinSize().x
> absoluteCellWidth
)
9574 absoluteCellWidth
= cell
->GetMinSize().x
;
9577 if (absoluteCellWidth
!= -1)
9579 if (absoluteCellWidth
> absoluteColWidths
[i
])
9580 absoluteColWidths
[i
] = absoluteCellWidth
;
9583 if (percentageCellWidth
!= -1)
9585 if (percentageCellWidth
> percentageColWidths
[i
])
9586 percentageColWidths
[i
] = percentageCellWidth
;
9589 if (colSpan
== 1 && cell
->GetMinSize().x
&& cell
->GetMinSize().x
> minColWidths
[i
])
9590 minColWidths
[i
] = cell
->GetMinSize().x
;
9591 if (colSpan
== 1 && cell
->GetMaxSize().x
&& cell
->GetMaxSize().x
> maxColWidths
[i
])
9592 maxColWidths
[i
] = cell
->GetMaxSize().x
;
9598 // (2) Allocate initial column widths from minimum widths, absolute values and proportions
9599 // TODO: simply merge this into (1).
9600 for (i
= 0; i
< m_colCount
; i
++)
9602 if (absoluteColWidths
[i
] > 0)
9604 colWidths
[i
] = absoluteColWidths
[i
];
9606 else if (percentageColWidths
[i
] > 0)
9608 colWidths
[i
] = percentageColWidths
[i
];
9610 // This is rubbish - we calculated the absolute widths from percentages, so
9611 // we can't do it again here.
9612 //colWidths[i] = (int) (double(percentageColWidths[i]) * double(tableWidth) / 100.0 + 0.5);
9616 // (3) Process absolute or proportional widths of spanning columns,
9617 // now that we know what our fixed column widths are going to be.
9618 // Spanned cells will try to adjust columns so the span will fit.
9619 // Even existing fixed column widths can be expanded if necessary.
9620 // Actually, currently fixed columns widths aren't adjusted; instead,
9621 // the algorithm favours earlier rows and adjusts unspecified column widths
9622 // the first time only. After that, we can't know whether the column has been
9623 // specified explicitly or not. (We could make a note if necessary.)
9624 for (j
= 0; j
< m_rowCount
; j
++)
9626 // First get the overall margins so we can calculate percentage widths based on
9627 // the available content space for all cells on the row
9629 int overallRowContentMargin
= 0;
9630 int visibleCellCount
= 0;
9632 for (i
= 0; i
< m_colCount
; i
++)
9634 wxRichTextBox
* cell
= GetCell(j
, i
);
9635 if (cell
->IsShown())
9637 int cellTotalLeftMargin
= 0, cellTotalRightMargin
= 0, cellTotalTopMargin
= 0, cellTotalBottomMargin
= 0;
9638 GetTotalMargin(dc
, buffer
, cell
->GetAttributes(), cellTotalLeftMargin
, cellTotalRightMargin
, cellTotalTopMargin
, cellTotalBottomMargin
);
9640 overallRowContentMargin
+= (cellTotalLeftMargin
+ cellTotalRightMargin
);
9641 visibleCellCount
++;
9645 // Add in inter-cell padding
9646 overallRowContentMargin
+= ((visibleCellCount
-1) * paddingX
);
9648 int rowContentWidth
= internalTableWidth
- overallRowContentMargin
;
9649 wxSize
rowTableSize(rowContentWidth
, 0);
9650 wxTextAttrDimensionConverter
converter(dc
, scale
, rowTableSize
);
9652 for (i
= 0; i
< m_colCount
; i
++)
9654 wxRichTextBox
* cell
= GetCell(j
, i
);
9655 if (cell
->IsShown())
9658 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9659 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9663 int spans
= wxMin(colSpan
, m_colCount
- i
);
9667 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9669 cellWidth
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetWidth());
9670 // Override absolute width with minimum width if necessary
9671 if (cell
->GetMinSize().x
> 0 && cellWidth
!=1 && cell
->GetMinSize().x
> cellWidth
)
9672 cellWidth
= cell
->GetMinSize().x
;
9676 // Do we want to do this? It's the only chance we get to
9677 // use the cell's min/max sizes, so we need to work out
9678 // how we're going to balance the unspecified spanning cell
9679 // width with the possibility more-constrained constituent cell widths.
9680 // Say there's a tiny bitmap giving it a max width of 10 pixels. We
9681 // don't want to constraint all the spanned columns to fit into this cell.
9682 // OK, let's say that if any of the constituent columns don't fit,
9683 // then we simply stop constraining the columns; instead, we'll just fit the spanning
9684 // cells to the columns later.
9685 cellWidth
= cell
->GetMinSize().x
;
9686 if (cell
->GetMaxSize().x
> cellWidth
)
9687 cellWidth
= cell
->GetMaxSize().x
;
9690 // Subtract the padding between cells
9691 int spanningWidth
= cellWidth
;
9692 spanningWidth
-= paddingX
* (spans
-1);
9694 if (spanningWidth
> 0)
9696 // Now share the spanning width between columns within that span
9697 // TODO: take into account min widths of columns within the span
9698 int spanningWidthLeft
= spanningWidth
;
9699 int stretchColCount
= 0;
9700 for (k
= i
; k
< (i
+spans
); k
++)
9702 if (colWidths
[k
] > 0) // absolute or proportional width has been specified
9703 spanningWidthLeft
-= colWidths
[k
];
9707 // Now divide what's left between the remaining columns
9709 if (stretchColCount
> 0)
9710 colShare
= spanningWidthLeft
/ stretchColCount
;
9711 int colShareRemainder
= spanningWidthLeft
- (colShare
* stretchColCount
);
9713 // If fixed-width columns are currently too big, then we'll later
9714 // stretch the spanned cell to fit.
9716 if (spanningWidthLeft
> 0)
9718 for (k
= i
; k
< (i
+spans
); k
++)
9720 if (colWidths
[k
] <= 0) // absolute or proportional width has not been specified
9722 int newWidth
= colShare
;
9723 if (k
== (i
+spans
-1))
9724 newWidth
+= colShareRemainder
; // ensure all pixels are filled
9725 colWidths
[k
] = newWidth
;
9736 // (4) Next, share any remaining space out between columns that have not yet been calculated.
9737 // TODO: take into account min widths of columns within the span
9738 int tableWidthMinusPadding
= internalTableWidth
- (m_colCount
-1)*paddingX
;
9739 int widthLeft
= tableWidthMinusPadding
;
9740 int stretchColCount
= 0;
9741 for (i
= 0; i
< m_colCount
; i
++)
9743 // TODO: we need to take into account min widths.
9744 // Subtract min width from width left, then
9745 // add the colShare to the min width
9746 if (colWidths
[i
] > 0) // absolute or proportional width has been specified
9747 widthLeft
-= colWidths
[i
];
9750 if (minColWidths
[i
] > 0)
9751 widthLeft
-= minColWidths
[i
];
9757 // Now divide what's left between the remaining columns
9759 if (stretchColCount
> 0)
9760 colShare
= widthLeft
/ stretchColCount
;
9761 int colShareRemainder
= widthLeft
- (colShare
* stretchColCount
);
9763 // Check we don't have enough space, in which case shrink all columns, overriding
9764 // any absolute/proportional widths
9765 // TODO: actually we would like to divide up the shrinkage according to size.
9766 // How do we calculate the proportions that will achieve this?
9767 // Could first choose an arbitrary value for stretching cells, and then calculate
9768 // factors to multiply each width by.
9769 // TODO: want to record this fact and pass to an iteration that tries e.g. min widths
9770 if (widthLeft
< 0 || (stretchToFitTableWidth
&& (stretchColCount
== 0)))
9772 colShare
= tableWidthMinusPadding
/ m_colCount
;
9773 colShareRemainder
= tableWidthMinusPadding
- (colShare
* m_colCount
);
9774 for (i
= 0; i
< m_colCount
; i
++)
9777 minColWidths
[i
] = 0;
9781 // We have to adjust the columns if either we need to shrink the
9782 // table to fit the parent/table width, or we explicitly set the
9783 // table width and need to stretch out the table.
9784 if (widthLeft
< 0 || stretchToFitTableWidth
)
9786 for (i
= 0; i
< m_colCount
; i
++)
9788 if (colWidths
[i
] <= 0) // absolute or proportional width has not been specified
9790 if (minColWidths
[i
] > 0)
9791 colWidths
[i
] = minColWidths
[i
] + colShare
;
9793 colWidths
[i
] = colShare
;
9794 if (i
== (m_colCount
-1))
9795 colWidths
[i
] += colShareRemainder
; // ensure all pixels are filled
9800 // TODO: if spanned cells have no specified or max width, make them the
9801 // as big as the columns they span. Do this for all spanned cells in all
9802 // rows, of course. Size any spanned cells left over at the end - even if they
9803 // have width > 0, make sure they're limited to the appropriate column edge.
9807 Sort out confusion between content width
9808 and overall width later. For now, assume we specify overall width.
9810 So, now we've laid out the table to fit into the given space
9811 and have used specified widths and minimum widths.
9813 Now we need to consider how we will try to take maximum width into account.
9817 // (??) TODO: take max width into account
9819 // (6) Lay out all cells again with the current values
9822 int y
= availableSpace
.y
;
9823 for (j
= 0; j
< m_rowCount
; j
++)
9825 int x
= availableSpace
.x
; // TODO: take into account centering etc.
9826 int maxCellHeight
= 0;
9827 int maxSpecifiedCellHeight
= 0;
9829 wxArrayInt actualWidths
;
9830 actualWidths
.Add(0, m_colCount
);
9832 wxTextAttrDimensionConverter
converter(dc
, scale
);
9833 for (i
= 0; i
< m_colCount
; i
++)
9835 wxRichTextCell
* cell
= GetCell(j
, i
);
9836 if (cell
->IsShown())
9838 // Get max specified cell height
9839 // Don't handle percentages for height
9840 if (cell
->GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && cell
->GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() != wxTEXT_ATTR_UNITS_PERCENTAGE
)
9842 int h
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetHeight());
9843 if (h
> maxSpecifiedCellHeight
)
9844 maxSpecifiedCellHeight
= h
;
9847 if (colWidths
[i
] > 0) // absolute or proportional width has been specified
9850 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9851 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9853 wxRect availableCellSpace
;
9855 // TODO: take into acount spans
9858 // Calculate the size of this spanning cell from its constituent columns
9860 int spans
= wxMin(colSpan
, m_colCount
- i
);
9861 for (k
= i
; k
< spans
; k
++)
9867 availableCellSpace
= wxRect(x
, y
, xx
, -1);
9870 availableCellSpace
= wxRect(x
, y
, colWidths
[i
], -1);
9872 // Store actual width so we can force cell to be the appropriate width on the final loop
9873 actualWidths
[i
] = availableCellSpace
.GetWidth();
9876 cell
->Invalidate(wxRICHTEXT_ALL
);
9877 cell
->Layout(dc
, context
, availableCellSpace
, availableSpace
, style
);
9879 // TODO: use GetCachedSize().x to compute 'natural' size
9881 x
+= (availableCellSpace
.GetWidth() + paddingX
);
9882 if (cell
->GetCachedSize().y
> maxCellHeight
)
9883 maxCellHeight
= cell
->GetCachedSize().y
;
9888 maxCellHeight
= wxMax(maxCellHeight
, maxSpecifiedCellHeight
);
9890 for (i
= 0; i
< m_colCount
; i
++)
9892 wxRichTextCell
* cell
= GetCell(j
, i
);
9893 if (cell
->IsShown())
9895 wxRect availableCellSpace
= wxRect(cell
->GetPosition(), wxSize(actualWidths
[i
], maxCellHeight
));
9896 // Lay out cell with new height
9897 cell
->Invalidate(wxRICHTEXT_ALL
);
9898 cell
->Layout(dc
, context
, availableCellSpace
, availableSpace
, style
);
9900 // Make sure the cell size really is the appropriate size,
9901 // not the calculated box size
9902 cell
->SetCachedSize(wxSize(actualWidths
[i
], maxCellHeight
));
9904 maxRight
= wxMax(maxRight
, cell
->GetPosition().x
+ cell
->GetCachedSize().x
);
9909 if (j
< (m_rowCount
-1))
9913 // We need to add back the margins etc.
9915 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
9916 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxRight
- availableSpace
.x
, y
- availableSpace
.y
));
9917 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
9918 SetCachedSize(marginRect
.GetSize());
9921 // TODO: calculate max size
9923 SetMaxSize(GetCachedSize());
9926 // TODO: calculate min size
9928 SetMinSize(GetCachedSize());
9931 // TODO: currently we use either a fixed table width or the parent's size.
9932 // We also want to be able to calculate the table width from its content,
9933 // whether using fixed column widths or cell content min/max width.
9934 // Probably need a boolean flag to say whether we need to stretch cells
9935 // to fit the table width, or to simply use min/max cell widths. The
9936 // trouble with this is that if cell widths are not specified, they
9937 // will be tiny; we could use arbitrary defaults but this seems unsatisfactory.
9938 // Anyway, ignoring that problem, we probably need to factor layout into a function
9939 // that can can calculate the maximum unconstrained layout in case table size is
9940 // not specified. Then LayoutToBestSize() can choose to use either parent size to
9941 // constrain Layout(), or the previously-calculated max size to constraint layout.
9946 // Finds the absolute position and row height for the given character position
9947 bool wxRichTextTable::FindPosition(wxDC
& dc
, wxRichTextDrawingContext
& context
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
9949 wxRichTextCell
* child
= GetCell(index
+1);
9952 // Find the position at the start of the child cell, since the table doesn't
9953 // have any caret position of its own.
9954 return child
->FindPosition(dc
, context
, -1, pt
, height
, forceLineStart
);
9960 // Get the cell at the given character position (in the range of the table).
9961 wxRichTextCell
* wxRichTextTable::GetCell(long pos
) const
9963 int row
= 0, col
= 0;
9964 if (GetCellRowColumnPosition(pos
, row
, col
))
9966 return GetCell(row
, col
);
9972 // Get the row/column for a given character position
9973 bool wxRichTextTable::GetCellRowColumnPosition(long pos
, int& row
, int& col
) const
9975 if (m_colCount
== 0 || m_rowCount
== 0)
9978 row
= (int) (pos
/ m_colCount
);
9979 col
= pos
- (row
* m_colCount
);
9981 wxASSERT(row
< m_rowCount
&& col
< m_colCount
);
9983 if (row
< m_rowCount
&& col
< m_colCount
)
9989 // Calculate range, taking row/cell ordering into account instead of relying
9990 // on list ordering.
9991 void wxRichTextTable::CalculateRange(long start
, long& end
)
9993 long current
= start
;
9994 long lastEnd
= current
;
10003 for (i
= 0; i
< m_rowCount
; i
++)
10005 for (j
= 0; j
< m_colCount
; j
++)
10007 wxRichTextCell
* child
= GetCell(i
, j
);
10012 child
->CalculateRange(current
, childEnd
);
10014 lastEnd
= childEnd
;
10015 current
= childEnd
+ 1;
10020 // A top-level object always has a range of size 1,
10021 // because its children don't count at this level.
10023 m_range
.SetRange(start
, start
);
10025 // An object with no children has zero length
10026 if (m_children
.GetCount() == 0)
10028 m_ownRange
.SetRange(0, lastEnd
);
10031 // Gets the range size.
10032 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
10034 return wxRichTextBox::GetRangeSize(range
, size
, descent
, dc
, context
, flags
, position
, parentSize
, partialExtents
);
10037 // Deletes content in the given range.
10038 bool wxRichTextTable::DeleteRange(const wxRichTextRange
& WXUNUSED(range
))
10040 // TODO: implement deletion of cells
10044 // Gets any text in this object for the given range.
10045 wxString
wxRichTextTable::GetTextForRange(const wxRichTextRange
& range
) const
10047 return wxRichTextBox::GetTextForRange(range
);
10050 // Copies this object.
10051 void wxRichTextTable::Copy(const wxRichTextTable
& obj
)
10053 wxRichTextBox::Copy(obj
);
10057 m_rowCount
= obj
.m_rowCount
;
10058 m_colCount
= obj
.m_colCount
;
10060 m_cells
.Add(wxRichTextObjectPtrArray(), m_rowCount
);
10063 for (i
= 0; i
< m_rowCount
; i
++)
10065 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
10066 for (j
= 0; j
< m_colCount
; j
++)
10068 wxRichTextCell
* cell
= wxDynamicCast(obj
.GetCell(i
, j
)->Clone(), wxRichTextCell
);
10071 colArray
.Add(cell
);
10076 void wxRichTextTable::ClearTable()
10082 bool wxRichTextTable::CreateTable(int rows
, int cols
)
10089 m_cells
.Add(wxRichTextObjectPtrArray(), rows
);
10092 for (i
= 0; i
< rows
; i
++)
10094 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
10095 for (j
= 0; j
< cols
; j
++)
10097 wxRichTextCell
* cell
= new wxRichTextCell
;
10099 cell
->AddParagraph(wxEmptyString
);
10101 colArray
.Add(cell
);
10108 wxRichTextCell
* wxRichTextTable::GetCell(int row
, int col
) const
10110 wxASSERT(row
< m_rowCount
);
10111 wxASSERT(col
< m_colCount
);
10113 if (row
< m_rowCount
&& col
< m_colCount
)
10115 wxRichTextObjectPtrArray
& colArray
= m_cells
[row
];
10116 wxRichTextObject
* obj
= colArray
[col
];
10117 return wxDynamicCast(obj
, wxRichTextCell
);
10123 // Returns a selection object specifying the selections between start and end character positions.
10124 // For example, a table would deduce what cells (of range length 1) are selected when dragging across the table.
10125 wxRichTextSelection
wxRichTextTable::GetSelection(long start
, long end
) const
10127 wxRichTextSelection selection
;
10128 selection
.SetContainer((wxRichTextTable
*) this);
10137 wxASSERT( start
>= 0 && end
< (m_colCount
* m_rowCount
));
10139 if (end
>= (m_colCount
* m_rowCount
))
10142 // We need to find the rectangle of cells that is described by the rectangle
10143 // with start, end as the diagonal. Make sure we don't add cells that are
10144 // not currenty visible because they are overlapped by spanning cells.
10146 --------------------------
10147 | 0 | 1 | 2 | 3 | 4 |
10148 --------------------------
10149 | 5 | 6 | 7 | 8 | 9 |
10150 --------------------------
10151 | 10 | 11 | 12 | 13 | 14 |
10152 --------------------------
10153 | 15 | 16 | 17 | 18 | 19 |
10154 --------------------------
10156 Let's say we select 6 -> 18.
10158 Left and right edge cols of rectangle are 1 and 3 inclusive. Find least/greatest to find
10159 which is left and which is right.
10161 Top and bottom edge rows are 1 and 3 inclusive. Again, find least/greatest to find top and bottom.
10163 Now go through rows from 1 to 3 and only add cells that are (a) within above column range
10169 int leftCol
= start
- m_colCount
* int(start
/m_colCount
);
10170 int rightCol
= end
- m_colCount
* int(end
/m_colCount
);
10172 int topRow
= int(start
/m_colCount
);
10173 int bottomRow
= int(end
/m_colCount
);
10175 if (leftCol
> rightCol
)
10177 int tmp
= rightCol
;
10178 rightCol
= leftCol
;
10182 if (topRow
> bottomRow
)
10184 int tmp
= bottomRow
;
10185 bottomRow
= topRow
;
10190 for (i
= topRow
; i
<= bottomRow
; i
++)
10192 for (j
= leftCol
; j
<= rightCol
; j
++)
10194 wxRichTextCell
* cell
= GetCell(i
, j
);
10195 if (cell
&& cell
->IsShown())
10196 selection
.Add(cell
->GetRange());
10203 // Sets the attributes for the cells specified by the selection.
10204 bool wxRichTextTable::SetCellStyle(const wxRichTextSelection
& selection
, const wxRichTextAttr
& style
, int flags
)
10206 if (selection
.GetContainer() != this)
10209 wxRichTextBuffer
* buffer
= GetBuffer();
10210 bool haveControl
= (buffer
&& buffer
->GetRichTextCtrl() != NULL
);
10211 bool withUndo
= haveControl
&& ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
10214 buffer
->BeginBatchUndo(_("Set Cell Style"));
10216 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
10219 wxRichTextCell
* cell
= wxDynamicCast(node
->GetData(), wxRichTextCell
);
10220 if (cell
&& selection
.WithinSelection(cell
->GetRange().GetStart()))
10221 SetStyle(cell
, style
, flags
);
10222 node
= node
->GetNext();
10225 // Do action, or delay it until end of batch.
10227 buffer
->EndBatchUndo();
10232 bool wxRichTextTable::DeleteRows(int startRow
, int noRows
)
10234 wxASSERT((startRow
+ noRows
) < m_rowCount
);
10235 if ((startRow
+ noRows
) >= m_rowCount
)
10239 for (i
= startRow
; i
< (startRow
+noRows
); i
++)
10241 wxRichTextObjectPtrArray
& colArray
= m_cells
[startRow
];
10242 for (j
= 0; j
< (int) colArray
.GetCount(); j
++)
10244 wxRichTextObject
* cell
= colArray
[j
];
10245 RemoveChild(cell
, true);
10248 // Keep deleting at the same position, since we move all
10250 m_cells
.RemoveAt(startRow
);
10253 m_rowCount
= m_rowCount
- noRows
;
10258 bool wxRichTextTable::DeleteColumns(int startCol
, int noCols
)
10260 wxASSERT((startCol
+ noCols
) < m_colCount
);
10261 if ((startCol
+ noCols
) >= m_colCount
)
10264 bool deleteRows
= (noCols
== m_colCount
);
10267 for (i
= 0; i
< m_rowCount
; i
++)
10269 wxRichTextObjectPtrArray
& colArray
= m_cells
[deleteRows
? 0 : i
];
10270 for (j
= startCol
; j
< (startCol
+noCols
); j
++)
10272 wxRichTextObject
* cell
= colArray
[j
];
10273 RemoveChild(cell
, true);
10277 m_cells
.RemoveAt(0);
10282 m_colCount
= m_colCount
- noCols
;
10287 bool wxRichTextTable::AddRows(int startRow
, int noRows
, const wxRichTextAttr
& attr
)
10289 wxASSERT(startRow
<= m_rowCount
);
10290 if (startRow
> m_rowCount
)
10294 for (i
= 0; i
< noRows
; i
++)
10297 if (startRow
== m_rowCount
)
10299 m_cells
.Add(wxRichTextObjectPtrArray());
10300 idx
= m_cells
.GetCount() - 1;
10304 m_cells
.Insert(wxRichTextObjectPtrArray(), startRow
+i
);
10308 wxRichTextObjectPtrArray
& colArray
= m_cells
[idx
];
10309 for (j
= 0; j
< m_colCount
; j
++)
10311 wxRichTextCell
* cell
= new wxRichTextCell
;
10312 cell
->GetAttributes() = attr
;
10315 colArray
.Add(cell
);
10319 m_rowCount
= m_rowCount
+ noRows
;
10323 bool wxRichTextTable::AddColumns(int startCol
, int noCols
, const wxRichTextAttr
& attr
)
10325 wxASSERT(startCol
<= m_colCount
);
10326 if (startCol
> m_colCount
)
10330 for (i
= 0; i
< m_rowCount
; i
++)
10332 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
10333 for (j
= 0; j
< noCols
; j
++)
10335 wxRichTextCell
* cell
= new wxRichTextCell
;
10336 cell
->GetAttributes() = attr
;
10340 if (startCol
== m_colCount
)
10341 colArray
.Add(cell
);
10343 colArray
.Insert(cell
, startCol
+j
);
10347 m_colCount
= m_colCount
+ noCols
;
10352 // Edit properties via a GUI
10353 bool wxRichTextTable::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
10355 wxRichTextObjectPropertiesDialog
boxDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Table Properties"));
10356 boxDlg
.SetAttributes(GetAttributes());
10358 if (boxDlg
.ShowModal() == wxID_OK
)
10360 boxDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
10368 * Module to initialise and clean up handlers
10371 class wxRichTextModule
: public wxModule
10373 DECLARE_DYNAMIC_CLASS(wxRichTextModule
)
10375 wxRichTextModule() {}
10378 wxRichTextBuffer::SetRenderer(new wxRichTextStdRenderer
);
10379 wxRichTextBuffer::InitStandardHandlers();
10380 wxRichTextParagraph::InitDefaultTabs();
10382 wxRichTextXMLHandler::RegisterNodeName(wxT("text"), wxT("wxRichTextPlainText"));
10383 wxRichTextXMLHandler::RegisterNodeName(wxT("symbol"), wxT("wxRichTextPlainText"));
10384 wxRichTextXMLHandler::RegisterNodeName(wxT("image"), wxT("wxRichTextImage"));
10385 wxRichTextXMLHandler::RegisterNodeName(wxT("paragraph"), wxT("wxRichTextParagraph"));
10386 wxRichTextXMLHandler::RegisterNodeName(wxT("paragraphlayout"), wxT("wxRichTextParagraphLayoutBox"));
10387 wxRichTextXMLHandler::RegisterNodeName(wxT("textbox"), wxT("wxRichTextBox"));
10388 wxRichTextXMLHandler::RegisterNodeName(wxT("cell"), wxT("wxRichTextCell"));
10389 wxRichTextXMLHandler::RegisterNodeName(wxT("table"), wxT("wxRichTextTable"));
10390 wxRichTextXMLHandler::RegisterNodeName(wxT("field"), wxT("wxRichTextField"));
10396 wxRichTextBuffer::CleanUpHandlers();
10397 wxRichTextBuffer::CleanUpDrawingHandlers();
10398 wxRichTextBuffer::CleanUpFieldTypes();
10399 wxRichTextXMLHandler::ClearNodeToClassMap();
10400 wxRichTextDecimalToRoman(-1);
10401 wxRichTextParagraph::ClearDefaultTabs();
10402 wxRichTextCtrl::ClearAvailableFontNames();
10403 wxRichTextBuffer::SetRenderer(NULL
);
10407 IMPLEMENT_DYNAMIC_CLASS(wxRichTextModule
, wxModule
)
10410 // If the richtext lib is dynamically loaded after the app has already started
10411 // (such as from wxPython) then the built-in module system will not init this
10412 // module. Provide this function to do it manually.
10413 void wxRichTextModuleInit()
10415 wxModule
* module = new wxRichTextModule
;
10417 wxModule::RegisterModule(module);
10422 * Commands for undo/redo
10426 wxRichTextCommand::wxRichTextCommand(const wxString
& name
, wxRichTextCommandId id
, wxRichTextBuffer
* buffer
,
10427 wxRichTextParagraphLayoutBox
* container
, wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
): wxCommand(true, name
)
10429 /* wxRichTextAction* action = */ new wxRichTextAction(this, name
, id
, buffer
, container
, ctrl
, ignoreFirstTime
);
10432 wxRichTextCommand::wxRichTextCommand(const wxString
& name
): wxCommand(true, name
)
10436 wxRichTextCommand::~wxRichTextCommand()
10441 void wxRichTextCommand::AddAction(wxRichTextAction
* action
)
10443 if (!m_actions
.Member(action
))
10444 m_actions
.Append(action
);
10447 bool wxRichTextCommand::Do()
10449 for (wxList::compatibility_iterator node
= m_actions
.GetFirst(); node
; node
= node
->GetNext())
10451 wxRichTextAction
* action
= (wxRichTextAction
*) node
->GetData();
10458 bool wxRichTextCommand::Undo()
10460 for (wxList::compatibility_iterator node
= m_actions
.GetLast(); node
; node
= node
->GetPrevious())
10462 wxRichTextAction
* action
= (wxRichTextAction
*) node
->GetData();
10469 void wxRichTextCommand::ClearActions()
10471 WX_CLEAR_LIST(wxList
, m_actions
);
10475 * Individual action
10479 wxRichTextAction::wxRichTextAction(wxRichTextCommand
* cmd
, const wxString
& name
, wxRichTextCommandId id
,
10480 wxRichTextBuffer
* buffer
, wxRichTextParagraphLayoutBox
* container
,
10481 wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
)
10485 m_containerAddress
.Create(buffer
, container
);
10486 m_ignoreThis
= ignoreFirstTime
;
10491 m_newParagraphs
.SetDefaultStyle(buffer
->GetDefaultStyle());
10492 m_newParagraphs
.SetBasicStyle(buffer
->GetBasicStyle());
10494 cmd
->AddAction(this);
10497 wxRichTextAction::~wxRichTextAction()
10503 // Returns the container that this action refers to, using the container address and top-level buffer.
10504 wxRichTextParagraphLayoutBox
* wxRichTextAction::GetContainer() const
10506 wxRichTextParagraphLayoutBox
* container
= wxDynamicCast(GetContainerAddress().GetObject(m_buffer
), wxRichTextParagraphLayoutBox
);
10511 void wxRichTextAction::CalculateRefreshOptimizations(wxArrayInt
& optimizationLineCharPositions
, wxArrayInt
& optimizationLineYPositions
)
10513 // Store a list of line start character and y positions so we can figure out which area
10514 // we need to refresh
10516 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10517 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10518 wxASSERT(container
!= NULL
);
10522 // NOTE: we're assuming that the buffer is laid out correctly at this point.
10523 // If we had several actions, which only invalidate and leave layout until the
10524 // paint handler is called, then this might not be true. So we may need to switch
10525 // optimisation on only when we're simply adding text and not simultaneously
10526 // deleting a selection, for example. Or, we make sure the buffer is laid out correctly
10527 // first, but of course this means we'll be doing it twice.
10528 if (!m_buffer
->IsDirty() && m_ctrl
) // can only do optimisation if the buffer is already laid out correctly
10530 wxSize clientSize
= m_ctrl
->GetUnscaledSize(m_ctrl
->GetClientSize());
10531 wxPoint firstVisiblePt
= m_ctrl
->GetUnscaledPoint(m_ctrl
->GetFirstVisiblePoint());
10532 int lastY
= firstVisiblePt
.y
+ clientSize
.y
;
10534 wxRichTextParagraph
* para
= container
->GetParagraphAtPosition(GetRange().GetStart());
10535 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(para
);
10538 wxRichTextParagraph
* child
= (wxRichTextParagraph
*) node
->GetData();
10539 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
10542 wxRichTextLine
* line
= node2
->GetData();
10543 wxPoint pt
= line
->GetAbsolutePosition();
10544 wxRichTextRange range
= line
->GetAbsoluteRange();
10548 node2
= wxRichTextLineList::compatibility_iterator();
10549 node
= wxRichTextObjectList::compatibility_iterator();
10551 else if (range
.GetStart() > GetPosition() && pt
.y
>= firstVisiblePt
.y
)
10553 optimizationLineCharPositions
.Add(range
.GetStart());
10554 optimizationLineYPositions
.Add(pt
.y
);
10558 node2
= node2
->GetNext();
10562 node
= node
->GetNext();
10568 bool wxRichTextAction::Do()
10570 m_buffer
->Modify(true);
10572 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10573 wxASSERT(container
!= NULL
);
10579 case wxRICHTEXT_INSERT
:
10581 // Store a list of line start character and y positions so we can figure out which area
10582 // we need to refresh
10583 wxArrayInt optimizationLineCharPositions
;
10584 wxArrayInt optimizationLineYPositions
;
10586 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10587 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
10590 container
->InsertFragment(GetRange().GetStart(), m_newParagraphs
);
10591 container
->UpdateRanges();
10593 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10594 // Layout() would stop prematurely at the top level.
10595 container
->InvalidateHierarchy(wxRichTextRange(wxMax(0, GetRange().GetStart()-1), GetRange().GetEnd()));
10597 long newCaretPosition
= GetPosition() + m_newParagraphs
.GetOwnRange().GetLength();
10599 // Character position to caret position
10600 newCaretPosition
--;
10602 // Don't take into account the last newline
10603 if (m_newParagraphs
.GetPartialParagraph())
10604 newCaretPosition
--;
10606 if (m_newParagraphs
.GetChildren().GetCount() > 1)
10608 wxRichTextObject
* p
= (wxRichTextObject
*) m_newParagraphs
.GetChildren().GetLast()->GetData();
10609 if (p
->GetRange().GetLength() == 1)
10610 newCaretPosition
--;
10613 newCaretPosition
= wxMin(newCaretPosition
, (container
->GetOwnRange().GetEnd()-1));
10615 UpdateAppearance(newCaretPosition
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */);
10617 wxRichTextEvent
cmdEvent(
10618 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED
,
10619 m_ctrl
? m_ctrl
->GetId() : -1);
10620 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10621 cmdEvent
.SetRange(GetRange());
10622 cmdEvent
.SetPosition(GetRange().GetStart());
10623 cmdEvent
.SetContainer(container
);
10625 m_buffer
->SendEvent(cmdEvent
);
10629 case wxRICHTEXT_DELETE
:
10631 wxArrayInt optimizationLineCharPositions
;
10632 wxArrayInt optimizationLineYPositions
;
10634 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10635 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
10638 container
->DeleteRange(GetRange());
10639 container
->UpdateRanges();
10640 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10641 // Layout() would stop prematurely at the top level.
10642 container
->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
10644 long caretPos
= GetRange().GetStart()-1;
10645 if (caretPos
>= container
->GetOwnRange().GetEnd())
10648 UpdateAppearance(caretPos
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */);
10650 wxRichTextEvent
cmdEvent(
10651 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED
,
10652 m_ctrl
? m_ctrl
->GetId() : -1);
10653 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10654 cmdEvent
.SetRange(GetRange());
10655 cmdEvent
.SetPosition(GetRange().GetStart());
10656 cmdEvent
.SetContainer(container
);
10658 m_buffer
->SendEvent(cmdEvent
);
10662 case wxRICHTEXT_CHANGE_STYLE
:
10663 case wxRICHTEXT_CHANGE_PROPERTIES
:
10665 ApplyParagraphs(GetNewParagraphs());
10667 // Invalidate the whole buffer if there were floating objects
10668 if (wxRichTextBuffer::GetFloatingLayoutMode() && container
->GetFloatingObjectCount() > 0)
10669 m_buffer
->InvalidateHierarchy(wxRICHTEXT_ALL
);
10672 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10673 // Layout() would stop prematurely at the top level.
10674 container
->InvalidateHierarchy(GetRange());
10677 UpdateAppearance(GetPosition());
10679 wxRichTextEvent
cmdEvent(
10680 m_cmdId
== wxRICHTEXT_CHANGE_STYLE
? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
: wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED
,
10681 m_ctrl
? m_ctrl
->GetId() : -1);
10682 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10683 cmdEvent
.SetRange(GetRange());
10684 cmdEvent
.SetPosition(GetRange().GetStart());
10685 cmdEvent
.SetContainer(container
);
10687 m_buffer
->SendEvent(cmdEvent
);
10691 case wxRICHTEXT_CHANGE_ATTRIBUTES
:
10693 wxRichTextObject
* obj
= m_objectAddress
.GetObject(m_buffer
); // container->GetChildAtPosition(GetRange().GetStart());
10696 wxRichTextAttr oldAttr
= obj
->GetAttributes();
10697 obj
->GetAttributes() = m_attributes
;
10698 m_attributes
= oldAttr
;
10701 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10702 // Layout() would stop prematurely at the top level.
10703 // Invalidate the whole buffer if there were floating objects
10704 if (wxRichTextBuffer::GetFloatingLayoutMode() && container
->GetFloatingObjectCount() > 0)
10705 m_buffer
->InvalidateHierarchy(wxRICHTEXT_ALL
);
10707 container
->InvalidateHierarchy(GetRange());
10709 UpdateAppearance(GetPosition());
10711 wxRichTextEvent
cmdEvent(
10712 wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
,
10713 m_ctrl
? m_ctrl
->GetId() : -1);
10714 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10715 cmdEvent
.SetRange(GetRange());
10716 cmdEvent
.SetPosition(GetRange().GetStart());
10717 cmdEvent
.SetContainer(container
);
10719 m_buffer
->SendEvent(cmdEvent
);
10723 case wxRICHTEXT_CHANGE_OBJECT
:
10725 wxRichTextObject
* obj
= m_objectAddress
.GetObject(m_buffer
);
10726 // wxRichTextObject* obj = container->GetChildAtPosition(GetRange().GetStart());
10727 if (obj
&& m_object
)
10729 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(obj
);
10732 wxRichTextObject
* obj
= node
->GetData();
10733 node
->SetData(m_object
);
10738 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10739 // Layout() would stop prematurely at the top level.
10740 // Invalidate the whole buffer if there were floating objects
10741 if (wxRichTextBuffer::GetFloatingLayoutMode() && container
->GetFloatingObjectCount() > 0)
10742 m_buffer
->InvalidateHierarchy(wxRICHTEXT_ALL
);
10744 container
->InvalidateHierarchy(GetRange());
10746 UpdateAppearance(GetPosition());
10748 // TODO: send new kind of modification event
10759 bool wxRichTextAction::Undo()
10761 m_buffer
->Modify(true);
10763 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10764 wxASSERT(container
!= NULL
);
10770 case wxRICHTEXT_INSERT
:
10772 wxArrayInt optimizationLineCharPositions
;
10773 wxArrayInt optimizationLineYPositions
;
10775 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10776 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
10779 container
->DeleteRange(GetRange());
10780 container
->UpdateRanges();
10782 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10783 // Layout() would stop prematurely at the top level.
10784 container
->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
10786 long newCaretPosition
= GetPosition() - 1;
10788 UpdateAppearance(newCaretPosition
, true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */);
10790 wxRichTextEvent
cmdEvent(
10791 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED
,
10792 m_ctrl
? m_ctrl
->GetId() : -1);
10793 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10794 cmdEvent
.SetRange(GetRange());
10795 cmdEvent
.SetPosition(GetRange().GetStart());
10796 cmdEvent
.SetContainer(container
);
10798 m_buffer
->SendEvent(cmdEvent
);
10802 case wxRICHTEXT_DELETE
:
10804 wxArrayInt optimizationLineCharPositions
;
10805 wxArrayInt optimizationLineYPositions
;
10807 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10808 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
10811 container
->InsertFragment(GetRange().GetStart(), m_oldParagraphs
);
10812 container
->UpdateRanges();
10814 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10815 // Layout() would stop prematurely at the top level.
10816 container
->InvalidateHierarchy(GetRange());
10818 UpdateAppearance(GetPosition(), true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */);
10820 wxRichTextEvent
cmdEvent(
10821 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED
,
10822 m_ctrl
? m_ctrl
->GetId() : -1);
10823 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10824 cmdEvent
.SetRange(GetRange());
10825 cmdEvent
.SetPosition(GetRange().GetStart());
10826 cmdEvent
.SetContainer(container
);
10828 m_buffer
->SendEvent(cmdEvent
);
10832 case wxRICHTEXT_CHANGE_STYLE
:
10833 case wxRICHTEXT_CHANGE_PROPERTIES
:
10835 ApplyParagraphs(GetOldParagraphs());
10836 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10837 // Layout() would stop prematurely at the top level.
10838 container
->InvalidateHierarchy(GetRange());
10840 UpdateAppearance(GetPosition());
10842 wxRichTextEvent
cmdEvent(
10843 m_cmdId
== wxRICHTEXT_CHANGE_STYLE
? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
: wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED
,
10844 m_ctrl
? m_ctrl
->GetId() : -1);
10845 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10846 cmdEvent
.SetRange(GetRange());
10847 cmdEvent
.SetPosition(GetRange().GetStart());
10848 cmdEvent
.SetContainer(container
);
10850 m_buffer
->SendEvent(cmdEvent
);
10854 case wxRICHTEXT_CHANGE_ATTRIBUTES
:
10855 case wxRICHTEXT_CHANGE_OBJECT
:
10866 /// Update the control appearance
10867 void wxRichTextAction::UpdateAppearance(long caretPosition
, bool sendUpdateEvent
, wxArrayInt
* optimizationLineCharPositions
, wxArrayInt
* optimizationLineYPositions
, bool isDoCmd
)
10869 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10870 wxASSERT(container
!= NULL
);
10876 m_ctrl
->SetFocusObject(container
);
10877 m_ctrl
->SetCaretPosition(caretPosition
);
10879 if (!m_ctrl
->IsFrozen())
10881 wxRect containerRect
= container
->GetRect();
10883 m_ctrl
->LayoutContent();
10885 // Refresh everything if there were floating objects or the container changed size
10886 // (we can't yet optimize in these cases, since more complex interaction with other content occurs)
10887 if ((wxRichTextBuffer::GetFloatingLayoutMode() && container
->GetFloatingObjectCount() > 0) || (container
->GetParent() && containerRect
!= container
->GetRect()))
10889 m_ctrl
->Refresh(false);
10893 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10894 // Find refresh rectangle if we are in a position to optimise refresh
10895 if ((m_cmdId
== wxRICHTEXT_INSERT
|| m_cmdId
== wxRICHTEXT_DELETE
) && optimizationLineCharPositions
)
10899 wxSize clientSize
= m_ctrl
->GetUnscaledSize(m_ctrl
->GetClientSize());
10900 wxPoint firstVisiblePt
= m_ctrl
->GetUnscaledPoint(m_ctrl
->GetFirstVisiblePoint());
10902 // Start/end positions
10904 int lastY
= firstVisiblePt
.y
+ clientSize
.y
;
10906 bool foundEnd
= false;
10908 // position offset - how many characters were inserted
10909 int positionOffset
= GetRange().GetLength();
10911 // Determine whether this is Do or Undo, and adjust positionOffset accordingly
10912 if ((m_cmdId
== wxRICHTEXT_DELETE
&& isDoCmd
) || (m_cmdId
== wxRICHTEXT_INSERT
&& !isDoCmd
))
10913 positionOffset
= - positionOffset
;
10915 // find the first line which is being drawn at the same position as it was
10916 // before. Since we're talking about a simple insertion, we can assume
10917 // that the rest of the window does not need to be redrawn.
10919 wxRichTextParagraph
* para
= container
->GetParagraphAtPosition(GetPosition());
10920 // Since we support floating layout, we should redraw the whole para instead of just
10921 // the first line touching the invalid range.
10924 firstY
= para
->GetPosition().y
;
10927 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(para
);
10930 wxRichTextParagraph
* child
= (wxRichTextParagraph
*) node
->GetData();
10931 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
10934 wxRichTextLine
* line
= node2
->GetData();
10935 wxPoint pt
= line
->GetAbsolutePosition();
10936 wxRichTextRange range
= line
->GetAbsoluteRange();
10938 // we want to find the first line that is in the same position
10939 // as before. This will mean we're at the end of the changed text.
10941 if (pt
.y
> lastY
) // going past the end of the window, no more info
10943 node2
= wxRichTextLineList::compatibility_iterator();
10944 node
= wxRichTextObjectList::compatibility_iterator();
10946 // Detect last line in the buffer
10947 else if (!node2
->GetNext() && para
->GetRange().Contains(container
->GetOwnRange().GetEnd()))
10949 // If deleting text, make sure we refresh below as well as above
10950 if (positionOffset
>= 0)
10953 lastY
= pt
.y
+ line
->GetSize().y
;
10956 node2
= wxRichTextLineList::compatibility_iterator();
10957 node
= wxRichTextObjectList::compatibility_iterator();
10963 // search for this line being at the same position as before
10964 for (i
= 0; i
< optimizationLineCharPositions
->GetCount(); i
++)
10966 if (((*optimizationLineCharPositions
)[i
] + positionOffset
== range
.GetStart()) &&
10967 ((*optimizationLineYPositions
)[i
] == pt
.y
))
10969 // Stop, we're now the same as we were
10974 node2
= wxRichTextLineList::compatibility_iterator();
10975 node
= wxRichTextObjectList::compatibility_iterator();
10983 node2
= node2
->GetNext();
10987 node
= node
->GetNext();
10990 firstY
= wxMax(firstVisiblePt
.y
, firstY
);
10992 lastY
= firstVisiblePt
.y
+ clientSize
.y
;
10994 // Convert to device coordinates
10995 wxRect
rect(m_ctrl
->GetPhysicalPoint(m_ctrl
->GetScaledPoint(wxPoint(firstVisiblePt
.x
, firstY
))), m_ctrl
->GetScaledSize(wxSize(clientSize
.x
, lastY
- firstY
)));
10996 m_ctrl
->RefreshRect(rect
);
11000 m_ctrl
->Refresh(false);
11002 m_ctrl
->PositionCaret();
11004 // This causes styles to persist when doing programmatic
11005 // content creation except when Freeze/Thaw is used, so
11006 // disable this and check for the consequences.
11007 // m_ctrl->SetDefaultStyleToCursorStyle();
11009 if (sendUpdateEvent
)
11010 wxTextCtrl::SendTextUpdatedEvent(m_ctrl
);
11015 /// Replace the buffer paragraphs with the new ones.
11016 void wxRichTextAction::ApplyParagraphs(const wxRichTextParagraphLayoutBox
& fragment
)
11018 wxRichTextParagraphLayoutBox
* container
= GetContainer();
11019 wxASSERT(container
!= NULL
);
11023 wxRichTextObjectList::compatibility_iterator node
= fragment
.GetChildren().GetFirst();
11026 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
11027 wxASSERT (para
!= NULL
);
11029 // We'll replace the existing paragraph by finding the paragraph at this position,
11030 // delete its node data, and setting a copy as the new node data.
11031 // TODO: make more efficient by simply swapping old and new paragraph objects.
11033 wxRichTextParagraph
* existingPara
= container
->GetParagraphAtPosition(para
->GetRange().GetStart());
11036 wxRichTextObjectList::compatibility_iterator bufferParaNode
= container
->GetChildren().Find(existingPara
);
11037 if (bufferParaNode
)
11039 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(*para
);
11040 newPara
->SetParent(container
);
11042 bufferParaNode
->SetData(newPara
);
11044 delete existingPara
;
11048 node
= node
->GetNext();
11055 * This stores beginning and end positions for a range of data.
11058 WX_DEFINE_OBJARRAY(wxRichTextRangeArray
);
11060 /// Limit this range to be within 'range'
11061 bool wxRichTextRange::LimitTo(const wxRichTextRange
& range
)
11063 if (m_start
< range
.m_start
)
11064 m_start
= range
.m_start
;
11066 if (m_end
> range
.m_end
)
11067 m_end
= range
.m_end
;
11073 * wxRichTextImage implementation
11074 * This object represents an image.
11077 IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage
, wxRichTextObject
)
11079 wxRichTextImage::wxRichTextImage(const wxImage
& image
, wxRichTextObject
* parent
, wxRichTextAttr
* charStyle
):
11080 wxRichTextObject(parent
)
11083 m_imageBlock
.MakeImageBlockDefaultQuality(image
, wxBITMAP_TYPE_PNG
);
11085 SetAttributes(*charStyle
);
11088 wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock
& imageBlock
, wxRichTextObject
* parent
, wxRichTextAttr
* charStyle
):
11089 wxRichTextObject(parent
)
11092 m_imageBlock
= imageBlock
;
11094 SetAttributes(*charStyle
);
11097 wxRichTextImage::~wxRichTextImage()
11101 void wxRichTextImage::Init()
11103 m_originalImageSize
= wxSize(-1, -1);
11106 /// Create a cached image at the required size
11107 bool wxRichTextImage::LoadImageCache(wxDC
& dc
, bool resetCache
, const wxSize
& parentSize
)
11109 if (!m_imageBlock
.IsOk())
11112 // If we have an original image size, use that to compute the cached bitmap size
11113 // instead of loading the image each time. This way we can avoid loading
11114 // the image so long as the new cached bitmap size hasn't changed.
11117 if (resetCache
|| m_originalImageSize
.GetWidth() <= 0 || m_originalImageSize
.GetHeight() <= 0)
11119 m_imageCache
= wxNullBitmap
;
11121 m_imageBlock
.Load(image
);
11125 m_originalImageSize
= wxSize(image
.GetWidth(), image
.GetHeight());
11128 int width
= m_originalImageSize
.GetWidth();
11129 int height
= m_originalImageSize
.GetHeight();
11131 int parentWidth
= 0;
11132 int parentHeight
= 0;
11135 int maxHeight
= -1;
11137 wxSize sz
= parentSize
;
11138 if (sz
== wxDefaultSize
)
11140 if (GetParent() && GetParent()->GetParent())
11141 sz
= GetParent()->GetParent()->GetCachedSize();
11144 if (sz
!= wxDefaultSize
)
11146 wxRichTextBuffer
* buffer
= GetBuffer();
11149 // Find the actual space available when margin is taken into account
11150 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
11151 marginRect
= wxRect(0, 0, sz
.x
, sz
.y
);
11152 if (GetParent() && GetParent()->GetParent())
11154 buffer
->GetBoxRects(dc
, buffer
, GetParent()->GetParent()->GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
11155 sz
= contentRect
.GetSize();
11158 // Use a minimum size to stop images becoming very small
11159 parentWidth
= wxMax(100, sz
.GetWidth());
11160 parentHeight
= wxMax(100, sz
.GetHeight());
11162 if (buffer
->GetRichTextCtrl())
11163 // Start with a maximum width of the control size, even if not specified by the content,
11164 // to minimize the amount of picture overlapping the right-hand side
11165 maxWidth
= parentWidth
;
11169 if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetWidth().GetValue() > 0)
11171 if (parentWidth
> 0 && GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
11172 width
= (int) ((GetAttributes().GetTextBoxAttr().GetWidth().GetValue() * parentWidth
)/100.0);
11173 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
11174 width
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetWidth().GetValue());
11175 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
11176 width
= GetAttributes().GetTextBoxAttr().GetWidth().GetValue();
11179 // Limit to max width
11181 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() > 0)
11185 if (parentWidth
> 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
11186 mw
= (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() * parentWidth
)/100.0);
11187 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
11188 mw
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue());
11189 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
11190 mw
= GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue();
11192 // If we already have a smaller max width due to the constraints of the control size,
11193 // don't use the larger max width.
11194 if (mw
!= -1 && ((maxWidth
== -1) || (mw
< maxWidth
)))
11198 if (maxWidth
> 0 && width
> maxWidth
)
11201 // Preserve the aspect ratio
11202 if (width
!= m_originalImageSize
.GetWidth())
11203 height
= (int) (float(m_originalImageSize
.GetHeight()) * (float(width
)/float(m_originalImageSize
.GetWidth())));
11205 if (GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetHeight().GetValue() > 0)
11207 if (parentHeight
> 0 && GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
11208 height
= (int) ((GetAttributes().GetTextBoxAttr().GetHeight().GetValue() * parentHeight
)/100.0);
11209 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
11210 height
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetHeight().GetValue());
11211 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
11212 height
= GetAttributes().GetTextBoxAttr().GetHeight().GetValue();
11214 // Preserve the aspect ratio
11215 if (height
!= m_originalImageSize
.GetHeight())
11216 width
= (int) (float(m_originalImageSize
.GetWidth()) * (float(height
)/float(m_originalImageSize
.GetHeight())));
11219 // Limit to max height
11221 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() > 0)
11223 if (parentHeight
> 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
11224 maxHeight
= (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() * parentHeight
)/100.0);
11225 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
11226 maxHeight
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue());
11227 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
11228 maxHeight
= GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue();
11231 if (maxHeight
> 0 && height
> maxHeight
)
11233 height
= maxHeight
;
11235 // Preserve the aspect ratio
11236 if (height
!= m_originalImageSize
.GetHeight())
11237 width
= (int) (float(m_originalImageSize
.GetWidth()) * (float(height
)/float(m_originalImageSize
.GetHeight())));
11240 // Prevent the use of zero size
11241 width
= wxMax(1, width
);
11242 height
= wxMax(1, height
);
11244 if (m_imageCache
.IsOk() && m_imageCache
.GetWidth() == width
&& m_imageCache
.GetHeight() == height
)
11246 // Do nothing, we didn't need to change the image cache
11252 m_imageBlock
.Load(image
);
11257 if (image
.GetWidth() == width
&& image
.GetHeight() == height
)
11258 m_imageCache
= wxBitmap(image
);
11261 // If the original width and height is small, e.g. 400 or below,
11262 // scale up and then down to improve image quality. This can make
11263 // a big difference, with not much performance hit.
11264 int upscaleThreshold
= 400;
11266 if (image
.GetWidth() <= upscaleThreshold
|| image
.GetHeight() <= upscaleThreshold
)
11268 img
= image
.Scale(image
.GetWidth()*2, image
.GetHeight()*2);
11269 img
.Rescale(width
, height
, wxIMAGE_QUALITY_HIGH
);
11272 img
= image
.Scale(width
, height
, wxIMAGE_QUALITY_HIGH
);
11273 m_imageCache
= wxBitmap(img
);
11277 return m_imageCache
.IsOk();
11281 bool wxRichTextImage::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& WXUNUSED(range
), const wxRichTextSelection
& selection
, const wxRect
& rect
, int WXUNUSED(descent
), int WXUNUSED(style
))
11286 // Don't need cached size AFAIK
11287 // wxSize size = GetCachedSize();
11288 if (!LoadImageCache(dc
))
11291 wxRichTextAttr
attr(GetAttributes());
11292 context
.ApplyVirtualAttributes(attr
, this);
11294 DrawBoxAttributes(dc
, GetBuffer(), attr
, wxRect(rect
.GetPosition(), GetCachedSize()));
11296 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
11297 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
11298 marginRect
= rect
; // outer rectangle, will calculate contentRect
11299 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
11301 dc
.DrawBitmap(m_imageCache
, contentRect
.x
, contentRect
.y
, true);
11303 if (selection
.WithinSelection(GetRange().GetStart(), this))
11305 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
11306 wxCheckSetPen(dc
, *wxBLACK_PEN
);
11307 dc
.SetLogicalFunction(wxINVERT
);
11308 dc
.DrawRectangle(contentRect
);
11309 dc
.SetLogicalFunction(wxCOPY
);
11315 /// Lay the item out
11316 bool wxRichTextImage::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& WXUNUSED(parentRect
), int WXUNUSED(style
))
11318 if (!LoadImageCache(dc
))
11321 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
11322 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
11323 contentRect
= wxRect(wxPoint(0,0), imageSize
);
11325 wxRichTextAttr
attr(GetAttributes());
11326 context
.ApplyVirtualAttributes(attr
, this);
11328 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
11330 wxSize overallSize
= marginRect
.GetSize();
11332 SetCachedSize(overallSize
);
11333 SetMaxSize(overallSize
);
11334 SetMinSize(overallSize
);
11335 SetPosition(rect
.GetPosition());
11340 /// Get/set the object size for the given range. Returns false if the range
11341 /// is invalid for this object.
11342 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
11344 if (!range
.IsWithin(GetRange()))
11347 if (!((wxRichTextImage
*)this)->LoadImageCache(dc
, false, parentSize
))
11349 size
.x
= 0; size
.y
= 0;
11350 if (partialExtents
)
11351 partialExtents
->Add(0);
11355 wxRichTextAttr
attr(GetAttributes());
11356 context
.ApplyVirtualAttributes(attr
, (wxRichTextObject
*) this);
11358 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
11359 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
11360 contentRect
= wxRect(wxPoint(0,0), imageSize
);
11361 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
11363 wxSize overallSize
= marginRect
.GetSize();
11365 if (partialExtents
)
11366 partialExtents
->Add(overallSize
.x
);
11368 size
= overallSize
;
11373 // Get the 'natural' size for an object. For an image, it would be the
11375 wxTextAttrSize
wxRichTextImage::GetNaturalSize() const
11377 wxTextAttrSize size
;
11378 if (GetImageCache().IsOk())
11380 size
.SetWidth(GetImageCache().GetWidth(), wxTEXT_ATTR_UNITS_PIXELS
);
11381 size
.SetHeight(GetImageCache().GetHeight(), wxTEXT_ATTR_UNITS_PIXELS
);
11388 void wxRichTextImage::Copy(const wxRichTextImage
& obj
)
11390 wxRichTextObject::Copy(obj
);
11392 m_imageBlock
= obj
.m_imageBlock
;
11393 m_originalImageSize
= obj
.m_originalImageSize
;
11396 /// Edit properties via a GUI
11397 bool wxRichTextImage::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
11399 wxRichTextObjectPropertiesDialog
imageDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Picture Properties"));
11400 imageDlg
.SetAttributes(GetAttributes());
11402 if (imageDlg
.ShowModal() == wxID_OK
)
11404 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
11405 // indeterminate in the object.
11406 imageDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
11418 /// Compare two attribute objects
11419 bool wxTextAttrEq(const wxRichTextAttr
& attr1
, const wxRichTextAttr
& attr2
)
11421 return (attr1
== attr2
);
11425 bool wxRichTextTabsEq(const wxArrayInt
& tabs1
, const wxArrayInt
& tabs2
)
11427 if (tabs1
.GetCount() != tabs2
.GetCount())
11431 for (i
= 0; i
< tabs1
.GetCount(); i
++)
11433 if (tabs1
[i
] != tabs2
[i
])
11439 bool wxRichTextApplyStyle(wxRichTextAttr
& destStyle
, const wxRichTextAttr
& style
, wxRichTextAttr
* compareWith
)
11441 return destStyle
.Apply(style
, compareWith
);
11444 // Remove attributes
11445 bool wxRichTextRemoveStyle(wxRichTextAttr
& destStyle
, const wxRichTextAttr
& style
)
11447 return destStyle
.RemoveStyle(style
);
11450 /// Combine two bitlists, specifying the bits of interest with separate flags.
11451 bool wxRichTextCombineBitlists(int& valueA
, int valueB
, int& flagsA
, int flagsB
)
11453 return wxRichTextAttr::CombineBitlists(valueA
, valueB
, flagsA
, flagsB
);
11456 /// Compare two bitlists
11457 bool wxRichTextBitlistsEqPartial(int valueA
, int valueB
, int flags
)
11459 return wxRichTextAttr::BitlistsEqPartial(valueA
, valueB
, flags
);
11462 /// Split into paragraph and character styles
11463 bool wxRichTextSplitParaCharStyles(const wxRichTextAttr
& style
, wxRichTextAttr
& parStyle
, wxRichTextAttr
& charStyle
)
11465 return wxRichTextAttr::SplitParaCharStyles(style
, parStyle
, charStyle
);
11468 /// Convert a decimal to Roman numerals
11469 wxString
wxRichTextDecimalToRoman(long n
)
11471 static wxArrayInt decimalNumbers
;
11472 static wxArrayString romanNumbers
;
11477 decimalNumbers
.Clear();
11478 romanNumbers
.Clear();
11479 return wxEmptyString
;
11482 if (decimalNumbers
.GetCount() == 0)
11484 #define wxRichTextAddDecRom(n, r) decimalNumbers.Add(n); romanNumbers.Add(r);
11486 wxRichTextAddDecRom(1000, wxT("M"));
11487 wxRichTextAddDecRom(900, wxT("CM"));
11488 wxRichTextAddDecRom(500, wxT("D"));
11489 wxRichTextAddDecRom(400, wxT("CD"));
11490 wxRichTextAddDecRom(100, wxT("C"));
11491 wxRichTextAddDecRom(90, wxT("XC"));
11492 wxRichTextAddDecRom(50, wxT("L"));
11493 wxRichTextAddDecRom(40, wxT("XL"));
11494 wxRichTextAddDecRom(10, wxT("X"));
11495 wxRichTextAddDecRom(9, wxT("IX"));
11496 wxRichTextAddDecRom(5, wxT("V"));
11497 wxRichTextAddDecRom(4, wxT("IV"));
11498 wxRichTextAddDecRom(1, wxT("I"));
11504 while (n
> 0 && i
< 13)
11506 if (n
>= decimalNumbers
[i
])
11508 n
-= decimalNumbers
[i
];
11509 roman
+= romanNumbers
[i
];
11516 if (roman
.IsEmpty())
11522 * wxRichTextFileHandler
11523 * Base class for file handlers
11526 IMPLEMENT_CLASS(wxRichTextFileHandler
, wxObject
)
11528 #if wxUSE_FFILE && wxUSE_STREAMS
11529 bool wxRichTextFileHandler::LoadFile(wxRichTextBuffer
*buffer
, const wxString
& filename
)
11531 wxFFileInputStream
stream(filename
);
11533 return LoadFile(buffer
, stream
);
11538 bool wxRichTextFileHandler::SaveFile(wxRichTextBuffer
*buffer
, const wxString
& filename
)
11540 wxFFileOutputStream
stream(filename
);
11542 return SaveFile(buffer
, stream
);
11546 #endif // wxUSE_FFILE && wxUSE_STREAMS
11548 /// Can we handle this filename (if using files)? By default, checks the extension.
11549 bool wxRichTextFileHandler::CanHandle(const wxString
& filename
) const
11551 wxString path
, file
, ext
;
11552 wxFileName::SplitPath(filename
, & path
, & file
, & ext
);
11554 return (ext
.Lower() == GetExtension());
11558 * wxRichTextTextHandler
11559 * Plain text handler
11562 IMPLEMENT_CLASS(wxRichTextPlainTextHandler
, wxRichTextFileHandler
)
11565 bool wxRichTextPlainTextHandler::DoLoadFile(wxRichTextBuffer
*buffer
, wxInputStream
& stream
)
11567 if (!stream
.IsOk())
11573 while (!stream
.Eof())
11575 int ch
= stream
.GetC();
11579 if (ch
== 10 && lastCh
!= 13)
11582 if (ch
> 0 && ch
!= 10)
11589 buffer
->ResetAndClearCommands();
11591 buffer
->AddParagraphs(str
);
11592 buffer
->UpdateRanges();
11597 bool wxRichTextPlainTextHandler::DoSaveFile(wxRichTextBuffer
*buffer
, wxOutputStream
& stream
)
11599 if (!stream
.IsOk())
11602 wxString text
= buffer
->GetText();
11604 wxString newLine
= wxRichTextLineBreakChar
;
11605 text
.Replace(newLine
, wxT("\n"));
11607 wxCharBuffer buf
= text
.ToAscii();
11609 stream
.Write((const char*) buf
, text
.length());
11612 #endif // wxUSE_STREAMS
11615 * Stores information about an image, in binary in-memory form
11618 wxRichTextImageBlock::wxRichTextImageBlock()
11623 wxRichTextImageBlock::wxRichTextImageBlock(const wxRichTextImageBlock
& block
):wxObject()
11629 wxRichTextImageBlock::~wxRichTextImageBlock()
11634 void wxRichTextImageBlock::Init()
11638 m_imageType
= wxBITMAP_TYPE_INVALID
;
11641 void wxRichTextImageBlock::Clear()
11645 m_imageType
= wxBITMAP_TYPE_INVALID
;
11649 // Load the original image into a memory block.
11650 // If the image is not a JPEG, we must convert it into a JPEG
11651 // to conserve space.
11652 // If it's not a JPEG we can make use of 'image', already scaled, so we don't have to
11653 // load the image a 2nd time.
11655 bool wxRichTextImageBlock::MakeImageBlock(const wxString
& filename
, wxBitmapType imageType
,
11656 wxImage
& image
, bool convertToJPEG
)
11658 m_imageType
= imageType
;
11660 wxString
filenameToRead(filename
);
11661 bool removeFile
= false;
11663 if (imageType
== wxBITMAP_TYPE_INVALID
)
11664 return false; // Could not determine image type
11666 if ((imageType
!= wxBITMAP_TYPE_JPEG
) && convertToJPEG
)
11668 wxString tempFile
=
11669 wxFileName::CreateTempFileName(_("image"));
11671 wxASSERT(!tempFile
.IsEmpty());
11673 image
.SaveFile(tempFile
, wxBITMAP_TYPE_JPEG
);
11674 filenameToRead
= tempFile
;
11677 m_imageType
= wxBITMAP_TYPE_JPEG
;
11680 if (!file
.Open(filenameToRead
))
11683 m_dataSize
= (size_t) file
.Length();
11688 m_data
= ReadBlock(filenameToRead
, m_dataSize
);
11691 wxRemoveFile(filenameToRead
);
11693 return (m_data
!= NULL
);
11696 // Make an image block from the wxImage in the given
11698 bool wxRichTextImageBlock::MakeImageBlock(wxImage
& image
, wxBitmapType imageType
, int quality
)
11700 image
.SetOption(wxT("quality"), quality
);
11702 if (imageType
== wxBITMAP_TYPE_INVALID
)
11703 return false; // Could not determine image type
11705 return DoMakeImageBlock(image
, imageType
);
11708 // Uses a const wxImage for efficiency, but can't set quality (only relevant for JPEG)
11709 bool wxRichTextImageBlock::MakeImageBlockDefaultQuality(const wxImage
& image
, wxBitmapType imageType
)
11711 if (imageType
== wxBITMAP_TYPE_INVALID
)
11712 return false; // Could not determine image type
11714 return DoMakeImageBlock(image
, imageType
);
11717 // Makes the image block
11718 bool wxRichTextImageBlock::DoMakeImageBlock(const wxImage
& image
, wxBitmapType imageType
)
11720 wxMemoryOutputStream memStream
;
11721 if (!image
.SaveFile(memStream
, imageType
))
11726 unsigned char* block
= new unsigned char[memStream
.GetSize()];
11734 m_imageType
= imageType
;
11735 m_dataSize
= memStream
.GetSize();
11737 memStream
.CopyTo(m_data
, m_dataSize
);
11739 return (m_data
!= NULL
);
11743 bool wxRichTextImageBlock::Write(const wxString
& filename
)
11745 return WriteBlock(filename
, m_data
, m_dataSize
);
11748 void wxRichTextImageBlock::Copy(const wxRichTextImageBlock
& block
)
11750 m_imageType
= block
.m_imageType
;
11752 m_dataSize
= block
.m_dataSize
;
11753 if (m_dataSize
== 0)
11756 m_data
= new unsigned char[m_dataSize
];
11758 for (i
= 0; i
< m_dataSize
; i
++)
11759 m_data
[i
] = block
.m_data
[i
];
11763 void wxRichTextImageBlock::operator=(const wxRichTextImageBlock
& block
)
11768 // Load a wxImage from the block
11769 bool wxRichTextImageBlock::Load(wxImage
& image
)
11774 // Read in the image.
11776 wxMemoryInputStream
mstream(m_data
, m_dataSize
);
11777 bool success
= image
.LoadFile(mstream
, GetImageType());
11779 wxString tempFile
= wxFileName::CreateTempFileName(_("image"));
11780 wxASSERT(!tempFile
.IsEmpty());
11782 if (!WriteBlock(tempFile
, m_data
, m_dataSize
))
11786 success
= image
.LoadFile(tempFile
, GetImageType());
11787 wxRemoveFile(tempFile
);
11793 // Write data in hex to a stream
11794 bool wxRichTextImageBlock::WriteHex(wxOutputStream
& stream
)
11796 if (m_dataSize
== 0)
11799 int bufSize
= 100000;
11800 if (int(2*m_dataSize
) < bufSize
)
11801 bufSize
= 2*m_dataSize
;
11802 char* buf
= new char[bufSize
+1];
11804 int left
= m_dataSize
;
11809 if (left
*2 > bufSize
)
11811 n
= bufSize
; left
-= (bufSize
/2);
11815 n
= left
*2; left
= 0;
11819 for (i
= 0; i
< (n
/2); i
++)
11821 wxDecToHex(m_data
[j
], b
, b
+1);
11826 stream
.Write((const char*) buf
, n
);
11832 // Read data in hex from a stream
11833 bool wxRichTextImageBlock::ReadHex(wxInputStream
& stream
, int length
, wxBitmapType imageType
)
11835 int dataSize
= length
/2;
11840 // create a null terminated temporary string:
11844 m_data
= new unsigned char[dataSize
];
11846 for (i
= 0; i
< dataSize
; i
++)
11848 str
[0] = (char)stream
.GetC();
11849 str
[1] = (char)stream
.GetC();
11851 m_data
[i
] = (unsigned char)wxHexToDec(str
);
11854 m_dataSize
= dataSize
;
11855 m_imageType
= imageType
;
11860 // Allocate and read from stream as a block of memory
11861 unsigned char* wxRichTextImageBlock::ReadBlock(wxInputStream
& stream
, size_t size
)
11863 unsigned char* block
= new unsigned char[size
];
11867 stream
.Read(block
, size
);
11872 unsigned char* wxRichTextImageBlock::ReadBlock(const wxString
& filename
, size_t size
)
11874 wxFileInputStream
stream(filename
);
11875 if (!stream
.IsOk())
11878 return ReadBlock(stream
, size
);
11881 // Write memory block to stream
11882 bool wxRichTextImageBlock::WriteBlock(wxOutputStream
& stream
, unsigned char* block
, size_t size
)
11884 stream
.Write((void*) block
, size
);
11885 return stream
.IsOk();
11889 // Write memory block to file
11890 bool wxRichTextImageBlock::WriteBlock(const wxString
& filename
, unsigned char* block
, size_t size
)
11892 wxFileOutputStream
outStream(filename
);
11893 if (!outStream
.IsOk())
11896 return WriteBlock(outStream
, block
, size
);
11899 // Gets the extension for the block's type
11900 wxString
wxRichTextImageBlock::GetExtension() const
11902 wxImageHandler
* handler
= wxImage::FindHandler(GetImageType());
11904 return handler
->GetExtension();
11906 return wxEmptyString
;
11912 * The data object for a wxRichTextBuffer
11915 const wxChar
*wxRichTextBufferDataObject::ms_richTextBufferFormatId
= wxT("wxRichText");
11917 wxRichTextBufferDataObject::wxRichTextBufferDataObject(wxRichTextBuffer
* richTextBuffer
)
11919 m_richTextBuffer
= richTextBuffer
;
11921 // this string should uniquely identify our format, but is otherwise
11923 m_formatRichTextBuffer
.SetId(GetRichTextBufferFormatId());
11925 SetFormat(m_formatRichTextBuffer
);
11928 wxRichTextBufferDataObject::~wxRichTextBufferDataObject()
11930 delete m_richTextBuffer
;
11933 // after a call to this function, the richTextBuffer is owned by the caller and it
11934 // is responsible for deleting it!
11935 wxRichTextBuffer
* wxRichTextBufferDataObject::GetRichTextBuffer()
11937 wxRichTextBuffer
* richTextBuffer
= m_richTextBuffer
;
11938 m_richTextBuffer
= NULL
;
11940 return richTextBuffer
;
11943 wxDataFormat
wxRichTextBufferDataObject::GetPreferredFormat(Direction
WXUNUSED(dir
)) const
11945 return m_formatRichTextBuffer
;
11948 size_t wxRichTextBufferDataObject::GetDataSize() const
11950 if (!m_richTextBuffer
)
11956 wxStringOutputStream
stream(& bufXML
);
11957 if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
))
11959 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
11965 wxCharBuffer buffer
= bufXML
.mb_str(wxConvUTF8
);
11966 return strlen(buffer
) + 1;
11968 return bufXML
.Length()+1;
11972 bool wxRichTextBufferDataObject::GetDataHere(void *pBuf
) const
11974 if (!pBuf
|| !m_richTextBuffer
)
11980 wxStringOutputStream
stream(& bufXML
);
11981 if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
))
11983 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
11989 wxCharBuffer buffer
= bufXML
.mb_str(wxConvUTF8
);
11990 size_t len
= strlen(buffer
);
11991 memcpy((char*) pBuf
, (const char*) buffer
, len
);
11992 ((char*) pBuf
)[len
] = 0;
11994 size_t len
= bufXML
.Length();
11995 memcpy((char*) pBuf
, (const char*) bufXML
.c_str(), len
);
11996 ((char*) pBuf
)[len
] = 0;
12002 bool wxRichTextBufferDataObject::SetData(size_t WXUNUSED(len
), const void *buf
)
12004 wxDELETE(m_richTextBuffer
);
12006 wxString
bufXML((const char*) buf
, wxConvUTF8
);
12008 m_richTextBuffer
= new wxRichTextBuffer
;
12010 wxStringInputStream
stream(bufXML
);
12011 if (!m_richTextBuffer
->LoadFile(stream
, wxRICHTEXT_TYPE_XML
))
12013 wxLogError(wxT("Could not read the buffer from an XML stream.\nYou may have forgotten to add the XML file handler."));
12015 wxDELETE(m_richTextBuffer
);
12027 * wxRichTextFontTable
12028 * Manages quick access to a pool of fonts for rendering rich text
12031 WX_DECLARE_STRING_HASH_MAP_WITH_DECL(wxFont
, wxRichTextFontTableHashMap
, class WXDLLIMPEXP_RICHTEXT
);
12033 class wxRichTextFontTableData
: public wxObjectRefData
12036 wxRichTextFontTableData() {}
12038 wxFont
FindFont(const wxRichTextAttr
& fontSpec
, double fontScale
);
12040 wxRichTextFontTableHashMap m_hashMap
;
12043 wxFont
wxRichTextFontTableData::FindFont(const wxRichTextAttr
& fontSpec
, double fontScale
)
12045 wxString
facename(fontSpec
.GetFontFaceName());
12047 int fontSize
= fontSpec
.GetFontSize();
12048 if (fontScale
!= 1.0)
12049 fontSize
= (int) ((double(fontSize
) * fontScale
) + 0.5);
12052 if (fontSpec
.HasFontPixelSize() && !fontSpec
.HasFontPointSize())
12056 wxString spec
= wxString::Format(wxT("%d-%s-%d-%d-%d-%d-%s-%d"),
12057 fontSize
, units
.c_str(), fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), (int) fontSpec
.GetFontUnderlined(), (int) fontSpec
.GetFontStrikethrough(),
12058 facename
.c_str(), (int) fontSpec
.GetFontEncoding());
12060 wxRichTextFontTableHashMap::iterator entry
= m_hashMap
.find(spec
);
12061 if ( entry
== m_hashMap
.end() )
12063 if (fontSpec
.HasFontPixelSize() && !fontSpec
.HasFontPointSize())
12065 wxFont
font(wxSize(0, fontSize
), wxFONTFAMILY_DEFAULT
, fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), fontSpec
.GetFontUnderlined(), facename
);
12066 if (fontSpec
.HasFontStrikethrough() && fontSpec
.GetFontStrikethrough())
12067 font
.SetStrikethrough(true);
12068 m_hashMap
[spec
] = font
;
12073 wxFont
font(fontSize
, wxFONTFAMILY_DEFAULT
, fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), fontSpec
.GetFontUnderlined(), facename
.c_str());
12074 if (fontSpec
.HasFontStrikethrough() && fontSpec
.GetFontStrikethrough())
12075 font
.SetStrikethrough(true);
12077 m_hashMap
[spec
] = font
;
12083 return entry
->second
;
12087 IMPLEMENT_DYNAMIC_CLASS(wxRichTextFontTable
, wxObject
)
12089 wxRichTextFontTable::wxRichTextFontTable()
12091 m_refData
= new wxRichTextFontTableData
;
12095 wxRichTextFontTable::wxRichTextFontTable(const wxRichTextFontTable
& table
)
12101 wxRichTextFontTable::~wxRichTextFontTable()
12106 bool wxRichTextFontTable::operator == (const wxRichTextFontTable
& table
) const
12108 return (m_refData
== table
.m_refData
);
12111 void wxRichTextFontTable::operator= (const wxRichTextFontTable
& table
)
12114 m_fontScale
= table
.m_fontScale
;
12117 wxFont
wxRichTextFontTable::FindFont(const wxRichTextAttr
& fontSpec
)
12119 wxRichTextFontTableData
* data
= (wxRichTextFontTableData
*) m_refData
;
12121 return data
->FindFont(fontSpec
, m_fontScale
);
12126 void wxRichTextFontTable::Clear()
12128 wxRichTextFontTableData
* data
= (wxRichTextFontTableData
*) m_refData
;
12130 data
->m_hashMap
.clear();
12133 void wxRichTextFontTable::SetFontScale(double fontScale
)
12135 if (fontScale
!= m_fontScale
)
12137 m_fontScale
= fontScale
;
12142 void wxTextBoxAttr::Reset()
12145 m_floatMode
= wxTEXT_BOX_ATTR_FLOAT_NONE
;
12146 m_clearMode
= wxTEXT_BOX_ATTR_CLEAR_NONE
;
12147 m_collapseMode
= wxTEXT_BOX_ATTR_COLLAPSE_NONE
;
12148 m_verticalAlignment
= wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_NONE
;
12149 m_boxStyleName
= wxEmptyString
;
12153 m_position
.Reset();
12164 bool wxTextBoxAttr::operator== (const wxTextBoxAttr
& attr
) const
12167 m_flags
== attr
.m_flags
&&
12168 m_floatMode
== attr
.m_floatMode
&&
12169 m_clearMode
== attr
.m_clearMode
&&
12170 m_collapseMode
== attr
.m_collapseMode
&&
12171 m_verticalAlignment
== attr
.m_verticalAlignment
&&
12173 m_margins
== attr
.m_margins
&&
12174 m_padding
== attr
.m_padding
&&
12175 m_position
== attr
.m_position
&&
12177 m_size
== attr
.m_size
&&
12178 m_minSize
== attr
.m_minSize
&&
12179 m_maxSize
== attr
.m_maxSize
&&
12181 m_border
== attr
.m_border
&&
12182 m_outline
== attr
.m_outline
&&
12184 m_boxStyleName
== attr
.m_boxStyleName
12188 // Partial equality test
12189 bool wxTextBoxAttr::EqPartial(const wxTextBoxAttr
& attr
, bool weakTest
) const
12192 ((!HasFloatMode() && attr
.HasFloatMode()) ||
12193 (!HasClearMode() && attr
.HasClearMode()) ||
12194 (!HasCollapseBorders() && attr
.HasCollapseBorders()) ||
12195 (!HasVerticalAlignment() && attr
.HasVerticalAlignment()) ||
12196 (!HasBoxStyleName() && attr
.HasBoxStyleName())))
12200 if (attr
.HasFloatMode() && HasFloatMode() && (GetFloatMode() != attr
.GetFloatMode()))
12203 if (attr
.HasClearMode() && HasClearMode() && (GetClearMode() != attr
.GetClearMode()))
12206 if (attr
.HasCollapseBorders() && HasCollapseBorders() && (attr
.GetCollapseBorders() != GetCollapseBorders()))
12209 if (attr
.HasVerticalAlignment() && HasVerticalAlignment() && (attr
.GetVerticalAlignment() != GetVerticalAlignment()))
12212 if (attr
.HasBoxStyleName() && HasBoxStyleName() && (attr
.GetBoxStyleName() != GetBoxStyleName()))
12217 if (!m_position
.EqPartial(attr
.m_position
, weakTest
))
12222 if (!m_size
.EqPartial(attr
.m_size
, weakTest
))
12224 if (!m_minSize
.EqPartial(attr
.m_minSize
, weakTest
))
12226 if (!m_maxSize
.EqPartial(attr
.m_maxSize
, weakTest
))
12231 if (!m_margins
.EqPartial(attr
.m_margins
, weakTest
))
12236 if (!m_padding
.EqPartial(attr
.m_padding
, weakTest
))
12241 if (!GetBorder().EqPartial(attr
.GetBorder(), weakTest
))
12246 if (!GetOutline().EqPartial(attr
.GetOutline(), weakTest
))
12252 // Merges the given attributes. If compareWith
12253 // is non-NULL, then it will be used to mask out those attributes that are the same in style
12254 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
12255 bool wxTextBoxAttr::Apply(const wxTextBoxAttr
& attr
, const wxTextBoxAttr
* compareWith
)
12257 if (attr
.HasFloatMode())
12259 if (!(compareWith
&& compareWith
->HasFloatMode() && compareWith
->GetFloatMode() == attr
.GetFloatMode()))
12260 SetFloatMode(attr
.GetFloatMode());
12263 if (attr
.HasClearMode())
12265 if (!(compareWith
&& compareWith
->HasClearMode() && compareWith
->GetClearMode() == attr
.GetClearMode()))
12266 SetClearMode(attr
.GetClearMode());
12269 if (attr
.HasCollapseBorders())
12271 if (!(compareWith
&& compareWith
->HasCollapseBorders() && compareWith
->GetCollapseBorders() == attr
.GetCollapseBorders()))
12272 SetCollapseBorders(attr
.GetCollapseBorders());
12275 if (attr
.HasVerticalAlignment())
12277 if (!(compareWith
&& compareWith
->HasVerticalAlignment() && compareWith
->GetVerticalAlignment() == attr
.GetVerticalAlignment()))
12278 SetVerticalAlignment(attr
.GetVerticalAlignment());
12281 if (attr
.HasBoxStyleName())
12283 if (!(compareWith
&& compareWith
->HasBoxStyleName() && compareWith
->GetBoxStyleName() == attr
.GetBoxStyleName()))
12284 SetBoxStyleName(attr
.GetBoxStyleName());
12287 m_margins
.Apply(attr
.m_margins
, compareWith
? (& attr
.m_margins
) : (const wxTextAttrDimensions
*) NULL
);
12288 m_padding
.Apply(attr
.m_padding
, compareWith
? (& attr
.m_padding
) : (const wxTextAttrDimensions
*) NULL
);
12289 m_position
.Apply(attr
.m_position
, compareWith
? (& attr
.m_position
) : (const wxTextAttrDimensions
*) NULL
);
12291 m_size
.Apply(attr
.m_size
, compareWith
? (& attr
.m_size
) : (const wxTextAttrSize
*) NULL
);
12292 m_minSize
.Apply(attr
.m_minSize
, compareWith
? (& attr
.m_minSize
) : (const wxTextAttrSize
*) NULL
);
12293 m_maxSize
.Apply(attr
.m_maxSize
, compareWith
? (& attr
.m_maxSize
) : (const wxTextAttrSize
*) NULL
);
12295 m_border
.Apply(attr
.m_border
, compareWith
? (& attr
.m_border
) : (const wxTextAttrBorders
*) NULL
);
12296 m_outline
.Apply(attr
.m_outline
, compareWith
? (& attr
.m_outline
) : (const wxTextAttrBorders
*) NULL
);
12301 // Remove specified attributes from this object
12302 bool wxTextBoxAttr::RemoveStyle(const wxTextBoxAttr
& attr
)
12304 if (attr
.HasFloatMode())
12305 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT
);
12307 if (attr
.HasClearMode())
12308 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR
);
12310 if (attr
.HasCollapseBorders())
12311 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
12313 if (attr
.HasVerticalAlignment())
12314 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
12316 if (attr
.HasBoxStyleName())
12318 SetBoxStyleName(wxEmptyString
);
12319 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
12322 m_margins
.RemoveStyle(attr
.m_margins
);
12323 m_padding
.RemoveStyle(attr
.m_padding
);
12324 m_position
.RemoveStyle(attr
.m_position
);
12326 m_size
.RemoveStyle(attr
.m_size
);
12327 m_minSize
.RemoveStyle(attr
.m_minSize
);
12328 m_maxSize
.RemoveStyle(attr
.m_maxSize
);
12330 m_border
.RemoveStyle(attr
.m_border
);
12331 m_outline
.RemoveStyle(attr
.m_outline
);
12336 // Collects the attributes that are common to a range of content, building up a note of
12337 // which attributes are absent in some objects and which clash in some objects.
12338 void wxTextBoxAttr::CollectCommonAttributes(const wxTextBoxAttr
& attr
, wxTextBoxAttr
& clashingAttr
, wxTextBoxAttr
& absentAttr
)
12340 if (attr
.HasFloatMode())
12342 if (!clashingAttr
.HasFloatMode() && !absentAttr
.HasFloatMode())
12344 if (HasFloatMode())
12346 if (GetFloatMode() != attr
.GetFloatMode())
12348 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_FLOAT
);
12349 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT
);
12353 SetFloatMode(attr
.GetFloatMode());
12357 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_FLOAT
);
12359 if (attr
.HasClearMode())
12361 if (!clashingAttr
.HasClearMode() && !absentAttr
.HasClearMode())
12363 if (HasClearMode())
12365 if (GetClearMode() != attr
.GetClearMode())
12367 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_CLEAR
);
12368 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR
);
12372 SetClearMode(attr
.GetClearMode());
12376 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_CLEAR
);
12378 if (attr
.HasCollapseBorders())
12380 if (!clashingAttr
.HasCollapseBorders() && !absentAttr
.HasCollapseBorders())
12382 if (HasCollapseBorders())
12384 if (GetCollapseBorders() != attr
.GetCollapseBorders())
12386 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
12387 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
12391 SetCollapseBorders(attr
.GetCollapseBorders());
12395 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
12397 if (attr
.HasVerticalAlignment())
12399 if (!clashingAttr
.HasVerticalAlignment() && !absentAttr
.HasVerticalAlignment())
12401 if (HasVerticalAlignment())
12403 if (GetVerticalAlignment() != attr
.GetVerticalAlignment())
12405 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
12406 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
12410 SetVerticalAlignment(attr
.GetVerticalAlignment());
12414 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
12416 if (attr
.HasBoxStyleName())
12418 if (!clashingAttr
.HasBoxStyleName() && !absentAttr
.HasBoxStyleName())
12420 if (HasBoxStyleName())
12422 if (GetBoxStyleName() != attr
.GetBoxStyleName())
12424 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
12425 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
12429 SetBoxStyleName(attr
.GetBoxStyleName());
12433 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
12435 m_margins
.CollectCommonAttributes(attr
.m_margins
, clashingAttr
.m_margins
, absentAttr
.m_margins
);
12436 m_padding
.CollectCommonAttributes(attr
.m_padding
, clashingAttr
.m_padding
, absentAttr
.m_padding
);
12437 m_position
.CollectCommonAttributes(attr
.m_position
, clashingAttr
.m_position
, absentAttr
.m_position
);
12439 m_size
.CollectCommonAttributes(attr
.m_size
, clashingAttr
.m_size
, absentAttr
.m_size
);
12440 m_minSize
.CollectCommonAttributes(attr
.m_minSize
, clashingAttr
.m_minSize
, absentAttr
.m_minSize
);
12441 m_maxSize
.CollectCommonAttributes(attr
.m_maxSize
, clashingAttr
.m_maxSize
, absentAttr
.m_maxSize
);
12443 m_border
.CollectCommonAttributes(attr
.m_border
, clashingAttr
.m_border
, absentAttr
.m_border
);
12444 m_outline
.CollectCommonAttributes(attr
.m_outline
, clashingAttr
.m_outline
, absentAttr
.m_outline
);
12447 bool wxTextBoxAttr::IsDefault() const
12449 return GetFlags() == 0 && !m_border
.IsValid() && !m_outline
.IsValid() &&
12450 !m_size
.IsValid() && !m_minSize
.IsValid() && !m_maxSize
.IsValid() &&
12451 !m_position
.IsValid() && !m_padding
.IsValid() && !m_margins
.IsValid();
12456 void wxRichTextAttr::Copy(const wxRichTextAttr
& attr
)
12458 wxTextAttr::Copy(attr
);
12460 m_textBoxAttr
= attr
.m_textBoxAttr
;
12463 bool wxRichTextAttr::operator==(const wxRichTextAttr
& attr
) const
12465 if (!(wxTextAttr::operator==(attr
)))
12468 return (m_textBoxAttr
== attr
.m_textBoxAttr
);
12471 // Partial equality test
12472 bool wxRichTextAttr::EqPartial(const wxRichTextAttr
& attr
, bool weakTest
) const
12474 if (!(wxTextAttr::EqPartial(attr
, weakTest
)))
12477 return m_textBoxAttr
.EqPartial(attr
.m_textBoxAttr
, weakTest
);
12480 // Merges the given attributes. If compareWith
12481 // is non-NULL, then it will be used to mask out those attributes that are the same in style
12482 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
12483 bool wxRichTextAttr::Apply(const wxRichTextAttr
& style
, const wxRichTextAttr
* compareWith
)
12485 wxTextAttr::Apply(style
, compareWith
);
12487 return m_textBoxAttr
.Apply(style
.m_textBoxAttr
, compareWith
? (& compareWith
->m_textBoxAttr
) : (const wxTextBoxAttr
*) NULL
);
12490 // Remove specified attributes from this object
12491 bool wxRichTextAttr::RemoveStyle(const wxRichTextAttr
& attr
)
12493 wxTextAttr::RemoveStyle(*this, attr
);
12495 return m_textBoxAttr
.RemoveStyle(attr
.m_textBoxAttr
);
12498 // Collects the attributes that are common to a range of content, building up a note of
12499 // which attributes are absent in some objects and which clash in some objects.
12500 void wxRichTextAttr::CollectCommonAttributes(const wxRichTextAttr
& attr
, wxRichTextAttr
& clashingAttr
, wxRichTextAttr
& absentAttr
)
12502 wxTextAttrCollectCommonAttributes(*this, attr
, clashingAttr
, absentAttr
);
12504 m_textBoxAttr
.CollectCommonAttributes(attr
.m_textBoxAttr
, clashingAttr
.m_textBoxAttr
, absentAttr
.m_textBoxAttr
);
12507 // Partial equality test
12508 bool wxTextAttrBorder::EqPartial(const wxTextAttrBorder
& border
, bool weakTest
) const
12511 ((!HasStyle() && border
.HasStyle()) ||
12512 (!HasColour() && border
.HasColour()) ||
12513 (!HasWidth() && border
.HasWidth())))
12518 if (border
.HasStyle() && HasStyle() && (border
.GetStyle() != GetStyle()))
12521 if (border
.HasColour() && HasColour() && (border
.GetColourLong() != GetColourLong()))
12524 if (border
.HasWidth() && HasWidth() && !(border
.GetWidth() == GetWidth()))
12530 // Apply border to 'this', but not if the same as compareWith
12531 bool wxTextAttrBorder::Apply(const wxTextAttrBorder
& border
, const wxTextAttrBorder
* compareWith
)
12533 if (border
.HasStyle())
12535 if (!(compareWith
&& (border
.GetStyle() == compareWith
->GetStyle())))
12536 SetStyle(border
.GetStyle());
12538 if (border
.HasColour())
12540 if (!(compareWith
&& (border
.GetColourLong() == compareWith
->GetColourLong())))
12541 SetColour(border
.GetColourLong());
12543 if (border
.HasWidth())
12545 if (!(compareWith
&& (border
.GetWidth() == compareWith
->GetWidth())))
12546 SetWidth(border
.GetWidth());
12552 // Remove specified attributes from this object
12553 bool wxTextAttrBorder::RemoveStyle(const wxTextAttrBorder
& attr
)
12555 if (attr
.HasStyle() && HasStyle())
12556 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_STYLE
);
12557 if (attr
.HasColour() && HasColour())
12558 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_COLOUR
);
12559 if (attr
.HasWidth() && HasWidth())
12560 m_borderWidth
.Reset();
12565 // Collects the attributes that are common to a range of content, building up a note of
12566 // which attributes are absent in some objects and which clash in some objects.
12567 void wxTextAttrBorder::CollectCommonAttributes(const wxTextAttrBorder
& attr
, wxTextAttrBorder
& clashingAttr
, wxTextAttrBorder
& absentAttr
)
12569 if (attr
.HasStyle())
12571 if (!clashingAttr
.HasStyle() && !absentAttr
.HasStyle())
12575 if (GetStyle() != attr
.GetStyle())
12577 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
12578 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
12582 SetStyle(attr
.GetStyle());
12586 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
12588 if (attr
.HasColour())
12590 if (!clashingAttr
.HasColour() && !absentAttr
.HasColour())
12594 if (GetColour() != attr
.GetColour())
12596 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
12597 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
12601 SetColour(attr
.GetColourLong());
12605 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
12607 m_borderWidth
.CollectCommonAttributes(attr
.m_borderWidth
, clashingAttr
.m_borderWidth
, absentAttr
.m_borderWidth
);
12610 // Partial equality test
12611 bool wxTextAttrBorders::EqPartial(const wxTextAttrBorders
& borders
, bool weakTest
) const
12613 return m_left
.EqPartial(borders
.m_left
, weakTest
) && m_right
.EqPartial(borders
.m_right
, weakTest
) &&
12614 m_top
.EqPartial(borders
.m_top
, weakTest
) && m_bottom
.EqPartial(borders
.m_bottom
, weakTest
);
12617 // Apply border to 'this', but not if the same as compareWith
12618 bool wxTextAttrBorders::Apply(const wxTextAttrBorders
& borders
, const wxTextAttrBorders
* compareWith
)
12620 m_left
.Apply(borders
.m_left
, compareWith
? (& compareWith
->m_left
) : (const wxTextAttrBorder
*) NULL
);
12621 m_right
.Apply(borders
.m_right
, compareWith
? (& compareWith
->m_right
) : (const wxTextAttrBorder
*) NULL
);
12622 m_top
.Apply(borders
.m_top
, compareWith
? (& compareWith
->m_top
) : (const wxTextAttrBorder
*) NULL
);
12623 m_bottom
.Apply(borders
.m_bottom
, compareWith
? (& compareWith
->m_bottom
) : (const wxTextAttrBorder
*) NULL
);
12627 // Remove specified attributes from this object
12628 bool wxTextAttrBorders::RemoveStyle(const wxTextAttrBorders
& attr
)
12630 m_left
.RemoveStyle(attr
.m_left
);
12631 m_right
.RemoveStyle(attr
.m_right
);
12632 m_top
.RemoveStyle(attr
.m_top
);
12633 m_bottom
.RemoveStyle(attr
.m_bottom
);
12637 // Collects the attributes that are common to a range of content, building up a note of
12638 // which attributes are absent in some objects and which clash in some objects.
12639 void wxTextAttrBorders::CollectCommonAttributes(const wxTextAttrBorders
& attr
, wxTextAttrBorders
& clashingAttr
, wxTextAttrBorders
& absentAttr
)
12641 m_left
.CollectCommonAttributes(attr
.m_left
, clashingAttr
.m_left
, absentAttr
.m_left
);
12642 m_right
.CollectCommonAttributes(attr
.m_right
, clashingAttr
.m_right
, absentAttr
.m_right
);
12643 m_top
.CollectCommonAttributes(attr
.m_top
, clashingAttr
.m_top
, absentAttr
.m_top
);
12644 m_bottom
.CollectCommonAttributes(attr
.m_bottom
, clashingAttr
.m_bottom
, absentAttr
.m_bottom
);
12647 // Set style of all borders
12648 void wxTextAttrBorders::SetStyle(int style
)
12650 m_left
.SetStyle(style
);
12651 m_right
.SetStyle(style
);
12652 m_top
.SetStyle(style
);
12653 m_bottom
.SetStyle(style
);
12656 // Set colour of all borders
12657 void wxTextAttrBorders::SetColour(unsigned long colour
)
12659 m_left
.SetColour(colour
);
12660 m_right
.SetColour(colour
);
12661 m_top
.SetColour(colour
);
12662 m_bottom
.SetColour(colour
);
12665 void wxTextAttrBorders::SetColour(const wxColour
& colour
)
12667 m_left
.SetColour(colour
);
12668 m_right
.SetColour(colour
);
12669 m_top
.SetColour(colour
);
12670 m_bottom
.SetColour(colour
);
12673 // Set width of all borders
12674 void wxTextAttrBorders::SetWidth(const wxTextAttrDimension
& width
)
12676 m_left
.SetWidth(width
);
12677 m_right
.SetWidth(width
);
12678 m_top
.SetWidth(width
);
12679 m_bottom
.SetWidth(width
);
12682 // Partial equality test
12683 bool wxTextAttrDimension::EqPartial(const wxTextAttrDimension
& dim
, bool weakTest
) const
12685 if (!weakTest
&& !IsValid() && dim
.IsValid())
12688 if (dim
.IsValid() && IsValid() && !((*this) == dim
))
12694 bool wxTextAttrDimension::Apply(const wxTextAttrDimension
& dim
, const wxTextAttrDimension
* compareWith
)
12698 if (!(compareWith
&& dim
== (*compareWith
)))
12705 // Collects the attributes that are common to a range of content, building up a note of
12706 // which attributes are absent in some objects and which clash in some objects.
12707 void wxTextAttrDimension::CollectCommonAttributes(const wxTextAttrDimension
& attr
, wxTextAttrDimension
& clashingAttr
, wxTextAttrDimension
& absentAttr
)
12709 if (attr
.IsValid())
12711 if (!clashingAttr
.IsValid() && !absentAttr
.IsValid())
12715 if (!((*this) == attr
))
12717 clashingAttr
.SetValid(true);
12726 absentAttr
.SetValid(true);
12729 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(wxDC
& dc
, double scale
, const wxSize
& parentSize
)
12731 m_ppi
= dc
.GetPPI().x
; m_scale
= scale
; m_parentSize
= parentSize
;
12734 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(int ppi
, double scale
, const wxSize
& parentSize
)
12736 m_ppi
= ppi
; m_scale
= scale
; m_parentSize
= parentSize
;
12739 int wxTextAttrDimensionConverter::ConvertTenthsMMToPixels(int units
) const
12741 return wxRichTextObject::ConvertTenthsMMToPixels(m_ppi
, units
, m_scale
);
12744 int wxTextAttrDimensionConverter::ConvertPixelsToTenthsMM(int pixels
) const
12746 return wxRichTextObject::ConvertPixelsToTenthsMM(m_ppi
, pixels
, m_scale
);
12749 int wxTextAttrDimensionConverter::GetPixels(const wxTextAttrDimension
& dim
, int direction
) const
12751 if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
12752 return ConvertTenthsMMToPixels(dim
.GetValue());
12753 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
12754 return dim
.GetValue();
12755 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
12757 wxASSERT(m_parentSize
!= wxDefaultSize
);
12758 if (direction
== wxHORIZONTAL
)
12759 return (int) (double(m_parentSize
.x
) * double(dim
.GetValue()) / 100.0);
12761 return (int) (double(m_parentSize
.y
) * double(dim
.GetValue()) / 100.0);
12770 int wxTextAttrDimensionConverter::GetTenthsMM(const wxTextAttrDimension
& dim
) const
12772 if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
12773 return dim
.GetValue();
12774 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
12775 return ConvertPixelsToTenthsMM(dim
.GetValue());
12783 // Partial equality test
12784 bool wxTextAttrDimensions::EqPartial(const wxTextAttrDimensions
& dims
, bool weakTest
) const
12786 if (!m_left
.EqPartial(dims
.m_left
, weakTest
))
12789 if (!m_right
.EqPartial(dims
.m_right
, weakTest
))
12792 if (!m_top
.EqPartial(dims
.m_top
, weakTest
))
12795 if (!m_bottom
.EqPartial(dims
.m_bottom
, weakTest
))
12801 // Apply border to 'this', but not if the same as compareWith
12802 bool wxTextAttrDimensions::Apply(const wxTextAttrDimensions
& dims
, const wxTextAttrDimensions
* compareWith
)
12804 m_left
.Apply(dims
.m_left
, compareWith
? (& compareWith
->m_left
) : (const wxTextAttrDimension
*) NULL
);
12805 m_right
.Apply(dims
.m_right
, compareWith
? (& compareWith
->m_right
): (const wxTextAttrDimension
*) NULL
);
12806 m_top
.Apply(dims
.m_top
, compareWith
? (& compareWith
->m_top
): (const wxTextAttrDimension
*) NULL
);
12807 m_bottom
.Apply(dims
.m_bottom
, compareWith
? (& compareWith
->m_bottom
): (const wxTextAttrDimension
*) NULL
);
12812 // Remove specified attributes from this object
12813 bool wxTextAttrDimensions::RemoveStyle(const wxTextAttrDimensions
& attr
)
12815 if (attr
.m_left
.IsValid())
12817 if (attr
.m_right
.IsValid())
12819 if (attr
.m_top
.IsValid())
12821 if (attr
.m_bottom
.IsValid())
12827 // Collects the attributes that are common to a range of content, building up a note of
12828 // which attributes are absent in some objects and which clash in some objects.
12829 void wxTextAttrDimensions::CollectCommonAttributes(const wxTextAttrDimensions
& attr
, wxTextAttrDimensions
& clashingAttr
, wxTextAttrDimensions
& absentAttr
)
12831 m_left
.CollectCommonAttributes(attr
.m_left
, clashingAttr
.m_left
, absentAttr
.m_left
);
12832 m_right
.CollectCommonAttributes(attr
.m_right
, clashingAttr
.m_right
, absentAttr
.m_right
);
12833 m_top
.CollectCommonAttributes(attr
.m_top
, clashingAttr
.m_top
, absentAttr
.m_top
);
12834 m_bottom
.CollectCommonAttributes(attr
.m_bottom
, clashingAttr
.m_bottom
, absentAttr
.m_bottom
);
12837 // Partial equality test
12838 bool wxTextAttrSize::EqPartial(const wxTextAttrSize
& size
, bool weakTest
) const
12840 if (!m_width
.EqPartial(size
.m_width
, weakTest
))
12843 if (!m_height
.EqPartial(size
.m_height
, weakTest
))
12849 // Apply border to 'this', but not if the same as compareWith
12850 bool wxTextAttrSize::Apply(const wxTextAttrSize
& size
, const wxTextAttrSize
* compareWith
)
12852 m_width
.Apply(size
.m_width
, compareWith
? (& compareWith
->m_width
) : (const wxTextAttrDimension
*) NULL
);
12853 m_height
.Apply(size
.m_height
, compareWith
? (& compareWith
->m_height
): (const wxTextAttrDimension
*) NULL
);
12858 // Remove specified attributes from this object
12859 bool wxTextAttrSize::RemoveStyle(const wxTextAttrSize
& attr
)
12861 if (attr
.m_width
.IsValid())
12863 if (attr
.m_height
.IsValid())
12869 // Collects the attributes that are common to a range of content, building up a note of
12870 // which attributes are absent in some objects and which clash in some objects.
12871 void wxTextAttrSize::CollectCommonAttributes(const wxTextAttrSize
& attr
, wxTextAttrSize
& clashingAttr
, wxTextAttrSize
& absentAttr
)
12873 m_width
.CollectCommonAttributes(attr
.m_width
, clashingAttr
.m_width
, absentAttr
.m_width
);
12874 m_height
.CollectCommonAttributes(attr
.m_height
, clashingAttr
.m_height
, absentAttr
.m_height
);
12877 // Collects the attributes that are common to a range of content, building up a note of
12878 // which attributes are absent in some objects and which clash in some objects.
12879 void wxTextAttrCollectCommonAttributes(wxTextAttr
& currentStyle
, const wxTextAttr
& attr
, wxTextAttr
& clashingAttr
, wxTextAttr
& absentAttr
)
12881 absentAttr
.SetFlags(absentAttr
.GetFlags() | (~attr
.GetFlags() & wxTEXT_ATTR_ALL
));
12882 absentAttr
.SetTextEffectFlags(absentAttr
.GetTextEffectFlags() | (~attr
.GetTextEffectFlags() & 0xFFFF));
12884 long forbiddenFlags
= clashingAttr
.GetFlags()|absentAttr
.GetFlags();
12886 // If different font size units are being used, this is a clash.
12887 if (((attr
.GetFlags() & wxTEXT_ATTR_FONT_SIZE
) | (currentStyle
.GetFlags() & wxTEXT_ATTR_FONT_SIZE
)) == wxTEXT_ATTR_FONT_SIZE
)
12889 currentStyle
.SetFontSize(0);
12890 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_SIZE
);
12891 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_SIZE
);
12895 if (attr
.HasFontPointSize() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_POINT_SIZE
))
12897 if (currentStyle
.HasFontPointSize())
12899 if (currentStyle
.GetFontSize() != attr
.GetFontSize())
12901 // Clash of attr - mark as such
12902 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE
);
12903 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE
);
12907 currentStyle
.SetFontSize(attr
.GetFontSize());
12909 else if (!attr
.HasFontPointSize() && currentStyle
.HasFontPointSize())
12911 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE
);
12912 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE
);
12915 if (attr
.HasFontPixelSize() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_PIXEL_SIZE
))
12917 if (currentStyle
.HasFontPixelSize())
12919 if (currentStyle
.GetFontSize() != attr
.GetFontSize())
12921 // Clash of attr - mark as such
12922 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE
);
12923 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE
);
12927 currentStyle
.SetFontPixelSize(attr
.GetFontSize());
12929 else if (!attr
.HasFontPixelSize() && currentStyle
.HasFontPixelSize())
12931 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE
);
12932 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE
);
12936 if (attr
.HasFontItalic() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_ITALIC
))
12938 if (currentStyle
.HasFontItalic())
12940 if (currentStyle
.GetFontStyle() != attr
.GetFontStyle())
12942 // Clash of attr - mark as such
12943 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_ITALIC
);
12944 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC
);
12948 currentStyle
.SetFontStyle(attr
.GetFontStyle());
12950 else if (!attr
.HasFontItalic() && currentStyle
.HasFontItalic())
12952 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_ITALIC
);
12953 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC
);
12956 if (attr
.HasFontFamily() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_FAMILY
))
12958 if (currentStyle
.HasFontFamily())
12960 if (currentStyle
.GetFontFamily() != attr
.GetFontFamily())
12962 // Clash of attr - mark as such
12963 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FAMILY
);
12964 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY
);
12968 currentStyle
.SetFontFamily(attr
.GetFontFamily());
12970 else if (!attr
.HasFontFamily() && currentStyle
.HasFontFamily())
12972 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FAMILY
);
12973 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY
);
12976 if (attr
.HasFontWeight() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_WEIGHT
))
12978 if (currentStyle
.HasFontWeight())
12980 if (currentStyle
.GetFontWeight() != attr
.GetFontWeight())
12982 // Clash of attr - mark as such
12983 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_WEIGHT
);
12984 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT
);
12988 currentStyle
.SetFontWeight(attr
.GetFontWeight());
12990 else if (!attr
.HasFontWeight() && currentStyle
.HasFontWeight())
12992 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_WEIGHT
);
12993 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT
);
12996 if (attr
.HasFontFaceName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_FACE
))
12998 if (currentStyle
.HasFontFaceName())
13000 wxString
faceName1(currentStyle
.GetFontFaceName());
13001 wxString
faceName2(attr
.GetFontFaceName());
13003 if (faceName1
!= faceName2
)
13005 // Clash of attr - mark as such
13006 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FACE
);
13007 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FACE
);
13011 currentStyle
.SetFontFaceName(attr
.GetFontFaceName());
13013 else if (!attr
.HasFontFaceName() && currentStyle
.HasFontFaceName())
13015 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FACE
);
13016 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FACE
);
13019 if (attr
.HasFontUnderlined() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_UNDERLINE
))
13021 if (currentStyle
.HasFontUnderlined())
13023 if (currentStyle
.GetFontUnderlined() != attr
.GetFontUnderlined())
13025 // Clash of attr - mark as such
13026 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
13027 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
13031 currentStyle
.SetFontUnderlined(attr
.GetFontUnderlined());
13033 else if (!attr
.HasFontUnderlined() && currentStyle
.HasFontUnderlined())
13035 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
13036 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
13039 if (attr
.HasFontStrikethrough() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_STRIKETHROUGH
))
13041 if (currentStyle
.HasFontStrikethrough())
13043 if (currentStyle
.GetFontStrikethrough() != attr
.GetFontStrikethrough())
13045 // Clash of attr - mark as such
13046 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH
);
13047 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH
);
13051 currentStyle
.SetFontStrikethrough(attr
.GetFontStrikethrough());
13053 else if (!attr
.HasFontStrikethrough() && currentStyle
.HasFontStrikethrough())
13055 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH
);
13056 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH
);
13059 if (attr
.HasTextColour() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_TEXT_COLOUR
))
13061 if (currentStyle
.HasTextColour())
13063 if (currentStyle
.GetTextColour() != attr
.GetTextColour())
13065 // Clash of attr - mark as such
13066 clashingAttr
.AddFlag(wxTEXT_ATTR_TEXT_COLOUR
);
13067 currentStyle
.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR
);
13071 currentStyle
.SetTextColour(attr
.GetTextColour());
13073 else if (!attr
.HasTextColour() && currentStyle
.HasTextColour())
13075 clashingAttr
.AddFlag(wxTEXT_ATTR_TEXT_COLOUR
);
13076 currentStyle
.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR
);
13079 if (attr
.HasBackgroundColour() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BACKGROUND_COLOUR
))
13081 if (currentStyle
.HasBackgroundColour())
13083 if (currentStyle
.GetBackgroundColour() != attr
.GetBackgroundColour())
13085 // Clash of attr - mark as such
13086 clashingAttr
.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
13087 currentStyle
.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
13091 currentStyle
.SetBackgroundColour(attr
.GetBackgroundColour());
13093 else if (!attr
.HasBackgroundColour() && currentStyle
.HasBackgroundColour())
13095 clashingAttr
.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
13096 currentStyle
.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
13099 if (attr
.HasAlignment() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_ALIGNMENT
))
13101 if (currentStyle
.HasAlignment())
13103 if (currentStyle
.GetAlignment() != attr
.GetAlignment())
13105 // Clash of attr - mark as such
13106 clashingAttr
.AddFlag(wxTEXT_ATTR_ALIGNMENT
);
13107 currentStyle
.RemoveFlag(wxTEXT_ATTR_ALIGNMENT
);
13111 currentStyle
.SetAlignment(attr
.GetAlignment());
13113 else if (!attr
.HasAlignment() && currentStyle
.HasAlignment())
13115 clashingAttr
.AddFlag(wxTEXT_ATTR_ALIGNMENT
);
13116 currentStyle
.RemoveFlag(wxTEXT_ATTR_ALIGNMENT
);
13119 if (attr
.HasTabs() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_TABS
))
13121 if (currentStyle
.HasTabs())
13123 if (!wxRichTextTabsEq(currentStyle
.GetTabs(), attr
.GetTabs()))
13125 // Clash of attr - mark as such
13126 clashingAttr
.AddFlag(wxTEXT_ATTR_TABS
);
13127 currentStyle
.RemoveFlag(wxTEXT_ATTR_TABS
);
13131 currentStyle
.SetTabs(attr
.GetTabs());
13133 else if (!attr
.HasTabs() && currentStyle
.HasTabs())
13135 clashingAttr
.AddFlag(wxTEXT_ATTR_TABS
);
13136 currentStyle
.RemoveFlag(wxTEXT_ATTR_TABS
);
13139 if (attr
.HasLeftIndent() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LEFT_INDENT
))
13141 if (currentStyle
.HasLeftIndent())
13143 if (currentStyle
.GetLeftIndent() != attr
.GetLeftIndent() || currentStyle
.GetLeftSubIndent() != attr
.GetLeftSubIndent())
13145 // Clash of attr - mark as such
13146 clashingAttr
.AddFlag(wxTEXT_ATTR_LEFT_INDENT
);
13147 currentStyle
.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT
);
13151 currentStyle
.SetLeftIndent(attr
.GetLeftIndent(), attr
.GetLeftSubIndent());
13153 else if (!attr
.HasLeftIndent() && currentStyle
.HasLeftIndent())
13155 clashingAttr
.AddFlag(wxTEXT_ATTR_LEFT_INDENT
);
13156 currentStyle
.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT
);
13159 if (attr
.HasRightIndent() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_RIGHT_INDENT
))
13161 if (currentStyle
.HasRightIndent())
13163 if (currentStyle
.GetRightIndent() != attr
.GetRightIndent())
13165 // Clash of attr - mark as such
13166 clashingAttr
.AddFlag(wxTEXT_ATTR_RIGHT_INDENT
);
13167 currentStyle
.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT
);
13171 currentStyle
.SetRightIndent(attr
.GetRightIndent());
13173 else if (!attr
.HasRightIndent() && currentStyle
.HasRightIndent())
13175 clashingAttr
.AddFlag(wxTEXT_ATTR_RIGHT_INDENT
);
13176 currentStyle
.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT
);
13179 if (attr
.HasParagraphSpacingAfter() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARA_SPACING_AFTER
))
13181 if (currentStyle
.HasParagraphSpacingAfter())
13183 if (currentStyle
.GetParagraphSpacingAfter() != attr
.GetParagraphSpacingAfter())
13185 // Clash of attr - mark as such
13186 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
13187 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
13191 currentStyle
.SetParagraphSpacingAfter(attr
.GetParagraphSpacingAfter());
13193 else if (!attr
.HasParagraphSpacingAfter() && currentStyle
.HasParagraphSpacingAfter())
13195 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
13196 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
13199 if (attr
.HasParagraphSpacingBefore() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARA_SPACING_BEFORE
))
13201 if (currentStyle
.HasParagraphSpacingBefore())
13203 if (currentStyle
.GetParagraphSpacingBefore() != attr
.GetParagraphSpacingBefore())
13205 // Clash of attr - mark as such
13206 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
13207 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
13211 currentStyle
.SetParagraphSpacingBefore(attr
.GetParagraphSpacingBefore());
13213 else if (!attr
.HasParagraphSpacingBefore() && currentStyle
.HasParagraphSpacingBefore())
13215 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
13216 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
13219 if (attr
.HasLineSpacing() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LINE_SPACING
))
13221 if (currentStyle
.HasLineSpacing())
13223 if (currentStyle
.GetLineSpacing() != attr
.GetLineSpacing())
13225 // Clash of attr - mark as such
13226 clashingAttr
.AddFlag(wxTEXT_ATTR_LINE_SPACING
);
13227 currentStyle
.RemoveFlag(wxTEXT_ATTR_LINE_SPACING
);
13231 currentStyle
.SetLineSpacing(attr
.GetLineSpacing());
13233 else if (!attr
.HasLineSpacing() && currentStyle
.HasLineSpacing())
13235 clashingAttr
.AddFlag(wxTEXT_ATTR_LINE_SPACING
);
13236 currentStyle
.RemoveFlag(wxTEXT_ATTR_LINE_SPACING
);
13239 if (attr
.HasCharacterStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_CHARACTER_STYLE_NAME
))
13241 if (currentStyle
.HasCharacterStyleName())
13243 if (currentStyle
.GetCharacterStyleName() != attr
.GetCharacterStyleName())
13245 // Clash of attr - mark as such
13246 clashingAttr
.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
13247 currentStyle
.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
13251 currentStyle
.SetCharacterStyleName(attr
.GetCharacterStyleName());
13253 else if (!attr
.HasCharacterStyleName() && currentStyle
.HasCharacterStyleName())
13255 clashingAttr
.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
13256 currentStyle
.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
13259 if (attr
.HasParagraphStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
))
13261 if (currentStyle
.HasParagraphStyleName())
13263 if (currentStyle
.GetParagraphStyleName() != attr
.GetParagraphStyleName())
13265 // Clash of attr - mark as such
13266 clashingAttr
.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
13267 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
13271 currentStyle
.SetParagraphStyleName(attr
.GetParagraphStyleName());
13273 else if (!attr
.HasParagraphStyleName() && currentStyle
.HasParagraphStyleName())
13275 clashingAttr
.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
13276 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
13279 if (attr
.HasListStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LIST_STYLE_NAME
))
13281 if (currentStyle
.HasListStyleName())
13283 if (currentStyle
.GetListStyleName() != attr
.GetListStyleName())
13285 // Clash of attr - mark as such
13286 clashingAttr
.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
13287 currentStyle
.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
13291 currentStyle
.SetListStyleName(attr
.GetListStyleName());
13293 else if (!attr
.HasListStyleName() && currentStyle
.HasListStyleName())
13295 clashingAttr
.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
13296 currentStyle
.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
13299 if (attr
.HasBulletStyle() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_STYLE
))
13301 if (currentStyle
.HasBulletStyle())
13303 if (currentStyle
.GetBulletStyle() != attr
.GetBulletStyle())
13305 // Clash of attr - mark as such
13306 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_STYLE
);
13307 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE
);
13311 currentStyle
.SetBulletStyle(attr
.GetBulletStyle());
13313 else if (!attr
.HasBulletStyle() && currentStyle
.HasBulletStyle())
13315 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_STYLE
);
13316 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE
);
13319 if (attr
.HasBulletNumber() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_NUMBER
))
13321 if (currentStyle
.HasBulletNumber())
13323 if (currentStyle
.GetBulletNumber() != attr
.GetBulletNumber())
13325 // Clash of attr - mark as such
13326 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NUMBER
);
13327 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER
);
13331 currentStyle
.SetBulletNumber(attr
.GetBulletNumber());
13333 else if (!attr
.HasBulletNumber() && currentStyle
.HasBulletNumber())
13335 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NUMBER
);
13336 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER
);
13339 if (attr
.HasBulletText() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_TEXT
))
13341 if (currentStyle
.HasBulletText())
13343 if (currentStyle
.GetBulletText() != attr
.GetBulletText())
13345 // Clash of attr - mark as such
13346 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_TEXT
);
13347 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT
);
13352 currentStyle
.SetBulletText(attr
.GetBulletText());
13353 currentStyle
.SetBulletFont(attr
.GetBulletFont());
13356 else if (!attr
.HasBulletText() && currentStyle
.HasBulletText())
13358 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_TEXT
);
13359 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT
);
13362 if (attr
.HasBulletName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_NAME
))
13364 if (currentStyle
.HasBulletName())
13366 if (currentStyle
.GetBulletName() != attr
.GetBulletName())
13368 // Clash of attr - mark as such
13369 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NAME
);
13370 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NAME
);
13375 currentStyle
.SetBulletName(attr
.GetBulletName());
13378 else if (!attr
.HasBulletName() && currentStyle
.HasBulletName())
13380 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NAME
);
13381 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NAME
);
13384 if (attr
.HasURL() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_URL
))
13386 if (currentStyle
.HasURL())
13388 if (currentStyle
.GetURL() != attr
.GetURL())
13390 // Clash of attr - mark as such
13391 clashingAttr
.AddFlag(wxTEXT_ATTR_URL
);
13392 currentStyle
.RemoveFlag(wxTEXT_ATTR_URL
);
13397 currentStyle
.SetURL(attr
.GetURL());
13400 else if (!attr
.HasURL() && currentStyle
.HasURL())
13402 clashingAttr
.AddFlag(wxTEXT_ATTR_URL
);
13403 currentStyle
.RemoveFlag(wxTEXT_ATTR_URL
);
13406 if (attr
.HasTextEffects() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_EFFECTS
))
13408 if (currentStyle
.HasTextEffects())
13410 // We need to find the bits in the new attr that are different:
13411 // just look at those bits that are specified by the new attr.
13413 // We need to remove the bits and flags that are not common between current attr
13414 // and new attr. In so doing we need to take account of the styles absent from one or more of the
13415 // previous styles.
13417 int currentRelevantTextEffects
= currentStyle
.GetTextEffects() & attr
.GetTextEffectFlags();
13418 int newRelevantTextEffects
= attr
.GetTextEffects() & attr
.GetTextEffectFlags();
13420 if (currentRelevantTextEffects
!= newRelevantTextEffects
)
13422 // Find the text effects that were different, using XOR
13423 int differentEffects
= currentRelevantTextEffects
^ newRelevantTextEffects
;
13425 // Clash of attr - mark as such
13426 clashingAttr
.SetTextEffectFlags(clashingAttr
.GetTextEffectFlags() | differentEffects
);
13427 currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~differentEffects
);
13432 currentStyle
.SetTextEffects(attr
.GetTextEffects());
13433 currentStyle
.SetTextEffectFlags(attr
.GetTextEffectFlags());
13436 // Mask out the flags and values that cannot be common because they were absent in one or more objecrs
13437 // that we've looked at so far
13438 currentStyle
.SetTextEffects(currentStyle
.GetTextEffects() & ~absentAttr
.GetTextEffectFlags());
13439 currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~absentAttr
.GetTextEffectFlags());
13441 if (currentStyle
.GetTextEffectFlags() == 0)
13442 currentStyle
.RemoveFlag(wxTEXT_ATTR_EFFECTS
);
13444 else if (!attr
.HasTextEffects() && currentStyle
.HasTextEffects())
13446 clashingAttr
.AddFlag(wxTEXT_ATTR_EFFECTS
);
13447 currentStyle
.RemoveFlag(wxTEXT_ATTR_EFFECTS
);
13450 if (attr
.HasOutlineLevel() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_OUTLINE_LEVEL
))
13452 if (currentStyle
.HasOutlineLevel())
13454 if (currentStyle
.GetOutlineLevel() != attr
.GetOutlineLevel())
13456 // Clash of attr - mark as such
13457 clashingAttr
.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
13458 currentStyle
.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
13462 currentStyle
.SetOutlineLevel(attr
.GetOutlineLevel());
13464 else if (!attr
.HasOutlineLevel() && currentStyle
.HasOutlineLevel())
13466 clashingAttr
.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
13467 currentStyle
.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
13471 WX_DEFINE_OBJARRAY(wxRichTextVariantArray
);
13474 WX_DEFINE_OBJARRAY(wxRichTextAttrArray
);
13476 IMPLEMENT_DYNAMIC_CLASS(wxRichTextProperties
, wxObject
)
13478 bool wxRichTextProperties::operator==(const wxRichTextProperties
& props
) const
13480 if (m_properties
.GetCount() != props
.GetCount())
13484 for (i
= 0; i
< m_properties
.GetCount(); i
++)
13486 const wxVariant
& var1
= m_properties
[i
];
13487 int idx
= props
.Find(var1
.GetName());
13490 const wxVariant
& var2
= props
.m_properties
[idx
];
13491 if (!(var1
== var2
))
13498 wxArrayString
wxRichTextProperties::GetPropertyNames() const
13502 for (i
= 0; i
< m_properties
.GetCount(); i
++)
13504 arr
.Add(m_properties
[i
].GetName());
13509 int wxRichTextProperties::Find(const wxString
& name
) const
13512 for (i
= 0; i
< m_properties
.GetCount(); i
++)
13514 if (m_properties
[i
].GetName() == name
)
13520 bool wxRichTextProperties::Remove(const wxString
& name
)
13522 int idx
= Find(name
);
13525 m_properties
.RemoveAt(idx
);
13532 wxVariant
* wxRichTextProperties::FindOrCreateProperty(const wxString
& name
)
13534 int idx
= Find(name
);
13535 if (idx
== wxNOT_FOUND
)
13536 SetProperty(name
, wxString());
13538 if (idx
!= wxNOT_FOUND
)
13540 return & (*this)[idx
];
13546 const wxVariant
& wxRichTextProperties::GetProperty(const wxString
& name
) const
13548 static const wxVariant nullVariant
;
13549 int idx
= Find(name
);
13551 return m_properties
[idx
];
13553 return nullVariant
;
13556 wxString
wxRichTextProperties::GetPropertyString(const wxString
& name
) const
13558 return GetProperty(name
).GetString();
13561 long wxRichTextProperties::GetPropertyLong(const wxString
& name
) const
13563 return GetProperty(name
).GetLong();
13566 bool wxRichTextProperties::GetPropertyBool(const wxString
& name
) const
13568 return GetProperty(name
).GetBool();
13571 double wxRichTextProperties::GetPropertyDouble(const wxString
& name
) const
13573 return GetProperty(name
).GetDouble();
13576 void wxRichTextProperties::SetProperty(const wxVariant
& variant
)
13578 wxASSERT(!variant
.GetName().IsEmpty());
13580 int idx
= Find(variant
.GetName());
13583 m_properties
.Add(variant
);
13585 m_properties
[idx
] = variant
;
13588 void wxRichTextProperties::SetProperty(const wxString
& name
, const wxVariant
& variant
)
13590 int idx
= Find(name
);
13591 wxVariant
var(variant
);
13595 m_properties
.Add(var
);
13597 m_properties
[idx
] = var
;
13600 void wxRichTextProperties::SetProperty(const wxString
& name
, const wxString
& value
)
13602 SetProperty(name
, wxVariant(value
, name
));
13605 void wxRichTextProperties::SetProperty(const wxString
& name
, long value
)
13607 SetProperty(name
, wxVariant(value
, name
));
13610 void wxRichTextProperties::SetProperty(const wxString
& name
, double value
)
13612 SetProperty(name
, wxVariant(value
, name
));
13615 void wxRichTextProperties::SetProperty(const wxString
& name
, bool value
)
13617 SetProperty(name
, wxVariant(value
, name
));
13620 void wxRichTextProperties::RemoveProperties(const wxRichTextProperties
& properties
)
13623 for (i
= 0; i
< properties
.GetCount(); i
++)
13625 wxString name
= properties
.GetProperties()[i
].GetName();
13626 if (HasProperty(name
))
13631 void wxRichTextProperties::MergeProperties(const wxRichTextProperties
& properties
)
13634 for (i
= 0; i
< properties
.GetCount(); i
++)
13636 SetProperty(properties
.GetProperties()[i
]);
13640 wxRichTextObject
* wxRichTextObjectAddress::GetObject(wxRichTextParagraphLayoutBox
* topLevelContainer
) const
13642 if (m_address
.GetCount() == 0)
13643 return topLevelContainer
;
13645 wxRichTextCompositeObject
* p
= topLevelContainer
;
13647 while (p
&& i
< m_address
.GetCount())
13649 int pos
= m_address
[i
];
13650 wxASSERT(pos
>= 0 && pos
< (int) p
->GetChildren().GetCount());
13651 if (pos
< 0 || pos
>= (int) p
->GetChildren().GetCount())
13654 wxRichTextObject
* p1
= p
->GetChild(pos
);
13655 if (i
== (m_address
.GetCount()-1))
13658 p
= wxDynamicCast(p1
, wxRichTextCompositeObject
);
13664 bool wxRichTextObjectAddress::Create(wxRichTextParagraphLayoutBox
* topLevelContainer
, wxRichTextObject
* obj
)
13668 if (topLevelContainer
== obj
)
13671 wxRichTextObject
* o
= obj
;
13674 wxRichTextCompositeObject
* p
= wxDynamicCast(o
->GetParent(), wxRichTextCompositeObject
);
13678 int pos
= p
->GetChildren().IndexOf(o
);
13682 m_address
.Insert(pos
, 0);
13684 if (p
== topLevelContainer
)
13693 bool wxRichTextSelection::operator==(const wxRichTextSelection
& sel
) const
13695 if (m_container
!= sel
.m_container
)
13697 if (m_ranges
.GetCount() != sel
.m_ranges
.GetCount())
13700 for (i
= 0; i
< m_ranges
.GetCount(); i
++)
13701 if (!(m_ranges
[i
] == sel
.m_ranges
[i
]))
13706 // Get the selections appropriate to the specified object, if any; returns wxRICHTEXT_NO_SELECTION if none
13707 // or none at the level of the object's container.
13708 wxRichTextRangeArray
wxRichTextSelection::GetSelectionForObject(wxRichTextObject
* obj
) const
13712 wxRichTextParagraphLayoutBox
* container
= obj
->GetParentContainer();
13714 if (container
== m_container
)
13717 container
= obj
->GetContainer();
13720 if (container
->GetParent())
13722 // If we found that our object's container is within the range of
13723 // a selection higher up, then assume the whole original object
13724 // is also selected.
13725 wxRichTextParagraphLayoutBox
* parentContainer
= container
->GetParentContainer();
13726 if (parentContainer
== m_container
)
13728 if (WithinSelection(container
->GetRange().GetStart(), m_ranges
))
13730 wxRichTextRangeArray ranges
;
13731 ranges
.Add(obj
->GetRange());
13736 container
= parentContainer
;
13745 return wxRichTextRangeArray();
13748 // Is the given position within the selection?
13749 bool wxRichTextSelection::WithinSelection(long pos
, wxRichTextObject
* obj
) const
13755 wxRichTextRangeArray selectionRanges
= GetSelectionForObject(obj
);
13756 return WithinSelection(pos
, selectionRanges
);
13760 // Is the given position within the selection range?
13761 bool wxRichTextSelection::WithinSelection(long pos
, const wxRichTextRangeArray
& ranges
)
13764 for (i
= 0; i
< ranges
.GetCount(); i
++)
13766 const wxRichTextRange
& range
= ranges
[i
];
13767 if (pos
>= range
.GetStart() && pos
<= range
.GetEnd())
13773 // Is the given range completely within the selection range?
13774 bool wxRichTextSelection::WithinSelection(const wxRichTextRange
& range
, const wxRichTextRangeArray
& ranges
)
13777 for (i
= 0; i
< ranges
.GetCount(); i
++)
13779 const wxRichTextRange
& eachRange
= ranges
[i
];
13780 if (range
.IsWithin(eachRange
))
13786 IMPLEMENT_CLASS(wxRichTextDrawingHandler
, wxObject
)
13787 IMPLEMENT_CLASS(wxRichTextDrawingContext
, wxObject
)
13789 wxRichTextDrawingContext::wxRichTextDrawingContext(wxRichTextBuffer
* buffer
)
13793 if (m_buffer
&& m_buffer
->GetRichTextCtrl())
13794 EnableVirtualAttributes(m_buffer
->GetRichTextCtrl()->GetVirtualAttributesEnabled());
13797 bool wxRichTextDrawingContext::HasVirtualAttributes(wxRichTextObject
* obj
) const
13799 if (!GetVirtualAttributesEnabled())
13802 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
13805 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13806 if (handler
->HasVirtualAttributes(obj
))
13809 node
= node
->GetNext();
13814 wxRichTextAttr
wxRichTextDrawingContext::GetVirtualAttributes(wxRichTextObject
* obj
) const
13816 wxRichTextAttr attr
;
13817 if (!GetVirtualAttributesEnabled())
13820 // We apply all handlers, so we can may combine several different attributes
13821 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
13824 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13825 if (handler
->HasVirtualAttributes(obj
))
13827 bool success
= handler
->GetVirtualAttributes(attr
, obj
);
13829 wxUnusedVar(success
);
13832 node
= node
->GetNext();
13837 bool wxRichTextDrawingContext::ApplyVirtualAttributes(wxRichTextAttr
& attr
, wxRichTextObject
* obj
) const
13839 if (!GetVirtualAttributesEnabled())
13842 if (HasVirtualAttributes(obj
))
13844 wxRichTextAttr
a(GetVirtualAttributes(obj
));
13852 int wxRichTextDrawingContext::GetVirtualSubobjectAttributesCount(wxRichTextObject
* obj
) const
13854 if (!GetVirtualAttributesEnabled())
13857 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
13860 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13861 int count
= handler
->GetVirtualSubobjectAttributesCount(obj
);
13865 node
= node
->GetNext();
13870 int wxRichTextDrawingContext::GetVirtualSubobjectAttributes(wxRichTextObject
* obj
, wxArrayInt
& positions
, wxRichTextAttrArray
& attributes
) const
13872 if (!GetVirtualAttributesEnabled())
13875 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
13878 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13879 if (handler
->GetVirtualSubobjectAttributes(obj
, positions
, attributes
))
13880 return positions
.GetCount();
13882 node
= node
->GetNext();
13887 bool wxRichTextDrawingContext::HasVirtualText(const wxRichTextPlainText
* obj
) const
13889 if (!GetVirtualAttributesEnabled())
13892 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
13895 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13896 if (handler
->HasVirtualText(obj
))
13899 node
= node
->GetNext();
13904 bool wxRichTextDrawingContext::GetVirtualText(const wxRichTextPlainText
* obj
, wxString
& text
) const
13906 if (!GetVirtualAttributesEnabled())
13909 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
13912 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13913 if (handler
->GetVirtualText(obj
, text
))
13916 node
= node
->GetNext();
13921 /// Adds a handler to the end
13922 void wxRichTextBuffer::AddDrawingHandler(wxRichTextDrawingHandler
*handler
)
13924 sm_drawingHandlers
.Append(handler
);
13927 /// Inserts a handler at the front
13928 void wxRichTextBuffer::InsertDrawingHandler(wxRichTextDrawingHandler
*handler
)
13930 sm_drawingHandlers
.Insert( handler
);
13933 /// Removes a handler
13934 bool wxRichTextBuffer::RemoveDrawingHandler(const wxString
& name
)
13936 wxRichTextDrawingHandler
*handler
= FindDrawingHandler(name
);
13939 sm_drawingHandlers
.DeleteObject(handler
);
13947 wxRichTextDrawingHandler
* wxRichTextBuffer::FindDrawingHandler(const wxString
& name
)
13949 wxList::compatibility_iterator node
= sm_drawingHandlers
.GetFirst();
13952 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13953 if (handler
->GetName().Lower() == name
.Lower()) return handler
;
13955 node
= node
->GetNext();
13960 void wxRichTextBuffer::CleanUpDrawingHandlers()
13962 wxList::compatibility_iterator node
= sm_drawingHandlers
.GetFirst();
13965 wxRichTextDrawingHandler
* handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13966 wxList::compatibility_iterator next
= node
->GetNext();
13971 sm_drawingHandlers
.Clear();
13974 void wxRichTextBuffer::AddFieldType(wxRichTextFieldType
*fieldType
)
13976 sm_fieldTypes
[fieldType
->GetName()] = fieldType
;
13979 bool wxRichTextBuffer::RemoveFieldType(const wxString
& name
)
13981 wxRichTextFieldTypeHashMap::iterator it
= sm_fieldTypes
.find(name
);
13982 if (it
== sm_fieldTypes
.end())
13986 wxRichTextFieldType
* fieldType
= it
->second
;
13987 sm_fieldTypes
.erase(it
);
13993 wxRichTextFieldType
*wxRichTextBuffer::FindFieldType(const wxString
& name
)
13995 wxRichTextFieldTypeHashMap::iterator it
= sm_fieldTypes
.find(name
);
13996 if (it
== sm_fieldTypes
.end())
14002 void wxRichTextBuffer::CleanUpFieldTypes()
14004 wxRichTextFieldTypeHashMap::iterator it
;
14005 for( it
= sm_fieldTypes
.begin(); it
!= sm_fieldTypes
.end(); ++it
)
14007 wxRichTextFieldType
* fieldType
= it
->second
;
14011 sm_fieldTypes
.clear();