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();
1450 wxRichTextObjectList::compatibility_iterator nextNode
= node
->GetNext();
1452 // First split child and nextChild so we have smaller fragments to merge.
1453 // Then Merge only has to test per-object virtual attributes
1454 // because for an object with all the same sub-object attributes,
1455 // then any general virtual attributes should be merged with sub-objects by
1456 // the implementation.
1458 wxRichTextObject
* nextChildAfterSplit
= nextChild
;
1460 if (nextChildAfterSplit
->CanSplit(context
))
1461 nextChildAfterSplit
= nextChild
->Split(context
);
1463 bool splitNextChild
= nextChild
!= nextChildAfterSplit
;
1465 // See if we can merge this new fragment with (perhaps the first part of) the next object.
1466 // Note that we use nextChild because if we had split nextChild, the first object always
1467 // remains (and further parts are appended). However we must use childAfterSplit since
1468 // it's the last part of a possibly split child.
1470 if (childAfterSplit
->CanMerge(nextChild
, context
) && childAfterSplit
->Merge(nextChild
, context
))
1472 nextChild
->Dereference();
1473 m_children
.Erase(node
->GetNext());
1475 // Don't set node -- we'll see if we can merge again with the next
1476 // child. UNLESS we split this or the next child, in which case we know we have to
1477 // move on to the end of the next child.
1479 node
= m_children
.Find(nextChildAfterSplit
);
1484 node
= m_children
.Find(nextChildAfterSplit
); // start from the last object in the split
1486 node
= node
->GetNext();
1490 node
= node
->GetNext();
1494 node
= node
->GetNext();
1497 // Delete any remaining empty objects, but leave at least one empty object per composite object.
1498 if (GetChildCount() > 1)
1500 node
= m_children
.GetFirst();
1503 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
1504 wxRichTextObject
* child
= node
->GetData();
1505 if (range
== wxRICHTEXT_ALL
|| !child
->GetRange().IsOutside(range
))
1507 if (child
->IsEmpty())
1509 child
->Dereference();
1510 m_children
.Erase(node
);
1515 node
= node
->GetNext();
1522 /// Dump to output stream for debugging
1523 void wxRichTextCompositeObject::Dump(wxTextOutputStream
& stream
)
1525 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1528 wxRichTextObject
* child
= node
->GetData();
1529 child
->Dump(stream
);
1530 node
= node
->GetNext();
1534 /// Get/set the object size for the given range. Returns false if the range
1535 /// is invalid for this object.
1536 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
1538 if (!range
.IsWithin(GetRange()))
1543 wxArrayInt childExtents
;
1550 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1553 wxRichTextObject
* child
= node
->GetData();
1554 if (!child
->GetRange().IsOutside(range
))
1556 // Floating objects have a zero size within the paragraph.
1557 if (child
->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode())
1562 if (partialExtents
->GetCount() > 0)
1563 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
1567 partialExtents
->Add(0 /* zero size */ + lastSize
);
1574 wxRichTextRange rangeToUse
= range
;
1575 rangeToUse
.LimitTo(child
->GetRange());
1576 if (child
->IsTopLevel())
1577 rangeToUse
= child
->GetOwnRange();
1579 int childDescent
= 0;
1581 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we're already cached the size,
1582 // but it's only going to be used after caching has taken place.
1583 if ((flags
& wxRICHTEXT_HEIGHT_ONLY
) && child
->GetCachedSize().y
!= 0)
1585 childDescent
= child
->GetDescent();
1586 childSize
= child
->GetCachedSize();
1588 sz
.y
= wxMax(sz
.y
, childSize
.y
);
1589 sz
.x
+= childSize
.x
;
1590 descent
= wxMax(descent
, childDescent
);
1592 else if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, context
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
), parentSize
, p
))
1594 sz
.y
= wxMax(sz
.y
, childSize
.y
);
1595 sz
.x
+= childSize
.x
;
1596 descent
= wxMax(descent
, childDescent
);
1598 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange() || child
->IsTopLevel()))
1600 child
->SetCachedSize(childSize
);
1601 child
->SetDescent(childDescent
);
1607 if (partialExtents
->GetCount() > 0)
1608 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
1613 for (i
= 0; i
< childExtents
.GetCount(); i
++)
1615 partialExtents
->Add(childExtents
[i
] + lastSize
);
1625 node
= node
->GetNext();
1631 // Invalidate the buffer. With no argument, invalidates whole buffer.
1632 void wxRichTextCompositeObject::Invalidate(const wxRichTextRange
& invalidRange
)
1634 wxRichTextObject::Invalidate(invalidRange
);
1636 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1639 wxRichTextObject
* child
= node
->GetData();
1640 if (invalidRange
!= wxRICHTEXT_ALL
&& invalidRange
!= wxRICHTEXT_NONE
&& child
->GetRange().IsOutside(invalidRange
))
1644 else if (child
->IsTopLevel())
1646 if (wxRichTextBuffer::GetFloatingLayoutMode() && child
->IsFloating() && GetBuffer()->GetFloatCollector() && GetBuffer()->GetFloatCollector()->HasFloat(child
))
1648 // Don't invalidate subhierarchy if we've already been laid out
1652 if (invalidRange
== wxRICHTEXT_NONE
)
1653 child
->Invalidate(wxRICHTEXT_NONE
);
1655 child
->Invalidate(wxRICHTEXT_ALL
); // All children must be invalidated if within parent range
1659 child
->Invalidate(invalidRange
);
1660 node
= node
->GetNext();
1664 // Move the object recursively, by adding the offset from old to new
1665 void wxRichTextCompositeObject::Move(const wxPoint
& pt
)
1667 wxPoint oldPos
= GetPosition();
1669 wxPoint offset
= pt
- oldPos
;
1671 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1674 wxRichTextObject
* child
= node
->GetData();
1675 wxPoint childPos
= child
->GetPosition() + offset
;
1676 child
->Move(childPos
);
1677 node
= node
->GetNext();
1683 * wxRichTextParagraphLayoutBox
1684 * This box knows how to lay out paragraphs.
1687 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraphLayoutBox
, wxRichTextCompositeObject
)
1689 wxRichTextParagraphLayoutBox::wxRichTextParagraphLayoutBox(wxRichTextObject
* parent
):
1690 wxRichTextCompositeObject(parent
)
1695 wxRichTextParagraphLayoutBox::~wxRichTextParagraphLayoutBox()
1697 if (m_floatCollector
)
1699 delete m_floatCollector
;
1700 m_floatCollector
= NULL
;
1704 /// Initialize the object.
1705 void wxRichTextParagraphLayoutBox::Init()
1709 // For now, assume is the only box and has no initial size.
1710 m_range
= wxRichTextRange(0, -1);
1711 m_ownRange
= wxRichTextRange(0, -1);
1713 m_invalidRange
= wxRICHTEXT_ALL
;
1715 m_partialParagraph
= false;
1716 m_floatCollector
= NULL
;
1719 void wxRichTextParagraphLayoutBox::Clear()
1723 if (m_floatCollector
)
1724 delete m_floatCollector
;
1725 m_floatCollector
= NULL
;
1726 m_partialParagraph
= false;
1730 void wxRichTextParagraphLayoutBox::Copy(const wxRichTextParagraphLayoutBox
& obj
)
1734 wxRichTextCompositeObject::Copy(obj
);
1736 m_partialParagraph
= obj
.m_partialParagraph
;
1737 m_defaultAttributes
= obj
.m_defaultAttributes
;
1740 // Gather information about floating objects; only gather floats for those paragraphs that
1741 // will not be formatted again due to optimization, after which floats will be gathered per-paragraph
1743 bool wxRichTextParagraphLayoutBox::UpdateFloatingObjects(const wxRect
& availableRect
, wxRichTextObject
* untilObj
)
1745 if (m_floatCollector
!= NULL
)
1746 delete m_floatCollector
;
1747 m_floatCollector
= new wxRichTextFloatCollector(availableRect
);
1748 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1749 // Only gather floats up to the point we'll start formatting paragraphs.
1750 while (untilObj
&& node
&& node
->GetData() != untilObj
)
1752 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
1753 wxASSERT (child
!= NULL
);
1755 m_floatCollector
->CollectFloat(child
);
1756 node
= node
->GetNext();
1762 // Returns the style sheet associated with the overall buffer.
1763 wxRichTextStyleSheet
* wxRichTextParagraphLayoutBox::GetStyleSheet() const
1765 return GetBuffer() ? GetBuffer()->GetStyleSheet() : (wxRichTextStyleSheet
*) NULL
;
1768 // Get the number of floating objects at this level
1769 int wxRichTextParagraphLayoutBox::GetFloatingObjectCount() const
1771 if (m_floatCollector
)
1772 return m_floatCollector
->GetFloatingObjectCount();
1777 // Get a list of floating objects
1778 bool wxRichTextParagraphLayoutBox::GetFloatingObjects(wxRichTextObjectList
& objects
) const
1780 if (m_floatCollector
)
1782 return m_floatCollector
->GetFloatingObjects(objects
);
1789 void wxRichTextParagraphLayoutBox::UpdateRanges()
1793 start
= GetRange().GetStart();
1795 CalculateRange(start
, end
);
1799 int wxRichTextParagraphLayoutBox::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
1802 return wxRICHTEXT_HITTEST_NONE
;
1804 int ret
= wxRICHTEXT_HITTEST_NONE
;
1805 if (wxRichTextBuffer::GetFloatingLayoutMode() && m_floatCollector
&& (flags
& wxRICHTEXT_HITTEST_NO_FLOATING_OBJECTS
) == 0)
1806 ret
= m_floatCollector
->HitTest(dc
, context
, pt
, textPosition
, obj
, flags
);
1808 if (ret
== wxRICHTEXT_HITTEST_NONE
)
1809 return wxRichTextCompositeObject::HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
1817 /// Draw the floating objects
1818 void wxRichTextParagraphLayoutBox::DrawFloats(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
1820 if (wxRichTextBuffer::GetFloatingLayoutMode() && m_floatCollector
)
1821 m_floatCollector
->Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
1824 void wxRichTextParagraphLayoutBox::MoveAnchoredObjectToParagraph(wxRichTextParagraph
* from
, wxRichTextParagraph
* to
, wxRichTextObject
* obj
)
1829 from
->RemoveChild(obj
);
1830 to
->AppendChild(obj
);
1834 bool wxRichTextParagraphLayoutBox::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
1839 wxRect
thisRect(GetPosition(), GetCachedSize());
1841 wxRichTextAttr
attr(GetAttributes());
1842 context
.ApplyVirtualAttributes(attr
, this);
1845 if (selection
.IsValid() && GetParentContainer() != this && selection
.WithinSelection(GetRange().GetStart(), GetParentContainer()))
1846 flags
|= wxRICHTEXT_DRAW_SELECTED
;
1848 // Don't draw guidelines if at top level
1849 int theseFlags
= flags
;
1851 theseFlags
&= ~wxRICHTEXT_DRAW_GUIDELINES
;
1852 DrawBoxAttributes(dc
, GetBuffer(), attr
, thisRect
, theseFlags
);
1854 if (wxRichTextBuffer::GetFloatingLayoutMode())
1855 DrawFloats(dc
, context
, range
, selection
, rect
, descent
, style
);
1857 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1860 wxRichTextObject
* child
= node
->GetData();
1862 if (child
&& !child
->GetRange().IsOutside(range
))
1864 wxRect
childRect(child
->GetPosition(), child
->GetCachedSize());
1865 wxRichTextRange childRange
= range
;
1866 if (child
->IsTopLevel())
1868 childRange
= child
->GetOwnRange();
1871 if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) == 0) && childRect
.GetTop() > rect
.GetBottom())
1876 else if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) == 0) && childRect
.GetBottom() < rect
.GetTop())
1881 child
->Draw(dc
, context
, childRange
, selection
, rect
, descent
, style
);
1884 node
= node
->GetNext();
1889 /// Lay the item out
1890 bool wxRichTextParagraphLayoutBox::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
)
1892 SetPosition(rect
.GetPosition());
1897 wxRect availableSpace
;
1898 bool formatRect
= (style
& wxRICHTEXT_LAYOUT_SPECIFIED_RECT
) == wxRICHTEXT_LAYOUT_SPECIFIED_RECT
;
1900 wxRichTextAttr
attr(GetAttributes());
1901 context
.ApplyVirtualAttributes(attr
, this);
1903 // If only laying out a specific area, the passed rect has a different meaning:
1904 // the visible part of the buffer. This is used in wxRichTextCtrl::OnSize,
1905 // so that during a size, only the visible part will be relaid out, or
1906 // it would take too long causing flicker. As an approximation, we assume that
1907 // everything up to the start of the visible area is laid out correctly.
1910 wxRect
rect2(0, 0, rect
.width
, rect
.height
);
1911 availableSpace
= GetAvailableContentArea(dc
, context
, rect2
);
1913 // Invalidate the part of the buffer from the first visible line
1914 // to the end. If other parts of the buffer are currently invalid,
1915 // then they too will be taken into account if they are above
1916 // the visible point.
1918 wxRichTextLine
* line
= GetLineAtYPosition(rect
.y
);
1920 startPos
= line
->GetAbsoluteRange().GetStart();
1922 Invalidate(wxRichTextRange(startPos
, GetOwnRange().GetEnd()));
1926 availableSpace
= GetAvailableContentArea(dc
, context
, rect
);
1929 // Fix the width if we're at the top level
1931 attr
.GetTextBoxAttr().GetWidth().SetValue(rect
.GetWidth(), wxTEXT_ATTR_UNITS_PIXELS
);
1933 int leftMargin
, rightMargin
, topMargin
, bottomMargin
;
1934 wxRichTextObject::GetTotalMargin(dc
, GetBuffer(), attr
, leftMargin
, rightMargin
,
1935 topMargin
, bottomMargin
);
1940 // The maximum paragraph maximum width, so we can set the overall maximum width for this object
1941 int maxMaxWidth
= 0;
1943 // The maximum paragraph minimum width, so we can set the overall minimum width for this object
1944 int maxMinWidth
= 0;
1946 // If we have vertical alignment, we must recalculate everything.
1947 bool hasVerticalAlignment
= (attr
.GetTextBoxAttr().HasVerticalAlignment() &&
1948 (attr
.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP
));
1950 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1952 bool layoutAll
= true;
1954 // Get invalid range, rounding to paragraph start/end.
1955 wxRichTextRange invalidRange
= GetInvalidRange(true);
1957 if (invalidRange
== wxRICHTEXT_NONE
&& !formatRect
)
1960 if (invalidRange
== wxRICHTEXT_ALL
|| hasVerticalAlignment
)
1962 else // If we know what range is affected, start laying out from that point on.
1963 if (invalidRange
.GetStart() >= GetOwnRange().GetStart())
1965 wxRichTextParagraph
* firstParagraph
= GetParagraphAtPosition(invalidRange
.GetStart());
1968 wxRichTextObjectList::compatibility_iterator firstNode
= m_children
.Find(firstParagraph
);
1969 wxRichTextObjectList::compatibility_iterator previousNode
;
1971 previousNode
= firstNode
->GetPrevious();
1976 wxRichTextParagraph
* previousParagraph
= wxDynamicCast(previousNode
->GetData(), wxRichTextParagraph
);
1977 availableSpace
.y
= previousParagraph
->GetPosition().y
+ previousParagraph
->GetCachedSize().y
;
1980 // Now we're going to start iterating from the first affected paragraph.
1988 // Gather information about only those floating objects that will not be formatted,
1989 // after which floats will be gathered per-paragraph during layout.
1990 if (wxRichTextBuffer::GetFloatingLayoutMode())
1991 UpdateFloatingObjects(availableSpace
, node
? node
->GetData() : (wxRichTextObject
*) NULL
);
1993 // A way to force speedy rest-of-buffer layout (the 'else' below)
1994 bool forceQuickLayout
= false;
1996 // First get the size of the paragraphs we won't be laying out
1997 wxRichTextObjectList::compatibility_iterator n
= m_children
.GetFirst();
1998 while (n
&& n
!= node
)
2000 wxRichTextParagraph
* child
= wxDynamicCast(n
->GetData(), wxRichTextParagraph
);
2003 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
2004 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
2005 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
2012 // Assume this box only contains paragraphs
2014 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2015 // Unsure if this is needed
2016 // wxCHECK_MSG( child, false, wxT("Unknown object in layout") );
2018 if (child
&& child
->IsShown())
2020 // TODO: what if the child hasn't been laid out (e.g. involved in Undo) but still has 'old' lines
2021 if ( !forceQuickLayout
&&
2023 child
->GetLines().IsEmpty() ||
2024 !child
->GetRange().IsOutside(invalidRange
)) )
2026 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
2027 // lays out the object again using the minimum size
2028 child
->LayoutToBestSize(dc
, context
, GetBuffer(),
2029 attr
, child
->GetAttributes(), availableSpace
, rect
, style
&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT
);
2031 // Layout must set the cached size
2032 availableSpace
.y
+= child
->GetCachedSize().y
;
2033 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
2034 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
2035 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
2037 // If we're just formatting the visible part of the buffer,
2038 // and we're now past the bottom of the window, and we don't have any
2039 // floating objects (since they may cause wrapping to change for the rest of the
2040 // the buffer), start quick layout.
2041 if (!hasVerticalAlignment
&& formatRect
&& child
->GetPosition().y
> rect
.GetBottom() && GetFloatingObjectCount() == 0)
2042 forceQuickLayout
= true;
2046 // We're outside the immediately affected range, so now let's just
2047 // move everything up or down. This assumes that all the children have previously
2048 // been laid out and have wrapped line lists associated with them.
2049 // TODO: check all paragraphs before the affected range.
2051 int inc
= availableSpace
.y
- child
->GetPosition().y
;
2055 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2058 if (child
->GetLines().GetCount() == 0)
2060 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
2061 // lays out the object again using the minimum size
2062 child
->LayoutToBestSize(dc
, context
, GetBuffer(),
2063 attr
, child
->GetAttributes(), availableSpace
, rect
, style
&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT
);
2065 //child->Layout(dc, availableChildRect, style);
2068 child
->Move(wxPoint(child
->GetPosition().x
, child
->GetPosition().y
+ inc
));
2070 availableSpace
.y
+= child
->GetCachedSize().y
;
2071 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
2072 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
2073 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
2076 node
= node
->GetNext();
2082 node
= node
->GetNext();
2085 node
= m_children
.GetLast();
2086 if (node
&& node
->GetData()->IsShown())
2088 wxRichTextObject
* child
= node
->GetData();
2089 maxHeight
= child
->GetPosition().y
- (GetPosition().y
+ topMargin
) + child
->GetCachedSize().y
;
2092 maxHeight
= 0; // topMargin + bottomMargin;
2094 // Check the bottom edge of any floating object
2095 if (wxRichTextBuffer::GetFloatingLayoutMode() && GetFloatCollector() && GetFloatCollector()->HasFloats())
2097 int bottom
= GetFloatCollector()->GetLastRectBottom();
2098 if (bottom
> maxHeight
)
2102 if (attr
.GetTextBoxAttr().GetSize().GetWidth().IsValid())
2104 wxRect r
= AdjustAvailableSpace(dc
, GetBuffer(), wxRichTextAttr() /* not used */, attr
, parentRect
, parentRect
);
2105 int w
= r
.GetWidth();
2107 // Convert external to content rect
2108 w
= w
- leftMargin
- rightMargin
;
2109 maxWidth
= wxMax(maxWidth
, w
);
2110 maxMaxWidth
= wxMax(maxMaxWidth
, w
);
2114 // TODO: Make sure the layout box's position reflects
2115 // the position of the children, but without
2116 // breaking layout of a box within a paragraph.
2119 // TODO: (also in para layout) should set the
2120 // object's size to an absolute one if specified,
2121 // but if not specified, calculate it from content.
2123 // We need to add back the margins etc.
2125 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2126 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxWidth
, maxHeight
));
2127 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2128 SetCachedSize(marginRect
.GetSize());
2131 // The maximum size is the greatest of all maximum widths for all paragraphs.
2133 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2134 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxMaxWidth
, maxHeight
)); // Actually max height is a lie, we can't know it
2135 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2136 SetMaxSize(marginRect
.GetSize());
2139 // The minimum size is the greatest of all minimum widths for all paragraphs.
2141 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2142 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxMinWidth
, maxHeight
)); // Actually max height is a lie, we can't know it
2143 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2144 SetMinSize(marginRect
.GetSize());
2147 if (attr
.GetTextBoxAttr().HasVerticalAlignment() &&
2148 (attr
.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP
))
2151 int leftOverSpace
= availableSpace
.height
- topMargin
- bottomMargin
- maxHeight
;
2152 if (leftOverSpace
> 0)
2154 if (attr
.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_CENTRE
)
2156 yOffset
= (leftOverSpace
/2);
2158 else if (attr
.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_BOTTOM
)
2160 yOffset
= leftOverSpace
;
2164 // Move all the children to vertically align the content
2165 // This doesn't take into account floating objects, unfortunately.
2168 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2171 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2173 child
->Move(wxPoint(child
->GetPosition().x
, child
->GetPosition().y
+ yOffset
));
2175 node
= node
->GetNext();
2180 m_invalidRange
= wxRICHTEXT_NONE
;
2185 /// Get/set the size for the given range.
2186 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
2190 wxRichTextObjectList::compatibility_iterator startPara
= wxRichTextObjectList::compatibility_iterator();
2191 wxRichTextObjectList::compatibility_iterator endPara
= wxRichTextObjectList::compatibility_iterator();
2193 // First find the first paragraph whose starting position is within the range.
2194 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2197 // child is a paragraph
2198 wxRichTextObject
* child
= node
->GetData();
2199 const wxRichTextRange
& r
= child
->GetRange();
2201 if (r
.GetStart() <= range
.GetStart() && r
.GetEnd() >= range
.GetStart())
2207 node
= node
->GetNext();
2210 // Next find the last paragraph containing part of the range
2211 node
= m_children
.GetFirst();
2214 // child is a paragraph
2215 wxRichTextObject
* child
= node
->GetData();
2216 const wxRichTextRange
& r
= child
->GetRange();
2218 if (r
.GetStart() <= range
.GetEnd() && r
.GetEnd() >= range
.GetEnd())
2224 node
= node
->GetNext();
2227 if (!startPara
|| !endPara
)
2230 // Now we can add up the sizes
2231 for (node
= startPara
; node
; node
= node
->GetNext())
2233 // child is a paragraph
2234 wxRichTextObject
* child
= node
->GetData();
2235 const wxRichTextRange
& childRange
= child
->GetRange();
2236 wxRichTextRange rangeToFind
= range
;
2237 rangeToFind
.LimitTo(childRange
);
2239 if (child
->IsTopLevel())
2240 rangeToFind
= child
->GetOwnRange();
2244 int childDescent
= 0;
2245 child
->GetRangeSize(rangeToFind
, childSize
, childDescent
, dc
, context
, flags
, position
, parentSize
);
2247 descent
= wxMax(childDescent
, descent
);
2249 sz
.x
= wxMax(sz
.x
, childSize
.x
);
2250 sz
.y
+= childSize
.y
;
2252 if (node
== endPara
)
2261 /// Get the paragraph at the given position
2262 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphAtPosition(long pos
, bool caretPosition
) const
2267 // First find the first paragraph whose starting position is within the range.
2268 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2271 // child is a paragraph
2272 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2273 // wxASSERT (child != NULL);
2277 // Return first child in buffer if position is -1
2281 if (child
->GetRange().Contains(pos
))
2285 node
= node
->GetNext();
2290 /// Get the line at the given position
2291 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineAtPosition(long pos
, bool caretPosition
) const
2296 // First find the first paragraph whose starting position is within the range.
2297 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2300 wxRichTextObject
* obj
= (wxRichTextObject
*) node
->GetData();
2301 if (obj
->GetRange().Contains(pos
))
2303 // child is a paragraph
2304 wxRichTextParagraph
* child
= wxDynamicCast(obj
, wxRichTextParagraph
);
2305 // wxASSERT (child != NULL);
2309 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2312 wxRichTextLine
* line
= node2
->GetData();
2314 wxRichTextRange range
= line
->GetAbsoluteRange();
2316 if (range
.Contains(pos
) ||
2318 // If the position is end-of-paragraph, then return the last line of
2319 // of the paragraph.
2320 ((range
.GetEnd() == child
->GetRange().GetEnd()-1) && (pos
== child
->GetRange().GetEnd())))
2323 node2
= node2
->GetNext();
2328 node
= node
->GetNext();
2331 int lineCount
= GetLineCount();
2333 return GetLineForVisibleLineNumber(lineCount
-1);
2338 /// Get the line at the given y pixel position, or the last line.
2339 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineAtYPosition(int y
) const
2341 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2344 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2345 // wxASSERT (child != NULL);
2349 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2352 wxRichTextLine
* line
= node2
->GetData();
2354 wxRect
rect(line
->GetRect());
2356 if (y
<= rect
.GetBottom())
2359 node2
= node2
->GetNext();
2363 node
= node
->GetNext();
2367 int lineCount
= GetLineCount();
2369 return GetLineForVisibleLineNumber(lineCount
-1);
2374 /// Get the number of visible lines
2375 int wxRichTextParagraphLayoutBox::GetLineCount() const
2379 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2382 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2383 // wxASSERT (child != NULL);
2386 count
+= child
->GetLines().GetCount();
2388 node
= node
->GetNext();
2394 /// Get the paragraph for a given line
2395 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphForLine(wxRichTextLine
* line
) const
2397 return GetParagraphAtPosition(line
->GetAbsoluteRange().GetStart());
2400 /// Get the line size at the given position
2401 wxSize
wxRichTextParagraphLayoutBox::GetLineSizeAtPosition(long pos
, bool caretPosition
) const
2403 wxRichTextLine
* line
= GetLineAtPosition(pos
, caretPosition
);
2406 return line
->GetSize();
2409 return wxSize(0, 0);
2413 /// Convenience function to add a paragraph of text
2414 wxRichTextRange
wxRichTextParagraphLayoutBox::AddParagraph(const wxString
& text
, wxRichTextAttr
* paraStyle
)
2416 // Don't use the base style, just the default style, and the base style will
2417 // be combined at display time.
2418 // Divide into paragraph and character styles.
2420 wxRichTextAttr defaultCharStyle
;
2421 wxRichTextAttr defaultParaStyle
;
2423 // If the default style is a named paragraph style, don't apply any character formatting
2424 // to the initial text string.
2425 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2427 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2429 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2432 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2434 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2435 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2437 wxRichTextParagraph
* para
= new wxRichTextParagraph(text
, this, pStyle
, cStyle
);
2438 para
->GetAttributes().GetTextBoxAttr().Reset();
2444 return para
->GetRange();
2447 /// Adds multiple paragraphs, based on newlines.
2448 wxRichTextRange
wxRichTextParagraphLayoutBox::AddParagraphs(const wxString
& text
, wxRichTextAttr
* paraStyle
)
2450 // Don't use the base style, just the default style, and the base style will
2451 // be combined at display time.
2452 // Divide into paragraph and character styles.
2454 wxRichTextAttr defaultCharStyle
;
2455 wxRichTextAttr defaultParaStyle
;
2457 // If the default style is a named paragraph style, don't apply any character formatting
2458 // to the initial text string.
2459 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2461 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2463 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2466 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2468 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2469 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2471 wxRichTextParagraph
* firstPara
= NULL
;
2472 wxRichTextParagraph
* lastPara
= NULL
;
2474 wxRichTextRange
range(-1, -1);
2477 size_t len
= text
.length();
2479 wxRichTextParagraph
* para
= new wxRichTextParagraph(wxEmptyString
, this, pStyle
, cStyle
);
2480 para
->GetAttributes().GetTextBoxAttr().Reset();
2489 wxChar ch
= text
[i
];
2490 if (ch
== wxT('\n') || ch
== wxT('\r'))
2494 wxRichTextPlainText
* plainText
= (wxRichTextPlainText
*) para
->GetChildren().GetFirst()->GetData();
2495 plainText
->SetText(line
);
2497 para
= new wxRichTextParagraph(wxEmptyString
, this, pStyle
, cStyle
);
2498 para
->GetAttributes().GetTextBoxAttr().Reset();
2503 line
= wxEmptyString
;
2514 wxRichTextPlainText
* plainText
= (wxRichTextPlainText
*) para
->GetChildren().GetFirst()->GetData();
2515 plainText
->SetText(line
);
2520 return wxRichTextRange(firstPara
->GetRange().GetStart(), lastPara
->GetRange().GetEnd());
2523 /// Convenience function to add an image
2524 wxRichTextRange
wxRichTextParagraphLayoutBox::AddImage(const wxImage
& image
, wxRichTextAttr
* paraStyle
)
2526 // Don't use the base style, just the default style, and the base style will
2527 // be combined at display time.
2528 // Divide into paragraph and character styles.
2530 wxRichTextAttr defaultCharStyle
;
2531 wxRichTextAttr defaultParaStyle
;
2533 // If the default style is a named paragraph style, don't apply any character formatting
2534 // to the initial text string.
2535 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2537 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2539 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2542 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2544 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2545 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2547 wxRichTextParagraph
* para
= new wxRichTextParagraph(this, pStyle
);
2548 para
->GetAttributes().GetTextBoxAttr().Reset();
2550 para
->AppendChild(new wxRichTextImage(image
, this, cStyle
));
2554 return para
->GetRange();
2558 /// Insert fragment into this box at the given position. If partialParagraph is true,
2559 /// it is assumed that the last (or only) paragraph is just a piece of data with no paragraph
2562 bool wxRichTextParagraphLayoutBox::InsertFragment(long position
, wxRichTextParagraphLayoutBox
& fragment
)
2564 // First, find the first paragraph whose starting position is within the range.
2565 wxRichTextParagraph
* para
= GetParagraphAtPosition(position
);
2568 wxRichTextAttr originalAttr
= para
->GetAttributes();
2570 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(para
);
2572 // Now split at this position, returning the object to insert the new
2573 // ones in front of.
2574 wxRichTextObject
* nextObject
= para
->SplitAt(position
);
2576 // Special case: partial paragraph, just one paragraph. Might be a small amount of
2577 // text, for example, so let's optimize.
2579 if (fragment
.GetPartialParagraph() && fragment
.GetChildren().GetCount() == 1)
2581 // Add the first para to this para...
2582 wxRichTextObjectList::compatibility_iterator firstParaNode
= fragment
.GetChildren().GetFirst();
2586 // Iterate through the fragment paragraph inserting the content into this paragraph.
2587 wxRichTextParagraph
* firstPara
= wxDynamicCast(firstParaNode
->GetData(), wxRichTextParagraph
);
2588 wxASSERT (firstPara
!= NULL
);
2590 wxRichTextObjectList::compatibility_iterator objectNode
= firstPara
->GetChildren().GetFirst();
2593 wxRichTextObject
* newObj
= objectNode
->GetData()->Clone();
2598 para
->AppendChild(newObj
);
2602 // Insert before nextObject
2603 para
->InsertChild(newObj
, nextObject
);
2606 objectNode
= objectNode
->GetNext();
2613 // Procedure for inserting a fragment consisting of a number of
2616 // 1. Remove and save the content that's after the insertion point, for adding
2617 // back once we've added the fragment.
2618 // 2. Add the content from the first fragment paragraph to the current
2620 // 3. Add remaining fragment paragraphs after the current paragraph.
2621 // 4. Add back the saved content from the first paragraph. If partialParagraph
2622 // is true, add it to the last paragraph added and not a new one.
2624 // 1. Remove and save objects after split point.
2625 wxList savedObjects
;
2627 para
->MoveToList(nextObject
, savedObjects
);
2629 // 2. Add the content from the 1st fragment paragraph.
2630 wxRichTextObjectList::compatibility_iterator firstParaNode
= fragment
.GetChildren().GetFirst();
2634 wxRichTextParagraph
* firstPara
= wxDynamicCast(firstParaNode
->GetData(), wxRichTextParagraph
);
2635 wxASSERT(firstPara
!= NULL
);
2637 if (!(fragment
.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
))
2638 para
->SetAttributes(firstPara
->GetAttributes());
2640 // Save empty paragraph attributes for appending later
2641 // These are character attributes deliberately set for a new paragraph. Without this,
2642 // we couldn't pass default attributes when appending a new paragraph.
2643 wxRichTextAttr emptyParagraphAttributes
;
2645 wxRichTextObjectList::compatibility_iterator objectNode
= firstPara
->GetChildren().GetFirst();
2647 if (objectNode
&& firstPara
->GetChildren().GetCount() == 1 && objectNode
->GetData()->IsEmpty())
2648 emptyParagraphAttributes
= objectNode
->GetData()->GetAttributes();
2652 wxRichTextObject
* newObj
= objectNode
->GetData()->Clone();
2655 para
->AppendChild(newObj
);
2657 objectNode
= objectNode
->GetNext();
2660 // 3. Add remaining fragment paragraphs after the current paragraph.
2661 wxRichTextObjectList::compatibility_iterator nextParagraphNode
= node
->GetNext();
2662 wxRichTextObject
* nextParagraph
= NULL
;
2663 if (nextParagraphNode
)
2664 nextParagraph
= nextParagraphNode
->GetData();
2666 wxRichTextObjectList::compatibility_iterator i
= fragment
.GetChildren().GetFirst()->GetNext();
2667 wxRichTextParagraph
* finalPara
= para
;
2669 bool needExtraPara
= (!i
|| !fragment
.GetPartialParagraph());
2671 // If there was only one paragraph, we need to insert a new one.
2674 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2675 wxASSERT( para
!= NULL
);
2677 finalPara
= (wxRichTextParagraph
*) para
->Clone();
2680 InsertChild(finalPara
, nextParagraph
);
2682 AppendChild(finalPara
);
2687 // If there was only one paragraph, or we have full paragraphs in our fragment,
2688 // we need to insert a new one.
2691 finalPara
= new wxRichTextParagraph
;
2694 InsertChild(finalPara
, nextParagraph
);
2696 AppendChild(finalPara
);
2699 // 4. Add back the remaining content.
2703 finalPara
->MoveFromList(savedObjects
);
2705 // Ensure there's at least one object
2706 if (finalPara
->GetChildCount() == 0)
2708 wxRichTextPlainText
* text
= new wxRichTextPlainText(wxEmptyString
);
2709 text
->SetAttributes(emptyParagraphAttributes
);
2711 finalPara
->AppendChild(text
);
2715 if ((fragment
.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
) && firstPara
)
2716 finalPara
->SetAttributes(firstPara
->GetAttributes());
2717 else if (finalPara
&& finalPara
!= para
)
2718 finalPara
->SetAttributes(originalAttr
);
2726 wxRichTextObjectList::compatibility_iterator i
= fragment
.GetChildren().GetFirst();
2729 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2730 wxASSERT( para
!= NULL
);
2732 AppendChild(para
->Clone());
2741 /// Make a copy of the fragment corresponding to the given range, putting it in 'fragment'.
2742 /// If there was an incomplete paragraph at the end, partialParagraph is set to true.
2743 bool wxRichTextParagraphLayoutBox::CopyFragment(const wxRichTextRange
& range
, wxRichTextParagraphLayoutBox
& fragment
)
2745 wxRichTextObjectList::compatibility_iterator i
= GetChildren().GetFirst();
2748 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2749 wxASSERT( para
!= NULL
);
2751 if (!para
->GetRange().IsOutside(range
))
2753 fragment
.AppendChild(para
->Clone());
2758 // Now top and tail the first and last paragraphs in our new fragment (which might be the same).
2759 if (!fragment
.IsEmpty())
2761 wxRichTextParagraph
* firstPara
= wxDynamicCast(fragment
.GetChildren().GetFirst()->GetData(), wxRichTextParagraph
);
2762 wxASSERT( firstPara
!= NULL
);
2764 wxRichTextParagraph
* lastPara
= wxDynamicCast(fragment
.GetChildren().GetLast()->GetData(), wxRichTextParagraph
);
2765 wxASSERT( lastPara
!= NULL
);
2767 if (!firstPara
|| !lastPara
)
2770 bool isFragment
= (range
.GetEnd() < lastPara
->GetRange().GetEnd());
2772 long firstPos
= firstPara
->GetRange().GetStart();
2774 // Adjust for renumbering from zero
2775 wxRichTextRange
topTailRange(range
.GetStart() - firstPos
, range
.GetEnd() - firstPos
);
2778 fragment
.CalculateRange(0, end
);
2780 // Chop off the start of the paragraph
2781 if (topTailRange
.GetStart() > 0)
2783 wxRichTextRange
r(0, topTailRange
.GetStart()-1);
2784 firstPara
->DeleteRange(r
);
2786 // Make sure the numbering is correct
2787 fragment
.CalculateRange(0, end
);
2789 // Now, we've deleted some positions, so adjust the range
2791 topTailRange
.SetStart(range
.GetLength());
2792 topTailRange
.SetEnd(fragment
.GetOwnRange().GetEnd());
2796 topTailRange
.SetStart(range
.GetLength());
2797 topTailRange
.SetEnd(fragment
.GetOwnRange().GetEnd());
2800 if (topTailRange
.GetStart() < lastPara
->GetRange().GetEnd())
2802 lastPara
->DeleteRange(topTailRange
);
2804 // Make sure the numbering is correct
2806 fragment
.CalculateRange(0, end
);
2808 // We only have part of a paragraph at the end
2809 fragment
.SetPartialParagraph(true);
2813 // We have a partial paragraph (don't save last new paragraph marker)
2814 // or complete paragraph
2815 fragment
.SetPartialParagraph(isFragment
);
2822 /// Given a position, get the number of the visible line (potentially many to a paragraph),
2823 /// starting from zero at the start of the buffer.
2824 long wxRichTextParagraphLayoutBox::GetVisibleLineNumber(long pos
, bool caretPosition
, bool startOfLine
) const
2831 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2834 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2835 // wxASSERT( child != NULL );
2839 if (child
->GetRange().Contains(pos
))
2841 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2844 wxRichTextLine
* line
= node2
->GetData();
2845 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
2847 if (lineRange
.Contains(pos
) || pos
== lineRange
.GetStart())
2849 // If the caret is displayed at the end of the previous wrapped line,
2850 // we want to return the line it's _displayed_ at (not the actual line
2851 // containing the position).
2852 if (lineRange
.GetStart() == pos
&& !startOfLine
&& child
->GetRange().GetStart() != pos
)
2853 return lineCount
- 1;
2860 node2
= node2
->GetNext();
2862 // If we didn't find it in the lines, it must be
2863 // the last position of the paragraph. So return the last line.
2867 lineCount
+= child
->GetLines().GetCount();
2870 node
= node
->GetNext();
2877 /// Given a line number, get the corresponding wxRichTextLine object.
2878 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineForVisibleLineNumber(long lineNumber
) const
2882 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2885 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2886 // wxASSERT(child != NULL);
2890 if (lineNumber
< (int) (child
->GetLines().GetCount() + lineCount
))
2892 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2895 wxRichTextLine
* line
= node2
->GetData();
2897 if (lineCount
== lineNumber
)
2902 node2
= node2
->GetNext();
2906 lineCount
+= child
->GetLines().GetCount();
2909 node
= node
->GetNext();
2916 /// Delete range from layout.
2917 bool wxRichTextParagraphLayoutBox::DeleteRange(const wxRichTextRange
& range
)
2919 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2921 wxRichTextParagraph
* firstPara
= NULL
;
2924 wxRichTextParagraph
* obj
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2925 // wxASSERT (obj != NULL);
2927 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
2931 // Delete the range in each paragraph
2933 if (!obj
->GetRange().IsOutside(range
))
2935 // Deletes the content of this object within the given range
2936 obj
->DeleteRange(range
);
2938 wxRichTextRange thisRange
= obj
->GetRange();
2939 wxRichTextAttr thisAttr
= obj
->GetAttributes();
2941 // If the whole paragraph is within the range to delete,
2942 // delete the whole thing.
2943 if (range
.GetStart() <= thisRange
.GetStart() && range
.GetEnd() >= thisRange
.GetEnd())
2945 // Delete the whole object
2946 RemoveChild(obj
, true);
2949 else if (!firstPara
)
2952 // If the range includes the paragraph end, we need to join this
2953 // and the next paragraph.
2954 if (range
.GetEnd() <= thisRange
.GetEnd())
2956 // We need to move the objects from the next paragraph
2957 // to this paragraph
2959 wxRichTextParagraph
* nextParagraph
= NULL
;
2960 if ((range
.GetEnd() < thisRange
.GetEnd()) && obj
)
2961 nextParagraph
= obj
;
2964 // We're ending at the end of the paragraph, so merge the _next_ paragraph.
2966 nextParagraph
= wxDynamicCast(next
->GetData(), wxRichTextParagraph
);
2969 bool applyFinalParagraphStyle
= firstPara
&& nextParagraph
&& nextParagraph
!= firstPara
;
2971 wxRichTextAttr nextParaAttr
;
2972 if (applyFinalParagraphStyle
)
2974 // Special case when deleting the end of a paragraph - use _this_ paragraph's style,
2975 // not the next one.
2976 if (range
.GetStart() == range
.GetEnd() && range
.GetStart() == thisRange
.GetEnd())
2977 nextParaAttr
= thisAttr
;
2979 nextParaAttr
= nextParagraph
->GetAttributes();
2982 if (firstPara
&& nextParagraph
&& firstPara
!= nextParagraph
)
2984 // Move the objects to the previous para
2985 wxRichTextObjectList::compatibility_iterator node1
= nextParagraph
->GetChildren().GetFirst();
2989 wxRichTextObject
* obj1
= node1
->GetData();
2991 firstPara
->AppendChild(obj1
);
2993 wxRichTextObjectList::compatibility_iterator next1
= node1
->GetNext();
2994 nextParagraph
->GetChildren().Erase(node1
);
2999 // Delete the paragraph
3000 RemoveChild(nextParagraph
, true);
3003 // Avoid empty paragraphs
3004 if (firstPara
&& firstPara
->GetChildren().GetCount() == 0)
3006 wxRichTextPlainText
* text
= new wxRichTextPlainText(wxEmptyString
);
3007 firstPara
->AppendChild(text
);
3010 if (applyFinalParagraphStyle
)
3011 firstPara
->SetAttributes(nextParaAttr
);
3024 /// Get any text in this object for the given range
3025 wxString
wxRichTextParagraphLayoutBox::GetTextForRange(const wxRichTextRange
& range
) const
3029 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3032 wxRichTextObject
* child
= node
->GetData();
3033 if (!child
->GetRange().IsOutside(range
))
3035 wxRichTextRange childRange
= range
;
3036 childRange
.LimitTo(child
->GetRange());
3038 wxString childText
= child
->GetTextForRange(childRange
);
3042 if ((childRange
.GetEnd() == child
->GetRange().GetEnd()) && node
->GetNext())
3047 node
= node
->GetNext();
3053 /// Get all the text
3054 wxString
wxRichTextParagraphLayoutBox::GetText() const
3056 return GetTextForRange(GetOwnRange());
3059 /// Get the paragraph by number
3060 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphAtLine(long paragraphNumber
) const
3062 if ((size_t) paragraphNumber
>= GetChildCount())
3065 return (wxRichTextParagraph
*) GetChild((size_t) paragraphNumber
);
3068 /// Get the length of the paragraph
3069 int wxRichTextParagraphLayoutBox::GetParagraphLength(long paragraphNumber
) const
3071 wxRichTextParagraph
* para
= GetParagraphAtLine(paragraphNumber
);
3073 return para
->GetRange().GetLength() - 1; // don't include newline
3078 /// Get the text of the paragraph
3079 wxString
wxRichTextParagraphLayoutBox::GetParagraphText(long paragraphNumber
) const
3081 wxRichTextParagraph
* para
= GetParagraphAtLine(paragraphNumber
);
3083 return para
->GetTextForRange(para
->GetRange());
3085 return wxEmptyString
;
3088 /// Convert zero-based line column and paragraph number to a position.
3089 long wxRichTextParagraphLayoutBox::XYToPosition(long x
, long y
) const
3091 wxRichTextParagraph
* para
= GetParagraphAtLine(y
);
3094 return para
->GetRange().GetStart() + x
;
3100 /// Convert zero-based position to line column and paragraph number
3101 bool wxRichTextParagraphLayoutBox::PositionToXY(long pos
, long* x
, long* y
) const
3103 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
);
3107 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3110 wxRichTextObject
* child
= node
->GetData();
3114 node
= node
->GetNext();
3118 *x
= pos
- para
->GetRange().GetStart();
3126 /// Get the leaf object in a paragraph at this position.
3127 /// Given a line number, get the corresponding wxRichTextLine object.
3128 wxRichTextObject
* wxRichTextParagraphLayoutBox::GetLeafObjectAtPosition(long position
) const
3130 wxRichTextParagraph
* para
= GetParagraphAtPosition(position
);
3133 wxRichTextObjectList::compatibility_iterator node
= para
->GetChildren().GetFirst();
3137 wxRichTextObject
* child
= node
->GetData();
3138 if (child
->GetRange().Contains(position
))
3141 node
= node
->GetNext();
3143 if (position
== para
->GetRange().GetEnd() && para
->GetChildCount() > 0)
3144 return para
->GetChildren().GetLast()->GetData();
3149 /// Set character or paragraph text attributes: apply character styles only to immediate text nodes
3150 bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange
& range
, const wxRichTextAttr
& style
, int flags
)
3152 bool characterStyle
= false;
3153 bool paragraphStyle
= false;
3155 if (style
.IsCharacterStyle())
3156 characterStyle
= true;
3157 if (style
.IsParagraphStyle())
3158 paragraphStyle
= true;
3160 wxRichTextBuffer
* buffer
= GetBuffer();
3162 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
3163 bool applyMinimal
= ((flags
& wxRICHTEXT_SETSTYLE_OPTIMIZE
) != 0);
3164 bool parasOnly
= ((flags
& wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY
) != 0);
3165 bool charactersOnly
= ((flags
& wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY
) != 0);
3166 bool resetExistingStyle
= ((flags
& wxRICHTEXT_SETSTYLE_RESET
) != 0);
3167 bool removeStyle
= ((flags
& wxRICHTEXT_SETSTYLE_REMOVE
) != 0);
3169 // Apply paragraph style first, if any
3170 wxRichTextAttr
wholeStyle(style
);
3172 if (!removeStyle
&& wholeStyle
.HasParagraphStyleName() && buffer
->GetStyleSheet())
3174 wxRichTextParagraphStyleDefinition
* def
= buffer
->GetStyleSheet()->FindParagraphStyle(wholeStyle
.GetParagraphStyleName());
3176 wxRichTextApplyStyle(wholeStyle
, def
->GetStyleMergedWithBase(buffer
->GetStyleSheet()));
3179 // Limit the attributes to be set to the content to only character attributes.
3180 wxRichTextAttr
characterAttributes(wholeStyle
);
3181 characterAttributes
.SetFlags(characterAttributes
.GetFlags() & (wxTEXT_ATTR_CHARACTER
));
3183 if (!removeStyle
&& characterAttributes
.HasCharacterStyleName() && buffer
->GetStyleSheet())
3185 wxRichTextCharacterStyleDefinition
* def
= buffer
->GetStyleSheet()->FindCharacterStyle(characterAttributes
.GetCharacterStyleName());
3187 wxRichTextApplyStyle(characterAttributes
, def
->GetStyleMergedWithBase(buffer
->GetStyleSheet()));
3190 // If we are associated with a control, make undoable; otherwise, apply immediately
3193 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3195 wxRichTextAction
* action
= NULL
;
3197 if (haveControl
&& withUndo
)
3199 action
= new wxRichTextAction(NULL
, _("Change Style"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
3200 action
->SetRange(range
);
3201 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3204 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3207 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3208 // wxASSERT (para != NULL);
3210 if (para
&& para
->GetChildCount() > 0)
3212 // Stop searching if we're beyond the range of interest
3213 if (para
->GetRange().GetStart() > range
.GetEnd())
3216 if (!para
->GetRange().IsOutside(range
))
3218 // We'll be using a copy of the paragraph to make style changes,
3219 // not updating the buffer directly.
3220 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3222 if (haveControl
&& withUndo
)
3224 newPara
= new wxRichTextParagraph(*para
);
3225 action
->GetNewParagraphs().AppendChild(newPara
);
3227 // Also store the old ones for Undo
3228 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3233 // If we're specifying paragraphs only, then we really mean character formatting
3234 // to be included in the paragraph style
3235 if ((paragraphStyle
|| parasOnly
) && !charactersOnly
)
3239 // Removes the given style from the paragraph
3240 wxRichTextRemoveStyle(newPara
->GetAttributes(), style
);
3242 else if (resetExistingStyle
)
3243 newPara
->GetAttributes() = wholeStyle
;
3248 // Only apply attributes that will make a difference to the combined
3249 // style as seen on the display
3250 wxRichTextAttr
combinedAttr(para
->GetCombinedAttributes(true));
3251 wxRichTextApplyStyle(newPara
->GetAttributes(), wholeStyle
, & combinedAttr
);
3254 wxRichTextApplyStyle(newPara
->GetAttributes(), wholeStyle
);
3258 // When applying paragraph styles dynamically, don't change the text objects' attributes
3259 // since they will computed as needed. Only apply the character styling if it's _only_
3260 // character styling. This policy is subject to change and might be put under user control.
3262 // Hm. we might well be applying a mix of paragraph and character styles, in which
3263 // case we _do_ want to apply character styles regardless of what para styles are set.
3264 // But if we're applying a paragraph style, which has some character attributes, but
3265 // we only want the paragraphs to hold this character style, then we _don't_ want to
3266 // apply the character style. So we need to be able to choose.
3268 if (!parasOnly
&& (characterStyle
|charactersOnly
) && range
.GetStart() != newPara
->GetRange().GetEnd())
3270 wxRichTextRange
childRange(range
);
3271 childRange
.LimitTo(newPara
->GetRange());
3273 // Find the starting position and if necessary split it so
3274 // we can start applying a different style.
3275 // TODO: check that the style actually changes or is different
3276 // from style outside of range
3277 wxRichTextObject
* firstObject
wxDUMMY_INITIALIZE(NULL
);
3278 wxRichTextObject
* lastObject
wxDUMMY_INITIALIZE(NULL
);
3280 if (childRange
.GetStart() == newPara
->GetRange().GetStart())
3281 firstObject
= newPara
->GetChildren().GetFirst()->GetData();
3283 firstObject
= newPara
->SplitAt(range
.GetStart());
3285 // Increment by 1 because we're apply the style one _after_ the split point
3286 long splitPoint
= childRange
.GetEnd();
3287 if (splitPoint
!= newPara
->GetRange().GetEnd())
3291 if (splitPoint
== newPara
->GetRange().GetEnd())
3292 lastObject
= newPara
->GetChildren().GetLast()->GetData();
3294 // lastObject is set as a side-effect of splitting. It's
3295 // returned as the object before the new object.
3296 (void) newPara
->SplitAt(splitPoint
, & lastObject
);
3298 wxASSERT(firstObject
!= NULL
);
3299 wxASSERT(lastObject
!= NULL
);
3301 if (!firstObject
|| !lastObject
)
3304 wxRichTextObjectList::compatibility_iterator firstNode
= newPara
->GetChildren().Find(firstObject
);
3305 wxRichTextObjectList::compatibility_iterator lastNode
= newPara
->GetChildren().Find(lastObject
);
3307 wxASSERT(firstNode
);
3310 wxRichTextObjectList::compatibility_iterator node2
= firstNode
;
3314 wxRichTextObject
* child
= node2
->GetData();
3318 // Removes the given style from the paragraph
3319 wxRichTextRemoveStyle(child
->GetAttributes(), style
);
3321 else if (resetExistingStyle
)
3322 child
->GetAttributes() = characterAttributes
;
3327 // Only apply attributes that will make a difference to the combined
3328 // style as seen on the display
3329 wxRichTextAttr
combinedAttr(newPara
->GetCombinedAttributes(child
->GetAttributes(), true));
3330 wxRichTextApplyStyle(child
->GetAttributes(), characterAttributes
, & combinedAttr
);
3333 wxRichTextApplyStyle(child
->GetAttributes(), characterAttributes
);
3336 if (node2
== lastNode
)
3339 node2
= node2
->GetNext();
3345 node
= node
->GetNext();
3348 // Do action, or delay it until end of batch.
3349 if (haveControl
&& withUndo
)
3350 buffer
->SubmitAction(action
);
3355 // Just change the attributes for this single object.
3356 void wxRichTextParagraphLayoutBox::SetStyle(wxRichTextObject
* obj
, const wxRichTextAttr
& textAttr
, int flags
)
3358 wxRichTextBuffer
* buffer
= GetBuffer();
3359 bool withUndo
= flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
;
3360 bool resetExistingStyle
= ((flags
& wxRICHTEXT_SETSTYLE_RESET
) != 0);
3361 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3363 wxRichTextAction
*action
= NULL
;
3364 wxRichTextAttr newAttr
= obj
->GetAttributes();
3365 if (resetExistingStyle
)
3368 newAttr
.Apply(textAttr
);
3370 if (haveControl
&& withUndo
)
3372 action
= new wxRichTextAction(NULL
, _("Change Object Style"), wxRICHTEXT_CHANGE_ATTRIBUTES
, buffer
, obj
->GetContainer(), buffer
->GetRichTextCtrl());
3373 action
->SetRange(obj
->GetRange().FromInternal());
3374 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3375 action
->MakeObject(obj
);
3377 action
->GetAttributes() = newAttr
;
3380 obj
->GetAttributes() = newAttr
;
3382 if (haveControl
&& withUndo
)
3383 buffer
->SubmitAction(action
);
3386 /// Get the text attributes for this position.
3387 bool wxRichTextParagraphLayoutBox::GetStyle(long position
, wxRichTextAttr
& style
)
3389 return DoGetStyle(position
, style
, true);
3392 bool wxRichTextParagraphLayoutBox::GetUncombinedStyle(long position
, wxRichTextAttr
& style
)
3394 return DoGetStyle(position
, style
, false);
3397 /// Implementation helper for GetStyle. If combineStyles is true, combine base, paragraph and
3398 /// context attributes.
3399 bool wxRichTextParagraphLayoutBox::DoGetStyle(long position
, wxRichTextAttr
& style
, bool combineStyles
)
3401 wxRichTextObject
* obj
wxDUMMY_INITIALIZE(NULL
);
3403 if (style
.IsParagraphStyle())
3405 obj
= GetParagraphAtPosition(position
);
3410 // Start with the base style
3411 style
= GetAttributes();
3412 style
.GetTextBoxAttr().Reset();
3414 // Apply the paragraph style
3415 wxRichTextApplyStyle(style
, obj
->GetAttributes());
3418 style
= obj
->GetAttributes();
3425 obj
= GetLeafObjectAtPosition(position
);
3430 wxRichTextParagraph
* para
= wxDynamicCast(obj
->GetParent(), wxRichTextParagraph
);
3431 style
= para
? para
->GetCombinedAttributes(obj
->GetAttributes()) : obj
->GetAttributes();
3434 style
= obj
->GetAttributes();
3442 static bool wxHasStyle(long flags
, long style
)
3444 return (flags
& style
) != 0;
3447 /// Combines 'style' with 'currentStyle' for the purpose of summarising the attributes of a range of
3449 bool wxRichTextParagraphLayoutBox::CollectStyle(wxRichTextAttr
& currentStyle
, const wxRichTextAttr
& style
, wxRichTextAttr
& clashingAttr
, wxRichTextAttr
& absentAttr
)
3451 currentStyle
.CollectCommonAttributes(style
, clashingAttr
, absentAttr
);
3456 /// Get the combined style for a range - if any attribute is different within the range,
3457 /// that attribute is not present within the flags.
3458 /// *** Note that this is not recursive, and so assumes that content inside a paragraph is not itself
3460 bool wxRichTextParagraphLayoutBox::GetStyleForRange(const wxRichTextRange
& range
, wxRichTextAttr
& style
)
3462 style
= wxRichTextAttr();
3464 wxRichTextAttr clashingAttrPara
, clashingAttrChar
;
3465 wxRichTextAttr absentAttrPara
, absentAttrChar
;
3467 wxRichTextObjectList::compatibility_iterator node
= GetChildren().GetFirst();
3470 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3471 if (para
&& !(para
->GetRange().GetStart() > range
.GetEnd() || para
->GetRange().GetEnd() < range
.GetStart()))
3473 if (para
->GetChildren().GetCount() == 0)
3475 wxRichTextAttr paraStyle
= para
->GetCombinedAttributes(true /* use box attributes */);
3477 CollectStyle(style
, paraStyle
, clashingAttrPara
, absentAttrPara
);
3481 wxRichTextRange
paraRange(para
->GetRange());
3482 paraRange
.LimitTo(range
);
3484 // First collect paragraph attributes only
3485 wxRichTextAttr paraStyle
= para
->GetCombinedAttributes();
3486 paraStyle
.SetFlags(paraStyle
.GetFlags() & wxTEXT_ATTR_PARAGRAPH
);
3487 CollectStyle(style
, paraStyle
, clashingAttrPara
, absentAttrPara
);
3489 wxRichTextObjectList::compatibility_iterator childNode
= para
->GetChildren().GetFirst();
3493 wxRichTextObject
* child
= childNode
->GetData();
3494 if (!(child
->GetRange().GetStart() > range
.GetEnd() || child
->GetRange().GetEnd() < range
.GetStart()))
3496 wxRichTextAttr childStyle
= para
->GetCombinedAttributes(child
->GetAttributes(), true /* include box attributes */);
3498 // Now collect character attributes only
3499 childStyle
.SetFlags(childStyle
.GetFlags() & wxTEXT_ATTR_CHARACTER
);
3501 CollectStyle(style
, childStyle
, clashingAttrChar
, absentAttrChar
);
3504 childNode
= childNode
->GetNext();
3508 node
= node
->GetNext();
3513 /// Set default style
3514 bool wxRichTextParagraphLayoutBox::SetDefaultStyle(const wxRichTextAttr
& style
)
3516 m_defaultAttributes
= style
;
3520 /// Test if this whole range has character attributes of the specified kind. If any
3521 /// of the attributes are different within the range, the test fails. You
3522 /// can use this to implement, for example, bold button updating. style must have
3523 /// flags indicating which attributes are of interest.
3524 bool wxRichTextParagraphLayoutBox::HasCharacterAttributes(const wxRichTextRange
& range
, const wxRichTextAttr
& style
) const
3527 int matchingCount
= 0;
3529 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3532 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3533 // wxASSERT (para != NULL);
3537 // Stop searching if we're beyond the range of interest
3538 if (para
->GetRange().GetStart() > range
.GetEnd())
3539 return foundCount
== matchingCount
&& foundCount
!= 0;
3541 if (!para
->GetRange().IsOutside(range
))
3543 wxRichTextObjectList::compatibility_iterator node2
= para
->GetChildren().GetFirst();
3547 wxRichTextObject
* child
= node2
->GetData();
3548 // Allow for empty string if no buffer
3549 wxRichTextRange childRange
= child
->GetRange();
3550 if (childRange
.GetLength() == 0 && GetRange().GetLength() == 1)
3551 childRange
.SetEnd(childRange
.GetEnd()+1);
3553 if (!childRange
.IsOutside(range
) && wxDynamicCast(child
, wxRichTextPlainText
))
3556 wxRichTextAttr textAttr
= para
->GetCombinedAttributes(child
->GetAttributes());
3558 if (textAttr
.EqPartial(style
, false /* strong test - attributes must be valid in both objects */))
3562 node2
= node2
->GetNext();
3567 node
= node
->GetNext();
3570 return foundCount
== matchingCount
&& foundCount
!= 0;
3573 /// Test if this whole range has paragraph attributes of the specified kind. If any
3574 /// of the attributes are different within the range, the test fails. You
3575 /// can use this to implement, for example, centering button updating. style must have
3576 /// flags indicating which attributes are of interest.
3577 bool wxRichTextParagraphLayoutBox::HasParagraphAttributes(const wxRichTextRange
& range
, const wxRichTextAttr
& style
) const
3580 int matchingCount
= 0;
3582 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3585 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3586 // wxASSERT (para != NULL);
3590 // Stop searching if we're beyond the range of interest
3591 if (para
->GetRange().GetStart() > range
.GetEnd())
3592 return foundCount
== matchingCount
&& foundCount
!= 0;
3594 if (!para
->GetRange().IsOutside(range
))
3596 wxRichTextAttr textAttr
= GetAttributes();
3597 // Apply the paragraph style
3598 wxRichTextApplyStyle(textAttr
, para
->GetAttributes());
3601 if (textAttr
.EqPartial(style
, false /* strong test */))
3606 node
= node
->GetNext();
3608 return foundCount
== matchingCount
&& foundCount
!= 0;
3611 void wxRichTextParagraphLayoutBox::PrepareContent(wxRichTextParagraphLayoutBox
& container
)
3613 wxRichTextBuffer
* buffer
= GetBuffer();
3614 if (buffer
&& buffer
->GetRichTextCtrl())
3615 buffer
->GetRichTextCtrl()->PrepareContent(container
);
3618 /// Set character or paragraph properties
3619 bool wxRichTextParagraphLayoutBox::SetProperties(const wxRichTextRange
& range
, const wxRichTextProperties
& properties
, int flags
)
3621 wxRichTextBuffer
* buffer
= GetBuffer();
3623 bool withUndo
= ((flags
& wxRICHTEXT_SETPROPERTIES_WITH_UNDO
) != 0);
3624 bool parasOnly
= ((flags
& wxRICHTEXT_SETPROPERTIES_PARAGRAPHS_ONLY
) != 0);
3625 bool charactersOnly
= ((flags
& wxRICHTEXT_SETPROPERTIES_CHARACTERS_ONLY
) != 0);
3626 bool resetExistingProperties
= ((flags
& wxRICHTEXT_SETPROPERTIES_RESET
) != 0);
3627 bool removeProperties
= ((flags
& wxRICHTEXT_SETPROPERTIES_REMOVE
) != 0);
3629 // If we are associated with a control, make undoable; otherwise, apply immediately
3632 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3634 wxRichTextAction
* action
= NULL
;
3636 if (haveControl
&& withUndo
)
3638 action
= new wxRichTextAction(NULL
, _("Change Properties"), wxRICHTEXT_CHANGE_PROPERTIES
, buffer
, this, buffer
->GetRichTextCtrl());
3639 action
->SetRange(range
);
3640 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3643 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3646 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3647 // wxASSERT (para != NULL);
3649 if (para
&& para
->GetChildCount() > 0)
3651 // Stop searching if we're beyond the range of interest
3652 if (para
->GetRange().GetStart() > range
.GetEnd())
3655 if (!para
->GetRange().IsOutside(range
))
3657 // We'll be using a copy of the paragraph to make style changes,
3658 // not updating the buffer directly.
3659 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3661 if (haveControl
&& withUndo
)
3663 newPara
= new wxRichTextParagraph(*para
);
3664 action
->GetNewParagraphs().AppendChild(newPara
);
3666 // Also store the old ones for Undo
3667 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3674 if (removeProperties
)
3676 // Removes the given style from the paragraph
3678 newPara
->GetProperties().RemoveProperties(properties
);
3680 else if (resetExistingProperties
)
3681 newPara
->GetProperties() = properties
;
3683 newPara
->GetProperties().MergeProperties(properties
);
3686 // When applying paragraph styles dynamically, don't change the text objects' attributes
3687 // since they will computed as needed. Only apply the character styling if it's _only_
3688 // character styling. This policy is subject to change and might be put under user control.
3690 // Hm. we might well be applying a mix of paragraph and character styles, in which
3691 // case we _do_ want to apply character styles regardless of what para styles are set.
3692 // But if we're applying a paragraph style, which has some character attributes, but
3693 // we only want the paragraphs to hold this character style, then we _don't_ want to
3694 // apply the character style. So we need to be able to choose.
3696 if (!parasOnly
&& charactersOnly
&& range
.GetStart() != newPara
->GetRange().GetEnd())
3698 wxRichTextRange
childRange(range
);
3699 childRange
.LimitTo(newPara
->GetRange());
3701 // Find the starting position and if necessary split it so
3702 // we can start applying different properties.
3703 // TODO: check that the properties actually change or are different
3704 // from properties outside of range
3705 wxRichTextObject
* firstObject
wxDUMMY_INITIALIZE(NULL
);
3706 wxRichTextObject
* lastObject
wxDUMMY_INITIALIZE(NULL
);
3708 if (childRange
.GetStart() == newPara
->GetRange().GetStart())
3709 firstObject
= newPara
->GetChildren().GetFirst()->GetData();
3711 firstObject
= newPara
->SplitAt(range
.GetStart());
3713 // Increment by 1 because we're apply the style one _after_ the split point
3714 long splitPoint
= childRange
.GetEnd();
3715 if (splitPoint
!= newPara
->GetRange().GetEnd())
3719 if (splitPoint
== newPara
->GetRange().GetEnd())
3720 lastObject
= newPara
->GetChildren().GetLast()->GetData();
3722 // lastObject is set as a side-effect of splitting. It's
3723 // returned as the object before the new object.
3724 (void) newPara
->SplitAt(splitPoint
, & lastObject
);
3726 wxASSERT(firstObject
!= NULL
);
3727 wxASSERT(lastObject
!= NULL
);
3729 if (!firstObject
|| !lastObject
)
3732 wxRichTextObjectList::compatibility_iterator firstNode
= newPara
->GetChildren().Find(firstObject
);
3733 wxRichTextObjectList::compatibility_iterator lastNode
= newPara
->GetChildren().Find(lastObject
);
3735 wxASSERT(firstNode
);
3738 wxRichTextObjectList::compatibility_iterator node2
= firstNode
;
3742 wxRichTextObject
* child
= node2
->GetData();
3744 if (removeProperties
)
3746 // Removes the given properties from the paragraph
3747 child
->GetProperties().RemoveProperties(properties
);
3749 else if (resetExistingProperties
)
3750 child
->GetProperties() = properties
;
3753 child
->GetProperties().MergeProperties(properties
);
3756 if (node2
== lastNode
)
3759 node2
= node2
->GetNext();
3765 node
= node
->GetNext();
3768 // Do action, or delay it until end of batch.
3769 if (haveControl
&& withUndo
)
3770 buffer
->SubmitAction(action
);
3775 void wxRichTextParagraphLayoutBox::Reset()
3779 wxRichTextBuffer
* buffer
= GetBuffer();
3780 if (buffer
&& buffer
->GetRichTextCtrl())
3782 wxRichTextEvent
event(wxEVT_COMMAND_RICHTEXT_BUFFER_RESET
, buffer
->GetRichTextCtrl()->GetId());
3783 event
.SetEventObject(buffer
->GetRichTextCtrl());
3784 event
.SetContainer(this);
3786 buffer
->SendEvent(event
, true);
3789 AddParagraph(wxEmptyString
);
3791 PrepareContent(*this);
3793 InvalidateHierarchy(wxRICHTEXT_ALL
);
3796 /// Invalidate the buffer. With no argument, invalidates whole buffer.
3797 void wxRichTextParagraphLayoutBox::Invalidate(const wxRichTextRange
& invalidRange
)
3799 wxRichTextCompositeObject::Invalidate(invalidRange
);
3801 DoInvalidate(invalidRange
);
3804 // Do the (in)validation for this object only
3805 void wxRichTextParagraphLayoutBox::DoInvalidate(const wxRichTextRange
& invalidRange
)
3807 if (invalidRange
== wxRICHTEXT_ALL
)
3809 m_invalidRange
= wxRICHTEXT_ALL
;
3811 // Already invalidating everything
3812 else if (m_invalidRange
== wxRICHTEXT_ALL
)
3817 if ((invalidRange
.GetStart() < m_invalidRange
.GetStart()) || m_invalidRange
.GetStart() == -1)
3818 m_invalidRange
.SetStart(invalidRange
.GetStart());
3819 if (invalidRange
.GetEnd() > m_invalidRange
.GetEnd())
3820 m_invalidRange
.SetEnd(invalidRange
.GetEnd());
3824 // Do the (in)validation both up and down the hierarchy
3825 void wxRichTextParagraphLayoutBox::InvalidateHierarchy(const wxRichTextRange
& invalidRange
)
3827 Invalidate(invalidRange
);
3829 if (invalidRange
!= wxRICHTEXT_NONE
)
3831 // Now go up the hierarchy
3832 wxRichTextObject
* thisObj
= this;
3833 wxRichTextObject
* p
= GetParent();
3836 wxRichTextParagraphLayoutBox
* l
= wxDynamicCast(p
, wxRichTextParagraphLayoutBox
);
3838 l
->DoInvalidate(thisObj
->GetRange());
3846 /// Get invalid range, rounding to entire paragraphs if argument is true.
3847 wxRichTextRange
wxRichTextParagraphLayoutBox::GetInvalidRange(bool wholeParagraphs
) const
3849 if (m_invalidRange
== wxRICHTEXT_ALL
|| m_invalidRange
== wxRICHTEXT_NONE
)
3850 return m_invalidRange
;
3852 wxRichTextRange range
= m_invalidRange
;
3854 if (wholeParagraphs
)
3856 wxRichTextParagraph
* para1
= GetParagraphAtPosition(range
.GetStart());
3858 range
.SetStart(para1
->GetRange().GetStart());
3860 // FIXME: be more intelligent about this. Check if we have floating objects
3861 // before the end of the range. But it's not clear how we can in general
3862 // tell where it's safe to stop laying out.
3863 // Anyway, this code is central to efficiency when laying in floating mode.
3864 if (!wxRichTextBuffer::GetFloatingLayoutMode())
3866 wxRichTextParagraph
* para2
= GetParagraphAtPosition(range
.GetEnd());
3868 range
.SetEnd(para2
->GetRange().GetEnd());
3871 // Floating layout means that all children should be laid out,
3872 // because we can't tell how the whole buffer will be affected.
3873 range
.SetEnd(GetOwnRange().GetEnd());
3878 /// Apply the style sheet to the buffer, for example if the styles have changed.
3879 bool wxRichTextParagraphLayoutBox::ApplyStyleSheet(wxRichTextStyleSheet
* styleSheet
)
3881 wxASSERT(styleSheet
!= NULL
);
3887 wxRichTextAttr
attr(GetBasicStyle());
3888 if (GetBasicStyle().HasParagraphStyleName())
3890 wxRichTextParagraphStyleDefinition
* paraDef
= styleSheet
->FindParagraphStyle(GetBasicStyle().GetParagraphStyleName());
3893 attr
.Apply(paraDef
->GetStyleMergedWithBase(styleSheet
));
3894 SetBasicStyle(attr
);
3899 if (GetBasicStyle().HasCharacterStyleName())
3901 wxRichTextCharacterStyleDefinition
* charDef
= styleSheet
->FindCharacterStyle(GetBasicStyle().GetCharacterStyleName());
3904 attr
.Apply(charDef
->GetStyleMergedWithBase(styleSheet
));
3905 SetBasicStyle(attr
);
3910 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3913 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3914 // wxASSERT (para != NULL);
3918 // Combine paragraph and list styles. If there is a list style in the original attributes,
3919 // the current indentation overrides anything else and is used to find the item indentation.
3920 // Also, for applying paragraph styles, consider having 2 modes: (1) we merge with what we have,
3921 // thereby taking into account all user changes, (2) reset the style completely (except for indentation/list
3922 // exception as above).
3923 // Problem: when changing from one list style to another, there's a danger that the level info will get lost.
3924 // So when changing a list style interactively, could retrieve level based on current style, then
3925 // set appropriate indent and apply new style.
3929 if (para
->GetAttributes().HasOutlineLevel())
3930 outline
= para
->GetAttributes().GetOutlineLevel();
3931 if (para
->GetAttributes().HasBulletNumber())
3932 num
= para
->GetAttributes().GetBulletNumber();
3934 if (!para
->GetAttributes().GetParagraphStyleName().IsEmpty() && !para
->GetAttributes().GetListStyleName().IsEmpty())
3936 int currentIndent
= para
->GetAttributes().GetLeftIndent();
3938 wxRichTextParagraphStyleDefinition
* paraDef
= styleSheet
->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
3939 wxRichTextListStyleDefinition
* listDef
= styleSheet
->FindListStyle(para
->GetAttributes().GetListStyleName());
3940 if (paraDef
&& !listDef
)
3942 para
->GetAttributes() = paraDef
->GetStyleMergedWithBase(styleSheet
);
3945 else if (listDef
&& !paraDef
)
3947 // Set overall style defined for the list style definition
3948 para
->GetAttributes() = listDef
->GetStyleMergedWithBase(styleSheet
);
3950 // Apply the style for this level
3951 wxRichTextApplyStyle(para
->GetAttributes(), * listDef
->GetLevelAttributes(listDef
->FindLevelForIndent(currentIndent
)));
3954 else if (listDef
&& paraDef
)
3956 // Combines overall list style, style for level, and paragraph style
3957 para
->GetAttributes() = listDef
->CombineWithParagraphStyle(currentIndent
, paraDef
->GetStyleMergedWithBase(styleSheet
));
3961 else if (para
->GetAttributes().GetParagraphStyleName().IsEmpty() && !para
->GetAttributes().GetListStyleName().IsEmpty())
3963 int currentIndent
= para
->GetAttributes().GetLeftIndent();
3965 wxRichTextListStyleDefinition
* listDef
= styleSheet
->FindListStyle(para
->GetAttributes().GetListStyleName());
3967 // Overall list definition style
3968 para
->GetAttributes() = listDef
->GetStyleMergedWithBase(styleSheet
);
3970 // Style for this level
3971 wxRichTextApplyStyle(para
->GetAttributes(), * listDef
->GetLevelAttributes(listDef
->FindLevelForIndent(currentIndent
)));
3975 else if (!para
->GetAttributes().GetParagraphStyleName().IsEmpty() && para
->GetAttributes().GetListStyleName().IsEmpty())
3977 wxRichTextParagraphStyleDefinition
* def
= styleSheet
->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
3980 para
->GetAttributes() = def
->GetStyleMergedWithBase(styleSheet
);
3986 para
->GetAttributes().SetOutlineLevel(outline
);
3988 para
->GetAttributes().SetBulletNumber(num
);
3991 node
= node
->GetNext();
3993 return foundCount
!= 0;
3997 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
3999 wxRichTextBuffer
* buffer
= GetBuffer();
4000 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
4002 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
4003 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
4004 bool specifyLevel
= ((flags
& wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL
) != 0);
4005 bool renumber
= ((flags
& wxRICHTEXT_SETSTYLE_RENUMBER
) != 0);
4007 // Current number, if numbering
4010 wxASSERT (!specifyLevel
|| (specifyLevel
&& (specifiedLevel
>= 0)));
4012 // If we are associated with a control, make undoable; otherwise, apply immediately
4015 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
4017 wxRichTextAction
* action
= NULL
;
4019 if (haveControl
&& withUndo
)
4021 action
= new wxRichTextAction(NULL
, _("Change List Style"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
4022 action
->SetRange(range
);
4023 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
4026 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
4029 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
4030 // wxASSERT (para != NULL);
4032 if (para
&& para
->GetChildCount() > 0)
4034 // Stop searching if we're beyond the range of interest
4035 if (para
->GetRange().GetStart() > range
.GetEnd())
4038 if (!para
->GetRange().IsOutside(range
))
4040 // We'll be using a copy of the paragraph to make style changes,
4041 // not updating the buffer directly.
4042 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
4044 if (haveControl
&& withUndo
)
4046 newPara
= new wxRichTextParagraph(*para
);
4047 action
->GetNewParagraphs().AppendChild(newPara
);
4049 // Also store the old ones for Undo
4050 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
4057 int thisIndent
= newPara
->GetAttributes().GetLeftIndent();
4058 int thisLevel
= specifyLevel
? specifiedLevel
: def
->FindLevelForIndent(thisIndent
);
4060 // How is numbering going to work?
4061 // If we are renumbering, or numbering for the first time, we need to keep
4062 // track of the number for each level. But we might be simply applying a different
4064 // In Word, applying a style to several paragraphs, even if at different levels,
4065 // reverts the level back to the same one. So we could do the same here.
4066 // Renumbering will need to be done when we promote/demote a paragraph.
4068 // Apply the overall list style, and item style for this level
4069 wxRichTextAttr
listStyle(def
->GetCombinedStyleForLevel(thisLevel
, styleSheet
));
4070 wxRichTextApplyStyle(newPara
->GetAttributes(), listStyle
);
4072 // Now we need to do numbering
4073 // Preserve the existing list item continuation bullet style, if any
4074 if (para
->GetAttributes().HasBulletStyle() && (para
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
))
4075 newPara
->GetAttributes().SetBulletStyle(newPara
->GetAttributes().GetBulletStyle()|wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
);
4080 newPara
->GetAttributes().SetBulletNumber(n
);
4086 else if (!newPara
->GetAttributes().GetListStyleName().IsEmpty())
4088 // if def is NULL, remove list style, applying any associated paragraph style
4089 // to restore the attributes
4091 newPara
->GetAttributes().SetListStyleName(wxEmptyString
);
4092 newPara
->GetAttributes().SetLeftIndent(0, 0);
4093 newPara
->GetAttributes().SetBulletText(wxEmptyString
);
4094 newPara
->GetAttributes().SetBulletStyle(0);
4096 // Eliminate the main list-related attributes
4097 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
);
4099 if (styleSheet
&& !newPara
->GetAttributes().GetParagraphStyleName().IsEmpty())
4101 wxRichTextParagraphStyleDefinition
* def
= styleSheet
->FindParagraphStyle(newPara
->GetAttributes().GetParagraphStyleName());
4104 newPara
->GetAttributes() = def
->GetStyleMergedWithBase(styleSheet
);
4111 node
= node
->GetNext();
4114 // Do action, or delay it until end of batch.
4115 if (haveControl
&& withUndo
)
4116 buffer
->SubmitAction(action
);
4121 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
)
4123 wxRichTextBuffer
* buffer
= GetBuffer();
4124 if (buffer
&& buffer
->GetStyleSheet())
4126 wxRichTextListStyleDefinition
* def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4128 return SetListStyle(range
, def
, flags
, startFrom
, specifiedLevel
);
4133 /// Clear list for given range
4134 bool wxRichTextParagraphLayoutBox::ClearListStyle(const wxRichTextRange
& range
, int flags
)
4136 return SetListStyle(range
, NULL
, flags
);
4139 /// Number/renumber any list elements in the given range
4140 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
4142 return DoNumberList(range
, range
, 0, def
, flags
, startFrom
, specifiedLevel
);
4145 /// Number/renumber any list elements in the given range. Also do promotion or demotion of items, if specified
4146 bool wxRichTextParagraphLayoutBox::DoNumberList(const wxRichTextRange
& range
, const wxRichTextRange
& promotionRange
, int promoteBy
,
4147 wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
4149 wxRichTextBuffer
* buffer
= GetBuffer();
4150 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
4152 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
4153 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
4155 bool specifyLevel
= ((flags
& wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL
) != 0);
4158 bool renumber
= ((flags
& wxRICHTEXT_SETSTYLE_RENUMBER
) != 0);
4160 // Max number of levels
4161 const int maxLevels
= 10;
4163 // The level we're looking at now
4164 int currentLevel
= -1;
4166 // The item number for each level
4167 int levels
[maxLevels
];
4170 // Reset all numbering
4171 for (i
= 0; i
< maxLevels
; i
++)
4173 if (startFrom
!= -1)
4174 levels
[i
] = startFrom
-1;
4175 else if (renumber
) // start again
4178 levels
[i
] = -1; // start from the number we found, if any
4182 wxASSERT(!specifyLevel
|| (specifyLevel
&& (specifiedLevel
>= 0)));
4185 // If we are associated with a control, make undoable; otherwise, apply immediately
4188 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
4190 wxRichTextAction
* action
= NULL
;
4192 if (haveControl
&& withUndo
)
4194 action
= new wxRichTextAction(NULL
, _("Renumber List"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
4195 action
->SetRange(range
);
4196 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
4199 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
4202 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
4203 // wxASSERT (para != NULL);
4205 if (para
&& para
->GetChildCount() > 0)
4207 // Stop searching if we're beyond the range of interest
4208 if (para
->GetRange().GetStart() > range
.GetEnd())
4211 if (!para
->GetRange().IsOutside(range
))
4213 // We'll be using a copy of the paragraph to make style changes,
4214 // not updating the buffer directly.
4215 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
4217 if (haveControl
&& withUndo
)
4219 newPara
= new wxRichTextParagraph(*para
);
4220 action
->GetNewParagraphs().AppendChild(newPara
);
4222 // Also store the old ones for Undo
4223 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
4228 wxRichTextListStyleDefinition
* defToUse
= def
;
4231 if (styleSheet
&& !newPara
->GetAttributes().GetListStyleName().IsEmpty())
4232 defToUse
= styleSheet
->FindListStyle(newPara
->GetAttributes().GetListStyleName());
4237 int thisIndent
= newPara
->GetAttributes().GetLeftIndent();
4238 int thisLevel
= defToUse
->FindLevelForIndent(thisIndent
);
4240 // If we've specified a level to apply to all, change the level.
4241 if (specifiedLevel
!= -1)
4242 thisLevel
= specifiedLevel
;
4244 // Do promotion if specified
4245 if ((promoteBy
!= 0) && !para
->GetRange().IsOutside(promotionRange
))
4247 thisLevel
= thisLevel
- promoteBy
;
4254 // Apply the overall list style, and item style for this level
4255 wxRichTextAttr
listStyle(defToUse
->GetCombinedStyleForLevel(thisLevel
, styleSheet
));
4256 wxRichTextApplyStyle(newPara
->GetAttributes(), listStyle
);
4258 // Preserve the existing list item continuation bullet style, if any
4259 if (para
->GetAttributes().HasBulletStyle() && (para
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
))
4260 newPara
->GetAttributes().SetBulletStyle(newPara
->GetAttributes().GetBulletStyle()|wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
);
4262 // OK, we've (re)applied the style, now let's get the numbering right.
4264 if (currentLevel
== -1)
4265 currentLevel
= thisLevel
;
4267 // Same level as before, do nothing except increment level's number afterwards
4268 if (currentLevel
== thisLevel
)
4271 // A deeper level: start renumbering all levels after current level
4272 else if (thisLevel
> currentLevel
)
4274 for (i
= currentLevel
+1; i
<= thisLevel
; i
++)
4278 currentLevel
= thisLevel
;
4280 else if (thisLevel
< currentLevel
)
4282 currentLevel
= thisLevel
;
4285 // Use the current numbering if -1 and we have a bullet number already
4286 if (levels
[currentLevel
] == -1)
4288 if (newPara
->GetAttributes().HasBulletNumber())
4289 levels
[currentLevel
] = newPara
->GetAttributes().GetBulletNumber();
4291 levels
[currentLevel
] = 1;
4295 if (!(para
->GetAttributes().HasBulletStyle() && (para
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
)))
4296 levels
[currentLevel
] ++;
4299 newPara
->GetAttributes().SetBulletNumber(levels
[currentLevel
]);
4301 // Create the bullet text if an outline list
4302 if (listStyle
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
)
4305 for (i
= 0; i
<= currentLevel
; i
++)
4307 if (!text
.IsEmpty())
4309 text
+= wxString::Format(wxT("%d"), levels
[i
]);
4311 newPara
->GetAttributes().SetBulletText(text
);
4317 node
= node
->GetNext();
4320 // Do action, or delay it until end of batch.
4321 if (haveControl
&& withUndo
)
4322 buffer
->SubmitAction(action
);
4327 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
)
4329 wxRichTextBuffer
* buffer
= GetBuffer();
4330 if (buffer
->GetStyleSheet())
4332 wxRichTextListStyleDefinition
* def
= NULL
;
4333 if (!defName
.IsEmpty())
4334 def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4335 return NumberList(range
, def
, flags
, startFrom
, specifiedLevel
);
4340 /// Promote the list items within the given range. promoteBy can be a positive or negative number, e.g. 1 or -1
4341 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy
, const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int specifiedLevel
)
4344 // One strategy is to first work out the range within which renumbering must occur. Then could pass these two ranges
4345 // to NumberList with a flag indicating promotion is required within one of the ranges.
4346 // Find first and last paragraphs in range. Then for first, calculate new indentation and look back until we find
4347 // a paragraph that either has no list style, or has one that is different or whose indentation is less.
4348 // We start renumbering from the para after that different para we found. We specify that the numbering of that
4349 // list position will start from 1.
4350 // Similarly, we look after the last para in the promote range for an indentation that is less (or no list style).
4351 // We can end the renumbering at this point.
4353 // For now, only renumber within the promotion range.
4355 return DoNumberList(range
, range
, promoteBy
, def
, flags
, 1, specifiedLevel
);
4358 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy
, const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int specifiedLevel
)
4360 wxRichTextBuffer
* buffer
= GetBuffer();
4361 if (buffer
->GetStyleSheet())
4363 wxRichTextListStyleDefinition
* def
= NULL
;
4364 if (!defName
.IsEmpty())
4365 def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4366 return PromoteList(promoteBy
, range
, def
, flags
, specifiedLevel
);
4371 /// Fills in the attributes for numbering a paragraph after previousParagraph. It also finds the
4372 /// position of the paragraph that it had to start looking from.
4373 bool wxRichTextParagraphLayoutBox::FindNextParagraphNumber(wxRichTextParagraph
* previousParagraph
, wxRichTextAttr
& attr
) const
4375 // TODO: add GetNextChild/GetPreviousChild to composite
4376 // Search for a paragraph that isn't a continuation paragraph (no bullet)
4377 while (previousParagraph
&& previousParagraph
->GetAttributes().HasBulletStyle() && previousParagraph
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
)
4379 wxRichTextObjectList::compatibility_iterator node
= ((wxRichTextCompositeObject
*) previousParagraph
->GetParent())->GetChildren().Find(previousParagraph
);
4382 node
= node
->GetPrevious();
4384 previousParagraph
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
4386 previousParagraph
= NULL
;
4389 previousParagraph
= NULL
;
4392 if (!previousParagraph
|| !previousParagraph
->GetAttributes().HasFlag(wxTEXT_ATTR_BULLET_STYLE
) || previousParagraph
->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
)
4395 wxRichTextBuffer
* buffer
= GetBuffer();
4396 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
4397 if (styleSheet
&& !previousParagraph
->GetAttributes().GetListStyleName().IsEmpty())
4399 wxRichTextListStyleDefinition
* def
= styleSheet
->FindListStyle(previousParagraph
->GetAttributes().GetListStyleName());
4402 // int thisIndent = previousParagraph->GetAttributes().GetLeftIndent();
4403 // int thisLevel = def->FindLevelForIndent(thisIndent);
4405 bool isOutline
= (previousParagraph
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
) != 0;
4407 attr
.SetFlags(previousParagraph
->GetAttributes().GetFlags() & (wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_BULLET_NUMBER
|wxTEXT_ATTR_BULLET_TEXT
|wxTEXT_ATTR_BULLET_NAME
));
4408 if (previousParagraph
->GetAttributes().HasBulletName())
4409 attr
.SetBulletName(previousParagraph
->GetAttributes().GetBulletName());
4410 attr
.SetBulletStyle(previousParagraph
->GetAttributes().GetBulletStyle());
4411 attr
.SetListStyleName(previousParagraph
->GetAttributes().GetListStyleName());
4413 int nextNumber
= previousParagraph
->GetAttributes().GetBulletNumber() + 1;
4414 attr
.SetBulletNumber(nextNumber
);
4418 wxString text
= previousParagraph
->GetAttributes().GetBulletText();
4419 if (!text
.IsEmpty())
4421 int pos
= text
.Find(wxT('.'), true);
4422 if (pos
!= wxNOT_FOUND
)
4424 text
= text
.Mid(0, text
.Length() - pos
- 1);
4427 text
= wxEmptyString
;
4428 if (!text
.IsEmpty())
4430 text
+= wxString::Format(wxT("%d"), nextNumber
);
4431 attr
.SetBulletText(text
);
4445 * wxRichTextParagraph
4446 * This object represents a single paragraph (or in a straight text editor, a line).
4449 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraph
, wxRichTextCompositeObject
)
4451 wxArrayInt
wxRichTextParagraph::sm_defaultTabs
;
4453 wxRichTextParagraph::wxRichTextParagraph(wxRichTextObject
* parent
, wxRichTextAttr
* style
):
4454 wxRichTextCompositeObject(parent
)
4457 SetAttributes(*style
);
4460 wxRichTextParagraph::wxRichTextParagraph(const wxString
& text
, wxRichTextObject
* parent
, wxRichTextAttr
* paraStyle
, wxRichTextAttr
* charStyle
):
4461 wxRichTextCompositeObject(parent
)
4464 SetAttributes(*paraStyle
);
4466 AppendChild(new wxRichTextPlainText(text
, this, charStyle
));
4469 wxRichTextParagraph::~wxRichTextParagraph()
4475 bool wxRichTextParagraph::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int WXUNUSED(descent
), int style
)
4480 // Currently we don't merge these attributes with the parent, but we
4481 // should consider whether we should (e.g. if we set a border colour
4482 // for all paragraphs). But generally box attributes are likely to be
4483 // different for different objects.
4484 wxRect paraRect
= GetRect();
4485 wxRichTextAttr attr
= GetCombinedAttributes();
4486 context
.ApplyVirtualAttributes(attr
, this);
4488 DrawBoxAttributes(dc
, GetBuffer(), attr
, paraRect
);
4490 // Draw the bullet, if any
4491 if ((attr
.GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
) == 0 && (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
) == 0)
4493 if (attr
.GetLeftSubIndent() != 0)
4495 int spaceBeforePara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingBefore());
4496 int leftIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftIndent());
4498 wxRichTextAttr
bulletAttr(attr
);
4500 // Combine with the font of the first piece of content, if one is specified
4501 if (GetChildren().GetCount() > 0)
4503 wxRichTextObject
* firstObj
= (wxRichTextObject
*) GetChildren().GetFirst()->GetData();
4504 if (!firstObj
->IsFloatable() && firstObj
->GetAttributes().HasFont())
4506 wxRichTextApplyStyle(bulletAttr
, firstObj
->GetAttributes());
4510 // Get line height from first line, if any
4511 wxRichTextLine
* line
= m_cachedLines
.GetFirst() ? (wxRichTextLine
* ) m_cachedLines
.GetFirst()->GetData() : NULL
;
4514 int lineHeight
wxDUMMY_INITIALIZE(0);
4517 lineHeight
= line
->GetSize().y
;
4518 linePos
= line
->GetPosition() + GetPosition();
4523 if (bulletAttr
.HasFont() && GetBuffer())
4524 font
= GetBuffer()->GetFontTable().FindFont(bulletAttr
);
4526 font
= (*wxNORMAL_FONT
);
4528 wxCheckSetFont(dc
, font
);
4530 lineHeight
= dc
.GetCharHeight();
4531 linePos
= GetPosition();
4532 linePos
.y
+= spaceBeforePara
;
4535 wxRect
bulletRect(GetPosition().x
+ leftIndent
, linePos
.y
, linePos
.x
- (GetPosition().x
+ leftIndent
), lineHeight
);
4537 if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
)
4539 if (wxRichTextBuffer::GetRenderer())
4540 wxRichTextBuffer::GetRenderer()->DrawBitmapBullet(this, dc
, bulletAttr
, bulletRect
);
4542 else if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_STANDARD
)
4544 if (wxRichTextBuffer::GetRenderer())
4545 wxRichTextBuffer::GetRenderer()->DrawStandardBullet(this, dc
, bulletAttr
, bulletRect
);
4549 wxString bulletText
= GetBulletText();
4551 if (!bulletText
.empty() && wxRichTextBuffer::GetRenderer())
4552 wxRichTextBuffer::GetRenderer()->DrawTextBullet(this, dc
, bulletAttr
, bulletRect
, bulletText
);
4557 // Draw the range for each line, one object at a time.
4559 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
4562 wxRichTextLine
* line
= node
->GetData();
4563 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
4565 // Lines are specified relative to the paragraph
4567 wxPoint linePosition
= line
->GetPosition() + GetPosition();
4569 // Don't draw if off the screen
4570 if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) != 0) || ((linePosition
.y
+ line
->GetSize().y
) >= rect
.y
&& linePosition
.y
<= rect
.y
+ rect
.height
))
4572 wxPoint objectPosition
= linePosition
;
4573 int maxDescent
= line
->GetDescent();
4575 // Loop through objects until we get to the one within range
4576 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
4581 wxRichTextObject
* child
= node2
->GetData();
4583 if ((!child
->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && child
->GetRange().GetLength() > 0 && !child
->GetRange().IsOutside(lineRange
) && !lineRange
.IsOutside(range
))
4585 // Draw this part of the line at the correct position
4586 wxRichTextRange
objectRange(child
->GetRange());
4587 objectRange
.LimitTo(lineRange
);
4590 if (child
->IsTopLevel())
4592 objectSize
= child
->GetCachedSize();
4593 objectRange
= child
->GetOwnRange();
4597 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING && wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4598 if (i
< (int) line
->GetObjectSizes().GetCount())
4600 objectSize
.x
= line
->GetObjectSizes()[(size_t) i
];
4606 child
->GetRangeSize(objectRange
, objectSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, objectPosition
);
4610 // Use the child object's width, but the whole line's height
4611 wxRect
childRect(objectPosition
, wxSize(objectSize
.x
, line
->GetSize().y
));
4612 child
->Draw(dc
, context
, objectRange
, selection
, childRect
, maxDescent
, style
);
4614 objectPosition
.x
+= objectSize
.x
;
4617 else if (child
->GetRange().GetStart() > lineRange
.GetEnd())
4618 // Can break out of inner loop now since we've passed this line's range
4621 node2
= node2
->GetNext();
4625 node
= node
->GetNext();
4631 // Get the range width using partial extents calculated for the whole paragraph.
4632 static int wxRichTextGetRangeWidth(const wxRichTextParagraph
& para
, const wxRichTextRange
& range
, const wxArrayInt
& partialExtents
)
4634 wxASSERT(partialExtents
.GetCount() >= (size_t) range
.GetLength());
4636 if (partialExtents
.GetCount() < (size_t) range
.GetLength())
4639 int leftMostPos
= 0;
4640 if (range
.GetStart() - para
.GetRange().GetStart() > 0)
4641 leftMostPos
= partialExtents
[range
.GetStart() - para
.GetRange().GetStart() - 1];
4643 int rightMostPos
= partialExtents
[range
.GetEnd() - para
.GetRange().GetStart()];
4645 int w
= rightMostPos
- leftMostPos
;
4650 /// Lay the item out
4651 bool wxRichTextParagraph::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
)
4653 // Deal with floating objects firstly before the normal layout
4654 wxRichTextBuffer
* buffer
= GetBuffer();
4657 wxRichTextFloatCollector
* collector
= GetContainer()->GetFloatCollector();
4659 if (wxRichTextBuffer::GetFloatingLayoutMode())
4661 wxASSERT(collector
!= NULL
);
4663 LayoutFloat(dc
, context
, rect
, parentRect
, style
, collector
);
4666 wxRichTextAttr attr
= GetCombinedAttributes();
4667 context
.ApplyVirtualAttributes(attr
, this);
4671 // Increase the size of the paragraph due to spacing
4672 int spaceBeforePara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingBefore());
4673 int spaceAfterPara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingAfter());
4674 int leftIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftIndent());
4675 int leftSubIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftSubIndent());
4676 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
4678 int lineSpacing
= 0;
4680 // Let's assume line spacing of 10 is normal, 15 is 1.5, 20 is 2, etc.
4681 if (attr
.HasLineSpacing() && attr
.GetLineSpacing() > 0 && attr
.HasFont())
4683 wxFont
font(buffer
->GetFontTable().FindFont(attr
));
4686 wxCheckSetFont(dc
, font
);
4687 lineSpacing
= (int) (double(dc
.GetCharHeight()) * (double(attr
.GetLineSpacing())/10.0 - 1.0));
4691 // Start position for each line relative to the paragraph
4692 int startPositionFirstLine
= leftIndent
;
4693 int startPositionSubsequentLines
= leftIndent
+ leftSubIndent
;
4695 // If we have a bullet in this paragraph, the start position for the first line's text
4696 // is actually leftIndent + leftSubIndent.
4697 if (attr
.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE
)
4698 startPositionFirstLine
= startPositionSubsequentLines
;
4700 long lastEndPos
= GetRange().GetStart()-1;
4701 long lastCompletedEndPos
= lastEndPos
;
4703 int currentWidth
= 0;
4704 SetPosition(rect
.GetPosition());
4706 wxPoint
currentPosition(0, spaceBeforePara
); // We will calculate lines relative to paragraph
4709 int maxHeight
= currentPosition
.y
;
4714 int lineDescent
= 0;
4716 wxRichTextObjectList::compatibility_iterator node
;
4718 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4720 wxArrayInt partialExtents
;
4723 int paraDescent
= 0;
4725 // This calculates the partial text extents
4726 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, rect
.GetPosition(), parentRect
.GetSize(), & partialExtents
);
4728 node
= m_children
.GetFirst();
4731 wxRichTextObject
* child
= node
->GetData();
4733 //child->SetCachedSize(wxDefaultSize);
4734 child
->Layout(dc
, context
, rect
, style
);
4736 node
= node
->GetNext();
4742 // We may need to go back to a previous child, in which case create the new line,
4743 // find the child corresponding to the start position of the string, and
4746 wxRect availableRect
;
4748 node
= m_children
.GetFirst();
4751 wxRichTextObject
* child
= node
->GetData();
4753 // If floating, ignore. We already laid out floats.
4754 // Also ignore if empty object, except if we haven't got any
4756 if ((child
->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode())
4757 || !child
->IsShown() || (child
->GetRange().GetLength() == 0 && maxHeight
> spaceBeforePara
)
4760 node
= node
->GetNext();
4764 // If this is e.g. a composite text box, it will need to be laid out itself.
4765 // But if just a text fragment or image, for example, this will
4766 // do nothing. NB: won't we need to set the position after layout?
4767 // since for example if position is dependent on vertical line size, we
4768 // can't tell the position until the size is determined. So possibly introduce
4769 // another layout phase.
4771 // We may only be looking at part of a child, if we searched back for wrapping
4772 // and found a suitable point some way into the child. So get the size for the fragment
4775 long nextBreakPos
= GetFirstLineBreakPosition(lastEndPos
+1);
4776 long lastPosToUse
= child
->GetRange().GetEnd();
4777 bool lineBreakInThisObject
= (nextBreakPos
> -1 && nextBreakPos
<= child
->GetRange().GetEnd());
4779 if (lineBreakInThisObject
)
4780 lastPosToUse
= nextBreakPos
;
4783 int childDescent
= 0;
4785 int startOffset
= (lineCount
== 0 ? startPositionFirstLine
: startPositionSubsequentLines
);
4786 availableRect
= wxRect(rect
.x
+ startOffset
, rect
.y
+ currentPosition
.y
,
4787 rect
.width
- startOffset
- rightIndent
, rect
.height
);
4789 if (child
->IsTopLevel())
4791 wxSize oldSize
= child
->GetCachedSize();
4793 child
->Invalidate(wxRICHTEXT_ALL
);
4794 child
->SetPosition(wxPoint(0, 0));
4796 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4797 // lays out the object again using the minimum size
4798 // The position will be determined by its location in its line,
4799 // and not by the child's actual position.
4800 child
->LayoutToBestSize(dc
, context
, buffer
,
4801 attr
, child
->GetAttributes(), availableRect
, parentRect
, style
);
4803 if (oldSize
!= child
->GetCachedSize())
4805 partialExtents
.Clear();
4807 // Recalculate the partial text extents since the child object changed size
4808 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), parentRect
.GetSize(), & partialExtents
);
4812 // Problem: we need to layout composites here for which we need the available width,
4813 // but we can't get the available width without using the float collector which
4814 // needs to know the object height.
4816 if ((nextBreakPos
== -1) && (lastEndPos
== child
->GetRange().GetStart() - 1)) // i.e. we want to get the whole thing
4818 childSize
= child
->GetCachedSize();
4819 childDescent
= child
->GetDescent();
4823 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4824 // Get height only, then the width using the partial extents
4825 GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
, wxPoint(0,0), parentRect
.GetSize());
4826 childSize
.x
= wxRichTextGetRangeWidth(*this, wxRichTextRange(lastEndPos
+1, lastPosToUse
), partialExtents
);
4828 GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, rect
.GetPosition(), parentRect
.GetSize());
4833 int loopIterations
= 0;
4835 // If there are nested objects that need to lay themselves out, we have to do this in a
4836 // loop because the height of the object may well depend on the available width.
4837 // And because of floating object positioning, the available width depends on the
4838 // height of the object and whether it will clash with the floating objects.
4839 // So, we see whether the available width changes due to the presence of floating images.
4840 // If it does, then we'll use the new restricted width to find the object height again.
4841 // If this causes another restriction in the available width, we'll try again, until
4842 // either we lose patience or the available width settles down.
4847 wxRect oldAvailableRect
= availableRect
;
4849 // Available width depends on the floating objects and the line height.
4850 // Note: the floating objects may be placed vertically along the two sides of
4851 // buffer, so we may have different available line widths with different
4852 // [startY, endY]. So, we can't determine how wide the available
4853 // space is until we know the exact line height.
4854 if (childDescent
== 0)
4856 lineHeight
= wxMax(lineHeight
, childSize
.y
);
4857 lineDescent
= maxDescent
;
4858 lineAscent
= maxAscent
;
4862 lineDescent
= wxMax(childDescent
, maxDescent
);
4863 lineAscent
= wxMax(childSize
.y
-childDescent
, maxAscent
);
4865 lineHeight
= wxMax(lineHeight
, (lineDescent
+ lineAscent
));
4867 if (wxRichTextBuffer::GetFloatingLayoutMode() && collector
)
4869 wxRect floatAvailableRect
= collector
->GetAvailableRect(rect
.y
+ currentPosition
.y
, rect
.y
+ currentPosition
.y
+ lineHeight
);
4871 // Adjust availableRect to the space that is available when taking floating objects into account.
4873 if (floatAvailableRect
.x
+ startOffset
> availableRect
.x
)
4875 int newX
= floatAvailableRect
.x
+ startOffset
;
4876 int newW
= availableRect
.width
- (newX
- availableRect
.x
);
4877 availableRect
.x
= newX
;
4878 availableRect
.width
= newW
;
4881 if (floatAvailableRect
.width
< availableRect
.width
)
4882 availableRect
.width
= floatAvailableRect
.width
;
4885 currentPosition
.x
= availableRect
.x
- rect
.x
;
4887 if (child
->IsTopLevel() && loopIterations
<= 20)
4889 if (availableRect
!= oldAvailableRect
)
4891 wxSize oldSize
= child
->GetCachedSize();
4893 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4894 // lays out the object again using the minimum size
4895 child
->Invalidate(wxRICHTEXT_ALL
);
4896 child
->LayoutToBestSize(dc
, context
, buffer
,
4897 attr
, child
->GetAttributes(), availableRect
, parentRect
.GetSize(), style
);
4898 childSize
= child
->GetCachedSize();
4899 childDescent
= child
->GetDescent();
4901 if (oldSize
!= child
->GetCachedSize())
4903 partialExtents
.Clear();
4905 // Recalculate the partial text extents since the child object changed size
4906 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), parentRect
.GetSize(), & partialExtents
);
4909 // Go around the loop finding the available rect for the given floating objects
4919 if (child
->IsTopLevel())
4921 // We can move it to the correct position at this point
4922 // TODO: probably need to add margin
4923 child
->Move(GetPosition() + wxPoint(currentWidth
+ (wxMax(leftIndent
, leftIndent
+ leftSubIndent
)), currentPosition
.y
));
4927 // 1) There was a line break BEFORE the natural break
4928 // 2) There was a line break AFTER the natural break
4929 // 3) It's the last line
4930 // 4) The child still fits (carry on) - 'else' clause
4932 if ((lineBreakInThisObject
&& (childSize
.x
+ currentWidth
<= availableRect
.width
))
4934 (childSize
.x
+ currentWidth
> availableRect
.width
)
4937 ((childSize
.x
+ currentWidth
<= availableRect
.width
) && !node
->GetNext())
4941 long wrapPosition
= 0;
4942 if ((childSize
.x
+ currentWidth
<= availableRect
.width
) && !node
->GetNext() && !lineBreakInThisObject
)
4943 wrapPosition
= child
->GetRange().GetEnd();
4946 // Find a place to wrap. This may walk back to previous children,
4947 // for example if a word spans several objects.
4948 // Note: one object must contains only one wxTextAtrr, so the line height will not
4949 // change inside one object. Thus, we can pass the remain line width to the
4950 // FindWrapPosition function.
4951 if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos
+1, child
->GetRange().GetEnd()), dc
, context
, availableRect
.width
, wrapPosition
, & partialExtents
))
4953 // If the function failed, just cut it off at the end of this child.
4954 wrapPosition
= child
->GetRange().GetEnd();
4957 // FindWrapPosition can still return a value that will put us in an endless wrapping loop
4958 if (wrapPosition
<= lastCompletedEndPos
)
4959 wrapPosition
= wxMax(lastCompletedEndPos
+1,child
->GetRange().GetEnd());
4961 // Line end position shouldn't be the same as the end, or greater.
4962 if (wrapPosition
>= GetRange().GetEnd())
4963 wrapPosition
= wxMax(0, GetRange().GetEnd()-1);
4965 // wxLogDebug(wxT("Split at %ld"), wrapPosition);
4967 // Let's find the actual size of the current line now
4969 wxRichTextRange
actualRange(lastCompletedEndPos
+1, wrapPosition
);
4973 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4974 if (!child
->IsEmpty())
4976 // Get height only, then the width using the partial extents
4977 GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
, wxPoint(0,0), parentRect
.GetSize());
4978 actualSize
.x
= wxRichTextGetRangeWidth(*this, actualRange
, partialExtents
);
4982 GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, wxPoint(0,0), parentRect
.GetSize());
4984 currentWidth
= actualSize
.x
;
4986 // The descent for the whole line at this point, is the correct max descent
4987 maxDescent
= childDescent
;
4989 maxAscent
= actualSize
.y
-childDescent
;
4991 // lineHeight is given by the height for the whole line, since it will
4992 // take into account ascend/descend.
4993 lineHeight
= actualSize
.y
;
4995 if (lineHeight
== 0 && buffer
)
4997 wxFont
font(buffer
->GetFontTable().FindFont(attr
));
4998 wxCheckSetFont(dc
, font
);
4999 lineHeight
= dc
.GetCharHeight();
5002 if (maxDescent
== 0)
5005 dc
.GetTextExtent(wxT("X"), & w
, &h
, & maxDescent
);
5009 wxRichTextLine
* line
= AllocateLine(lineCount
);
5011 // Set relative range so we won't have to change line ranges when paragraphs are moved
5012 line
->SetRange(wxRichTextRange(actualRange
.GetStart() - GetRange().GetStart(), actualRange
.GetEnd() - GetRange().GetStart()));
5013 line
->SetPosition(currentPosition
);
5014 line
->SetSize(wxSize(currentWidth
, lineHeight
));
5015 line
->SetDescent(maxDescent
);
5017 maxHeight
= currentPosition
.y
+ lineHeight
;
5019 // Now move down a line. TODO: add margins, spacing
5020 currentPosition
.y
+= lineHeight
;
5021 currentPosition
.y
+= lineSpacing
;
5024 maxWidth
= wxMax(maxWidth
, currentWidth
+startOffset
);
5029 // TODO: account for zero-length objects
5030 // wxASSERT(wrapPosition > lastCompletedEndPos);
5032 lastEndPos
= wrapPosition
;
5033 lastCompletedEndPos
= lastEndPos
;
5037 if (wrapPosition
< GetRange().GetEnd()-1)
5039 // May need to set the node back to a previous one, due to searching back in wrapping
5040 wxRichTextObject
* childAfterWrapPosition
= FindObjectAtPosition(wrapPosition
+1);
5041 if (childAfterWrapPosition
)
5042 node
= m_children
.Find(childAfterWrapPosition
);
5044 node
= node
->GetNext();
5047 node
= node
->GetNext();
5049 // Apply paragraph styles such as alignment to the wrapped line
5050 ApplyParagraphStyle(line
, attr
, availableRect
, dc
);
5054 // We still fit, so don't add a line, and keep going
5055 currentWidth
+= childSize
.x
;
5057 if (childDescent
== 0)
5059 // An object with a zero descend value wants to take up the whole
5060 // height regardless of baseline
5061 lineHeight
= wxMax(lineHeight
, childSize
.y
);
5065 maxDescent
= wxMax(childDescent
, maxDescent
);
5066 maxAscent
= wxMax(childSize
.y
-childDescent
, maxAscent
);
5069 lineHeight
= wxMax(lineHeight
, (maxDescent
+ maxAscent
));
5071 maxWidth
= wxMax(maxWidth
, currentWidth
+startOffset
);
5072 lastEndPos
= child
->GetRange().GetEnd();
5074 node
= node
->GetNext();
5078 //wxASSERT(!(lastCompletedEndPos != -1 && lastCompletedEndPos < GetRange().GetEnd()-1));
5080 // Add the last line - it's the current pos -> last para pos
5081 // Substract -1 because the last position is always the end-paragraph position.
5082 if (lastCompletedEndPos
<= GetRange().GetEnd()-1)
5084 currentPosition
.x
= (lineCount
== 0 ? startPositionFirstLine
: startPositionSubsequentLines
);
5086 wxRichTextLine
* line
= AllocateLine(lineCount
);
5088 wxRichTextRange
actualRange(lastCompletedEndPos
+1, GetRange().GetEnd()-1);
5090 // Set relative range so we won't have to change line ranges when paragraphs are moved
5091 line
->SetRange(wxRichTextRange(actualRange
.GetStart() - GetRange().GetStart(), actualRange
.GetEnd() - GetRange().GetStart()));
5093 line
->SetPosition(currentPosition
);
5095 if (lineHeight
== 0 && buffer
)
5097 wxFont
font(buffer
->GetFontTable().FindFont(attr
));
5098 wxCheckSetFont(dc
, font
);
5099 lineHeight
= dc
.GetCharHeight();
5102 if (maxDescent
== 0)
5105 dc
.GetTextExtent(wxT("X"), & w
, &h
, & maxDescent
);
5108 line
->SetSize(wxSize(currentWidth
, lineHeight
));
5109 line
->SetDescent(maxDescent
);
5110 currentPosition
.y
+= lineHeight
;
5111 currentPosition
.y
+= lineSpacing
;
5115 // Remove remaining unused line objects, if any
5116 ClearUnusedLines(lineCount
);
5118 // We need to add back the margins etc.
5120 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
5121 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxWidth
, currentPosition
.y
+ spaceAfterPara
));
5122 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
5123 SetCachedSize(marginRect
.GetSize());
5126 // The maximum size is the length of the paragraph stretched out into a line.
5127 // So if there were a single word, or an image, or a fixed-size text box, the object could be shrunk around
5128 // this size. TODO: take into account line breaks.
5130 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
5131 contentRect
= wxRect(wxPoint(0, 0), wxSize(paraSize
.x
+ wxMax(leftIndent
, leftIndent
+ leftSubIndent
) + rightIndent
, currentPosition
.y
+ spaceAfterPara
));
5132 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
5133 SetMaxSize(marginRect
.GetSize());
5136 // Find the greatest minimum size. Currently we only look at non-text objects,
5137 // which isn't ideal but it would be slow to find the maximum word width to
5138 // use as the minimum.
5141 node
= m_children
.GetFirst();
5144 wxRichTextObject
* child
= node
->GetData();
5146 // If floating, ignore. We already laid out floats.
5147 // Also ignore if empty object, except if we haven't got any
5149 if ((!child
->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && child
->GetRange().GetLength() != 0 && !wxDynamicCast(child
, wxRichTextPlainText
))
5151 if (child
->GetCachedSize().x
> minWidth
)
5152 minWidth
= child
->GetMinSize().x
;
5154 node
= node
->GetNext();
5157 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
5158 contentRect
= wxRect(wxPoint(0, 0), wxSize(minWidth
, currentPosition
.y
+ spaceAfterPara
));
5159 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
5160 SetMinSize(marginRect
.GetSize());
5163 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5164 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
5165 // Use the text extents to calculate the size of each fragment in each line
5166 wxRichTextLineList::compatibility_iterator lineNode
= m_cachedLines
.GetFirst();
5169 wxRichTextLine
* line
= lineNode
->GetData();
5170 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5172 // Loop through objects until we get to the one within range
5173 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
5177 wxRichTextObject
* child
= node2
->GetData();
5179 if (child
->GetRange().GetLength() > 0 && !child
->GetRange().IsOutside(lineRange
))
5181 wxRichTextRange rangeToUse
= lineRange
;
5182 rangeToUse
.LimitTo(child
->GetRange());
5184 // Find the size of the child from the text extents, and store in an array
5185 // for drawing later
5187 if (rangeToUse
.GetStart() > GetRange().GetStart())
5188 left
= partialExtents
[(rangeToUse
.GetStart()-1) - GetRange().GetStart()];
5189 int right
= partialExtents
[rangeToUse
.GetEnd() - GetRange().GetStart()];
5190 int sz
= right
- left
;
5191 line
->GetObjectSizes().Add(sz
);
5193 else if (child
->GetRange().GetStart() > lineRange
.GetEnd())
5194 // Can break out of inner loop now since we've passed this line's range
5197 node2
= node2
->GetNext();
5200 lineNode
= lineNode
->GetNext();
5208 /// Apply paragraph styles, such as centering, to wrapped lines
5209 /// TODO: take into account box attributes, possibly
5210 void wxRichTextParagraph::ApplyParagraphStyle(wxRichTextLine
* line
, const wxRichTextAttr
& attr
, const wxRect
& rect
, wxDC
& dc
)
5212 if (!attr
.HasAlignment())
5215 wxPoint pos
= line
->GetPosition();
5216 wxPoint originalPos
= pos
;
5217 wxSize size
= line
->GetSize();
5219 // centering, right-justification
5220 if (attr
.HasAlignment() && attr
.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE
)
5222 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
5223 pos
.x
= (rect
.GetWidth() - rightIndent
- size
.x
)/2 + pos
.x
;
5224 line
->SetPosition(pos
);
5226 else if (attr
.HasAlignment() && attr
.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT
)
5228 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
5229 pos
.x
= pos
.x
+ rect
.GetWidth() - size
.x
- rightIndent
;
5230 line
->SetPosition(pos
);
5233 if (pos
!= originalPos
)
5235 wxPoint inc
= pos
- originalPos
;
5237 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5241 wxRichTextObject
* child
= node
->GetData();
5242 if (child
->IsTopLevel() && !child
->GetRange().IsOutside(line
->GetAbsoluteRange()))
5243 child
->Move(child
->GetPosition() + inc
);
5245 node
= node
->GetNext();
5250 /// Insert text at the given position
5251 bool wxRichTextParagraph::InsertText(long pos
, const wxString
& text
)
5253 wxRichTextObject
* childToUse
= NULL
;
5254 wxRichTextObjectList::compatibility_iterator nodeToUse
= wxRichTextObjectList::compatibility_iterator();
5256 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5259 wxRichTextObject
* child
= node
->GetData();
5260 if (child
->GetRange().Contains(pos
) && child
->GetRange().GetLength() > 0)
5267 node
= node
->GetNext();
5272 wxRichTextPlainText
* textObject
= wxDynamicCast(childToUse
, wxRichTextPlainText
);
5275 int posInString
= pos
- textObject
->GetRange().GetStart();
5277 wxString newText
= textObject
->GetText().Mid(0, posInString
) +
5278 text
+ textObject
->GetText().Mid(posInString
);
5279 textObject
->SetText(newText
);
5281 int textLength
= text
.length();
5283 textObject
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart(),
5284 textObject
->GetRange().GetEnd() + textLength
));
5286 // Increment the end range of subsequent fragments in this paragraph.
5287 // We'll set the paragraph range itself at a higher level.
5289 wxRichTextObjectList::compatibility_iterator node
= nodeToUse
->GetNext();
5292 wxRichTextObject
* child
= node
->GetData();
5293 child
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart() + textLength
,
5294 textObject
->GetRange().GetEnd() + textLength
));
5296 node
= node
->GetNext();
5303 // TODO: if not a text object, insert at closest position, e.g. in front of it
5309 // Don't pass parent initially to suppress auto-setting of parent range.
5310 // We'll do that at a higher level.
5311 wxRichTextPlainText
* textObject
= new wxRichTextPlainText(text
, this);
5313 AppendChild(textObject
);
5320 void wxRichTextParagraph::Copy(const wxRichTextParagraph
& obj
)
5322 wxRichTextCompositeObject::Copy(obj
);
5325 /// Clear the cached lines
5326 void wxRichTextParagraph::ClearLines()
5328 WX_CLEAR_LIST(wxRichTextLineList
, m_cachedLines
);
5331 /// Get/set the object size for the given range. Returns false if the range
5332 /// is invalid for this object.
5333 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
5335 if (!range
.IsWithin(GetRange()))
5338 if (flags
& wxRICHTEXT_UNFORMATTED
)
5340 // Just use unformatted data, assume no line breaks
5343 wxArrayInt childExtents
;
5352 int maxLineHeight
= 0;
5354 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5357 wxRichTextObject
* child
= node
->GetData();
5358 if (!child
->GetRange().IsOutside(range
))
5360 // Floating objects have a zero size within the paragraph.
5361 if (child
->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode())
5366 if (partialExtents
->GetCount() > 0)
5367 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5371 partialExtents
->Add(0 /* zero size */ + lastSize
);
5378 wxRichTextRange rangeToUse
= range
;
5379 rangeToUse
.LimitTo(child
->GetRange());
5380 int childDescent
= 0;
5382 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we've already cached the size,
5383 // but it's only going to be used after caching has taken place.
5384 if ((flags
& wxRICHTEXT_HEIGHT_ONLY
) && child
->GetCachedSize().y
!= 0)
5386 childDescent
= child
->GetDescent();
5387 childSize
= child
->GetCachedSize();
5389 if (childDescent
== 0)
5391 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5395 maxDescent
= wxMax(maxDescent
, childDescent
);
5396 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5399 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5401 sz
.y
= wxMax(sz
.y
, maxLineHeight
);
5402 sz
.x
+= childSize
.x
;
5403 descent
= maxDescent
;
5405 else if (child
->IsTopLevel())
5407 childDescent
= child
->GetDescent();
5408 childSize
= child
->GetCachedSize();
5410 if (childDescent
== 0)
5412 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5416 maxDescent
= wxMax(maxDescent
, childDescent
);
5417 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5420 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5422 sz
.y
= wxMax(sz
.y
, maxLineHeight
);
5423 sz
.x
+= childSize
.x
;
5424 descent
= maxDescent
;
5426 // FIXME: this won't change the original values.
5427 // Should we be calling GetRangeSize above instead of using cached values?
5429 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange()))
5431 child
->SetCachedSize(childSize
);
5432 child
->SetDescent(childDescent
);
5439 if (partialExtents
->GetCount() > 0)
5440 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5444 partialExtents
->Add(childSize
.x
+ lastSize
);
5447 else if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, context
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
), parentSize
, p
))
5449 if (childDescent
== 0)
5451 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5455 maxDescent
= wxMax(maxDescent
, childDescent
);
5456 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5459 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5461 sz
.y
= wxMax(sz
.y
, maxLineHeight
);
5462 sz
.x
+= childSize
.x
;
5463 descent
= maxDescent
;
5465 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange()))
5467 child
->SetCachedSize(childSize
);
5468 child
->SetDescent(childDescent
);
5474 if (partialExtents
->GetCount() > 0)
5475 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5480 for (i
= 0; i
< childExtents
.GetCount(); i
++)
5482 partialExtents
->Add(childExtents
[i
] + lastSize
);
5492 node
= node
->GetNext();
5498 // Use formatted data, with line breaks
5501 // We're going to loop through each line, and then for each line,
5502 // call GetRangeSize for the fragment that comprises that line.
5503 // Only we have to do that multiple times within the line, because
5504 // the line may be broken into pieces. For now ignore line break commands
5505 // (so we can assume that getting the unformatted size for a fragment
5506 // within a line is the actual size)
5508 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5511 wxRichTextLine
* line
= node
->GetData();
5512 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5513 if (!lineRange
.IsOutside(range
))
5517 int maxLineHeight
= 0;
5518 int maxLineWidth
= 0;
5520 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
5523 wxRichTextObject
* child
= node2
->GetData();
5525 if ((!child
->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && !child
->GetRange().IsOutside(lineRange
))
5527 wxRichTextRange rangeToUse
= lineRange
;
5528 rangeToUse
.LimitTo(child
->GetRange());
5529 if (child
->IsTopLevel())
5530 rangeToUse
= child
->GetOwnRange();
5533 int childDescent
= 0;
5534 if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, context
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
), parentSize
))
5536 if (childDescent
== 0)
5538 // Assume that if descent is zero, this child can occupy the full line height
5539 // and does not need space for the line's maximum descent. So we influence
5540 // the overall max line height only.
5541 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5545 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5546 maxDescent
= wxMax(maxAscent
, childDescent
);
5548 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5549 maxLineWidth
+= childSize
.x
;
5553 node2
= node2
->GetNext();
5556 descent
= wxMax(descent
, maxDescent
);
5558 // Increase size by a line (TODO: paragraph spacing)
5559 sz
.y
+= maxLineHeight
;
5560 sz
.x
= wxMax(sz
.x
, maxLineWidth
);
5562 node
= node
->GetNext();
5569 /// Finds the absolute position and row height for the given character position
5570 bool wxRichTextParagraph::FindPosition(wxDC
& dc
, wxRichTextDrawingContext
& context
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
5574 wxRichTextLine
* line
= ((wxRichTextParagraphLayoutBox
*)GetParent())->GetLineAtPosition(0);
5576 *height
= line
->GetSize().y
;
5578 *height
= dc
.GetCharHeight();
5580 // -1 means 'the start of the buffer'.
5583 pt
= pt
+ line
->GetPosition();
5588 // The final position in a paragraph is taken to mean the position
5589 // at the start of the next paragraph.
5590 if (index
== GetRange().GetEnd())
5592 wxRichTextParagraphLayoutBox
* parent
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
5593 wxASSERT( parent
!= NULL
);
5595 // Find the height at the next paragraph, if any
5596 wxRichTextLine
* line
= parent
->GetLineAtPosition(index
+ 1);
5599 *height
= line
->GetSize().y
;
5600 pt
= line
->GetAbsolutePosition();
5604 *height
= dc
.GetCharHeight();
5605 int indent
= ConvertTenthsMMToPixels(dc
, m_attributes
.GetLeftIndent());
5606 pt
= wxPoint(indent
, GetCachedSize().y
);
5612 if (index
< GetRange().GetStart() || index
> GetRange().GetEnd())
5615 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5618 wxRichTextLine
* line
= node
->GetData();
5619 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5620 if (index
>= lineRange
.GetStart() && index
<= lineRange
.GetEnd())
5622 // If this is the last point in the line, and we're forcing the
5623 // returned value to be the start of the next line, do the required
5625 if (index
== lineRange
.GetEnd() && forceLineStart
)
5627 if (node
->GetNext())
5629 wxRichTextLine
* nextLine
= node
->GetNext()->GetData();
5630 *height
= nextLine
->GetSize().y
;
5631 pt
= nextLine
->GetAbsolutePosition();
5636 pt
.y
= line
->GetPosition().y
+ GetPosition().y
;
5638 wxRichTextRange
r(lineRange
.GetStart(), index
);
5642 // We find the size of the line up to this point,
5643 // then we can add this size to the line start position and
5644 // paragraph start position to find the actual position.
5646 if (GetRangeSize(r
, rangeSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, line
->GetPosition()+ GetPosition()))
5648 pt
.x
= line
->GetPosition().x
+ GetPosition().x
+ rangeSize
.x
;
5649 *height
= line
->GetSize().y
;
5656 node
= node
->GetNext();
5662 /// Hit-testing: returns a flag indicating hit test details, plus
5663 /// information about position
5664 int wxRichTextParagraph::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
5667 return wxRICHTEXT_HITTEST_NONE
;
5669 // If we're in the top-level container, then we can return
5670 // a suitable hit test code even if the point is outside the container area,
5671 // so that we can position the caret sensibly even if we don't
5672 // click on valid content. If we're not at the top-level, and the point
5673 // is not within this paragraph object, then we don't want to stop more
5674 // precise hit-testing from working prematurely, so return immediately.
5675 // NEW STRATEGY: use the parent boundary to test whether we're in the
5676 // right region, not the paragraph, since the paragraph may be positioned
5677 // some way in from where the user clicks.
5680 wxRichTextObject
* tempObj
, *tempContextObj
;
5681 if (GetParent() && GetParent()->wxRichTextObject::HitTest(dc
, context
, pt
, tmpPos
, & tempObj
, & tempContextObj
, flags
) == wxRICHTEXT_HITTEST_NONE
)
5682 return wxRICHTEXT_HITTEST_NONE
;
5685 wxRichTextObjectList::compatibility_iterator objNode
= m_children
.GetFirst();
5688 wxRichTextObject
* child
= objNode
->GetData();
5689 // Don't recurse if we have wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS,
5690 // and also, if this seems composite but actually is marked as atomic,
5692 if (child
->IsTopLevel() && ((flags
& wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS
) == 0) &&
5693 (! (((flags
& wxRICHTEXT_HITTEST_HONOUR_ATOMIC
) != 0) && child
->IsAtomic())))
5696 int hitTest
= child
->HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
);
5697 if (hitTest
!= wxRICHTEXT_HITTEST_NONE
)
5702 objNode
= objNode
->GetNext();
5705 wxPoint paraPos
= GetPosition();
5707 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5710 wxRichTextLine
* line
= node
->GetData();
5711 wxPoint linePos
= paraPos
+ line
->GetPosition();
5712 wxSize lineSize
= line
->GetSize();
5713 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5715 if (pt
.y
<= linePos
.y
+ lineSize
.y
)
5717 if (pt
.x
< linePos
.x
)
5719 textPosition
= lineRange
.GetStart();
5720 *obj
= FindObjectAtPosition(textPosition
);
5721 *contextObj
= GetContainer();
5722 return wxRICHTEXT_HITTEST_BEFORE
|wxRICHTEXT_HITTEST_OUTSIDE
;
5724 else if (pt
.x
>= (linePos
.x
+ lineSize
.x
))
5726 textPosition
= lineRange
.GetEnd();
5727 *obj
= FindObjectAtPosition(textPosition
);
5728 *contextObj
= GetContainer();
5729 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
;
5733 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5734 wxArrayInt partialExtents
;
5739 // This calculates the partial text extents
5740 GetRangeSize(lineRange
, paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, linePos
, wxDefaultSize
, & partialExtents
);
5742 int lastX
= linePos
.x
;
5744 for (i
= 0; i
< partialExtents
.GetCount(); i
++)
5746 int nextX
= partialExtents
[i
] + linePos
.x
;
5748 if (pt
.x
>= lastX
&& pt
.x
<= nextX
)
5750 textPosition
= i
+ lineRange
.GetStart(); // minus 1?
5752 *obj
= FindObjectAtPosition(textPosition
);
5753 *contextObj
= GetContainer();
5755 // So now we know it's between i-1 and i.
5756 // Let's see if we can be more precise about
5757 // which side of the position it's on.
5759 int midPoint
= (nextX
+ lastX
)/2;
5760 if (pt
.x
>= midPoint
)
5761 return wxRICHTEXT_HITTEST_AFTER
;
5763 return wxRICHTEXT_HITTEST_BEFORE
;
5770 int lastX
= linePos
.x
;
5771 for (i
= lineRange
.GetStart(); i
<= lineRange
.GetEnd(); i
++)
5776 wxRichTextRange
rangeToUse(lineRange
.GetStart(), i
);
5778 GetRangeSize(rangeToUse
, childSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, linePos
);
5780 int nextX
= childSize
.x
+ linePos
.x
;
5782 if (pt
.x
>= lastX
&& pt
.x
<= nextX
)
5786 *obj
= FindObjectAtPosition(textPosition
);
5787 *contextObj
= GetContainer();
5789 // So now we know it's between i-1 and i.
5790 // Let's see if we can be more precise about
5791 // which side of the position it's on.
5793 int midPoint
= (nextX
+ lastX
)/2;
5794 if (pt
.x
>= midPoint
)
5795 return wxRICHTEXT_HITTEST_AFTER
;
5797 return wxRICHTEXT_HITTEST_BEFORE
;
5808 node
= node
->GetNext();
5811 return wxRICHTEXT_HITTEST_NONE
;
5814 /// Split an object at this position if necessary, and return
5815 /// the previous object, or NULL if inserting at beginning.
5816 wxRichTextObject
* wxRichTextParagraph::SplitAt(long pos
, wxRichTextObject
** previousObject
)
5818 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5821 wxRichTextObject
* child
= node
->GetData();
5823 if (pos
== child
->GetRange().GetStart())
5827 if (node
->GetPrevious())
5828 *previousObject
= node
->GetPrevious()->GetData();
5830 *previousObject
= NULL
;
5836 if (child
->GetRange().Contains(pos
))
5838 // This should create a new object, transferring part of
5839 // the content to the old object and the rest to the new object.
5840 wxRichTextObject
* newObject
= child
->DoSplit(pos
);
5842 // If we couldn't split this object, just insert in front of it.
5845 // Maybe this is an empty string, try the next one
5850 // Insert the new object after 'child'
5851 if (node
->GetNext())
5852 m_children
.Insert(node
->GetNext(), newObject
);
5854 m_children
.Append(newObject
);
5855 newObject
->SetParent(this);
5858 *previousObject
= child
;
5864 node
= node
->GetNext();
5867 *previousObject
= NULL
;
5871 /// Move content to a list from obj on
5872 void wxRichTextParagraph::MoveToList(wxRichTextObject
* obj
, wxList
& list
)
5874 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(obj
);
5877 wxRichTextObject
* child
= node
->GetData();
5880 wxRichTextObjectList::compatibility_iterator oldNode
= node
;
5882 node
= node
->GetNext();
5884 m_children
.DeleteNode(oldNode
);
5888 /// Add content back from list
5889 void wxRichTextParagraph::MoveFromList(wxList
& list
)
5891 for (wxList::compatibility_iterator node
= list
.GetFirst(); node
; node
= node
->GetNext())
5893 AppendChild((wxRichTextObject
*) node
->GetData());
5898 void wxRichTextParagraph::CalculateRange(long start
, long& end
)
5900 wxRichTextCompositeObject::CalculateRange(start
, end
);
5902 // Add one for end of paragraph
5905 m_range
.SetRange(start
, end
);
5908 /// Find the object at the given position
5909 wxRichTextObject
* wxRichTextParagraph::FindObjectAtPosition(long position
)
5911 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5914 wxRichTextObject
* obj
= node
->GetData();
5915 if (obj
->GetRange().Contains(position
) ||
5916 obj
->GetRange().GetStart() == position
||
5917 obj
->GetRange().GetEnd() == position
)
5920 node
= node
->GetNext();
5925 /// Get the plain text searching from the start or end of the range.
5926 /// The resulting string may be shorter than the range given.
5927 bool wxRichTextParagraph::GetContiguousPlainText(wxString
& text
, const wxRichTextRange
& range
, bool fromStart
)
5929 text
= wxEmptyString
;
5933 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5936 wxRichTextObject
* obj
= node
->GetData();
5937 if (!obj
->GetRange().IsOutside(range
))
5939 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5942 text
+= textObj
->GetTextForRange(range
);
5950 node
= node
->GetNext();
5955 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetLast();
5958 wxRichTextObject
* obj
= node
->GetData();
5959 if (!obj
->GetRange().IsOutside(range
))
5961 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5964 text
= textObj
->GetTextForRange(range
) + text
;
5968 text
= wxT(" ") + text
;
5972 node
= node
->GetPrevious();
5979 /// Find a suitable wrap position.
5980 bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange
& range
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int availableSpace
, long& wrapPosition
, wxArrayInt
* partialExtents
)
5982 if (range
.GetLength() <= 0)
5985 // Find the first position where the line exceeds the available space.
5987 long breakPosition
= range
.GetEnd();
5989 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5990 if (partialExtents
&& partialExtents
->GetCount() >= (size_t) (GetRange().GetLength()-1)) // the final position in a paragraph is the newline
5994 if (range
.GetStart() > GetRange().GetStart())
5995 widthBefore
= (*partialExtents
)[range
.GetStart() - GetRange().GetStart() - 1];
6000 for (i
= (size_t) range
.GetStart(); i
<= (size_t) range
.GetEnd(); i
++)
6002 int widthFromStartOfThisRange
= (*partialExtents
)[i
- GetRange().GetStart()] - widthBefore
;
6004 if (widthFromStartOfThisRange
> availableSpace
)
6006 breakPosition
= i
-1;
6014 // Binary chop for speed
6015 long minPos
= range
.GetStart();
6016 long maxPos
= range
.GetEnd();
6019 if (minPos
== maxPos
)
6022 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
6024 if (sz
.x
> availableSpace
)
6025 breakPosition
= minPos
- 1;
6028 else if ((maxPos
- minPos
) == 1)
6031 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
6033 if (sz
.x
> availableSpace
)
6034 breakPosition
= minPos
- 1;
6037 GetRangeSize(wxRichTextRange(range
.GetStart(), maxPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
6038 if (sz
.x
> availableSpace
)
6039 breakPosition
= maxPos
-1;
6045 long nextPos
= minPos
+ ((maxPos
- minPos
) / 2);
6048 GetRangeSize(wxRichTextRange(range
.GetStart(), nextPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
6050 if (sz
.x
> availableSpace
)
6062 // Now we know the last position on the line.
6063 // Let's try to find a word break.
6066 if (GetContiguousPlainText(plainText
, wxRichTextRange(range
.GetStart(), breakPosition
), false))
6068 int newLinePos
= plainText
.Find(wxRichTextLineBreakChar
);
6069 if (newLinePos
!= wxNOT_FOUND
)
6071 breakPosition
= wxMax(0, range
.GetStart() + newLinePos
);
6075 int spacePos
= plainText
.Find(wxT(' '), true);
6076 int tabPos
= plainText
.Find(wxT('\t'), true);
6077 int pos
= wxMax(spacePos
, tabPos
);
6078 if (pos
!= wxNOT_FOUND
)
6080 int positionsFromEndOfString
= plainText
.length() - pos
- 1;
6081 breakPosition
= breakPosition
- positionsFromEndOfString
;
6086 wrapPosition
= breakPosition
;
6091 /// Get the bullet text for this paragraph.
6092 wxString
wxRichTextParagraph::GetBulletText()
6094 if (GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
||
6095 (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
))
6096 return wxEmptyString
;
6098 int number
= GetAttributes().GetBulletNumber();
6101 if ((GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ARABIC
) || (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
))
6103 text
.Printf(wxT("%d"), number
);
6105 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_UPPER
)
6107 // TODO: Unicode, and also check if number > 26
6108 text
.Printf(wxT("%c"), (wxChar
) (number
+64));
6110 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_LOWER
)
6112 // TODO: Unicode, and also check if number > 26
6113 text
.Printf(wxT("%c"), (wxChar
) (number
+96));
6115 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER
)
6117 text
= wxRichTextDecimalToRoman(number
);
6119 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER
)
6121 text
= wxRichTextDecimalToRoman(number
);
6124 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
)
6126 text
= GetAttributes().GetBulletText();
6129 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
)
6131 // The outline style relies on the text being computed statically,
6132 // since it depends on other levels points (e.g. 1.2.1.1). So normally the bullet text
6133 // should be stored in the attributes; if not, just use the number for this
6134 // level, as previously computed.
6135 if (!GetAttributes().GetBulletText().IsEmpty())
6136 text
= GetAttributes().GetBulletText();
6139 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PARENTHESES
)
6141 text
= wxT("(") + text
+ wxT(")");
6143 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_RIGHT_PARENTHESIS
)
6145 text
= text
+ wxT(")");
6148 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PERIOD
)
6156 /// Allocate or reuse a line object
6157 wxRichTextLine
* wxRichTextParagraph::AllocateLine(int pos
)
6159 if (pos
< (int) m_cachedLines
.GetCount())
6161 wxRichTextLine
* line
= m_cachedLines
.Item(pos
)->GetData();
6167 wxRichTextLine
* line
= new wxRichTextLine(this);
6168 m_cachedLines
.Append(line
);
6173 /// Clear remaining unused line objects, if any
6174 bool wxRichTextParagraph::ClearUnusedLines(int lineCount
)
6176 int cachedLineCount
= m_cachedLines
.GetCount();
6177 if ((int) cachedLineCount
> lineCount
)
6179 for (int i
= 0; i
< (int) (cachedLineCount
- lineCount
); i
++)
6181 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetLast();
6182 wxRichTextLine
* line
= node
->GetData();
6183 m_cachedLines
.Erase(node
);
6190 /// Get combined attributes of the base style, paragraph style and character style. We use this to dynamically
6191 /// retrieve the actual style.
6192 wxRichTextAttr
wxRichTextParagraph::GetCombinedAttributes(const wxRichTextAttr
& contentStyle
, bool includingBoxAttr
) const
6194 wxRichTextAttr attr
;
6195 wxRichTextParagraphLayoutBox
* buf
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
6198 attr
= buf
->GetBasicStyle();
6199 if (!includingBoxAttr
)
6201 attr
.GetTextBoxAttr().Reset();
6202 // The background colour will be painted by the container, and we don't
6203 // want to unnecessarily overwrite the background when we're drawing text
6204 // because this may erase the guideline (which appears just under the text
6205 // if there's no padding).
6206 attr
.SetFlags(attr
.GetFlags() & ~wxTEXT_ATTR_BACKGROUND_COLOUR
);
6208 wxRichTextApplyStyle(attr
, GetAttributes());
6211 attr
= GetAttributes();
6213 wxRichTextApplyStyle(attr
, contentStyle
);
6217 /// Get combined attributes of the base style and paragraph style.
6218 wxRichTextAttr
wxRichTextParagraph::GetCombinedAttributes(bool includingBoxAttr
) const
6220 wxRichTextAttr attr
;
6221 wxRichTextParagraphLayoutBox
* buf
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
6224 attr
= buf
->GetBasicStyle();
6225 if (!includingBoxAttr
)
6226 attr
.GetTextBoxAttr().Reset();
6227 wxRichTextApplyStyle(attr
, GetAttributes());
6230 attr
= GetAttributes();
6235 // Create default tabstop array
6236 void wxRichTextParagraph::InitDefaultTabs()
6238 // create a default tab list at 10 mm each.
6239 for (int i
= 0; i
< 20; ++i
)
6241 sm_defaultTabs
.Add(i
*100);
6245 // Clear default tabstop array
6246 void wxRichTextParagraph::ClearDefaultTabs()
6248 sm_defaultTabs
.Clear();
6251 void wxRichTextParagraph::LayoutFloat(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
, wxRichTextFloatCollector
* floatCollector
)
6253 wxRichTextObjectList::compatibility_iterator node
= GetChildren().GetFirst();
6256 wxRichTextObject
* anchored
= node
->GetData();
6257 if (anchored
&& anchored
->IsFloating() && !floatCollector
->HasFloat(anchored
))
6260 wxRichTextAttr
parentAttr(GetAttributes());
6261 context
.ApplyVirtualAttributes(parentAttr
, this);
6264 wxRect availableSpace
= GetParent()->GetAvailableContentArea(dc
, context
, rect
);
6266 anchored
->LayoutToBestSize(dc
, context
, GetBuffer(),
6267 parentAttr
, anchored
->GetAttributes(),
6268 parentRect
, availableSpace
,
6270 wxSize size
= anchored
->GetCachedSize();
6274 anchored
->GetRangeSize(anchored
->GetRange(), size
, descent
, dc
, context
, style
);
6278 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().IsValid())
6280 offsetY
= anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetValue();
6281 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
6283 offsetY
= ConvertTenthsMMToPixels(dc
, offsetY
);
6287 int pos
= floatCollector
->GetFitPosition(anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode(), rect
.y
+ offsetY
, size
.y
);
6289 /* Update the offset */
6290 int newOffsetY
= pos
- rect
.y
;
6291 if (newOffsetY
!= offsetY
)
6293 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
6294 newOffsetY
= ConvertPixelsToTenthsMM(dc
, newOffsetY
);
6295 anchored
->GetAttributes().GetTextBoxAttr().GetTop().SetValue(newOffsetY
);
6298 if (anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT
)
6300 else if (anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT
)
6301 x
= rect
.x
+ rect
.width
- size
.x
;
6303 //anchored->SetPosition(wxPoint(x, pos));
6304 anchored
->Move(wxPoint(x
, pos
)); // should move children
6305 anchored
->SetCachedSize(size
);
6306 floatCollector
->CollectFloat(this, anchored
);
6309 node
= node
->GetNext();
6313 // Get the first position from pos that has a line break character.
6314 long wxRichTextParagraph::GetFirstLineBreakPosition(long pos
)
6316 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
6319 wxRichTextObject
* obj
= node
->GetData();
6320 if (pos
>= obj
->GetRange().GetStart() && pos
<= obj
->GetRange().GetEnd())
6322 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
6325 long breakPos
= textObj
->GetFirstLineBreakPosition(pos
);
6330 node
= node
->GetNext();
6337 * This object represents a line in a paragraph, and stores
6338 * offsets from the start of the paragraph representing the
6339 * start and end positions of the line.
6342 wxRichTextLine::wxRichTextLine(wxRichTextParagraph
* parent
)
6348 void wxRichTextLine::Init(wxRichTextParagraph
* parent
)
6351 m_range
.SetRange(-1, -1);
6352 m_pos
= wxPoint(0, 0);
6353 m_size
= wxSize(0, 0);
6355 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6356 m_objectSizes
.Clear();
6361 void wxRichTextLine::Copy(const wxRichTextLine
& obj
)
6363 m_range
= obj
.m_range
;
6364 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6365 m_objectSizes
= obj
.m_objectSizes
;
6369 /// Get the absolute object position
6370 wxPoint
wxRichTextLine::GetAbsolutePosition() const
6372 return m_parent
->GetPosition() + m_pos
;
6375 /// Get the absolute range
6376 wxRichTextRange
wxRichTextLine::GetAbsoluteRange() const
6378 wxRichTextRange
range(m_range
.GetStart() + m_parent
->GetRange().GetStart(), 0);
6379 range
.SetEnd(range
.GetStart() + m_range
.GetLength()-1);
6384 * wxRichTextPlainText
6385 * This object represents a single piece of text.
6388 IMPLEMENT_DYNAMIC_CLASS(wxRichTextPlainText
, wxRichTextObject
)
6390 wxRichTextPlainText::wxRichTextPlainText(const wxString
& text
, wxRichTextObject
* parent
, wxRichTextAttr
* style
):
6391 wxRichTextObject(parent
)
6394 SetAttributes(*style
);
6399 #define USE_KERNING_FIX 1
6401 // If insufficient tabs are defined, this is the tab width used
6402 #define WIDTH_FOR_DEFAULT_TABS 50
6405 bool wxRichTextPlainText::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int WXUNUSED(style
))
6407 wxRichTextParagraph
* para
= wxDynamicCast(GetParent(), wxRichTextParagraph
);
6408 wxASSERT (para
!= NULL
);
6410 wxRichTextAttr
textAttr(para
? para
->GetCombinedAttributes(GetAttributes(), false /* no box attributes */) : GetAttributes());
6411 context
.ApplyVirtualAttributes(textAttr
, this);
6413 // Let's make the assumption for now that for content in a paragraph, including
6414 // text, we never have a discontinuous selection. So we only deal with a
6416 wxRichTextRange selectionRange
;
6417 if (selection
.IsValid())
6419 wxRichTextRangeArray selectionRanges
= selection
.GetSelectionForObject(this);
6420 if (selectionRanges
.GetCount() > 0)
6421 selectionRange
= selectionRanges
[0];
6423 selectionRange
= wxRICHTEXT_NO_SELECTION
;
6426 selectionRange
= wxRICHTEXT_NO_SELECTION
;
6428 int offset
= GetRange().GetStart();
6430 wxString str
= m_text
;
6431 if (context
.HasVirtualText(this))
6433 if (!context
.GetVirtualText(this, str
) || str
.Length() != m_text
.Length())
6437 // Replace line break characters with spaces
6438 wxString toRemove
= wxRichTextLineBreakChar
;
6439 str
.Replace(toRemove
, wxT(" "));
6440 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS
|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS
)))
6443 long len
= range
.GetLength();
6444 wxString stringChunk
= str
.Mid(range
.GetStart() - offset
, (size_t) len
);
6446 // Test for the optimized situations where all is selected, or none
6449 wxFont
textFont(GetBuffer()->GetFontTable().FindFont(textAttr
));
6450 wxCheckSetFont(dc
, textFont
);
6451 int charHeight
= dc
.GetCharHeight();
6454 if ( textFont
.IsOk() )
6456 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SMALL_CAPITALS
))
6458 textFont
.SetPointSize((int) (textFont
.GetPointSize()*0.75));
6459 wxCheckSetFont(dc
, textFont
);
6460 charHeight
= dc
.GetCharHeight();
6463 if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
) )
6465 if (textFont
.IsUsingSizeInPixels())
6467 double size
= static_cast<double>(textFont
.GetPixelSize().y
) / wxSCRIPT_MUL_FACTOR
;
6468 textFont
.SetPixelSize(wxSize(0, static_cast<int>(size
)));
6474 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6475 textFont
.SetPointSize(static_cast<int>(size
));
6479 wxCheckSetFont(dc
, textFont
);
6481 else if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) )
6483 if (textFont
.IsUsingSizeInPixels())
6485 double size
= static_cast<double>(textFont
.GetPixelSize().y
) / wxSCRIPT_MUL_FACTOR
;
6486 textFont
.SetPixelSize(wxSize(0, static_cast<int>(size
)));
6488 int sub_height
= static_cast<int>(static_cast<double>(charHeight
) / wxSCRIPT_MUL_FACTOR
);
6489 y
= rect
.y
+ (rect
.height
- sub_height
+ (descent
- m_descent
));
6493 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6494 textFont
.SetPointSize(static_cast<int>(size
));
6496 int sub_height
= static_cast<int>(static_cast<double>(charHeight
) / wxSCRIPT_MUL_FACTOR
);
6497 y
= rect
.y
+ (rect
.height
- sub_height
+ (descent
- m_descent
));
6499 wxCheckSetFont(dc
, textFont
);
6504 y
= rect
.y
+ (rect
.height
- charHeight
- (descent
- m_descent
));
6510 y
= rect
.y
+ (rect
.height
- charHeight
- (descent
- m_descent
));
6513 // TODO: new selection code
6515 // (a) All selected.
6516 if (selectionRange
.GetStart() <= range
.GetStart() && selectionRange
.GetEnd() >= range
.GetEnd())
6518 DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, true);
6520 // (b) None selected.
6521 else if (selectionRange
.GetEnd() < range
.GetStart() || selectionRange
.GetStart() > range
.GetEnd())
6523 // Draw all unselected
6524 DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, false);
6528 // (c) Part selected, part not
6529 // Let's draw unselected chunk, selected chunk, then unselected chunk.
6531 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6533 // 1. Initial unselected chunk, if any, up until start of selection.
6534 if (selectionRange
.GetStart() > range
.GetStart() && selectionRange
.GetStart() <= range
.GetEnd())
6536 int r1
= range
.GetStart();
6537 int s1
= selectionRange
.GetStart()-1;
6538 int fragmentLen
= s1
- r1
+ 1;
6539 if (fragmentLen
< 0)
6541 wxLogDebug(wxT("Mid(%d, %d"), (int)(r1
- offset
), (int)fragmentLen
);
6543 wxString stringFragment
= str
.Mid(r1
- offset
, fragmentLen
);
6545 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false);
6548 if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
)
6550 // Compensate for kerning difference
6551 wxString
stringFragment2(str
.Mid(r1
- offset
, fragmentLen
+1));
6552 wxString
stringFragment3(str
.Mid(r1
- offset
+ fragmentLen
, 1));
6554 wxCoord w1
, h1
, w2
, h2
, w3
, h3
;
6555 dc
.GetTextExtent(stringFragment
, & w1
, & h1
);
6556 dc
.GetTextExtent(stringFragment2
, & w2
, & h2
);
6557 dc
.GetTextExtent(stringFragment3
, & w3
, & h3
);
6559 int kerningDiff
= (w1
+ w3
) - w2
;
6560 x
= x
- kerningDiff
;
6565 // 2. Selected chunk, if any.
6566 if (selectionRange
.GetEnd() >= range
.GetStart())
6568 int s1
= wxMax(selectionRange
.GetStart(), range
.GetStart());
6569 int s2
= wxMin(selectionRange
.GetEnd(), range
.GetEnd());
6571 int fragmentLen
= s2
- s1
+ 1;
6572 if (fragmentLen
< 0)
6574 wxLogDebug(wxT("Mid(%d, %d"), (int)(s1
- offset
), (int)fragmentLen
);
6576 wxString stringFragment
= str
.Mid(s1
- offset
, fragmentLen
);
6578 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, true);
6581 if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
)
6583 // Compensate for kerning difference
6584 wxString
stringFragment2(str
.Mid(s1
- offset
, fragmentLen
+1));
6585 wxString
stringFragment3(str
.Mid(s1
- offset
+ fragmentLen
, 1));
6587 wxCoord w1
, h1
, w2
, h2
, w3
, h3
;
6588 dc
.GetTextExtent(stringFragment
, & w1
, & h1
);
6589 dc
.GetTextExtent(stringFragment2
, & w2
, & h2
);
6590 dc
.GetTextExtent(stringFragment3
, & w3
, & h3
);
6592 int kerningDiff
= (w1
+ w3
) - w2
;
6593 x
= x
- kerningDiff
;
6598 // 3. Remaining unselected chunk, if any
6599 if (selectionRange
.GetEnd() < range
.GetEnd())
6601 int s2
= wxMin(selectionRange
.GetEnd()+1, range
.GetEnd());
6602 int r2
= range
.GetEnd();
6604 int fragmentLen
= r2
- s2
+ 1;
6605 if (fragmentLen
< 0)
6607 wxLogDebug(wxT("Mid(%d, %d"), (int)(s2
- offset
), (int)fragmentLen
);
6609 wxString stringFragment
= str
.Mid(s2
- offset
, fragmentLen
);
6611 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false);
6618 bool wxRichTextPlainText::DrawTabbedString(wxDC
& dc
, const wxRichTextAttr
& attr
, const wxRect
& rect
,wxString
& str
, wxCoord
& x
, wxCoord
& y
, bool selected
)
6620 bool hasTabs
= (str
.Find(wxT('\t')) != wxNOT_FOUND
);
6622 wxArrayInt tabArray
;
6626 if (attr
.GetTabs().IsEmpty())
6627 tabArray
= wxRichTextParagraph::GetDefaultTabs();
6629 tabArray
= attr
.GetTabs();
6630 tabCount
= tabArray
.GetCount();
6632 for (int i
= 0; i
< tabCount
; ++i
)
6634 int pos
= tabArray
[i
];
6635 pos
= ConvertTenthsMMToPixels(dc
, pos
);
6642 int nextTabPos
= -1;
6648 wxColour
highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
));
6649 wxColour
highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
));
6651 wxCheckSetBrush(dc
, wxBrush(highlightColour
));
6652 wxCheckSetPen(dc
, wxPen(highlightColour
));
6653 dc
.SetTextForeground(highlightTextColour
);
6654 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6658 dc
.SetTextForeground(attr
.GetTextColour());
6660 if (attr
.HasFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
) && attr
.GetBackgroundColour().IsOk())
6662 dc
.SetBackgroundMode(wxBRUSHSTYLE_SOLID
);
6663 dc
.SetTextBackground(attr
.GetBackgroundColour());
6666 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6669 wxCoord x_orig
= GetParent()->GetPosition().x
;
6672 // the string has a tab
6673 // break up the string at the Tab
6674 wxString stringChunk
= str
.BeforeFirst(wxT('\t'));
6675 str
= str
.AfterFirst(wxT('\t'));
6676 dc
.GetTextExtent(stringChunk
, & w
, & h
);
6678 bool not_found
= true;
6679 for (int i
= 0; i
< tabCount
&& not_found
; ++i
)
6681 nextTabPos
= tabArray
.Item(i
) + x_orig
;
6683 // Find the next tab position.
6684 // Even if we're at the end of the tab array, we must still draw the chunk.
6686 if (nextTabPos
> tabPos
|| (i
== (tabCount
- 1)))
6688 if (nextTabPos
<= tabPos
)
6690 int defaultTabWidth
= ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
);
6691 nextTabPos
= tabPos
+ defaultTabWidth
;
6698 wxRect
selRect(x
, rect
.y
, w
, rect
.GetHeight());
6699 dc
.DrawRectangle(selRect
);
6701 dc
.DrawText(stringChunk
, x
, y
);
6703 if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
))
6705 wxPen oldPen
= dc
.GetPen();
6706 wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1));
6707 dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5));
6708 wxCheckSetPen(dc
, oldPen
);
6714 hasTabs
= (str
.Find(wxT('\t')) != wxNOT_FOUND
);
6719 dc
.GetTextExtent(str
, & w
, & h
);
6722 wxRect
selRect(x
, rect
.y
, w
, rect
.GetHeight());
6723 dc
.DrawRectangle(selRect
);
6725 dc
.DrawText(str
, x
, y
);
6727 if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
))
6729 wxPen oldPen
= dc
.GetPen();
6730 wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1));
6731 dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5));
6732 wxCheckSetPen(dc
, oldPen
);
6741 /// Lay the item out
6742 bool wxRichTextPlainText::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& WXUNUSED(rect
), const wxRect
& WXUNUSED(parentRect
), int WXUNUSED(style
))
6744 // Only lay out if we haven't already cached the size
6746 GetRangeSize(GetRange(), m_size
, m_descent
, dc
, context
, 0, wxPoint(0, 0));
6748 // Eventually we want to have a reasonable estimate of minimum size.
6749 m_minSize
= wxSize(0, 0);
6754 void wxRichTextPlainText::Copy(const wxRichTextPlainText
& obj
)
6756 wxRichTextObject::Copy(obj
);
6758 m_text
= obj
.m_text
;
6761 /// Get/set the object size for the given range. Returns false if the range
6762 /// is invalid for this object.
6763 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
6765 if (!range
.IsWithin(GetRange()))
6768 wxRichTextParagraph
* para
= wxDynamicCast(GetParent(), wxRichTextParagraph
);
6769 wxASSERT (para
!= NULL
);
6771 int relativeX
= position
.x
- GetParent()->GetPosition().x
;
6773 wxRichTextAttr
textAttr(para
? para
->GetCombinedAttributes(GetAttributes()) : GetAttributes());
6774 context
.ApplyVirtualAttributes(textAttr
, (wxRichTextObject
*) this);
6776 // Always assume unformatted text, since at this level we have no knowledge
6777 // of line breaks - and we don't need it, since we'll calculate size within
6778 // formatted text by doing it in chunks according to the line ranges
6780 bool bScript(false);
6781 wxFont
font(GetBuffer()->GetFontTable().FindFont(textAttr
));
6784 if ( textAttr
.HasTextEffects() && ( (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
)
6785 || (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) ) )
6787 wxFont textFont
= font
;
6788 if (textFont
.IsUsingSizeInPixels())
6790 double size
= static_cast<double>(textFont
.GetPixelSize().y
) / wxSCRIPT_MUL_FACTOR
;
6791 textFont
.SetPixelSize(wxSize(0, static_cast<int>(size
)));
6795 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6796 textFont
.SetPointSize(static_cast<int>(size
));
6798 wxCheckSetFont(dc
, textFont
);
6801 else if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SMALL_CAPITALS
))
6803 wxFont textFont
= font
;
6804 textFont
.SetPointSize((int) (textFont
.GetPointSize()*0.75));
6805 wxCheckSetFont(dc
, textFont
);
6810 wxCheckSetFont(dc
, font
);
6814 bool haveDescent
= false;
6815 int startPos
= range
.GetStart() - GetRange().GetStart();
6816 long len
= range
.GetLength();
6818 wxString
str(m_text
);
6819 if (context
.HasVirtualText(this))
6821 if (!context
.GetVirtualText(this, str
) || str
.Length() != m_text
.Length())
6825 wxString toReplace
= wxRichTextLineBreakChar
;
6826 str
.Replace(toReplace
, wxT(" "));
6828 wxString stringChunk
= str
.Mid(startPos
, (size_t) len
);
6830 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS
|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS
)))
6831 stringChunk
.MakeUpper();
6835 if (stringChunk
.Find(wxT('\t')) != wxNOT_FOUND
)
6837 // the string has a tab
6838 wxArrayInt tabArray
;
6839 if (textAttr
.GetTabs().IsEmpty())
6840 tabArray
= wxRichTextParagraph::GetDefaultTabs();
6842 tabArray
= textAttr
.GetTabs();
6844 int tabCount
= tabArray
.GetCount();
6846 for (int i
= 0; i
< tabCount
; ++i
)
6848 int pos
= tabArray
[i
];
6849 pos
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, pos
);
6853 int nextTabPos
= -1;
6855 while (stringChunk
.Find(wxT('\t')) >= 0)
6857 int absoluteWidth
= 0;
6859 // the string has a tab
6860 // break up the string at the Tab
6861 wxString stringFragment
= stringChunk
.BeforeFirst(wxT('\t'));
6862 stringChunk
= stringChunk
.AfterFirst(wxT('\t'));
6867 if (partialExtents
->GetCount() > 0)
6868 oldWidth
= (*partialExtents
)[partialExtents
->GetCount()-1];
6872 // Add these partial extents
6874 dc
.GetPartialTextExtents(stringFragment
, p
);
6876 for (j
= 0; j
< p
.GetCount(); j
++)
6877 partialExtents
->Add(oldWidth
+ p
[j
]);
6879 if (partialExtents
->GetCount() > 0)
6880 absoluteWidth
= (*partialExtents
)[(*partialExtents
).GetCount()-1] + relativeX
;
6882 absoluteWidth
= relativeX
;
6886 dc
.GetTextExtent(stringFragment
, & w
, & h
);
6888 absoluteWidth
= width
+ relativeX
;
6892 bool notFound
= true;
6893 for (int i
= 0; i
< tabCount
&& notFound
; ++i
)
6895 nextTabPos
= tabArray
.Item(i
);
6897 // Find the next tab position.
6898 // Even if we're at the end of the tab array, we must still process the chunk.
6900 if (nextTabPos
> absoluteWidth
|| (i
== (tabCount
- 1)))
6902 if (nextTabPos
<= absoluteWidth
)
6904 int defaultTabWidth
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
);
6905 nextTabPos
= absoluteWidth
+ defaultTabWidth
;
6909 width
= nextTabPos
- relativeX
;
6912 partialExtents
->Add(width
);
6918 if (!stringChunk
.IsEmpty())
6923 if (partialExtents
->GetCount() > 0)
6924 oldWidth
= (*partialExtents
)[partialExtents
->GetCount()-1];
6928 // Add these partial extents
6930 dc
.GetPartialTextExtents(stringChunk
, p
);
6932 for (j
= 0; j
< p
.GetCount(); j
++)
6933 partialExtents
->Add(oldWidth
+ p
[j
]);
6937 dc
.GetTextExtent(stringChunk
, & w
, & h
, & descent
);
6945 int charHeight
= dc
.GetCharHeight();
6946 if ((*partialExtents
).GetCount() > 0)
6947 w
= (*partialExtents
)[partialExtents
->GetCount()-1];
6950 size
= wxSize(w
, charHeight
);
6954 size
= wxSize(width
, dc
.GetCharHeight());
6958 dc
.GetTextExtent(wxT("X"), & w
, & h
, & descent
);
6966 /// Do a split, returning an object containing the second part, and setting
6967 /// the first part in 'this'.
6968 wxRichTextObject
* wxRichTextPlainText::DoSplit(long pos
)
6970 long index
= pos
- GetRange().GetStart();
6972 if (index
< 0 || index
>= (int) m_text
.length())
6975 wxString firstPart
= m_text
.Mid(0, index
);
6976 wxString secondPart
= m_text
.Mid(index
);
6980 wxRichTextPlainText
* newObject
= new wxRichTextPlainText(secondPart
);
6981 newObject
->SetAttributes(GetAttributes());
6982 newObject
->SetProperties(GetProperties());
6984 newObject
->SetRange(wxRichTextRange(pos
, GetRange().GetEnd()));
6985 GetRange().SetEnd(pos
-1);
6991 void wxRichTextPlainText::CalculateRange(long start
, long& end
)
6993 end
= start
+ m_text
.length() - 1;
6994 m_range
.SetRange(start
, end
);
6998 bool wxRichTextPlainText::DeleteRange(const wxRichTextRange
& range
)
7000 wxRichTextRange r
= range
;
7002 r
.LimitTo(GetRange());
7004 if (r
.GetStart() == GetRange().GetStart() && r
.GetEnd() == GetRange().GetEnd())
7010 long startIndex
= r
.GetStart() - GetRange().GetStart();
7011 long len
= r
.GetLength();
7013 m_text
= m_text
.Mid(0, startIndex
) + m_text
.Mid(startIndex
+len
);
7017 /// Get text for the given range.
7018 wxString
wxRichTextPlainText::GetTextForRange(const wxRichTextRange
& range
) const
7020 wxRichTextRange r
= range
;
7022 r
.LimitTo(GetRange());
7024 long startIndex
= r
.GetStart() - GetRange().GetStart();
7025 long len
= r
.GetLength();
7027 return m_text
.Mid(startIndex
, len
);
7030 /// Returns true if this object can merge itself with the given one.
7031 bool wxRichTextPlainText::CanMerge(wxRichTextObject
* object
, wxRichTextDrawingContext
& context
) const
7034 if (!context
.GetVirtualAttributesEnabled())
7036 return object
->GetClassInfo() == wxCLASSINFO(wxRichTextPlainText
) &&
7037 (m_text
.empty() || (wxTextAttrEq(GetAttributes(), object
->GetAttributes()) && m_properties
== object
->GetProperties()));
7041 wxRichTextPlainText
* otherObj
= wxDynamicCast(object
, wxRichTextPlainText
);
7042 if (!otherObj
|| m_text
.empty())
7045 if (!wxTextAttrEq(GetAttributes(), object
->GetAttributes()) || !(m_properties
== object
->GetProperties()))
7048 // Check if differing virtual attributes makes it impossible to merge
7051 bool hasVirtualAttr1
= context
.HasVirtualAttributes((wxRichTextObject
*) this);
7052 bool hasVirtualAttr2
= context
.HasVirtualAttributes((wxRichTextObject
*) object
);
7053 if (!hasVirtualAttr1
&& !hasVirtualAttr2
)
7055 else if (hasVirtualAttr1
!= hasVirtualAttr2
)
7059 wxRichTextAttr virtualAttr1
= context
.GetVirtualAttributes((wxRichTextObject
*) this);
7060 wxRichTextAttr virtualAttr2
= context
.GetVirtualAttributes((wxRichTextObject
*) object
);
7061 return virtualAttr1
== virtualAttr2
;
7066 /// Returns true if this object merged itself with the given one.
7067 /// The calling code will then delete the given object.
7068 bool wxRichTextPlainText::Merge(wxRichTextObject
* object
, wxRichTextDrawingContext
& WXUNUSED(context
))
7070 wxRichTextPlainText
* textObject
= wxDynamicCast(object
, wxRichTextPlainText
);
7071 wxASSERT( textObject
!= NULL
);
7075 m_text
+= textObject
->GetText();
7076 wxRichTextApplyStyle(m_attributes
, textObject
->GetAttributes());
7083 bool wxRichTextPlainText::CanSplit(wxRichTextDrawingContext
& context
) const
7085 // If this object has any virtual attributes at all, whether for the whole object
7086 // or individual ones, we should try splitting it by calling Split.
7087 // Must be more than one character in order to be able to split.
7088 return m_text
.Length() > 1 && context
.HasVirtualAttributes((wxRichTextObject
*) this);
7091 wxRichTextObject
* wxRichTextPlainText::Split(wxRichTextDrawingContext
& context
)
7093 int count
= context
.GetVirtualSubobjectAttributesCount(this);
7094 if (count
> 0 && GetParent())
7096 wxRichTextCompositeObject
* parent
= wxDynamicCast(GetParent(), wxRichTextCompositeObject
);
7097 wxRichTextObjectList::compatibility_iterator node
= parent
->GetChildren().Find(this);
7100 const wxRichTextAttr emptyAttr
;
7101 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
7103 wxArrayInt positions
;
7104 wxRichTextAttrArray attributes
;
7105 if (context
.GetVirtualSubobjectAttributes(this, positions
, attributes
) && positions
.GetCount() > 0)
7107 wxASSERT(positions
.GetCount() == attributes
.GetCount());
7109 // We will gather up runs of text with the same virtual attributes
7111 int len
= m_text
.Length();
7114 // runStart and runEnd represent the accumulated run with a consistent attribute
7115 // that hasn't yet been appended
7118 wxRichTextAttr currentAttr
;
7119 wxString text
= m_text
;
7120 wxRichTextPlainText
* lastPlainText
= this;
7122 for (i
= 0; i
< (int) positions
.GetCount(); i
++)
7124 int pos
= positions
[i
];
7125 wxASSERT(pos
>= 0 && pos
< len
);
7126 if (pos
>= 0 && pos
< len
)
7128 const wxRichTextAttr
& attr
= attributes
[i
];
7135 // Check if there was a gap from the last known attribute and this.
7136 // In that case, we need to do something with the span of non-attributed text.
7137 else if ((pos
-1) > runEnd
)
7141 // We hadn't processed anything previously, so the previous run is from the text start
7142 // to just before this position. The current attribute remains empty.
7148 // If the previous attribute matches the gap's attribute (i.e., no attributes)
7149 // then just extend the run.
7150 if (currentAttr
.IsDefault())
7156 // We need to add an object, or reuse the existing one.
7159 lastPlainText
= this;
7160 SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7164 wxRichTextPlainText
* obj
= new wxRichTextPlainText
;
7165 lastPlainText
= obj
;
7166 obj
->SetAttributes(GetAttributes());
7167 obj
->SetProperties(GetProperties());
7168 obj
->SetParent(parent
);
7170 obj
->SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7172 parent
->GetChildren().Insert(next
, obj
);
7174 parent
->GetChildren().Append(obj
);
7177 runStart
= runEnd
+1;
7180 currentAttr
= emptyAttr
;
7185 wxASSERT(runEnd
== pos
-1);
7187 // Now we only have to deal with the previous run
7188 if (currentAttr
== attr
)
7190 // If we still have the same attributes, then we
7191 // simply increase the run size.
7198 // We need to add an object, or reuse the existing one.
7201 lastPlainText
= this;
7202 SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7206 wxRichTextPlainText
* obj
= new wxRichTextPlainText
;
7207 lastPlainText
= obj
;
7208 obj
->SetAttributes(GetAttributes());
7209 obj
->SetProperties(GetProperties());
7210 obj
->SetParent(parent
);
7212 obj
->SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7214 parent
->GetChildren().Insert(next
, obj
);
7216 parent
->GetChildren().Append(obj
);
7228 // We may still have a run to add, and possibly a no-attribute text fragment after that.
7229 // If the whole string was already a single attribute (the run covers the whole string), don't split.
7230 if ((runStart
!= -1) && !(runStart
== 0 && runEnd
== (len
-1)))
7232 // If the current attribute is empty, merge the run with the next fragment
7233 // which by definition (because it's not specified) has empty attributes.
7234 if (currentAttr
.IsDefault())
7237 if (runEnd
< (len
-1))
7239 // We need to add an object, or reuse the existing one.
7242 lastPlainText
= this;
7243 SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7247 wxRichTextPlainText
* obj
= new wxRichTextPlainText
;
7248 lastPlainText
= obj
;
7249 obj
->SetAttributes(GetAttributes());
7250 obj
->SetProperties(GetProperties());
7251 obj
->SetParent(parent
);
7253 obj
->SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7255 parent
->GetChildren().Insert(next
, obj
);
7257 parent
->GetChildren().Append(obj
);
7260 runStart
= runEnd
+1;
7264 // Now the last, non-attributed fragment at the end, if any
7265 if ((runStart
< len
) && !(runStart
== 0 && runEnd
== (len
-1)))
7267 wxASSERT(runStart
!= 0);
7269 wxRichTextPlainText
* obj
= new wxRichTextPlainText
;
7270 obj
->SetAttributes(GetAttributes());
7271 obj
->SetProperties(GetProperties());
7272 obj
->SetParent(parent
);
7274 obj
->SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7276 parent
->GetChildren().Insert(next
, obj
);
7278 parent
->GetChildren().Append(obj
);
7280 lastPlainText
= obj
;
7284 return lastPlainText
;
7291 /// Dump to output stream for debugging
7292 void wxRichTextPlainText::Dump(wxTextOutputStream
& stream
)
7294 wxRichTextObject::Dump(stream
);
7295 stream
<< m_text
<< wxT("\n");
7298 /// Get the first position from pos that has a line break character.
7299 long wxRichTextPlainText::GetFirstLineBreakPosition(long pos
)
7302 int len
= m_text
.length();
7303 int startPos
= pos
- m_range
.GetStart();
7304 for (i
= startPos
; i
< len
; i
++)
7306 wxChar ch
= m_text
[i
];
7307 if (ch
== wxRichTextLineBreakChar
)
7309 return i
+ m_range
.GetStart();
7317 * This is a kind of box, used to represent the whole buffer
7320 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBuffer
, wxRichTextParagraphLayoutBox
)
7322 wxList
wxRichTextBuffer::sm_handlers
;
7323 wxList
wxRichTextBuffer::sm_drawingHandlers
;
7324 wxRichTextFieldTypeHashMap
wxRichTextBuffer::sm_fieldTypes
;
7325 wxRichTextRenderer
* wxRichTextBuffer::sm_renderer
= NULL
;
7326 int wxRichTextBuffer::sm_bulletRightMargin
= 20;
7327 float wxRichTextBuffer::sm_bulletProportion
= (float) 0.3;
7328 bool wxRichTextBuffer::sm_floatingLayoutMode
= true;
7331 void wxRichTextBuffer::Init()
7333 m_commandProcessor
= new wxCommandProcessor
;
7334 m_styleSheet
= NULL
;
7336 m_batchedCommandDepth
= 0;
7337 m_batchedCommand
= NULL
;
7341 m_dimensionScale
= 1.0;
7347 wxRichTextBuffer::~wxRichTextBuffer()
7349 delete m_commandProcessor
;
7350 delete m_batchedCommand
;
7353 ClearEventHandlers();
7356 void wxRichTextBuffer::ResetAndClearCommands()
7360 GetCommandProcessor()->ClearCommands();
7363 Invalidate(wxRICHTEXT_ALL
);
7366 void wxRichTextBuffer::Copy(const wxRichTextBuffer
& obj
)
7368 wxRichTextParagraphLayoutBox::Copy(obj
);
7370 m_styleSheet
= obj
.m_styleSheet
;
7371 m_modified
= obj
.m_modified
;
7372 m_batchedCommandDepth
= 0;
7373 if (m_batchedCommand
)
7374 delete m_batchedCommand
;
7375 m_batchedCommand
= NULL
;
7376 m_suppressUndo
= obj
.m_suppressUndo
;
7377 m_invalidRange
= obj
.m_invalidRange
;
7378 m_dimensionScale
= obj
.m_dimensionScale
;
7379 m_fontScale
= obj
.m_fontScale
;
7382 /// Push style sheet to top of stack
7383 bool wxRichTextBuffer::PushStyleSheet(wxRichTextStyleSheet
* styleSheet
)
7386 styleSheet
->InsertSheet(m_styleSheet
);
7388 SetStyleSheet(styleSheet
);
7393 /// Pop style sheet from top of stack
7394 wxRichTextStyleSheet
* wxRichTextBuffer::PopStyleSheet()
7398 wxRichTextStyleSheet
* oldSheet
= m_styleSheet
;
7399 m_styleSheet
= oldSheet
->GetNextSheet();
7408 /// Submit command to insert paragraphs
7409 bool wxRichTextBuffer::InsertParagraphsWithUndo(long pos
, const wxRichTextParagraphLayoutBox
& paragraphs
, wxRichTextCtrl
* ctrl
, int flags
)
7411 return ctrl
->GetFocusObject()->InsertParagraphsWithUndo(this, pos
, paragraphs
, ctrl
, flags
);
7414 /// Submit command to insert paragraphs
7415 bool wxRichTextParagraphLayoutBox::InsertParagraphsWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxRichTextParagraphLayoutBox
& paragraphs
, wxRichTextCtrl
* ctrl
, int WXUNUSED(flags
))
7417 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7419 action
->GetNewParagraphs() = paragraphs
;
7421 action
->SetPosition(pos
);
7423 wxRichTextRange range
= wxRichTextRange(pos
, pos
+ paragraphs
.GetOwnRange().GetEnd() - 1);
7424 if (!paragraphs
.GetPartialParagraph())
7425 range
.SetEnd(range
.GetEnd()+1);
7427 // Set the range we'll need to delete in Undo
7428 action
->SetRange(range
);
7430 buffer
->SubmitAction(action
);
7435 /// Submit command to insert the given text
7436 bool wxRichTextBuffer::InsertTextWithUndo(long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
, int flags
)
7438 return ctrl
->GetFocusObject()->InsertTextWithUndo(this, pos
, text
, ctrl
, flags
);
7441 /// Submit command to insert the given text
7442 bool wxRichTextParagraphLayoutBox::InsertTextWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
, int flags
)
7444 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7446 wxRichTextAttr
* p
= NULL
;
7447 wxRichTextAttr paraAttr
;
7448 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7450 // Get appropriate paragraph style
7451 paraAttr
= GetStyleForNewParagraph(buffer
, pos
, false, false);
7452 if (!paraAttr
.IsDefault())
7456 action
->GetNewParagraphs().AddParagraphs(text
, p
);
7458 int length
= action
->GetNewParagraphs().GetOwnRange().GetLength();
7460 if (!text
.empty() && text
.Last() != wxT('\n'))
7462 // Don't count the newline when undoing
7464 action
->GetNewParagraphs().SetPartialParagraph(true);
7466 else if (!text
.empty() && text
.Last() == wxT('\n'))
7469 action
->SetPosition(pos
);
7471 // Set the range we'll need to delete in Undo
7472 action
->SetRange(wxRichTextRange(pos
, pos
+ length
- 1));
7474 buffer
->SubmitAction(action
);
7479 /// Submit command to insert the given text
7480 bool wxRichTextBuffer::InsertNewlineWithUndo(long pos
, wxRichTextCtrl
* ctrl
, int flags
)
7482 return ctrl
->GetFocusObject()->InsertNewlineWithUndo(this, pos
, ctrl
, flags
);
7485 /// Submit command to insert the given text
7486 bool wxRichTextParagraphLayoutBox::InsertNewlineWithUndo(wxRichTextBuffer
* buffer
, long pos
, wxRichTextCtrl
* ctrl
, int flags
)
7488 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7490 wxRichTextAttr
* p
= NULL
;
7491 wxRichTextAttr paraAttr
;
7492 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7494 paraAttr
= GetStyleForNewParagraph(buffer
, pos
, false, true /* look for next paragraph style */);
7495 if (!paraAttr
.IsDefault())
7499 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7500 // Don't include box attributes such as margins
7501 attr
.GetTextBoxAttr().Reset();
7503 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(wxEmptyString
, this, & attr
);
7504 action
->GetNewParagraphs().AppendChild(newPara
);
7505 action
->GetNewParagraphs().UpdateRanges();
7506 action
->GetNewParagraphs().SetPartialParagraph(false);
7507 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
, false);
7511 newPara
->SetAttributes(*p
);
7513 if (flags
& wxRICHTEXT_INSERT_INTERACTIVE
)
7515 if (para
&& para
->GetRange().GetEnd() == pos
)
7518 // Now see if we need to number the paragraph.
7519 if (newPara
->GetAttributes().HasBulletNumber())
7521 wxRichTextAttr numberingAttr
;
7522 if (FindNextParagraphNumber(para
, numberingAttr
))
7523 wxRichTextApplyStyle(newPara
->GetAttributes(), (const wxRichTextAttr
&) numberingAttr
);
7527 action
->SetPosition(pos
);
7529 // Use the default character style
7530 if (!buffer
->GetDefaultStyle().IsDefault() && newPara
->GetChildren().GetFirst())
7532 // Check whether the default style merely reflects the paragraph/basic style,
7533 // in which case don't apply it.
7534 wxRichTextAttr
defaultStyle(buffer
->GetDefaultStyle());
7535 defaultStyle
.GetTextBoxAttr().Reset();
7536 wxRichTextAttr toApply
;
7539 wxRichTextAttr combinedAttr
= para
->GetCombinedAttributes(true /* include box attributes */);
7540 wxRichTextAttr newAttr
;
7541 // This filters out attributes that are accounted for by the current
7542 // paragraph/basic style
7543 wxRichTextApplyStyle(toApply
, defaultStyle
, & combinedAttr
);
7546 toApply
= defaultStyle
;
7548 if (!toApply
.IsDefault())
7549 newPara
->GetChildren().GetFirst()->GetData()->SetAttributes(toApply
);
7552 // Set the range we'll need to delete in Undo
7553 action
->SetRange(wxRichTextRange(pos1
, pos1
));
7555 buffer
->SubmitAction(action
);
7560 /// Submit command to insert the given image
7561 bool wxRichTextBuffer::InsertImageWithUndo(long pos
, const wxRichTextImageBlock
& imageBlock
, wxRichTextCtrl
* ctrl
, int flags
,
7562 const wxRichTextAttr
& textAttr
)
7564 return ctrl
->GetFocusObject()->InsertImageWithUndo(this, pos
, imageBlock
, ctrl
, flags
, textAttr
);
7567 /// Submit command to insert the given image
7568 bool wxRichTextParagraphLayoutBox::InsertImageWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxRichTextImageBlock
& imageBlock
,
7569 wxRichTextCtrl
* ctrl
, int flags
,
7570 const wxRichTextAttr
& textAttr
)
7572 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7574 wxRichTextAttr
* p
= NULL
;
7575 wxRichTextAttr paraAttr
;
7576 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7578 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
7579 if (!paraAttr
.IsDefault())
7583 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7585 // Don't include box attributes such as margins
7586 attr
.GetTextBoxAttr().Reset();
7588 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7590 newPara
->SetAttributes(*p
);
7592 wxRichTextImage
* imageObject
= new wxRichTextImage(imageBlock
, newPara
);
7593 newPara
->AppendChild(imageObject
);
7594 imageObject
->SetAttributes(textAttr
);
7595 action
->GetNewParagraphs().AppendChild(newPara
);
7596 action
->GetNewParagraphs().UpdateRanges();
7598 action
->GetNewParagraphs().SetPartialParagraph(true);
7600 action
->SetPosition(pos
);
7602 // Set the range we'll need to delete in Undo
7603 action
->SetRange(wxRichTextRange(pos
, pos
));
7605 buffer
->SubmitAction(action
);
7610 // Insert an object with no change of it
7611 wxRichTextObject
* wxRichTextBuffer::InsertObjectWithUndo(long pos
, wxRichTextObject
*object
, wxRichTextCtrl
* ctrl
, int flags
)
7613 return ctrl
->GetFocusObject()->InsertObjectWithUndo(this, pos
, object
, ctrl
, flags
);
7616 // Insert an object with no change of it
7617 wxRichTextObject
* wxRichTextParagraphLayoutBox::InsertObjectWithUndo(wxRichTextBuffer
* buffer
, long pos
, wxRichTextObject
*object
, wxRichTextCtrl
* ctrl
, int flags
)
7619 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Object"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7621 wxRichTextAttr
* p
= NULL
;
7622 wxRichTextAttr paraAttr
;
7623 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7625 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
7626 if (!paraAttr
.IsDefault())
7630 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7632 // Don't include box attributes such as margins
7633 attr
.GetTextBoxAttr().Reset();
7635 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7637 newPara
->SetAttributes(*p
);
7639 newPara
->AppendChild(object
);
7640 action
->GetNewParagraphs().AppendChild(newPara
);
7641 action
->GetNewParagraphs().UpdateRanges();
7643 action
->GetNewParagraphs().SetPartialParagraph(true);
7645 action
->SetPosition(pos
);
7647 // Set the range we'll need to delete in Undo
7648 action
->SetRange(wxRichTextRange(pos
, pos
));
7650 buffer
->SubmitAction(action
);
7652 wxRichTextObject
* obj
= GetLeafObjectAtPosition(pos
);
7656 wxRichTextField
* wxRichTextParagraphLayoutBox::InsertFieldWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxString
& fieldType
,
7657 const wxRichTextProperties
& properties
,
7658 wxRichTextCtrl
* ctrl
, int flags
,
7659 const wxRichTextAttr
& textAttr
)
7661 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Field"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7663 wxRichTextAttr
* p
= NULL
;
7664 wxRichTextAttr paraAttr
;
7665 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7667 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
7668 if (!paraAttr
.IsDefault())
7672 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7674 // Don't include box attributes such as margins
7675 attr
.GetTextBoxAttr().Reset();
7677 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7679 newPara
->SetAttributes(*p
);
7681 wxRichTextField
* fieldObject
= new wxRichTextField();
7682 fieldObject
->wxRichTextObject::SetProperties(properties
);
7683 fieldObject
->SetFieldType(fieldType
);
7684 fieldObject
->SetAttributes(textAttr
);
7685 newPara
->AppendChild(fieldObject
);
7686 action
->GetNewParagraphs().AppendChild(newPara
);
7687 action
->GetNewParagraphs().UpdateRanges();
7688 action
->GetNewParagraphs().SetPartialParagraph(true);
7689 action
->SetPosition(pos
);
7691 // Set the range we'll need to delete in Undo
7692 action
->SetRange(wxRichTextRange(pos
, pos
));
7694 buffer
->SubmitAction(action
);
7696 wxRichTextField
* obj
= wxDynamicCast(GetLeafObjectAtPosition(pos
), wxRichTextField
);
7700 /// Get the style that is appropriate for a new paragraph at this position.
7701 /// If the previous paragraph has a paragraph style name, look up the next-paragraph
7703 wxRichTextAttr
wxRichTextParagraphLayoutBox::GetStyleForNewParagraph(wxRichTextBuffer
* buffer
, long pos
, bool caretPosition
, bool lookUpNewParaStyle
) const
7705 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
, caretPosition
);
7708 wxRichTextAttr attr
;
7709 bool foundAttributes
= false;
7711 // Look for a matching paragraph style
7712 if (lookUpNewParaStyle
&& !para
->GetAttributes().GetParagraphStyleName().IsEmpty() && buffer
->GetStyleSheet())
7714 wxRichTextParagraphStyleDefinition
* paraDef
= buffer
->GetStyleSheet()->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
7717 // If we're not at the end of the paragraph, then we apply THIS style, and not the designated next style.
7718 if (para
->GetRange().GetEnd() == pos
&& !paraDef
->GetNextStyle().IsEmpty())
7720 wxRichTextParagraphStyleDefinition
* nextParaDef
= buffer
->GetStyleSheet()->FindParagraphStyle(paraDef
->GetNextStyle());
7723 foundAttributes
= true;
7724 attr
= nextParaDef
->GetStyleMergedWithBase(buffer
->GetStyleSheet());
7728 // If we didn't find the 'next style', use this style instead.
7729 if (!foundAttributes
)
7731 foundAttributes
= true;
7732 attr
= paraDef
->GetStyleMergedWithBase(buffer
->GetStyleSheet());
7737 // Also apply list style if present
7738 if (lookUpNewParaStyle
&& !para
->GetAttributes().GetListStyleName().IsEmpty() && buffer
->GetStyleSheet())
7740 wxRichTextListStyleDefinition
* listDef
= buffer
->GetStyleSheet()->FindListStyle(para
->GetAttributes().GetListStyleName());
7743 int thisIndent
= para
->GetAttributes().GetLeftIndent();
7744 int thisLevel
= para
->GetAttributes().HasOutlineLevel() ? para
->GetAttributes().GetOutlineLevel() : listDef
->FindLevelForIndent(thisIndent
);
7746 // Apply the overall list style, and item style for this level
7747 wxRichTextAttr
listStyle(listDef
->GetCombinedStyleForLevel(thisLevel
, buffer
->GetStyleSheet()));
7748 wxRichTextApplyStyle(attr
, listStyle
);
7749 attr
.SetOutlineLevel(thisLevel
);
7750 if (para
->GetAttributes().HasBulletNumber())
7751 attr
.SetBulletNumber(para
->GetAttributes().GetBulletNumber());
7755 if (!foundAttributes
)
7757 attr
= para
->GetAttributes();
7758 int flags
= attr
.GetFlags();
7760 // Eliminate character styles
7761 flags
&= ( (~ wxTEXT_ATTR_FONT
) |
7762 (~ wxTEXT_ATTR_TEXT_COLOUR
) |
7763 (~ wxTEXT_ATTR_BACKGROUND_COLOUR
) );
7764 attr
.SetFlags(flags
);
7770 return wxRichTextAttr();
7773 /// Submit command to delete this range
7774 bool wxRichTextBuffer::DeleteRangeWithUndo(const wxRichTextRange
& range
, wxRichTextCtrl
* ctrl
)
7776 return ctrl
->GetFocusObject()->DeleteRangeWithUndo(range
, ctrl
, this);
7779 /// Submit command to delete this range
7780 bool wxRichTextParagraphLayoutBox::DeleteRangeWithUndo(const wxRichTextRange
& range
, wxRichTextCtrl
* ctrl
, wxRichTextBuffer
* buffer
)
7782 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Delete"), wxRICHTEXT_DELETE
, buffer
, this, ctrl
);
7784 action
->SetPosition(ctrl
->GetCaretPosition());
7786 // Set the range to delete
7787 action
->SetRange(range
);
7789 // Copy the fragment that we'll need to restore in Undo
7790 CopyFragment(range
, action
->GetOldParagraphs());
7792 // See if we're deleting a paragraph marker, in which case we need to
7793 // make a note not to copy the attributes from the 2nd paragraph to the 1st.
7794 if (range
.GetStart() == range
.GetEnd())
7796 wxRichTextParagraph
* para
= GetParagraphAtPosition(range
.GetStart());
7797 if (para
&& para
->GetRange().GetEnd() == range
.GetEnd())
7799 wxRichTextParagraph
* nextPara
= GetParagraphAtPosition(range
.GetStart()+1);
7800 if (nextPara
&& nextPara
!= para
)
7802 action
->GetOldParagraphs().GetChildren().GetFirst()->GetData()->SetAttributes(nextPara
->GetAttributes());
7803 action
->GetOldParagraphs().GetAttributes().SetFlags(action
->GetOldParagraphs().GetAttributes().GetFlags() | wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
);
7808 buffer
->SubmitAction(action
);
7813 /// Collapse undo/redo commands
7814 bool wxRichTextBuffer::BeginBatchUndo(const wxString
& cmdName
)
7816 if (m_batchedCommandDepth
== 0)
7818 wxASSERT(m_batchedCommand
== NULL
);
7819 if (m_batchedCommand
)
7821 GetCommandProcessor()->Store(m_batchedCommand
);
7823 m_batchedCommand
= new wxRichTextCommand(cmdName
);
7826 m_batchedCommandDepth
++;
7831 /// Collapse undo/redo commands
7832 bool wxRichTextBuffer::EndBatchUndo()
7834 m_batchedCommandDepth
--;
7836 wxASSERT(m_batchedCommandDepth
>= 0);
7837 wxASSERT(m_batchedCommand
!= NULL
);
7839 if (m_batchedCommandDepth
== 0)
7841 GetCommandProcessor()->Store(m_batchedCommand
);
7842 m_batchedCommand
= NULL
;
7848 /// Submit immediately, or delay according to whether collapsing is on
7849 bool wxRichTextBuffer::SubmitAction(wxRichTextAction
* action
)
7851 if (action
&& !action
->GetNewParagraphs().IsEmpty())
7852 PrepareContent(action
->GetNewParagraphs());
7854 if (BatchingUndo() && m_batchedCommand
&& !SuppressingUndo())
7856 wxRichTextCommand
* cmd
= new wxRichTextCommand(action
->GetName());
7857 cmd
->AddAction(action
);
7859 cmd
->GetActions().Clear();
7862 m_batchedCommand
->AddAction(action
);
7866 wxRichTextCommand
* cmd
= new wxRichTextCommand(action
->GetName());
7867 cmd
->AddAction(action
);
7869 // Only store it if we're not suppressing undo.
7870 return GetCommandProcessor()->Submit(cmd
, !SuppressingUndo());
7876 /// Begin suppressing undo/redo commands.
7877 bool wxRichTextBuffer::BeginSuppressUndo()
7884 /// End suppressing undo/redo commands.
7885 bool wxRichTextBuffer::EndSuppressUndo()
7892 /// Begin using a style
7893 bool wxRichTextBuffer::BeginStyle(const wxRichTextAttr
& style
)
7895 wxRichTextAttr
newStyle(GetDefaultStyle());
7896 newStyle
.GetTextBoxAttr().Reset();
7898 // Save the old default style
7899 m_attributeStack
.Append((wxObject
*) new wxRichTextAttr(newStyle
));
7901 wxRichTextApplyStyle(newStyle
, style
);
7902 newStyle
.SetFlags(style
.GetFlags()|newStyle
.GetFlags());
7904 SetDefaultStyle(newStyle
);
7910 bool wxRichTextBuffer::EndStyle()
7912 if (!m_attributeStack
.GetFirst())
7914 wxLogDebug(_("Too many EndStyle calls!"));
7918 wxList::compatibility_iterator node
= m_attributeStack
.GetLast();
7919 wxRichTextAttr
* attr
= (wxRichTextAttr
*)node
->GetData();
7920 m_attributeStack
.Erase(node
);
7922 SetDefaultStyle(*attr
);
7929 bool wxRichTextBuffer::EndAllStyles()
7931 while (m_attributeStack
.GetCount() != 0)
7936 /// Clear the style stack
7937 void wxRichTextBuffer::ClearStyleStack()
7939 for (wxList::compatibility_iterator node
= m_attributeStack
.GetFirst(); node
; node
= node
->GetNext())
7940 delete (wxRichTextAttr
*) node
->GetData();
7941 m_attributeStack
.Clear();
7944 /// Begin using bold
7945 bool wxRichTextBuffer::BeginBold()
7947 wxRichTextAttr attr
;
7948 attr
.SetFontWeight(wxFONTWEIGHT_BOLD
);
7950 return BeginStyle(attr
);
7953 /// Begin using italic
7954 bool wxRichTextBuffer::BeginItalic()
7956 wxRichTextAttr attr
;
7957 attr
.SetFontStyle(wxFONTSTYLE_ITALIC
);
7959 return BeginStyle(attr
);
7962 /// Begin using underline
7963 bool wxRichTextBuffer::BeginUnderline()
7965 wxRichTextAttr attr
;
7966 attr
.SetFontUnderlined(true);
7968 return BeginStyle(attr
);
7971 /// Begin using point size
7972 bool wxRichTextBuffer::BeginFontSize(int pointSize
)
7974 wxRichTextAttr attr
;
7975 attr
.SetFontSize(pointSize
);
7977 return BeginStyle(attr
);
7980 /// Begin using this font
7981 bool wxRichTextBuffer::BeginFont(const wxFont
& font
)
7983 wxRichTextAttr attr
;
7986 return BeginStyle(attr
);
7989 /// Begin using this colour
7990 bool wxRichTextBuffer::BeginTextColour(const wxColour
& colour
)
7992 wxRichTextAttr attr
;
7993 attr
.SetFlags(wxTEXT_ATTR_TEXT_COLOUR
);
7994 attr
.SetTextColour(colour
);
7996 return BeginStyle(attr
);
7999 /// Begin using alignment
8000 bool wxRichTextBuffer::BeginAlignment(wxTextAttrAlignment alignment
)
8002 wxRichTextAttr attr
;
8003 attr
.SetFlags(wxTEXT_ATTR_ALIGNMENT
);
8004 attr
.SetAlignment(alignment
);
8006 return BeginStyle(attr
);
8009 /// Begin left indent
8010 bool wxRichTextBuffer::BeginLeftIndent(int leftIndent
, int leftSubIndent
)
8012 wxRichTextAttr attr
;
8013 attr
.SetFlags(wxTEXT_ATTR_LEFT_INDENT
);
8014 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
8016 return BeginStyle(attr
);
8019 /// Begin right indent
8020 bool wxRichTextBuffer::BeginRightIndent(int rightIndent
)
8022 wxRichTextAttr attr
;
8023 attr
.SetFlags(wxTEXT_ATTR_RIGHT_INDENT
);
8024 attr
.SetRightIndent(rightIndent
);
8026 return BeginStyle(attr
);
8029 /// Begin paragraph spacing
8030 bool wxRichTextBuffer::BeginParagraphSpacing(int before
, int after
)
8034 flags
|= wxTEXT_ATTR_PARA_SPACING_BEFORE
;
8036 flags
|= wxTEXT_ATTR_PARA_SPACING_AFTER
;
8038 wxRichTextAttr attr
;
8039 attr
.SetFlags(flags
);
8040 attr
.SetParagraphSpacingBefore(before
);
8041 attr
.SetParagraphSpacingAfter(after
);
8043 return BeginStyle(attr
);
8046 /// Begin line spacing
8047 bool wxRichTextBuffer::BeginLineSpacing(int lineSpacing
)
8049 wxRichTextAttr attr
;
8050 attr
.SetFlags(wxTEXT_ATTR_LINE_SPACING
);
8051 attr
.SetLineSpacing(lineSpacing
);
8053 return BeginStyle(attr
);
8056 /// Begin numbered bullet
8057 bool wxRichTextBuffer::BeginNumberedBullet(int bulletNumber
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
8059 wxRichTextAttr attr
;
8060 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
8061 attr
.SetBulletStyle(bulletStyle
);
8062 attr
.SetBulletNumber(bulletNumber
);
8063 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
8065 return BeginStyle(attr
);
8068 /// Begin symbol bullet
8069 bool wxRichTextBuffer::BeginSymbolBullet(const wxString
& symbol
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
8071 wxRichTextAttr attr
;
8072 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
8073 attr
.SetBulletStyle(bulletStyle
);
8074 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
8075 attr
.SetBulletText(symbol
);
8077 return BeginStyle(attr
);
8080 /// Begin standard bullet
8081 bool wxRichTextBuffer::BeginStandardBullet(const wxString
& bulletName
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
8083 wxRichTextAttr attr
;
8084 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
8085 attr
.SetBulletStyle(bulletStyle
);
8086 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
8087 attr
.SetBulletName(bulletName
);
8089 return BeginStyle(attr
);
8092 /// Begin named character style
8093 bool wxRichTextBuffer::BeginCharacterStyle(const wxString
& characterStyle
)
8095 if (GetStyleSheet())
8097 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->FindCharacterStyle(characterStyle
);
8100 wxRichTextAttr attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
8101 return BeginStyle(attr
);
8107 /// Begin named paragraph style
8108 bool wxRichTextBuffer::BeginParagraphStyle(const wxString
& paragraphStyle
)
8110 if (GetStyleSheet())
8112 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(paragraphStyle
);
8115 wxRichTextAttr attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
8116 return BeginStyle(attr
);
8122 /// Begin named list style
8123 bool wxRichTextBuffer::BeginListStyle(const wxString
& listStyle
, int level
, int number
)
8125 if (GetStyleSheet())
8127 wxRichTextListStyleDefinition
* def
= GetStyleSheet()->FindListStyle(listStyle
);
8130 wxRichTextAttr
attr(def
->GetCombinedStyleForLevel(level
));
8132 attr
.SetBulletNumber(number
);
8134 return BeginStyle(attr
);
8141 bool wxRichTextBuffer::BeginURL(const wxString
& url
, const wxString
& characterStyle
)
8143 wxRichTextAttr attr
;
8145 if (!characterStyle
.IsEmpty() && GetStyleSheet())
8147 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->FindCharacterStyle(characterStyle
);
8150 attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
8155 return BeginStyle(attr
);
8158 /// Adds a handler to the end
8159 void wxRichTextBuffer::AddHandler(wxRichTextFileHandler
*handler
)
8161 sm_handlers
.Append(handler
);
8164 /// Inserts a handler at the front
8165 void wxRichTextBuffer::InsertHandler(wxRichTextFileHandler
*handler
)
8167 sm_handlers
.Insert( handler
);
8170 /// Removes a handler
8171 bool wxRichTextBuffer::RemoveHandler(const wxString
& name
)
8173 wxRichTextFileHandler
*handler
= FindHandler(name
);
8176 sm_handlers
.DeleteObject(handler
);
8184 /// Finds a handler by filename or, if supplied, type
8185 wxRichTextFileHandler
*wxRichTextBuffer::FindHandlerFilenameOrType(const wxString
& filename
,
8186 wxRichTextFileType imageType
)
8188 if (imageType
!= wxRICHTEXT_TYPE_ANY
)
8189 return FindHandler(imageType
);
8190 else if (!filename
.IsEmpty())
8192 wxString path
, file
, ext
;
8193 wxFileName::SplitPath(filename
, & path
, & file
, & ext
);
8194 return FindHandler(ext
, imageType
);
8201 /// Finds a handler by name
8202 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(const wxString
& name
)
8204 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
8207 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
8208 if (handler
->GetName().Lower() == name
.Lower()) return handler
;
8210 node
= node
->GetNext();
8215 /// Finds a handler by extension and type
8216 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(const wxString
& extension
, wxRichTextFileType type
)
8218 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
8221 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
8222 if ( handler
->GetExtension().Lower() == extension
.Lower() &&
8223 (type
== wxRICHTEXT_TYPE_ANY
|| handler
->GetType() == type
) )
8225 node
= node
->GetNext();
8230 /// Finds a handler by type
8231 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(wxRichTextFileType type
)
8233 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
8236 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
8237 if (handler
->GetType() == type
) return handler
;
8238 node
= node
->GetNext();
8243 void wxRichTextBuffer::InitStandardHandlers()
8245 if (!FindHandler(wxRICHTEXT_TYPE_TEXT
))
8246 AddHandler(new wxRichTextPlainTextHandler
);
8249 void wxRichTextBuffer::CleanUpHandlers()
8251 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
8254 wxRichTextFileHandler
* handler
= (wxRichTextFileHandler
*)node
->GetData();
8255 wxList::compatibility_iterator next
= node
->GetNext();
8260 sm_handlers
.Clear();
8263 wxString
wxRichTextBuffer::GetExtWildcard(bool combine
, bool save
, wxArrayInt
* types
)
8270 wxList::compatibility_iterator node
= GetHandlers().GetFirst();
8274 wxRichTextFileHandler
* handler
= (wxRichTextFileHandler
*) node
->GetData();
8275 if (handler
->IsVisible() && ((save
&& handler
->CanSave()) || (!save
&& handler
->CanLoad())))
8280 wildcard
+= wxT(";");
8281 wildcard
+= wxT("*.") + handler
->GetExtension();
8286 wildcard
+= wxT("|");
8287 wildcard
+= handler
->GetName();
8288 wildcard
+= wxT(" ");
8289 wildcard
+= _("files");
8290 wildcard
+= wxT(" (*.");
8291 wildcard
+= handler
->GetExtension();
8292 wildcard
+= wxT(")|*.");
8293 wildcard
+= handler
->GetExtension();
8295 types
->Add(handler
->GetType());
8300 node
= node
->GetNext();
8304 wildcard
= wxT("(") + wildcard
+ wxT(")|") + wildcard
;
8309 bool wxRichTextBuffer::LoadFile(const wxString
& filename
, wxRichTextFileType type
)
8311 wxRichTextFileHandler
* handler
= FindHandlerFilenameOrType(filename
, type
);
8314 SetDefaultStyle(wxRichTextAttr());
8315 handler
->SetFlags(GetHandlerFlags());
8316 bool success
= handler
->LoadFile(this, filename
);
8317 Invalidate(wxRICHTEXT_ALL
);
8325 bool wxRichTextBuffer::SaveFile(const wxString
& filename
, wxRichTextFileType type
)
8327 wxRichTextFileHandler
* handler
= FindHandlerFilenameOrType(filename
, type
);
8330 handler
->SetFlags(GetHandlerFlags());
8331 return handler
->SaveFile(this, filename
);
8337 /// Load from a stream
8338 bool wxRichTextBuffer::LoadFile(wxInputStream
& stream
, wxRichTextFileType type
)
8340 wxRichTextFileHandler
* handler
= FindHandler(type
);
8343 SetDefaultStyle(wxRichTextAttr());
8344 handler
->SetFlags(GetHandlerFlags());
8345 bool success
= handler
->LoadFile(this, stream
);
8346 Invalidate(wxRICHTEXT_ALL
);
8353 /// Save to a stream
8354 bool wxRichTextBuffer::SaveFile(wxOutputStream
& stream
, wxRichTextFileType type
)
8356 wxRichTextFileHandler
* handler
= FindHandler(type
);
8359 handler
->SetFlags(GetHandlerFlags());
8360 return handler
->SaveFile(this, stream
);
8366 /// Copy the range to the clipboard
8367 bool wxRichTextBuffer::CopyToClipboard(const wxRichTextRange
& range
)
8369 bool success
= false;
8370 wxRichTextParagraphLayoutBox
* container
= this;
8371 if (GetRichTextCtrl())
8372 container
= GetRichTextCtrl()->GetFocusObject();
8374 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
8376 if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open())
8378 wxTheClipboard
->Clear();
8380 // Add composite object
8382 wxDataObjectComposite
* compositeObject
= new wxDataObjectComposite();
8385 wxString text
= container
->GetTextForRange(range
);
8388 text
= wxTextFile::Translate(text
, wxTextFileType_Dos
);
8391 compositeObject
->Add(new wxTextDataObject(text
), false /* not preferred */);
8394 // Add rich text buffer data object. This needs the XML handler to be present.
8396 if (FindHandler(wxRICHTEXT_TYPE_XML
))
8398 wxRichTextBuffer
* richTextBuf
= new wxRichTextBuffer
;
8399 container
->CopyFragment(range
, *richTextBuf
);
8401 compositeObject
->Add(new wxRichTextBufferDataObject(richTextBuf
), true /* preferred */);
8404 if (wxTheClipboard
->SetData(compositeObject
))
8407 wxTheClipboard
->Close();
8416 /// Paste the clipboard content to the buffer
8417 bool wxRichTextBuffer::PasteFromClipboard(long position
)
8419 bool success
= false;
8420 wxRichTextParagraphLayoutBox
* container
= this;
8421 if (GetRichTextCtrl())
8422 container
= GetRichTextCtrl()->GetFocusObject();
8424 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
8425 if (CanPasteFromClipboard())
8427 if (wxTheClipboard
->Open())
8429 if (wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())))
8431 wxRichTextBufferDataObject data
;
8432 wxTheClipboard
->GetData(data
);
8433 wxRichTextBuffer
* richTextBuffer
= data
.GetRichTextBuffer();
8436 container
->InsertParagraphsWithUndo(this, position
+1, *richTextBuffer
, GetRichTextCtrl(), 0);
8437 if (GetRichTextCtrl())
8438 GetRichTextCtrl()->ShowPosition(position
+ richTextBuffer
->GetOwnRange().GetEnd());
8439 delete richTextBuffer
;
8442 else if (wxTheClipboard
->IsSupported(wxDF_TEXT
)
8444 || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
)
8448 wxTextDataObject data
;
8449 wxTheClipboard
->GetData(data
);
8450 wxString
text(data
.GetText());
8453 text2
.Alloc(text
.Length()+1);
8455 for (i
= 0; i
< text
.Length(); i
++)
8457 wxChar ch
= text
[i
];
8458 if (ch
!= wxT('\r'))
8462 wxString text2
= text
;
8464 container
->InsertTextWithUndo(this, position
+1, text2
, GetRichTextCtrl(), wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
);
8466 if (GetRichTextCtrl())
8467 GetRichTextCtrl()->ShowPosition(position
+ text2
.Length());
8471 else if (wxTheClipboard
->IsSupported(wxDF_BITMAP
))
8473 wxBitmapDataObject data
;
8474 wxTheClipboard
->GetData(data
);
8475 wxBitmap
bitmap(data
.GetBitmap());
8476 wxImage
image(bitmap
.ConvertToImage());
8478 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, this, container
, GetRichTextCtrl(), false);
8480 action
->GetNewParagraphs().AddImage(image
);
8482 if (action
->GetNewParagraphs().GetChildCount() == 1)
8483 action
->GetNewParagraphs().SetPartialParagraph(true);
8485 action
->SetPosition(position
+1);
8487 // Set the range we'll need to delete in Undo
8488 action
->SetRange(wxRichTextRange(position
+1, position
+1));
8490 SubmitAction(action
);
8494 wxTheClipboard
->Close();
8498 wxUnusedVar(position
);
8503 /// Can we paste from the clipboard?
8504 bool wxRichTextBuffer::CanPasteFromClipboard() const
8506 bool canPaste
= false;
8507 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
8508 if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open())
8510 if (wxTheClipboard
->IsSupported(wxDF_TEXT
)
8512 || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
)
8514 || wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())) ||
8515 wxTheClipboard
->IsSupported(wxDF_BITMAP
))
8519 wxTheClipboard
->Close();
8525 /// Dumps contents of buffer for debugging purposes
8526 void wxRichTextBuffer::Dump()
8530 wxStringOutputStream
stream(& text
);
8531 wxTextOutputStream
textStream(stream
);
8538 /// Add an event handler
8539 bool wxRichTextBuffer::AddEventHandler(wxEvtHandler
* handler
)
8541 m_eventHandlers
.Append(handler
);
8545 /// Remove an event handler
8546 bool wxRichTextBuffer::RemoveEventHandler(wxEvtHandler
* handler
, bool deleteHandler
)
8548 wxList::compatibility_iterator node
= m_eventHandlers
.Find(handler
);
8551 m_eventHandlers
.Erase(node
);
8561 /// Clear event handlers
8562 void wxRichTextBuffer::ClearEventHandlers()
8564 m_eventHandlers
.Clear();
8567 /// Send event to event handlers. If sendToAll is true, will send to all event handlers,
8568 /// otherwise will stop at the first successful one.
8569 bool wxRichTextBuffer::SendEvent(wxEvent
& event
, bool sendToAll
)
8571 bool success
= false;
8572 for (wxList::compatibility_iterator node
= m_eventHandlers
.GetFirst(); node
; node
= node
->GetNext())
8574 wxEvtHandler
* handler
= (wxEvtHandler
*) node
->GetData();
8575 if (handler
->ProcessEvent(event
))
8585 /// Set style sheet and notify of the change
8586 bool wxRichTextBuffer::SetStyleSheetAndNotify(wxRichTextStyleSheet
* sheet
)
8588 wxRichTextStyleSheet
* oldSheet
= GetStyleSheet();
8590 wxWindowID winid
= wxID_ANY
;
8591 if (GetRichTextCtrl())
8592 winid
= GetRichTextCtrl()->GetId();
8594 wxRichTextEvent
event(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACING
, winid
);
8595 event
.SetEventObject(GetRichTextCtrl());
8596 event
.SetContainer(GetRichTextCtrl()->GetFocusObject());
8597 event
.SetOldStyleSheet(oldSheet
);
8598 event
.SetNewStyleSheet(sheet
);
8601 if (SendEvent(event
) && !event
.IsAllowed())
8603 if (sheet
!= oldSheet
)
8609 if (oldSheet
&& oldSheet
!= sheet
)
8612 SetStyleSheet(sheet
);
8614 event
.SetEventType(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACED
);
8615 event
.SetOldStyleSheet(NULL
);
8618 return SendEvent(event
);
8621 /// Set renderer, deleting old one
8622 void wxRichTextBuffer::SetRenderer(wxRichTextRenderer
* renderer
)
8626 sm_renderer
= renderer
;
8629 /// Hit-testing: returns a flag indicating hit test details, plus
8630 /// information about position
8631 int wxRichTextBuffer::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
8633 int ret
= wxRichTextParagraphLayoutBox::HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
8634 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
8640 textPosition
= m_ownRange
.GetEnd()-1;
8643 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
;
8647 void wxRichTextBuffer::SetFontScale(double fontScale
)
8649 m_fontScale
= fontScale
;
8650 m_fontTable
.SetFontScale(fontScale
);
8653 void wxRichTextBuffer::SetDimensionScale(double dimScale
)
8655 m_dimensionScale
= dimScale
;
8658 bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxRichTextAttr
& bulletAttr
, const wxRect
& rect
)
8660 if (bulletAttr
.GetTextColour().IsOk())
8662 wxCheckSetPen(dc
, wxPen(bulletAttr
.GetTextColour()));
8663 wxCheckSetBrush(dc
, wxBrush(bulletAttr
.GetTextColour()));
8667 wxCheckSetPen(dc
, *wxBLACK_PEN
);
8668 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
8672 if (bulletAttr
.HasFont())
8674 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(bulletAttr
);
8677 font
= (*wxNORMAL_FONT
);
8679 wxCheckSetFont(dc
, font
);
8681 int charHeight
= dc
.GetCharHeight();
8683 int bulletWidth
= (int) (((float) charHeight
) * wxRichTextBuffer::GetBulletProportion());
8684 int bulletHeight
= bulletWidth
;
8688 // Calculate the top position of the character (as opposed to the whole line height)
8689 int y
= rect
.y
+ (rect
.height
- charHeight
);
8691 // Calculate where the bullet should be positioned
8692 y
= y
+ (charHeight
+1)/2 - (bulletHeight
+1)/2;
8694 // The margin between a bullet and text.
8695 int margin
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer::GetBulletRightMargin());
8697 if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
)
8698 x
= rect
.x
+ rect
.width
- bulletWidth
- margin
;
8699 else if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
)
8700 x
= x
+ (rect
.width
)/2 - bulletWidth
/2;
8702 if (bulletAttr
.GetBulletName() == wxT("standard/square"))
8704 dc
.DrawRectangle(x
, y
, bulletWidth
, bulletHeight
);
8706 else if (bulletAttr
.GetBulletName() == wxT("standard/diamond"))
8709 pts
[0].x
= x
; pts
[0].y
= y
+ bulletHeight
/2;
8710 pts
[1].x
= x
+ bulletWidth
/2; pts
[1].y
= y
;
8711 pts
[2].x
= x
+ bulletWidth
; pts
[2].y
= y
+ bulletHeight
/2;
8712 pts
[3].x
= x
+ bulletWidth
/2; pts
[3].y
= y
+ bulletHeight
;
8714 dc
.DrawPolygon(4, pts
);
8716 else if (bulletAttr
.GetBulletName() == wxT("standard/triangle"))
8719 pts
[0].x
= x
; pts
[0].y
= y
;
8720 pts
[1].x
= x
+ bulletWidth
; pts
[1].y
= y
+ bulletHeight
/2;
8721 pts
[2].x
= x
; pts
[2].y
= y
+ bulletHeight
;
8723 dc
.DrawPolygon(3, pts
);
8725 else if (bulletAttr
.GetBulletName() == wxT("standard/circle-outline"))
8727 wxCheckSetBrush(dc
, *wxTRANSPARENT_BRUSH
);
8728 dc
.DrawEllipse(x
, y
, bulletWidth
, bulletHeight
);
8730 else // "standard/circle", and catch-all
8732 dc
.DrawEllipse(x
, y
, bulletWidth
, bulletHeight
);
8738 bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxRichTextAttr
& attr
, const wxRect
& rect
, const wxString
& text
)
8743 if ((attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
) && !attr
.GetBulletFont().IsEmpty() && attr
.HasFont())
8745 wxRichTextAttr fontAttr
;
8746 if (attr
.HasFontPixelSize())
8747 fontAttr
.SetFontPixelSize(attr
.GetFontSize());
8749 fontAttr
.SetFontPointSize(attr
.GetFontSize());
8750 fontAttr
.SetFontStyle(attr
.GetFontStyle());
8751 fontAttr
.SetFontWeight(attr
.GetFontWeight());
8752 fontAttr
.SetFontUnderlined(attr
.GetFontUnderlined());
8753 fontAttr
.SetFontFaceName(attr
.GetBulletFont());
8754 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(fontAttr
);
8756 else if (attr
.HasFont())
8757 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(attr
);
8759 font
= (*wxNORMAL_FONT
);
8761 wxCheckSetFont(dc
, font
);
8763 if (attr
.GetTextColour().IsOk())
8764 dc
.SetTextForeground(attr
.GetTextColour());
8766 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
8768 int charHeight
= dc
.GetCharHeight();
8770 dc
.GetTextExtent(text
, & tw
, & th
);
8774 // Calculate the top position of the character (as opposed to the whole line height)
8775 int y
= rect
.y
+ (rect
.height
- charHeight
);
8777 // The margin between a bullet and text.
8778 int margin
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer::GetBulletRightMargin());
8780 if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
)
8781 x
= (rect
.x
+ rect
.width
) - tw
- margin
;
8782 else if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
)
8783 x
= x
+ (rect
.width
)/2 - tw
/2;
8785 dc
.DrawText(text
, x
, y
);
8793 bool wxRichTextStdRenderer::DrawBitmapBullet(wxRichTextParagraph
* WXUNUSED(paragraph
), wxDC
& WXUNUSED(dc
), const wxRichTextAttr
& WXUNUSED(attr
), const wxRect
& WXUNUSED(rect
))
8795 // Currently unimplemented. The intention is to store bitmaps by name in a media store associated
8796 // with the buffer. The store will allow retrieval from memory, disk or other means.
8800 /// Enumerate the standard bullet names currently supported
8801 bool wxRichTextStdRenderer::EnumerateStandardBulletNames(wxArrayString
& bulletNames
)
8803 bulletNames
.Add(wxTRANSLATE("standard/circle"));
8804 bulletNames
.Add(wxTRANSLATE("standard/circle-outline"));
8805 bulletNames
.Add(wxTRANSLATE("standard/square"));
8806 bulletNames
.Add(wxTRANSLATE("standard/diamond"));
8807 bulletNames
.Add(wxTRANSLATE("standard/triangle"));
8816 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBox
, wxRichTextParagraphLayoutBox
)
8818 wxRichTextBox::wxRichTextBox(wxRichTextObject
* parent
):
8819 wxRichTextParagraphLayoutBox(parent
)
8824 bool wxRichTextBox::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8829 // TODO: if the active object in the control, draw an indication.
8830 // We need to add the concept of active object, and not just focus object,
8831 // so we can apply commands (properties, delete, ...) to objects such as text boxes and images.
8832 // Ultimately we would like to be able to interactively resize an active object
8833 // using drag handles.
8834 return wxRichTextParagraphLayoutBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8838 void wxRichTextBox::Copy(const wxRichTextBox
& obj
)
8840 wxRichTextParagraphLayoutBox::Copy(obj
);
8843 // Edit properties via a GUI
8844 bool wxRichTextBox::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8846 wxRichTextObjectPropertiesDialog
boxDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Box Properties"));
8847 boxDlg
.SetAttributes(GetAttributes());
8849 if (boxDlg
.ShowModal() == wxID_OK
)
8851 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
8852 // indeterminate in the object.
8853 boxDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
8864 IMPLEMENT_DYNAMIC_CLASS(wxRichTextField
, wxRichTextParagraphLayoutBox
)
8866 wxRichTextField::wxRichTextField(const wxString
& fieldType
, wxRichTextObject
* parent
):
8867 wxRichTextParagraphLayoutBox(parent
)
8869 SetFieldType(fieldType
);
8873 bool wxRichTextField::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8878 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8879 if (fieldType
&& fieldType
->Draw(this, dc
, context
, range
, selection
, rect
, descent
, style
))
8882 // Fallback; but don't draw guidelines.
8883 style
&= ~wxRICHTEXT_DRAW_GUIDELINES
;
8884 return wxRichTextParagraphLayoutBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8887 bool wxRichTextField::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
)
8889 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8890 if (fieldType
&& fieldType
->Layout(this, dc
, context
, rect
, parentRect
, style
))
8894 return wxRichTextParagraphLayoutBox::Layout(dc
, context
, rect
, parentRect
, style
);
8897 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
8899 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8901 return fieldType
->GetRangeSize((wxRichTextField
*) this, range
, size
, descent
, dc
, context
, flags
, position
, parentSize
, partialExtents
);
8903 return wxRichTextParagraphLayoutBox::GetRangeSize(range
, size
, descent
, dc
, context
, flags
, position
, parentSize
, partialExtents
);
8907 void wxRichTextField::CalculateRange(long start
, long& end
)
8910 wxRichTextParagraphLayoutBox::CalculateRange(start
, end
);
8912 wxRichTextObject::CalculateRange(start
, end
);
8916 void wxRichTextField::Copy(const wxRichTextField
& obj
)
8918 wxRichTextParagraphLayoutBox::Copy(obj
);
8920 UpdateField(GetBuffer());
8923 // Edit properties via a GUI
8924 bool wxRichTextField::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8926 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8928 return fieldType
->EditProperties(this, parent
, buffer
);
8933 bool wxRichTextField::CanEditProperties() const
8935 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8937 return fieldType
->CanEditProperties((wxRichTextField
*) this);
8942 wxString
wxRichTextField::GetPropertiesMenuLabel() const
8944 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8946 return fieldType
->GetPropertiesMenuLabel((wxRichTextField
*) this);
8948 return wxEmptyString
;
8951 bool wxRichTextField::UpdateField(wxRichTextBuffer
* buffer
)
8953 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8955 return fieldType
->UpdateField(buffer
, (wxRichTextField
*) this);
8960 bool wxRichTextField::IsTopLevel() const
8962 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8964 return fieldType
->IsTopLevel((wxRichTextField
*) this);
8969 IMPLEMENT_CLASS(wxRichTextFieldType
, wxObject
)
8971 IMPLEMENT_CLASS(wxRichTextFieldTypeStandard
, wxRichTextFieldType
)
8973 wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString
& name
, const wxString
& label
, int displayStyle
)
8979 SetDisplayStyle(displayStyle
);
8982 wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString
& name
, const wxBitmap
& bitmap
, int displayStyle
)
8988 SetDisplayStyle(displayStyle
);
8991 void wxRichTextFieldTypeStandard::Init()
8993 m_displayStyle
= wxRICHTEXT_FIELD_STYLE_RECTANGLE
;
8994 m_font
= wxFont(6, wxFONTFAMILY_SWISS
, wxFONTSTYLE_NORMAL
, wxFONTWEIGHT_NORMAL
);
8995 m_textColour
= *wxWHITE
;
8996 m_borderColour
= *wxBLACK
;
8997 m_backgroundColour
= *wxBLACK
;
8998 m_verticalPadding
= 1;
8999 m_horizontalPadding
= 3;
9000 m_horizontalMargin
= 2;
9001 m_verticalMargin
= 0;
9004 void wxRichTextFieldTypeStandard::Copy(const wxRichTextFieldTypeStandard
& field
)
9006 wxRichTextFieldType::Copy(field
);
9008 m_label
= field
.m_label
;
9009 m_displayStyle
= field
.m_displayStyle
;
9010 m_font
= field
.m_font
;
9011 m_textColour
= field
.m_textColour
;
9012 m_borderColour
= field
.m_borderColour
;
9013 m_backgroundColour
= field
.m_backgroundColour
;
9014 m_verticalPadding
= field
.m_verticalPadding
;
9015 m_horizontalPadding
= field
.m_horizontalPadding
;
9016 m_horizontalMargin
= field
.m_horizontalMargin
;
9017 m_bitmap
= field
.m_bitmap
;
9020 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
))
9022 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_COMPOSITE
)
9023 return false; // USe default composite drawing
9024 else // if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_RECTANGLE || m_displayStyle == wxRICHTEXT_FIELD_STYLE_NOBORDER)
9028 wxPen
borderPen(m_borderColour
, 1, wxSOLID
);
9029 wxBrush
backgroundBrush(m_backgroundColour
);
9030 wxColour
textColour(m_textColour
);
9032 if (selection
.WithinSelection(obj
->GetRange().GetStart(), obj
))
9034 wxColour
highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
));
9035 wxColour
highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
));
9037 borderPen
= wxPen(highlightTextColour
, 1, wxSOLID
);
9038 backgroundBrush
= wxBrush(highlightColour
);
9040 wxCheckSetBrush(dc
, backgroundBrush
);
9041 wxCheckSetPen(dc
, wxPen(highlightColour
, 1, wxSOLID
));
9042 dc
.DrawRectangle(rect
);
9045 if (m_displayStyle
!= wxRICHTEXT_FIELD_STYLE_NO_BORDER
)
9048 // objectRect is the area where the content is drawn, after margins around it have been taken into account
9049 wxRect objectRect
= wxRect(wxPoint(rect
.x
+ m_horizontalMargin
, rect
.y
+ wxMax(0, rect
.height
- descent
- obj
->GetCachedSize().y
)),
9050 wxSize(obj
->GetCachedSize().x
- 2*m_horizontalMargin
- borderSize
, obj
->GetCachedSize().y
));
9052 // clientArea is where the text is actually written
9053 wxRect clientArea
= objectRect
;
9055 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_RECTANGLE
)
9057 dc
.SetPen(borderPen
);
9058 dc
.SetBrush(backgroundBrush
);
9059 dc
.DrawRoundedRectangle(objectRect
, 4.0);
9061 else if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_START_TAG
)
9063 int arrowLength
= objectRect
.height
/2;
9064 clientArea
.width
-= (arrowLength
- m_horizontalPadding
);
9067 pts
[0].x
= objectRect
.x
; pts
[0].y
= objectRect
.y
;
9068 pts
[1].x
= objectRect
.x
+ objectRect
.width
- arrowLength
; pts
[1].y
= objectRect
.y
;
9069 pts
[2].x
= objectRect
.x
+ objectRect
.width
; pts
[2].y
= objectRect
.y
+ (objectRect
.height
/2);
9070 pts
[3].x
= objectRect
.x
+ objectRect
.width
- arrowLength
; pts
[3].y
= objectRect
.y
+ objectRect
.height
;
9071 pts
[4].x
= objectRect
.x
; pts
[4].y
= objectRect
.y
+ objectRect
.height
;
9072 dc
.SetPen(borderPen
);
9073 dc
.SetBrush(backgroundBrush
);
9074 dc
.DrawPolygon(5, pts
);
9076 else if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_END_TAG
)
9078 int arrowLength
= objectRect
.height
/2;
9079 clientArea
.width
-= (arrowLength
- m_horizontalPadding
);
9080 clientArea
.x
+= (arrowLength
- m_horizontalPadding
);
9083 pts
[0].x
= objectRect
.x
+ objectRect
.width
; pts
[0].y
= objectRect
.y
;
9084 pts
[1].x
= objectRect
.x
+ arrowLength
; pts
[1].y
= objectRect
.y
;
9085 pts
[2].x
= objectRect
.x
; pts
[2].y
= objectRect
.y
+ (objectRect
.height
/2);
9086 pts
[3].x
= objectRect
.x
+ arrowLength
; pts
[3].y
= objectRect
.y
+ objectRect
.height
;
9087 pts
[4].x
= objectRect
.x
+ objectRect
.width
; pts
[4].y
= objectRect
.y
+ objectRect
.height
;
9088 dc
.SetPen(borderPen
);
9089 dc
.SetBrush(backgroundBrush
);
9090 dc
.DrawPolygon(5, pts
);
9093 if (m_bitmap
.IsOk())
9095 int x
= clientArea
.x
+ (clientArea
.width
- m_bitmap
.GetWidth())/2;
9096 int y
= clientArea
.y
+ m_verticalPadding
;
9097 dc
.DrawBitmap(m_bitmap
, x
, y
, true);
9099 if (selection
.WithinSelection(obj
->GetRange().GetStart(), obj
))
9101 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
9102 wxCheckSetPen(dc
, *wxBLACK_PEN
);
9103 dc
.SetLogicalFunction(wxINVERT
);
9104 dc
.DrawRectangle(wxRect(x
, y
, m_bitmap
.GetWidth(), m_bitmap
.GetHeight()));
9105 dc
.SetLogicalFunction(wxCOPY
);
9110 wxString
label(m_label
);
9111 if (label
.IsEmpty())
9113 int w
, h
, maxDescent
;
9115 dc
.GetTextExtent(m_label
, & w
, &h
, & maxDescent
);
9116 dc
.SetTextForeground(textColour
);
9118 int x
= clientArea
.x
+ (clientArea
.width
- w
)/2;
9119 int y
= clientArea
.y
+ (clientArea
.height
- (h
- maxDescent
))/2;
9120 dc
.DrawText(m_label
, x
, y
);
9127 bool wxRichTextFieldTypeStandard::Layout(wxRichTextField
* obj
, wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& WXUNUSED(rect
), const wxRect
& WXUNUSED(parentRect
), int style
)
9129 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_COMPOSITE
)
9130 return false; // USe default composite layout
9132 wxSize size
= GetSize(obj
, dc
, context
, style
);
9133 obj
->SetCachedSize(size
);
9134 obj
->SetMinSize(size
);
9135 obj
->SetMaxSize(size
);
9139 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
9141 if (IsTopLevel(obj
))
9142 return obj
->wxRichTextParagraphLayoutBox::GetRangeSize(range
, size
, descent
, dc
, context
, flags
, position
, parentSize
);
9145 wxSize sz
= GetSize(obj
, dc
, context
, 0);
9149 if (partialExtents
->GetCount() > 0)
9150 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
9153 partialExtents
->Add(lastSize
+ sz
.x
);
9160 wxSize
wxRichTextFieldTypeStandard::GetSize(wxRichTextField
* WXUNUSED(obj
), wxDC
& dc
, wxRichTextDrawingContext
& WXUNUSED(context
), int WXUNUSED(style
)) const
9163 int w
= 0, h
= 0, maxDescent
= 0;
9166 if (m_bitmap
.IsOk())
9168 w
= m_bitmap
.GetWidth();
9169 h
= m_bitmap
.GetHeight();
9171 sz
= wxSize(w
+ m_horizontalMargin
*2, h
+ m_verticalMargin
*2);
9175 wxString
label(m_label
);
9176 if (label
.IsEmpty())
9179 dc
.GetTextExtent(label
, & w
, &h
, & maxDescent
);
9181 sz
= wxSize(w
+ m_horizontalPadding
*2 + m_horizontalMargin
*2, h
+ m_verticalPadding
*2 + m_verticalMargin
*2);
9184 if (m_displayStyle
!= wxRICHTEXT_FIELD_STYLE_NO_BORDER
)
9186 sz
.x
+= borderSize
*2;
9187 sz
.y
+= borderSize
*2;
9190 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_START_TAG
|| m_displayStyle
== wxRICHTEXT_FIELD_STYLE_END_TAG
)
9192 // Add space for the arrow
9193 sz
.x
+= (sz
.y
/2 - m_horizontalPadding
);
9199 IMPLEMENT_DYNAMIC_CLASS(wxRichTextCell
, wxRichTextBox
)
9201 wxRichTextCell::wxRichTextCell(wxRichTextObject
* parent
):
9202 wxRichTextBox(parent
)
9207 bool wxRichTextCell::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
9209 return wxRichTextBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
9213 void wxRichTextCell::Copy(const wxRichTextCell
& obj
)
9215 wxRichTextBox::Copy(obj
);
9218 // Edit properties via a GUI
9219 bool wxRichTextCell::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
9221 // We need to gather common attributes for all selected cells.
9223 wxRichTextTable
* table
= wxDynamicCast(GetParent(), wxRichTextTable
);
9224 bool multipleCells
= false;
9225 wxRichTextAttr attr
;
9227 if (table
&& buffer
&& buffer
->GetRichTextCtrl() && buffer
->GetRichTextCtrl()->GetSelection().IsValid() &&
9228 buffer
->GetRichTextCtrl()->GetSelection().GetContainer() == GetParent())
9230 wxRichTextAttr clashingAttr
, absentAttr
;
9231 const wxRichTextSelection
& sel
= buffer
->GetRichTextCtrl()->GetSelection();
9233 int selectedCellCount
= 0;
9234 for (i
= 0; i
< sel
.GetCount(); i
++)
9236 const wxRichTextRange
& range
= sel
[i
];
9237 wxRichTextCell
* cell
= table
->GetCell(range
.GetStart());
9240 wxRichTextAttr cellStyle
= cell
->GetAttributes();
9242 CollectStyle(attr
, cellStyle
, clashingAttr
, absentAttr
);
9244 selectedCellCount
++;
9247 multipleCells
= selectedCellCount
> 1;
9251 attr
= GetAttributes();
9256 caption
= _("Multiple Cell Properties");
9258 caption
= _("Cell Properties");
9260 wxRichTextObjectPropertiesDialog
cellDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, caption
);
9261 cellDlg
.SetAttributes(attr
);
9263 wxRichTextSizePage
* sizePage
= wxDynamicCast(cellDlg
.FindPage(wxCLASSINFO(wxRichTextSizePage
)), wxRichTextSizePage
);
9266 // We don't want position and floating controls for a cell.
9267 sizePage
->ShowPositionControls(false);
9268 sizePage
->ShowFloatingControls(false);
9271 if (cellDlg
.ShowModal() == wxID_OK
)
9275 const wxRichTextSelection
& sel
= buffer
->GetRichTextCtrl()->GetSelection();
9276 // Apply the style; we interpret indeterminate attributes as 'don't touch this attribute'
9277 // since it may represent clashing attributes across multiple objects.
9278 table
->SetCellStyle(sel
, attr
);
9281 // For a single object, indeterminate attributes set by the user should be reflected in the
9282 // actual object style, so pass the wxRICHTEXT_SETSTYLE_RESET flag to assign
9283 // the style directly instead of applying (which ignores indeterminate attributes,
9284 // leaving them as they were).
9285 cellDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
9292 WX_DEFINE_OBJARRAY(wxRichTextObjectPtrArrayArray
)
9294 IMPLEMENT_DYNAMIC_CLASS(wxRichTextTable
, wxRichTextBox
)
9296 wxRichTextTable::wxRichTextTable(wxRichTextObject
* parent
): wxRichTextBox(parent
)
9302 // Draws the object.
9303 bool wxRichTextTable::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
9305 return wxRichTextBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
9308 WX_DECLARE_OBJARRAY(wxRect
, wxRichTextRectArray
);
9309 WX_DEFINE_OBJARRAY(wxRichTextRectArray
);
9311 // Lays the object out. rect is the space available for layout. Often it will
9312 // be the specified overall space for this object, if trying to constrain
9313 // layout to a particular size, or it could be the total space available in the
9314 // parent. rect is the overall size, so we must subtract margins and padding.
9315 // to get the actual available space.
9316 bool wxRichTextTable::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& WXUNUSED(parentRect
), int style
)
9318 SetPosition(rect
.GetPosition());
9320 // TODO: the meaty bit. Calculate sizes of all cells and rows. Try to use
9321 // minimum size if within alloted size, then divide up remaining size
9322 // between rows/cols.
9325 wxRichTextBuffer
* buffer
= GetBuffer();
9326 if (buffer
) scale
= buffer
->GetScale();
9328 wxRect availableSpace
= GetAvailableContentArea(dc
, context
, rect
);
9329 wxTextAttrDimensionConverter
converter(dc
, scale
, availableSpace
.GetSize());
9331 wxRichTextAttr
attr(GetAttributes());
9332 context
.ApplyVirtualAttributes(attr
, this);
9334 // If we have no fixed table size, and assuming we're not pushed for
9335 // space, then we don't have to try to stretch the table to fit the contents.
9336 bool stretchToFitTableWidth
= false;
9338 int tableWidth
= rect
.width
;
9339 if (attr
.GetTextBoxAttr().GetWidth().IsValid())
9341 tableWidth
= converter
.GetPixels(attr
.GetTextBoxAttr().GetWidth());
9343 // Fixed table width, so we do want to stretch columns out if necessary.
9344 stretchToFitTableWidth
= true;
9346 // Shouldn't be able to exceed the size passed to this function
9347 tableWidth
= wxMin(rect
.width
, tableWidth
);
9350 // Get internal padding
9351 int paddingLeft
= 0, paddingTop
= 0;
9352 if (attr
.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
9353 paddingLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetLeft());
9354 if (attr
.GetTextBoxAttr().GetPadding().GetTop().IsValid())
9355 paddingTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetTop());
9357 // Assume that left and top padding are also used for inter-cell padding.
9358 int paddingX
= paddingLeft
;
9359 int paddingY
= paddingTop
;
9361 int totalLeftMargin
= 0, totalRightMargin
= 0, totalTopMargin
= 0, totalBottomMargin
= 0;
9362 GetTotalMargin(dc
, buffer
, attr
, totalLeftMargin
, totalRightMargin
, totalTopMargin
, totalBottomMargin
);
9364 // Internal table width - the area for content
9365 int internalTableWidth
= tableWidth
- totalLeftMargin
- totalRightMargin
;
9367 int rowCount
= m_cells
.GetCount();
9368 if (m_colCount
== 0 || rowCount
== 0)
9370 wxRect
overallRect(rect
.x
, rect
.y
, totalLeftMargin
+ totalRightMargin
, totalTopMargin
+ totalBottomMargin
);
9371 SetCachedSize(overallRect
.GetSize());
9373 // Zero content size
9374 SetMinSize(overallRect
.GetSize());
9375 SetMaxSize(GetMinSize());
9379 // The final calculated widths
9380 wxArrayInt colWidths
;
9381 colWidths
.Add(0, m_colCount
);
9383 wxArrayInt absoluteColWidths
;
9384 absoluteColWidths
.Add(0, m_colCount
);
9386 wxArrayInt percentageColWidths
;
9387 percentageColWidths
.Add(0, m_colCount
);
9388 // wxArrayInt percentageColWidthsSpanning(m_colCount);
9389 // These are only relevant when the first column contains spanning information.
9390 // wxArrayInt columnSpans(m_colCount); // Each contains 1 for non-spanning cell, > 1 for spanning cell.
9391 wxArrayInt maxColWidths
;
9392 maxColWidths
.Add(0, m_colCount
);
9393 wxArrayInt minColWidths
;
9394 minColWidths
.Add(0, m_colCount
);
9396 wxSize
tableSize(tableWidth
, 0);
9400 for (i
= 0; i
< m_colCount
; i
++)
9402 absoluteColWidths
[i
] = 0;
9403 // absoluteColWidthsSpanning[i] = 0;
9404 percentageColWidths
[i
] = -1;
9405 // percentageColWidthsSpanning[i] = -1;
9407 maxColWidths
[i
] = 0;
9408 minColWidths
[i
] = 0;
9409 // columnSpans[i] = 1;
9412 // (0) Determine which cells are visible according to spans
9414 // __________________
9419 // |------------------|
9420 // |__________________| 4
9422 // To calculate cell visibility:
9423 // First find all spanning cells. Build an array of span records with start x, y and end x, y.
9424 // Then for each cell, test whether we're within one of those cells, and unless we're at the start of
9425 // that cell, hide the cell.
9427 // We can also use this array to match the size of spanning cells to the grid. Or just do
9428 // this when we iterate through all cells.
9430 // 0.1: add spanning cells to an array
9431 wxRichTextRectArray rectArray
;
9432 for (j
= 0; j
< m_rowCount
; j
++)
9434 for (i
= 0; i
< m_colCount
; i
++)
9436 wxRichTextBox
* cell
= GetCell(j
, i
);
9437 int colSpan
= 1, rowSpan
= 1;
9438 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9439 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9440 if (cell
->GetProperties().HasProperty(wxT("rowspan")))
9441 rowSpan
= cell
->GetProperties().GetPropertyLong(wxT("rowspan"));
9442 if (colSpan
> 1 || rowSpan
> 1)
9444 rectArray
.Add(wxRect(i
, j
, colSpan
, rowSpan
));
9448 // 0.2: find which cells are subsumed by a spanning cell
9449 for (j
= 0; j
< m_rowCount
; j
++)
9451 for (i
= 0; i
< m_colCount
; i
++)
9453 wxRichTextBox
* cell
= GetCell(j
, i
);
9454 if (rectArray
.GetCount() == 0)
9460 int colSpan
= 1, rowSpan
= 1;
9461 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9462 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9463 if (cell
->GetProperties().HasProperty(wxT("rowspan")))
9464 rowSpan
= cell
->GetProperties().GetPropertyLong(wxT("rowspan"));
9465 if (colSpan
> 1 || rowSpan
> 1)
9467 // Assume all spanning cells are shown
9473 for (k
= 0; k
< (int) rectArray
.GetCount(); k
++)
9475 if (rectArray
[k
].Contains(wxPoint(i
, j
)))
9487 // TODO: find the first spanned cell in each row that spans the most columns and doesn't
9488 // overlap with a spanned cell starting at a previous column position.
9489 // This means we need to keep an array of rects so we can check. However
9490 // it does also mean that some spans simply may not be taken into account
9491 // where there are different spans happening on different rows. In these cases,
9492 // they will simply be as wide as their constituent columns.
9494 // (1) Do an initial layout for all cells to get minimum and maximum size, and get
9495 // the absolute or percentage width of each column.
9497 for (j
= 0; j
< m_rowCount
; j
++)
9499 // First get the overall margins so we can calculate percentage widths based on
9500 // the available content space for all cells on the row
9502 int overallRowContentMargin
= 0;
9503 int visibleCellCount
= 0;
9505 for (i
= 0; i
< m_colCount
; i
++)
9507 wxRichTextBox
* cell
= GetCell(j
, i
);
9508 if (cell
->IsShown())
9510 int cellTotalLeftMargin
= 0, cellTotalRightMargin
= 0, cellTotalTopMargin
= 0, cellTotalBottomMargin
= 0;
9511 GetTotalMargin(dc
, buffer
, cell
->GetAttributes(), cellTotalLeftMargin
, cellTotalRightMargin
, cellTotalTopMargin
, cellTotalBottomMargin
);
9513 overallRowContentMargin
+= (cellTotalLeftMargin
+ cellTotalRightMargin
);
9514 visibleCellCount
++;
9518 // Add in inter-cell padding
9519 overallRowContentMargin
+= ((visibleCellCount
-1) * paddingX
);
9521 int rowContentWidth
= internalTableWidth
- overallRowContentMargin
;
9522 wxSize
rowTableSize(rowContentWidth
, 0);
9523 wxTextAttrDimensionConverter
converter(dc
, scale
, rowTableSize
);
9525 for (i
= 0; i
< m_colCount
; i
++)
9527 wxRichTextBox
* cell
= GetCell(j
, i
);
9528 if (cell
->IsShown())
9531 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9532 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9534 // Lay out cell to find min/max widths
9535 cell
->Invalidate(wxRICHTEXT_ALL
);
9536 cell
->Layout(dc
, context
, availableSpace
, availableSpace
, style
);
9540 int absoluteCellWidth
= -1;
9541 int percentageCellWidth
= -1;
9543 // I think we need to calculate percentages from the internal table size,
9544 // minus the padding between cells which we'll need to calculate from the
9545 // (number of VISIBLE cells - 1)*paddingX. Then percentages that add up to 100%
9546 // will add up to 100%. In CSS, the width specifies the cell's content rect width,
9547 // so if we want to conform to that we'll need to add in the overall cell margins.
9548 // However, this will make it difficult to specify percentages that add up to
9549 // 100% and still fit within the table width.
9550 // Let's say two cells have 50% width. They have 10 pixels of overall margin each.
9551 // The table content rect is 500 pixels and the inter-cell padding is 20 pixels.
9552 // If we're using internal content size for the width, we would calculate the
9553 // the overall cell width for n cells as:
9554 // (500 - 20*(n-1) - overallCellMargin1 - overallCellMargin2 - ...) * percentage / 100
9555 // + thisOverallCellMargin
9556 // = 500 - 20 - 10 - 10) * 0.5 + 10 = 240 pixels overall cell width.
9557 // Adding this back, we get 240 + 240 + 20 = 500 pixels.
9559 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9561 int w
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetWidth());
9562 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
9564 percentageCellWidth
= w
;
9568 absoluteCellWidth
= w
;
9570 // Override absolute width with minimum width if necessary
9571 if (cell
->GetMinSize().x
> 0 && absoluteCellWidth
!=1 && cell
->GetMinSize().x
> absoluteCellWidth
)
9572 absoluteCellWidth
= cell
->GetMinSize().x
;
9575 if (absoluteCellWidth
!= -1)
9577 if (absoluteCellWidth
> absoluteColWidths
[i
])
9578 absoluteColWidths
[i
] = absoluteCellWidth
;
9581 if (percentageCellWidth
!= -1)
9583 if (percentageCellWidth
> percentageColWidths
[i
])
9584 percentageColWidths
[i
] = percentageCellWidth
;
9587 if (colSpan
== 1 && cell
->GetMinSize().x
&& cell
->GetMinSize().x
> minColWidths
[i
])
9588 minColWidths
[i
] = cell
->GetMinSize().x
;
9589 if (colSpan
== 1 && cell
->GetMaxSize().x
&& cell
->GetMaxSize().x
> maxColWidths
[i
])
9590 maxColWidths
[i
] = cell
->GetMaxSize().x
;
9596 // (2) Allocate initial column widths from minimum widths, absolute values and proportions
9597 // TODO: simply merge this into (1).
9598 for (i
= 0; i
< m_colCount
; i
++)
9600 if (absoluteColWidths
[i
] > 0)
9602 colWidths
[i
] = absoluteColWidths
[i
];
9604 else if (percentageColWidths
[i
] > 0)
9606 colWidths
[i
] = percentageColWidths
[i
];
9608 // This is rubbish - we calculated the absolute widths from percentages, so
9609 // we can't do it again here.
9610 //colWidths[i] = (int) (double(percentageColWidths[i]) * double(tableWidth) / 100.0 + 0.5);
9614 // (3) Process absolute or proportional widths of spanning columns,
9615 // now that we know what our fixed column widths are going to be.
9616 // Spanned cells will try to adjust columns so the span will fit.
9617 // Even existing fixed column widths can be expanded if necessary.
9618 // Actually, currently fixed columns widths aren't adjusted; instead,
9619 // the algorithm favours earlier rows and adjusts unspecified column widths
9620 // the first time only. After that, we can't know whether the column has been
9621 // specified explicitly or not. (We could make a note if necessary.)
9622 for (j
= 0; j
< m_rowCount
; j
++)
9624 // First get the overall margins so we can calculate percentage widths based on
9625 // the available content space for all cells on the row
9627 int overallRowContentMargin
= 0;
9628 int visibleCellCount
= 0;
9630 for (i
= 0; i
< m_colCount
; i
++)
9632 wxRichTextBox
* cell
= GetCell(j
, i
);
9633 if (cell
->IsShown())
9635 int cellTotalLeftMargin
= 0, cellTotalRightMargin
= 0, cellTotalTopMargin
= 0, cellTotalBottomMargin
= 0;
9636 GetTotalMargin(dc
, buffer
, cell
->GetAttributes(), cellTotalLeftMargin
, cellTotalRightMargin
, cellTotalTopMargin
, cellTotalBottomMargin
);
9638 overallRowContentMargin
+= (cellTotalLeftMargin
+ cellTotalRightMargin
);
9639 visibleCellCount
++;
9643 // Add in inter-cell padding
9644 overallRowContentMargin
+= ((visibleCellCount
-1) * paddingX
);
9646 int rowContentWidth
= internalTableWidth
- overallRowContentMargin
;
9647 wxSize
rowTableSize(rowContentWidth
, 0);
9648 wxTextAttrDimensionConverter
converter(dc
, scale
, rowTableSize
);
9650 for (i
= 0; i
< m_colCount
; i
++)
9652 wxRichTextBox
* cell
= GetCell(j
, i
);
9653 if (cell
->IsShown())
9656 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9657 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9661 int spans
= wxMin(colSpan
, m_colCount
- i
);
9665 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9667 cellWidth
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetWidth());
9668 // Override absolute width with minimum width if necessary
9669 if (cell
->GetMinSize().x
> 0 && cellWidth
!=1 && cell
->GetMinSize().x
> cellWidth
)
9670 cellWidth
= cell
->GetMinSize().x
;
9674 // Do we want to do this? It's the only chance we get to
9675 // use the cell's min/max sizes, so we need to work out
9676 // how we're going to balance the unspecified spanning cell
9677 // width with the possibility more-constrained constituent cell widths.
9678 // Say there's a tiny bitmap giving it a max width of 10 pixels. We
9679 // don't want to constraint all the spanned columns to fit into this cell.
9680 // OK, let's say that if any of the constituent columns don't fit,
9681 // then we simply stop constraining the columns; instead, we'll just fit the spanning
9682 // cells to the columns later.
9683 cellWidth
= cell
->GetMinSize().x
;
9684 if (cell
->GetMaxSize().x
> cellWidth
)
9685 cellWidth
= cell
->GetMaxSize().x
;
9688 // Subtract the padding between cells
9689 int spanningWidth
= cellWidth
;
9690 spanningWidth
-= paddingX
* (spans
-1);
9692 if (spanningWidth
> 0)
9694 // Now share the spanning width between columns within that span
9695 // TODO: take into account min widths of columns within the span
9696 int spanningWidthLeft
= spanningWidth
;
9697 int stretchColCount
= 0;
9698 for (k
= i
; k
< (i
+spans
); k
++)
9700 if (colWidths
[k
] > 0) // absolute or proportional width has been specified
9701 spanningWidthLeft
-= colWidths
[k
];
9705 // Now divide what's left between the remaining columns
9707 if (stretchColCount
> 0)
9708 colShare
= spanningWidthLeft
/ stretchColCount
;
9709 int colShareRemainder
= spanningWidthLeft
- (colShare
* stretchColCount
);
9711 // If fixed-width columns are currently too big, then we'll later
9712 // stretch the spanned cell to fit.
9714 if (spanningWidthLeft
> 0)
9716 for (k
= i
; k
< (i
+spans
); k
++)
9718 if (colWidths
[k
] <= 0) // absolute or proportional width has not been specified
9720 int newWidth
= colShare
;
9721 if (k
== (i
+spans
-1))
9722 newWidth
+= colShareRemainder
; // ensure all pixels are filled
9723 colWidths
[k
] = newWidth
;
9734 // (4) Next, share any remaining space out between columns that have not yet been calculated.
9735 // TODO: take into account min widths of columns within the span
9736 int tableWidthMinusPadding
= internalTableWidth
- (m_colCount
-1)*paddingX
;
9737 int widthLeft
= tableWidthMinusPadding
;
9738 int stretchColCount
= 0;
9739 for (i
= 0; i
< m_colCount
; i
++)
9741 // TODO: we need to take into account min widths.
9742 // Subtract min width from width left, then
9743 // add the colShare to the min width
9744 if (colWidths
[i
] > 0) // absolute or proportional width has been specified
9745 widthLeft
-= colWidths
[i
];
9748 if (minColWidths
[i
] > 0)
9749 widthLeft
-= minColWidths
[i
];
9755 // Now divide what's left between the remaining columns
9757 if (stretchColCount
> 0)
9758 colShare
= widthLeft
/ stretchColCount
;
9759 int colShareRemainder
= widthLeft
- (colShare
* stretchColCount
);
9761 // Check we don't have enough space, in which case shrink all columns, overriding
9762 // any absolute/proportional widths
9763 // TODO: actually we would like to divide up the shrinkage according to size.
9764 // How do we calculate the proportions that will achieve this?
9765 // Could first choose an arbitrary value for stretching cells, and then calculate
9766 // factors to multiply each width by.
9767 // TODO: want to record this fact and pass to an iteration that tries e.g. min widths
9768 if (widthLeft
< 0 || (stretchToFitTableWidth
&& (stretchColCount
== 0)))
9770 colShare
= tableWidthMinusPadding
/ m_colCount
;
9771 colShareRemainder
= tableWidthMinusPadding
- (colShare
* m_colCount
);
9772 for (i
= 0; i
< m_colCount
; i
++)
9775 minColWidths
[i
] = 0;
9779 // We have to adjust the columns if either we need to shrink the
9780 // table to fit the parent/table width, or we explicitly set the
9781 // table width and need to stretch out the table.
9782 if (widthLeft
< 0 || stretchToFitTableWidth
)
9784 for (i
= 0; i
< m_colCount
; i
++)
9786 if (colWidths
[i
] <= 0) // absolute or proportional width has not been specified
9788 if (minColWidths
[i
] > 0)
9789 colWidths
[i
] = minColWidths
[i
] + colShare
;
9791 colWidths
[i
] = colShare
;
9792 if (i
== (m_colCount
-1))
9793 colWidths
[i
] += colShareRemainder
; // ensure all pixels are filled
9798 // TODO: if spanned cells have no specified or max width, make them the
9799 // as big as the columns they span. Do this for all spanned cells in all
9800 // rows, of course. Size any spanned cells left over at the end - even if they
9801 // have width > 0, make sure they're limited to the appropriate column edge.
9805 Sort out confusion between content width
9806 and overall width later. For now, assume we specify overall width.
9808 So, now we've laid out the table to fit into the given space
9809 and have used specified widths and minimum widths.
9811 Now we need to consider how we will try to take maximum width into account.
9815 // (??) TODO: take max width into account
9817 // (6) Lay out all cells again with the current values
9820 int y
= availableSpace
.y
;
9821 for (j
= 0; j
< m_rowCount
; j
++)
9823 int x
= availableSpace
.x
; // TODO: take into account centering etc.
9824 int maxCellHeight
= 0;
9825 int maxSpecifiedCellHeight
= 0;
9827 wxArrayInt actualWidths
;
9828 actualWidths
.Add(0, m_colCount
);
9830 wxTextAttrDimensionConverter
converter(dc
, scale
);
9831 for (i
= 0; i
< m_colCount
; i
++)
9833 wxRichTextCell
* cell
= GetCell(j
, i
);
9834 if (cell
->IsShown())
9836 // Get max specified cell height
9837 // Don't handle percentages for height
9838 if (cell
->GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && cell
->GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() != wxTEXT_ATTR_UNITS_PERCENTAGE
)
9840 int h
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetHeight());
9841 if (h
> maxSpecifiedCellHeight
)
9842 maxSpecifiedCellHeight
= h
;
9845 if (colWidths
[i
] > 0) // absolute or proportional width has been specified
9848 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9849 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9851 wxRect availableCellSpace
;
9853 // TODO: take into acount spans
9856 // Calculate the size of this spanning cell from its constituent columns
9858 int spans
= wxMin(colSpan
, m_colCount
- i
);
9859 for (k
= i
; k
< spans
; k
++)
9865 availableCellSpace
= wxRect(x
, y
, xx
, -1);
9868 availableCellSpace
= wxRect(x
, y
, colWidths
[i
], -1);
9870 // Store actual width so we can force cell to be the appropriate width on the final loop
9871 actualWidths
[i
] = availableCellSpace
.GetWidth();
9874 cell
->Invalidate(wxRICHTEXT_ALL
);
9875 cell
->Layout(dc
, context
, availableCellSpace
, availableSpace
, style
);
9877 // TODO: use GetCachedSize().x to compute 'natural' size
9879 x
+= (availableCellSpace
.GetWidth() + paddingX
);
9880 if (cell
->GetCachedSize().y
> maxCellHeight
)
9881 maxCellHeight
= cell
->GetCachedSize().y
;
9886 maxCellHeight
= wxMax(maxCellHeight
, maxSpecifiedCellHeight
);
9888 for (i
= 0; i
< m_colCount
; i
++)
9890 wxRichTextCell
* cell
= GetCell(j
, i
);
9891 if (cell
->IsShown())
9893 wxRect availableCellSpace
= wxRect(cell
->GetPosition(), wxSize(actualWidths
[i
], maxCellHeight
));
9894 // Lay out cell with new height
9895 cell
->Invalidate(wxRICHTEXT_ALL
);
9896 cell
->Layout(dc
, context
, availableCellSpace
, availableSpace
, style
);
9898 // Make sure the cell size really is the appropriate size,
9899 // not the calculated box size
9900 cell
->SetCachedSize(wxSize(actualWidths
[i
], maxCellHeight
));
9902 maxRight
= wxMax(maxRight
, cell
->GetPosition().x
+ cell
->GetCachedSize().x
);
9907 if (j
< (m_rowCount
-1))
9911 // We need to add back the margins etc.
9913 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
9914 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxRight
- availableSpace
.x
, y
- availableSpace
.y
));
9915 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
9916 SetCachedSize(marginRect
.GetSize());
9919 // TODO: calculate max size
9921 SetMaxSize(GetCachedSize());
9924 // TODO: calculate min size
9926 SetMinSize(GetCachedSize());
9929 // TODO: currently we use either a fixed table width or the parent's size.
9930 // We also want to be able to calculate the table width from its content,
9931 // whether using fixed column widths or cell content min/max width.
9932 // Probably need a boolean flag to say whether we need to stretch cells
9933 // to fit the table width, or to simply use min/max cell widths. The
9934 // trouble with this is that if cell widths are not specified, they
9935 // will be tiny; we could use arbitrary defaults but this seems unsatisfactory.
9936 // Anyway, ignoring that problem, we probably need to factor layout into a function
9937 // that can can calculate the maximum unconstrained layout in case table size is
9938 // not specified. Then LayoutToBestSize() can choose to use either parent size to
9939 // constrain Layout(), or the previously-calculated max size to constraint layout.
9944 // Finds the absolute position and row height for the given character position
9945 bool wxRichTextTable::FindPosition(wxDC
& dc
, wxRichTextDrawingContext
& context
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
9947 wxRichTextCell
* child
= GetCell(index
+1);
9950 // Find the position at the start of the child cell, since the table doesn't
9951 // have any caret position of its own.
9952 return child
->FindPosition(dc
, context
, -1, pt
, height
, forceLineStart
);
9958 // Get the cell at the given character position (in the range of the table).
9959 wxRichTextCell
* wxRichTextTable::GetCell(long pos
) const
9961 int row
= 0, col
= 0;
9962 if (GetCellRowColumnPosition(pos
, row
, col
))
9964 return GetCell(row
, col
);
9970 // Get the row/column for a given character position
9971 bool wxRichTextTable::GetCellRowColumnPosition(long pos
, int& row
, int& col
) const
9973 if (m_colCount
== 0 || m_rowCount
== 0)
9976 row
= (int) (pos
/ m_colCount
);
9977 col
= pos
- (row
* m_colCount
);
9979 wxASSERT(row
< m_rowCount
&& col
< m_colCount
);
9981 if (row
< m_rowCount
&& col
< m_colCount
)
9987 // Calculate range, taking row/cell ordering into account instead of relying
9988 // on list ordering.
9989 void wxRichTextTable::CalculateRange(long start
, long& end
)
9991 long current
= start
;
9992 long lastEnd
= current
;
10001 for (i
= 0; i
< m_rowCount
; i
++)
10003 for (j
= 0; j
< m_colCount
; j
++)
10005 wxRichTextCell
* child
= GetCell(i
, j
);
10010 child
->CalculateRange(current
, childEnd
);
10012 lastEnd
= childEnd
;
10013 current
= childEnd
+ 1;
10018 // A top-level object always has a range of size 1,
10019 // because its children don't count at this level.
10021 m_range
.SetRange(start
, start
);
10023 // An object with no children has zero length
10024 if (m_children
.GetCount() == 0)
10026 m_ownRange
.SetRange(0, lastEnd
);
10029 // Gets the range size.
10030 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
10032 return wxRichTextBox::GetRangeSize(range
, size
, descent
, dc
, context
, flags
, position
, parentSize
, partialExtents
);
10035 // Deletes content in the given range.
10036 bool wxRichTextTable::DeleteRange(const wxRichTextRange
& WXUNUSED(range
))
10038 // TODO: implement deletion of cells
10042 // Gets any text in this object for the given range.
10043 wxString
wxRichTextTable::GetTextForRange(const wxRichTextRange
& range
) const
10045 return wxRichTextBox::GetTextForRange(range
);
10048 // Copies this object.
10049 void wxRichTextTable::Copy(const wxRichTextTable
& obj
)
10051 wxRichTextBox::Copy(obj
);
10055 m_rowCount
= obj
.m_rowCount
;
10056 m_colCount
= obj
.m_colCount
;
10058 m_cells
.Add(wxRichTextObjectPtrArray(), m_rowCount
);
10061 for (i
= 0; i
< m_rowCount
; i
++)
10063 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
10064 for (j
= 0; j
< m_colCount
; j
++)
10066 wxRichTextCell
* cell
= wxDynamicCast(obj
.GetCell(i
, j
)->Clone(), wxRichTextCell
);
10069 colArray
.Add(cell
);
10074 void wxRichTextTable::ClearTable()
10080 bool wxRichTextTable::CreateTable(int rows
, int cols
)
10087 m_cells
.Add(wxRichTextObjectPtrArray(), rows
);
10090 for (i
= 0; i
< rows
; i
++)
10092 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
10093 for (j
= 0; j
< cols
; j
++)
10095 wxRichTextCell
* cell
= new wxRichTextCell
;
10097 cell
->AddParagraph(wxEmptyString
);
10099 colArray
.Add(cell
);
10106 wxRichTextCell
* wxRichTextTable::GetCell(int row
, int col
) const
10108 wxASSERT(row
< m_rowCount
);
10109 wxASSERT(col
< m_colCount
);
10111 if (row
< m_rowCount
&& col
< m_colCount
)
10113 wxRichTextObjectPtrArray
& colArray
= m_cells
[row
];
10114 wxRichTextObject
* obj
= colArray
[col
];
10115 return wxDynamicCast(obj
, wxRichTextCell
);
10121 // Returns a selection object specifying the selections between start and end character positions.
10122 // For example, a table would deduce what cells (of range length 1) are selected when dragging across the table.
10123 wxRichTextSelection
wxRichTextTable::GetSelection(long start
, long end
) const
10125 wxRichTextSelection selection
;
10126 selection
.SetContainer((wxRichTextTable
*) this);
10135 wxASSERT( start
>= 0 && end
< (m_colCount
* m_rowCount
));
10137 if (end
>= (m_colCount
* m_rowCount
))
10140 // We need to find the rectangle of cells that is described by the rectangle
10141 // with start, end as the diagonal. Make sure we don't add cells that are
10142 // not currenty visible because they are overlapped by spanning cells.
10144 --------------------------
10145 | 0 | 1 | 2 | 3 | 4 |
10146 --------------------------
10147 | 5 | 6 | 7 | 8 | 9 |
10148 --------------------------
10149 | 10 | 11 | 12 | 13 | 14 |
10150 --------------------------
10151 | 15 | 16 | 17 | 18 | 19 |
10152 --------------------------
10154 Let's say we select 6 -> 18.
10156 Left and right edge cols of rectangle are 1 and 3 inclusive. Find least/greatest to find
10157 which is left and which is right.
10159 Top and bottom edge rows are 1 and 3 inclusive. Again, find least/greatest to find top and bottom.
10161 Now go through rows from 1 to 3 and only add cells that are (a) within above column range
10167 int leftCol
= start
- m_colCount
* int(start
/m_colCount
);
10168 int rightCol
= end
- m_colCount
* int(end
/m_colCount
);
10170 int topRow
= int(start
/m_colCount
);
10171 int bottomRow
= int(end
/m_colCount
);
10173 if (leftCol
> rightCol
)
10175 int tmp
= rightCol
;
10176 rightCol
= leftCol
;
10180 if (topRow
> bottomRow
)
10182 int tmp
= bottomRow
;
10183 bottomRow
= topRow
;
10188 for (i
= topRow
; i
<= bottomRow
; i
++)
10190 for (j
= leftCol
; j
<= rightCol
; j
++)
10192 wxRichTextCell
* cell
= GetCell(i
, j
);
10193 if (cell
&& cell
->IsShown())
10194 selection
.Add(cell
->GetRange());
10201 // Sets the attributes for the cells specified by the selection.
10202 bool wxRichTextTable::SetCellStyle(const wxRichTextSelection
& selection
, const wxRichTextAttr
& style
, int flags
)
10204 if (selection
.GetContainer() != this)
10207 wxRichTextBuffer
* buffer
= GetBuffer();
10208 bool haveControl
= (buffer
&& buffer
->GetRichTextCtrl() != NULL
);
10209 bool withUndo
= haveControl
&& ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
10212 buffer
->BeginBatchUndo(_("Set Cell Style"));
10214 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
10217 wxRichTextCell
* cell
= wxDynamicCast(node
->GetData(), wxRichTextCell
);
10218 if (cell
&& selection
.WithinSelection(cell
->GetRange().GetStart()))
10219 SetStyle(cell
, style
, flags
);
10220 node
= node
->GetNext();
10223 // Do action, or delay it until end of batch.
10225 buffer
->EndBatchUndo();
10230 bool wxRichTextTable::DeleteRows(int startRow
, int noRows
)
10232 wxASSERT((startRow
+ noRows
) < m_rowCount
);
10233 if ((startRow
+ noRows
) >= m_rowCount
)
10237 for (i
= startRow
; i
< (startRow
+noRows
); i
++)
10239 wxRichTextObjectPtrArray
& colArray
= m_cells
[startRow
];
10240 for (j
= 0; j
< (int) colArray
.GetCount(); j
++)
10242 wxRichTextObject
* cell
= colArray
[j
];
10243 RemoveChild(cell
, true);
10246 // Keep deleting at the same position, since we move all
10248 m_cells
.RemoveAt(startRow
);
10251 m_rowCount
= m_rowCount
- noRows
;
10256 bool wxRichTextTable::DeleteColumns(int startCol
, int noCols
)
10258 wxASSERT((startCol
+ noCols
) < m_colCount
);
10259 if ((startCol
+ noCols
) >= m_colCount
)
10262 bool deleteRows
= (noCols
== m_colCount
);
10265 for (i
= 0; i
< m_rowCount
; i
++)
10267 wxRichTextObjectPtrArray
& colArray
= m_cells
[deleteRows
? 0 : i
];
10268 for (j
= startCol
; j
< (startCol
+noCols
); j
++)
10270 wxRichTextObject
* cell
= colArray
[j
];
10271 RemoveChild(cell
, true);
10275 m_cells
.RemoveAt(0);
10280 m_colCount
= m_colCount
- noCols
;
10285 bool wxRichTextTable::AddRows(int startRow
, int noRows
, const wxRichTextAttr
& attr
)
10287 wxASSERT(startRow
<= m_rowCount
);
10288 if (startRow
> m_rowCount
)
10292 for (i
= 0; i
< noRows
; i
++)
10295 if (startRow
== m_rowCount
)
10297 m_cells
.Add(wxRichTextObjectPtrArray());
10298 idx
= m_cells
.GetCount() - 1;
10302 m_cells
.Insert(wxRichTextObjectPtrArray(), startRow
+i
);
10306 wxRichTextObjectPtrArray
& colArray
= m_cells
[idx
];
10307 for (j
= 0; j
< m_colCount
; j
++)
10309 wxRichTextCell
* cell
= new wxRichTextCell
;
10310 cell
->GetAttributes() = attr
;
10313 colArray
.Add(cell
);
10317 m_rowCount
= m_rowCount
+ noRows
;
10321 bool wxRichTextTable::AddColumns(int startCol
, int noCols
, const wxRichTextAttr
& attr
)
10323 wxASSERT(startCol
<= m_colCount
);
10324 if (startCol
> m_colCount
)
10328 for (i
= 0; i
< m_rowCount
; i
++)
10330 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
10331 for (j
= 0; j
< noCols
; j
++)
10333 wxRichTextCell
* cell
= new wxRichTextCell
;
10334 cell
->GetAttributes() = attr
;
10338 if (startCol
== m_colCount
)
10339 colArray
.Add(cell
);
10341 colArray
.Insert(cell
, startCol
+j
);
10345 m_colCount
= m_colCount
+ noCols
;
10350 // Edit properties via a GUI
10351 bool wxRichTextTable::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
10353 wxRichTextObjectPropertiesDialog
boxDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Table Properties"));
10354 boxDlg
.SetAttributes(GetAttributes());
10356 if (boxDlg
.ShowModal() == wxID_OK
)
10358 boxDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
10366 * Module to initialise and clean up handlers
10369 class wxRichTextModule
: public wxModule
10371 DECLARE_DYNAMIC_CLASS(wxRichTextModule
)
10373 wxRichTextModule() {}
10376 wxRichTextBuffer::SetRenderer(new wxRichTextStdRenderer
);
10377 wxRichTextBuffer::InitStandardHandlers();
10378 wxRichTextParagraph::InitDefaultTabs();
10380 wxRichTextXMLHandler::RegisterNodeName(wxT("text"), wxT("wxRichTextPlainText"));
10381 wxRichTextXMLHandler::RegisterNodeName(wxT("symbol"), wxT("wxRichTextPlainText"));
10382 wxRichTextXMLHandler::RegisterNodeName(wxT("image"), wxT("wxRichTextImage"));
10383 wxRichTextXMLHandler::RegisterNodeName(wxT("paragraph"), wxT("wxRichTextParagraph"));
10384 wxRichTextXMLHandler::RegisterNodeName(wxT("paragraphlayout"), wxT("wxRichTextParagraphLayoutBox"));
10385 wxRichTextXMLHandler::RegisterNodeName(wxT("textbox"), wxT("wxRichTextBox"));
10386 wxRichTextXMLHandler::RegisterNodeName(wxT("cell"), wxT("wxRichTextCell"));
10387 wxRichTextXMLHandler::RegisterNodeName(wxT("table"), wxT("wxRichTextTable"));
10388 wxRichTextXMLHandler::RegisterNodeName(wxT("field"), wxT("wxRichTextField"));
10394 wxRichTextBuffer::CleanUpHandlers();
10395 wxRichTextBuffer::CleanUpDrawingHandlers();
10396 wxRichTextBuffer::CleanUpFieldTypes();
10397 wxRichTextXMLHandler::ClearNodeToClassMap();
10398 wxRichTextDecimalToRoman(-1);
10399 wxRichTextParagraph::ClearDefaultTabs();
10400 wxRichTextCtrl::ClearAvailableFontNames();
10401 wxRichTextBuffer::SetRenderer(NULL
);
10405 IMPLEMENT_DYNAMIC_CLASS(wxRichTextModule
, wxModule
)
10408 // If the richtext lib is dynamically loaded after the app has already started
10409 // (such as from wxPython) then the built-in module system will not init this
10410 // module. Provide this function to do it manually.
10411 void wxRichTextModuleInit()
10413 wxModule
* module = new wxRichTextModule
;
10415 wxModule::RegisterModule(module);
10420 * Commands for undo/redo
10424 wxRichTextCommand::wxRichTextCommand(const wxString
& name
, wxRichTextCommandId id
, wxRichTextBuffer
* buffer
,
10425 wxRichTextParagraphLayoutBox
* container
, wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
): wxCommand(true, name
)
10427 /* wxRichTextAction* action = */ new wxRichTextAction(this, name
, id
, buffer
, container
, ctrl
, ignoreFirstTime
);
10430 wxRichTextCommand::wxRichTextCommand(const wxString
& name
): wxCommand(true, name
)
10434 wxRichTextCommand::~wxRichTextCommand()
10439 void wxRichTextCommand::AddAction(wxRichTextAction
* action
)
10441 if (!m_actions
.Member(action
))
10442 m_actions
.Append(action
);
10445 bool wxRichTextCommand::Do()
10447 for (wxList::compatibility_iterator node
= m_actions
.GetFirst(); node
; node
= node
->GetNext())
10449 wxRichTextAction
* action
= (wxRichTextAction
*) node
->GetData();
10456 bool wxRichTextCommand::Undo()
10458 for (wxList::compatibility_iterator node
= m_actions
.GetLast(); node
; node
= node
->GetPrevious())
10460 wxRichTextAction
* action
= (wxRichTextAction
*) node
->GetData();
10467 void wxRichTextCommand::ClearActions()
10469 WX_CLEAR_LIST(wxList
, m_actions
);
10473 * Individual action
10477 wxRichTextAction::wxRichTextAction(wxRichTextCommand
* cmd
, const wxString
& name
, wxRichTextCommandId id
,
10478 wxRichTextBuffer
* buffer
, wxRichTextParagraphLayoutBox
* container
,
10479 wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
)
10483 m_containerAddress
.Create(buffer
, container
);
10484 m_ignoreThis
= ignoreFirstTime
;
10489 m_newParagraphs
.SetDefaultStyle(buffer
->GetDefaultStyle());
10490 m_newParagraphs
.SetBasicStyle(buffer
->GetBasicStyle());
10492 cmd
->AddAction(this);
10495 wxRichTextAction::~wxRichTextAction()
10501 // Returns the container that this action refers to, using the container address and top-level buffer.
10502 wxRichTextParagraphLayoutBox
* wxRichTextAction::GetContainer() const
10504 wxRichTextParagraphLayoutBox
* container
= wxDynamicCast(GetContainerAddress().GetObject(m_buffer
), wxRichTextParagraphLayoutBox
);
10509 void wxRichTextAction::CalculateRefreshOptimizations(wxArrayInt
& optimizationLineCharPositions
, wxArrayInt
& optimizationLineYPositions
)
10511 // Store a list of line start character and y positions so we can figure out which area
10512 // we need to refresh
10514 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10515 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10516 wxASSERT(container
!= NULL
);
10520 // NOTE: we're assuming that the buffer is laid out correctly at this point.
10521 // If we had several actions, which only invalidate and leave layout until the
10522 // paint handler is called, then this might not be true. So we may need to switch
10523 // optimisation on only when we're simply adding text and not simultaneously
10524 // deleting a selection, for example. Or, we make sure the buffer is laid out correctly
10525 // first, but of course this means we'll be doing it twice.
10526 if (!m_buffer
->IsDirty() && m_ctrl
) // can only do optimisation if the buffer is already laid out correctly
10528 wxSize clientSize
= m_ctrl
->GetUnscaledSize(m_ctrl
->GetClientSize());
10529 wxPoint firstVisiblePt
= m_ctrl
->GetUnscaledPoint(m_ctrl
->GetFirstVisiblePoint());
10530 int lastY
= firstVisiblePt
.y
+ clientSize
.y
;
10532 wxRichTextParagraph
* para
= container
->GetParagraphAtPosition(GetRange().GetStart());
10533 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(para
);
10536 wxRichTextParagraph
* child
= (wxRichTextParagraph
*) node
->GetData();
10537 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
10540 wxRichTextLine
* line
= node2
->GetData();
10541 wxPoint pt
= line
->GetAbsolutePosition();
10542 wxRichTextRange range
= line
->GetAbsoluteRange();
10546 node2
= wxRichTextLineList::compatibility_iterator();
10547 node
= wxRichTextObjectList::compatibility_iterator();
10549 else if (range
.GetStart() > GetPosition() && pt
.y
>= firstVisiblePt
.y
)
10551 optimizationLineCharPositions
.Add(range
.GetStart());
10552 optimizationLineYPositions
.Add(pt
.y
);
10556 node2
= node2
->GetNext();
10560 node
= node
->GetNext();
10566 bool wxRichTextAction::Do()
10568 m_buffer
->Modify(true);
10570 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10571 wxASSERT(container
!= NULL
);
10577 case wxRICHTEXT_INSERT
:
10579 // Store a list of line start character and y positions so we can figure out which area
10580 // we need to refresh
10581 wxArrayInt optimizationLineCharPositions
;
10582 wxArrayInt optimizationLineYPositions
;
10584 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10585 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
10588 container
->InsertFragment(GetRange().GetStart(), m_newParagraphs
);
10589 container
->UpdateRanges();
10591 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10592 // Layout() would stop prematurely at the top level.
10593 container
->InvalidateHierarchy(wxRichTextRange(wxMax(0, GetRange().GetStart()-1), GetRange().GetEnd()));
10595 long newCaretPosition
= GetPosition() + m_newParagraphs
.GetOwnRange().GetLength();
10597 // Character position to caret position
10598 newCaretPosition
--;
10600 // Don't take into account the last newline
10601 if (m_newParagraphs
.GetPartialParagraph())
10602 newCaretPosition
--;
10604 if (m_newParagraphs
.GetChildren().GetCount() > 1)
10606 wxRichTextObject
* p
= (wxRichTextObject
*) m_newParagraphs
.GetChildren().GetLast()->GetData();
10607 if (p
->GetRange().GetLength() == 1)
10608 newCaretPosition
--;
10611 newCaretPosition
= wxMin(newCaretPosition
, (container
->GetOwnRange().GetEnd()-1));
10613 UpdateAppearance(newCaretPosition
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */);
10615 wxRichTextEvent
cmdEvent(
10616 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED
,
10617 m_ctrl
? m_ctrl
->GetId() : -1);
10618 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10619 cmdEvent
.SetRange(GetRange());
10620 cmdEvent
.SetPosition(GetRange().GetStart());
10621 cmdEvent
.SetContainer(container
);
10623 m_buffer
->SendEvent(cmdEvent
);
10627 case wxRICHTEXT_DELETE
:
10629 wxArrayInt optimizationLineCharPositions
;
10630 wxArrayInt optimizationLineYPositions
;
10632 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10633 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
10636 container
->DeleteRange(GetRange());
10637 container
->UpdateRanges();
10638 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10639 // Layout() would stop prematurely at the top level.
10640 container
->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
10642 long caretPos
= GetRange().GetStart()-1;
10643 if (caretPos
>= container
->GetOwnRange().GetEnd())
10646 UpdateAppearance(caretPos
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */);
10648 wxRichTextEvent
cmdEvent(
10649 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED
,
10650 m_ctrl
? m_ctrl
->GetId() : -1);
10651 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10652 cmdEvent
.SetRange(GetRange());
10653 cmdEvent
.SetPosition(GetRange().GetStart());
10654 cmdEvent
.SetContainer(container
);
10656 m_buffer
->SendEvent(cmdEvent
);
10660 case wxRICHTEXT_CHANGE_STYLE
:
10661 case wxRICHTEXT_CHANGE_PROPERTIES
:
10663 ApplyParagraphs(GetNewParagraphs());
10665 // Invalidate the whole buffer if there were floating objects
10666 if (wxRichTextBuffer::GetFloatingLayoutMode() && container
->GetFloatingObjectCount() > 0)
10667 m_buffer
->InvalidateHierarchy(wxRICHTEXT_ALL
);
10670 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10671 // Layout() would stop prematurely at the top level.
10672 container
->InvalidateHierarchy(GetRange());
10675 UpdateAppearance(GetPosition());
10677 wxRichTextEvent
cmdEvent(
10678 m_cmdId
== wxRICHTEXT_CHANGE_STYLE
? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
: wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED
,
10679 m_ctrl
? m_ctrl
->GetId() : -1);
10680 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10681 cmdEvent
.SetRange(GetRange());
10682 cmdEvent
.SetPosition(GetRange().GetStart());
10683 cmdEvent
.SetContainer(container
);
10685 m_buffer
->SendEvent(cmdEvent
);
10689 case wxRICHTEXT_CHANGE_ATTRIBUTES
:
10691 wxRichTextObject
* obj
= m_objectAddress
.GetObject(m_buffer
); // container->GetChildAtPosition(GetRange().GetStart());
10694 wxRichTextAttr oldAttr
= obj
->GetAttributes();
10695 obj
->GetAttributes() = m_attributes
;
10696 m_attributes
= oldAttr
;
10699 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10700 // Layout() would stop prematurely at the top level.
10701 // Invalidate the whole buffer if there were floating objects
10702 if (wxRichTextBuffer::GetFloatingLayoutMode() && container
->GetFloatingObjectCount() > 0)
10703 m_buffer
->InvalidateHierarchy(wxRICHTEXT_ALL
);
10705 container
->InvalidateHierarchy(GetRange());
10707 UpdateAppearance(GetPosition());
10709 wxRichTextEvent
cmdEvent(
10710 wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
,
10711 m_ctrl
? m_ctrl
->GetId() : -1);
10712 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10713 cmdEvent
.SetRange(GetRange());
10714 cmdEvent
.SetPosition(GetRange().GetStart());
10715 cmdEvent
.SetContainer(container
);
10717 m_buffer
->SendEvent(cmdEvent
);
10721 case wxRICHTEXT_CHANGE_OBJECT
:
10723 wxRichTextObject
* obj
= m_objectAddress
.GetObject(m_buffer
);
10724 // wxRichTextObject* obj = container->GetChildAtPosition(GetRange().GetStart());
10725 if (obj
&& m_object
)
10727 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(obj
);
10730 wxRichTextObject
* obj
= node
->GetData();
10731 node
->SetData(m_object
);
10736 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10737 // Layout() would stop prematurely at the top level.
10738 // Invalidate the whole buffer if there were floating objects
10739 if (wxRichTextBuffer::GetFloatingLayoutMode() && container
->GetFloatingObjectCount() > 0)
10740 m_buffer
->InvalidateHierarchy(wxRICHTEXT_ALL
);
10742 container
->InvalidateHierarchy(GetRange());
10744 UpdateAppearance(GetPosition());
10746 // TODO: send new kind of modification event
10757 bool wxRichTextAction::Undo()
10759 m_buffer
->Modify(true);
10761 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10762 wxASSERT(container
!= NULL
);
10768 case wxRICHTEXT_INSERT
:
10770 wxArrayInt optimizationLineCharPositions
;
10771 wxArrayInt optimizationLineYPositions
;
10773 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10774 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
10777 container
->DeleteRange(GetRange());
10778 container
->UpdateRanges();
10780 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10781 // Layout() would stop prematurely at the top level.
10782 container
->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
10784 long newCaretPosition
= GetPosition() - 1;
10786 UpdateAppearance(newCaretPosition
, true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */);
10788 wxRichTextEvent
cmdEvent(
10789 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED
,
10790 m_ctrl
? m_ctrl
->GetId() : -1);
10791 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10792 cmdEvent
.SetRange(GetRange());
10793 cmdEvent
.SetPosition(GetRange().GetStart());
10794 cmdEvent
.SetContainer(container
);
10796 m_buffer
->SendEvent(cmdEvent
);
10800 case wxRICHTEXT_DELETE
:
10802 wxArrayInt optimizationLineCharPositions
;
10803 wxArrayInt optimizationLineYPositions
;
10805 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10806 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
10809 container
->InsertFragment(GetRange().GetStart(), m_oldParagraphs
);
10810 container
->UpdateRanges();
10812 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10813 // Layout() would stop prematurely at the top level.
10814 container
->InvalidateHierarchy(GetRange());
10816 UpdateAppearance(GetPosition(), true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */);
10818 wxRichTextEvent
cmdEvent(
10819 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED
,
10820 m_ctrl
? m_ctrl
->GetId() : -1);
10821 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10822 cmdEvent
.SetRange(GetRange());
10823 cmdEvent
.SetPosition(GetRange().GetStart());
10824 cmdEvent
.SetContainer(container
);
10826 m_buffer
->SendEvent(cmdEvent
);
10830 case wxRICHTEXT_CHANGE_STYLE
:
10831 case wxRICHTEXT_CHANGE_PROPERTIES
:
10833 ApplyParagraphs(GetOldParagraphs());
10834 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10835 // Layout() would stop prematurely at the top level.
10836 container
->InvalidateHierarchy(GetRange());
10838 UpdateAppearance(GetPosition());
10840 wxRichTextEvent
cmdEvent(
10841 m_cmdId
== wxRICHTEXT_CHANGE_STYLE
? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
: wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED
,
10842 m_ctrl
? m_ctrl
->GetId() : -1);
10843 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10844 cmdEvent
.SetRange(GetRange());
10845 cmdEvent
.SetPosition(GetRange().GetStart());
10846 cmdEvent
.SetContainer(container
);
10848 m_buffer
->SendEvent(cmdEvent
);
10852 case wxRICHTEXT_CHANGE_ATTRIBUTES
:
10853 case wxRICHTEXT_CHANGE_OBJECT
:
10864 /// Update the control appearance
10865 void wxRichTextAction::UpdateAppearance(long caretPosition
, bool sendUpdateEvent
, wxArrayInt
* optimizationLineCharPositions
, wxArrayInt
* optimizationLineYPositions
, bool isDoCmd
)
10867 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10868 wxASSERT(container
!= NULL
);
10874 m_ctrl
->SetFocusObject(container
);
10875 m_ctrl
->SetCaretPosition(caretPosition
);
10877 if (!m_ctrl
->IsFrozen())
10879 wxRect containerRect
= container
->GetRect();
10881 m_ctrl
->LayoutContent();
10883 // Refresh everything if there were floating objects or the container changed size
10884 // (we can't yet optimize in these cases, since more complex interaction with other content occurs)
10885 if ((wxRichTextBuffer::GetFloatingLayoutMode() && container
->GetFloatingObjectCount() > 0) || (container
->GetParent() && containerRect
!= container
->GetRect()))
10887 m_ctrl
->Refresh(false);
10891 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10892 // Find refresh rectangle if we are in a position to optimise refresh
10893 if ((m_cmdId
== wxRICHTEXT_INSERT
|| m_cmdId
== wxRICHTEXT_DELETE
) && optimizationLineCharPositions
)
10897 wxSize clientSize
= m_ctrl
->GetUnscaledSize(m_ctrl
->GetClientSize());
10898 wxPoint firstVisiblePt
= m_ctrl
->GetUnscaledPoint(m_ctrl
->GetFirstVisiblePoint());
10900 // Start/end positions
10902 int lastY
= firstVisiblePt
.y
+ clientSize
.y
;
10904 bool foundEnd
= false;
10906 // position offset - how many characters were inserted
10907 int positionOffset
= GetRange().GetLength();
10909 // Determine whether this is Do or Undo, and adjust positionOffset accordingly
10910 if ((m_cmdId
== wxRICHTEXT_DELETE
&& isDoCmd
) || (m_cmdId
== wxRICHTEXT_INSERT
&& !isDoCmd
))
10911 positionOffset
= - positionOffset
;
10913 // find the first line which is being drawn at the same position as it was
10914 // before. Since we're talking about a simple insertion, we can assume
10915 // that the rest of the window does not need to be redrawn.
10917 wxRichTextParagraph
* para
= container
->GetParagraphAtPosition(GetPosition());
10918 // Since we support floating layout, we should redraw the whole para instead of just
10919 // the first line touching the invalid range.
10922 firstY
= para
->GetPosition().y
;
10925 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(para
);
10928 wxRichTextParagraph
* child
= (wxRichTextParagraph
*) node
->GetData();
10929 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
10932 wxRichTextLine
* line
= node2
->GetData();
10933 wxPoint pt
= line
->GetAbsolutePosition();
10934 wxRichTextRange range
= line
->GetAbsoluteRange();
10936 // we want to find the first line that is in the same position
10937 // as before. This will mean we're at the end of the changed text.
10939 if (pt
.y
> lastY
) // going past the end of the window, no more info
10941 node2
= wxRichTextLineList::compatibility_iterator();
10942 node
= wxRichTextObjectList::compatibility_iterator();
10944 // Detect last line in the buffer
10945 else if (!node2
->GetNext() && para
->GetRange().Contains(container
->GetOwnRange().GetEnd()))
10947 // If deleting text, make sure we refresh below as well as above
10948 if (positionOffset
>= 0)
10951 lastY
= pt
.y
+ line
->GetSize().y
;
10954 node2
= wxRichTextLineList::compatibility_iterator();
10955 node
= wxRichTextObjectList::compatibility_iterator();
10961 // search for this line being at the same position as before
10962 for (i
= 0; i
< optimizationLineCharPositions
->GetCount(); i
++)
10964 if (((*optimizationLineCharPositions
)[i
] + positionOffset
== range
.GetStart()) &&
10965 ((*optimizationLineYPositions
)[i
] == pt
.y
))
10967 // Stop, we're now the same as we were
10972 node2
= wxRichTextLineList::compatibility_iterator();
10973 node
= wxRichTextObjectList::compatibility_iterator();
10981 node2
= node2
->GetNext();
10985 node
= node
->GetNext();
10988 firstY
= wxMax(firstVisiblePt
.y
, firstY
);
10990 lastY
= firstVisiblePt
.y
+ clientSize
.y
;
10992 // Convert to device coordinates
10993 wxRect
rect(m_ctrl
->GetPhysicalPoint(m_ctrl
->GetScaledPoint(wxPoint(firstVisiblePt
.x
, firstY
))), m_ctrl
->GetScaledSize(wxSize(clientSize
.x
, lastY
- firstY
)));
10994 m_ctrl
->RefreshRect(rect
);
10998 m_ctrl
->Refresh(false);
11000 m_ctrl
->PositionCaret();
11002 // This causes styles to persist when doing programmatic
11003 // content creation except when Freeze/Thaw is used, so
11004 // disable this and check for the consequences.
11005 // m_ctrl->SetDefaultStyleToCursorStyle();
11007 if (sendUpdateEvent
)
11008 wxTextCtrl::SendTextUpdatedEvent(m_ctrl
);
11013 /// Replace the buffer paragraphs with the new ones.
11014 void wxRichTextAction::ApplyParagraphs(const wxRichTextParagraphLayoutBox
& fragment
)
11016 wxRichTextParagraphLayoutBox
* container
= GetContainer();
11017 wxASSERT(container
!= NULL
);
11021 wxRichTextObjectList::compatibility_iterator node
= fragment
.GetChildren().GetFirst();
11024 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
11025 wxASSERT (para
!= NULL
);
11027 // We'll replace the existing paragraph by finding the paragraph at this position,
11028 // delete its node data, and setting a copy as the new node data.
11029 // TODO: make more efficient by simply swapping old and new paragraph objects.
11031 wxRichTextParagraph
* existingPara
= container
->GetParagraphAtPosition(para
->GetRange().GetStart());
11034 wxRichTextObjectList::compatibility_iterator bufferParaNode
= container
->GetChildren().Find(existingPara
);
11035 if (bufferParaNode
)
11037 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(*para
);
11038 newPara
->SetParent(container
);
11040 bufferParaNode
->SetData(newPara
);
11042 delete existingPara
;
11046 node
= node
->GetNext();
11053 * This stores beginning and end positions for a range of data.
11056 WX_DEFINE_OBJARRAY(wxRichTextRangeArray
);
11058 /// Limit this range to be within 'range'
11059 bool wxRichTextRange::LimitTo(const wxRichTextRange
& range
)
11061 if (m_start
< range
.m_start
)
11062 m_start
= range
.m_start
;
11064 if (m_end
> range
.m_end
)
11065 m_end
= range
.m_end
;
11071 * wxRichTextImage implementation
11072 * This object represents an image.
11075 IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage
, wxRichTextObject
)
11077 wxRichTextImage::wxRichTextImage(const wxImage
& image
, wxRichTextObject
* parent
, wxRichTextAttr
* charStyle
):
11078 wxRichTextObject(parent
)
11081 m_imageBlock
.MakeImageBlockDefaultQuality(image
, wxBITMAP_TYPE_PNG
);
11083 SetAttributes(*charStyle
);
11086 wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock
& imageBlock
, wxRichTextObject
* parent
, wxRichTextAttr
* charStyle
):
11087 wxRichTextObject(parent
)
11090 m_imageBlock
= imageBlock
;
11092 SetAttributes(*charStyle
);
11095 wxRichTextImage::~wxRichTextImage()
11099 void wxRichTextImage::Init()
11101 m_originalImageSize
= wxSize(-1, -1);
11104 /// Create a cached image at the required size
11105 bool wxRichTextImage::LoadImageCache(wxDC
& dc
, bool resetCache
, const wxSize
& parentSize
)
11107 if (!m_imageBlock
.IsOk())
11110 // If we have an original image size, use that to compute the cached bitmap size
11111 // instead of loading the image each time. This way we can avoid loading
11112 // the image so long as the new cached bitmap size hasn't changed.
11115 if (resetCache
|| m_originalImageSize
.GetWidth() <= 0 || m_originalImageSize
.GetHeight() <= 0)
11117 m_imageCache
= wxNullBitmap
;
11119 m_imageBlock
.Load(image
);
11123 m_originalImageSize
= wxSize(image
.GetWidth(), image
.GetHeight());
11126 int width
= m_originalImageSize
.GetWidth();
11127 int height
= m_originalImageSize
.GetHeight();
11129 int parentWidth
= 0;
11130 int parentHeight
= 0;
11133 int maxHeight
= -1;
11135 wxSize sz
= parentSize
;
11136 if (sz
== wxDefaultSize
)
11138 if (GetParent() && GetParent()->GetParent())
11139 sz
= GetParent()->GetParent()->GetCachedSize();
11142 if (sz
!= wxDefaultSize
)
11144 wxRichTextBuffer
* buffer
= GetBuffer();
11147 // Find the actual space available when margin is taken into account
11148 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
11149 marginRect
= wxRect(0, 0, sz
.x
, sz
.y
);
11150 if (GetParent() && GetParent()->GetParent())
11152 buffer
->GetBoxRects(dc
, buffer
, GetParent()->GetParent()->GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
11153 sz
= contentRect
.GetSize();
11156 // Use a minimum size to stop images becoming very small
11157 parentWidth
= wxMax(100, sz
.GetWidth());
11158 parentHeight
= wxMax(100, sz
.GetHeight());
11160 if (buffer
->GetRichTextCtrl())
11161 // Start with a maximum width of the control size, even if not specified by the content,
11162 // to minimize the amount of picture overlapping the right-hand side
11163 maxWidth
= parentWidth
;
11167 if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetWidth().GetValue() > 0)
11169 if (parentWidth
> 0 && GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
11170 width
= (int) ((GetAttributes().GetTextBoxAttr().GetWidth().GetValue() * parentWidth
)/100.0);
11171 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
11172 width
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetWidth().GetValue());
11173 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
11174 width
= GetAttributes().GetTextBoxAttr().GetWidth().GetValue();
11177 // Limit to max width
11179 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() > 0)
11183 if (parentWidth
> 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
11184 mw
= (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() * parentWidth
)/100.0);
11185 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
11186 mw
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue());
11187 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
11188 mw
= GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue();
11190 // If we already have a smaller max width due to the constraints of the control size,
11191 // don't use the larger max width.
11192 if (mw
!= -1 && ((maxWidth
== -1) || (mw
< maxWidth
)))
11196 if (maxWidth
> 0 && width
> maxWidth
)
11199 // Preserve the aspect ratio
11200 if (width
!= m_originalImageSize
.GetWidth())
11201 height
= (int) (float(m_originalImageSize
.GetHeight()) * (float(width
)/float(m_originalImageSize
.GetWidth())));
11203 if (GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetHeight().GetValue() > 0)
11205 if (parentHeight
> 0 && GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
11206 height
= (int) ((GetAttributes().GetTextBoxAttr().GetHeight().GetValue() * parentHeight
)/100.0);
11207 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
11208 height
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetHeight().GetValue());
11209 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
11210 height
= GetAttributes().GetTextBoxAttr().GetHeight().GetValue();
11212 // Preserve the aspect ratio
11213 if (height
!= m_originalImageSize
.GetHeight())
11214 width
= (int) (float(m_originalImageSize
.GetWidth()) * (float(height
)/float(m_originalImageSize
.GetHeight())));
11217 // Limit to max height
11219 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() > 0)
11221 if (parentHeight
> 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
11222 maxHeight
= (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() * parentHeight
)/100.0);
11223 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
11224 maxHeight
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue());
11225 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
11226 maxHeight
= GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue();
11229 if (maxHeight
> 0 && height
> maxHeight
)
11231 height
= maxHeight
;
11233 // Preserve the aspect ratio
11234 if (height
!= m_originalImageSize
.GetHeight())
11235 width
= (int) (float(m_originalImageSize
.GetWidth()) * (float(height
)/float(m_originalImageSize
.GetHeight())));
11238 // Prevent the use of zero size
11239 width
= wxMax(1, width
);
11240 height
= wxMax(1, height
);
11242 if (m_imageCache
.IsOk() && m_imageCache
.GetWidth() == width
&& m_imageCache
.GetHeight() == height
)
11244 // Do nothing, we didn't need to change the image cache
11250 m_imageBlock
.Load(image
);
11255 if (image
.GetWidth() == width
&& image
.GetHeight() == height
)
11256 m_imageCache
= wxBitmap(image
);
11259 // If the original width and height is small, e.g. 400 or below,
11260 // scale up and then down to improve image quality. This can make
11261 // a big difference, with not much performance hit.
11262 int upscaleThreshold
= 400;
11264 if (image
.GetWidth() <= upscaleThreshold
|| image
.GetHeight() <= upscaleThreshold
)
11266 img
= image
.Scale(image
.GetWidth()*2, image
.GetHeight()*2);
11267 img
.Rescale(width
, height
, wxIMAGE_QUALITY_HIGH
);
11270 img
= image
.Scale(width
, height
, wxIMAGE_QUALITY_HIGH
);
11271 m_imageCache
= wxBitmap(img
);
11275 return m_imageCache
.IsOk();
11279 bool wxRichTextImage::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& WXUNUSED(range
), const wxRichTextSelection
& selection
, const wxRect
& rect
, int WXUNUSED(descent
), int WXUNUSED(style
))
11284 // Don't need cached size AFAIK
11285 // wxSize size = GetCachedSize();
11286 if (!LoadImageCache(dc
))
11289 wxRichTextAttr
attr(GetAttributes());
11290 context
.ApplyVirtualAttributes(attr
, this);
11292 DrawBoxAttributes(dc
, GetBuffer(), attr
, wxRect(rect
.GetPosition(), GetCachedSize()));
11294 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
11295 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
11296 marginRect
= rect
; // outer rectangle, will calculate contentRect
11297 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
11299 dc
.DrawBitmap(m_imageCache
, contentRect
.x
, contentRect
.y
, true);
11301 if (selection
.WithinSelection(GetRange().GetStart(), this))
11303 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
11304 wxCheckSetPen(dc
, *wxBLACK_PEN
);
11305 dc
.SetLogicalFunction(wxINVERT
);
11306 dc
.DrawRectangle(contentRect
);
11307 dc
.SetLogicalFunction(wxCOPY
);
11313 /// Lay the item out
11314 bool wxRichTextImage::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& WXUNUSED(parentRect
), int WXUNUSED(style
))
11316 if (!LoadImageCache(dc
))
11319 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
11320 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
11321 contentRect
= wxRect(wxPoint(0,0), imageSize
);
11323 wxRichTextAttr
attr(GetAttributes());
11324 context
.ApplyVirtualAttributes(attr
, this);
11326 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
11328 wxSize overallSize
= marginRect
.GetSize();
11330 SetCachedSize(overallSize
);
11331 SetMaxSize(overallSize
);
11332 SetMinSize(overallSize
);
11333 SetPosition(rect
.GetPosition());
11338 /// Get/set the object size for the given range. Returns false if the range
11339 /// is invalid for this object.
11340 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
11342 if (!range
.IsWithin(GetRange()))
11345 if (!((wxRichTextImage
*)this)->LoadImageCache(dc
, false, parentSize
))
11347 size
.x
= 0; size
.y
= 0;
11348 if (partialExtents
)
11349 partialExtents
->Add(0);
11353 wxRichTextAttr
attr(GetAttributes());
11354 context
.ApplyVirtualAttributes(attr
, (wxRichTextObject
*) this);
11356 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
11357 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
11358 contentRect
= wxRect(wxPoint(0,0), imageSize
);
11359 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
11361 wxSize overallSize
= marginRect
.GetSize();
11363 if (partialExtents
)
11364 partialExtents
->Add(overallSize
.x
);
11366 size
= overallSize
;
11371 // Get the 'natural' size for an object. For an image, it would be the
11373 wxTextAttrSize
wxRichTextImage::GetNaturalSize() const
11375 wxTextAttrSize size
;
11376 if (GetImageCache().IsOk())
11378 size
.SetWidth(GetImageCache().GetWidth(), wxTEXT_ATTR_UNITS_PIXELS
);
11379 size
.SetHeight(GetImageCache().GetHeight(), wxTEXT_ATTR_UNITS_PIXELS
);
11386 void wxRichTextImage::Copy(const wxRichTextImage
& obj
)
11388 wxRichTextObject::Copy(obj
);
11390 m_imageBlock
= obj
.m_imageBlock
;
11391 m_originalImageSize
= obj
.m_originalImageSize
;
11394 /// Edit properties via a GUI
11395 bool wxRichTextImage::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
11397 wxRichTextObjectPropertiesDialog
imageDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Picture Properties"));
11398 imageDlg
.SetAttributes(GetAttributes());
11400 if (imageDlg
.ShowModal() == wxID_OK
)
11402 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
11403 // indeterminate in the object.
11404 imageDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
11416 /// Compare two attribute objects
11417 bool wxTextAttrEq(const wxRichTextAttr
& attr1
, const wxRichTextAttr
& attr2
)
11419 return (attr1
== attr2
);
11423 bool wxRichTextTabsEq(const wxArrayInt
& tabs1
, const wxArrayInt
& tabs2
)
11425 if (tabs1
.GetCount() != tabs2
.GetCount())
11429 for (i
= 0; i
< tabs1
.GetCount(); i
++)
11431 if (tabs1
[i
] != tabs2
[i
])
11437 bool wxRichTextApplyStyle(wxRichTextAttr
& destStyle
, const wxRichTextAttr
& style
, wxRichTextAttr
* compareWith
)
11439 return destStyle
.Apply(style
, compareWith
);
11442 // Remove attributes
11443 bool wxRichTextRemoveStyle(wxRichTextAttr
& destStyle
, const wxRichTextAttr
& style
)
11445 return destStyle
.RemoveStyle(style
);
11448 /// Combine two bitlists, specifying the bits of interest with separate flags.
11449 bool wxRichTextCombineBitlists(int& valueA
, int valueB
, int& flagsA
, int flagsB
)
11451 return wxRichTextAttr::CombineBitlists(valueA
, valueB
, flagsA
, flagsB
);
11454 /// Compare two bitlists
11455 bool wxRichTextBitlistsEqPartial(int valueA
, int valueB
, int flags
)
11457 return wxRichTextAttr::BitlistsEqPartial(valueA
, valueB
, flags
);
11460 /// Split into paragraph and character styles
11461 bool wxRichTextSplitParaCharStyles(const wxRichTextAttr
& style
, wxRichTextAttr
& parStyle
, wxRichTextAttr
& charStyle
)
11463 return wxRichTextAttr::SplitParaCharStyles(style
, parStyle
, charStyle
);
11466 /// Convert a decimal to Roman numerals
11467 wxString
wxRichTextDecimalToRoman(long n
)
11469 static wxArrayInt decimalNumbers
;
11470 static wxArrayString romanNumbers
;
11475 decimalNumbers
.Clear();
11476 romanNumbers
.Clear();
11477 return wxEmptyString
;
11480 if (decimalNumbers
.GetCount() == 0)
11482 #define wxRichTextAddDecRom(n, r) decimalNumbers.Add(n); romanNumbers.Add(r);
11484 wxRichTextAddDecRom(1000, wxT("M"));
11485 wxRichTextAddDecRom(900, wxT("CM"));
11486 wxRichTextAddDecRom(500, wxT("D"));
11487 wxRichTextAddDecRom(400, wxT("CD"));
11488 wxRichTextAddDecRom(100, wxT("C"));
11489 wxRichTextAddDecRom(90, wxT("XC"));
11490 wxRichTextAddDecRom(50, wxT("L"));
11491 wxRichTextAddDecRom(40, wxT("XL"));
11492 wxRichTextAddDecRom(10, wxT("X"));
11493 wxRichTextAddDecRom(9, wxT("IX"));
11494 wxRichTextAddDecRom(5, wxT("V"));
11495 wxRichTextAddDecRom(4, wxT("IV"));
11496 wxRichTextAddDecRom(1, wxT("I"));
11502 while (n
> 0 && i
< 13)
11504 if (n
>= decimalNumbers
[i
])
11506 n
-= decimalNumbers
[i
];
11507 roman
+= romanNumbers
[i
];
11514 if (roman
.IsEmpty())
11520 * wxRichTextFileHandler
11521 * Base class for file handlers
11524 IMPLEMENT_CLASS(wxRichTextFileHandler
, wxObject
)
11526 #if wxUSE_FFILE && wxUSE_STREAMS
11527 bool wxRichTextFileHandler::LoadFile(wxRichTextBuffer
*buffer
, const wxString
& filename
)
11529 wxFFileInputStream
stream(filename
);
11531 return LoadFile(buffer
, stream
);
11536 bool wxRichTextFileHandler::SaveFile(wxRichTextBuffer
*buffer
, const wxString
& filename
)
11538 wxFFileOutputStream
stream(filename
);
11540 return SaveFile(buffer
, stream
);
11544 #endif // wxUSE_FFILE && wxUSE_STREAMS
11546 /// Can we handle this filename (if using files)? By default, checks the extension.
11547 bool wxRichTextFileHandler::CanHandle(const wxString
& filename
) const
11549 wxString path
, file
, ext
;
11550 wxFileName::SplitPath(filename
, & path
, & file
, & ext
);
11552 return (ext
.Lower() == GetExtension());
11556 * wxRichTextTextHandler
11557 * Plain text handler
11560 IMPLEMENT_CLASS(wxRichTextPlainTextHandler
, wxRichTextFileHandler
)
11563 bool wxRichTextPlainTextHandler::DoLoadFile(wxRichTextBuffer
*buffer
, wxInputStream
& stream
)
11565 if (!stream
.IsOk())
11571 while (!stream
.Eof())
11573 int ch
= stream
.GetC();
11577 if (ch
== 10 && lastCh
!= 13)
11580 if (ch
> 0 && ch
!= 10)
11587 buffer
->ResetAndClearCommands();
11589 buffer
->AddParagraphs(str
);
11590 buffer
->UpdateRanges();
11595 bool wxRichTextPlainTextHandler::DoSaveFile(wxRichTextBuffer
*buffer
, wxOutputStream
& stream
)
11597 if (!stream
.IsOk())
11600 wxString text
= buffer
->GetText();
11602 wxString newLine
= wxRichTextLineBreakChar
;
11603 text
.Replace(newLine
, wxT("\n"));
11605 wxCharBuffer buf
= text
.ToAscii();
11607 stream
.Write((const char*) buf
, text
.length());
11610 #endif // wxUSE_STREAMS
11613 * Stores information about an image, in binary in-memory form
11616 wxRichTextImageBlock::wxRichTextImageBlock()
11621 wxRichTextImageBlock::wxRichTextImageBlock(const wxRichTextImageBlock
& block
):wxObject()
11627 wxRichTextImageBlock::~wxRichTextImageBlock()
11632 void wxRichTextImageBlock::Init()
11636 m_imageType
= wxBITMAP_TYPE_INVALID
;
11639 void wxRichTextImageBlock::Clear()
11643 m_imageType
= wxBITMAP_TYPE_INVALID
;
11647 // Load the original image into a memory block.
11648 // If the image is not a JPEG, we must convert it into a JPEG
11649 // to conserve space.
11650 // If it's not a JPEG we can make use of 'image', already scaled, so we don't have to
11651 // load the image a 2nd time.
11653 bool wxRichTextImageBlock::MakeImageBlock(const wxString
& filename
, wxBitmapType imageType
,
11654 wxImage
& image
, bool convertToJPEG
)
11656 m_imageType
= imageType
;
11658 wxString
filenameToRead(filename
);
11659 bool removeFile
= false;
11661 if (imageType
== wxBITMAP_TYPE_INVALID
)
11662 return false; // Could not determine image type
11664 if ((imageType
!= wxBITMAP_TYPE_JPEG
) && convertToJPEG
)
11666 wxString tempFile
=
11667 wxFileName::CreateTempFileName(_("image"));
11669 wxASSERT(!tempFile
.IsEmpty());
11671 image
.SaveFile(tempFile
, wxBITMAP_TYPE_JPEG
);
11672 filenameToRead
= tempFile
;
11675 m_imageType
= wxBITMAP_TYPE_JPEG
;
11678 if (!file
.Open(filenameToRead
))
11681 m_dataSize
= (size_t) file
.Length();
11686 m_data
= ReadBlock(filenameToRead
, m_dataSize
);
11689 wxRemoveFile(filenameToRead
);
11691 return (m_data
!= NULL
);
11694 // Make an image block from the wxImage in the given
11696 bool wxRichTextImageBlock::MakeImageBlock(wxImage
& image
, wxBitmapType imageType
, int quality
)
11698 image
.SetOption(wxT("quality"), quality
);
11700 if (imageType
== wxBITMAP_TYPE_INVALID
)
11701 return false; // Could not determine image type
11703 return DoMakeImageBlock(image
, imageType
);
11706 // Uses a const wxImage for efficiency, but can't set quality (only relevant for JPEG)
11707 bool wxRichTextImageBlock::MakeImageBlockDefaultQuality(const wxImage
& image
, wxBitmapType imageType
)
11709 if (imageType
== wxBITMAP_TYPE_INVALID
)
11710 return false; // Could not determine image type
11712 return DoMakeImageBlock(image
, imageType
);
11715 // Makes the image block
11716 bool wxRichTextImageBlock::DoMakeImageBlock(const wxImage
& image
, wxBitmapType imageType
)
11718 wxMemoryOutputStream memStream
;
11719 if (!image
.SaveFile(memStream
, imageType
))
11724 unsigned char* block
= new unsigned char[memStream
.GetSize()];
11732 m_imageType
= imageType
;
11733 m_dataSize
= memStream
.GetSize();
11735 memStream
.CopyTo(m_data
, m_dataSize
);
11737 return (m_data
!= NULL
);
11741 bool wxRichTextImageBlock::Write(const wxString
& filename
)
11743 return WriteBlock(filename
, m_data
, m_dataSize
);
11746 void wxRichTextImageBlock::Copy(const wxRichTextImageBlock
& block
)
11748 m_imageType
= block
.m_imageType
;
11750 m_dataSize
= block
.m_dataSize
;
11751 if (m_dataSize
== 0)
11754 m_data
= new unsigned char[m_dataSize
];
11756 for (i
= 0; i
< m_dataSize
; i
++)
11757 m_data
[i
] = block
.m_data
[i
];
11761 void wxRichTextImageBlock::operator=(const wxRichTextImageBlock
& block
)
11766 // Load a wxImage from the block
11767 bool wxRichTextImageBlock::Load(wxImage
& image
)
11772 // Read in the image.
11774 wxMemoryInputStream
mstream(m_data
, m_dataSize
);
11775 bool success
= image
.LoadFile(mstream
, GetImageType());
11777 wxString tempFile
= wxFileName::CreateTempFileName(_("image"));
11778 wxASSERT(!tempFile
.IsEmpty());
11780 if (!WriteBlock(tempFile
, m_data
, m_dataSize
))
11784 success
= image
.LoadFile(tempFile
, GetImageType());
11785 wxRemoveFile(tempFile
);
11791 // Write data in hex to a stream
11792 bool wxRichTextImageBlock::WriteHex(wxOutputStream
& stream
)
11794 if (m_dataSize
== 0)
11797 int bufSize
= 100000;
11798 if (int(2*m_dataSize
) < bufSize
)
11799 bufSize
= 2*m_dataSize
;
11800 char* buf
= new char[bufSize
+1];
11802 int left
= m_dataSize
;
11807 if (left
*2 > bufSize
)
11809 n
= bufSize
; left
-= (bufSize
/2);
11813 n
= left
*2; left
= 0;
11817 for (i
= 0; i
< (n
/2); i
++)
11819 wxDecToHex(m_data
[j
], b
, b
+1);
11824 stream
.Write((const char*) buf
, n
);
11830 // Read data in hex from a stream
11831 bool wxRichTextImageBlock::ReadHex(wxInputStream
& stream
, int length
, wxBitmapType imageType
)
11833 int dataSize
= length
/2;
11838 // create a null terminated temporary string:
11842 m_data
= new unsigned char[dataSize
];
11844 for (i
= 0; i
< dataSize
; i
++)
11846 str
[0] = (char)stream
.GetC();
11847 str
[1] = (char)stream
.GetC();
11849 m_data
[i
] = (unsigned char)wxHexToDec(str
);
11852 m_dataSize
= dataSize
;
11853 m_imageType
= imageType
;
11858 // Allocate and read from stream as a block of memory
11859 unsigned char* wxRichTextImageBlock::ReadBlock(wxInputStream
& stream
, size_t size
)
11861 unsigned char* block
= new unsigned char[size
];
11865 stream
.Read(block
, size
);
11870 unsigned char* wxRichTextImageBlock::ReadBlock(const wxString
& filename
, size_t size
)
11872 wxFileInputStream
stream(filename
);
11873 if (!stream
.IsOk())
11876 return ReadBlock(stream
, size
);
11879 // Write memory block to stream
11880 bool wxRichTextImageBlock::WriteBlock(wxOutputStream
& stream
, unsigned char* block
, size_t size
)
11882 stream
.Write((void*) block
, size
);
11883 return stream
.IsOk();
11887 // Write memory block to file
11888 bool wxRichTextImageBlock::WriteBlock(const wxString
& filename
, unsigned char* block
, size_t size
)
11890 wxFileOutputStream
outStream(filename
);
11891 if (!outStream
.IsOk())
11894 return WriteBlock(outStream
, block
, size
);
11897 // Gets the extension for the block's type
11898 wxString
wxRichTextImageBlock::GetExtension() const
11900 wxImageHandler
* handler
= wxImage::FindHandler(GetImageType());
11902 return handler
->GetExtension();
11904 return wxEmptyString
;
11910 * The data object for a wxRichTextBuffer
11913 const wxChar
*wxRichTextBufferDataObject::ms_richTextBufferFormatId
= wxT("wxShape");
11915 wxRichTextBufferDataObject::wxRichTextBufferDataObject(wxRichTextBuffer
* richTextBuffer
)
11917 m_richTextBuffer
= richTextBuffer
;
11919 // this string should uniquely identify our format, but is otherwise
11921 m_formatRichTextBuffer
.SetId(GetRichTextBufferFormatId());
11923 SetFormat(m_formatRichTextBuffer
);
11926 wxRichTextBufferDataObject::~wxRichTextBufferDataObject()
11928 delete m_richTextBuffer
;
11931 // after a call to this function, the richTextBuffer is owned by the caller and it
11932 // is responsible for deleting it!
11933 wxRichTextBuffer
* wxRichTextBufferDataObject::GetRichTextBuffer()
11935 wxRichTextBuffer
* richTextBuffer
= m_richTextBuffer
;
11936 m_richTextBuffer
= NULL
;
11938 return richTextBuffer
;
11941 wxDataFormat
wxRichTextBufferDataObject::GetPreferredFormat(Direction
WXUNUSED(dir
)) const
11943 return m_formatRichTextBuffer
;
11946 size_t wxRichTextBufferDataObject::GetDataSize() const
11948 if (!m_richTextBuffer
)
11954 wxStringOutputStream
stream(& bufXML
);
11955 if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
))
11957 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
11963 wxCharBuffer buffer
= bufXML
.mb_str(wxConvUTF8
);
11964 return strlen(buffer
) + 1;
11966 return bufXML
.Length()+1;
11970 bool wxRichTextBufferDataObject::GetDataHere(void *pBuf
) const
11972 if (!pBuf
|| !m_richTextBuffer
)
11978 wxStringOutputStream
stream(& bufXML
);
11979 if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
))
11981 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
11987 wxCharBuffer buffer
= bufXML
.mb_str(wxConvUTF8
);
11988 size_t len
= strlen(buffer
);
11989 memcpy((char*) pBuf
, (const char*) buffer
, len
);
11990 ((char*) pBuf
)[len
] = 0;
11992 size_t len
= bufXML
.Length();
11993 memcpy((char*) pBuf
, (const char*) bufXML
.c_str(), len
);
11994 ((char*) pBuf
)[len
] = 0;
12000 bool wxRichTextBufferDataObject::SetData(size_t WXUNUSED(len
), const void *buf
)
12002 wxDELETE(m_richTextBuffer
);
12004 wxString
bufXML((const char*) buf
, wxConvUTF8
);
12006 m_richTextBuffer
= new wxRichTextBuffer
;
12008 wxStringInputStream
stream(bufXML
);
12009 if (!m_richTextBuffer
->LoadFile(stream
, wxRICHTEXT_TYPE_XML
))
12011 wxLogError(wxT("Could not read the buffer from an XML stream.\nYou may have forgotten to add the XML file handler."));
12013 wxDELETE(m_richTextBuffer
);
12025 * wxRichTextFontTable
12026 * Manages quick access to a pool of fonts for rendering rich text
12029 WX_DECLARE_STRING_HASH_MAP_WITH_DECL(wxFont
, wxRichTextFontTableHashMap
, class WXDLLIMPEXP_RICHTEXT
);
12031 class wxRichTextFontTableData
: public wxObjectRefData
12034 wxRichTextFontTableData() {}
12036 wxFont
FindFont(const wxRichTextAttr
& fontSpec
, double fontScale
);
12038 wxRichTextFontTableHashMap m_hashMap
;
12041 wxFont
wxRichTextFontTableData::FindFont(const wxRichTextAttr
& fontSpec
, double fontScale
)
12043 wxString
facename(fontSpec
.GetFontFaceName());
12045 int fontSize
= fontSpec
.GetFontSize();
12046 if (fontScale
!= 1.0)
12047 fontSize
= (int) ((double(fontSize
) * fontScale
) + 0.5);
12050 if (fontSpec
.HasFontPixelSize() && !fontSpec
.HasFontPointSize())
12054 wxString spec
= wxString::Format(wxT("%d-%s-%d-%d-%d-%d-%s-%d"),
12055 fontSize
, units
.c_str(), fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), (int) fontSpec
.GetFontUnderlined(), (int) fontSpec
.GetFontStrikethrough(),
12056 facename
.c_str(), (int) fontSpec
.GetFontEncoding());
12058 wxRichTextFontTableHashMap::iterator entry
= m_hashMap
.find(spec
);
12059 if ( entry
== m_hashMap
.end() )
12061 if (fontSpec
.HasFontPixelSize() && !fontSpec
.HasFontPointSize())
12063 wxFont
font(wxSize(0, fontSize
), wxFONTFAMILY_DEFAULT
, fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), fontSpec
.GetFontUnderlined(), facename
);
12064 if (fontSpec
.HasFontStrikethrough() && fontSpec
.GetFontStrikethrough())
12065 font
.SetStrikethrough(true);
12066 m_hashMap
[spec
] = font
;
12071 wxFont
font(fontSize
, wxFONTFAMILY_DEFAULT
, fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), fontSpec
.GetFontUnderlined(), facename
.c_str());
12072 if (fontSpec
.HasFontStrikethrough() && fontSpec
.GetFontStrikethrough())
12073 font
.SetStrikethrough(true);
12075 m_hashMap
[spec
] = font
;
12081 return entry
->second
;
12085 IMPLEMENT_DYNAMIC_CLASS(wxRichTextFontTable
, wxObject
)
12087 wxRichTextFontTable::wxRichTextFontTable()
12089 m_refData
= new wxRichTextFontTableData
;
12093 wxRichTextFontTable::wxRichTextFontTable(const wxRichTextFontTable
& table
)
12099 wxRichTextFontTable::~wxRichTextFontTable()
12104 bool wxRichTextFontTable::operator == (const wxRichTextFontTable
& table
) const
12106 return (m_refData
== table
.m_refData
);
12109 void wxRichTextFontTable::operator= (const wxRichTextFontTable
& table
)
12112 m_fontScale
= table
.m_fontScale
;
12115 wxFont
wxRichTextFontTable::FindFont(const wxRichTextAttr
& fontSpec
)
12117 wxRichTextFontTableData
* data
= (wxRichTextFontTableData
*) m_refData
;
12119 return data
->FindFont(fontSpec
, m_fontScale
);
12124 void wxRichTextFontTable::Clear()
12126 wxRichTextFontTableData
* data
= (wxRichTextFontTableData
*) m_refData
;
12128 data
->m_hashMap
.clear();
12131 void wxRichTextFontTable::SetFontScale(double fontScale
)
12133 if (fontScale
!= m_fontScale
)
12135 m_fontScale
= fontScale
;
12140 void wxTextBoxAttr::Reset()
12143 m_floatMode
= wxTEXT_BOX_ATTR_FLOAT_NONE
;
12144 m_clearMode
= wxTEXT_BOX_ATTR_CLEAR_NONE
;
12145 m_collapseMode
= wxTEXT_BOX_ATTR_COLLAPSE_NONE
;
12146 m_verticalAlignment
= wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_NONE
;
12147 m_boxStyleName
= wxEmptyString
;
12151 m_position
.Reset();
12162 bool wxTextBoxAttr::operator== (const wxTextBoxAttr
& attr
) const
12165 m_flags
== attr
.m_flags
&&
12166 m_floatMode
== attr
.m_floatMode
&&
12167 m_clearMode
== attr
.m_clearMode
&&
12168 m_collapseMode
== attr
.m_collapseMode
&&
12169 m_verticalAlignment
== attr
.m_verticalAlignment
&&
12171 m_margins
== attr
.m_margins
&&
12172 m_padding
== attr
.m_padding
&&
12173 m_position
== attr
.m_position
&&
12175 m_size
== attr
.m_size
&&
12176 m_minSize
== attr
.m_minSize
&&
12177 m_maxSize
== attr
.m_maxSize
&&
12179 m_border
== attr
.m_border
&&
12180 m_outline
== attr
.m_outline
&&
12182 m_boxStyleName
== attr
.m_boxStyleName
12186 // Partial equality test
12187 bool wxTextBoxAttr::EqPartial(const wxTextBoxAttr
& attr
, bool weakTest
) const
12190 ((!HasFloatMode() && attr
.HasFloatMode()) ||
12191 (!HasClearMode() && attr
.HasClearMode()) ||
12192 (!HasCollapseBorders() && attr
.HasCollapseBorders()) ||
12193 (!HasVerticalAlignment() && attr
.HasVerticalAlignment()) ||
12194 (!HasBoxStyleName() && attr
.HasBoxStyleName())))
12198 if (attr
.HasFloatMode() && HasFloatMode() && (GetFloatMode() != attr
.GetFloatMode()))
12201 if (attr
.HasClearMode() && HasClearMode() && (GetClearMode() != attr
.GetClearMode()))
12204 if (attr
.HasCollapseBorders() && HasCollapseBorders() && (attr
.GetCollapseBorders() != GetCollapseBorders()))
12207 if (attr
.HasVerticalAlignment() && HasVerticalAlignment() && (attr
.GetVerticalAlignment() != GetVerticalAlignment()))
12210 if (attr
.HasBoxStyleName() && HasBoxStyleName() && (attr
.GetBoxStyleName() != GetBoxStyleName()))
12215 if (!m_position
.EqPartial(attr
.m_position
, weakTest
))
12220 if (!m_size
.EqPartial(attr
.m_size
, weakTest
))
12222 if (!m_minSize
.EqPartial(attr
.m_minSize
, weakTest
))
12224 if (!m_maxSize
.EqPartial(attr
.m_maxSize
, weakTest
))
12229 if (!m_margins
.EqPartial(attr
.m_margins
, weakTest
))
12234 if (!m_padding
.EqPartial(attr
.m_padding
, weakTest
))
12239 if (!GetBorder().EqPartial(attr
.GetBorder(), weakTest
))
12244 if (!GetOutline().EqPartial(attr
.GetOutline(), weakTest
))
12250 // Merges the given attributes. If compareWith
12251 // is non-NULL, then it will be used to mask out those attributes that are the same in style
12252 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
12253 bool wxTextBoxAttr::Apply(const wxTextBoxAttr
& attr
, const wxTextBoxAttr
* compareWith
)
12255 if (attr
.HasFloatMode())
12257 if (!(compareWith
&& compareWith
->HasFloatMode() && compareWith
->GetFloatMode() == attr
.GetFloatMode()))
12258 SetFloatMode(attr
.GetFloatMode());
12261 if (attr
.HasClearMode())
12263 if (!(compareWith
&& compareWith
->HasClearMode() && compareWith
->GetClearMode() == attr
.GetClearMode()))
12264 SetClearMode(attr
.GetClearMode());
12267 if (attr
.HasCollapseBorders())
12269 if (!(compareWith
&& compareWith
->HasCollapseBorders() && compareWith
->GetCollapseBorders() == attr
.GetCollapseBorders()))
12270 SetCollapseBorders(attr
.GetCollapseBorders());
12273 if (attr
.HasVerticalAlignment())
12275 if (!(compareWith
&& compareWith
->HasVerticalAlignment() && compareWith
->GetVerticalAlignment() == attr
.GetVerticalAlignment()))
12276 SetVerticalAlignment(attr
.GetVerticalAlignment());
12279 if (attr
.HasBoxStyleName())
12281 if (!(compareWith
&& compareWith
->HasBoxStyleName() && compareWith
->GetBoxStyleName() == attr
.GetBoxStyleName()))
12282 SetBoxStyleName(attr
.GetBoxStyleName());
12285 m_margins
.Apply(attr
.m_margins
, compareWith
? (& attr
.m_margins
) : (const wxTextAttrDimensions
*) NULL
);
12286 m_padding
.Apply(attr
.m_padding
, compareWith
? (& attr
.m_padding
) : (const wxTextAttrDimensions
*) NULL
);
12287 m_position
.Apply(attr
.m_position
, compareWith
? (& attr
.m_position
) : (const wxTextAttrDimensions
*) NULL
);
12289 m_size
.Apply(attr
.m_size
, compareWith
? (& attr
.m_size
) : (const wxTextAttrSize
*) NULL
);
12290 m_minSize
.Apply(attr
.m_minSize
, compareWith
? (& attr
.m_minSize
) : (const wxTextAttrSize
*) NULL
);
12291 m_maxSize
.Apply(attr
.m_maxSize
, compareWith
? (& attr
.m_maxSize
) : (const wxTextAttrSize
*) NULL
);
12293 m_border
.Apply(attr
.m_border
, compareWith
? (& attr
.m_border
) : (const wxTextAttrBorders
*) NULL
);
12294 m_outline
.Apply(attr
.m_outline
, compareWith
? (& attr
.m_outline
) : (const wxTextAttrBorders
*) NULL
);
12299 // Remove specified attributes from this object
12300 bool wxTextBoxAttr::RemoveStyle(const wxTextBoxAttr
& attr
)
12302 if (attr
.HasFloatMode())
12303 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT
);
12305 if (attr
.HasClearMode())
12306 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR
);
12308 if (attr
.HasCollapseBorders())
12309 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
12311 if (attr
.HasVerticalAlignment())
12312 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
12314 if (attr
.HasBoxStyleName())
12316 SetBoxStyleName(wxEmptyString
);
12317 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
12320 m_margins
.RemoveStyle(attr
.m_margins
);
12321 m_padding
.RemoveStyle(attr
.m_padding
);
12322 m_position
.RemoveStyle(attr
.m_position
);
12324 m_size
.RemoveStyle(attr
.m_size
);
12325 m_minSize
.RemoveStyle(attr
.m_minSize
);
12326 m_maxSize
.RemoveStyle(attr
.m_maxSize
);
12328 m_border
.RemoveStyle(attr
.m_border
);
12329 m_outline
.RemoveStyle(attr
.m_outline
);
12334 // Collects the attributes that are common to a range of content, building up a note of
12335 // which attributes are absent in some objects and which clash in some objects.
12336 void wxTextBoxAttr::CollectCommonAttributes(const wxTextBoxAttr
& attr
, wxTextBoxAttr
& clashingAttr
, wxTextBoxAttr
& absentAttr
)
12338 if (attr
.HasFloatMode())
12340 if (!clashingAttr
.HasFloatMode() && !absentAttr
.HasFloatMode())
12342 if (HasFloatMode())
12344 if (GetFloatMode() != attr
.GetFloatMode())
12346 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_FLOAT
);
12347 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT
);
12351 SetFloatMode(attr
.GetFloatMode());
12355 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_FLOAT
);
12357 if (attr
.HasClearMode())
12359 if (!clashingAttr
.HasClearMode() && !absentAttr
.HasClearMode())
12361 if (HasClearMode())
12363 if (GetClearMode() != attr
.GetClearMode())
12365 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_CLEAR
);
12366 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR
);
12370 SetClearMode(attr
.GetClearMode());
12374 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_CLEAR
);
12376 if (attr
.HasCollapseBorders())
12378 if (!clashingAttr
.HasCollapseBorders() && !absentAttr
.HasCollapseBorders())
12380 if (HasCollapseBorders())
12382 if (GetCollapseBorders() != attr
.GetCollapseBorders())
12384 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
12385 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
12389 SetCollapseBorders(attr
.GetCollapseBorders());
12393 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
12395 if (attr
.HasVerticalAlignment())
12397 if (!clashingAttr
.HasVerticalAlignment() && !absentAttr
.HasVerticalAlignment())
12399 if (HasVerticalAlignment())
12401 if (GetVerticalAlignment() != attr
.GetVerticalAlignment())
12403 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
12404 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
12408 SetVerticalAlignment(attr
.GetVerticalAlignment());
12412 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
12414 if (attr
.HasBoxStyleName())
12416 if (!clashingAttr
.HasBoxStyleName() && !absentAttr
.HasBoxStyleName())
12418 if (HasBoxStyleName())
12420 if (GetBoxStyleName() != attr
.GetBoxStyleName())
12422 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
12423 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
12427 SetBoxStyleName(attr
.GetBoxStyleName());
12431 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
12433 m_margins
.CollectCommonAttributes(attr
.m_margins
, clashingAttr
.m_margins
, absentAttr
.m_margins
);
12434 m_padding
.CollectCommonAttributes(attr
.m_padding
, clashingAttr
.m_padding
, absentAttr
.m_padding
);
12435 m_position
.CollectCommonAttributes(attr
.m_position
, clashingAttr
.m_position
, absentAttr
.m_position
);
12437 m_size
.CollectCommonAttributes(attr
.m_size
, clashingAttr
.m_size
, absentAttr
.m_size
);
12438 m_minSize
.CollectCommonAttributes(attr
.m_minSize
, clashingAttr
.m_minSize
, absentAttr
.m_minSize
);
12439 m_maxSize
.CollectCommonAttributes(attr
.m_maxSize
, clashingAttr
.m_maxSize
, absentAttr
.m_maxSize
);
12441 m_border
.CollectCommonAttributes(attr
.m_border
, clashingAttr
.m_border
, absentAttr
.m_border
);
12442 m_outline
.CollectCommonAttributes(attr
.m_outline
, clashingAttr
.m_outline
, absentAttr
.m_outline
);
12445 bool wxTextBoxAttr::IsDefault() const
12447 return GetFlags() == 0 && !m_border
.IsValid() && !m_outline
.IsValid() &&
12448 !m_size
.IsValid() && !m_minSize
.IsValid() && !m_maxSize
.IsValid() &&
12449 !m_position
.IsValid() && !m_padding
.IsValid() && !m_margins
.IsValid();
12454 void wxRichTextAttr::Copy(const wxRichTextAttr
& attr
)
12456 wxTextAttr::Copy(attr
);
12458 m_textBoxAttr
= attr
.m_textBoxAttr
;
12461 bool wxRichTextAttr::operator==(const wxRichTextAttr
& attr
) const
12463 if (!(wxTextAttr::operator==(attr
)))
12466 return (m_textBoxAttr
== attr
.m_textBoxAttr
);
12469 // Partial equality test
12470 bool wxRichTextAttr::EqPartial(const wxRichTextAttr
& attr
, bool weakTest
) const
12472 if (!(wxTextAttr::EqPartial(attr
, weakTest
)))
12475 return m_textBoxAttr
.EqPartial(attr
.m_textBoxAttr
, weakTest
);
12478 // Merges the given attributes. If compareWith
12479 // is non-NULL, then it will be used to mask out those attributes that are the same in style
12480 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
12481 bool wxRichTextAttr::Apply(const wxRichTextAttr
& style
, const wxRichTextAttr
* compareWith
)
12483 wxTextAttr::Apply(style
, compareWith
);
12485 return m_textBoxAttr
.Apply(style
.m_textBoxAttr
, compareWith
? (& compareWith
->m_textBoxAttr
) : (const wxTextBoxAttr
*) NULL
);
12488 // Remove specified attributes from this object
12489 bool wxRichTextAttr::RemoveStyle(const wxRichTextAttr
& attr
)
12491 wxTextAttr::RemoveStyle(*this, attr
);
12493 return m_textBoxAttr
.RemoveStyle(attr
.m_textBoxAttr
);
12496 // Collects the attributes that are common to a range of content, building up a note of
12497 // which attributes are absent in some objects and which clash in some objects.
12498 void wxRichTextAttr::CollectCommonAttributes(const wxRichTextAttr
& attr
, wxRichTextAttr
& clashingAttr
, wxRichTextAttr
& absentAttr
)
12500 wxTextAttrCollectCommonAttributes(*this, attr
, clashingAttr
, absentAttr
);
12502 m_textBoxAttr
.CollectCommonAttributes(attr
.m_textBoxAttr
, clashingAttr
.m_textBoxAttr
, absentAttr
.m_textBoxAttr
);
12505 // Partial equality test
12506 bool wxTextAttrBorder::EqPartial(const wxTextAttrBorder
& border
, bool weakTest
) const
12509 ((!HasStyle() && border
.HasStyle()) ||
12510 (!HasColour() && border
.HasColour()) ||
12511 (!HasWidth() && border
.HasWidth())))
12516 if (border
.HasStyle() && HasStyle() && (border
.GetStyle() != GetStyle()))
12519 if (border
.HasColour() && HasColour() && (border
.GetColourLong() != GetColourLong()))
12522 if (border
.HasWidth() && HasWidth() && !(border
.GetWidth() == GetWidth()))
12528 // Apply border to 'this', but not if the same as compareWith
12529 bool wxTextAttrBorder::Apply(const wxTextAttrBorder
& border
, const wxTextAttrBorder
* compareWith
)
12531 if (border
.HasStyle())
12533 if (!(compareWith
&& (border
.GetStyle() == compareWith
->GetStyle())))
12534 SetStyle(border
.GetStyle());
12536 if (border
.HasColour())
12538 if (!(compareWith
&& (border
.GetColourLong() == compareWith
->GetColourLong())))
12539 SetColour(border
.GetColourLong());
12541 if (border
.HasWidth())
12543 if (!(compareWith
&& (border
.GetWidth() == compareWith
->GetWidth())))
12544 SetWidth(border
.GetWidth());
12550 // Remove specified attributes from this object
12551 bool wxTextAttrBorder::RemoveStyle(const wxTextAttrBorder
& attr
)
12553 if (attr
.HasStyle() && HasStyle())
12554 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_STYLE
);
12555 if (attr
.HasColour() && HasColour())
12556 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_COLOUR
);
12557 if (attr
.HasWidth() && HasWidth())
12558 m_borderWidth
.Reset();
12563 // Collects the attributes that are common to a range of content, building up a note of
12564 // which attributes are absent in some objects and which clash in some objects.
12565 void wxTextAttrBorder::CollectCommonAttributes(const wxTextAttrBorder
& attr
, wxTextAttrBorder
& clashingAttr
, wxTextAttrBorder
& absentAttr
)
12567 if (attr
.HasStyle())
12569 if (!clashingAttr
.HasStyle() && !absentAttr
.HasStyle())
12573 if (GetStyle() != attr
.GetStyle())
12575 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
12576 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
12580 SetStyle(attr
.GetStyle());
12584 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
12586 if (attr
.HasColour())
12588 if (!clashingAttr
.HasColour() && !absentAttr
.HasColour())
12592 if (GetColour() != attr
.GetColour())
12594 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
12595 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
12599 SetColour(attr
.GetColourLong());
12603 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
12605 m_borderWidth
.CollectCommonAttributes(attr
.m_borderWidth
, clashingAttr
.m_borderWidth
, absentAttr
.m_borderWidth
);
12608 // Partial equality test
12609 bool wxTextAttrBorders::EqPartial(const wxTextAttrBorders
& borders
, bool weakTest
) const
12611 return m_left
.EqPartial(borders
.m_left
, weakTest
) && m_right
.EqPartial(borders
.m_right
, weakTest
) &&
12612 m_top
.EqPartial(borders
.m_top
, weakTest
) && m_bottom
.EqPartial(borders
.m_bottom
, weakTest
);
12615 // Apply border to 'this', but not if the same as compareWith
12616 bool wxTextAttrBorders::Apply(const wxTextAttrBorders
& borders
, const wxTextAttrBorders
* compareWith
)
12618 m_left
.Apply(borders
.m_left
, compareWith
? (& compareWith
->m_left
) : (const wxTextAttrBorder
*) NULL
);
12619 m_right
.Apply(borders
.m_right
, compareWith
? (& compareWith
->m_right
) : (const wxTextAttrBorder
*) NULL
);
12620 m_top
.Apply(borders
.m_top
, compareWith
? (& compareWith
->m_top
) : (const wxTextAttrBorder
*) NULL
);
12621 m_bottom
.Apply(borders
.m_bottom
, compareWith
? (& compareWith
->m_bottom
) : (const wxTextAttrBorder
*) NULL
);
12625 // Remove specified attributes from this object
12626 bool wxTextAttrBorders::RemoveStyle(const wxTextAttrBorders
& attr
)
12628 m_left
.RemoveStyle(attr
.m_left
);
12629 m_right
.RemoveStyle(attr
.m_right
);
12630 m_top
.RemoveStyle(attr
.m_top
);
12631 m_bottom
.RemoveStyle(attr
.m_bottom
);
12635 // Collects the attributes that are common to a range of content, building up a note of
12636 // which attributes are absent in some objects and which clash in some objects.
12637 void wxTextAttrBorders::CollectCommonAttributes(const wxTextAttrBorders
& attr
, wxTextAttrBorders
& clashingAttr
, wxTextAttrBorders
& absentAttr
)
12639 m_left
.CollectCommonAttributes(attr
.m_left
, clashingAttr
.m_left
, absentAttr
.m_left
);
12640 m_right
.CollectCommonAttributes(attr
.m_right
, clashingAttr
.m_right
, absentAttr
.m_right
);
12641 m_top
.CollectCommonAttributes(attr
.m_top
, clashingAttr
.m_top
, absentAttr
.m_top
);
12642 m_bottom
.CollectCommonAttributes(attr
.m_bottom
, clashingAttr
.m_bottom
, absentAttr
.m_bottom
);
12645 // Set style of all borders
12646 void wxTextAttrBorders::SetStyle(int style
)
12648 m_left
.SetStyle(style
);
12649 m_right
.SetStyle(style
);
12650 m_top
.SetStyle(style
);
12651 m_bottom
.SetStyle(style
);
12654 // Set colour of all borders
12655 void wxTextAttrBorders::SetColour(unsigned long colour
)
12657 m_left
.SetColour(colour
);
12658 m_right
.SetColour(colour
);
12659 m_top
.SetColour(colour
);
12660 m_bottom
.SetColour(colour
);
12663 void wxTextAttrBorders::SetColour(const wxColour
& colour
)
12665 m_left
.SetColour(colour
);
12666 m_right
.SetColour(colour
);
12667 m_top
.SetColour(colour
);
12668 m_bottom
.SetColour(colour
);
12671 // Set width of all borders
12672 void wxTextAttrBorders::SetWidth(const wxTextAttrDimension
& width
)
12674 m_left
.SetWidth(width
);
12675 m_right
.SetWidth(width
);
12676 m_top
.SetWidth(width
);
12677 m_bottom
.SetWidth(width
);
12680 // Partial equality test
12681 bool wxTextAttrDimension::EqPartial(const wxTextAttrDimension
& dim
, bool weakTest
) const
12683 if (!weakTest
&& !IsValid() && dim
.IsValid())
12686 if (dim
.IsValid() && IsValid() && !((*this) == dim
))
12692 bool wxTextAttrDimension::Apply(const wxTextAttrDimension
& dim
, const wxTextAttrDimension
* compareWith
)
12696 if (!(compareWith
&& dim
== (*compareWith
)))
12703 // Collects the attributes that are common to a range of content, building up a note of
12704 // which attributes are absent in some objects and which clash in some objects.
12705 void wxTextAttrDimension::CollectCommonAttributes(const wxTextAttrDimension
& attr
, wxTextAttrDimension
& clashingAttr
, wxTextAttrDimension
& absentAttr
)
12707 if (attr
.IsValid())
12709 if (!clashingAttr
.IsValid() && !absentAttr
.IsValid())
12713 if (!((*this) == attr
))
12715 clashingAttr
.SetValid(true);
12724 absentAttr
.SetValid(true);
12727 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(wxDC
& dc
, double scale
, const wxSize
& parentSize
)
12729 m_ppi
= dc
.GetPPI().x
; m_scale
= scale
; m_parentSize
= parentSize
;
12732 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(int ppi
, double scale
, const wxSize
& parentSize
)
12734 m_ppi
= ppi
; m_scale
= scale
; m_parentSize
= parentSize
;
12737 int wxTextAttrDimensionConverter::ConvertTenthsMMToPixels(int units
) const
12739 return wxRichTextObject::ConvertTenthsMMToPixels(m_ppi
, units
, m_scale
);
12742 int wxTextAttrDimensionConverter::ConvertPixelsToTenthsMM(int pixels
) const
12744 return wxRichTextObject::ConvertPixelsToTenthsMM(m_ppi
, pixels
, m_scale
);
12747 int wxTextAttrDimensionConverter::GetPixels(const wxTextAttrDimension
& dim
, int direction
) const
12749 if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
12750 return ConvertTenthsMMToPixels(dim
.GetValue());
12751 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
12752 return dim
.GetValue();
12753 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
12755 wxASSERT(m_parentSize
!= wxDefaultSize
);
12756 if (direction
== wxHORIZONTAL
)
12757 return (int) (double(m_parentSize
.x
) * double(dim
.GetValue()) / 100.0);
12759 return (int) (double(m_parentSize
.y
) * double(dim
.GetValue()) / 100.0);
12768 int wxTextAttrDimensionConverter::GetTenthsMM(const wxTextAttrDimension
& dim
) const
12770 if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
12771 return dim
.GetValue();
12772 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
12773 return ConvertPixelsToTenthsMM(dim
.GetValue());
12781 // Partial equality test
12782 bool wxTextAttrDimensions::EqPartial(const wxTextAttrDimensions
& dims
, bool weakTest
) const
12784 if (!m_left
.EqPartial(dims
.m_left
, weakTest
))
12787 if (!m_right
.EqPartial(dims
.m_right
, weakTest
))
12790 if (!m_top
.EqPartial(dims
.m_top
, weakTest
))
12793 if (!m_bottom
.EqPartial(dims
.m_bottom
, weakTest
))
12799 // Apply border to 'this', but not if the same as compareWith
12800 bool wxTextAttrDimensions::Apply(const wxTextAttrDimensions
& dims
, const wxTextAttrDimensions
* compareWith
)
12802 m_left
.Apply(dims
.m_left
, compareWith
? (& compareWith
->m_left
) : (const wxTextAttrDimension
*) NULL
);
12803 m_right
.Apply(dims
.m_right
, compareWith
? (& compareWith
->m_right
): (const wxTextAttrDimension
*) NULL
);
12804 m_top
.Apply(dims
.m_top
, compareWith
? (& compareWith
->m_top
): (const wxTextAttrDimension
*) NULL
);
12805 m_bottom
.Apply(dims
.m_bottom
, compareWith
? (& compareWith
->m_bottom
): (const wxTextAttrDimension
*) NULL
);
12810 // Remove specified attributes from this object
12811 bool wxTextAttrDimensions::RemoveStyle(const wxTextAttrDimensions
& attr
)
12813 if (attr
.m_left
.IsValid())
12815 if (attr
.m_right
.IsValid())
12817 if (attr
.m_top
.IsValid())
12819 if (attr
.m_bottom
.IsValid())
12825 // Collects the attributes that are common to a range of content, building up a note of
12826 // which attributes are absent in some objects and which clash in some objects.
12827 void wxTextAttrDimensions::CollectCommonAttributes(const wxTextAttrDimensions
& attr
, wxTextAttrDimensions
& clashingAttr
, wxTextAttrDimensions
& absentAttr
)
12829 m_left
.CollectCommonAttributes(attr
.m_left
, clashingAttr
.m_left
, absentAttr
.m_left
);
12830 m_right
.CollectCommonAttributes(attr
.m_right
, clashingAttr
.m_right
, absentAttr
.m_right
);
12831 m_top
.CollectCommonAttributes(attr
.m_top
, clashingAttr
.m_top
, absentAttr
.m_top
);
12832 m_bottom
.CollectCommonAttributes(attr
.m_bottom
, clashingAttr
.m_bottom
, absentAttr
.m_bottom
);
12835 // Partial equality test
12836 bool wxTextAttrSize::EqPartial(const wxTextAttrSize
& size
, bool weakTest
) const
12838 if (!m_width
.EqPartial(size
.m_width
, weakTest
))
12841 if (!m_height
.EqPartial(size
.m_height
, weakTest
))
12847 // Apply border to 'this', but not if the same as compareWith
12848 bool wxTextAttrSize::Apply(const wxTextAttrSize
& size
, const wxTextAttrSize
* compareWith
)
12850 m_width
.Apply(size
.m_width
, compareWith
? (& compareWith
->m_width
) : (const wxTextAttrDimension
*) NULL
);
12851 m_height
.Apply(size
.m_height
, compareWith
? (& compareWith
->m_height
): (const wxTextAttrDimension
*) NULL
);
12856 // Remove specified attributes from this object
12857 bool wxTextAttrSize::RemoveStyle(const wxTextAttrSize
& attr
)
12859 if (attr
.m_width
.IsValid())
12861 if (attr
.m_height
.IsValid())
12867 // Collects the attributes that are common to a range of content, building up a note of
12868 // which attributes are absent in some objects and which clash in some objects.
12869 void wxTextAttrSize::CollectCommonAttributes(const wxTextAttrSize
& attr
, wxTextAttrSize
& clashingAttr
, wxTextAttrSize
& absentAttr
)
12871 m_width
.CollectCommonAttributes(attr
.m_width
, clashingAttr
.m_width
, absentAttr
.m_width
);
12872 m_height
.CollectCommonAttributes(attr
.m_height
, clashingAttr
.m_height
, absentAttr
.m_height
);
12875 // Collects the attributes that are common to a range of content, building up a note of
12876 // which attributes are absent in some objects and which clash in some objects.
12877 void wxTextAttrCollectCommonAttributes(wxTextAttr
& currentStyle
, const wxTextAttr
& attr
, wxTextAttr
& clashingAttr
, wxTextAttr
& absentAttr
)
12879 absentAttr
.SetFlags(absentAttr
.GetFlags() | (~attr
.GetFlags() & wxTEXT_ATTR_ALL
));
12880 absentAttr
.SetTextEffectFlags(absentAttr
.GetTextEffectFlags() | (~attr
.GetTextEffectFlags() & 0xFFFF));
12882 long forbiddenFlags
= clashingAttr
.GetFlags()|absentAttr
.GetFlags();
12884 // If different font size units are being used, this is a clash.
12885 if (((attr
.GetFlags() & wxTEXT_ATTR_FONT_SIZE
) | (currentStyle
.GetFlags() & wxTEXT_ATTR_FONT_SIZE
)) == wxTEXT_ATTR_FONT_SIZE
)
12887 currentStyle
.SetFontSize(0);
12888 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_SIZE
);
12889 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_SIZE
);
12893 if (attr
.HasFontPointSize() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_POINT_SIZE
))
12895 if (currentStyle
.HasFontPointSize())
12897 if (currentStyle
.GetFontSize() != attr
.GetFontSize())
12899 // Clash of attr - mark as such
12900 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE
);
12901 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE
);
12905 currentStyle
.SetFontSize(attr
.GetFontSize());
12907 else if (!attr
.HasFontPointSize() && currentStyle
.HasFontPointSize())
12909 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE
);
12910 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE
);
12913 if (attr
.HasFontPixelSize() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_PIXEL_SIZE
))
12915 if (currentStyle
.HasFontPixelSize())
12917 if (currentStyle
.GetFontSize() != attr
.GetFontSize())
12919 // Clash of attr - mark as such
12920 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE
);
12921 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE
);
12925 currentStyle
.SetFontPixelSize(attr
.GetFontSize());
12927 else if (!attr
.HasFontPixelSize() && currentStyle
.HasFontPixelSize())
12929 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE
);
12930 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE
);
12934 if (attr
.HasFontItalic() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_ITALIC
))
12936 if (currentStyle
.HasFontItalic())
12938 if (currentStyle
.GetFontStyle() != attr
.GetFontStyle())
12940 // Clash of attr - mark as such
12941 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_ITALIC
);
12942 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC
);
12946 currentStyle
.SetFontStyle(attr
.GetFontStyle());
12948 else if (!attr
.HasFontItalic() && currentStyle
.HasFontItalic())
12950 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_ITALIC
);
12951 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC
);
12954 if (attr
.HasFontFamily() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_FAMILY
))
12956 if (currentStyle
.HasFontFamily())
12958 if (currentStyle
.GetFontFamily() != attr
.GetFontFamily())
12960 // Clash of attr - mark as such
12961 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FAMILY
);
12962 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY
);
12966 currentStyle
.SetFontFamily(attr
.GetFontFamily());
12968 else if (!attr
.HasFontFamily() && currentStyle
.HasFontFamily())
12970 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FAMILY
);
12971 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY
);
12974 if (attr
.HasFontWeight() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_WEIGHT
))
12976 if (currentStyle
.HasFontWeight())
12978 if (currentStyle
.GetFontWeight() != attr
.GetFontWeight())
12980 // Clash of attr - mark as such
12981 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_WEIGHT
);
12982 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT
);
12986 currentStyle
.SetFontWeight(attr
.GetFontWeight());
12988 else if (!attr
.HasFontWeight() && currentStyle
.HasFontWeight())
12990 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_WEIGHT
);
12991 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT
);
12994 if (attr
.HasFontFaceName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_FACE
))
12996 if (currentStyle
.HasFontFaceName())
12998 wxString
faceName1(currentStyle
.GetFontFaceName());
12999 wxString
faceName2(attr
.GetFontFaceName());
13001 if (faceName1
!= faceName2
)
13003 // Clash of attr - mark as such
13004 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FACE
);
13005 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FACE
);
13009 currentStyle
.SetFontFaceName(attr
.GetFontFaceName());
13011 else if (!attr
.HasFontFaceName() && currentStyle
.HasFontFaceName())
13013 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FACE
);
13014 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FACE
);
13017 if (attr
.HasFontUnderlined() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_UNDERLINE
))
13019 if (currentStyle
.HasFontUnderlined())
13021 if (currentStyle
.GetFontUnderlined() != attr
.GetFontUnderlined())
13023 // Clash of attr - mark as such
13024 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
13025 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
13029 currentStyle
.SetFontUnderlined(attr
.GetFontUnderlined());
13031 else if (!attr
.HasFontUnderlined() && currentStyle
.HasFontUnderlined())
13033 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
13034 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
13037 if (attr
.HasFontStrikethrough() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_STRIKETHROUGH
))
13039 if (currentStyle
.HasFontStrikethrough())
13041 if (currentStyle
.GetFontStrikethrough() != attr
.GetFontStrikethrough())
13043 // Clash of attr - mark as such
13044 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH
);
13045 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH
);
13049 currentStyle
.SetFontStrikethrough(attr
.GetFontStrikethrough());
13051 else if (!attr
.HasFontStrikethrough() && currentStyle
.HasFontStrikethrough())
13053 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH
);
13054 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH
);
13057 if (attr
.HasTextColour() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_TEXT_COLOUR
))
13059 if (currentStyle
.HasTextColour())
13061 if (currentStyle
.GetTextColour() != attr
.GetTextColour())
13063 // Clash of attr - mark as such
13064 clashingAttr
.AddFlag(wxTEXT_ATTR_TEXT_COLOUR
);
13065 currentStyle
.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR
);
13069 currentStyle
.SetTextColour(attr
.GetTextColour());
13071 else if (!attr
.HasTextColour() && currentStyle
.HasTextColour())
13073 clashingAttr
.AddFlag(wxTEXT_ATTR_TEXT_COLOUR
);
13074 currentStyle
.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR
);
13077 if (attr
.HasBackgroundColour() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BACKGROUND_COLOUR
))
13079 if (currentStyle
.HasBackgroundColour())
13081 if (currentStyle
.GetBackgroundColour() != attr
.GetBackgroundColour())
13083 // Clash of attr - mark as such
13084 clashingAttr
.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
13085 currentStyle
.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
13089 currentStyle
.SetBackgroundColour(attr
.GetBackgroundColour());
13091 else if (!attr
.HasBackgroundColour() && currentStyle
.HasBackgroundColour())
13093 clashingAttr
.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
13094 currentStyle
.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
13097 if (attr
.HasAlignment() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_ALIGNMENT
))
13099 if (currentStyle
.HasAlignment())
13101 if (currentStyle
.GetAlignment() != attr
.GetAlignment())
13103 // Clash of attr - mark as such
13104 clashingAttr
.AddFlag(wxTEXT_ATTR_ALIGNMENT
);
13105 currentStyle
.RemoveFlag(wxTEXT_ATTR_ALIGNMENT
);
13109 currentStyle
.SetAlignment(attr
.GetAlignment());
13111 else if (!attr
.HasAlignment() && currentStyle
.HasAlignment())
13113 clashingAttr
.AddFlag(wxTEXT_ATTR_ALIGNMENT
);
13114 currentStyle
.RemoveFlag(wxTEXT_ATTR_ALIGNMENT
);
13117 if (attr
.HasTabs() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_TABS
))
13119 if (currentStyle
.HasTabs())
13121 if (!wxRichTextTabsEq(currentStyle
.GetTabs(), attr
.GetTabs()))
13123 // Clash of attr - mark as such
13124 clashingAttr
.AddFlag(wxTEXT_ATTR_TABS
);
13125 currentStyle
.RemoveFlag(wxTEXT_ATTR_TABS
);
13129 currentStyle
.SetTabs(attr
.GetTabs());
13131 else if (!attr
.HasTabs() && currentStyle
.HasTabs())
13133 clashingAttr
.AddFlag(wxTEXT_ATTR_TABS
);
13134 currentStyle
.RemoveFlag(wxTEXT_ATTR_TABS
);
13137 if (attr
.HasLeftIndent() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LEFT_INDENT
))
13139 if (currentStyle
.HasLeftIndent())
13141 if (currentStyle
.GetLeftIndent() != attr
.GetLeftIndent() || currentStyle
.GetLeftSubIndent() != attr
.GetLeftSubIndent())
13143 // Clash of attr - mark as such
13144 clashingAttr
.AddFlag(wxTEXT_ATTR_LEFT_INDENT
);
13145 currentStyle
.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT
);
13149 currentStyle
.SetLeftIndent(attr
.GetLeftIndent(), attr
.GetLeftSubIndent());
13151 else if (!attr
.HasLeftIndent() && currentStyle
.HasLeftIndent())
13153 clashingAttr
.AddFlag(wxTEXT_ATTR_LEFT_INDENT
);
13154 currentStyle
.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT
);
13157 if (attr
.HasRightIndent() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_RIGHT_INDENT
))
13159 if (currentStyle
.HasRightIndent())
13161 if (currentStyle
.GetRightIndent() != attr
.GetRightIndent())
13163 // Clash of attr - mark as such
13164 clashingAttr
.AddFlag(wxTEXT_ATTR_RIGHT_INDENT
);
13165 currentStyle
.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT
);
13169 currentStyle
.SetRightIndent(attr
.GetRightIndent());
13171 else if (!attr
.HasRightIndent() && currentStyle
.HasRightIndent())
13173 clashingAttr
.AddFlag(wxTEXT_ATTR_RIGHT_INDENT
);
13174 currentStyle
.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT
);
13177 if (attr
.HasParagraphSpacingAfter() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARA_SPACING_AFTER
))
13179 if (currentStyle
.HasParagraphSpacingAfter())
13181 if (currentStyle
.GetParagraphSpacingAfter() != attr
.GetParagraphSpacingAfter())
13183 // Clash of attr - mark as such
13184 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
13185 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
13189 currentStyle
.SetParagraphSpacingAfter(attr
.GetParagraphSpacingAfter());
13191 else if (!attr
.HasParagraphSpacingAfter() && currentStyle
.HasParagraphSpacingAfter())
13193 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
13194 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
13197 if (attr
.HasParagraphSpacingBefore() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARA_SPACING_BEFORE
))
13199 if (currentStyle
.HasParagraphSpacingBefore())
13201 if (currentStyle
.GetParagraphSpacingBefore() != attr
.GetParagraphSpacingBefore())
13203 // Clash of attr - mark as such
13204 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
13205 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
13209 currentStyle
.SetParagraphSpacingBefore(attr
.GetParagraphSpacingBefore());
13211 else if (!attr
.HasParagraphSpacingBefore() && currentStyle
.HasParagraphSpacingBefore())
13213 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
13214 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
13217 if (attr
.HasLineSpacing() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LINE_SPACING
))
13219 if (currentStyle
.HasLineSpacing())
13221 if (currentStyle
.GetLineSpacing() != attr
.GetLineSpacing())
13223 // Clash of attr - mark as such
13224 clashingAttr
.AddFlag(wxTEXT_ATTR_LINE_SPACING
);
13225 currentStyle
.RemoveFlag(wxTEXT_ATTR_LINE_SPACING
);
13229 currentStyle
.SetLineSpacing(attr
.GetLineSpacing());
13231 else if (!attr
.HasLineSpacing() && currentStyle
.HasLineSpacing())
13233 clashingAttr
.AddFlag(wxTEXT_ATTR_LINE_SPACING
);
13234 currentStyle
.RemoveFlag(wxTEXT_ATTR_LINE_SPACING
);
13237 if (attr
.HasCharacterStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_CHARACTER_STYLE_NAME
))
13239 if (currentStyle
.HasCharacterStyleName())
13241 if (currentStyle
.GetCharacterStyleName() != attr
.GetCharacterStyleName())
13243 // Clash of attr - mark as such
13244 clashingAttr
.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
13245 currentStyle
.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
13249 currentStyle
.SetCharacterStyleName(attr
.GetCharacterStyleName());
13251 else if (!attr
.HasCharacterStyleName() && currentStyle
.HasCharacterStyleName())
13253 clashingAttr
.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
13254 currentStyle
.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
13257 if (attr
.HasParagraphStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
))
13259 if (currentStyle
.HasParagraphStyleName())
13261 if (currentStyle
.GetParagraphStyleName() != attr
.GetParagraphStyleName())
13263 // Clash of attr - mark as such
13264 clashingAttr
.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
13265 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
13269 currentStyle
.SetParagraphStyleName(attr
.GetParagraphStyleName());
13271 else if (!attr
.HasParagraphStyleName() && currentStyle
.HasParagraphStyleName())
13273 clashingAttr
.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
13274 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
13277 if (attr
.HasListStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LIST_STYLE_NAME
))
13279 if (currentStyle
.HasListStyleName())
13281 if (currentStyle
.GetListStyleName() != attr
.GetListStyleName())
13283 // Clash of attr - mark as such
13284 clashingAttr
.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
13285 currentStyle
.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
13289 currentStyle
.SetListStyleName(attr
.GetListStyleName());
13291 else if (!attr
.HasListStyleName() && currentStyle
.HasListStyleName())
13293 clashingAttr
.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
13294 currentStyle
.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
13297 if (attr
.HasBulletStyle() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_STYLE
))
13299 if (currentStyle
.HasBulletStyle())
13301 if (currentStyle
.GetBulletStyle() != attr
.GetBulletStyle())
13303 // Clash of attr - mark as such
13304 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_STYLE
);
13305 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE
);
13309 currentStyle
.SetBulletStyle(attr
.GetBulletStyle());
13311 else if (!attr
.HasBulletStyle() && currentStyle
.HasBulletStyle())
13313 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_STYLE
);
13314 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE
);
13317 if (attr
.HasBulletNumber() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_NUMBER
))
13319 if (currentStyle
.HasBulletNumber())
13321 if (currentStyle
.GetBulletNumber() != attr
.GetBulletNumber())
13323 // Clash of attr - mark as such
13324 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NUMBER
);
13325 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER
);
13329 currentStyle
.SetBulletNumber(attr
.GetBulletNumber());
13331 else if (!attr
.HasBulletNumber() && currentStyle
.HasBulletNumber())
13333 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NUMBER
);
13334 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER
);
13337 if (attr
.HasBulletText() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_TEXT
))
13339 if (currentStyle
.HasBulletText())
13341 if (currentStyle
.GetBulletText() != attr
.GetBulletText())
13343 // Clash of attr - mark as such
13344 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_TEXT
);
13345 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT
);
13350 currentStyle
.SetBulletText(attr
.GetBulletText());
13351 currentStyle
.SetBulletFont(attr
.GetBulletFont());
13354 else if (!attr
.HasBulletText() && currentStyle
.HasBulletText())
13356 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_TEXT
);
13357 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT
);
13360 if (attr
.HasBulletName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_NAME
))
13362 if (currentStyle
.HasBulletName())
13364 if (currentStyle
.GetBulletName() != attr
.GetBulletName())
13366 // Clash of attr - mark as such
13367 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NAME
);
13368 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NAME
);
13373 currentStyle
.SetBulletName(attr
.GetBulletName());
13376 else if (!attr
.HasBulletName() && currentStyle
.HasBulletName())
13378 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NAME
);
13379 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NAME
);
13382 if (attr
.HasURL() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_URL
))
13384 if (currentStyle
.HasURL())
13386 if (currentStyle
.GetURL() != attr
.GetURL())
13388 // Clash of attr - mark as such
13389 clashingAttr
.AddFlag(wxTEXT_ATTR_URL
);
13390 currentStyle
.RemoveFlag(wxTEXT_ATTR_URL
);
13395 currentStyle
.SetURL(attr
.GetURL());
13398 else if (!attr
.HasURL() && currentStyle
.HasURL())
13400 clashingAttr
.AddFlag(wxTEXT_ATTR_URL
);
13401 currentStyle
.RemoveFlag(wxTEXT_ATTR_URL
);
13404 if (attr
.HasTextEffects() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_EFFECTS
))
13406 if (currentStyle
.HasTextEffects())
13408 // We need to find the bits in the new attr that are different:
13409 // just look at those bits that are specified by the new attr.
13411 // We need to remove the bits and flags that are not common between current attr
13412 // and new attr. In so doing we need to take account of the styles absent from one or more of the
13413 // previous styles.
13415 int currentRelevantTextEffects
= currentStyle
.GetTextEffects() & attr
.GetTextEffectFlags();
13416 int newRelevantTextEffects
= attr
.GetTextEffects() & attr
.GetTextEffectFlags();
13418 if (currentRelevantTextEffects
!= newRelevantTextEffects
)
13420 // Find the text effects that were different, using XOR
13421 int differentEffects
= currentRelevantTextEffects
^ newRelevantTextEffects
;
13423 // Clash of attr - mark as such
13424 clashingAttr
.SetTextEffectFlags(clashingAttr
.GetTextEffectFlags() | differentEffects
);
13425 currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~differentEffects
);
13430 currentStyle
.SetTextEffects(attr
.GetTextEffects());
13431 currentStyle
.SetTextEffectFlags(attr
.GetTextEffectFlags());
13434 // Mask out the flags and values that cannot be common because they were absent in one or more objecrs
13435 // that we've looked at so far
13436 currentStyle
.SetTextEffects(currentStyle
.GetTextEffects() & ~absentAttr
.GetTextEffectFlags());
13437 currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~absentAttr
.GetTextEffectFlags());
13439 if (currentStyle
.GetTextEffectFlags() == 0)
13440 currentStyle
.RemoveFlag(wxTEXT_ATTR_EFFECTS
);
13442 else if (!attr
.HasTextEffects() && currentStyle
.HasTextEffects())
13444 clashingAttr
.AddFlag(wxTEXT_ATTR_EFFECTS
);
13445 currentStyle
.RemoveFlag(wxTEXT_ATTR_EFFECTS
);
13448 if (attr
.HasOutlineLevel() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_OUTLINE_LEVEL
))
13450 if (currentStyle
.HasOutlineLevel())
13452 if (currentStyle
.GetOutlineLevel() != attr
.GetOutlineLevel())
13454 // Clash of attr - mark as such
13455 clashingAttr
.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
13456 currentStyle
.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
13460 currentStyle
.SetOutlineLevel(attr
.GetOutlineLevel());
13462 else if (!attr
.HasOutlineLevel() && currentStyle
.HasOutlineLevel())
13464 clashingAttr
.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
13465 currentStyle
.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
13469 WX_DEFINE_OBJARRAY(wxRichTextVariantArray
);
13472 WX_DEFINE_OBJARRAY(wxRichTextAttrArray
);
13474 IMPLEMENT_DYNAMIC_CLASS(wxRichTextProperties
, wxObject
)
13476 bool wxRichTextProperties::operator==(const wxRichTextProperties
& props
) const
13478 if (m_properties
.GetCount() != props
.GetCount())
13482 for (i
= 0; i
< m_properties
.GetCount(); i
++)
13484 const wxVariant
& var1
= m_properties
[i
];
13485 int idx
= props
.Find(var1
.GetName());
13488 const wxVariant
& var2
= props
.m_properties
[idx
];
13489 if (!(var1
== var2
))
13496 wxArrayString
wxRichTextProperties::GetPropertyNames() const
13500 for (i
= 0; i
< m_properties
.GetCount(); i
++)
13502 arr
.Add(m_properties
[i
].GetName());
13507 int wxRichTextProperties::Find(const wxString
& name
) const
13510 for (i
= 0; i
< m_properties
.GetCount(); i
++)
13512 if (m_properties
[i
].GetName() == name
)
13518 bool wxRichTextProperties::Remove(const wxString
& name
)
13520 int idx
= Find(name
);
13523 m_properties
.RemoveAt(idx
);
13530 wxVariant
* wxRichTextProperties::FindOrCreateProperty(const wxString
& name
)
13532 int idx
= Find(name
);
13533 if (idx
== wxNOT_FOUND
)
13534 SetProperty(name
, wxString());
13536 if (idx
!= wxNOT_FOUND
)
13538 return & (*this)[idx
];
13544 const wxVariant
& wxRichTextProperties::GetProperty(const wxString
& name
) const
13546 static const wxVariant nullVariant
;
13547 int idx
= Find(name
);
13549 return m_properties
[idx
];
13551 return nullVariant
;
13554 wxString
wxRichTextProperties::GetPropertyString(const wxString
& name
) const
13556 return GetProperty(name
).GetString();
13559 long wxRichTextProperties::GetPropertyLong(const wxString
& name
) const
13561 return GetProperty(name
).GetLong();
13564 bool wxRichTextProperties::GetPropertyBool(const wxString
& name
) const
13566 return GetProperty(name
).GetBool();
13569 double wxRichTextProperties::GetPropertyDouble(const wxString
& name
) const
13571 return GetProperty(name
).GetDouble();
13574 void wxRichTextProperties::SetProperty(const wxVariant
& variant
)
13576 wxASSERT(!variant
.GetName().IsEmpty());
13578 int idx
= Find(variant
.GetName());
13581 m_properties
.Add(variant
);
13583 m_properties
[idx
] = variant
;
13586 void wxRichTextProperties::SetProperty(const wxString
& name
, const wxVariant
& variant
)
13588 int idx
= Find(name
);
13589 wxVariant
var(variant
);
13593 m_properties
.Add(var
);
13595 m_properties
[idx
] = var
;
13598 void wxRichTextProperties::SetProperty(const wxString
& name
, const wxString
& value
)
13600 SetProperty(name
, wxVariant(value
, name
));
13603 void wxRichTextProperties::SetProperty(const wxString
& name
, long value
)
13605 SetProperty(name
, wxVariant(value
, name
));
13608 void wxRichTextProperties::SetProperty(const wxString
& name
, double value
)
13610 SetProperty(name
, wxVariant(value
, name
));
13613 void wxRichTextProperties::SetProperty(const wxString
& name
, bool value
)
13615 SetProperty(name
, wxVariant(value
, name
));
13618 void wxRichTextProperties::RemoveProperties(const wxRichTextProperties
& properties
)
13621 for (i
= 0; i
< properties
.GetCount(); i
++)
13623 wxString name
= properties
.GetProperties()[i
].GetName();
13624 if (HasProperty(name
))
13629 void wxRichTextProperties::MergeProperties(const wxRichTextProperties
& properties
)
13632 for (i
= 0; i
< properties
.GetCount(); i
++)
13634 SetProperty(properties
.GetProperties()[i
]);
13638 wxRichTextObject
* wxRichTextObjectAddress::GetObject(wxRichTextParagraphLayoutBox
* topLevelContainer
) const
13640 if (m_address
.GetCount() == 0)
13641 return topLevelContainer
;
13643 wxRichTextCompositeObject
* p
= topLevelContainer
;
13645 while (p
&& i
< m_address
.GetCount())
13647 int pos
= m_address
[i
];
13648 wxASSERT(pos
>= 0 && pos
< (int) p
->GetChildren().GetCount());
13649 if (pos
< 0 || pos
>= (int) p
->GetChildren().GetCount())
13652 wxRichTextObject
* p1
= p
->GetChild(pos
);
13653 if (i
== (m_address
.GetCount()-1))
13656 p
= wxDynamicCast(p1
, wxRichTextCompositeObject
);
13662 bool wxRichTextObjectAddress::Create(wxRichTextParagraphLayoutBox
* topLevelContainer
, wxRichTextObject
* obj
)
13666 if (topLevelContainer
== obj
)
13669 wxRichTextObject
* o
= obj
;
13672 wxRichTextCompositeObject
* p
= wxDynamicCast(o
->GetParent(), wxRichTextCompositeObject
);
13676 int pos
= p
->GetChildren().IndexOf(o
);
13680 m_address
.Insert(pos
, 0);
13682 if (p
== topLevelContainer
)
13691 bool wxRichTextSelection::operator==(const wxRichTextSelection
& sel
) const
13693 if (m_container
!= sel
.m_container
)
13695 if (m_ranges
.GetCount() != sel
.m_ranges
.GetCount())
13698 for (i
= 0; i
< m_ranges
.GetCount(); i
++)
13699 if (!(m_ranges
[i
] == sel
.m_ranges
[i
]))
13704 // Get the selections appropriate to the specified object, if any; returns wxRICHTEXT_NO_SELECTION if none
13705 // or none at the level of the object's container.
13706 wxRichTextRangeArray
wxRichTextSelection::GetSelectionForObject(wxRichTextObject
* obj
) const
13710 wxRichTextParagraphLayoutBox
* container
= obj
->GetParentContainer();
13712 if (container
== m_container
)
13715 container
= obj
->GetContainer();
13718 if (container
->GetParent())
13720 // If we found that our object's container is within the range of
13721 // a selection higher up, then assume the whole original object
13722 // is also selected.
13723 wxRichTextParagraphLayoutBox
* parentContainer
= container
->GetParentContainer();
13724 if (parentContainer
== m_container
)
13726 if (WithinSelection(container
->GetRange().GetStart(), m_ranges
))
13728 wxRichTextRangeArray ranges
;
13729 ranges
.Add(obj
->GetRange());
13734 container
= parentContainer
;
13743 return wxRichTextRangeArray();
13746 // Is the given position within the selection?
13747 bool wxRichTextSelection::WithinSelection(long pos
, wxRichTextObject
* obj
) const
13753 wxRichTextRangeArray selectionRanges
= GetSelectionForObject(obj
);
13754 return WithinSelection(pos
, selectionRanges
);
13758 // Is the given position within the selection range?
13759 bool wxRichTextSelection::WithinSelection(long pos
, const wxRichTextRangeArray
& ranges
)
13762 for (i
= 0; i
< ranges
.GetCount(); i
++)
13764 const wxRichTextRange
& range
= ranges
[i
];
13765 if (pos
>= range
.GetStart() && pos
<= range
.GetEnd())
13771 // Is the given range completely within the selection range?
13772 bool wxRichTextSelection::WithinSelection(const wxRichTextRange
& range
, const wxRichTextRangeArray
& ranges
)
13775 for (i
= 0; i
< ranges
.GetCount(); i
++)
13777 const wxRichTextRange
& eachRange
= ranges
[i
];
13778 if (range
.IsWithin(eachRange
))
13784 IMPLEMENT_CLASS(wxRichTextDrawingHandler
, wxObject
)
13785 IMPLEMENT_CLASS(wxRichTextDrawingContext
, wxObject
)
13787 wxRichTextDrawingContext::wxRichTextDrawingContext(wxRichTextBuffer
* buffer
)
13791 if (m_buffer
&& m_buffer
->GetRichTextCtrl())
13792 EnableVirtualAttributes(m_buffer
->GetRichTextCtrl()->GetVirtualAttributesEnabled());
13795 bool wxRichTextDrawingContext::HasVirtualAttributes(wxRichTextObject
* obj
) const
13797 if (!GetVirtualAttributesEnabled())
13800 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
13803 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13804 if (handler
->HasVirtualAttributes(obj
))
13807 node
= node
->GetNext();
13812 wxRichTextAttr
wxRichTextDrawingContext::GetVirtualAttributes(wxRichTextObject
* obj
) const
13814 wxRichTextAttr attr
;
13815 if (!GetVirtualAttributesEnabled())
13818 // We apply all handlers, so we can may combine several different attributes
13819 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
13822 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13823 if (handler
->HasVirtualAttributes(obj
))
13825 bool success
= handler
->GetVirtualAttributes(attr
, obj
);
13827 wxUnusedVar(success
);
13830 node
= node
->GetNext();
13835 bool wxRichTextDrawingContext::ApplyVirtualAttributes(wxRichTextAttr
& attr
, wxRichTextObject
* obj
) const
13837 if (!GetVirtualAttributesEnabled())
13840 if (HasVirtualAttributes(obj
))
13842 wxRichTextAttr
a(GetVirtualAttributes(obj
));
13850 int wxRichTextDrawingContext::GetVirtualSubobjectAttributesCount(wxRichTextObject
* obj
) const
13852 if (!GetVirtualAttributesEnabled())
13855 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
13858 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13859 int count
= handler
->GetVirtualSubobjectAttributesCount(obj
);
13863 node
= node
->GetNext();
13868 int wxRichTextDrawingContext::GetVirtualSubobjectAttributes(wxRichTextObject
* obj
, wxArrayInt
& positions
, wxRichTextAttrArray
& attributes
) const
13870 if (!GetVirtualAttributesEnabled())
13873 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
13876 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13877 if (handler
->GetVirtualSubobjectAttributes(obj
, positions
, attributes
))
13878 return positions
.GetCount();
13880 node
= node
->GetNext();
13885 bool wxRichTextDrawingContext::HasVirtualText(const wxRichTextPlainText
* obj
) const
13887 if (!GetVirtualAttributesEnabled())
13890 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
13893 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13894 if (handler
->HasVirtualText(obj
))
13897 node
= node
->GetNext();
13902 bool wxRichTextDrawingContext::GetVirtualText(const wxRichTextPlainText
* obj
, wxString
& text
) const
13904 if (!GetVirtualAttributesEnabled())
13907 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
13910 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13911 if (handler
->GetVirtualText(obj
, text
))
13914 node
= node
->GetNext();
13919 /// Adds a handler to the end
13920 void wxRichTextBuffer::AddDrawingHandler(wxRichTextDrawingHandler
*handler
)
13922 sm_drawingHandlers
.Append(handler
);
13925 /// Inserts a handler at the front
13926 void wxRichTextBuffer::InsertDrawingHandler(wxRichTextDrawingHandler
*handler
)
13928 sm_drawingHandlers
.Insert( handler
);
13931 /// Removes a handler
13932 bool wxRichTextBuffer::RemoveDrawingHandler(const wxString
& name
)
13934 wxRichTextDrawingHandler
*handler
= FindDrawingHandler(name
);
13937 sm_drawingHandlers
.DeleteObject(handler
);
13945 wxRichTextDrawingHandler
* wxRichTextBuffer::FindDrawingHandler(const wxString
& name
)
13947 wxList::compatibility_iterator node
= sm_drawingHandlers
.GetFirst();
13950 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13951 if (handler
->GetName().Lower() == name
.Lower()) return handler
;
13953 node
= node
->GetNext();
13958 void wxRichTextBuffer::CleanUpDrawingHandlers()
13960 wxList::compatibility_iterator node
= sm_drawingHandlers
.GetFirst();
13963 wxRichTextDrawingHandler
* handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13964 wxList::compatibility_iterator next
= node
->GetNext();
13969 sm_drawingHandlers
.Clear();
13972 void wxRichTextBuffer::AddFieldType(wxRichTextFieldType
*fieldType
)
13974 sm_fieldTypes
[fieldType
->GetName()] = fieldType
;
13977 bool wxRichTextBuffer::RemoveFieldType(const wxString
& name
)
13979 wxRichTextFieldTypeHashMap::iterator it
= sm_fieldTypes
.find(name
);
13980 if (it
== sm_fieldTypes
.end())
13984 wxRichTextFieldType
* fieldType
= it
->second
;
13985 sm_fieldTypes
.erase(it
);
13991 wxRichTextFieldType
*wxRichTextBuffer::FindFieldType(const wxString
& name
)
13993 wxRichTextFieldTypeHashMap::iterator it
= sm_fieldTypes
.find(name
);
13994 if (it
== sm_fieldTypes
.end())
14000 void wxRichTextBuffer::CleanUpFieldTypes()
14002 wxRichTextFieldTypeHashMap::iterator it
;
14003 for( it
= sm_fieldTypes
.begin(); it
!= sm_fieldTypes
.end(); ++it
)
14005 wxRichTextFieldType
* fieldType
= it
->second
;
14009 sm_fieldTypes
.clear();