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
.GetFont().IsOk())
4683 wxCheckSetFont(dc
, attr
.GetFont());
4684 lineSpacing
= (int) (double(dc
.GetCharHeight()) * (double(attr
.GetLineSpacing())/10.0 - 1.0));
4687 // Start position for each line relative to the paragraph
4688 int startPositionFirstLine
= leftIndent
;
4689 int startPositionSubsequentLines
= leftIndent
+ leftSubIndent
;
4691 // If we have a bullet in this paragraph, the start position for the first line's text
4692 // is actually leftIndent + leftSubIndent.
4693 if (attr
.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE
)
4694 startPositionFirstLine
= startPositionSubsequentLines
;
4696 long lastEndPos
= GetRange().GetStart()-1;
4697 long lastCompletedEndPos
= lastEndPos
;
4699 int currentWidth
= 0;
4700 SetPosition(rect
.GetPosition());
4702 wxPoint
currentPosition(0, spaceBeforePara
); // We will calculate lines relative to paragraph
4705 int maxHeight
= currentPosition
.y
;
4710 int lineDescent
= 0;
4712 wxRichTextObjectList::compatibility_iterator node
;
4714 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4716 wxArrayInt partialExtents
;
4719 int paraDescent
= 0;
4721 // This calculates the partial text extents
4722 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, rect
.GetPosition(), parentRect
.GetSize(), & partialExtents
);
4724 node
= m_children
.GetFirst();
4727 wxRichTextObject
* child
= node
->GetData();
4729 //child->SetCachedSize(wxDefaultSize);
4730 child
->Layout(dc
, context
, rect
, style
);
4732 node
= node
->GetNext();
4738 // We may need to go back to a previous child, in which case create the new line,
4739 // find the child corresponding to the start position of the string, and
4742 wxRect availableRect
;
4744 node
= m_children
.GetFirst();
4747 wxRichTextObject
* child
= node
->GetData();
4749 // If floating, ignore. We already laid out floats.
4750 // Also ignore if empty object, except if we haven't got any
4752 if ((child
->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode())
4753 || !child
->IsShown() || (child
->GetRange().GetLength() == 0 && maxHeight
> spaceBeforePara
)
4756 node
= node
->GetNext();
4760 // If this is e.g. a composite text box, it will need to be laid out itself.
4761 // But if just a text fragment or image, for example, this will
4762 // do nothing. NB: won't we need to set the position after layout?
4763 // since for example if position is dependent on vertical line size, we
4764 // can't tell the position until the size is determined. So possibly introduce
4765 // another layout phase.
4767 // We may only be looking at part of a child, if we searched back for wrapping
4768 // and found a suitable point some way into the child. So get the size for the fragment
4771 long nextBreakPos
= GetFirstLineBreakPosition(lastEndPos
+1);
4772 long lastPosToUse
= child
->GetRange().GetEnd();
4773 bool lineBreakInThisObject
= (nextBreakPos
> -1 && nextBreakPos
<= child
->GetRange().GetEnd());
4775 if (lineBreakInThisObject
)
4776 lastPosToUse
= nextBreakPos
;
4779 int childDescent
= 0;
4781 int startOffset
= (lineCount
== 0 ? startPositionFirstLine
: startPositionSubsequentLines
);
4782 availableRect
= wxRect(rect
.x
+ startOffset
, rect
.y
+ currentPosition
.y
,
4783 rect
.width
- startOffset
- rightIndent
, rect
.height
);
4785 if (child
->IsTopLevel())
4787 wxSize oldSize
= child
->GetCachedSize();
4789 child
->Invalidate(wxRICHTEXT_ALL
);
4790 child
->SetPosition(wxPoint(0, 0));
4792 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4793 // lays out the object again using the minimum size
4794 // The position will be determined by its location in its line,
4795 // and not by the child's actual position.
4796 child
->LayoutToBestSize(dc
, context
, buffer
,
4797 attr
, child
->GetAttributes(), availableRect
, parentRect
, style
);
4799 if (oldSize
!= child
->GetCachedSize())
4801 partialExtents
.Clear();
4803 // Recalculate the partial text extents since the child object changed size
4804 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), parentRect
.GetSize(), & partialExtents
);
4808 // Problem: we need to layout composites here for which we need the available width,
4809 // but we can't get the available width without using the float collector which
4810 // needs to know the object height.
4812 if ((nextBreakPos
== -1) && (lastEndPos
== child
->GetRange().GetStart() - 1)) // i.e. we want to get the whole thing
4814 childSize
= child
->GetCachedSize();
4815 childDescent
= child
->GetDescent();
4819 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4820 // Get height only, then the width using the partial extents
4821 GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
, wxPoint(0,0), parentRect
.GetSize());
4822 childSize
.x
= wxRichTextGetRangeWidth(*this, wxRichTextRange(lastEndPos
+1, lastPosToUse
), partialExtents
);
4824 GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, rect
.GetPosition(), parentRect
.GetSize());
4829 int loopIterations
= 0;
4831 // If there are nested objects that need to lay themselves out, we have to do this in a
4832 // loop because the height of the object may well depend on the available width.
4833 // And because of floating object positioning, the available width depends on the
4834 // height of the object and whether it will clash with the floating objects.
4835 // So, we see whether the available width changes due to the presence of floating images.
4836 // If it does, then we'll use the new restricted width to find the object height again.
4837 // If this causes another restriction in the available width, we'll try again, until
4838 // either we lose patience or the available width settles down.
4843 wxRect oldAvailableRect
= availableRect
;
4845 // Available width depends on the floating objects and the line height.
4846 // Note: the floating objects may be placed vertically along the two sides of
4847 // buffer, so we may have different available line widths with different
4848 // [startY, endY]. So, we can't determine how wide the available
4849 // space is until we know the exact line height.
4850 if (childDescent
== 0)
4852 lineHeight
= wxMax(lineHeight
, childSize
.y
);
4853 lineDescent
= maxDescent
;
4854 lineAscent
= maxAscent
;
4858 lineDescent
= wxMax(childDescent
, maxDescent
);
4859 lineAscent
= wxMax(childSize
.y
-childDescent
, maxAscent
);
4861 lineHeight
= wxMax(lineHeight
, (lineDescent
+ lineAscent
));
4863 if (wxRichTextBuffer::GetFloatingLayoutMode() && collector
)
4865 wxRect floatAvailableRect
= collector
->GetAvailableRect(rect
.y
+ currentPosition
.y
, rect
.y
+ currentPosition
.y
+ lineHeight
);
4867 // Adjust availableRect to the space that is available when taking floating objects into account.
4869 if (floatAvailableRect
.x
+ startOffset
> availableRect
.x
)
4871 int newX
= floatAvailableRect
.x
+ startOffset
;
4872 int newW
= availableRect
.width
- (newX
- availableRect
.x
);
4873 availableRect
.x
= newX
;
4874 availableRect
.width
= newW
;
4877 if (floatAvailableRect
.width
< availableRect
.width
)
4878 availableRect
.width
= floatAvailableRect
.width
;
4881 currentPosition
.x
= availableRect
.x
- rect
.x
;
4883 if (child
->IsTopLevel() && loopIterations
<= 20)
4885 if (availableRect
!= oldAvailableRect
)
4887 wxSize oldSize
= child
->GetCachedSize();
4889 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4890 // lays out the object again using the minimum size
4891 child
->Invalidate(wxRICHTEXT_ALL
);
4892 child
->LayoutToBestSize(dc
, context
, buffer
,
4893 attr
, child
->GetAttributes(), availableRect
, parentRect
.GetSize(), style
);
4894 childSize
= child
->GetCachedSize();
4895 childDescent
= child
->GetDescent();
4897 if (oldSize
!= child
->GetCachedSize())
4899 partialExtents
.Clear();
4901 // Recalculate the partial text extents since the child object changed size
4902 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), parentRect
.GetSize(), & partialExtents
);
4905 // Go around the loop finding the available rect for the given floating objects
4915 if (child
->IsTopLevel())
4917 // We can move it to the correct position at this point
4918 // TODO: probably need to add margin
4919 child
->Move(GetPosition() + wxPoint(currentWidth
+ (wxMax(leftIndent
, leftIndent
+ leftSubIndent
)), currentPosition
.y
));
4923 // 1) There was a line break BEFORE the natural break
4924 // 2) There was a line break AFTER the natural break
4925 // 3) It's the last line
4926 // 4) The child still fits (carry on) - 'else' clause
4928 if ((lineBreakInThisObject
&& (childSize
.x
+ currentWidth
<= availableRect
.width
))
4930 (childSize
.x
+ currentWidth
> availableRect
.width
)
4933 ((childSize
.x
+ currentWidth
<= availableRect
.width
) && !node
->GetNext())
4937 long wrapPosition
= 0;
4938 if ((childSize
.x
+ currentWidth
<= availableRect
.width
) && !node
->GetNext() && !lineBreakInThisObject
)
4939 wrapPosition
= child
->GetRange().GetEnd();
4942 // Find a place to wrap. This may walk back to previous children,
4943 // for example if a word spans several objects.
4944 // Note: one object must contains only one wxTextAtrr, so the line height will not
4945 // change inside one object. Thus, we can pass the remain line width to the
4946 // FindWrapPosition function.
4947 if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos
+1, child
->GetRange().GetEnd()), dc
, context
, availableRect
.width
, wrapPosition
, & partialExtents
))
4949 // If the function failed, just cut it off at the end of this child.
4950 wrapPosition
= child
->GetRange().GetEnd();
4953 // FindWrapPosition can still return a value that will put us in an endless wrapping loop
4954 if (wrapPosition
<= lastCompletedEndPos
)
4955 wrapPosition
= wxMax(lastCompletedEndPos
+1,child
->GetRange().GetEnd());
4957 // Line end position shouldn't be the same as the end, or greater.
4958 if (wrapPosition
>= GetRange().GetEnd())
4959 wrapPosition
= wxMax(0, GetRange().GetEnd()-1);
4961 // wxLogDebug(wxT("Split at %ld"), wrapPosition);
4963 // Let's find the actual size of the current line now
4965 wxRichTextRange
actualRange(lastCompletedEndPos
+1, wrapPosition
);
4969 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4970 if (!child
->IsEmpty())
4972 // Get height only, then the width using the partial extents
4973 GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
, wxPoint(0,0), parentRect
.GetSize());
4974 actualSize
.x
= wxRichTextGetRangeWidth(*this, actualRange
, partialExtents
);
4978 GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, wxPoint(0,0), parentRect
.GetSize());
4980 currentWidth
= actualSize
.x
;
4982 // The descent for the whole line at this point, is the correct max descent
4983 maxDescent
= childDescent
;
4985 maxAscent
= actualSize
.y
-childDescent
;
4987 // lineHeight is given by the height for the whole line, since it will
4988 // take into account ascend/descend.
4989 lineHeight
= actualSize
.y
;
4991 if (lineHeight
== 0 && buffer
)
4993 wxFont
font(buffer
->GetFontTable().FindFont(attr
));
4994 wxCheckSetFont(dc
, font
);
4995 lineHeight
= dc
.GetCharHeight();
4998 if (maxDescent
== 0)
5001 dc
.GetTextExtent(wxT("X"), & w
, &h
, & maxDescent
);
5005 wxRichTextLine
* line
= AllocateLine(lineCount
);
5007 // Set relative range so we won't have to change line ranges when paragraphs are moved
5008 line
->SetRange(wxRichTextRange(actualRange
.GetStart() - GetRange().GetStart(), actualRange
.GetEnd() - GetRange().GetStart()));
5009 line
->SetPosition(currentPosition
);
5010 line
->SetSize(wxSize(currentWidth
, lineHeight
));
5011 line
->SetDescent(maxDescent
);
5013 maxHeight
= currentPosition
.y
+ lineHeight
;
5015 // Now move down a line. TODO: add margins, spacing
5016 currentPosition
.y
+= lineHeight
;
5017 currentPosition
.y
+= lineSpacing
;
5020 maxWidth
= wxMax(maxWidth
, currentWidth
+startOffset
);
5025 // TODO: account for zero-length objects
5026 // wxASSERT(wrapPosition > lastCompletedEndPos);
5028 lastEndPos
= wrapPosition
;
5029 lastCompletedEndPos
= lastEndPos
;
5033 if (wrapPosition
< GetRange().GetEnd()-1)
5035 // May need to set the node back to a previous one, due to searching back in wrapping
5036 wxRichTextObject
* childAfterWrapPosition
= FindObjectAtPosition(wrapPosition
+1);
5037 if (childAfterWrapPosition
)
5038 node
= m_children
.Find(childAfterWrapPosition
);
5040 node
= node
->GetNext();
5043 node
= node
->GetNext();
5045 // Apply paragraph styles such as alignment to the wrapped line
5046 ApplyParagraphStyle(line
, attr
, availableRect
, dc
);
5050 // We still fit, so don't add a line, and keep going
5051 currentWidth
+= childSize
.x
;
5053 if (childDescent
== 0)
5055 // An object with a zero descend value wants to take up the whole
5056 // height regardless of baseline
5057 lineHeight
= wxMax(lineHeight
, childSize
.y
);
5061 maxDescent
= wxMax(childDescent
, maxDescent
);
5062 maxAscent
= wxMax(childSize
.y
-childDescent
, maxAscent
);
5065 lineHeight
= wxMax(lineHeight
, (maxDescent
+ maxAscent
));
5067 maxWidth
= wxMax(maxWidth
, currentWidth
+startOffset
);
5068 lastEndPos
= child
->GetRange().GetEnd();
5070 node
= node
->GetNext();
5074 //wxASSERT(!(lastCompletedEndPos != -1 && lastCompletedEndPos < GetRange().GetEnd()-1));
5076 // Add the last line - it's the current pos -> last para pos
5077 // Substract -1 because the last position is always the end-paragraph position.
5078 if (lastCompletedEndPos
<= GetRange().GetEnd()-1)
5080 currentPosition
.x
= (lineCount
== 0 ? startPositionFirstLine
: startPositionSubsequentLines
);
5082 wxRichTextLine
* line
= AllocateLine(lineCount
);
5084 wxRichTextRange
actualRange(lastCompletedEndPos
+1, GetRange().GetEnd()-1);
5086 // Set relative range so we won't have to change line ranges when paragraphs are moved
5087 line
->SetRange(wxRichTextRange(actualRange
.GetStart() - GetRange().GetStart(), actualRange
.GetEnd() - GetRange().GetStart()));
5089 line
->SetPosition(currentPosition
);
5091 if (lineHeight
== 0 && buffer
)
5093 wxFont
font(buffer
->GetFontTable().FindFont(attr
));
5094 wxCheckSetFont(dc
, font
);
5095 lineHeight
= dc
.GetCharHeight();
5098 if (maxDescent
== 0)
5101 dc
.GetTextExtent(wxT("X"), & w
, &h
, & maxDescent
);
5104 line
->SetSize(wxSize(currentWidth
, lineHeight
));
5105 line
->SetDescent(maxDescent
);
5106 currentPosition
.y
+= lineHeight
;
5107 currentPosition
.y
+= lineSpacing
;
5111 // Remove remaining unused line objects, if any
5112 ClearUnusedLines(lineCount
);
5114 // We need to add back the margins etc.
5116 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
5117 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxWidth
, currentPosition
.y
+ spaceAfterPara
));
5118 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
5119 SetCachedSize(marginRect
.GetSize());
5122 // The maximum size is the length of the paragraph stretched out into a line.
5123 // So if there were a single word, or an image, or a fixed-size text box, the object could be shrunk around
5124 // this size. TODO: take into account line breaks.
5126 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
5127 contentRect
= wxRect(wxPoint(0, 0), wxSize(paraSize
.x
+ wxMax(leftIndent
, leftIndent
+ leftSubIndent
) + rightIndent
, currentPosition
.y
+ spaceAfterPara
));
5128 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
5129 SetMaxSize(marginRect
.GetSize());
5132 // Find the greatest minimum size. Currently we only look at non-text objects,
5133 // which isn't ideal but it would be slow to find the maximum word width to
5134 // use as the minimum.
5137 node
= m_children
.GetFirst();
5140 wxRichTextObject
* child
= node
->GetData();
5142 // If floating, ignore. We already laid out floats.
5143 // Also ignore if empty object, except if we haven't got any
5145 if ((!child
->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && child
->GetRange().GetLength() != 0 && !wxDynamicCast(child
, wxRichTextPlainText
))
5147 if (child
->GetCachedSize().x
> minWidth
)
5148 minWidth
= child
->GetMinSize().x
;
5150 node
= node
->GetNext();
5153 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
5154 contentRect
= wxRect(wxPoint(0, 0), wxSize(minWidth
, currentPosition
.y
+ spaceAfterPara
));
5155 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
5156 SetMinSize(marginRect
.GetSize());
5159 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5160 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
5161 // Use the text extents to calculate the size of each fragment in each line
5162 wxRichTextLineList::compatibility_iterator lineNode
= m_cachedLines
.GetFirst();
5165 wxRichTextLine
* line
= lineNode
->GetData();
5166 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5168 // Loop through objects until we get to the one within range
5169 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
5173 wxRichTextObject
* child
= node2
->GetData();
5175 if (child
->GetRange().GetLength() > 0 && !child
->GetRange().IsOutside(lineRange
))
5177 wxRichTextRange rangeToUse
= lineRange
;
5178 rangeToUse
.LimitTo(child
->GetRange());
5180 // Find the size of the child from the text extents, and store in an array
5181 // for drawing later
5183 if (rangeToUse
.GetStart() > GetRange().GetStart())
5184 left
= partialExtents
[(rangeToUse
.GetStart()-1) - GetRange().GetStart()];
5185 int right
= partialExtents
[rangeToUse
.GetEnd() - GetRange().GetStart()];
5186 int sz
= right
- left
;
5187 line
->GetObjectSizes().Add(sz
);
5189 else if (child
->GetRange().GetStart() > lineRange
.GetEnd())
5190 // Can break out of inner loop now since we've passed this line's range
5193 node2
= node2
->GetNext();
5196 lineNode
= lineNode
->GetNext();
5204 /// Apply paragraph styles, such as centering, to wrapped lines
5205 /// TODO: take into account box attributes, possibly
5206 void wxRichTextParagraph::ApplyParagraphStyle(wxRichTextLine
* line
, const wxRichTextAttr
& attr
, const wxRect
& rect
, wxDC
& dc
)
5208 if (!attr
.HasAlignment())
5211 wxPoint pos
= line
->GetPosition();
5212 wxPoint originalPos
= pos
;
5213 wxSize size
= line
->GetSize();
5215 // centering, right-justification
5216 if (attr
.HasAlignment() && attr
.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE
)
5218 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
5219 pos
.x
= (rect
.GetWidth() - rightIndent
- size
.x
)/2 + pos
.x
;
5220 line
->SetPosition(pos
);
5222 else if (attr
.HasAlignment() && attr
.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT
)
5224 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
5225 pos
.x
= pos
.x
+ rect
.GetWidth() - size
.x
- rightIndent
;
5226 line
->SetPosition(pos
);
5229 if (pos
!= originalPos
)
5231 wxPoint inc
= pos
- originalPos
;
5233 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5237 wxRichTextObject
* child
= node
->GetData();
5238 if (child
->IsTopLevel() && !child
->GetRange().IsOutside(line
->GetAbsoluteRange()))
5239 child
->Move(child
->GetPosition() + inc
);
5241 node
= node
->GetNext();
5246 /// Insert text at the given position
5247 bool wxRichTextParagraph::InsertText(long pos
, const wxString
& text
)
5249 wxRichTextObject
* childToUse
= NULL
;
5250 wxRichTextObjectList::compatibility_iterator nodeToUse
= wxRichTextObjectList::compatibility_iterator();
5252 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5255 wxRichTextObject
* child
= node
->GetData();
5256 if (child
->GetRange().Contains(pos
) && child
->GetRange().GetLength() > 0)
5263 node
= node
->GetNext();
5268 wxRichTextPlainText
* textObject
= wxDynamicCast(childToUse
, wxRichTextPlainText
);
5271 int posInString
= pos
- textObject
->GetRange().GetStart();
5273 wxString newText
= textObject
->GetText().Mid(0, posInString
) +
5274 text
+ textObject
->GetText().Mid(posInString
);
5275 textObject
->SetText(newText
);
5277 int textLength
= text
.length();
5279 textObject
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart(),
5280 textObject
->GetRange().GetEnd() + textLength
));
5282 // Increment the end range of subsequent fragments in this paragraph.
5283 // We'll set the paragraph range itself at a higher level.
5285 wxRichTextObjectList::compatibility_iterator node
= nodeToUse
->GetNext();
5288 wxRichTextObject
* child
= node
->GetData();
5289 child
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart() + textLength
,
5290 textObject
->GetRange().GetEnd() + textLength
));
5292 node
= node
->GetNext();
5299 // TODO: if not a text object, insert at closest position, e.g. in front of it
5305 // Don't pass parent initially to suppress auto-setting of parent range.
5306 // We'll do that at a higher level.
5307 wxRichTextPlainText
* textObject
= new wxRichTextPlainText(text
, this);
5309 AppendChild(textObject
);
5316 void wxRichTextParagraph::Copy(const wxRichTextParagraph
& obj
)
5318 wxRichTextCompositeObject::Copy(obj
);
5321 /// Clear the cached lines
5322 void wxRichTextParagraph::ClearLines()
5324 WX_CLEAR_LIST(wxRichTextLineList
, m_cachedLines
);
5327 /// Get/set the object size for the given range. Returns false if the range
5328 /// is invalid for this object.
5329 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
5331 if (!range
.IsWithin(GetRange()))
5334 if (flags
& wxRICHTEXT_UNFORMATTED
)
5336 // Just use unformatted data, assume no line breaks
5339 wxArrayInt childExtents
;
5348 int maxLineHeight
= 0;
5350 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5353 wxRichTextObject
* child
= node
->GetData();
5354 if (!child
->GetRange().IsOutside(range
))
5356 // Floating objects have a zero size within the paragraph.
5357 if (child
->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode())
5362 if (partialExtents
->GetCount() > 0)
5363 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5367 partialExtents
->Add(0 /* zero size */ + lastSize
);
5374 wxRichTextRange rangeToUse
= range
;
5375 rangeToUse
.LimitTo(child
->GetRange());
5376 int childDescent
= 0;
5378 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we've already cached the size,
5379 // but it's only going to be used after caching has taken place.
5380 if ((flags
& wxRICHTEXT_HEIGHT_ONLY
) && child
->GetCachedSize().y
!= 0)
5382 childDescent
= child
->GetDescent();
5383 childSize
= child
->GetCachedSize();
5385 if (childDescent
== 0)
5387 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5391 maxDescent
= wxMax(maxDescent
, childDescent
);
5392 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5395 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5397 sz
.y
= wxMax(sz
.y
, maxLineHeight
);
5398 sz
.x
+= childSize
.x
;
5399 descent
= maxDescent
;
5401 else if (child
->IsTopLevel())
5403 childDescent
= child
->GetDescent();
5404 childSize
= child
->GetCachedSize();
5406 if (childDescent
== 0)
5408 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5412 maxDescent
= wxMax(maxDescent
, childDescent
);
5413 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5416 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5418 sz
.y
= wxMax(sz
.y
, maxLineHeight
);
5419 sz
.x
+= childSize
.x
;
5420 descent
= maxDescent
;
5422 // FIXME: this won't change the original values.
5423 // Should we be calling GetRangeSize above instead of using cached values?
5425 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange()))
5427 child
->SetCachedSize(childSize
);
5428 child
->SetDescent(childDescent
);
5435 if (partialExtents
->GetCount() > 0)
5436 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5440 partialExtents
->Add(childSize
.x
+ lastSize
);
5443 else if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, context
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
), parentSize
, p
))
5445 if (childDescent
== 0)
5447 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5451 maxDescent
= wxMax(maxDescent
, childDescent
);
5452 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5455 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5457 sz
.y
= wxMax(sz
.y
, maxLineHeight
);
5458 sz
.x
+= childSize
.x
;
5459 descent
= maxDescent
;
5461 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange()))
5463 child
->SetCachedSize(childSize
);
5464 child
->SetDescent(childDescent
);
5470 if (partialExtents
->GetCount() > 0)
5471 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5476 for (i
= 0; i
< childExtents
.GetCount(); i
++)
5478 partialExtents
->Add(childExtents
[i
] + lastSize
);
5488 node
= node
->GetNext();
5494 // Use formatted data, with line breaks
5497 // We're going to loop through each line, and then for each line,
5498 // call GetRangeSize for the fragment that comprises that line.
5499 // Only we have to do that multiple times within the line, because
5500 // the line may be broken into pieces. For now ignore line break commands
5501 // (so we can assume that getting the unformatted size for a fragment
5502 // within a line is the actual size)
5504 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5507 wxRichTextLine
* line
= node
->GetData();
5508 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5509 if (!lineRange
.IsOutside(range
))
5513 int maxLineHeight
= 0;
5514 int maxLineWidth
= 0;
5516 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
5519 wxRichTextObject
* child
= node2
->GetData();
5521 if ((!child
->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && !child
->GetRange().IsOutside(lineRange
))
5523 wxRichTextRange rangeToUse
= lineRange
;
5524 rangeToUse
.LimitTo(child
->GetRange());
5525 if (child
->IsTopLevel())
5526 rangeToUse
= child
->GetOwnRange();
5529 int childDescent
= 0;
5530 if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, context
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
), parentSize
))
5532 if (childDescent
== 0)
5534 // Assume that if descent is zero, this child can occupy the full line height
5535 // and does not need space for the line's maximum descent. So we influence
5536 // the overall max line height only.
5537 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5541 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5542 maxDescent
= wxMax(maxAscent
, childDescent
);
5544 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5545 maxLineWidth
+= childSize
.x
;
5549 node2
= node2
->GetNext();
5552 descent
= wxMax(descent
, maxDescent
);
5554 // Increase size by a line (TODO: paragraph spacing)
5555 sz
.y
+= maxLineHeight
;
5556 sz
.x
= wxMax(sz
.x
, maxLineWidth
);
5558 node
= node
->GetNext();
5565 /// Finds the absolute position and row height for the given character position
5566 bool wxRichTextParagraph::FindPosition(wxDC
& dc
, wxRichTextDrawingContext
& context
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
5570 wxRichTextLine
* line
= ((wxRichTextParagraphLayoutBox
*)GetParent())->GetLineAtPosition(0);
5572 *height
= line
->GetSize().y
;
5574 *height
= dc
.GetCharHeight();
5576 // -1 means 'the start of the buffer'.
5579 pt
= pt
+ line
->GetPosition();
5584 // The final position in a paragraph is taken to mean the position
5585 // at the start of the next paragraph.
5586 if (index
== GetRange().GetEnd())
5588 wxRichTextParagraphLayoutBox
* parent
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
5589 wxASSERT( parent
!= NULL
);
5591 // Find the height at the next paragraph, if any
5592 wxRichTextLine
* line
= parent
->GetLineAtPosition(index
+ 1);
5595 *height
= line
->GetSize().y
;
5596 pt
= line
->GetAbsolutePosition();
5600 *height
= dc
.GetCharHeight();
5601 int indent
= ConvertTenthsMMToPixels(dc
, m_attributes
.GetLeftIndent());
5602 pt
= wxPoint(indent
, GetCachedSize().y
);
5608 if (index
< GetRange().GetStart() || index
> GetRange().GetEnd())
5611 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5614 wxRichTextLine
* line
= node
->GetData();
5615 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5616 if (index
>= lineRange
.GetStart() && index
<= lineRange
.GetEnd())
5618 // If this is the last point in the line, and we're forcing the
5619 // returned value to be the start of the next line, do the required
5621 if (index
== lineRange
.GetEnd() && forceLineStart
)
5623 if (node
->GetNext())
5625 wxRichTextLine
* nextLine
= node
->GetNext()->GetData();
5626 *height
= nextLine
->GetSize().y
;
5627 pt
= nextLine
->GetAbsolutePosition();
5632 pt
.y
= line
->GetPosition().y
+ GetPosition().y
;
5634 wxRichTextRange
r(lineRange
.GetStart(), index
);
5638 // We find the size of the line up to this point,
5639 // then we can add this size to the line start position and
5640 // paragraph start position to find the actual position.
5642 if (GetRangeSize(r
, rangeSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, line
->GetPosition()+ GetPosition()))
5644 pt
.x
= line
->GetPosition().x
+ GetPosition().x
+ rangeSize
.x
;
5645 *height
= line
->GetSize().y
;
5652 node
= node
->GetNext();
5658 /// Hit-testing: returns a flag indicating hit test details, plus
5659 /// information about position
5660 int wxRichTextParagraph::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
5663 return wxRICHTEXT_HITTEST_NONE
;
5665 // If we're in the top-level container, then we can return
5666 // a suitable hit test code even if the point is outside the container area,
5667 // so that we can position the caret sensibly even if we don't
5668 // click on valid content. If we're not at the top-level, and the point
5669 // is not within this paragraph object, then we don't want to stop more
5670 // precise hit-testing from working prematurely, so return immediately.
5671 // NEW STRATEGY: use the parent boundary to test whether we're in the
5672 // right region, not the paragraph, since the paragraph may be positioned
5673 // some way in from where the user clicks.
5676 wxRichTextObject
* tempObj
, *tempContextObj
;
5677 if (GetParent() && GetParent()->wxRichTextObject::HitTest(dc
, context
, pt
, tmpPos
, & tempObj
, & tempContextObj
, flags
) == wxRICHTEXT_HITTEST_NONE
)
5678 return wxRICHTEXT_HITTEST_NONE
;
5681 wxRichTextObjectList::compatibility_iterator objNode
= m_children
.GetFirst();
5684 wxRichTextObject
* child
= objNode
->GetData();
5685 // Don't recurse if we have wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS,
5686 // and also, if this seems composite but actually is marked as atomic,
5688 if (child
->IsTopLevel() && ((flags
& wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS
) == 0) &&
5689 (! (((flags
& wxRICHTEXT_HITTEST_HONOUR_ATOMIC
) != 0) && child
->IsAtomic())))
5692 int hitTest
= child
->HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
);
5693 if (hitTest
!= wxRICHTEXT_HITTEST_NONE
)
5698 objNode
= objNode
->GetNext();
5701 wxPoint paraPos
= GetPosition();
5703 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5706 wxRichTextLine
* line
= node
->GetData();
5707 wxPoint linePos
= paraPos
+ line
->GetPosition();
5708 wxSize lineSize
= line
->GetSize();
5709 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5711 if (pt
.y
<= linePos
.y
+ lineSize
.y
)
5713 if (pt
.x
< linePos
.x
)
5715 textPosition
= lineRange
.GetStart();
5716 *obj
= FindObjectAtPosition(textPosition
);
5717 *contextObj
= GetContainer();
5718 return wxRICHTEXT_HITTEST_BEFORE
|wxRICHTEXT_HITTEST_OUTSIDE
;
5720 else if (pt
.x
>= (linePos
.x
+ lineSize
.x
))
5722 textPosition
= lineRange
.GetEnd();
5723 *obj
= FindObjectAtPosition(textPosition
);
5724 *contextObj
= GetContainer();
5725 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
;
5729 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5730 wxArrayInt partialExtents
;
5735 // This calculates the partial text extents
5736 GetRangeSize(lineRange
, paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, linePos
, wxDefaultSize
, & partialExtents
);
5738 int lastX
= linePos
.x
;
5740 for (i
= 0; i
< partialExtents
.GetCount(); i
++)
5742 int nextX
= partialExtents
[i
] + linePos
.x
;
5744 if (pt
.x
>= lastX
&& pt
.x
<= nextX
)
5746 textPosition
= i
+ lineRange
.GetStart(); // minus 1?
5748 *obj
= FindObjectAtPosition(textPosition
);
5749 *contextObj
= GetContainer();
5751 // So now we know it's between i-1 and i.
5752 // Let's see if we can be more precise about
5753 // which side of the position it's on.
5755 int midPoint
= (nextX
+ lastX
)/2;
5756 if (pt
.x
>= midPoint
)
5757 return wxRICHTEXT_HITTEST_AFTER
;
5759 return wxRICHTEXT_HITTEST_BEFORE
;
5766 int lastX
= linePos
.x
;
5767 for (i
= lineRange
.GetStart(); i
<= lineRange
.GetEnd(); i
++)
5772 wxRichTextRange
rangeToUse(lineRange
.GetStart(), i
);
5774 GetRangeSize(rangeToUse
, childSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, linePos
);
5776 int nextX
= childSize
.x
+ linePos
.x
;
5778 if (pt
.x
>= lastX
&& pt
.x
<= nextX
)
5782 *obj
= FindObjectAtPosition(textPosition
);
5783 *contextObj
= GetContainer();
5785 // So now we know it's between i-1 and i.
5786 // Let's see if we can be more precise about
5787 // which side of the position it's on.
5789 int midPoint
= (nextX
+ lastX
)/2;
5790 if (pt
.x
>= midPoint
)
5791 return wxRICHTEXT_HITTEST_AFTER
;
5793 return wxRICHTEXT_HITTEST_BEFORE
;
5804 node
= node
->GetNext();
5807 return wxRICHTEXT_HITTEST_NONE
;
5810 /// Split an object at this position if necessary, and return
5811 /// the previous object, or NULL if inserting at beginning.
5812 wxRichTextObject
* wxRichTextParagraph::SplitAt(long pos
, wxRichTextObject
** previousObject
)
5814 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5817 wxRichTextObject
* child
= node
->GetData();
5819 if (pos
== child
->GetRange().GetStart())
5823 if (node
->GetPrevious())
5824 *previousObject
= node
->GetPrevious()->GetData();
5826 *previousObject
= NULL
;
5832 if (child
->GetRange().Contains(pos
))
5834 // This should create a new object, transferring part of
5835 // the content to the old object and the rest to the new object.
5836 wxRichTextObject
* newObject
= child
->DoSplit(pos
);
5838 // If we couldn't split this object, just insert in front of it.
5841 // Maybe this is an empty string, try the next one
5846 // Insert the new object after 'child'
5847 if (node
->GetNext())
5848 m_children
.Insert(node
->GetNext(), newObject
);
5850 m_children
.Append(newObject
);
5851 newObject
->SetParent(this);
5854 *previousObject
= child
;
5860 node
= node
->GetNext();
5863 *previousObject
= NULL
;
5867 /// Move content to a list from obj on
5868 void wxRichTextParagraph::MoveToList(wxRichTextObject
* obj
, wxList
& list
)
5870 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(obj
);
5873 wxRichTextObject
* child
= node
->GetData();
5876 wxRichTextObjectList::compatibility_iterator oldNode
= node
;
5878 node
= node
->GetNext();
5880 m_children
.DeleteNode(oldNode
);
5884 /// Add content back from list
5885 void wxRichTextParagraph::MoveFromList(wxList
& list
)
5887 for (wxList::compatibility_iterator node
= list
.GetFirst(); node
; node
= node
->GetNext())
5889 AppendChild((wxRichTextObject
*) node
->GetData());
5894 void wxRichTextParagraph::CalculateRange(long start
, long& end
)
5896 wxRichTextCompositeObject::CalculateRange(start
, end
);
5898 // Add one for end of paragraph
5901 m_range
.SetRange(start
, end
);
5904 /// Find the object at the given position
5905 wxRichTextObject
* wxRichTextParagraph::FindObjectAtPosition(long position
)
5907 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5910 wxRichTextObject
* obj
= node
->GetData();
5911 if (obj
->GetRange().Contains(position
) ||
5912 obj
->GetRange().GetStart() == position
||
5913 obj
->GetRange().GetEnd() == position
)
5916 node
= node
->GetNext();
5921 /// Get the plain text searching from the start or end of the range.
5922 /// The resulting string may be shorter than the range given.
5923 bool wxRichTextParagraph::GetContiguousPlainText(wxString
& text
, const wxRichTextRange
& range
, bool fromStart
)
5925 text
= wxEmptyString
;
5929 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5932 wxRichTextObject
* obj
= node
->GetData();
5933 if (!obj
->GetRange().IsOutside(range
))
5935 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5938 text
+= textObj
->GetTextForRange(range
);
5946 node
= node
->GetNext();
5951 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetLast();
5954 wxRichTextObject
* obj
= node
->GetData();
5955 if (!obj
->GetRange().IsOutside(range
))
5957 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5960 text
= textObj
->GetTextForRange(range
) + text
;
5964 text
= wxT(" ") + text
;
5968 node
= node
->GetPrevious();
5975 /// Find a suitable wrap position.
5976 bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange
& range
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int availableSpace
, long& wrapPosition
, wxArrayInt
* partialExtents
)
5978 if (range
.GetLength() <= 0)
5981 // Find the first position where the line exceeds the available space.
5983 long breakPosition
= range
.GetEnd();
5985 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5986 if (partialExtents
&& partialExtents
->GetCount() >= (size_t) (GetRange().GetLength()-1)) // the final position in a paragraph is the newline
5990 if (range
.GetStart() > GetRange().GetStart())
5991 widthBefore
= (*partialExtents
)[range
.GetStart() - GetRange().GetStart() - 1];
5996 for (i
= (size_t) range
.GetStart(); i
<= (size_t) range
.GetEnd(); i
++)
5998 int widthFromStartOfThisRange
= (*partialExtents
)[i
- GetRange().GetStart()] - widthBefore
;
6000 if (widthFromStartOfThisRange
> availableSpace
)
6002 breakPosition
= i
-1;
6010 // Binary chop for speed
6011 long minPos
= range
.GetStart();
6012 long maxPos
= range
.GetEnd();
6015 if (minPos
== maxPos
)
6018 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
6020 if (sz
.x
> availableSpace
)
6021 breakPosition
= minPos
- 1;
6024 else if ((maxPos
- minPos
) == 1)
6027 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
6029 if (sz
.x
> availableSpace
)
6030 breakPosition
= minPos
- 1;
6033 GetRangeSize(wxRichTextRange(range
.GetStart(), maxPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
6034 if (sz
.x
> availableSpace
)
6035 breakPosition
= maxPos
-1;
6041 long nextPos
= minPos
+ ((maxPos
- minPos
) / 2);
6044 GetRangeSize(wxRichTextRange(range
.GetStart(), nextPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
6046 if (sz
.x
> availableSpace
)
6058 // Now we know the last position on the line.
6059 // Let's try to find a word break.
6062 if (GetContiguousPlainText(plainText
, wxRichTextRange(range
.GetStart(), breakPosition
), false))
6064 int newLinePos
= plainText
.Find(wxRichTextLineBreakChar
);
6065 if (newLinePos
!= wxNOT_FOUND
)
6067 breakPosition
= wxMax(0, range
.GetStart() + newLinePos
);
6071 int spacePos
= plainText
.Find(wxT(' '), true);
6072 int tabPos
= plainText
.Find(wxT('\t'), true);
6073 int pos
= wxMax(spacePos
, tabPos
);
6074 if (pos
!= wxNOT_FOUND
)
6076 int positionsFromEndOfString
= plainText
.length() - pos
- 1;
6077 breakPosition
= breakPosition
- positionsFromEndOfString
;
6082 wrapPosition
= breakPosition
;
6087 /// Get the bullet text for this paragraph.
6088 wxString
wxRichTextParagraph::GetBulletText()
6090 if (GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
||
6091 (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
))
6092 return wxEmptyString
;
6094 int number
= GetAttributes().GetBulletNumber();
6097 if ((GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ARABIC
) || (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
))
6099 text
.Printf(wxT("%d"), number
);
6101 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_UPPER
)
6103 // TODO: Unicode, and also check if number > 26
6104 text
.Printf(wxT("%c"), (wxChar
) (number
+64));
6106 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_LOWER
)
6108 // TODO: Unicode, and also check if number > 26
6109 text
.Printf(wxT("%c"), (wxChar
) (number
+96));
6111 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER
)
6113 text
= wxRichTextDecimalToRoman(number
);
6115 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER
)
6117 text
= wxRichTextDecimalToRoman(number
);
6120 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
)
6122 text
= GetAttributes().GetBulletText();
6125 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
)
6127 // The outline style relies on the text being computed statically,
6128 // since it depends on other levels points (e.g. 1.2.1.1). So normally the bullet text
6129 // should be stored in the attributes; if not, just use the number for this
6130 // level, as previously computed.
6131 if (!GetAttributes().GetBulletText().IsEmpty())
6132 text
= GetAttributes().GetBulletText();
6135 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PARENTHESES
)
6137 text
= wxT("(") + text
+ wxT(")");
6139 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_RIGHT_PARENTHESIS
)
6141 text
= text
+ wxT(")");
6144 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PERIOD
)
6152 /// Allocate or reuse a line object
6153 wxRichTextLine
* wxRichTextParagraph::AllocateLine(int pos
)
6155 if (pos
< (int) m_cachedLines
.GetCount())
6157 wxRichTextLine
* line
= m_cachedLines
.Item(pos
)->GetData();
6163 wxRichTextLine
* line
= new wxRichTextLine(this);
6164 m_cachedLines
.Append(line
);
6169 /// Clear remaining unused line objects, if any
6170 bool wxRichTextParagraph::ClearUnusedLines(int lineCount
)
6172 int cachedLineCount
= m_cachedLines
.GetCount();
6173 if ((int) cachedLineCount
> lineCount
)
6175 for (int i
= 0; i
< (int) (cachedLineCount
- lineCount
); i
++)
6177 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetLast();
6178 wxRichTextLine
* line
= node
->GetData();
6179 m_cachedLines
.Erase(node
);
6186 /// Get combined attributes of the base style, paragraph style and character style. We use this to dynamically
6187 /// retrieve the actual style.
6188 wxRichTextAttr
wxRichTextParagraph::GetCombinedAttributes(const wxRichTextAttr
& contentStyle
, bool includingBoxAttr
) const
6190 wxRichTextAttr attr
;
6191 wxRichTextParagraphLayoutBox
* buf
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
6194 attr
= buf
->GetBasicStyle();
6195 if (!includingBoxAttr
)
6197 attr
.GetTextBoxAttr().Reset();
6198 // The background colour will be painted by the container, and we don't
6199 // want to unnecessarily overwrite the background when we're drawing text
6200 // because this may erase the guideline (which appears just under the text
6201 // if there's no padding).
6202 attr
.SetFlags(attr
.GetFlags() & ~wxTEXT_ATTR_BACKGROUND_COLOUR
);
6204 wxRichTextApplyStyle(attr
, GetAttributes());
6207 attr
= GetAttributes();
6209 wxRichTextApplyStyle(attr
, contentStyle
);
6213 /// Get combined attributes of the base style and paragraph style.
6214 wxRichTextAttr
wxRichTextParagraph::GetCombinedAttributes(bool includingBoxAttr
) const
6216 wxRichTextAttr attr
;
6217 wxRichTextParagraphLayoutBox
* buf
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
6220 attr
= buf
->GetBasicStyle();
6221 if (!includingBoxAttr
)
6222 attr
.GetTextBoxAttr().Reset();
6223 wxRichTextApplyStyle(attr
, GetAttributes());
6226 attr
= GetAttributes();
6231 // Create default tabstop array
6232 void wxRichTextParagraph::InitDefaultTabs()
6234 // create a default tab list at 10 mm each.
6235 for (int i
= 0; i
< 20; ++i
)
6237 sm_defaultTabs
.Add(i
*100);
6241 // Clear default tabstop array
6242 void wxRichTextParagraph::ClearDefaultTabs()
6244 sm_defaultTabs
.Clear();
6247 void wxRichTextParagraph::LayoutFloat(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
, wxRichTextFloatCollector
* floatCollector
)
6249 wxRichTextObjectList::compatibility_iterator node
= GetChildren().GetFirst();
6252 wxRichTextObject
* anchored
= node
->GetData();
6253 if (anchored
&& anchored
->IsFloating() && !floatCollector
->HasFloat(anchored
))
6256 wxRichTextAttr
parentAttr(GetAttributes());
6257 context
.ApplyVirtualAttributes(parentAttr
, this);
6260 wxRect availableSpace
= GetParent()->GetAvailableContentArea(dc
, context
, rect
);
6262 anchored
->LayoutToBestSize(dc
, context
, GetBuffer(),
6263 parentAttr
, anchored
->GetAttributes(),
6264 parentRect
, availableSpace
,
6266 wxSize size
= anchored
->GetCachedSize();
6270 anchored
->GetRangeSize(anchored
->GetRange(), size
, descent
, dc
, context
, style
);
6274 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().IsValid())
6276 offsetY
= anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetValue();
6277 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
6279 offsetY
= ConvertTenthsMMToPixels(dc
, offsetY
);
6283 int pos
= floatCollector
->GetFitPosition(anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode(), rect
.y
+ offsetY
, size
.y
);
6285 /* Update the offset */
6286 int newOffsetY
= pos
- rect
.y
;
6287 if (newOffsetY
!= offsetY
)
6289 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
6290 newOffsetY
= ConvertPixelsToTenthsMM(dc
, newOffsetY
);
6291 anchored
->GetAttributes().GetTextBoxAttr().GetTop().SetValue(newOffsetY
);
6294 if (anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT
)
6296 else if (anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT
)
6297 x
= rect
.x
+ rect
.width
- size
.x
;
6299 //anchored->SetPosition(wxPoint(x, pos));
6300 anchored
->Move(wxPoint(x
, pos
)); // should move children
6301 anchored
->SetCachedSize(size
);
6302 floatCollector
->CollectFloat(this, anchored
);
6305 node
= node
->GetNext();
6309 // Get the first position from pos that has a line break character.
6310 long wxRichTextParagraph::GetFirstLineBreakPosition(long pos
)
6312 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
6315 wxRichTextObject
* obj
= node
->GetData();
6316 if (pos
>= obj
->GetRange().GetStart() && pos
<= obj
->GetRange().GetEnd())
6318 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
6321 long breakPos
= textObj
->GetFirstLineBreakPosition(pos
);
6326 node
= node
->GetNext();
6333 * This object represents a line in a paragraph, and stores
6334 * offsets from the start of the paragraph representing the
6335 * start and end positions of the line.
6338 wxRichTextLine::wxRichTextLine(wxRichTextParagraph
* parent
)
6344 void wxRichTextLine::Init(wxRichTextParagraph
* parent
)
6347 m_range
.SetRange(-1, -1);
6348 m_pos
= wxPoint(0, 0);
6349 m_size
= wxSize(0, 0);
6351 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6352 m_objectSizes
.Clear();
6357 void wxRichTextLine::Copy(const wxRichTextLine
& obj
)
6359 m_range
= obj
.m_range
;
6360 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6361 m_objectSizes
= obj
.m_objectSizes
;
6365 /// Get the absolute object position
6366 wxPoint
wxRichTextLine::GetAbsolutePosition() const
6368 return m_parent
->GetPosition() + m_pos
;
6371 /// Get the absolute range
6372 wxRichTextRange
wxRichTextLine::GetAbsoluteRange() const
6374 wxRichTextRange
range(m_range
.GetStart() + m_parent
->GetRange().GetStart(), 0);
6375 range
.SetEnd(range
.GetStart() + m_range
.GetLength()-1);
6380 * wxRichTextPlainText
6381 * This object represents a single piece of text.
6384 IMPLEMENT_DYNAMIC_CLASS(wxRichTextPlainText
, wxRichTextObject
)
6386 wxRichTextPlainText::wxRichTextPlainText(const wxString
& text
, wxRichTextObject
* parent
, wxRichTextAttr
* style
):
6387 wxRichTextObject(parent
)
6390 SetAttributes(*style
);
6395 #define USE_KERNING_FIX 1
6397 // If insufficient tabs are defined, this is the tab width used
6398 #define WIDTH_FOR_DEFAULT_TABS 50
6401 bool wxRichTextPlainText::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int WXUNUSED(style
))
6403 wxRichTextParagraph
* para
= wxDynamicCast(GetParent(), wxRichTextParagraph
);
6404 wxASSERT (para
!= NULL
);
6406 wxRichTextAttr
textAttr(para
? para
->GetCombinedAttributes(GetAttributes(), false /* no box attributes */) : GetAttributes());
6407 context
.ApplyVirtualAttributes(textAttr
, this);
6409 // Let's make the assumption for now that for content in a paragraph, including
6410 // text, we never have a discontinuous selection. So we only deal with a
6412 wxRichTextRange selectionRange
;
6413 if (selection
.IsValid())
6415 wxRichTextRangeArray selectionRanges
= selection
.GetSelectionForObject(this);
6416 if (selectionRanges
.GetCount() > 0)
6417 selectionRange
= selectionRanges
[0];
6419 selectionRange
= wxRICHTEXT_NO_SELECTION
;
6422 selectionRange
= wxRICHTEXT_NO_SELECTION
;
6424 int offset
= GetRange().GetStart();
6426 wxString str
= m_text
;
6427 if (context
.HasVirtualText(this))
6429 if (!context
.GetVirtualText(this, str
) || str
.Length() != m_text
.Length())
6433 // Replace line break characters with spaces
6434 wxString toRemove
= wxRichTextLineBreakChar
;
6435 str
.Replace(toRemove
, wxT(" "));
6436 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS
|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS
)))
6439 long len
= range
.GetLength();
6440 wxString stringChunk
= str
.Mid(range
.GetStart() - offset
, (size_t) len
);
6442 // Test for the optimized situations where all is selected, or none
6445 wxFont
textFont(GetBuffer()->GetFontTable().FindFont(textAttr
));
6446 wxCheckSetFont(dc
, textFont
);
6447 int charHeight
= dc
.GetCharHeight();
6450 if ( textFont
.IsOk() )
6452 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SMALL_CAPITALS
))
6454 textFont
.SetPointSize((int) (textFont
.GetPointSize()*0.75));
6455 wxCheckSetFont(dc
, textFont
);
6456 charHeight
= dc
.GetCharHeight();
6459 if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
) )
6461 if (textFont
.IsUsingSizeInPixels())
6463 double size
= static_cast<double>(textFont
.GetPixelSize().y
) / wxSCRIPT_MUL_FACTOR
;
6464 textFont
.SetPixelSize(wxSize(0, static_cast<int>(size
)));
6470 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6471 textFont
.SetPointSize(static_cast<int>(size
));
6475 wxCheckSetFont(dc
, textFont
);
6477 else if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) )
6479 if (textFont
.IsUsingSizeInPixels())
6481 double size
= static_cast<double>(textFont
.GetPixelSize().y
) / wxSCRIPT_MUL_FACTOR
;
6482 textFont
.SetPixelSize(wxSize(0, static_cast<int>(size
)));
6484 int sub_height
= static_cast<int>(static_cast<double>(charHeight
) / wxSCRIPT_MUL_FACTOR
);
6485 y
= rect
.y
+ (rect
.height
- sub_height
+ (descent
- m_descent
));
6489 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6490 textFont
.SetPointSize(static_cast<int>(size
));
6492 int sub_height
= static_cast<int>(static_cast<double>(charHeight
) / wxSCRIPT_MUL_FACTOR
);
6493 y
= rect
.y
+ (rect
.height
- sub_height
+ (descent
- m_descent
));
6495 wxCheckSetFont(dc
, textFont
);
6500 y
= rect
.y
+ (rect
.height
- charHeight
- (descent
- m_descent
));
6506 y
= rect
.y
+ (rect
.height
- charHeight
- (descent
- m_descent
));
6509 // TODO: new selection code
6511 // (a) All selected.
6512 if (selectionRange
.GetStart() <= range
.GetStart() && selectionRange
.GetEnd() >= range
.GetEnd())
6514 DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, true);
6516 // (b) None selected.
6517 else if (selectionRange
.GetEnd() < range
.GetStart() || selectionRange
.GetStart() > range
.GetEnd())
6519 // Draw all unselected
6520 DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, false);
6524 // (c) Part selected, part not
6525 // Let's draw unselected chunk, selected chunk, then unselected chunk.
6527 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6529 // 1. Initial unselected chunk, if any, up until start of selection.
6530 if (selectionRange
.GetStart() > range
.GetStart() && selectionRange
.GetStart() <= range
.GetEnd())
6532 int r1
= range
.GetStart();
6533 int s1
= selectionRange
.GetStart()-1;
6534 int fragmentLen
= s1
- r1
+ 1;
6535 if (fragmentLen
< 0)
6537 wxLogDebug(wxT("Mid(%d, %d"), (int)(r1
- offset
), (int)fragmentLen
);
6539 wxString stringFragment
= str
.Mid(r1
- offset
, fragmentLen
);
6541 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false);
6544 if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
)
6546 // Compensate for kerning difference
6547 wxString
stringFragment2(str
.Mid(r1
- offset
, fragmentLen
+1));
6548 wxString
stringFragment3(str
.Mid(r1
- offset
+ fragmentLen
, 1));
6550 wxCoord w1
, h1
, w2
, h2
, w3
, h3
;
6551 dc
.GetTextExtent(stringFragment
, & w1
, & h1
);
6552 dc
.GetTextExtent(stringFragment2
, & w2
, & h2
);
6553 dc
.GetTextExtent(stringFragment3
, & w3
, & h3
);
6555 int kerningDiff
= (w1
+ w3
) - w2
;
6556 x
= x
- kerningDiff
;
6561 // 2. Selected chunk, if any.
6562 if (selectionRange
.GetEnd() >= range
.GetStart())
6564 int s1
= wxMax(selectionRange
.GetStart(), range
.GetStart());
6565 int s2
= wxMin(selectionRange
.GetEnd(), range
.GetEnd());
6567 int fragmentLen
= s2
- s1
+ 1;
6568 if (fragmentLen
< 0)
6570 wxLogDebug(wxT("Mid(%d, %d"), (int)(s1
- offset
), (int)fragmentLen
);
6572 wxString stringFragment
= str
.Mid(s1
- offset
, fragmentLen
);
6574 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, true);
6577 if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
)
6579 // Compensate for kerning difference
6580 wxString
stringFragment2(str
.Mid(s1
- offset
, fragmentLen
+1));
6581 wxString
stringFragment3(str
.Mid(s1
- offset
+ fragmentLen
, 1));
6583 wxCoord w1
, h1
, w2
, h2
, w3
, h3
;
6584 dc
.GetTextExtent(stringFragment
, & w1
, & h1
);
6585 dc
.GetTextExtent(stringFragment2
, & w2
, & h2
);
6586 dc
.GetTextExtent(stringFragment3
, & w3
, & h3
);
6588 int kerningDiff
= (w1
+ w3
) - w2
;
6589 x
= x
- kerningDiff
;
6594 // 3. Remaining unselected chunk, if any
6595 if (selectionRange
.GetEnd() < range
.GetEnd())
6597 int s2
= wxMin(selectionRange
.GetEnd()+1, range
.GetEnd());
6598 int r2
= range
.GetEnd();
6600 int fragmentLen
= r2
- s2
+ 1;
6601 if (fragmentLen
< 0)
6603 wxLogDebug(wxT("Mid(%d, %d"), (int)(s2
- offset
), (int)fragmentLen
);
6605 wxString stringFragment
= str
.Mid(s2
- offset
, fragmentLen
);
6607 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false);
6614 bool wxRichTextPlainText::DrawTabbedString(wxDC
& dc
, const wxRichTextAttr
& attr
, const wxRect
& rect
,wxString
& str
, wxCoord
& x
, wxCoord
& y
, bool selected
)
6616 bool hasTabs
= (str
.Find(wxT('\t')) != wxNOT_FOUND
);
6618 wxArrayInt tabArray
;
6622 if (attr
.GetTabs().IsEmpty())
6623 tabArray
= wxRichTextParagraph::GetDefaultTabs();
6625 tabArray
= attr
.GetTabs();
6626 tabCount
= tabArray
.GetCount();
6628 for (int i
= 0; i
< tabCount
; ++i
)
6630 int pos
= tabArray
[i
];
6631 pos
= ConvertTenthsMMToPixels(dc
, pos
);
6638 int nextTabPos
= -1;
6644 wxColour
highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
));
6645 wxColour
highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
));
6647 wxCheckSetBrush(dc
, wxBrush(highlightColour
));
6648 wxCheckSetPen(dc
, wxPen(highlightColour
));
6649 dc
.SetTextForeground(highlightTextColour
);
6650 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6654 dc
.SetTextForeground(attr
.GetTextColour());
6656 if (attr
.HasFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
) && attr
.GetBackgroundColour().IsOk())
6658 dc
.SetBackgroundMode(wxBRUSHSTYLE_SOLID
);
6659 dc
.SetTextBackground(attr
.GetBackgroundColour());
6662 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6665 wxCoord x_orig
= GetParent()->GetPosition().x
;
6668 // the string has a tab
6669 // break up the string at the Tab
6670 wxString stringChunk
= str
.BeforeFirst(wxT('\t'));
6671 str
= str
.AfterFirst(wxT('\t'));
6672 dc
.GetTextExtent(stringChunk
, & w
, & h
);
6674 bool not_found
= true;
6675 for (int i
= 0; i
< tabCount
&& not_found
; ++i
)
6677 nextTabPos
= tabArray
.Item(i
) + x_orig
;
6679 // Find the next tab position.
6680 // Even if we're at the end of the tab array, we must still draw the chunk.
6682 if (nextTabPos
> tabPos
|| (i
== (tabCount
- 1)))
6684 if (nextTabPos
<= tabPos
)
6686 int defaultTabWidth
= ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
);
6687 nextTabPos
= tabPos
+ defaultTabWidth
;
6694 wxRect
selRect(x
, rect
.y
, w
, rect
.GetHeight());
6695 dc
.DrawRectangle(selRect
);
6697 dc
.DrawText(stringChunk
, x
, y
);
6699 if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
))
6701 wxPen oldPen
= dc
.GetPen();
6702 wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1));
6703 dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5));
6704 wxCheckSetPen(dc
, oldPen
);
6710 hasTabs
= (str
.Find(wxT('\t')) != wxNOT_FOUND
);
6715 dc
.GetTextExtent(str
, & w
, & h
);
6718 wxRect
selRect(x
, rect
.y
, w
, rect
.GetHeight());
6719 dc
.DrawRectangle(selRect
);
6721 dc
.DrawText(str
, x
, y
);
6723 if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
))
6725 wxPen oldPen
= dc
.GetPen();
6726 wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1));
6727 dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5));
6728 wxCheckSetPen(dc
, oldPen
);
6737 /// Lay the item out
6738 bool wxRichTextPlainText::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& WXUNUSED(rect
), const wxRect
& WXUNUSED(parentRect
), int WXUNUSED(style
))
6740 // Only lay out if we haven't already cached the size
6742 GetRangeSize(GetRange(), m_size
, m_descent
, dc
, context
, 0, wxPoint(0, 0));
6744 // Eventually we want to have a reasonable estimate of minimum size.
6745 m_minSize
= wxSize(0, 0);
6750 void wxRichTextPlainText::Copy(const wxRichTextPlainText
& obj
)
6752 wxRichTextObject::Copy(obj
);
6754 m_text
= obj
.m_text
;
6757 /// Get/set the object size for the given range. Returns false if the range
6758 /// is invalid for this object.
6759 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
6761 if (!range
.IsWithin(GetRange()))
6764 wxRichTextParagraph
* para
= wxDynamicCast(GetParent(), wxRichTextParagraph
);
6765 wxASSERT (para
!= NULL
);
6767 int relativeX
= position
.x
- GetParent()->GetPosition().x
;
6769 wxRichTextAttr
textAttr(para
? para
->GetCombinedAttributes(GetAttributes()) : GetAttributes());
6770 context
.ApplyVirtualAttributes(textAttr
, (wxRichTextObject
*) this);
6772 // Always assume unformatted text, since at this level we have no knowledge
6773 // of line breaks - and we don't need it, since we'll calculate size within
6774 // formatted text by doing it in chunks according to the line ranges
6776 bool bScript(false);
6777 wxFont
font(GetBuffer()->GetFontTable().FindFont(textAttr
));
6780 if ( textAttr
.HasTextEffects() && ( (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
)
6781 || (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) ) )
6783 wxFont textFont
= font
;
6784 if (textFont
.IsUsingSizeInPixels())
6786 double size
= static_cast<double>(textFont
.GetPixelSize().y
) / wxSCRIPT_MUL_FACTOR
;
6787 textFont
.SetPixelSize(wxSize(0, static_cast<int>(size
)));
6791 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6792 textFont
.SetPointSize(static_cast<int>(size
));
6794 wxCheckSetFont(dc
, textFont
);
6797 else if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SMALL_CAPITALS
))
6799 wxFont textFont
= font
;
6800 textFont
.SetPointSize((int) (textFont
.GetPointSize()*0.75));
6801 wxCheckSetFont(dc
, textFont
);
6806 wxCheckSetFont(dc
, font
);
6810 bool haveDescent
= false;
6811 int startPos
= range
.GetStart() - GetRange().GetStart();
6812 long len
= range
.GetLength();
6814 wxString
str(m_text
);
6815 if (context
.HasVirtualText(this))
6817 if (!context
.GetVirtualText(this, str
) || str
.Length() != m_text
.Length())
6821 wxString toReplace
= wxRichTextLineBreakChar
;
6822 str
.Replace(toReplace
, wxT(" "));
6824 wxString stringChunk
= str
.Mid(startPos
, (size_t) len
);
6826 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS
|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS
)))
6827 stringChunk
.MakeUpper();
6831 if (stringChunk
.Find(wxT('\t')) != wxNOT_FOUND
)
6833 // the string has a tab
6834 wxArrayInt tabArray
;
6835 if (textAttr
.GetTabs().IsEmpty())
6836 tabArray
= wxRichTextParagraph::GetDefaultTabs();
6838 tabArray
= textAttr
.GetTabs();
6840 int tabCount
= tabArray
.GetCount();
6842 for (int i
= 0; i
< tabCount
; ++i
)
6844 int pos
= tabArray
[i
];
6845 pos
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, pos
);
6849 int nextTabPos
= -1;
6851 while (stringChunk
.Find(wxT('\t')) >= 0)
6853 int absoluteWidth
= 0;
6855 // the string has a tab
6856 // break up the string at the Tab
6857 wxString stringFragment
= stringChunk
.BeforeFirst(wxT('\t'));
6858 stringChunk
= stringChunk
.AfterFirst(wxT('\t'));
6863 if (partialExtents
->GetCount() > 0)
6864 oldWidth
= (*partialExtents
)[partialExtents
->GetCount()-1];
6868 // Add these partial extents
6870 dc
.GetPartialTextExtents(stringFragment
, p
);
6872 for (j
= 0; j
< p
.GetCount(); j
++)
6873 partialExtents
->Add(oldWidth
+ p
[j
]);
6875 if (partialExtents
->GetCount() > 0)
6876 absoluteWidth
= (*partialExtents
)[(*partialExtents
).GetCount()-1] + relativeX
;
6878 absoluteWidth
= relativeX
;
6882 dc
.GetTextExtent(stringFragment
, & w
, & h
);
6884 absoluteWidth
= width
+ relativeX
;
6888 bool notFound
= true;
6889 for (int i
= 0; i
< tabCount
&& notFound
; ++i
)
6891 nextTabPos
= tabArray
.Item(i
);
6893 // Find the next tab position.
6894 // Even if we're at the end of the tab array, we must still process the chunk.
6896 if (nextTabPos
> absoluteWidth
|| (i
== (tabCount
- 1)))
6898 if (nextTabPos
<= absoluteWidth
)
6900 int defaultTabWidth
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
);
6901 nextTabPos
= absoluteWidth
+ defaultTabWidth
;
6905 width
= nextTabPos
- relativeX
;
6908 partialExtents
->Add(width
);
6914 if (!stringChunk
.IsEmpty())
6919 if (partialExtents
->GetCount() > 0)
6920 oldWidth
= (*partialExtents
)[partialExtents
->GetCount()-1];
6924 // Add these partial extents
6926 dc
.GetPartialTextExtents(stringChunk
, p
);
6928 for (j
= 0; j
< p
.GetCount(); j
++)
6929 partialExtents
->Add(oldWidth
+ p
[j
]);
6933 dc
.GetTextExtent(stringChunk
, & w
, & h
, & descent
);
6941 int charHeight
= dc
.GetCharHeight();
6942 if ((*partialExtents
).GetCount() > 0)
6943 w
= (*partialExtents
)[partialExtents
->GetCount()-1];
6946 size
= wxSize(w
, charHeight
);
6950 size
= wxSize(width
, dc
.GetCharHeight());
6954 dc
.GetTextExtent(wxT("X"), & w
, & h
, & descent
);
6962 /// Do a split, returning an object containing the second part, and setting
6963 /// the first part in 'this'.
6964 wxRichTextObject
* wxRichTextPlainText::DoSplit(long pos
)
6966 long index
= pos
- GetRange().GetStart();
6968 if (index
< 0 || index
>= (int) m_text
.length())
6971 wxString firstPart
= m_text
.Mid(0, index
);
6972 wxString secondPart
= m_text
.Mid(index
);
6976 wxRichTextPlainText
* newObject
= new wxRichTextPlainText(secondPart
);
6977 newObject
->SetAttributes(GetAttributes());
6978 newObject
->SetProperties(GetProperties());
6980 newObject
->SetRange(wxRichTextRange(pos
, GetRange().GetEnd()));
6981 GetRange().SetEnd(pos
-1);
6987 void wxRichTextPlainText::CalculateRange(long start
, long& end
)
6989 end
= start
+ m_text
.length() - 1;
6990 m_range
.SetRange(start
, end
);
6994 bool wxRichTextPlainText::DeleteRange(const wxRichTextRange
& range
)
6996 wxRichTextRange r
= range
;
6998 r
.LimitTo(GetRange());
7000 if (r
.GetStart() == GetRange().GetStart() && r
.GetEnd() == GetRange().GetEnd())
7006 long startIndex
= r
.GetStart() - GetRange().GetStart();
7007 long len
= r
.GetLength();
7009 m_text
= m_text
.Mid(0, startIndex
) + m_text
.Mid(startIndex
+len
);
7013 /// Get text for the given range.
7014 wxString
wxRichTextPlainText::GetTextForRange(const wxRichTextRange
& range
) const
7016 wxRichTextRange r
= range
;
7018 r
.LimitTo(GetRange());
7020 long startIndex
= r
.GetStart() - GetRange().GetStart();
7021 long len
= r
.GetLength();
7023 return m_text
.Mid(startIndex
, len
);
7026 /// Returns true if this object can merge itself with the given one.
7027 bool wxRichTextPlainText::CanMerge(wxRichTextObject
* object
, wxRichTextDrawingContext
& context
) const
7030 if (!context
.GetVirtualAttributesEnabled())
7032 return object
->GetClassInfo() == wxCLASSINFO(wxRichTextPlainText
) &&
7033 (m_text
.empty() || (wxTextAttrEq(GetAttributes(), object
->GetAttributes()) && m_properties
== object
->GetProperties()));
7037 wxRichTextPlainText
* otherObj
= wxDynamicCast(object
, wxRichTextPlainText
);
7038 if (!otherObj
|| m_text
.empty())
7041 if (!wxTextAttrEq(GetAttributes(), object
->GetAttributes()) || !(m_properties
== object
->GetProperties()))
7044 // Check if differing virtual attributes makes it impossible to merge
7047 bool hasVirtualAttr1
= context
.HasVirtualAttributes((wxRichTextObject
*) this);
7048 bool hasVirtualAttr2
= context
.HasVirtualAttributes((wxRichTextObject
*) object
);
7049 if (!hasVirtualAttr1
&& !hasVirtualAttr2
)
7051 else if (hasVirtualAttr1
!= hasVirtualAttr2
)
7055 wxRichTextAttr virtualAttr1
= context
.GetVirtualAttributes((wxRichTextObject
*) this);
7056 wxRichTextAttr virtualAttr2
= context
.GetVirtualAttributes((wxRichTextObject
*) object
);
7057 return virtualAttr1
== virtualAttr2
;
7062 /// Returns true if this object merged itself with the given one.
7063 /// The calling code will then delete the given object.
7064 bool wxRichTextPlainText::Merge(wxRichTextObject
* object
, wxRichTextDrawingContext
& WXUNUSED(context
))
7066 wxRichTextPlainText
* textObject
= wxDynamicCast(object
, wxRichTextPlainText
);
7067 wxASSERT( textObject
!= NULL
);
7071 m_text
+= textObject
->GetText();
7072 wxRichTextApplyStyle(m_attributes
, textObject
->GetAttributes());
7079 bool wxRichTextPlainText::CanSplit(wxRichTextDrawingContext
& context
) const
7081 // If this object has any virtual attributes at all, whether for the whole object
7082 // or individual ones, we should try splitting it by calling Split.
7083 // Must be more than one character in order to be able to split.
7084 return m_text
.Length() > 1 && context
.HasVirtualAttributes((wxRichTextObject
*) this);
7087 wxRichTextObject
* wxRichTextPlainText::Split(wxRichTextDrawingContext
& context
)
7089 int count
= context
.GetVirtualSubobjectAttributesCount(this);
7090 if (count
> 0 && GetParent())
7092 wxRichTextCompositeObject
* parent
= wxDynamicCast(GetParent(), wxRichTextCompositeObject
);
7093 wxRichTextObjectList::compatibility_iterator node
= parent
->GetChildren().Find(this);
7096 const wxRichTextAttr emptyAttr
;
7097 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
7099 wxArrayInt positions
;
7100 wxRichTextAttrArray attributes
;
7101 if (context
.GetVirtualSubobjectAttributes(this, positions
, attributes
) && positions
.GetCount() > 0)
7103 wxASSERT(positions
.GetCount() == attributes
.GetCount());
7105 // We will gather up runs of text with the same virtual attributes
7107 int len
= m_text
.Length();
7110 // runStart and runEnd represent the accumulated run with a consistent attribute
7111 // that hasn't yet been appended
7114 wxRichTextAttr currentAttr
;
7115 wxString text
= m_text
;
7116 wxRichTextPlainText
* lastPlainText
= this;
7118 for (i
= 0; i
< (int) positions
.GetCount(); i
++)
7120 int pos
= positions
[i
];
7121 wxASSERT(pos
>= 0 && pos
< len
);
7122 if (pos
>= 0 && pos
< len
)
7124 const wxRichTextAttr
& attr
= attributes
[i
];
7131 // Check if there was a gap from the last known attribute and this.
7132 // In that case, we need to do something with the span of non-attributed text.
7133 else if ((pos
-1) > runEnd
)
7137 // We hadn't processed anything previously, so the previous run is from the text start
7138 // to just before this position. The current attribute remains empty.
7144 // If the previous attribute matches the gap's attribute (i.e., no attributes)
7145 // then just extend the run.
7146 if (currentAttr
.IsDefault())
7152 // We need to add an object, or reuse the existing one.
7155 lastPlainText
= this;
7156 SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7160 wxRichTextPlainText
* obj
= new wxRichTextPlainText
;
7161 lastPlainText
= obj
;
7162 obj
->SetAttributes(GetAttributes());
7163 obj
->SetProperties(GetProperties());
7164 obj
->SetParent(parent
);
7166 obj
->SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7168 parent
->GetChildren().Insert(next
, obj
);
7170 parent
->GetChildren().Append(obj
);
7173 runStart
= runEnd
+1;
7176 currentAttr
= emptyAttr
;
7181 wxASSERT(runEnd
== pos
-1);
7183 // Now we only have to deal with the previous run
7184 if (currentAttr
== attr
)
7186 // If we still have the same attributes, then we
7187 // simply increase the run size.
7194 // We need to add an object, or reuse the existing one.
7197 lastPlainText
= this;
7198 SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7202 wxRichTextPlainText
* obj
= new wxRichTextPlainText
;
7203 lastPlainText
= obj
;
7204 obj
->SetAttributes(GetAttributes());
7205 obj
->SetProperties(GetProperties());
7206 obj
->SetParent(parent
);
7208 obj
->SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7210 parent
->GetChildren().Insert(next
, obj
);
7212 parent
->GetChildren().Append(obj
);
7224 // We may still have a run to add, and possibly a no-attribute text fragment after that.
7225 // If the whole string was already a single attribute (the run covers the whole string), don't split.
7226 if ((runStart
!= -1) && !(runStart
== 0 && runEnd
== (len
-1)))
7228 // If the current attribute is empty, merge the run with the next fragment
7229 // which by definition (because it's not specified) has empty attributes.
7230 if (currentAttr
.IsDefault())
7233 if (runEnd
< (len
-1))
7235 // We need to add an object, or reuse the existing one.
7238 lastPlainText
= this;
7239 SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7243 wxRichTextPlainText
* obj
= new wxRichTextPlainText
;
7244 lastPlainText
= obj
;
7245 obj
->SetAttributes(GetAttributes());
7246 obj
->SetProperties(GetProperties());
7247 obj
->SetParent(parent
);
7249 obj
->SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7251 parent
->GetChildren().Insert(next
, obj
);
7253 parent
->GetChildren().Append(obj
);
7256 runStart
= runEnd
+1;
7260 // Now the last, non-attributed fragment at the end, if any
7261 if ((runStart
< len
) && !(runStart
== 0 && runEnd
== (len
-1)))
7263 wxASSERT(runStart
!= 0);
7265 wxRichTextPlainText
* obj
= new wxRichTextPlainText
;
7266 obj
->SetAttributes(GetAttributes());
7267 obj
->SetProperties(GetProperties());
7268 obj
->SetParent(parent
);
7270 obj
->SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7272 parent
->GetChildren().Insert(next
, obj
);
7274 parent
->GetChildren().Append(obj
);
7276 lastPlainText
= obj
;
7280 return lastPlainText
;
7287 /// Dump to output stream for debugging
7288 void wxRichTextPlainText::Dump(wxTextOutputStream
& stream
)
7290 wxRichTextObject::Dump(stream
);
7291 stream
<< m_text
<< wxT("\n");
7294 /// Get the first position from pos that has a line break character.
7295 long wxRichTextPlainText::GetFirstLineBreakPosition(long pos
)
7298 int len
= m_text
.length();
7299 int startPos
= pos
- m_range
.GetStart();
7300 for (i
= startPos
; i
< len
; i
++)
7302 wxChar ch
= m_text
[i
];
7303 if (ch
== wxRichTextLineBreakChar
)
7305 return i
+ m_range
.GetStart();
7313 * This is a kind of box, used to represent the whole buffer
7316 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBuffer
, wxRichTextParagraphLayoutBox
)
7318 wxList
wxRichTextBuffer::sm_handlers
;
7319 wxList
wxRichTextBuffer::sm_drawingHandlers
;
7320 wxRichTextFieldTypeHashMap
wxRichTextBuffer::sm_fieldTypes
;
7321 wxRichTextRenderer
* wxRichTextBuffer::sm_renderer
= NULL
;
7322 int wxRichTextBuffer::sm_bulletRightMargin
= 20;
7323 float wxRichTextBuffer::sm_bulletProportion
= (float) 0.3;
7324 bool wxRichTextBuffer::sm_floatingLayoutMode
= true;
7327 void wxRichTextBuffer::Init()
7329 m_commandProcessor
= new wxCommandProcessor
;
7330 m_styleSheet
= NULL
;
7332 m_batchedCommandDepth
= 0;
7333 m_batchedCommand
= NULL
;
7337 m_dimensionScale
= 1.0;
7343 wxRichTextBuffer::~wxRichTextBuffer()
7345 delete m_commandProcessor
;
7346 delete m_batchedCommand
;
7349 ClearEventHandlers();
7352 void wxRichTextBuffer::ResetAndClearCommands()
7356 GetCommandProcessor()->ClearCommands();
7359 Invalidate(wxRICHTEXT_ALL
);
7362 void wxRichTextBuffer::Copy(const wxRichTextBuffer
& obj
)
7364 wxRichTextParagraphLayoutBox::Copy(obj
);
7366 m_styleSheet
= obj
.m_styleSheet
;
7367 m_modified
= obj
.m_modified
;
7368 m_batchedCommandDepth
= 0;
7369 if (m_batchedCommand
)
7370 delete m_batchedCommand
;
7371 m_batchedCommand
= NULL
;
7372 m_suppressUndo
= obj
.m_suppressUndo
;
7373 m_invalidRange
= obj
.m_invalidRange
;
7374 m_dimensionScale
= obj
.m_dimensionScale
;
7375 m_fontScale
= obj
.m_fontScale
;
7378 /// Push style sheet to top of stack
7379 bool wxRichTextBuffer::PushStyleSheet(wxRichTextStyleSheet
* styleSheet
)
7382 styleSheet
->InsertSheet(m_styleSheet
);
7384 SetStyleSheet(styleSheet
);
7389 /// Pop style sheet from top of stack
7390 wxRichTextStyleSheet
* wxRichTextBuffer::PopStyleSheet()
7394 wxRichTextStyleSheet
* oldSheet
= m_styleSheet
;
7395 m_styleSheet
= oldSheet
->GetNextSheet();
7404 /// Submit command to insert paragraphs
7405 bool wxRichTextBuffer::InsertParagraphsWithUndo(long pos
, const wxRichTextParagraphLayoutBox
& paragraphs
, wxRichTextCtrl
* ctrl
, int flags
)
7407 return ctrl
->GetFocusObject()->InsertParagraphsWithUndo(this, pos
, paragraphs
, ctrl
, flags
);
7410 /// Submit command to insert paragraphs
7411 bool wxRichTextParagraphLayoutBox::InsertParagraphsWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxRichTextParagraphLayoutBox
& paragraphs
, wxRichTextCtrl
* ctrl
, int WXUNUSED(flags
))
7413 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7415 action
->GetNewParagraphs() = paragraphs
;
7417 action
->SetPosition(pos
);
7419 wxRichTextRange range
= wxRichTextRange(pos
, pos
+ paragraphs
.GetOwnRange().GetEnd() - 1);
7420 if (!paragraphs
.GetPartialParagraph())
7421 range
.SetEnd(range
.GetEnd()+1);
7423 // Set the range we'll need to delete in Undo
7424 action
->SetRange(range
);
7426 buffer
->SubmitAction(action
);
7431 /// Submit command to insert the given text
7432 bool wxRichTextBuffer::InsertTextWithUndo(long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
, int flags
)
7434 return ctrl
->GetFocusObject()->InsertTextWithUndo(this, pos
, text
, ctrl
, flags
);
7437 /// Submit command to insert the given text
7438 bool wxRichTextParagraphLayoutBox::InsertTextWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
, int flags
)
7440 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7442 wxRichTextAttr
* p
= NULL
;
7443 wxRichTextAttr paraAttr
;
7444 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7446 // Get appropriate paragraph style
7447 paraAttr
= GetStyleForNewParagraph(buffer
, pos
, false, false);
7448 if (!paraAttr
.IsDefault())
7452 action
->GetNewParagraphs().AddParagraphs(text
, p
);
7454 int length
= action
->GetNewParagraphs().GetOwnRange().GetLength();
7456 if (!text
.empty() && text
.Last() != wxT('\n'))
7458 // Don't count the newline when undoing
7460 action
->GetNewParagraphs().SetPartialParagraph(true);
7462 else if (!text
.empty() && text
.Last() == wxT('\n'))
7465 action
->SetPosition(pos
);
7467 // Set the range we'll need to delete in Undo
7468 action
->SetRange(wxRichTextRange(pos
, pos
+ length
- 1));
7470 buffer
->SubmitAction(action
);
7475 /// Submit command to insert the given text
7476 bool wxRichTextBuffer::InsertNewlineWithUndo(long pos
, wxRichTextCtrl
* ctrl
, int flags
)
7478 return ctrl
->GetFocusObject()->InsertNewlineWithUndo(this, pos
, ctrl
, flags
);
7481 /// Submit command to insert the given text
7482 bool wxRichTextParagraphLayoutBox::InsertNewlineWithUndo(wxRichTextBuffer
* buffer
, long pos
, wxRichTextCtrl
* ctrl
, int flags
)
7484 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7486 wxRichTextAttr
* p
= NULL
;
7487 wxRichTextAttr paraAttr
;
7488 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7490 paraAttr
= GetStyleForNewParagraph(buffer
, pos
, false, true /* look for next paragraph style */);
7491 if (!paraAttr
.IsDefault())
7495 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7496 // Don't include box attributes such as margins
7497 attr
.GetTextBoxAttr().Reset();
7499 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(wxEmptyString
, this, & attr
);
7500 action
->GetNewParagraphs().AppendChild(newPara
);
7501 action
->GetNewParagraphs().UpdateRanges();
7502 action
->GetNewParagraphs().SetPartialParagraph(false);
7503 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
, false);
7507 newPara
->SetAttributes(*p
);
7509 if (flags
& wxRICHTEXT_INSERT_INTERACTIVE
)
7511 if (para
&& para
->GetRange().GetEnd() == pos
)
7514 // Now see if we need to number the paragraph.
7515 if (newPara
->GetAttributes().HasBulletNumber())
7517 wxRichTextAttr numberingAttr
;
7518 if (FindNextParagraphNumber(para
, numberingAttr
))
7519 wxRichTextApplyStyle(newPara
->GetAttributes(), (const wxRichTextAttr
&) numberingAttr
);
7523 action
->SetPosition(pos
);
7525 // Use the default character style
7526 if (!buffer
->GetDefaultStyle().IsDefault() && newPara
->GetChildren().GetFirst())
7528 // Check whether the default style merely reflects the paragraph/basic style,
7529 // in which case don't apply it.
7530 wxRichTextAttr
defaultStyle(buffer
->GetDefaultStyle());
7531 defaultStyle
.GetTextBoxAttr().Reset();
7532 wxRichTextAttr toApply
;
7535 wxRichTextAttr combinedAttr
= para
->GetCombinedAttributes(true /* include box attributes */);
7536 wxRichTextAttr newAttr
;
7537 // This filters out attributes that are accounted for by the current
7538 // paragraph/basic style
7539 wxRichTextApplyStyle(toApply
, defaultStyle
, & combinedAttr
);
7542 toApply
= defaultStyle
;
7544 if (!toApply
.IsDefault())
7545 newPara
->GetChildren().GetFirst()->GetData()->SetAttributes(toApply
);
7548 // Set the range we'll need to delete in Undo
7549 action
->SetRange(wxRichTextRange(pos1
, pos1
));
7551 buffer
->SubmitAction(action
);
7556 /// Submit command to insert the given image
7557 bool wxRichTextBuffer::InsertImageWithUndo(long pos
, const wxRichTextImageBlock
& imageBlock
, wxRichTextCtrl
* ctrl
, int flags
,
7558 const wxRichTextAttr
& textAttr
)
7560 return ctrl
->GetFocusObject()->InsertImageWithUndo(this, pos
, imageBlock
, ctrl
, flags
, textAttr
);
7563 /// Submit command to insert the given image
7564 bool wxRichTextParagraphLayoutBox::InsertImageWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxRichTextImageBlock
& imageBlock
,
7565 wxRichTextCtrl
* ctrl
, int flags
,
7566 const wxRichTextAttr
& textAttr
)
7568 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7570 wxRichTextAttr
* p
= NULL
;
7571 wxRichTextAttr paraAttr
;
7572 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7574 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
7575 if (!paraAttr
.IsDefault())
7579 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7581 // Don't include box attributes such as margins
7582 attr
.GetTextBoxAttr().Reset();
7584 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7586 newPara
->SetAttributes(*p
);
7588 wxRichTextImage
* imageObject
= new wxRichTextImage(imageBlock
, newPara
);
7589 newPara
->AppendChild(imageObject
);
7590 imageObject
->SetAttributes(textAttr
);
7591 action
->GetNewParagraphs().AppendChild(newPara
);
7592 action
->GetNewParagraphs().UpdateRanges();
7594 action
->GetNewParagraphs().SetPartialParagraph(true);
7596 action
->SetPosition(pos
);
7598 // Set the range we'll need to delete in Undo
7599 action
->SetRange(wxRichTextRange(pos
, pos
));
7601 buffer
->SubmitAction(action
);
7606 // Insert an object with no change of it
7607 wxRichTextObject
* wxRichTextBuffer::InsertObjectWithUndo(long pos
, wxRichTextObject
*object
, wxRichTextCtrl
* ctrl
, int flags
)
7609 return ctrl
->GetFocusObject()->InsertObjectWithUndo(this, pos
, object
, ctrl
, flags
);
7612 // Insert an object with no change of it
7613 wxRichTextObject
* wxRichTextParagraphLayoutBox::InsertObjectWithUndo(wxRichTextBuffer
* buffer
, long pos
, wxRichTextObject
*object
, wxRichTextCtrl
* ctrl
, int flags
)
7615 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Object"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7617 wxRichTextAttr
* p
= NULL
;
7618 wxRichTextAttr paraAttr
;
7619 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7621 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
7622 if (!paraAttr
.IsDefault())
7626 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7628 // Don't include box attributes such as margins
7629 attr
.GetTextBoxAttr().Reset();
7631 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7633 newPara
->SetAttributes(*p
);
7635 newPara
->AppendChild(object
);
7636 action
->GetNewParagraphs().AppendChild(newPara
);
7637 action
->GetNewParagraphs().UpdateRanges();
7639 action
->GetNewParagraphs().SetPartialParagraph(true);
7641 action
->SetPosition(pos
);
7643 // Set the range we'll need to delete in Undo
7644 action
->SetRange(wxRichTextRange(pos
, pos
));
7646 buffer
->SubmitAction(action
);
7648 wxRichTextObject
* obj
= GetLeafObjectAtPosition(pos
);
7652 wxRichTextField
* wxRichTextParagraphLayoutBox::InsertFieldWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxString
& fieldType
,
7653 const wxRichTextProperties
& properties
,
7654 wxRichTextCtrl
* ctrl
, int flags
,
7655 const wxRichTextAttr
& textAttr
)
7657 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Field"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7659 wxRichTextAttr
* p
= NULL
;
7660 wxRichTextAttr paraAttr
;
7661 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7663 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
7664 if (!paraAttr
.IsDefault())
7668 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7670 // Don't include box attributes such as margins
7671 attr
.GetTextBoxAttr().Reset();
7673 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7675 newPara
->SetAttributes(*p
);
7677 wxRichTextField
* fieldObject
= new wxRichTextField();
7678 fieldObject
->wxRichTextObject::SetProperties(properties
);
7679 fieldObject
->SetFieldType(fieldType
);
7680 fieldObject
->SetAttributes(textAttr
);
7681 newPara
->AppendChild(fieldObject
);
7682 action
->GetNewParagraphs().AppendChild(newPara
);
7683 action
->GetNewParagraphs().UpdateRanges();
7684 action
->GetNewParagraphs().SetPartialParagraph(true);
7685 action
->SetPosition(pos
);
7687 // Set the range we'll need to delete in Undo
7688 action
->SetRange(wxRichTextRange(pos
, pos
));
7690 buffer
->SubmitAction(action
);
7692 wxRichTextField
* obj
= wxDynamicCast(GetLeafObjectAtPosition(pos
), wxRichTextField
);
7696 /// Get the style that is appropriate for a new paragraph at this position.
7697 /// If the previous paragraph has a paragraph style name, look up the next-paragraph
7699 wxRichTextAttr
wxRichTextParagraphLayoutBox::GetStyleForNewParagraph(wxRichTextBuffer
* buffer
, long pos
, bool caretPosition
, bool lookUpNewParaStyle
) const
7701 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
, caretPosition
);
7704 wxRichTextAttr attr
;
7705 bool foundAttributes
= false;
7707 // Look for a matching paragraph style
7708 if (lookUpNewParaStyle
&& !para
->GetAttributes().GetParagraphStyleName().IsEmpty() && buffer
->GetStyleSheet())
7710 wxRichTextParagraphStyleDefinition
* paraDef
= buffer
->GetStyleSheet()->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
7713 // If we're not at the end of the paragraph, then we apply THIS style, and not the designated next style.
7714 if (para
->GetRange().GetEnd() == pos
&& !paraDef
->GetNextStyle().IsEmpty())
7716 wxRichTextParagraphStyleDefinition
* nextParaDef
= buffer
->GetStyleSheet()->FindParagraphStyle(paraDef
->GetNextStyle());
7719 foundAttributes
= true;
7720 attr
= nextParaDef
->GetStyleMergedWithBase(buffer
->GetStyleSheet());
7724 // If we didn't find the 'next style', use this style instead.
7725 if (!foundAttributes
)
7727 foundAttributes
= true;
7728 attr
= paraDef
->GetStyleMergedWithBase(buffer
->GetStyleSheet());
7733 // Also apply list style if present
7734 if (lookUpNewParaStyle
&& !para
->GetAttributes().GetListStyleName().IsEmpty() && buffer
->GetStyleSheet())
7736 wxRichTextListStyleDefinition
* listDef
= buffer
->GetStyleSheet()->FindListStyle(para
->GetAttributes().GetListStyleName());
7739 int thisIndent
= para
->GetAttributes().GetLeftIndent();
7740 int thisLevel
= para
->GetAttributes().HasOutlineLevel() ? para
->GetAttributes().GetOutlineLevel() : listDef
->FindLevelForIndent(thisIndent
);
7742 // Apply the overall list style, and item style for this level
7743 wxRichTextAttr
listStyle(listDef
->GetCombinedStyleForLevel(thisLevel
, buffer
->GetStyleSheet()));
7744 wxRichTextApplyStyle(attr
, listStyle
);
7745 attr
.SetOutlineLevel(thisLevel
);
7746 if (para
->GetAttributes().HasBulletNumber())
7747 attr
.SetBulletNumber(para
->GetAttributes().GetBulletNumber());
7751 if (!foundAttributes
)
7753 attr
= para
->GetAttributes();
7754 int flags
= attr
.GetFlags();
7756 // Eliminate character styles
7757 flags
&= ( (~ wxTEXT_ATTR_FONT
) |
7758 (~ wxTEXT_ATTR_TEXT_COLOUR
) |
7759 (~ wxTEXT_ATTR_BACKGROUND_COLOUR
) );
7760 attr
.SetFlags(flags
);
7766 return wxRichTextAttr();
7769 /// Submit command to delete this range
7770 bool wxRichTextBuffer::DeleteRangeWithUndo(const wxRichTextRange
& range
, wxRichTextCtrl
* ctrl
)
7772 return ctrl
->GetFocusObject()->DeleteRangeWithUndo(range
, ctrl
, this);
7775 /// Submit command to delete this range
7776 bool wxRichTextParagraphLayoutBox::DeleteRangeWithUndo(const wxRichTextRange
& range
, wxRichTextCtrl
* ctrl
, wxRichTextBuffer
* buffer
)
7778 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Delete"), wxRICHTEXT_DELETE
, buffer
, this, ctrl
);
7780 action
->SetPosition(ctrl
->GetCaretPosition());
7782 // Set the range to delete
7783 action
->SetRange(range
);
7785 // Copy the fragment that we'll need to restore in Undo
7786 CopyFragment(range
, action
->GetOldParagraphs());
7788 // See if we're deleting a paragraph marker, in which case we need to
7789 // make a note not to copy the attributes from the 2nd paragraph to the 1st.
7790 if (range
.GetStart() == range
.GetEnd())
7792 wxRichTextParagraph
* para
= GetParagraphAtPosition(range
.GetStart());
7793 if (para
&& para
->GetRange().GetEnd() == range
.GetEnd())
7795 wxRichTextParagraph
* nextPara
= GetParagraphAtPosition(range
.GetStart()+1);
7796 if (nextPara
&& nextPara
!= para
)
7798 action
->GetOldParagraphs().GetChildren().GetFirst()->GetData()->SetAttributes(nextPara
->GetAttributes());
7799 action
->GetOldParagraphs().GetAttributes().SetFlags(action
->GetOldParagraphs().GetAttributes().GetFlags() | wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
);
7804 buffer
->SubmitAction(action
);
7809 /// Collapse undo/redo commands
7810 bool wxRichTextBuffer::BeginBatchUndo(const wxString
& cmdName
)
7812 if (m_batchedCommandDepth
== 0)
7814 wxASSERT(m_batchedCommand
== NULL
);
7815 if (m_batchedCommand
)
7817 GetCommandProcessor()->Store(m_batchedCommand
);
7819 m_batchedCommand
= new wxRichTextCommand(cmdName
);
7822 m_batchedCommandDepth
++;
7827 /// Collapse undo/redo commands
7828 bool wxRichTextBuffer::EndBatchUndo()
7830 m_batchedCommandDepth
--;
7832 wxASSERT(m_batchedCommandDepth
>= 0);
7833 wxASSERT(m_batchedCommand
!= NULL
);
7835 if (m_batchedCommandDepth
== 0)
7837 GetCommandProcessor()->Store(m_batchedCommand
);
7838 m_batchedCommand
= NULL
;
7844 /// Submit immediately, or delay according to whether collapsing is on
7845 bool wxRichTextBuffer::SubmitAction(wxRichTextAction
* action
)
7847 if (action
&& !action
->GetNewParagraphs().IsEmpty())
7848 PrepareContent(action
->GetNewParagraphs());
7850 if (BatchingUndo() && m_batchedCommand
&& !SuppressingUndo())
7852 wxRichTextCommand
* cmd
= new wxRichTextCommand(action
->GetName());
7853 cmd
->AddAction(action
);
7855 cmd
->GetActions().Clear();
7858 m_batchedCommand
->AddAction(action
);
7862 wxRichTextCommand
* cmd
= new wxRichTextCommand(action
->GetName());
7863 cmd
->AddAction(action
);
7865 // Only store it if we're not suppressing undo.
7866 return GetCommandProcessor()->Submit(cmd
, !SuppressingUndo());
7872 /// Begin suppressing undo/redo commands.
7873 bool wxRichTextBuffer::BeginSuppressUndo()
7880 /// End suppressing undo/redo commands.
7881 bool wxRichTextBuffer::EndSuppressUndo()
7888 /// Begin using a style
7889 bool wxRichTextBuffer::BeginStyle(const wxRichTextAttr
& style
)
7891 wxRichTextAttr
newStyle(GetDefaultStyle());
7892 newStyle
.GetTextBoxAttr().Reset();
7894 // Save the old default style
7895 m_attributeStack
.Append((wxObject
*) new wxRichTextAttr(newStyle
));
7897 wxRichTextApplyStyle(newStyle
, style
);
7898 newStyle
.SetFlags(style
.GetFlags()|newStyle
.GetFlags());
7900 SetDefaultStyle(newStyle
);
7906 bool wxRichTextBuffer::EndStyle()
7908 if (!m_attributeStack
.GetFirst())
7910 wxLogDebug(_("Too many EndStyle calls!"));
7914 wxList::compatibility_iterator node
= m_attributeStack
.GetLast();
7915 wxRichTextAttr
* attr
= (wxRichTextAttr
*)node
->GetData();
7916 m_attributeStack
.Erase(node
);
7918 SetDefaultStyle(*attr
);
7925 bool wxRichTextBuffer::EndAllStyles()
7927 while (m_attributeStack
.GetCount() != 0)
7932 /// Clear the style stack
7933 void wxRichTextBuffer::ClearStyleStack()
7935 for (wxList::compatibility_iterator node
= m_attributeStack
.GetFirst(); node
; node
= node
->GetNext())
7936 delete (wxRichTextAttr
*) node
->GetData();
7937 m_attributeStack
.Clear();
7940 /// Begin using bold
7941 bool wxRichTextBuffer::BeginBold()
7943 wxRichTextAttr attr
;
7944 attr
.SetFontWeight(wxFONTWEIGHT_BOLD
);
7946 return BeginStyle(attr
);
7949 /// Begin using italic
7950 bool wxRichTextBuffer::BeginItalic()
7952 wxRichTextAttr attr
;
7953 attr
.SetFontStyle(wxFONTSTYLE_ITALIC
);
7955 return BeginStyle(attr
);
7958 /// Begin using underline
7959 bool wxRichTextBuffer::BeginUnderline()
7961 wxRichTextAttr attr
;
7962 attr
.SetFontUnderlined(true);
7964 return BeginStyle(attr
);
7967 /// Begin using point size
7968 bool wxRichTextBuffer::BeginFontSize(int pointSize
)
7970 wxRichTextAttr attr
;
7971 attr
.SetFontSize(pointSize
);
7973 return BeginStyle(attr
);
7976 /// Begin using this font
7977 bool wxRichTextBuffer::BeginFont(const wxFont
& font
)
7979 wxRichTextAttr attr
;
7982 return BeginStyle(attr
);
7985 /// Begin using this colour
7986 bool wxRichTextBuffer::BeginTextColour(const wxColour
& colour
)
7988 wxRichTextAttr attr
;
7989 attr
.SetFlags(wxTEXT_ATTR_TEXT_COLOUR
);
7990 attr
.SetTextColour(colour
);
7992 return BeginStyle(attr
);
7995 /// Begin using alignment
7996 bool wxRichTextBuffer::BeginAlignment(wxTextAttrAlignment alignment
)
7998 wxRichTextAttr attr
;
7999 attr
.SetFlags(wxTEXT_ATTR_ALIGNMENT
);
8000 attr
.SetAlignment(alignment
);
8002 return BeginStyle(attr
);
8005 /// Begin left indent
8006 bool wxRichTextBuffer::BeginLeftIndent(int leftIndent
, int leftSubIndent
)
8008 wxRichTextAttr attr
;
8009 attr
.SetFlags(wxTEXT_ATTR_LEFT_INDENT
);
8010 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
8012 return BeginStyle(attr
);
8015 /// Begin right indent
8016 bool wxRichTextBuffer::BeginRightIndent(int rightIndent
)
8018 wxRichTextAttr attr
;
8019 attr
.SetFlags(wxTEXT_ATTR_RIGHT_INDENT
);
8020 attr
.SetRightIndent(rightIndent
);
8022 return BeginStyle(attr
);
8025 /// Begin paragraph spacing
8026 bool wxRichTextBuffer::BeginParagraphSpacing(int before
, int after
)
8030 flags
|= wxTEXT_ATTR_PARA_SPACING_BEFORE
;
8032 flags
|= wxTEXT_ATTR_PARA_SPACING_AFTER
;
8034 wxRichTextAttr attr
;
8035 attr
.SetFlags(flags
);
8036 attr
.SetParagraphSpacingBefore(before
);
8037 attr
.SetParagraphSpacingAfter(after
);
8039 return BeginStyle(attr
);
8042 /// Begin line spacing
8043 bool wxRichTextBuffer::BeginLineSpacing(int lineSpacing
)
8045 wxRichTextAttr attr
;
8046 attr
.SetFlags(wxTEXT_ATTR_LINE_SPACING
);
8047 attr
.SetLineSpacing(lineSpacing
);
8049 return BeginStyle(attr
);
8052 /// Begin numbered bullet
8053 bool wxRichTextBuffer::BeginNumberedBullet(int bulletNumber
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
8055 wxRichTextAttr attr
;
8056 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
8057 attr
.SetBulletStyle(bulletStyle
);
8058 attr
.SetBulletNumber(bulletNumber
);
8059 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
8061 return BeginStyle(attr
);
8064 /// Begin symbol bullet
8065 bool wxRichTextBuffer::BeginSymbolBullet(const wxString
& symbol
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
8067 wxRichTextAttr attr
;
8068 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
8069 attr
.SetBulletStyle(bulletStyle
);
8070 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
8071 attr
.SetBulletText(symbol
);
8073 return BeginStyle(attr
);
8076 /// Begin standard bullet
8077 bool wxRichTextBuffer::BeginStandardBullet(const wxString
& bulletName
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
8079 wxRichTextAttr attr
;
8080 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
8081 attr
.SetBulletStyle(bulletStyle
);
8082 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
8083 attr
.SetBulletName(bulletName
);
8085 return BeginStyle(attr
);
8088 /// Begin named character style
8089 bool wxRichTextBuffer::BeginCharacterStyle(const wxString
& characterStyle
)
8091 if (GetStyleSheet())
8093 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->FindCharacterStyle(characterStyle
);
8096 wxRichTextAttr attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
8097 return BeginStyle(attr
);
8103 /// Begin named paragraph style
8104 bool wxRichTextBuffer::BeginParagraphStyle(const wxString
& paragraphStyle
)
8106 if (GetStyleSheet())
8108 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(paragraphStyle
);
8111 wxRichTextAttr attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
8112 return BeginStyle(attr
);
8118 /// Begin named list style
8119 bool wxRichTextBuffer::BeginListStyle(const wxString
& listStyle
, int level
, int number
)
8121 if (GetStyleSheet())
8123 wxRichTextListStyleDefinition
* def
= GetStyleSheet()->FindListStyle(listStyle
);
8126 wxRichTextAttr
attr(def
->GetCombinedStyleForLevel(level
));
8128 attr
.SetBulletNumber(number
);
8130 return BeginStyle(attr
);
8137 bool wxRichTextBuffer::BeginURL(const wxString
& url
, const wxString
& characterStyle
)
8139 wxRichTextAttr attr
;
8141 if (!characterStyle
.IsEmpty() && GetStyleSheet())
8143 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->FindCharacterStyle(characterStyle
);
8146 attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
8151 return BeginStyle(attr
);
8154 /// Adds a handler to the end
8155 void wxRichTextBuffer::AddHandler(wxRichTextFileHandler
*handler
)
8157 sm_handlers
.Append(handler
);
8160 /// Inserts a handler at the front
8161 void wxRichTextBuffer::InsertHandler(wxRichTextFileHandler
*handler
)
8163 sm_handlers
.Insert( handler
);
8166 /// Removes a handler
8167 bool wxRichTextBuffer::RemoveHandler(const wxString
& name
)
8169 wxRichTextFileHandler
*handler
= FindHandler(name
);
8172 sm_handlers
.DeleteObject(handler
);
8180 /// Finds a handler by filename or, if supplied, type
8181 wxRichTextFileHandler
*wxRichTextBuffer::FindHandlerFilenameOrType(const wxString
& filename
,
8182 wxRichTextFileType imageType
)
8184 if (imageType
!= wxRICHTEXT_TYPE_ANY
)
8185 return FindHandler(imageType
);
8186 else if (!filename
.IsEmpty())
8188 wxString path
, file
, ext
;
8189 wxFileName::SplitPath(filename
, & path
, & file
, & ext
);
8190 return FindHandler(ext
, imageType
);
8197 /// Finds a handler by name
8198 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(const wxString
& name
)
8200 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
8203 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
8204 if (handler
->GetName().Lower() == name
.Lower()) return handler
;
8206 node
= node
->GetNext();
8211 /// Finds a handler by extension and type
8212 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(const wxString
& extension
, wxRichTextFileType type
)
8214 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
8217 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
8218 if ( handler
->GetExtension().Lower() == extension
.Lower() &&
8219 (type
== wxRICHTEXT_TYPE_ANY
|| handler
->GetType() == type
) )
8221 node
= node
->GetNext();
8226 /// Finds a handler by type
8227 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(wxRichTextFileType type
)
8229 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
8232 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
8233 if (handler
->GetType() == type
) return handler
;
8234 node
= node
->GetNext();
8239 void wxRichTextBuffer::InitStandardHandlers()
8241 if (!FindHandler(wxRICHTEXT_TYPE_TEXT
))
8242 AddHandler(new wxRichTextPlainTextHandler
);
8245 void wxRichTextBuffer::CleanUpHandlers()
8247 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
8250 wxRichTextFileHandler
* handler
= (wxRichTextFileHandler
*)node
->GetData();
8251 wxList::compatibility_iterator next
= node
->GetNext();
8256 sm_handlers
.Clear();
8259 wxString
wxRichTextBuffer::GetExtWildcard(bool combine
, bool save
, wxArrayInt
* types
)
8266 wxList::compatibility_iterator node
= GetHandlers().GetFirst();
8270 wxRichTextFileHandler
* handler
= (wxRichTextFileHandler
*) node
->GetData();
8271 if (handler
->IsVisible() && ((save
&& handler
->CanSave()) || (!save
&& handler
->CanLoad())))
8276 wildcard
+= wxT(";");
8277 wildcard
+= wxT("*.") + handler
->GetExtension();
8282 wildcard
+= wxT("|");
8283 wildcard
+= handler
->GetName();
8284 wildcard
+= wxT(" ");
8285 wildcard
+= _("files");
8286 wildcard
+= wxT(" (*.");
8287 wildcard
+= handler
->GetExtension();
8288 wildcard
+= wxT(")|*.");
8289 wildcard
+= handler
->GetExtension();
8291 types
->Add(handler
->GetType());
8296 node
= node
->GetNext();
8300 wildcard
= wxT("(") + wildcard
+ wxT(")|") + wildcard
;
8305 bool wxRichTextBuffer::LoadFile(const wxString
& filename
, wxRichTextFileType type
)
8307 wxRichTextFileHandler
* handler
= FindHandlerFilenameOrType(filename
, type
);
8310 SetDefaultStyle(wxRichTextAttr());
8311 handler
->SetFlags(GetHandlerFlags());
8312 bool success
= handler
->LoadFile(this, filename
);
8313 Invalidate(wxRICHTEXT_ALL
);
8321 bool wxRichTextBuffer::SaveFile(const wxString
& filename
, wxRichTextFileType type
)
8323 wxRichTextFileHandler
* handler
= FindHandlerFilenameOrType(filename
, type
);
8326 handler
->SetFlags(GetHandlerFlags());
8327 return handler
->SaveFile(this, filename
);
8333 /// Load from a stream
8334 bool wxRichTextBuffer::LoadFile(wxInputStream
& stream
, wxRichTextFileType type
)
8336 wxRichTextFileHandler
* handler
= FindHandler(type
);
8339 SetDefaultStyle(wxRichTextAttr());
8340 handler
->SetFlags(GetHandlerFlags());
8341 bool success
= handler
->LoadFile(this, stream
);
8342 Invalidate(wxRICHTEXT_ALL
);
8349 /// Save to a stream
8350 bool wxRichTextBuffer::SaveFile(wxOutputStream
& stream
, wxRichTextFileType type
)
8352 wxRichTextFileHandler
* handler
= FindHandler(type
);
8355 handler
->SetFlags(GetHandlerFlags());
8356 return handler
->SaveFile(this, stream
);
8362 /// Copy the range to the clipboard
8363 bool wxRichTextBuffer::CopyToClipboard(const wxRichTextRange
& range
)
8365 bool success
= false;
8366 wxRichTextParagraphLayoutBox
* container
= this;
8367 if (GetRichTextCtrl())
8368 container
= GetRichTextCtrl()->GetFocusObject();
8370 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
8372 if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open())
8374 wxTheClipboard
->Clear();
8376 // Add composite object
8378 wxDataObjectComposite
* compositeObject
= new wxDataObjectComposite();
8381 wxString text
= container
->GetTextForRange(range
);
8384 text
= wxTextFile::Translate(text
, wxTextFileType_Dos
);
8387 compositeObject
->Add(new wxTextDataObject(text
), false /* not preferred */);
8390 // Add rich text buffer data object. This needs the XML handler to be present.
8392 if (FindHandler(wxRICHTEXT_TYPE_XML
))
8394 wxRichTextBuffer
* richTextBuf
= new wxRichTextBuffer
;
8395 container
->CopyFragment(range
, *richTextBuf
);
8397 compositeObject
->Add(new wxRichTextBufferDataObject(richTextBuf
), true /* preferred */);
8400 if (wxTheClipboard
->SetData(compositeObject
))
8403 wxTheClipboard
->Close();
8412 /// Paste the clipboard content to the buffer
8413 bool wxRichTextBuffer::PasteFromClipboard(long position
)
8415 bool success
= false;
8416 wxRichTextParagraphLayoutBox
* container
= this;
8417 if (GetRichTextCtrl())
8418 container
= GetRichTextCtrl()->GetFocusObject();
8420 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
8421 if (CanPasteFromClipboard())
8423 if (wxTheClipboard
->Open())
8425 if (wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())))
8427 wxRichTextBufferDataObject data
;
8428 wxTheClipboard
->GetData(data
);
8429 wxRichTextBuffer
* richTextBuffer
= data
.GetRichTextBuffer();
8432 container
->InsertParagraphsWithUndo(this, position
+1, *richTextBuffer
, GetRichTextCtrl(), 0);
8433 if (GetRichTextCtrl())
8434 GetRichTextCtrl()->ShowPosition(position
+ richTextBuffer
->GetOwnRange().GetEnd());
8435 delete richTextBuffer
;
8438 else if (wxTheClipboard
->IsSupported(wxDF_TEXT
)
8440 || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
)
8444 wxTextDataObject data
;
8445 wxTheClipboard
->GetData(data
);
8446 wxString
text(data
.GetText());
8449 text2
.Alloc(text
.Length()+1);
8451 for (i
= 0; i
< text
.Length(); i
++)
8453 wxChar ch
= text
[i
];
8454 if (ch
!= wxT('\r'))
8458 wxString text2
= text
;
8460 container
->InsertTextWithUndo(this, position
+1, text2
, GetRichTextCtrl(), wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
);
8462 if (GetRichTextCtrl())
8463 GetRichTextCtrl()->ShowPosition(position
+ text2
.Length());
8467 else if (wxTheClipboard
->IsSupported(wxDF_BITMAP
))
8469 wxBitmapDataObject data
;
8470 wxTheClipboard
->GetData(data
);
8471 wxBitmap
bitmap(data
.GetBitmap());
8472 wxImage
image(bitmap
.ConvertToImage());
8474 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, this, container
, GetRichTextCtrl(), false);
8476 action
->GetNewParagraphs().AddImage(image
);
8478 if (action
->GetNewParagraphs().GetChildCount() == 1)
8479 action
->GetNewParagraphs().SetPartialParagraph(true);
8481 action
->SetPosition(position
+1);
8483 // Set the range we'll need to delete in Undo
8484 action
->SetRange(wxRichTextRange(position
+1, position
+1));
8486 SubmitAction(action
);
8490 wxTheClipboard
->Close();
8494 wxUnusedVar(position
);
8499 /// Can we paste from the clipboard?
8500 bool wxRichTextBuffer::CanPasteFromClipboard() const
8502 bool canPaste
= false;
8503 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
8504 if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open())
8506 if (wxTheClipboard
->IsSupported(wxDF_TEXT
)
8508 || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
)
8510 || wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())) ||
8511 wxTheClipboard
->IsSupported(wxDF_BITMAP
))
8515 wxTheClipboard
->Close();
8521 /// Dumps contents of buffer for debugging purposes
8522 void wxRichTextBuffer::Dump()
8526 wxStringOutputStream
stream(& text
);
8527 wxTextOutputStream
textStream(stream
);
8534 /// Add an event handler
8535 bool wxRichTextBuffer::AddEventHandler(wxEvtHandler
* handler
)
8537 m_eventHandlers
.Append(handler
);
8541 /// Remove an event handler
8542 bool wxRichTextBuffer::RemoveEventHandler(wxEvtHandler
* handler
, bool deleteHandler
)
8544 wxList::compatibility_iterator node
= m_eventHandlers
.Find(handler
);
8547 m_eventHandlers
.Erase(node
);
8557 /// Clear event handlers
8558 void wxRichTextBuffer::ClearEventHandlers()
8560 m_eventHandlers
.Clear();
8563 /// Send event to event handlers. If sendToAll is true, will send to all event handlers,
8564 /// otherwise will stop at the first successful one.
8565 bool wxRichTextBuffer::SendEvent(wxEvent
& event
, bool sendToAll
)
8567 bool success
= false;
8568 for (wxList::compatibility_iterator node
= m_eventHandlers
.GetFirst(); node
; node
= node
->GetNext())
8570 wxEvtHandler
* handler
= (wxEvtHandler
*) node
->GetData();
8571 if (handler
->ProcessEvent(event
))
8581 /// Set style sheet and notify of the change
8582 bool wxRichTextBuffer::SetStyleSheetAndNotify(wxRichTextStyleSheet
* sheet
)
8584 wxRichTextStyleSheet
* oldSheet
= GetStyleSheet();
8586 wxWindowID winid
= wxID_ANY
;
8587 if (GetRichTextCtrl())
8588 winid
= GetRichTextCtrl()->GetId();
8590 wxRichTextEvent
event(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACING
, winid
);
8591 event
.SetEventObject(GetRichTextCtrl());
8592 event
.SetContainer(GetRichTextCtrl()->GetFocusObject());
8593 event
.SetOldStyleSheet(oldSheet
);
8594 event
.SetNewStyleSheet(sheet
);
8597 if (SendEvent(event
) && !event
.IsAllowed())
8599 if (sheet
!= oldSheet
)
8605 if (oldSheet
&& oldSheet
!= sheet
)
8608 SetStyleSheet(sheet
);
8610 event
.SetEventType(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACED
);
8611 event
.SetOldStyleSheet(NULL
);
8614 return SendEvent(event
);
8617 /// Set renderer, deleting old one
8618 void wxRichTextBuffer::SetRenderer(wxRichTextRenderer
* renderer
)
8622 sm_renderer
= renderer
;
8625 /// Hit-testing: returns a flag indicating hit test details, plus
8626 /// information about position
8627 int wxRichTextBuffer::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
8629 int ret
= wxRichTextParagraphLayoutBox::HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
8630 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
8636 textPosition
= m_ownRange
.GetEnd()-1;
8639 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
;
8643 void wxRichTextBuffer::SetFontScale(double fontScale
)
8645 m_fontScale
= fontScale
;
8646 m_fontTable
.SetFontScale(fontScale
);
8649 void wxRichTextBuffer::SetDimensionScale(double dimScale
)
8651 m_dimensionScale
= dimScale
;
8654 bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxRichTextAttr
& bulletAttr
, const wxRect
& rect
)
8656 if (bulletAttr
.GetTextColour().IsOk())
8658 wxCheckSetPen(dc
, wxPen(bulletAttr
.GetTextColour()));
8659 wxCheckSetBrush(dc
, wxBrush(bulletAttr
.GetTextColour()));
8663 wxCheckSetPen(dc
, *wxBLACK_PEN
);
8664 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
8668 if (bulletAttr
.HasFont())
8670 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(bulletAttr
);
8673 font
= (*wxNORMAL_FONT
);
8675 wxCheckSetFont(dc
, font
);
8677 int charHeight
= dc
.GetCharHeight();
8679 int bulletWidth
= (int) (((float) charHeight
) * wxRichTextBuffer::GetBulletProportion());
8680 int bulletHeight
= bulletWidth
;
8684 // Calculate the top position of the character (as opposed to the whole line height)
8685 int y
= rect
.y
+ (rect
.height
- charHeight
);
8687 // Calculate where the bullet should be positioned
8688 y
= y
+ (charHeight
+1)/2 - (bulletHeight
+1)/2;
8690 // The margin between a bullet and text.
8691 int margin
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer::GetBulletRightMargin());
8693 if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
)
8694 x
= rect
.x
+ rect
.width
- bulletWidth
- margin
;
8695 else if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
)
8696 x
= x
+ (rect
.width
)/2 - bulletWidth
/2;
8698 if (bulletAttr
.GetBulletName() == wxT("standard/square"))
8700 dc
.DrawRectangle(x
, y
, bulletWidth
, bulletHeight
);
8702 else if (bulletAttr
.GetBulletName() == wxT("standard/diamond"))
8705 pts
[0].x
= x
; pts
[0].y
= y
+ bulletHeight
/2;
8706 pts
[1].x
= x
+ bulletWidth
/2; pts
[1].y
= y
;
8707 pts
[2].x
= x
+ bulletWidth
; pts
[2].y
= y
+ bulletHeight
/2;
8708 pts
[3].x
= x
+ bulletWidth
/2; pts
[3].y
= y
+ bulletHeight
;
8710 dc
.DrawPolygon(4, pts
);
8712 else if (bulletAttr
.GetBulletName() == wxT("standard/triangle"))
8715 pts
[0].x
= x
; pts
[0].y
= y
;
8716 pts
[1].x
= x
+ bulletWidth
; pts
[1].y
= y
+ bulletHeight
/2;
8717 pts
[2].x
= x
; pts
[2].y
= y
+ bulletHeight
;
8719 dc
.DrawPolygon(3, pts
);
8721 else if (bulletAttr
.GetBulletName() == wxT("standard/circle-outline"))
8723 wxCheckSetBrush(dc
, *wxTRANSPARENT_BRUSH
);
8724 dc
.DrawEllipse(x
, y
, bulletWidth
, bulletHeight
);
8726 else // "standard/circle", and catch-all
8728 dc
.DrawEllipse(x
, y
, bulletWidth
, bulletHeight
);
8734 bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxRichTextAttr
& attr
, const wxRect
& rect
, const wxString
& text
)
8739 if ((attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
) && !attr
.GetBulletFont().IsEmpty() && attr
.HasFont())
8741 wxRichTextAttr fontAttr
;
8742 if (attr
.HasFontPixelSize())
8743 fontAttr
.SetFontPixelSize(attr
.GetFontSize());
8745 fontAttr
.SetFontPointSize(attr
.GetFontSize());
8746 fontAttr
.SetFontStyle(attr
.GetFontStyle());
8747 fontAttr
.SetFontWeight(attr
.GetFontWeight());
8748 fontAttr
.SetFontUnderlined(attr
.GetFontUnderlined());
8749 fontAttr
.SetFontFaceName(attr
.GetBulletFont());
8750 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(fontAttr
);
8752 else if (attr
.HasFont())
8753 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(attr
);
8755 font
= (*wxNORMAL_FONT
);
8757 wxCheckSetFont(dc
, font
);
8759 if (attr
.GetTextColour().IsOk())
8760 dc
.SetTextForeground(attr
.GetTextColour());
8762 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
8764 int charHeight
= dc
.GetCharHeight();
8766 dc
.GetTextExtent(text
, & tw
, & th
);
8770 // Calculate the top position of the character (as opposed to the whole line height)
8771 int y
= rect
.y
+ (rect
.height
- charHeight
);
8773 // The margin between a bullet and text.
8774 int margin
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer::GetBulletRightMargin());
8776 if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
)
8777 x
= (rect
.x
+ rect
.width
) - tw
- margin
;
8778 else if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
)
8779 x
= x
+ (rect
.width
)/2 - tw
/2;
8781 dc
.DrawText(text
, x
, y
);
8789 bool wxRichTextStdRenderer::DrawBitmapBullet(wxRichTextParagraph
* WXUNUSED(paragraph
), wxDC
& WXUNUSED(dc
), const wxRichTextAttr
& WXUNUSED(attr
), const wxRect
& WXUNUSED(rect
))
8791 // Currently unimplemented. The intention is to store bitmaps by name in a media store associated
8792 // with the buffer. The store will allow retrieval from memory, disk or other means.
8796 /// Enumerate the standard bullet names currently supported
8797 bool wxRichTextStdRenderer::EnumerateStandardBulletNames(wxArrayString
& bulletNames
)
8799 bulletNames
.Add(wxTRANSLATE("standard/circle"));
8800 bulletNames
.Add(wxTRANSLATE("standard/circle-outline"));
8801 bulletNames
.Add(wxTRANSLATE("standard/square"));
8802 bulletNames
.Add(wxTRANSLATE("standard/diamond"));
8803 bulletNames
.Add(wxTRANSLATE("standard/triangle"));
8812 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBox
, wxRichTextParagraphLayoutBox
)
8814 wxRichTextBox::wxRichTextBox(wxRichTextObject
* parent
):
8815 wxRichTextParagraphLayoutBox(parent
)
8820 bool wxRichTextBox::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8825 // TODO: if the active object in the control, draw an indication.
8826 // We need to add the concept of active object, and not just focus object,
8827 // so we can apply commands (properties, delete, ...) to objects such as text boxes and images.
8828 // Ultimately we would like to be able to interactively resize an active object
8829 // using drag handles.
8830 return wxRichTextParagraphLayoutBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8834 void wxRichTextBox::Copy(const wxRichTextBox
& obj
)
8836 wxRichTextParagraphLayoutBox::Copy(obj
);
8839 // Edit properties via a GUI
8840 bool wxRichTextBox::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8842 wxRichTextObjectPropertiesDialog
boxDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Box Properties"));
8843 boxDlg
.SetAttributes(GetAttributes());
8845 if (boxDlg
.ShowModal() == wxID_OK
)
8847 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
8848 // indeterminate in the object.
8849 boxDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
8860 IMPLEMENT_DYNAMIC_CLASS(wxRichTextField
, wxRichTextParagraphLayoutBox
)
8862 wxRichTextField::wxRichTextField(const wxString
& fieldType
, wxRichTextObject
* parent
):
8863 wxRichTextParagraphLayoutBox(parent
)
8865 SetFieldType(fieldType
);
8869 bool wxRichTextField::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8874 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8875 if (fieldType
&& fieldType
->Draw(this, dc
, context
, range
, selection
, rect
, descent
, style
))
8878 // Fallback; but don't draw guidelines.
8879 style
&= ~wxRICHTEXT_DRAW_GUIDELINES
;
8880 return wxRichTextParagraphLayoutBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8883 bool wxRichTextField::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
)
8885 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8886 if (fieldType
&& fieldType
->Layout(this, dc
, context
, rect
, parentRect
, style
))
8890 return wxRichTextParagraphLayoutBox::Layout(dc
, context
, rect
, parentRect
, style
);
8893 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
8895 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8897 return fieldType
->GetRangeSize((wxRichTextField
*) this, range
, size
, descent
, dc
, context
, flags
, position
, parentSize
, partialExtents
);
8899 return wxRichTextParagraphLayoutBox::GetRangeSize(range
, size
, descent
, dc
, context
, flags
, position
, parentSize
, partialExtents
);
8903 void wxRichTextField::CalculateRange(long start
, long& end
)
8906 wxRichTextParagraphLayoutBox::CalculateRange(start
, end
);
8908 wxRichTextObject::CalculateRange(start
, end
);
8912 void wxRichTextField::Copy(const wxRichTextField
& obj
)
8914 wxRichTextParagraphLayoutBox::Copy(obj
);
8916 UpdateField(GetBuffer());
8919 // Edit properties via a GUI
8920 bool wxRichTextField::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8922 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8924 return fieldType
->EditProperties(this, parent
, buffer
);
8929 bool wxRichTextField::CanEditProperties() const
8931 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8933 return fieldType
->CanEditProperties((wxRichTextField
*) this);
8938 wxString
wxRichTextField::GetPropertiesMenuLabel() const
8940 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8942 return fieldType
->GetPropertiesMenuLabel((wxRichTextField
*) this);
8944 return wxEmptyString
;
8947 bool wxRichTextField::UpdateField(wxRichTextBuffer
* buffer
)
8949 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8951 return fieldType
->UpdateField(buffer
, (wxRichTextField
*) this);
8956 bool wxRichTextField::IsTopLevel() const
8958 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8960 return fieldType
->IsTopLevel((wxRichTextField
*) this);
8965 IMPLEMENT_CLASS(wxRichTextFieldType
, wxObject
)
8967 IMPLEMENT_CLASS(wxRichTextFieldTypeStandard
, wxRichTextFieldType
)
8969 wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString
& name
, const wxString
& label
, int displayStyle
)
8975 SetDisplayStyle(displayStyle
);
8978 wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString
& name
, const wxBitmap
& bitmap
, int displayStyle
)
8984 SetDisplayStyle(displayStyle
);
8987 void wxRichTextFieldTypeStandard::Init()
8989 m_displayStyle
= wxRICHTEXT_FIELD_STYLE_RECTANGLE
;
8990 m_font
= wxFont(6, wxFONTFAMILY_SWISS
, wxFONTSTYLE_NORMAL
, wxFONTWEIGHT_NORMAL
);
8991 m_textColour
= *wxWHITE
;
8992 m_borderColour
= *wxBLACK
;
8993 m_backgroundColour
= *wxBLACK
;
8994 m_verticalPadding
= 1;
8995 m_horizontalPadding
= 3;
8996 m_horizontalMargin
= 2;
8997 m_verticalMargin
= 0;
9000 void wxRichTextFieldTypeStandard::Copy(const wxRichTextFieldTypeStandard
& field
)
9002 wxRichTextFieldType::Copy(field
);
9004 m_label
= field
.m_label
;
9005 m_displayStyle
= field
.m_displayStyle
;
9006 m_font
= field
.m_font
;
9007 m_textColour
= field
.m_textColour
;
9008 m_borderColour
= field
.m_borderColour
;
9009 m_backgroundColour
= field
.m_backgroundColour
;
9010 m_verticalPadding
= field
.m_verticalPadding
;
9011 m_horizontalPadding
= field
.m_horizontalPadding
;
9012 m_horizontalMargin
= field
.m_horizontalMargin
;
9013 m_bitmap
= field
.m_bitmap
;
9016 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
))
9018 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_COMPOSITE
)
9019 return false; // USe default composite drawing
9020 else // if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_RECTANGLE || m_displayStyle == wxRICHTEXT_FIELD_STYLE_NOBORDER)
9024 wxPen
borderPen(m_borderColour
, 1, wxSOLID
);
9025 wxBrush
backgroundBrush(m_backgroundColour
);
9026 wxColour
textColour(m_textColour
);
9028 if (selection
.WithinSelection(obj
->GetRange().GetStart(), obj
))
9030 wxColour
highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
));
9031 wxColour
highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
));
9033 borderPen
= wxPen(highlightTextColour
, 1, wxSOLID
);
9034 backgroundBrush
= wxBrush(highlightColour
);
9036 wxCheckSetBrush(dc
, backgroundBrush
);
9037 wxCheckSetPen(dc
, wxPen(highlightColour
, 1, wxSOLID
));
9038 dc
.DrawRectangle(rect
);
9041 if (m_displayStyle
!= wxRICHTEXT_FIELD_STYLE_NO_BORDER
)
9044 // objectRect is the area where the content is drawn, after margins around it have been taken into account
9045 wxRect objectRect
= wxRect(wxPoint(rect
.x
+ m_horizontalMargin
, rect
.y
+ wxMax(0, rect
.height
- descent
- obj
->GetCachedSize().y
)),
9046 wxSize(obj
->GetCachedSize().x
- 2*m_horizontalMargin
- borderSize
, obj
->GetCachedSize().y
));
9048 // clientArea is where the text is actually written
9049 wxRect clientArea
= objectRect
;
9051 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_RECTANGLE
)
9053 dc
.SetPen(borderPen
);
9054 dc
.SetBrush(backgroundBrush
);
9055 dc
.DrawRoundedRectangle(objectRect
, 4.0);
9057 else if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_START_TAG
)
9059 int arrowLength
= objectRect
.height
/2;
9060 clientArea
.width
-= (arrowLength
- m_horizontalPadding
);
9063 pts
[0].x
= objectRect
.x
; pts
[0].y
= objectRect
.y
;
9064 pts
[1].x
= objectRect
.x
+ objectRect
.width
- arrowLength
; pts
[1].y
= objectRect
.y
;
9065 pts
[2].x
= objectRect
.x
+ objectRect
.width
; pts
[2].y
= objectRect
.y
+ (objectRect
.height
/2);
9066 pts
[3].x
= objectRect
.x
+ objectRect
.width
- arrowLength
; pts
[3].y
= objectRect
.y
+ objectRect
.height
;
9067 pts
[4].x
= objectRect
.x
; pts
[4].y
= objectRect
.y
+ objectRect
.height
;
9068 dc
.SetPen(borderPen
);
9069 dc
.SetBrush(backgroundBrush
);
9070 dc
.DrawPolygon(5, pts
);
9072 else if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_END_TAG
)
9074 int arrowLength
= objectRect
.height
/2;
9075 clientArea
.width
-= (arrowLength
- m_horizontalPadding
);
9076 clientArea
.x
+= (arrowLength
- m_horizontalPadding
);
9079 pts
[0].x
= objectRect
.x
+ objectRect
.width
; pts
[0].y
= objectRect
.y
;
9080 pts
[1].x
= objectRect
.x
+ arrowLength
; pts
[1].y
= objectRect
.y
;
9081 pts
[2].x
= objectRect
.x
; pts
[2].y
= objectRect
.y
+ (objectRect
.height
/2);
9082 pts
[3].x
= objectRect
.x
+ arrowLength
; pts
[3].y
= objectRect
.y
+ objectRect
.height
;
9083 pts
[4].x
= objectRect
.x
+ objectRect
.width
; pts
[4].y
= objectRect
.y
+ objectRect
.height
;
9084 dc
.SetPen(borderPen
);
9085 dc
.SetBrush(backgroundBrush
);
9086 dc
.DrawPolygon(5, pts
);
9089 if (m_bitmap
.IsOk())
9091 int x
= clientArea
.x
+ (clientArea
.width
- m_bitmap
.GetWidth())/2;
9092 int y
= clientArea
.y
+ m_verticalPadding
;
9093 dc
.DrawBitmap(m_bitmap
, x
, y
, true);
9095 if (selection
.WithinSelection(obj
->GetRange().GetStart(), obj
))
9097 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
9098 wxCheckSetPen(dc
, *wxBLACK_PEN
);
9099 dc
.SetLogicalFunction(wxINVERT
);
9100 dc
.DrawRectangle(wxRect(x
, y
, m_bitmap
.GetWidth(), m_bitmap
.GetHeight()));
9101 dc
.SetLogicalFunction(wxCOPY
);
9106 wxString
label(m_label
);
9107 if (label
.IsEmpty())
9109 int w
, h
, maxDescent
;
9111 dc
.GetTextExtent(m_label
, & w
, &h
, & maxDescent
);
9112 dc
.SetTextForeground(textColour
);
9114 int x
= clientArea
.x
+ (clientArea
.width
- w
)/2;
9115 int y
= clientArea
.y
+ (clientArea
.height
- (h
- maxDescent
))/2;
9116 dc
.DrawText(m_label
, x
, y
);
9123 bool wxRichTextFieldTypeStandard::Layout(wxRichTextField
* obj
, wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& WXUNUSED(rect
), const wxRect
& WXUNUSED(parentRect
), int style
)
9125 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_COMPOSITE
)
9126 return false; // USe default composite layout
9128 wxSize size
= GetSize(obj
, dc
, context
, style
);
9129 obj
->SetCachedSize(size
);
9130 obj
->SetMinSize(size
);
9131 obj
->SetMaxSize(size
);
9135 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
9137 if (IsTopLevel(obj
))
9138 return obj
->wxRichTextParagraphLayoutBox::GetRangeSize(range
, size
, descent
, dc
, context
, flags
, position
, parentSize
);
9141 wxSize sz
= GetSize(obj
, dc
, context
, 0);
9145 if (partialExtents
->GetCount() > 0)
9146 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
9149 partialExtents
->Add(lastSize
+ sz
.x
);
9156 wxSize
wxRichTextFieldTypeStandard::GetSize(wxRichTextField
* WXUNUSED(obj
), wxDC
& dc
, wxRichTextDrawingContext
& WXUNUSED(context
), int WXUNUSED(style
)) const
9159 int w
= 0, h
= 0, maxDescent
= 0;
9162 if (m_bitmap
.IsOk())
9164 w
= m_bitmap
.GetWidth();
9165 h
= m_bitmap
.GetHeight();
9167 sz
= wxSize(w
+ m_horizontalMargin
*2, h
+ m_verticalMargin
*2);
9171 wxString
label(m_label
);
9172 if (label
.IsEmpty())
9175 dc
.GetTextExtent(label
, & w
, &h
, & maxDescent
);
9177 sz
= wxSize(w
+ m_horizontalPadding
*2 + m_horizontalMargin
*2, h
+ m_verticalPadding
*2 + m_verticalMargin
*2);
9180 if (m_displayStyle
!= wxRICHTEXT_FIELD_STYLE_NO_BORDER
)
9182 sz
.x
+= borderSize
*2;
9183 sz
.y
+= borderSize
*2;
9186 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_START_TAG
|| m_displayStyle
== wxRICHTEXT_FIELD_STYLE_END_TAG
)
9188 // Add space for the arrow
9189 sz
.x
+= (sz
.y
/2 - m_horizontalPadding
);
9195 IMPLEMENT_DYNAMIC_CLASS(wxRichTextCell
, wxRichTextBox
)
9197 wxRichTextCell::wxRichTextCell(wxRichTextObject
* parent
):
9198 wxRichTextBox(parent
)
9203 bool wxRichTextCell::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
9205 return wxRichTextBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
9209 void wxRichTextCell::Copy(const wxRichTextCell
& obj
)
9211 wxRichTextBox::Copy(obj
);
9214 // Edit properties via a GUI
9215 bool wxRichTextCell::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
9217 // We need to gather common attributes for all selected cells.
9219 wxRichTextTable
* table
= wxDynamicCast(GetParent(), wxRichTextTable
);
9220 bool multipleCells
= false;
9221 wxRichTextAttr attr
;
9223 if (table
&& buffer
&& buffer
->GetRichTextCtrl() && buffer
->GetRichTextCtrl()->GetSelection().IsValid() &&
9224 buffer
->GetRichTextCtrl()->GetSelection().GetContainer() == GetParent())
9226 wxRichTextAttr clashingAttr
, absentAttr
;
9227 const wxRichTextSelection
& sel
= buffer
->GetRichTextCtrl()->GetSelection();
9229 int selectedCellCount
= 0;
9230 for (i
= 0; i
< sel
.GetCount(); i
++)
9232 const wxRichTextRange
& range
= sel
[i
];
9233 wxRichTextCell
* cell
= table
->GetCell(range
.GetStart());
9236 wxRichTextAttr cellStyle
= cell
->GetAttributes();
9238 CollectStyle(attr
, cellStyle
, clashingAttr
, absentAttr
);
9240 selectedCellCount
++;
9243 multipleCells
= selectedCellCount
> 1;
9247 attr
= GetAttributes();
9252 caption
= _("Multiple Cell Properties");
9254 caption
= _("Cell Properties");
9256 wxRichTextObjectPropertiesDialog
cellDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, caption
);
9257 cellDlg
.SetAttributes(attr
);
9259 wxRichTextSizePage
* sizePage
= wxDynamicCast(cellDlg
.FindPage(wxCLASSINFO(wxRichTextSizePage
)), wxRichTextSizePage
);
9262 // We don't want position and floating controls for a cell.
9263 sizePage
->ShowPositionControls(false);
9264 sizePage
->ShowFloatingControls(false);
9267 if (cellDlg
.ShowModal() == wxID_OK
)
9271 const wxRichTextSelection
& sel
= buffer
->GetRichTextCtrl()->GetSelection();
9272 // Apply the style; we interpret indeterminate attributes as 'don't touch this attribute'
9273 // since it may represent clashing attributes across multiple objects.
9274 table
->SetCellStyle(sel
, attr
);
9277 // For a single object, indeterminate attributes set by the user should be reflected in the
9278 // actual object style, so pass the wxRICHTEXT_SETSTYLE_RESET flag to assign
9279 // the style directly instead of applying (which ignores indeterminate attributes,
9280 // leaving them as they were).
9281 cellDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
9288 WX_DEFINE_OBJARRAY(wxRichTextObjectPtrArrayArray
)
9290 IMPLEMENT_DYNAMIC_CLASS(wxRichTextTable
, wxRichTextBox
)
9292 wxRichTextTable::wxRichTextTable(wxRichTextObject
* parent
): wxRichTextBox(parent
)
9298 // Draws the object.
9299 bool wxRichTextTable::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
9301 return wxRichTextBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
9304 WX_DECLARE_OBJARRAY(wxRect
, wxRichTextRectArray
);
9305 WX_DEFINE_OBJARRAY(wxRichTextRectArray
);
9307 // Lays the object out. rect is the space available for layout. Often it will
9308 // be the specified overall space for this object, if trying to constrain
9309 // layout to a particular size, or it could be the total space available in the
9310 // parent. rect is the overall size, so we must subtract margins and padding.
9311 // to get the actual available space.
9312 bool wxRichTextTable::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& WXUNUSED(parentRect
), int style
)
9314 SetPosition(rect
.GetPosition());
9316 // TODO: the meaty bit. Calculate sizes of all cells and rows. Try to use
9317 // minimum size if within alloted size, then divide up remaining size
9318 // between rows/cols.
9321 wxRichTextBuffer
* buffer
= GetBuffer();
9322 if (buffer
) scale
= buffer
->GetScale();
9324 wxRect availableSpace
= GetAvailableContentArea(dc
, context
, rect
);
9325 wxTextAttrDimensionConverter
converter(dc
, scale
, availableSpace
.GetSize());
9327 wxRichTextAttr
attr(GetAttributes());
9328 context
.ApplyVirtualAttributes(attr
, this);
9330 // If we have no fixed table size, and assuming we're not pushed for
9331 // space, then we don't have to try to stretch the table to fit the contents.
9332 bool stretchToFitTableWidth
= false;
9334 int tableWidth
= rect
.width
;
9335 if (attr
.GetTextBoxAttr().GetWidth().IsValid())
9337 tableWidth
= converter
.GetPixels(attr
.GetTextBoxAttr().GetWidth());
9339 // Fixed table width, so we do want to stretch columns out if necessary.
9340 stretchToFitTableWidth
= true;
9342 // Shouldn't be able to exceed the size passed to this function
9343 tableWidth
= wxMin(rect
.width
, tableWidth
);
9346 // Get internal padding
9347 int paddingLeft
= 0, paddingTop
= 0;
9348 if (attr
.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
9349 paddingLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetLeft());
9350 if (attr
.GetTextBoxAttr().GetPadding().GetTop().IsValid())
9351 paddingTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetTop());
9353 // Assume that left and top padding are also used for inter-cell padding.
9354 int paddingX
= paddingLeft
;
9355 int paddingY
= paddingTop
;
9357 int totalLeftMargin
= 0, totalRightMargin
= 0, totalTopMargin
= 0, totalBottomMargin
= 0;
9358 GetTotalMargin(dc
, buffer
, attr
, totalLeftMargin
, totalRightMargin
, totalTopMargin
, totalBottomMargin
);
9360 // Internal table width - the area for content
9361 int internalTableWidth
= tableWidth
- totalLeftMargin
- totalRightMargin
;
9363 int rowCount
= m_cells
.GetCount();
9364 if (m_colCount
== 0 || rowCount
== 0)
9366 wxRect
overallRect(rect
.x
, rect
.y
, totalLeftMargin
+ totalRightMargin
, totalTopMargin
+ totalBottomMargin
);
9367 SetCachedSize(overallRect
.GetSize());
9369 // Zero content size
9370 SetMinSize(overallRect
.GetSize());
9371 SetMaxSize(GetMinSize());
9375 // The final calculated widths
9376 wxArrayInt colWidths
;
9377 colWidths
.Add(0, m_colCount
);
9379 wxArrayInt absoluteColWidths
;
9380 absoluteColWidths
.Add(0, m_colCount
);
9382 wxArrayInt percentageColWidths
;
9383 percentageColWidths
.Add(0, m_colCount
);
9384 // wxArrayInt percentageColWidthsSpanning(m_colCount);
9385 // These are only relevant when the first column contains spanning information.
9386 // wxArrayInt columnSpans(m_colCount); // Each contains 1 for non-spanning cell, > 1 for spanning cell.
9387 wxArrayInt maxColWidths
;
9388 maxColWidths
.Add(0, m_colCount
);
9389 wxArrayInt minColWidths
;
9390 minColWidths
.Add(0, m_colCount
);
9392 wxSize
tableSize(tableWidth
, 0);
9396 for (i
= 0; i
< m_colCount
; i
++)
9398 absoluteColWidths
[i
] = 0;
9399 // absoluteColWidthsSpanning[i] = 0;
9400 percentageColWidths
[i
] = -1;
9401 // percentageColWidthsSpanning[i] = -1;
9403 maxColWidths
[i
] = 0;
9404 minColWidths
[i
] = 0;
9405 // columnSpans[i] = 1;
9408 // (0) Determine which cells are visible according to spans
9410 // __________________
9415 // |------------------|
9416 // |__________________| 4
9418 // To calculate cell visibility:
9419 // First find all spanning cells. Build an array of span records with start x, y and end x, y.
9420 // Then for each cell, test whether we're within one of those cells, and unless we're at the start of
9421 // that cell, hide the cell.
9423 // We can also use this array to match the size of spanning cells to the grid. Or just do
9424 // this when we iterate through all cells.
9426 // 0.1: add spanning cells to an array
9427 wxRichTextRectArray rectArray
;
9428 for (j
= 0; j
< m_rowCount
; j
++)
9430 for (i
= 0; i
< m_colCount
; i
++)
9432 wxRichTextBox
* cell
= GetCell(j
, i
);
9433 int colSpan
= 1, rowSpan
= 1;
9434 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9435 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9436 if (cell
->GetProperties().HasProperty(wxT("rowspan")))
9437 rowSpan
= cell
->GetProperties().GetPropertyLong(wxT("rowspan"));
9438 if (colSpan
> 1 || rowSpan
> 1)
9440 rectArray
.Add(wxRect(i
, j
, colSpan
, rowSpan
));
9444 // 0.2: find which cells are subsumed by a spanning cell
9445 for (j
= 0; j
< m_rowCount
; j
++)
9447 for (i
= 0; i
< m_colCount
; i
++)
9449 wxRichTextBox
* cell
= GetCell(j
, i
);
9450 if (rectArray
.GetCount() == 0)
9456 int colSpan
= 1, rowSpan
= 1;
9457 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9458 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9459 if (cell
->GetProperties().HasProperty(wxT("rowspan")))
9460 rowSpan
= cell
->GetProperties().GetPropertyLong(wxT("rowspan"));
9461 if (colSpan
> 1 || rowSpan
> 1)
9463 // Assume all spanning cells are shown
9469 for (k
= 0; k
< (int) rectArray
.GetCount(); k
++)
9471 if (rectArray
[k
].Contains(wxPoint(i
, j
)))
9483 // TODO: find the first spanned cell in each row that spans the most columns and doesn't
9484 // overlap with a spanned cell starting at a previous column position.
9485 // This means we need to keep an array of rects so we can check. However
9486 // it does also mean that some spans simply may not be taken into account
9487 // where there are different spans happening on different rows. In these cases,
9488 // they will simply be as wide as their constituent columns.
9490 // (1) Do an initial layout for all cells to get minimum and maximum size, and get
9491 // the absolute or percentage width of each column.
9493 for (j
= 0; j
< m_rowCount
; j
++)
9495 // First get the overall margins so we can calculate percentage widths based on
9496 // the available content space for all cells on the row
9498 int overallRowContentMargin
= 0;
9499 int visibleCellCount
= 0;
9501 for (i
= 0; i
< m_colCount
; i
++)
9503 wxRichTextBox
* cell
= GetCell(j
, i
);
9504 if (cell
->IsShown())
9506 int cellTotalLeftMargin
= 0, cellTotalRightMargin
= 0, cellTotalTopMargin
= 0, cellTotalBottomMargin
= 0;
9507 GetTotalMargin(dc
, buffer
, cell
->GetAttributes(), cellTotalLeftMargin
, cellTotalRightMargin
, cellTotalTopMargin
, cellTotalBottomMargin
);
9509 overallRowContentMargin
+= (cellTotalLeftMargin
+ cellTotalRightMargin
);
9510 visibleCellCount
++;
9514 // Add in inter-cell padding
9515 overallRowContentMargin
+= ((visibleCellCount
-1) * paddingX
);
9517 int rowContentWidth
= internalTableWidth
- overallRowContentMargin
;
9518 wxSize
rowTableSize(rowContentWidth
, 0);
9519 wxTextAttrDimensionConverter
converter(dc
, scale
, rowTableSize
);
9521 for (i
= 0; i
< m_colCount
; i
++)
9523 wxRichTextBox
* cell
= GetCell(j
, i
);
9524 if (cell
->IsShown())
9527 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9528 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9530 // Lay out cell to find min/max widths
9531 cell
->Invalidate(wxRICHTEXT_ALL
);
9532 cell
->Layout(dc
, context
, availableSpace
, availableSpace
, style
);
9536 int absoluteCellWidth
= -1;
9537 int percentageCellWidth
= -1;
9539 // I think we need to calculate percentages from the internal table size,
9540 // minus the padding between cells which we'll need to calculate from the
9541 // (number of VISIBLE cells - 1)*paddingX. Then percentages that add up to 100%
9542 // will add up to 100%. In CSS, the width specifies the cell's content rect width,
9543 // so if we want to conform to that we'll need to add in the overall cell margins.
9544 // However, this will make it difficult to specify percentages that add up to
9545 // 100% and still fit within the table width.
9546 // Let's say two cells have 50% width. They have 10 pixels of overall margin each.
9547 // The table content rect is 500 pixels and the inter-cell padding is 20 pixels.
9548 // If we're using internal content size for the width, we would calculate the
9549 // the overall cell width for n cells as:
9550 // (500 - 20*(n-1) - overallCellMargin1 - overallCellMargin2 - ...) * percentage / 100
9551 // + thisOverallCellMargin
9552 // = 500 - 20 - 10 - 10) * 0.5 + 10 = 240 pixels overall cell width.
9553 // Adding this back, we get 240 + 240 + 20 = 500 pixels.
9555 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9557 int w
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetWidth());
9558 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
9560 percentageCellWidth
= w
;
9564 absoluteCellWidth
= w
;
9566 // Override absolute width with minimum width if necessary
9567 if (cell
->GetMinSize().x
> 0 && absoluteCellWidth
!=1 && cell
->GetMinSize().x
> absoluteCellWidth
)
9568 absoluteCellWidth
= cell
->GetMinSize().x
;
9571 if (absoluteCellWidth
!= -1)
9573 if (absoluteCellWidth
> absoluteColWidths
[i
])
9574 absoluteColWidths
[i
] = absoluteCellWidth
;
9577 if (percentageCellWidth
!= -1)
9579 if (percentageCellWidth
> percentageColWidths
[i
])
9580 percentageColWidths
[i
] = percentageCellWidth
;
9583 if (colSpan
== 1 && cell
->GetMinSize().x
&& cell
->GetMinSize().x
> minColWidths
[i
])
9584 minColWidths
[i
] = cell
->GetMinSize().x
;
9585 if (colSpan
== 1 && cell
->GetMaxSize().x
&& cell
->GetMaxSize().x
> maxColWidths
[i
])
9586 maxColWidths
[i
] = cell
->GetMaxSize().x
;
9592 // (2) Allocate initial column widths from minimum widths, absolute values and proportions
9593 // TODO: simply merge this into (1).
9594 for (i
= 0; i
< m_colCount
; i
++)
9596 if (absoluteColWidths
[i
] > 0)
9598 colWidths
[i
] = absoluteColWidths
[i
];
9600 else if (percentageColWidths
[i
] > 0)
9602 colWidths
[i
] = percentageColWidths
[i
];
9604 // This is rubbish - we calculated the absolute widths from percentages, so
9605 // we can't do it again here.
9606 //colWidths[i] = (int) (double(percentageColWidths[i]) * double(tableWidth) / 100.0 + 0.5);
9610 // (3) Process absolute or proportional widths of spanning columns,
9611 // now that we know what our fixed column widths are going to be.
9612 // Spanned cells will try to adjust columns so the span will fit.
9613 // Even existing fixed column widths can be expanded if necessary.
9614 // Actually, currently fixed columns widths aren't adjusted; instead,
9615 // the algorithm favours earlier rows and adjusts unspecified column widths
9616 // the first time only. After that, we can't know whether the column has been
9617 // specified explicitly or not. (We could make a note if necessary.)
9618 for (j
= 0; j
< m_rowCount
; j
++)
9620 // First get the overall margins so we can calculate percentage widths based on
9621 // the available content space for all cells on the row
9623 int overallRowContentMargin
= 0;
9624 int visibleCellCount
= 0;
9626 for (i
= 0; i
< m_colCount
; i
++)
9628 wxRichTextBox
* cell
= GetCell(j
, i
);
9629 if (cell
->IsShown())
9631 int cellTotalLeftMargin
= 0, cellTotalRightMargin
= 0, cellTotalTopMargin
= 0, cellTotalBottomMargin
= 0;
9632 GetTotalMargin(dc
, buffer
, cell
->GetAttributes(), cellTotalLeftMargin
, cellTotalRightMargin
, cellTotalTopMargin
, cellTotalBottomMargin
);
9634 overallRowContentMargin
+= (cellTotalLeftMargin
+ cellTotalRightMargin
);
9635 visibleCellCount
++;
9639 // Add in inter-cell padding
9640 overallRowContentMargin
+= ((visibleCellCount
-1) * paddingX
);
9642 int rowContentWidth
= internalTableWidth
- overallRowContentMargin
;
9643 wxSize
rowTableSize(rowContentWidth
, 0);
9644 wxTextAttrDimensionConverter
converter(dc
, scale
, rowTableSize
);
9646 for (i
= 0; i
< m_colCount
; i
++)
9648 wxRichTextBox
* cell
= GetCell(j
, i
);
9649 if (cell
->IsShown())
9652 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9653 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9657 int spans
= wxMin(colSpan
, m_colCount
- i
);
9661 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9663 cellWidth
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetWidth());
9664 // Override absolute width with minimum width if necessary
9665 if (cell
->GetMinSize().x
> 0 && cellWidth
!=1 && cell
->GetMinSize().x
> cellWidth
)
9666 cellWidth
= cell
->GetMinSize().x
;
9670 // Do we want to do this? It's the only chance we get to
9671 // use the cell's min/max sizes, so we need to work out
9672 // how we're going to balance the unspecified spanning cell
9673 // width with the possibility more-constrained constituent cell widths.
9674 // Say there's a tiny bitmap giving it a max width of 10 pixels. We
9675 // don't want to constraint all the spanned columns to fit into this cell.
9676 // OK, let's say that if any of the constituent columns don't fit,
9677 // then we simply stop constraining the columns; instead, we'll just fit the spanning
9678 // cells to the columns later.
9679 cellWidth
= cell
->GetMinSize().x
;
9680 if (cell
->GetMaxSize().x
> cellWidth
)
9681 cellWidth
= cell
->GetMaxSize().x
;
9684 // Subtract the padding between cells
9685 int spanningWidth
= cellWidth
;
9686 spanningWidth
-= paddingX
* (spans
-1);
9688 if (spanningWidth
> 0)
9690 // Now share the spanning width between columns within that span
9691 // TODO: take into account min widths of columns within the span
9692 int spanningWidthLeft
= spanningWidth
;
9693 int stretchColCount
= 0;
9694 for (k
= i
; k
< (i
+spans
); k
++)
9696 if (colWidths
[k
] > 0) // absolute or proportional width has been specified
9697 spanningWidthLeft
-= colWidths
[k
];
9701 // Now divide what's left between the remaining columns
9703 if (stretchColCount
> 0)
9704 colShare
= spanningWidthLeft
/ stretchColCount
;
9705 int colShareRemainder
= spanningWidthLeft
- (colShare
* stretchColCount
);
9707 // If fixed-width columns are currently too big, then we'll later
9708 // stretch the spanned cell to fit.
9710 if (spanningWidthLeft
> 0)
9712 for (k
= i
; k
< (i
+spans
); k
++)
9714 if (colWidths
[k
] <= 0) // absolute or proportional width has not been specified
9716 int newWidth
= colShare
;
9717 if (k
== (i
+spans
-1))
9718 newWidth
+= colShareRemainder
; // ensure all pixels are filled
9719 colWidths
[k
] = newWidth
;
9730 // (4) Next, share any remaining space out between columns that have not yet been calculated.
9731 // TODO: take into account min widths of columns within the span
9732 int tableWidthMinusPadding
= internalTableWidth
- (m_colCount
-1)*paddingX
;
9733 int widthLeft
= tableWidthMinusPadding
;
9734 int stretchColCount
= 0;
9735 for (i
= 0; i
< m_colCount
; i
++)
9737 // TODO: we need to take into account min widths.
9738 // Subtract min width from width left, then
9739 // add the colShare to the min width
9740 if (colWidths
[i
] > 0) // absolute or proportional width has been specified
9741 widthLeft
-= colWidths
[i
];
9744 if (minColWidths
[i
] > 0)
9745 widthLeft
-= minColWidths
[i
];
9751 // Now divide what's left between the remaining columns
9753 if (stretchColCount
> 0)
9754 colShare
= widthLeft
/ stretchColCount
;
9755 int colShareRemainder
= widthLeft
- (colShare
* stretchColCount
);
9757 // Check we don't have enough space, in which case shrink all columns, overriding
9758 // any absolute/proportional widths
9759 // TODO: actually we would like to divide up the shrinkage according to size.
9760 // How do we calculate the proportions that will achieve this?
9761 // Could first choose an arbitrary value for stretching cells, and then calculate
9762 // factors to multiply each width by.
9763 // TODO: want to record this fact and pass to an iteration that tries e.g. min widths
9764 if (widthLeft
< 0 || (stretchToFitTableWidth
&& (stretchColCount
== 0)))
9766 colShare
= tableWidthMinusPadding
/ m_colCount
;
9767 colShareRemainder
= tableWidthMinusPadding
- (colShare
* m_colCount
);
9768 for (i
= 0; i
< m_colCount
; i
++)
9771 minColWidths
[i
] = 0;
9775 // We have to adjust the columns if either we need to shrink the
9776 // table to fit the parent/table width, or we explicitly set the
9777 // table width and need to stretch out the table.
9778 if (widthLeft
< 0 || stretchToFitTableWidth
)
9780 for (i
= 0; i
< m_colCount
; i
++)
9782 if (colWidths
[i
] <= 0) // absolute or proportional width has not been specified
9784 if (minColWidths
[i
] > 0)
9785 colWidths
[i
] = minColWidths
[i
] + colShare
;
9787 colWidths
[i
] = colShare
;
9788 if (i
== (m_colCount
-1))
9789 colWidths
[i
] += colShareRemainder
; // ensure all pixels are filled
9794 // TODO: if spanned cells have no specified or max width, make them the
9795 // as big as the columns they span. Do this for all spanned cells in all
9796 // rows, of course. Size any spanned cells left over at the end - even if they
9797 // have width > 0, make sure they're limited to the appropriate column edge.
9801 Sort out confusion between content width
9802 and overall width later. For now, assume we specify overall width.
9804 So, now we've laid out the table to fit into the given space
9805 and have used specified widths and minimum widths.
9807 Now we need to consider how we will try to take maximum width into account.
9811 // (??) TODO: take max width into account
9813 // (6) Lay out all cells again with the current values
9816 int y
= availableSpace
.y
;
9817 for (j
= 0; j
< m_rowCount
; j
++)
9819 int x
= availableSpace
.x
; // TODO: take into account centering etc.
9820 int maxCellHeight
= 0;
9821 int maxSpecifiedCellHeight
= 0;
9823 wxArrayInt actualWidths
;
9824 actualWidths
.Add(0, m_colCount
);
9826 wxTextAttrDimensionConverter
converter(dc
, scale
);
9827 for (i
= 0; i
< m_colCount
; i
++)
9829 wxRichTextCell
* cell
= GetCell(j
, i
);
9830 if (cell
->IsShown())
9832 // Get max specified cell height
9833 // Don't handle percentages for height
9834 if (cell
->GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && cell
->GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() != wxTEXT_ATTR_UNITS_PERCENTAGE
)
9836 int h
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetHeight());
9837 if (h
> maxSpecifiedCellHeight
)
9838 maxSpecifiedCellHeight
= h
;
9841 if (colWidths
[i
] > 0) // absolute or proportional width has been specified
9844 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9845 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9847 wxRect availableCellSpace
;
9849 // TODO: take into acount spans
9852 // Calculate the size of this spanning cell from its constituent columns
9854 int spans
= wxMin(colSpan
, m_colCount
- i
);
9855 for (k
= i
; k
< spans
; k
++)
9861 availableCellSpace
= wxRect(x
, y
, xx
, -1);
9864 availableCellSpace
= wxRect(x
, y
, colWidths
[i
], -1);
9866 // Store actual width so we can force cell to be the appropriate width on the final loop
9867 actualWidths
[i
] = availableCellSpace
.GetWidth();
9870 cell
->Invalidate(wxRICHTEXT_ALL
);
9871 cell
->Layout(dc
, context
, availableCellSpace
, availableSpace
, style
);
9873 // TODO: use GetCachedSize().x to compute 'natural' size
9875 x
+= (availableCellSpace
.GetWidth() + paddingX
);
9876 if (cell
->GetCachedSize().y
> maxCellHeight
)
9877 maxCellHeight
= cell
->GetCachedSize().y
;
9882 maxCellHeight
= wxMax(maxCellHeight
, maxSpecifiedCellHeight
);
9884 for (i
= 0; i
< m_colCount
; i
++)
9886 wxRichTextCell
* cell
= GetCell(j
, i
);
9887 if (cell
->IsShown())
9889 wxRect availableCellSpace
= wxRect(cell
->GetPosition(), wxSize(actualWidths
[i
], maxCellHeight
));
9890 // Lay out cell with new height
9891 cell
->Invalidate(wxRICHTEXT_ALL
);
9892 cell
->Layout(dc
, context
, availableCellSpace
, availableSpace
, style
);
9894 // Make sure the cell size really is the appropriate size,
9895 // not the calculated box size
9896 cell
->SetCachedSize(wxSize(actualWidths
[i
], maxCellHeight
));
9898 maxRight
= wxMax(maxRight
, cell
->GetPosition().x
+ cell
->GetCachedSize().x
);
9903 if (j
< (m_rowCount
-1))
9907 // We need to add back the margins etc.
9909 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
9910 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxRight
- availableSpace
.x
, y
- availableSpace
.y
));
9911 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
9912 SetCachedSize(marginRect
.GetSize());
9915 // TODO: calculate max size
9917 SetMaxSize(GetCachedSize());
9920 // TODO: calculate min size
9922 SetMinSize(GetCachedSize());
9925 // TODO: currently we use either a fixed table width or the parent's size.
9926 // We also want to be able to calculate the table width from its content,
9927 // whether using fixed column widths or cell content min/max width.
9928 // Probably need a boolean flag to say whether we need to stretch cells
9929 // to fit the table width, or to simply use min/max cell widths. The
9930 // trouble with this is that if cell widths are not specified, they
9931 // will be tiny; we could use arbitrary defaults but this seems unsatisfactory.
9932 // Anyway, ignoring that problem, we probably need to factor layout into a function
9933 // that can can calculate the maximum unconstrained layout in case table size is
9934 // not specified. Then LayoutToBestSize() can choose to use either parent size to
9935 // constrain Layout(), or the previously-calculated max size to constraint layout.
9940 // Finds the absolute position and row height for the given character position
9941 bool wxRichTextTable::FindPosition(wxDC
& dc
, wxRichTextDrawingContext
& context
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
9943 wxRichTextCell
* child
= GetCell(index
+1);
9946 // Find the position at the start of the child cell, since the table doesn't
9947 // have any caret position of its own.
9948 return child
->FindPosition(dc
, context
, -1, pt
, height
, forceLineStart
);
9954 // Get the cell at the given character position (in the range of the table).
9955 wxRichTextCell
* wxRichTextTable::GetCell(long pos
) const
9957 int row
= 0, col
= 0;
9958 if (GetCellRowColumnPosition(pos
, row
, col
))
9960 return GetCell(row
, col
);
9966 // Get the row/column for a given character position
9967 bool wxRichTextTable::GetCellRowColumnPosition(long pos
, int& row
, int& col
) const
9969 if (m_colCount
== 0 || m_rowCount
== 0)
9972 row
= (int) (pos
/ m_colCount
);
9973 col
= pos
- (row
* m_colCount
);
9975 wxASSERT(row
< m_rowCount
&& col
< m_colCount
);
9977 if (row
< m_rowCount
&& col
< m_colCount
)
9983 // Calculate range, taking row/cell ordering into account instead of relying
9984 // on list ordering.
9985 void wxRichTextTable::CalculateRange(long start
, long& end
)
9987 long current
= start
;
9988 long lastEnd
= current
;
9997 for (i
= 0; i
< m_rowCount
; i
++)
9999 for (j
= 0; j
< m_colCount
; j
++)
10001 wxRichTextCell
* child
= GetCell(i
, j
);
10006 child
->CalculateRange(current
, childEnd
);
10008 lastEnd
= childEnd
;
10009 current
= childEnd
+ 1;
10014 // A top-level object always has a range of size 1,
10015 // because its children don't count at this level.
10017 m_range
.SetRange(start
, start
);
10019 // An object with no children has zero length
10020 if (m_children
.GetCount() == 0)
10022 m_ownRange
.SetRange(0, lastEnd
);
10025 // Gets the range size.
10026 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
10028 return wxRichTextBox::GetRangeSize(range
, size
, descent
, dc
, context
, flags
, position
, parentSize
, partialExtents
);
10031 // Deletes content in the given range.
10032 bool wxRichTextTable::DeleteRange(const wxRichTextRange
& WXUNUSED(range
))
10034 // TODO: implement deletion of cells
10038 // Gets any text in this object for the given range.
10039 wxString
wxRichTextTable::GetTextForRange(const wxRichTextRange
& range
) const
10041 return wxRichTextBox::GetTextForRange(range
);
10044 // Copies this object.
10045 void wxRichTextTable::Copy(const wxRichTextTable
& obj
)
10047 wxRichTextBox::Copy(obj
);
10051 m_rowCount
= obj
.m_rowCount
;
10052 m_colCount
= obj
.m_colCount
;
10054 m_cells
.Add(wxRichTextObjectPtrArray(), m_rowCount
);
10057 for (i
= 0; i
< m_rowCount
; i
++)
10059 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
10060 for (j
= 0; j
< m_colCount
; j
++)
10062 wxRichTextCell
* cell
= wxDynamicCast(obj
.GetCell(i
, j
)->Clone(), wxRichTextCell
);
10065 colArray
.Add(cell
);
10070 void wxRichTextTable::ClearTable()
10076 bool wxRichTextTable::CreateTable(int rows
, int cols
)
10083 m_cells
.Add(wxRichTextObjectPtrArray(), rows
);
10086 for (i
= 0; i
< rows
; i
++)
10088 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
10089 for (j
= 0; j
< cols
; j
++)
10091 wxRichTextCell
* cell
= new wxRichTextCell
;
10093 cell
->AddParagraph(wxEmptyString
);
10095 colArray
.Add(cell
);
10102 wxRichTextCell
* wxRichTextTable::GetCell(int row
, int col
) const
10104 wxASSERT(row
< m_rowCount
);
10105 wxASSERT(col
< m_colCount
);
10107 if (row
< m_rowCount
&& col
< m_colCount
)
10109 wxRichTextObjectPtrArray
& colArray
= m_cells
[row
];
10110 wxRichTextObject
* obj
= colArray
[col
];
10111 return wxDynamicCast(obj
, wxRichTextCell
);
10117 // Returns a selection object specifying the selections between start and end character positions.
10118 // For example, a table would deduce what cells (of range length 1) are selected when dragging across the table.
10119 wxRichTextSelection
wxRichTextTable::GetSelection(long start
, long end
) const
10121 wxRichTextSelection selection
;
10122 selection
.SetContainer((wxRichTextTable
*) this);
10131 wxASSERT( start
>= 0 && end
< (m_colCount
* m_rowCount
));
10133 if (end
>= (m_colCount
* m_rowCount
))
10136 // We need to find the rectangle of cells that is described by the rectangle
10137 // with start, end as the diagonal. Make sure we don't add cells that are
10138 // not currenty visible because they are overlapped by spanning cells.
10140 --------------------------
10141 | 0 | 1 | 2 | 3 | 4 |
10142 --------------------------
10143 | 5 | 6 | 7 | 8 | 9 |
10144 --------------------------
10145 | 10 | 11 | 12 | 13 | 14 |
10146 --------------------------
10147 | 15 | 16 | 17 | 18 | 19 |
10148 --------------------------
10150 Let's say we select 6 -> 18.
10152 Left and right edge cols of rectangle are 1 and 3 inclusive. Find least/greatest to find
10153 which is left and which is right.
10155 Top and bottom edge rows are 1 and 3 inclusive. Again, find least/greatest to find top and bottom.
10157 Now go through rows from 1 to 3 and only add cells that are (a) within above column range
10163 int leftCol
= start
- m_colCount
* int(start
/m_colCount
);
10164 int rightCol
= end
- m_colCount
* int(end
/m_colCount
);
10166 int topRow
= int(start
/m_colCount
);
10167 int bottomRow
= int(end
/m_colCount
);
10169 if (leftCol
> rightCol
)
10171 int tmp
= rightCol
;
10172 rightCol
= leftCol
;
10176 if (topRow
> bottomRow
)
10178 int tmp
= bottomRow
;
10179 bottomRow
= topRow
;
10184 for (i
= topRow
; i
<= bottomRow
; i
++)
10186 for (j
= leftCol
; j
<= rightCol
; j
++)
10188 wxRichTextCell
* cell
= GetCell(i
, j
);
10189 if (cell
&& cell
->IsShown())
10190 selection
.Add(cell
->GetRange());
10197 // Sets the attributes for the cells specified by the selection.
10198 bool wxRichTextTable::SetCellStyle(const wxRichTextSelection
& selection
, const wxRichTextAttr
& style
, int flags
)
10200 if (selection
.GetContainer() != this)
10203 wxRichTextBuffer
* buffer
= GetBuffer();
10204 bool haveControl
= (buffer
&& buffer
->GetRichTextCtrl() != NULL
);
10205 bool withUndo
= haveControl
&& ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
10208 buffer
->BeginBatchUndo(_("Set Cell Style"));
10210 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
10213 wxRichTextCell
* cell
= wxDynamicCast(node
->GetData(), wxRichTextCell
);
10214 if (cell
&& selection
.WithinSelection(cell
->GetRange().GetStart()))
10215 SetStyle(cell
, style
, flags
);
10216 node
= node
->GetNext();
10219 // Do action, or delay it until end of batch.
10221 buffer
->EndBatchUndo();
10226 bool wxRichTextTable::DeleteRows(int startRow
, int noRows
)
10228 wxASSERT((startRow
+ noRows
) < m_rowCount
);
10229 if ((startRow
+ noRows
) >= m_rowCount
)
10233 for (i
= startRow
; i
< (startRow
+noRows
); i
++)
10235 wxRichTextObjectPtrArray
& colArray
= m_cells
[startRow
];
10236 for (j
= 0; j
< (int) colArray
.GetCount(); j
++)
10238 wxRichTextObject
* cell
= colArray
[j
];
10239 RemoveChild(cell
, true);
10242 // Keep deleting at the same position, since we move all
10244 m_cells
.RemoveAt(startRow
);
10247 m_rowCount
= m_rowCount
- noRows
;
10252 bool wxRichTextTable::DeleteColumns(int startCol
, int noCols
)
10254 wxASSERT((startCol
+ noCols
) < m_colCount
);
10255 if ((startCol
+ noCols
) >= m_colCount
)
10258 bool deleteRows
= (noCols
== m_colCount
);
10261 for (i
= 0; i
< m_rowCount
; i
++)
10263 wxRichTextObjectPtrArray
& colArray
= m_cells
[deleteRows
? 0 : i
];
10264 for (j
= startCol
; j
< (startCol
+noCols
); j
++)
10266 wxRichTextObject
* cell
= colArray
[j
];
10267 RemoveChild(cell
, true);
10271 m_cells
.RemoveAt(0);
10276 m_colCount
= m_colCount
- noCols
;
10281 bool wxRichTextTable::AddRows(int startRow
, int noRows
, const wxRichTextAttr
& attr
)
10283 wxASSERT(startRow
<= m_rowCount
);
10284 if (startRow
> m_rowCount
)
10288 for (i
= 0; i
< noRows
; i
++)
10291 if (startRow
== m_rowCount
)
10293 m_cells
.Add(wxRichTextObjectPtrArray());
10294 idx
= m_cells
.GetCount() - 1;
10298 m_cells
.Insert(wxRichTextObjectPtrArray(), startRow
+i
);
10302 wxRichTextObjectPtrArray
& colArray
= m_cells
[idx
];
10303 for (j
= 0; j
< m_colCount
; j
++)
10305 wxRichTextCell
* cell
= new wxRichTextCell
;
10306 cell
->GetAttributes() = attr
;
10309 colArray
.Add(cell
);
10313 m_rowCount
= m_rowCount
+ noRows
;
10317 bool wxRichTextTable::AddColumns(int startCol
, int noCols
, const wxRichTextAttr
& attr
)
10319 wxASSERT(startCol
<= m_colCount
);
10320 if (startCol
> m_colCount
)
10324 for (i
= 0; i
< m_rowCount
; i
++)
10326 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
10327 for (j
= 0; j
< noCols
; j
++)
10329 wxRichTextCell
* cell
= new wxRichTextCell
;
10330 cell
->GetAttributes() = attr
;
10334 if (startCol
== m_colCount
)
10335 colArray
.Add(cell
);
10337 colArray
.Insert(cell
, startCol
+j
);
10341 m_colCount
= m_colCount
+ noCols
;
10346 // Edit properties via a GUI
10347 bool wxRichTextTable::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
10349 wxRichTextObjectPropertiesDialog
boxDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Table Properties"));
10350 boxDlg
.SetAttributes(GetAttributes());
10352 if (boxDlg
.ShowModal() == wxID_OK
)
10354 boxDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
10362 * Module to initialise and clean up handlers
10365 class wxRichTextModule
: public wxModule
10367 DECLARE_DYNAMIC_CLASS(wxRichTextModule
)
10369 wxRichTextModule() {}
10372 wxRichTextBuffer::SetRenderer(new wxRichTextStdRenderer
);
10373 wxRichTextBuffer::InitStandardHandlers();
10374 wxRichTextParagraph::InitDefaultTabs();
10376 wxRichTextXMLHandler::RegisterNodeName(wxT("text"), wxT("wxRichTextPlainText"));
10377 wxRichTextXMLHandler::RegisterNodeName(wxT("symbol"), wxT("wxRichTextPlainText"));
10378 wxRichTextXMLHandler::RegisterNodeName(wxT("image"), wxT("wxRichTextImage"));
10379 wxRichTextXMLHandler::RegisterNodeName(wxT("paragraph"), wxT("wxRichTextParagraph"));
10380 wxRichTextXMLHandler::RegisterNodeName(wxT("paragraphlayout"), wxT("wxRichTextParagraphLayoutBox"));
10381 wxRichTextXMLHandler::RegisterNodeName(wxT("textbox"), wxT("wxRichTextBox"));
10382 wxRichTextXMLHandler::RegisterNodeName(wxT("cell"), wxT("wxRichTextCell"));
10383 wxRichTextXMLHandler::RegisterNodeName(wxT("table"), wxT("wxRichTextTable"));
10384 wxRichTextXMLHandler::RegisterNodeName(wxT("field"), wxT("wxRichTextField"));
10390 wxRichTextBuffer::CleanUpHandlers();
10391 wxRichTextBuffer::CleanUpDrawingHandlers();
10392 wxRichTextBuffer::CleanUpFieldTypes();
10393 wxRichTextXMLHandler::ClearNodeToClassMap();
10394 wxRichTextDecimalToRoman(-1);
10395 wxRichTextParagraph::ClearDefaultTabs();
10396 wxRichTextCtrl::ClearAvailableFontNames();
10397 wxRichTextBuffer::SetRenderer(NULL
);
10401 IMPLEMENT_DYNAMIC_CLASS(wxRichTextModule
, wxModule
)
10404 // If the richtext lib is dynamically loaded after the app has already started
10405 // (such as from wxPython) then the built-in module system will not init this
10406 // module. Provide this function to do it manually.
10407 void wxRichTextModuleInit()
10409 wxModule
* module = new wxRichTextModule
;
10411 wxModule::RegisterModule(module);
10416 * Commands for undo/redo
10420 wxRichTextCommand::wxRichTextCommand(const wxString
& name
, wxRichTextCommandId id
, wxRichTextBuffer
* buffer
,
10421 wxRichTextParagraphLayoutBox
* container
, wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
): wxCommand(true, name
)
10423 /* wxRichTextAction* action = */ new wxRichTextAction(this, name
, id
, buffer
, container
, ctrl
, ignoreFirstTime
);
10426 wxRichTextCommand::wxRichTextCommand(const wxString
& name
): wxCommand(true, name
)
10430 wxRichTextCommand::~wxRichTextCommand()
10435 void wxRichTextCommand::AddAction(wxRichTextAction
* action
)
10437 if (!m_actions
.Member(action
))
10438 m_actions
.Append(action
);
10441 bool wxRichTextCommand::Do()
10443 for (wxList::compatibility_iterator node
= m_actions
.GetFirst(); node
; node
= node
->GetNext())
10445 wxRichTextAction
* action
= (wxRichTextAction
*) node
->GetData();
10452 bool wxRichTextCommand::Undo()
10454 for (wxList::compatibility_iterator node
= m_actions
.GetLast(); node
; node
= node
->GetPrevious())
10456 wxRichTextAction
* action
= (wxRichTextAction
*) node
->GetData();
10463 void wxRichTextCommand::ClearActions()
10465 WX_CLEAR_LIST(wxList
, m_actions
);
10469 * Individual action
10473 wxRichTextAction::wxRichTextAction(wxRichTextCommand
* cmd
, const wxString
& name
, wxRichTextCommandId id
,
10474 wxRichTextBuffer
* buffer
, wxRichTextParagraphLayoutBox
* container
,
10475 wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
)
10479 m_containerAddress
.Create(buffer
, container
);
10480 m_ignoreThis
= ignoreFirstTime
;
10485 m_newParagraphs
.SetDefaultStyle(buffer
->GetDefaultStyle());
10486 m_newParagraphs
.SetBasicStyle(buffer
->GetBasicStyle());
10488 cmd
->AddAction(this);
10491 wxRichTextAction::~wxRichTextAction()
10497 // Returns the container that this action refers to, using the container address and top-level buffer.
10498 wxRichTextParagraphLayoutBox
* wxRichTextAction::GetContainer() const
10500 wxRichTextParagraphLayoutBox
* container
= wxDynamicCast(GetContainerAddress().GetObject(m_buffer
), wxRichTextParagraphLayoutBox
);
10505 void wxRichTextAction::CalculateRefreshOptimizations(wxArrayInt
& optimizationLineCharPositions
, wxArrayInt
& optimizationLineYPositions
)
10507 // Store a list of line start character and y positions so we can figure out which area
10508 // we need to refresh
10510 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10511 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10512 wxASSERT(container
!= NULL
);
10516 // NOTE: we're assuming that the buffer is laid out correctly at this point.
10517 // If we had several actions, which only invalidate and leave layout until the
10518 // paint handler is called, then this might not be true. So we may need to switch
10519 // optimisation on only when we're simply adding text and not simultaneously
10520 // deleting a selection, for example. Or, we make sure the buffer is laid out correctly
10521 // first, but of course this means we'll be doing it twice.
10522 if (!m_buffer
->IsDirty() && m_ctrl
) // can only do optimisation if the buffer is already laid out correctly
10524 wxSize clientSize
= m_ctrl
->GetUnscaledSize(m_ctrl
->GetClientSize());
10525 wxPoint firstVisiblePt
= m_ctrl
->GetUnscaledPoint(m_ctrl
->GetFirstVisiblePoint());
10526 int lastY
= firstVisiblePt
.y
+ clientSize
.y
;
10528 wxRichTextParagraph
* para
= container
->GetParagraphAtPosition(GetRange().GetStart());
10529 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(para
);
10532 wxRichTextParagraph
* child
= (wxRichTextParagraph
*) node
->GetData();
10533 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
10536 wxRichTextLine
* line
= node2
->GetData();
10537 wxPoint pt
= line
->GetAbsolutePosition();
10538 wxRichTextRange range
= line
->GetAbsoluteRange();
10542 node2
= wxRichTextLineList::compatibility_iterator();
10543 node
= wxRichTextObjectList::compatibility_iterator();
10545 else if (range
.GetStart() > GetPosition() && pt
.y
>= firstVisiblePt
.y
)
10547 optimizationLineCharPositions
.Add(range
.GetStart());
10548 optimizationLineYPositions
.Add(pt
.y
);
10552 node2
= node2
->GetNext();
10556 node
= node
->GetNext();
10562 bool wxRichTextAction::Do()
10564 m_buffer
->Modify(true);
10566 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10567 wxASSERT(container
!= NULL
);
10573 case wxRICHTEXT_INSERT
:
10575 // Store a list of line start character and y positions so we can figure out which area
10576 // we need to refresh
10577 wxArrayInt optimizationLineCharPositions
;
10578 wxArrayInt optimizationLineYPositions
;
10580 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10581 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
10584 container
->InsertFragment(GetRange().GetStart(), m_newParagraphs
);
10585 container
->UpdateRanges();
10587 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10588 // Layout() would stop prematurely at the top level.
10589 container
->InvalidateHierarchy(wxRichTextRange(wxMax(0, GetRange().GetStart()-1), GetRange().GetEnd()));
10591 long newCaretPosition
= GetPosition() + m_newParagraphs
.GetOwnRange().GetLength();
10593 // Character position to caret position
10594 newCaretPosition
--;
10596 // Don't take into account the last newline
10597 if (m_newParagraphs
.GetPartialParagraph())
10598 newCaretPosition
--;
10600 if (m_newParagraphs
.GetChildren().GetCount() > 1)
10602 wxRichTextObject
* p
= (wxRichTextObject
*) m_newParagraphs
.GetChildren().GetLast()->GetData();
10603 if (p
->GetRange().GetLength() == 1)
10604 newCaretPosition
--;
10607 newCaretPosition
= wxMin(newCaretPosition
, (container
->GetOwnRange().GetEnd()-1));
10609 UpdateAppearance(newCaretPosition
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */);
10611 wxRichTextEvent
cmdEvent(
10612 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED
,
10613 m_ctrl
? m_ctrl
->GetId() : -1);
10614 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10615 cmdEvent
.SetRange(GetRange());
10616 cmdEvent
.SetPosition(GetRange().GetStart());
10617 cmdEvent
.SetContainer(container
);
10619 m_buffer
->SendEvent(cmdEvent
);
10623 case wxRICHTEXT_DELETE
:
10625 wxArrayInt optimizationLineCharPositions
;
10626 wxArrayInt optimizationLineYPositions
;
10628 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10629 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
10632 container
->DeleteRange(GetRange());
10633 container
->UpdateRanges();
10634 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10635 // Layout() would stop prematurely at the top level.
10636 container
->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
10638 long caretPos
= GetRange().GetStart()-1;
10639 if (caretPos
>= container
->GetOwnRange().GetEnd())
10642 UpdateAppearance(caretPos
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */);
10644 wxRichTextEvent
cmdEvent(
10645 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED
,
10646 m_ctrl
? m_ctrl
->GetId() : -1);
10647 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10648 cmdEvent
.SetRange(GetRange());
10649 cmdEvent
.SetPosition(GetRange().GetStart());
10650 cmdEvent
.SetContainer(container
);
10652 m_buffer
->SendEvent(cmdEvent
);
10656 case wxRICHTEXT_CHANGE_STYLE
:
10657 case wxRICHTEXT_CHANGE_PROPERTIES
:
10659 ApplyParagraphs(GetNewParagraphs());
10661 // Invalidate the whole buffer if there were floating objects
10662 if (wxRichTextBuffer::GetFloatingLayoutMode() && container
->GetFloatingObjectCount() > 0)
10663 m_buffer
->InvalidateHierarchy(wxRICHTEXT_ALL
);
10666 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10667 // Layout() would stop prematurely at the top level.
10668 container
->InvalidateHierarchy(GetRange());
10671 UpdateAppearance(GetPosition());
10673 wxRichTextEvent
cmdEvent(
10674 m_cmdId
== wxRICHTEXT_CHANGE_STYLE
? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
: wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED
,
10675 m_ctrl
? m_ctrl
->GetId() : -1);
10676 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10677 cmdEvent
.SetRange(GetRange());
10678 cmdEvent
.SetPosition(GetRange().GetStart());
10679 cmdEvent
.SetContainer(container
);
10681 m_buffer
->SendEvent(cmdEvent
);
10685 case wxRICHTEXT_CHANGE_ATTRIBUTES
:
10687 wxRichTextObject
* obj
= m_objectAddress
.GetObject(m_buffer
); // container->GetChildAtPosition(GetRange().GetStart());
10690 wxRichTextAttr oldAttr
= obj
->GetAttributes();
10691 obj
->GetAttributes() = m_attributes
;
10692 m_attributes
= oldAttr
;
10695 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10696 // Layout() would stop prematurely at the top level.
10697 // Invalidate the whole buffer if there were floating objects
10698 if (wxRichTextBuffer::GetFloatingLayoutMode() && container
->GetFloatingObjectCount() > 0)
10699 m_buffer
->InvalidateHierarchy(wxRICHTEXT_ALL
);
10701 container
->InvalidateHierarchy(GetRange());
10703 UpdateAppearance(GetPosition());
10705 wxRichTextEvent
cmdEvent(
10706 wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
,
10707 m_ctrl
? m_ctrl
->GetId() : -1);
10708 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10709 cmdEvent
.SetRange(GetRange());
10710 cmdEvent
.SetPosition(GetRange().GetStart());
10711 cmdEvent
.SetContainer(container
);
10713 m_buffer
->SendEvent(cmdEvent
);
10717 case wxRICHTEXT_CHANGE_OBJECT
:
10719 wxRichTextObject
* obj
= m_objectAddress
.GetObject(m_buffer
);
10720 // wxRichTextObject* obj = container->GetChildAtPosition(GetRange().GetStart());
10721 if (obj
&& m_object
)
10723 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(obj
);
10726 wxRichTextObject
* obj
= node
->GetData();
10727 node
->SetData(m_object
);
10732 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10733 // Layout() would stop prematurely at the top level.
10734 // Invalidate the whole buffer if there were floating objects
10735 if (wxRichTextBuffer::GetFloatingLayoutMode() && container
->GetFloatingObjectCount() > 0)
10736 m_buffer
->InvalidateHierarchy(wxRICHTEXT_ALL
);
10738 container
->InvalidateHierarchy(GetRange());
10740 UpdateAppearance(GetPosition());
10742 // TODO: send new kind of modification event
10753 bool wxRichTextAction::Undo()
10755 m_buffer
->Modify(true);
10757 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10758 wxASSERT(container
!= NULL
);
10764 case wxRICHTEXT_INSERT
:
10766 wxArrayInt optimizationLineCharPositions
;
10767 wxArrayInt optimizationLineYPositions
;
10769 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10770 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
10773 container
->DeleteRange(GetRange());
10774 container
->UpdateRanges();
10776 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10777 // Layout() would stop prematurely at the top level.
10778 container
->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
10780 long newCaretPosition
= GetPosition() - 1;
10782 UpdateAppearance(newCaretPosition
, true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */);
10784 wxRichTextEvent
cmdEvent(
10785 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED
,
10786 m_ctrl
? m_ctrl
->GetId() : -1);
10787 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10788 cmdEvent
.SetRange(GetRange());
10789 cmdEvent
.SetPosition(GetRange().GetStart());
10790 cmdEvent
.SetContainer(container
);
10792 m_buffer
->SendEvent(cmdEvent
);
10796 case wxRICHTEXT_DELETE
:
10798 wxArrayInt optimizationLineCharPositions
;
10799 wxArrayInt optimizationLineYPositions
;
10801 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10802 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
10805 container
->InsertFragment(GetRange().GetStart(), m_oldParagraphs
);
10806 container
->UpdateRanges();
10808 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10809 // Layout() would stop prematurely at the top level.
10810 container
->InvalidateHierarchy(GetRange());
10812 UpdateAppearance(GetPosition(), true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */);
10814 wxRichTextEvent
cmdEvent(
10815 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED
,
10816 m_ctrl
? m_ctrl
->GetId() : -1);
10817 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10818 cmdEvent
.SetRange(GetRange());
10819 cmdEvent
.SetPosition(GetRange().GetStart());
10820 cmdEvent
.SetContainer(container
);
10822 m_buffer
->SendEvent(cmdEvent
);
10826 case wxRICHTEXT_CHANGE_STYLE
:
10827 case wxRICHTEXT_CHANGE_PROPERTIES
:
10829 ApplyParagraphs(GetOldParagraphs());
10830 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10831 // Layout() would stop prematurely at the top level.
10832 container
->InvalidateHierarchy(GetRange());
10834 UpdateAppearance(GetPosition());
10836 wxRichTextEvent
cmdEvent(
10837 m_cmdId
== wxRICHTEXT_CHANGE_STYLE
? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
: wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED
,
10838 m_ctrl
? m_ctrl
->GetId() : -1);
10839 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10840 cmdEvent
.SetRange(GetRange());
10841 cmdEvent
.SetPosition(GetRange().GetStart());
10842 cmdEvent
.SetContainer(container
);
10844 m_buffer
->SendEvent(cmdEvent
);
10848 case wxRICHTEXT_CHANGE_ATTRIBUTES
:
10849 case wxRICHTEXT_CHANGE_OBJECT
:
10860 /// Update the control appearance
10861 void wxRichTextAction::UpdateAppearance(long caretPosition
, bool sendUpdateEvent
, wxArrayInt
* optimizationLineCharPositions
, wxArrayInt
* optimizationLineYPositions
, bool isDoCmd
)
10863 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10864 wxASSERT(container
!= NULL
);
10870 m_ctrl
->SetFocusObject(container
);
10871 m_ctrl
->SetCaretPosition(caretPosition
);
10873 if (!m_ctrl
->IsFrozen())
10875 wxRect containerRect
= container
->GetRect();
10877 m_ctrl
->LayoutContent();
10879 // Refresh everything if there were floating objects or the container changed size
10880 // (we can't yet optimize in these cases, since more complex interaction with other content occurs)
10881 if ((wxRichTextBuffer::GetFloatingLayoutMode() && container
->GetFloatingObjectCount() > 0) || (container
->GetParent() && containerRect
!= container
->GetRect()))
10883 m_ctrl
->Refresh(false);
10887 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10888 // Find refresh rectangle if we are in a position to optimise refresh
10889 if ((m_cmdId
== wxRICHTEXT_INSERT
|| m_cmdId
== wxRICHTEXT_DELETE
) && optimizationLineCharPositions
)
10893 wxSize clientSize
= m_ctrl
->GetUnscaledSize(m_ctrl
->GetClientSize());
10894 wxPoint firstVisiblePt
= m_ctrl
->GetUnscaledPoint(m_ctrl
->GetFirstVisiblePoint());
10896 // Start/end positions
10898 int lastY
= firstVisiblePt
.y
+ clientSize
.y
;
10900 bool foundEnd
= false;
10902 // position offset - how many characters were inserted
10903 int positionOffset
= GetRange().GetLength();
10905 // Determine whether this is Do or Undo, and adjust positionOffset accordingly
10906 if ((m_cmdId
== wxRICHTEXT_DELETE
&& isDoCmd
) || (m_cmdId
== wxRICHTEXT_INSERT
&& !isDoCmd
))
10907 positionOffset
= - positionOffset
;
10909 // find the first line which is being drawn at the same position as it was
10910 // before. Since we're talking about a simple insertion, we can assume
10911 // that the rest of the window does not need to be redrawn.
10913 wxRichTextParagraph
* para
= container
->GetParagraphAtPosition(GetPosition());
10914 // Since we support floating layout, we should redraw the whole para instead of just
10915 // the first line touching the invalid range.
10918 firstY
= para
->GetPosition().y
;
10921 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(para
);
10924 wxRichTextParagraph
* child
= (wxRichTextParagraph
*) node
->GetData();
10925 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
10928 wxRichTextLine
* line
= node2
->GetData();
10929 wxPoint pt
= line
->GetAbsolutePosition();
10930 wxRichTextRange range
= line
->GetAbsoluteRange();
10932 // we want to find the first line that is in the same position
10933 // as before. This will mean we're at the end of the changed text.
10935 if (pt
.y
> lastY
) // going past the end of the window, no more info
10937 node2
= wxRichTextLineList::compatibility_iterator();
10938 node
= wxRichTextObjectList::compatibility_iterator();
10940 // Detect last line in the buffer
10941 else if (!node2
->GetNext() && para
->GetRange().Contains(container
->GetOwnRange().GetEnd()))
10943 // If deleting text, make sure we refresh below as well as above
10944 if (positionOffset
>= 0)
10947 lastY
= pt
.y
+ line
->GetSize().y
;
10950 node2
= wxRichTextLineList::compatibility_iterator();
10951 node
= wxRichTextObjectList::compatibility_iterator();
10957 // search for this line being at the same position as before
10958 for (i
= 0; i
< optimizationLineCharPositions
->GetCount(); i
++)
10960 if (((*optimizationLineCharPositions
)[i
] + positionOffset
== range
.GetStart()) &&
10961 ((*optimizationLineYPositions
)[i
] == pt
.y
))
10963 // Stop, we're now the same as we were
10968 node2
= wxRichTextLineList::compatibility_iterator();
10969 node
= wxRichTextObjectList::compatibility_iterator();
10977 node2
= node2
->GetNext();
10981 node
= node
->GetNext();
10984 firstY
= wxMax(firstVisiblePt
.y
, firstY
);
10986 lastY
= firstVisiblePt
.y
+ clientSize
.y
;
10988 // Convert to device coordinates
10989 wxRect
rect(m_ctrl
->GetPhysicalPoint(m_ctrl
->GetScaledPoint(wxPoint(firstVisiblePt
.x
, firstY
))), m_ctrl
->GetScaledSize(wxSize(clientSize
.x
, lastY
- firstY
)));
10990 m_ctrl
->RefreshRect(rect
);
10994 m_ctrl
->Refresh(false);
10996 m_ctrl
->PositionCaret();
10998 // This causes styles to persist when doing programmatic
10999 // content creation except when Freeze/Thaw is used, so
11000 // disable this and check for the consequences.
11001 // m_ctrl->SetDefaultStyleToCursorStyle();
11003 if (sendUpdateEvent
)
11004 wxTextCtrl::SendTextUpdatedEvent(m_ctrl
);
11009 /// Replace the buffer paragraphs with the new ones.
11010 void wxRichTextAction::ApplyParagraphs(const wxRichTextParagraphLayoutBox
& fragment
)
11012 wxRichTextParagraphLayoutBox
* container
= GetContainer();
11013 wxASSERT(container
!= NULL
);
11017 wxRichTextObjectList::compatibility_iterator node
= fragment
.GetChildren().GetFirst();
11020 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
11021 wxASSERT (para
!= NULL
);
11023 // We'll replace the existing paragraph by finding the paragraph at this position,
11024 // delete its node data, and setting a copy as the new node data.
11025 // TODO: make more efficient by simply swapping old and new paragraph objects.
11027 wxRichTextParagraph
* existingPara
= container
->GetParagraphAtPosition(para
->GetRange().GetStart());
11030 wxRichTextObjectList::compatibility_iterator bufferParaNode
= container
->GetChildren().Find(existingPara
);
11031 if (bufferParaNode
)
11033 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(*para
);
11034 newPara
->SetParent(container
);
11036 bufferParaNode
->SetData(newPara
);
11038 delete existingPara
;
11042 node
= node
->GetNext();
11049 * This stores beginning and end positions for a range of data.
11052 WX_DEFINE_OBJARRAY(wxRichTextRangeArray
);
11054 /// Limit this range to be within 'range'
11055 bool wxRichTextRange::LimitTo(const wxRichTextRange
& range
)
11057 if (m_start
< range
.m_start
)
11058 m_start
= range
.m_start
;
11060 if (m_end
> range
.m_end
)
11061 m_end
= range
.m_end
;
11067 * wxRichTextImage implementation
11068 * This object represents an image.
11071 IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage
, wxRichTextObject
)
11073 wxRichTextImage::wxRichTextImage(const wxImage
& image
, wxRichTextObject
* parent
, wxRichTextAttr
* charStyle
):
11074 wxRichTextObject(parent
)
11077 m_imageBlock
.MakeImageBlockDefaultQuality(image
, wxBITMAP_TYPE_PNG
);
11079 SetAttributes(*charStyle
);
11082 wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock
& imageBlock
, wxRichTextObject
* parent
, wxRichTextAttr
* charStyle
):
11083 wxRichTextObject(parent
)
11086 m_imageBlock
= imageBlock
;
11088 SetAttributes(*charStyle
);
11091 wxRichTextImage::~wxRichTextImage()
11095 void wxRichTextImage::Init()
11097 m_originalImageSize
= wxSize(-1, -1);
11100 /// Create a cached image at the required size
11101 bool wxRichTextImage::LoadImageCache(wxDC
& dc
, bool resetCache
, const wxSize
& parentSize
)
11103 if (!m_imageBlock
.IsOk())
11106 // If we have an original image size, use that to compute the cached bitmap size
11107 // instead of loading the image each time. This way we can avoid loading
11108 // the image so long as the new cached bitmap size hasn't changed.
11111 if (resetCache
|| m_originalImageSize
.GetWidth() <= 0 || m_originalImageSize
.GetHeight() <= 0)
11113 m_imageCache
= wxNullBitmap
;
11115 m_imageBlock
.Load(image
);
11119 m_originalImageSize
= wxSize(image
.GetWidth(), image
.GetHeight());
11122 int width
= m_originalImageSize
.GetWidth();
11123 int height
= m_originalImageSize
.GetHeight();
11125 int parentWidth
= 0;
11126 int parentHeight
= 0;
11129 int maxHeight
= -1;
11131 wxSize sz
= parentSize
;
11132 if (sz
== wxDefaultSize
)
11134 if (GetParent() && GetParent()->GetParent())
11135 sz
= GetParent()->GetParent()->GetCachedSize();
11138 if (sz
!= wxDefaultSize
)
11140 wxRichTextBuffer
* buffer
= GetBuffer();
11143 // Find the actual space available when margin is taken into account
11144 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
11145 marginRect
= wxRect(0, 0, sz
.x
, sz
.y
);
11146 if (GetParent() && GetParent()->GetParent())
11148 buffer
->GetBoxRects(dc
, buffer
, GetParent()->GetParent()->GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
11149 sz
= contentRect
.GetSize();
11152 // Use a minimum size to stop images becoming very small
11153 parentWidth
= wxMax(100, sz
.GetWidth());
11154 parentHeight
= wxMax(100, sz
.GetHeight());
11156 if (buffer
->GetRichTextCtrl())
11157 // Start with a maximum width of the control size, even if not specified by the content,
11158 // to minimize the amount of picture overlapping the right-hand side
11159 maxWidth
= parentWidth
;
11163 if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetWidth().GetValue() > 0)
11165 if (parentWidth
> 0 && GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
11166 width
= (int) ((GetAttributes().GetTextBoxAttr().GetWidth().GetValue() * parentWidth
)/100.0);
11167 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
11168 width
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetWidth().GetValue());
11169 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
11170 width
= GetAttributes().GetTextBoxAttr().GetWidth().GetValue();
11173 // Limit to max width
11175 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() > 0)
11179 if (parentWidth
> 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
11180 mw
= (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() * parentWidth
)/100.0);
11181 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
11182 mw
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue());
11183 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
11184 mw
= GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue();
11186 // If we already have a smaller max width due to the constraints of the control size,
11187 // don't use the larger max width.
11188 if (mw
!= -1 && ((maxWidth
== -1) || (mw
< maxWidth
)))
11192 if (maxWidth
> 0 && width
> maxWidth
)
11195 // Preserve the aspect ratio
11196 if (width
!= m_originalImageSize
.GetWidth())
11197 height
= (int) (float(m_originalImageSize
.GetHeight()) * (float(width
)/float(m_originalImageSize
.GetWidth())));
11199 if (GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetHeight().GetValue() > 0)
11201 if (parentHeight
> 0 && GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
11202 height
= (int) ((GetAttributes().GetTextBoxAttr().GetHeight().GetValue() * parentHeight
)/100.0);
11203 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
11204 height
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetHeight().GetValue());
11205 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
11206 height
= GetAttributes().GetTextBoxAttr().GetHeight().GetValue();
11208 // Preserve the aspect ratio
11209 if (height
!= m_originalImageSize
.GetHeight())
11210 width
= (int) (float(m_originalImageSize
.GetWidth()) * (float(height
)/float(m_originalImageSize
.GetHeight())));
11213 // Limit to max height
11215 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() > 0)
11217 if (parentHeight
> 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
11218 maxHeight
= (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() * parentHeight
)/100.0);
11219 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
11220 maxHeight
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue());
11221 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
11222 maxHeight
= GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue();
11225 if (maxHeight
> 0 && height
> maxHeight
)
11227 height
= maxHeight
;
11229 // Preserve the aspect ratio
11230 if (height
!= m_originalImageSize
.GetHeight())
11231 width
= (int) (float(m_originalImageSize
.GetWidth()) * (float(height
)/float(m_originalImageSize
.GetHeight())));
11234 // Prevent the use of zero size
11235 width
= wxMax(1, width
);
11236 height
= wxMax(1, height
);
11238 if (m_imageCache
.IsOk() && m_imageCache
.GetWidth() == width
&& m_imageCache
.GetHeight() == height
)
11240 // Do nothing, we didn't need to change the image cache
11246 m_imageBlock
.Load(image
);
11251 if (image
.GetWidth() == width
&& image
.GetHeight() == height
)
11252 m_imageCache
= wxBitmap(image
);
11255 // If the original width and height is small, e.g. 400 or below,
11256 // scale up and then down to improve image quality. This can make
11257 // a big difference, with not much performance hit.
11258 int upscaleThreshold
= 400;
11260 if (image
.GetWidth() <= upscaleThreshold
|| image
.GetHeight() <= upscaleThreshold
)
11262 img
= image
.Scale(image
.GetWidth()*2, image
.GetHeight()*2);
11263 img
.Rescale(width
, height
, wxIMAGE_QUALITY_HIGH
);
11266 img
= image
.Scale(width
, height
, wxIMAGE_QUALITY_HIGH
);
11267 m_imageCache
= wxBitmap(img
);
11271 return m_imageCache
.IsOk();
11275 bool wxRichTextImage::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& WXUNUSED(range
), const wxRichTextSelection
& selection
, const wxRect
& rect
, int WXUNUSED(descent
), int WXUNUSED(style
))
11280 // Don't need cached size AFAIK
11281 // wxSize size = GetCachedSize();
11282 if (!LoadImageCache(dc
))
11285 wxRichTextAttr
attr(GetAttributes());
11286 context
.ApplyVirtualAttributes(attr
, this);
11288 DrawBoxAttributes(dc
, GetBuffer(), attr
, wxRect(rect
.GetPosition(), GetCachedSize()));
11290 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
11291 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
11292 marginRect
= rect
; // outer rectangle, will calculate contentRect
11293 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
11295 dc
.DrawBitmap(m_imageCache
, contentRect
.x
, contentRect
.y
, true);
11297 if (selection
.WithinSelection(GetRange().GetStart(), this))
11299 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
11300 wxCheckSetPen(dc
, *wxBLACK_PEN
);
11301 dc
.SetLogicalFunction(wxINVERT
);
11302 dc
.DrawRectangle(contentRect
);
11303 dc
.SetLogicalFunction(wxCOPY
);
11309 /// Lay the item out
11310 bool wxRichTextImage::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& WXUNUSED(parentRect
), int WXUNUSED(style
))
11312 if (!LoadImageCache(dc
))
11315 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
11316 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
11317 contentRect
= wxRect(wxPoint(0,0), imageSize
);
11319 wxRichTextAttr
attr(GetAttributes());
11320 context
.ApplyVirtualAttributes(attr
, this);
11322 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
11324 wxSize overallSize
= marginRect
.GetSize();
11326 SetCachedSize(overallSize
);
11327 SetMaxSize(overallSize
);
11328 SetMinSize(overallSize
);
11329 SetPosition(rect
.GetPosition());
11334 /// Get/set the object size for the given range. Returns false if the range
11335 /// is invalid for this object.
11336 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
11338 if (!range
.IsWithin(GetRange()))
11341 if (!((wxRichTextImage
*)this)->LoadImageCache(dc
, false, parentSize
))
11343 size
.x
= 0; size
.y
= 0;
11344 if (partialExtents
)
11345 partialExtents
->Add(0);
11349 wxRichTextAttr
attr(GetAttributes());
11350 context
.ApplyVirtualAttributes(attr
, (wxRichTextObject
*) this);
11352 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
11353 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
11354 contentRect
= wxRect(wxPoint(0,0), imageSize
);
11355 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
11357 wxSize overallSize
= marginRect
.GetSize();
11359 if (partialExtents
)
11360 partialExtents
->Add(overallSize
.x
);
11362 size
= overallSize
;
11367 // Get the 'natural' size for an object. For an image, it would be the
11369 wxTextAttrSize
wxRichTextImage::GetNaturalSize() const
11371 wxTextAttrSize size
;
11372 if (GetImageCache().IsOk())
11374 size
.SetWidth(GetImageCache().GetWidth(), wxTEXT_ATTR_UNITS_PIXELS
);
11375 size
.SetHeight(GetImageCache().GetHeight(), wxTEXT_ATTR_UNITS_PIXELS
);
11382 void wxRichTextImage::Copy(const wxRichTextImage
& obj
)
11384 wxRichTextObject::Copy(obj
);
11386 m_imageBlock
= obj
.m_imageBlock
;
11387 m_originalImageSize
= obj
.m_originalImageSize
;
11390 /// Edit properties via a GUI
11391 bool wxRichTextImage::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
11393 wxRichTextObjectPropertiesDialog
imageDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Picture Properties"));
11394 imageDlg
.SetAttributes(GetAttributes());
11396 if (imageDlg
.ShowModal() == wxID_OK
)
11398 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
11399 // indeterminate in the object.
11400 imageDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
11412 /// Compare two attribute objects
11413 bool wxTextAttrEq(const wxRichTextAttr
& attr1
, const wxRichTextAttr
& attr2
)
11415 return (attr1
== attr2
);
11419 bool wxRichTextTabsEq(const wxArrayInt
& tabs1
, const wxArrayInt
& tabs2
)
11421 if (tabs1
.GetCount() != tabs2
.GetCount())
11425 for (i
= 0; i
< tabs1
.GetCount(); i
++)
11427 if (tabs1
[i
] != tabs2
[i
])
11433 bool wxRichTextApplyStyle(wxRichTextAttr
& destStyle
, const wxRichTextAttr
& style
, wxRichTextAttr
* compareWith
)
11435 return destStyle
.Apply(style
, compareWith
);
11438 // Remove attributes
11439 bool wxRichTextRemoveStyle(wxRichTextAttr
& destStyle
, const wxRichTextAttr
& style
)
11441 return destStyle
.RemoveStyle(style
);
11444 /// Combine two bitlists, specifying the bits of interest with separate flags.
11445 bool wxRichTextCombineBitlists(int& valueA
, int valueB
, int& flagsA
, int flagsB
)
11447 return wxRichTextAttr::CombineBitlists(valueA
, valueB
, flagsA
, flagsB
);
11450 /// Compare two bitlists
11451 bool wxRichTextBitlistsEqPartial(int valueA
, int valueB
, int flags
)
11453 return wxRichTextAttr::BitlistsEqPartial(valueA
, valueB
, flags
);
11456 /// Split into paragraph and character styles
11457 bool wxRichTextSplitParaCharStyles(const wxRichTextAttr
& style
, wxRichTextAttr
& parStyle
, wxRichTextAttr
& charStyle
)
11459 return wxRichTextAttr::SplitParaCharStyles(style
, parStyle
, charStyle
);
11462 /// Convert a decimal to Roman numerals
11463 wxString
wxRichTextDecimalToRoman(long n
)
11465 static wxArrayInt decimalNumbers
;
11466 static wxArrayString romanNumbers
;
11471 decimalNumbers
.Clear();
11472 romanNumbers
.Clear();
11473 return wxEmptyString
;
11476 if (decimalNumbers
.GetCount() == 0)
11478 #define wxRichTextAddDecRom(n, r) decimalNumbers.Add(n); romanNumbers.Add(r);
11480 wxRichTextAddDecRom(1000, wxT("M"));
11481 wxRichTextAddDecRom(900, wxT("CM"));
11482 wxRichTextAddDecRom(500, wxT("D"));
11483 wxRichTextAddDecRom(400, wxT("CD"));
11484 wxRichTextAddDecRom(100, wxT("C"));
11485 wxRichTextAddDecRom(90, wxT("XC"));
11486 wxRichTextAddDecRom(50, wxT("L"));
11487 wxRichTextAddDecRom(40, wxT("XL"));
11488 wxRichTextAddDecRom(10, wxT("X"));
11489 wxRichTextAddDecRom(9, wxT("IX"));
11490 wxRichTextAddDecRom(5, wxT("V"));
11491 wxRichTextAddDecRom(4, wxT("IV"));
11492 wxRichTextAddDecRom(1, wxT("I"));
11498 while (n
> 0 && i
< 13)
11500 if (n
>= decimalNumbers
[i
])
11502 n
-= decimalNumbers
[i
];
11503 roman
+= romanNumbers
[i
];
11510 if (roman
.IsEmpty())
11516 * wxRichTextFileHandler
11517 * Base class for file handlers
11520 IMPLEMENT_CLASS(wxRichTextFileHandler
, wxObject
)
11522 #if wxUSE_FFILE && wxUSE_STREAMS
11523 bool wxRichTextFileHandler::LoadFile(wxRichTextBuffer
*buffer
, const wxString
& filename
)
11525 wxFFileInputStream
stream(filename
);
11527 return LoadFile(buffer
, stream
);
11532 bool wxRichTextFileHandler::SaveFile(wxRichTextBuffer
*buffer
, const wxString
& filename
)
11534 wxFFileOutputStream
stream(filename
);
11536 return SaveFile(buffer
, stream
);
11540 #endif // wxUSE_FFILE && wxUSE_STREAMS
11542 /// Can we handle this filename (if using files)? By default, checks the extension.
11543 bool wxRichTextFileHandler::CanHandle(const wxString
& filename
) const
11545 wxString path
, file
, ext
;
11546 wxFileName::SplitPath(filename
, & path
, & file
, & ext
);
11548 return (ext
.Lower() == GetExtension());
11552 * wxRichTextTextHandler
11553 * Plain text handler
11556 IMPLEMENT_CLASS(wxRichTextPlainTextHandler
, wxRichTextFileHandler
)
11559 bool wxRichTextPlainTextHandler::DoLoadFile(wxRichTextBuffer
*buffer
, wxInputStream
& stream
)
11561 if (!stream
.IsOk())
11567 while (!stream
.Eof())
11569 int ch
= stream
.GetC();
11573 if (ch
== 10 && lastCh
!= 13)
11576 if (ch
> 0 && ch
!= 10)
11583 buffer
->ResetAndClearCommands();
11585 buffer
->AddParagraphs(str
);
11586 buffer
->UpdateRanges();
11591 bool wxRichTextPlainTextHandler::DoSaveFile(wxRichTextBuffer
*buffer
, wxOutputStream
& stream
)
11593 if (!stream
.IsOk())
11596 wxString text
= buffer
->GetText();
11598 wxString newLine
= wxRichTextLineBreakChar
;
11599 text
.Replace(newLine
, wxT("\n"));
11601 wxCharBuffer buf
= text
.ToAscii();
11603 stream
.Write((const char*) buf
, text
.length());
11606 #endif // wxUSE_STREAMS
11609 * Stores information about an image, in binary in-memory form
11612 wxRichTextImageBlock::wxRichTextImageBlock()
11617 wxRichTextImageBlock::wxRichTextImageBlock(const wxRichTextImageBlock
& block
):wxObject()
11623 wxRichTextImageBlock::~wxRichTextImageBlock()
11628 void wxRichTextImageBlock::Init()
11632 m_imageType
= wxBITMAP_TYPE_INVALID
;
11635 void wxRichTextImageBlock::Clear()
11639 m_imageType
= wxBITMAP_TYPE_INVALID
;
11643 // Load the original image into a memory block.
11644 // If the image is not a JPEG, we must convert it into a JPEG
11645 // to conserve space.
11646 // If it's not a JPEG we can make use of 'image', already scaled, so we don't have to
11647 // load the image a 2nd time.
11649 bool wxRichTextImageBlock::MakeImageBlock(const wxString
& filename
, wxBitmapType imageType
,
11650 wxImage
& image
, bool convertToJPEG
)
11652 m_imageType
= imageType
;
11654 wxString
filenameToRead(filename
);
11655 bool removeFile
= false;
11657 if (imageType
== wxBITMAP_TYPE_INVALID
)
11658 return false; // Could not determine image type
11660 if ((imageType
!= wxBITMAP_TYPE_JPEG
) && convertToJPEG
)
11662 wxString tempFile
=
11663 wxFileName::CreateTempFileName(_("image"));
11665 wxASSERT(!tempFile
.IsEmpty());
11667 image
.SaveFile(tempFile
, wxBITMAP_TYPE_JPEG
);
11668 filenameToRead
= tempFile
;
11671 m_imageType
= wxBITMAP_TYPE_JPEG
;
11674 if (!file
.Open(filenameToRead
))
11677 m_dataSize
= (size_t) file
.Length();
11682 m_data
= ReadBlock(filenameToRead
, m_dataSize
);
11685 wxRemoveFile(filenameToRead
);
11687 return (m_data
!= NULL
);
11690 // Make an image block from the wxImage in the given
11692 bool wxRichTextImageBlock::MakeImageBlock(wxImage
& image
, wxBitmapType imageType
, int quality
)
11694 image
.SetOption(wxT("quality"), quality
);
11696 if (imageType
== wxBITMAP_TYPE_INVALID
)
11697 return false; // Could not determine image type
11699 return DoMakeImageBlock(image
, imageType
);
11702 // Uses a const wxImage for efficiency, but can't set quality (only relevant for JPEG)
11703 bool wxRichTextImageBlock::MakeImageBlockDefaultQuality(const wxImage
& image
, wxBitmapType imageType
)
11705 if (imageType
== wxBITMAP_TYPE_INVALID
)
11706 return false; // Could not determine image type
11708 return DoMakeImageBlock(image
, imageType
);
11711 // Makes the image block
11712 bool wxRichTextImageBlock::DoMakeImageBlock(const wxImage
& image
, wxBitmapType imageType
)
11714 wxMemoryOutputStream memStream
;
11715 if (!image
.SaveFile(memStream
, imageType
))
11720 unsigned char* block
= new unsigned char[memStream
.GetSize()];
11728 m_imageType
= imageType
;
11729 m_dataSize
= memStream
.GetSize();
11731 memStream
.CopyTo(m_data
, m_dataSize
);
11733 return (m_data
!= NULL
);
11737 bool wxRichTextImageBlock::Write(const wxString
& filename
)
11739 return WriteBlock(filename
, m_data
, m_dataSize
);
11742 void wxRichTextImageBlock::Copy(const wxRichTextImageBlock
& block
)
11744 m_imageType
= block
.m_imageType
;
11746 m_dataSize
= block
.m_dataSize
;
11747 if (m_dataSize
== 0)
11750 m_data
= new unsigned char[m_dataSize
];
11752 for (i
= 0; i
< m_dataSize
; i
++)
11753 m_data
[i
] = block
.m_data
[i
];
11757 void wxRichTextImageBlock::operator=(const wxRichTextImageBlock
& block
)
11762 // Load a wxImage from the block
11763 bool wxRichTextImageBlock::Load(wxImage
& image
)
11768 // Read in the image.
11770 wxMemoryInputStream
mstream(m_data
, m_dataSize
);
11771 bool success
= image
.LoadFile(mstream
, GetImageType());
11773 wxString tempFile
= wxFileName::CreateTempFileName(_("image"));
11774 wxASSERT(!tempFile
.IsEmpty());
11776 if (!WriteBlock(tempFile
, m_data
, m_dataSize
))
11780 success
= image
.LoadFile(tempFile
, GetImageType());
11781 wxRemoveFile(tempFile
);
11787 // Write data in hex to a stream
11788 bool wxRichTextImageBlock::WriteHex(wxOutputStream
& stream
)
11790 if (m_dataSize
== 0)
11793 int bufSize
= 100000;
11794 if (int(2*m_dataSize
) < bufSize
)
11795 bufSize
= 2*m_dataSize
;
11796 char* buf
= new char[bufSize
+1];
11798 int left
= m_dataSize
;
11803 if (left
*2 > bufSize
)
11805 n
= bufSize
; left
-= (bufSize
/2);
11809 n
= left
*2; left
= 0;
11813 for (i
= 0; i
< (n
/2); i
++)
11815 wxDecToHex(m_data
[j
], b
, b
+1);
11820 stream
.Write((const char*) buf
, n
);
11826 // Read data in hex from a stream
11827 bool wxRichTextImageBlock::ReadHex(wxInputStream
& stream
, int length
, wxBitmapType imageType
)
11829 int dataSize
= length
/2;
11834 // create a null terminated temporary string:
11838 m_data
= new unsigned char[dataSize
];
11840 for (i
= 0; i
< dataSize
; i
++)
11842 str
[0] = (char)stream
.GetC();
11843 str
[1] = (char)stream
.GetC();
11845 m_data
[i
] = (unsigned char)wxHexToDec(str
);
11848 m_dataSize
= dataSize
;
11849 m_imageType
= imageType
;
11854 // Allocate and read from stream as a block of memory
11855 unsigned char* wxRichTextImageBlock::ReadBlock(wxInputStream
& stream
, size_t size
)
11857 unsigned char* block
= new unsigned char[size
];
11861 stream
.Read(block
, size
);
11866 unsigned char* wxRichTextImageBlock::ReadBlock(const wxString
& filename
, size_t size
)
11868 wxFileInputStream
stream(filename
);
11869 if (!stream
.IsOk())
11872 return ReadBlock(stream
, size
);
11875 // Write memory block to stream
11876 bool wxRichTextImageBlock::WriteBlock(wxOutputStream
& stream
, unsigned char* block
, size_t size
)
11878 stream
.Write((void*) block
, size
);
11879 return stream
.IsOk();
11883 // Write memory block to file
11884 bool wxRichTextImageBlock::WriteBlock(const wxString
& filename
, unsigned char* block
, size_t size
)
11886 wxFileOutputStream
outStream(filename
);
11887 if (!outStream
.IsOk())
11890 return WriteBlock(outStream
, block
, size
);
11893 // Gets the extension for the block's type
11894 wxString
wxRichTextImageBlock::GetExtension() const
11896 wxImageHandler
* handler
= wxImage::FindHandler(GetImageType());
11898 return handler
->GetExtension();
11900 return wxEmptyString
;
11906 * The data object for a wxRichTextBuffer
11909 const wxChar
*wxRichTextBufferDataObject::ms_richTextBufferFormatId
= wxT("wxShape");
11911 wxRichTextBufferDataObject::wxRichTextBufferDataObject(wxRichTextBuffer
* richTextBuffer
)
11913 m_richTextBuffer
= richTextBuffer
;
11915 // this string should uniquely identify our format, but is otherwise
11917 m_formatRichTextBuffer
.SetId(GetRichTextBufferFormatId());
11919 SetFormat(m_formatRichTextBuffer
);
11922 wxRichTextBufferDataObject::~wxRichTextBufferDataObject()
11924 delete m_richTextBuffer
;
11927 // after a call to this function, the richTextBuffer is owned by the caller and it
11928 // is responsible for deleting it!
11929 wxRichTextBuffer
* wxRichTextBufferDataObject::GetRichTextBuffer()
11931 wxRichTextBuffer
* richTextBuffer
= m_richTextBuffer
;
11932 m_richTextBuffer
= NULL
;
11934 return richTextBuffer
;
11937 wxDataFormat
wxRichTextBufferDataObject::GetPreferredFormat(Direction
WXUNUSED(dir
)) const
11939 return m_formatRichTextBuffer
;
11942 size_t wxRichTextBufferDataObject::GetDataSize() const
11944 if (!m_richTextBuffer
)
11950 wxStringOutputStream
stream(& bufXML
);
11951 if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
))
11953 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
11959 wxCharBuffer buffer
= bufXML
.mb_str(wxConvUTF8
);
11960 return strlen(buffer
) + 1;
11962 return bufXML
.Length()+1;
11966 bool wxRichTextBufferDataObject::GetDataHere(void *pBuf
) const
11968 if (!pBuf
|| !m_richTextBuffer
)
11974 wxStringOutputStream
stream(& bufXML
);
11975 if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
))
11977 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
11983 wxCharBuffer buffer
= bufXML
.mb_str(wxConvUTF8
);
11984 size_t len
= strlen(buffer
);
11985 memcpy((char*) pBuf
, (const char*) buffer
, len
);
11986 ((char*) pBuf
)[len
] = 0;
11988 size_t len
= bufXML
.Length();
11989 memcpy((char*) pBuf
, (const char*) bufXML
.c_str(), len
);
11990 ((char*) pBuf
)[len
] = 0;
11996 bool wxRichTextBufferDataObject::SetData(size_t WXUNUSED(len
), const void *buf
)
11998 wxDELETE(m_richTextBuffer
);
12000 wxString
bufXML((const char*) buf
, wxConvUTF8
);
12002 m_richTextBuffer
= new wxRichTextBuffer
;
12004 wxStringInputStream
stream(bufXML
);
12005 if (!m_richTextBuffer
->LoadFile(stream
, wxRICHTEXT_TYPE_XML
))
12007 wxLogError(wxT("Could not read the buffer from an XML stream.\nYou may have forgotten to add the XML file handler."));
12009 wxDELETE(m_richTextBuffer
);
12021 * wxRichTextFontTable
12022 * Manages quick access to a pool of fonts for rendering rich text
12025 WX_DECLARE_STRING_HASH_MAP_WITH_DECL(wxFont
, wxRichTextFontTableHashMap
, class WXDLLIMPEXP_RICHTEXT
);
12027 class wxRichTextFontTableData
: public wxObjectRefData
12030 wxRichTextFontTableData() {}
12032 wxFont
FindFont(const wxRichTextAttr
& fontSpec
, double fontScale
);
12034 wxRichTextFontTableHashMap m_hashMap
;
12037 wxFont
wxRichTextFontTableData::FindFont(const wxRichTextAttr
& fontSpec
, double fontScale
)
12039 wxString
facename(fontSpec
.GetFontFaceName());
12041 int fontSize
= fontSpec
.GetFontSize();
12042 if (fontScale
!= 1.0)
12043 fontSize
= (int) ((double(fontSize
) * fontScale
) + 0.5);
12046 if (fontSpec
.HasFontPixelSize() && !fontSpec
.HasFontPointSize())
12050 wxString spec
= wxString::Format(wxT("%d-%s-%d-%d-%d-%d-%s-%d"),
12051 fontSize
, units
.c_str(), fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), (int) fontSpec
.GetFontUnderlined(), (int) fontSpec
.GetFontStrikethrough(),
12052 facename
.c_str(), (int) fontSpec
.GetFontEncoding());
12054 wxRichTextFontTableHashMap::iterator entry
= m_hashMap
.find(spec
);
12055 if ( entry
== m_hashMap
.end() )
12057 if (fontSpec
.HasFontPixelSize() && !fontSpec
.HasFontPointSize())
12059 wxFont
font(wxSize(0, fontSize
), wxFONTFAMILY_DEFAULT
, fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), fontSpec
.GetFontUnderlined(), facename
);
12060 if (fontSpec
.HasFontStrikethrough() && fontSpec
.GetFontStrikethrough())
12061 font
.SetStrikethrough(true);
12062 m_hashMap
[spec
] = font
;
12067 wxFont
font(fontSize
, wxFONTFAMILY_DEFAULT
, fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), fontSpec
.GetFontUnderlined(), facename
.c_str());
12068 if (fontSpec
.HasFontStrikethrough() && fontSpec
.GetFontStrikethrough())
12069 font
.SetStrikethrough(true);
12071 m_hashMap
[spec
] = font
;
12077 return entry
->second
;
12081 IMPLEMENT_DYNAMIC_CLASS(wxRichTextFontTable
, wxObject
)
12083 wxRichTextFontTable::wxRichTextFontTable()
12085 m_refData
= new wxRichTextFontTableData
;
12089 wxRichTextFontTable::wxRichTextFontTable(const wxRichTextFontTable
& table
)
12095 wxRichTextFontTable::~wxRichTextFontTable()
12100 bool wxRichTextFontTable::operator == (const wxRichTextFontTable
& table
) const
12102 return (m_refData
== table
.m_refData
);
12105 void wxRichTextFontTable::operator= (const wxRichTextFontTable
& table
)
12108 m_fontScale
= table
.m_fontScale
;
12111 wxFont
wxRichTextFontTable::FindFont(const wxRichTextAttr
& fontSpec
)
12113 wxRichTextFontTableData
* data
= (wxRichTextFontTableData
*) m_refData
;
12115 return data
->FindFont(fontSpec
, m_fontScale
);
12120 void wxRichTextFontTable::Clear()
12122 wxRichTextFontTableData
* data
= (wxRichTextFontTableData
*) m_refData
;
12124 data
->m_hashMap
.clear();
12127 void wxRichTextFontTable::SetFontScale(double fontScale
)
12129 if (fontScale
!= m_fontScale
)
12131 m_fontScale
= fontScale
;
12136 void wxTextBoxAttr::Reset()
12139 m_floatMode
= wxTEXT_BOX_ATTR_FLOAT_NONE
;
12140 m_clearMode
= wxTEXT_BOX_ATTR_CLEAR_NONE
;
12141 m_collapseMode
= wxTEXT_BOX_ATTR_COLLAPSE_NONE
;
12142 m_verticalAlignment
= wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_NONE
;
12143 m_boxStyleName
= wxEmptyString
;
12147 m_position
.Reset();
12158 bool wxTextBoxAttr::operator== (const wxTextBoxAttr
& attr
) const
12161 m_flags
== attr
.m_flags
&&
12162 m_floatMode
== attr
.m_floatMode
&&
12163 m_clearMode
== attr
.m_clearMode
&&
12164 m_collapseMode
== attr
.m_collapseMode
&&
12165 m_verticalAlignment
== attr
.m_verticalAlignment
&&
12167 m_margins
== attr
.m_margins
&&
12168 m_padding
== attr
.m_padding
&&
12169 m_position
== attr
.m_position
&&
12171 m_size
== attr
.m_size
&&
12172 m_minSize
== attr
.m_minSize
&&
12173 m_maxSize
== attr
.m_maxSize
&&
12175 m_border
== attr
.m_border
&&
12176 m_outline
== attr
.m_outline
&&
12178 m_boxStyleName
== attr
.m_boxStyleName
12182 // Partial equality test
12183 bool wxTextBoxAttr::EqPartial(const wxTextBoxAttr
& attr
, bool weakTest
) const
12186 ((!HasFloatMode() && attr
.HasFloatMode()) ||
12187 (!HasClearMode() && attr
.HasClearMode()) ||
12188 (!HasCollapseBorders() && attr
.HasCollapseBorders()) ||
12189 (!HasVerticalAlignment() && attr
.HasVerticalAlignment()) ||
12190 (!HasBoxStyleName() && attr
.HasBoxStyleName())))
12194 if (attr
.HasFloatMode() && HasFloatMode() && (GetFloatMode() != attr
.GetFloatMode()))
12197 if (attr
.HasClearMode() && HasClearMode() && (GetClearMode() != attr
.GetClearMode()))
12200 if (attr
.HasCollapseBorders() && HasCollapseBorders() && (attr
.GetCollapseBorders() != GetCollapseBorders()))
12203 if (attr
.HasVerticalAlignment() && HasVerticalAlignment() && (attr
.GetVerticalAlignment() != GetVerticalAlignment()))
12206 if (attr
.HasBoxStyleName() && HasBoxStyleName() && (attr
.GetBoxStyleName() != GetBoxStyleName()))
12211 if (!m_position
.EqPartial(attr
.m_position
, weakTest
))
12216 if (!m_size
.EqPartial(attr
.m_size
, weakTest
))
12218 if (!m_minSize
.EqPartial(attr
.m_minSize
, weakTest
))
12220 if (!m_maxSize
.EqPartial(attr
.m_maxSize
, weakTest
))
12225 if (!m_margins
.EqPartial(attr
.m_margins
, weakTest
))
12230 if (!m_padding
.EqPartial(attr
.m_padding
, weakTest
))
12235 if (!GetBorder().EqPartial(attr
.GetBorder(), weakTest
))
12240 if (!GetOutline().EqPartial(attr
.GetOutline(), weakTest
))
12246 // Merges the given attributes. If compareWith
12247 // is non-NULL, then it will be used to mask out those attributes that are the same in style
12248 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
12249 bool wxTextBoxAttr::Apply(const wxTextBoxAttr
& attr
, const wxTextBoxAttr
* compareWith
)
12251 if (attr
.HasFloatMode())
12253 if (!(compareWith
&& compareWith
->HasFloatMode() && compareWith
->GetFloatMode() == attr
.GetFloatMode()))
12254 SetFloatMode(attr
.GetFloatMode());
12257 if (attr
.HasClearMode())
12259 if (!(compareWith
&& compareWith
->HasClearMode() && compareWith
->GetClearMode() == attr
.GetClearMode()))
12260 SetClearMode(attr
.GetClearMode());
12263 if (attr
.HasCollapseBorders())
12265 if (!(compareWith
&& compareWith
->HasCollapseBorders() && compareWith
->GetCollapseBorders() == attr
.GetCollapseBorders()))
12266 SetCollapseBorders(attr
.GetCollapseBorders());
12269 if (attr
.HasVerticalAlignment())
12271 if (!(compareWith
&& compareWith
->HasVerticalAlignment() && compareWith
->GetVerticalAlignment() == attr
.GetVerticalAlignment()))
12272 SetVerticalAlignment(attr
.GetVerticalAlignment());
12275 if (attr
.HasBoxStyleName())
12277 if (!(compareWith
&& compareWith
->HasBoxStyleName() && compareWith
->GetBoxStyleName() == attr
.GetBoxStyleName()))
12278 SetBoxStyleName(attr
.GetBoxStyleName());
12281 m_margins
.Apply(attr
.m_margins
, compareWith
? (& attr
.m_margins
) : (const wxTextAttrDimensions
*) NULL
);
12282 m_padding
.Apply(attr
.m_padding
, compareWith
? (& attr
.m_padding
) : (const wxTextAttrDimensions
*) NULL
);
12283 m_position
.Apply(attr
.m_position
, compareWith
? (& attr
.m_position
) : (const wxTextAttrDimensions
*) NULL
);
12285 m_size
.Apply(attr
.m_size
, compareWith
? (& attr
.m_size
) : (const wxTextAttrSize
*) NULL
);
12286 m_minSize
.Apply(attr
.m_minSize
, compareWith
? (& attr
.m_minSize
) : (const wxTextAttrSize
*) NULL
);
12287 m_maxSize
.Apply(attr
.m_maxSize
, compareWith
? (& attr
.m_maxSize
) : (const wxTextAttrSize
*) NULL
);
12289 m_border
.Apply(attr
.m_border
, compareWith
? (& attr
.m_border
) : (const wxTextAttrBorders
*) NULL
);
12290 m_outline
.Apply(attr
.m_outline
, compareWith
? (& attr
.m_outline
) : (const wxTextAttrBorders
*) NULL
);
12295 // Remove specified attributes from this object
12296 bool wxTextBoxAttr::RemoveStyle(const wxTextBoxAttr
& attr
)
12298 if (attr
.HasFloatMode())
12299 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT
);
12301 if (attr
.HasClearMode())
12302 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR
);
12304 if (attr
.HasCollapseBorders())
12305 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
12307 if (attr
.HasVerticalAlignment())
12308 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
12310 if (attr
.HasBoxStyleName())
12312 SetBoxStyleName(wxEmptyString
);
12313 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
12316 m_margins
.RemoveStyle(attr
.m_margins
);
12317 m_padding
.RemoveStyle(attr
.m_padding
);
12318 m_position
.RemoveStyle(attr
.m_position
);
12320 m_size
.RemoveStyle(attr
.m_size
);
12321 m_minSize
.RemoveStyle(attr
.m_minSize
);
12322 m_maxSize
.RemoveStyle(attr
.m_maxSize
);
12324 m_border
.RemoveStyle(attr
.m_border
);
12325 m_outline
.RemoveStyle(attr
.m_outline
);
12330 // Collects the attributes that are common to a range of content, building up a note of
12331 // which attributes are absent in some objects and which clash in some objects.
12332 void wxTextBoxAttr::CollectCommonAttributes(const wxTextBoxAttr
& attr
, wxTextBoxAttr
& clashingAttr
, wxTextBoxAttr
& absentAttr
)
12334 if (attr
.HasFloatMode())
12336 if (!clashingAttr
.HasFloatMode() && !absentAttr
.HasFloatMode())
12338 if (HasFloatMode())
12340 if (GetFloatMode() != attr
.GetFloatMode())
12342 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_FLOAT
);
12343 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT
);
12347 SetFloatMode(attr
.GetFloatMode());
12351 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_FLOAT
);
12353 if (attr
.HasClearMode())
12355 if (!clashingAttr
.HasClearMode() && !absentAttr
.HasClearMode())
12357 if (HasClearMode())
12359 if (GetClearMode() != attr
.GetClearMode())
12361 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_CLEAR
);
12362 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR
);
12366 SetClearMode(attr
.GetClearMode());
12370 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_CLEAR
);
12372 if (attr
.HasCollapseBorders())
12374 if (!clashingAttr
.HasCollapseBorders() && !absentAttr
.HasCollapseBorders())
12376 if (HasCollapseBorders())
12378 if (GetCollapseBorders() != attr
.GetCollapseBorders())
12380 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
12381 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
12385 SetCollapseBorders(attr
.GetCollapseBorders());
12389 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
12391 if (attr
.HasVerticalAlignment())
12393 if (!clashingAttr
.HasVerticalAlignment() && !absentAttr
.HasVerticalAlignment())
12395 if (HasVerticalAlignment())
12397 if (GetVerticalAlignment() != attr
.GetVerticalAlignment())
12399 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
12400 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
12404 SetVerticalAlignment(attr
.GetVerticalAlignment());
12408 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
12410 if (attr
.HasBoxStyleName())
12412 if (!clashingAttr
.HasBoxStyleName() && !absentAttr
.HasBoxStyleName())
12414 if (HasBoxStyleName())
12416 if (GetBoxStyleName() != attr
.GetBoxStyleName())
12418 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
12419 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
12423 SetBoxStyleName(attr
.GetBoxStyleName());
12427 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
12429 m_margins
.CollectCommonAttributes(attr
.m_margins
, clashingAttr
.m_margins
, absentAttr
.m_margins
);
12430 m_padding
.CollectCommonAttributes(attr
.m_padding
, clashingAttr
.m_padding
, absentAttr
.m_padding
);
12431 m_position
.CollectCommonAttributes(attr
.m_position
, clashingAttr
.m_position
, absentAttr
.m_position
);
12433 m_size
.CollectCommonAttributes(attr
.m_size
, clashingAttr
.m_size
, absentAttr
.m_size
);
12434 m_minSize
.CollectCommonAttributes(attr
.m_minSize
, clashingAttr
.m_minSize
, absentAttr
.m_minSize
);
12435 m_maxSize
.CollectCommonAttributes(attr
.m_maxSize
, clashingAttr
.m_maxSize
, absentAttr
.m_maxSize
);
12437 m_border
.CollectCommonAttributes(attr
.m_border
, clashingAttr
.m_border
, absentAttr
.m_border
);
12438 m_outline
.CollectCommonAttributes(attr
.m_outline
, clashingAttr
.m_outline
, absentAttr
.m_outline
);
12441 bool wxTextBoxAttr::IsDefault() const
12443 return GetFlags() == 0 && !m_border
.IsValid() && !m_outline
.IsValid() &&
12444 !m_size
.IsValid() && !m_minSize
.IsValid() && !m_maxSize
.IsValid() &&
12445 !m_position
.IsValid() && !m_padding
.IsValid() && !m_margins
.IsValid();
12450 void wxRichTextAttr::Copy(const wxRichTextAttr
& attr
)
12452 wxTextAttr::Copy(attr
);
12454 m_textBoxAttr
= attr
.m_textBoxAttr
;
12457 bool wxRichTextAttr::operator==(const wxRichTextAttr
& attr
) const
12459 if (!(wxTextAttr::operator==(attr
)))
12462 return (m_textBoxAttr
== attr
.m_textBoxAttr
);
12465 // Partial equality test
12466 bool wxRichTextAttr::EqPartial(const wxRichTextAttr
& attr
, bool weakTest
) const
12468 if (!(wxTextAttr::EqPartial(attr
, weakTest
)))
12471 return m_textBoxAttr
.EqPartial(attr
.m_textBoxAttr
, weakTest
);
12474 // Merges the given attributes. If compareWith
12475 // is non-NULL, then it will be used to mask out those attributes that are the same in style
12476 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
12477 bool wxRichTextAttr::Apply(const wxRichTextAttr
& style
, const wxRichTextAttr
* compareWith
)
12479 wxTextAttr::Apply(style
, compareWith
);
12481 return m_textBoxAttr
.Apply(style
.m_textBoxAttr
, compareWith
? (& compareWith
->m_textBoxAttr
) : (const wxTextBoxAttr
*) NULL
);
12484 // Remove specified attributes from this object
12485 bool wxRichTextAttr::RemoveStyle(const wxRichTextAttr
& attr
)
12487 wxTextAttr::RemoveStyle(*this, attr
);
12489 return m_textBoxAttr
.RemoveStyle(attr
.m_textBoxAttr
);
12492 // Collects the attributes that are common to a range of content, building up a note of
12493 // which attributes are absent in some objects and which clash in some objects.
12494 void wxRichTextAttr::CollectCommonAttributes(const wxRichTextAttr
& attr
, wxRichTextAttr
& clashingAttr
, wxRichTextAttr
& absentAttr
)
12496 wxTextAttrCollectCommonAttributes(*this, attr
, clashingAttr
, absentAttr
);
12498 m_textBoxAttr
.CollectCommonAttributes(attr
.m_textBoxAttr
, clashingAttr
.m_textBoxAttr
, absentAttr
.m_textBoxAttr
);
12501 // Partial equality test
12502 bool wxTextAttrBorder::EqPartial(const wxTextAttrBorder
& border
, bool weakTest
) const
12505 ((!HasStyle() && border
.HasStyle()) ||
12506 (!HasColour() && border
.HasColour()) ||
12507 (!HasWidth() && border
.HasWidth())))
12512 if (border
.HasStyle() && HasStyle() && (border
.GetStyle() != GetStyle()))
12515 if (border
.HasColour() && HasColour() && (border
.GetColourLong() != GetColourLong()))
12518 if (border
.HasWidth() && HasWidth() && !(border
.GetWidth() == GetWidth()))
12524 // Apply border to 'this', but not if the same as compareWith
12525 bool wxTextAttrBorder::Apply(const wxTextAttrBorder
& border
, const wxTextAttrBorder
* compareWith
)
12527 if (border
.HasStyle())
12529 if (!(compareWith
&& (border
.GetStyle() == compareWith
->GetStyle())))
12530 SetStyle(border
.GetStyle());
12532 if (border
.HasColour())
12534 if (!(compareWith
&& (border
.GetColourLong() == compareWith
->GetColourLong())))
12535 SetColour(border
.GetColourLong());
12537 if (border
.HasWidth())
12539 if (!(compareWith
&& (border
.GetWidth() == compareWith
->GetWidth())))
12540 SetWidth(border
.GetWidth());
12546 // Remove specified attributes from this object
12547 bool wxTextAttrBorder::RemoveStyle(const wxTextAttrBorder
& attr
)
12549 if (attr
.HasStyle() && HasStyle())
12550 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_STYLE
);
12551 if (attr
.HasColour() && HasColour())
12552 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_COLOUR
);
12553 if (attr
.HasWidth() && HasWidth())
12554 m_borderWidth
.Reset();
12559 // Collects the attributes that are common to a range of content, building up a note of
12560 // which attributes are absent in some objects and which clash in some objects.
12561 void wxTextAttrBorder::CollectCommonAttributes(const wxTextAttrBorder
& attr
, wxTextAttrBorder
& clashingAttr
, wxTextAttrBorder
& absentAttr
)
12563 if (attr
.HasStyle())
12565 if (!clashingAttr
.HasStyle() && !absentAttr
.HasStyle())
12569 if (GetStyle() != attr
.GetStyle())
12571 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
12572 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
12576 SetStyle(attr
.GetStyle());
12580 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
12582 if (attr
.HasColour())
12584 if (!clashingAttr
.HasColour() && !absentAttr
.HasColour())
12588 if (GetColour() != attr
.GetColour())
12590 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
12591 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
12595 SetColour(attr
.GetColourLong());
12599 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
12601 m_borderWidth
.CollectCommonAttributes(attr
.m_borderWidth
, clashingAttr
.m_borderWidth
, absentAttr
.m_borderWidth
);
12604 // Partial equality test
12605 bool wxTextAttrBorders::EqPartial(const wxTextAttrBorders
& borders
, bool weakTest
) const
12607 return m_left
.EqPartial(borders
.m_left
, weakTest
) && m_right
.EqPartial(borders
.m_right
, weakTest
) &&
12608 m_top
.EqPartial(borders
.m_top
, weakTest
) && m_bottom
.EqPartial(borders
.m_bottom
, weakTest
);
12611 // Apply border to 'this', but not if the same as compareWith
12612 bool wxTextAttrBorders::Apply(const wxTextAttrBorders
& borders
, const wxTextAttrBorders
* compareWith
)
12614 m_left
.Apply(borders
.m_left
, compareWith
? (& compareWith
->m_left
) : (const wxTextAttrBorder
*) NULL
);
12615 m_right
.Apply(borders
.m_right
, compareWith
? (& compareWith
->m_right
) : (const wxTextAttrBorder
*) NULL
);
12616 m_top
.Apply(borders
.m_top
, compareWith
? (& compareWith
->m_top
) : (const wxTextAttrBorder
*) NULL
);
12617 m_bottom
.Apply(borders
.m_bottom
, compareWith
? (& compareWith
->m_bottom
) : (const wxTextAttrBorder
*) NULL
);
12621 // Remove specified attributes from this object
12622 bool wxTextAttrBorders::RemoveStyle(const wxTextAttrBorders
& attr
)
12624 m_left
.RemoveStyle(attr
.m_left
);
12625 m_right
.RemoveStyle(attr
.m_right
);
12626 m_top
.RemoveStyle(attr
.m_top
);
12627 m_bottom
.RemoveStyle(attr
.m_bottom
);
12631 // Collects the attributes that are common to a range of content, building up a note of
12632 // which attributes are absent in some objects and which clash in some objects.
12633 void wxTextAttrBorders::CollectCommonAttributes(const wxTextAttrBorders
& attr
, wxTextAttrBorders
& clashingAttr
, wxTextAttrBorders
& absentAttr
)
12635 m_left
.CollectCommonAttributes(attr
.m_left
, clashingAttr
.m_left
, absentAttr
.m_left
);
12636 m_right
.CollectCommonAttributes(attr
.m_right
, clashingAttr
.m_right
, absentAttr
.m_right
);
12637 m_top
.CollectCommonAttributes(attr
.m_top
, clashingAttr
.m_top
, absentAttr
.m_top
);
12638 m_bottom
.CollectCommonAttributes(attr
.m_bottom
, clashingAttr
.m_bottom
, absentAttr
.m_bottom
);
12641 // Set style of all borders
12642 void wxTextAttrBorders::SetStyle(int style
)
12644 m_left
.SetStyle(style
);
12645 m_right
.SetStyle(style
);
12646 m_top
.SetStyle(style
);
12647 m_bottom
.SetStyle(style
);
12650 // Set colour of all borders
12651 void wxTextAttrBorders::SetColour(unsigned long colour
)
12653 m_left
.SetColour(colour
);
12654 m_right
.SetColour(colour
);
12655 m_top
.SetColour(colour
);
12656 m_bottom
.SetColour(colour
);
12659 void wxTextAttrBorders::SetColour(const wxColour
& colour
)
12661 m_left
.SetColour(colour
);
12662 m_right
.SetColour(colour
);
12663 m_top
.SetColour(colour
);
12664 m_bottom
.SetColour(colour
);
12667 // Set width of all borders
12668 void wxTextAttrBorders::SetWidth(const wxTextAttrDimension
& width
)
12670 m_left
.SetWidth(width
);
12671 m_right
.SetWidth(width
);
12672 m_top
.SetWidth(width
);
12673 m_bottom
.SetWidth(width
);
12676 // Partial equality test
12677 bool wxTextAttrDimension::EqPartial(const wxTextAttrDimension
& dim
, bool weakTest
) const
12679 if (!weakTest
&& !IsValid() && dim
.IsValid())
12682 if (dim
.IsValid() && IsValid() && !((*this) == dim
))
12688 bool wxTextAttrDimension::Apply(const wxTextAttrDimension
& dim
, const wxTextAttrDimension
* compareWith
)
12692 if (!(compareWith
&& dim
== (*compareWith
)))
12699 // Collects the attributes that are common to a range of content, building up a note of
12700 // which attributes are absent in some objects and which clash in some objects.
12701 void wxTextAttrDimension::CollectCommonAttributes(const wxTextAttrDimension
& attr
, wxTextAttrDimension
& clashingAttr
, wxTextAttrDimension
& absentAttr
)
12703 if (attr
.IsValid())
12705 if (!clashingAttr
.IsValid() && !absentAttr
.IsValid())
12709 if (!((*this) == attr
))
12711 clashingAttr
.SetValid(true);
12720 absentAttr
.SetValid(true);
12723 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(wxDC
& dc
, double scale
, const wxSize
& parentSize
)
12725 m_ppi
= dc
.GetPPI().x
; m_scale
= scale
; m_parentSize
= parentSize
;
12728 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(int ppi
, double scale
, const wxSize
& parentSize
)
12730 m_ppi
= ppi
; m_scale
= scale
; m_parentSize
= parentSize
;
12733 int wxTextAttrDimensionConverter::ConvertTenthsMMToPixels(int units
) const
12735 return wxRichTextObject::ConvertTenthsMMToPixels(m_ppi
, units
, m_scale
);
12738 int wxTextAttrDimensionConverter::ConvertPixelsToTenthsMM(int pixels
) const
12740 return wxRichTextObject::ConvertPixelsToTenthsMM(m_ppi
, pixels
, m_scale
);
12743 int wxTextAttrDimensionConverter::GetPixels(const wxTextAttrDimension
& dim
, int direction
) const
12745 if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
12746 return ConvertTenthsMMToPixels(dim
.GetValue());
12747 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
12748 return dim
.GetValue();
12749 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
12751 wxASSERT(m_parentSize
!= wxDefaultSize
);
12752 if (direction
== wxHORIZONTAL
)
12753 return (int) (double(m_parentSize
.x
) * double(dim
.GetValue()) / 100.0);
12755 return (int) (double(m_parentSize
.y
) * double(dim
.GetValue()) / 100.0);
12764 int wxTextAttrDimensionConverter::GetTenthsMM(const wxTextAttrDimension
& dim
) const
12766 if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
12767 return dim
.GetValue();
12768 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
12769 return ConvertPixelsToTenthsMM(dim
.GetValue());
12777 // Partial equality test
12778 bool wxTextAttrDimensions::EqPartial(const wxTextAttrDimensions
& dims
, bool weakTest
) const
12780 if (!m_left
.EqPartial(dims
.m_left
, weakTest
))
12783 if (!m_right
.EqPartial(dims
.m_right
, weakTest
))
12786 if (!m_top
.EqPartial(dims
.m_top
, weakTest
))
12789 if (!m_bottom
.EqPartial(dims
.m_bottom
, weakTest
))
12795 // Apply border to 'this', but not if the same as compareWith
12796 bool wxTextAttrDimensions::Apply(const wxTextAttrDimensions
& dims
, const wxTextAttrDimensions
* compareWith
)
12798 m_left
.Apply(dims
.m_left
, compareWith
? (& compareWith
->m_left
) : (const wxTextAttrDimension
*) NULL
);
12799 m_right
.Apply(dims
.m_right
, compareWith
? (& compareWith
->m_right
): (const wxTextAttrDimension
*) NULL
);
12800 m_top
.Apply(dims
.m_top
, compareWith
? (& compareWith
->m_top
): (const wxTextAttrDimension
*) NULL
);
12801 m_bottom
.Apply(dims
.m_bottom
, compareWith
? (& compareWith
->m_bottom
): (const wxTextAttrDimension
*) NULL
);
12806 // Remove specified attributes from this object
12807 bool wxTextAttrDimensions::RemoveStyle(const wxTextAttrDimensions
& attr
)
12809 if (attr
.m_left
.IsValid())
12811 if (attr
.m_right
.IsValid())
12813 if (attr
.m_top
.IsValid())
12815 if (attr
.m_bottom
.IsValid())
12821 // Collects the attributes that are common to a range of content, building up a note of
12822 // which attributes are absent in some objects and which clash in some objects.
12823 void wxTextAttrDimensions::CollectCommonAttributes(const wxTextAttrDimensions
& attr
, wxTextAttrDimensions
& clashingAttr
, wxTextAttrDimensions
& absentAttr
)
12825 m_left
.CollectCommonAttributes(attr
.m_left
, clashingAttr
.m_left
, absentAttr
.m_left
);
12826 m_right
.CollectCommonAttributes(attr
.m_right
, clashingAttr
.m_right
, absentAttr
.m_right
);
12827 m_top
.CollectCommonAttributes(attr
.m_top
, clashingAttr
.m_top
, absentAttr
.m_top
);
12828 m_bottom
.CollectCommonAttributes(attr
.m_bottom
, clashingAttr
.m_bottom
, absentAttr
.m_bottom
);
12831 // Partial equality test
12832 bool wxTextAttrSize::EqPartial(const wxTextAttrSize
& size
, bool weakTest
) const
12834 if (!m_width
.EqPartial(size
.m_width
, weakTest
))
12837 if (!m_height
.EqPartial(size
.m_height
, weakTest
))
12843 // Apply border to 'this', but not if the same as compareWith
12844 bool wxTextAttrSize::Apply(const wxTextAttrSize
& size
, const wxTextAttrSize
* compareWith
)
12846 m_width
.Apply(size
.m_width
, compareWith
? (& compareWith
->m_width
) : (const wxTextAttrDimension
*) NULL
);
12847 m_height
.Apply(size
.m_height
, compareWith
? (& compareWith
->m_height
): (const wxTextAttrDimension
*) NULL
);
12852 // Remove specified attributes from this object
12853 bool wxTextAttrSize::RemoveStyle(const wxTextAttrSize
& attr
)
12855 if (attr
.m_width
.IsValid())
12857 if (attr
.m_height
.IsValid())
12863 // Collects the attributes that are common to a range of content, building up a note of
12864 // which attributes are absent in some objects and which clash in some objects.
12865 void wxTextAttrSize::CollectCommonAttributes(const wxTextAttrSize
& attr
, wxTextAttrSize
& clashingAttr
, wxTextAttrSize
& absentAttr
)
12867 m_width
.CollectCommonAttributes(attr
.m_width
, clashingAttr
.m_width
, absentAttr
.m_width
);
12868 m_height
.CollectCommonAttributes(attr
.m_height
, clashingAttr
.m_height
, absentAttr
.m_height
);
12871 // Collects the attributes that are common to a range of content, building up a note of
12872 // which attributes are absent in some objects and which clash in some objects.
12873 void wxTextAttrCollectCommonAttributes(wxTextAttr
& currentStyle
, const wxTextAttr
& attr
, wxTextAttr
& clashingAttr
, wxTextAttr
& absentAttr
)
12875 absentAttr
.SetFlags(absentAttr
.GetFlags() | (~attr
.GetFlags() & wxTEXT_ATTR_ALL
));
12876 absentAttr
.SetTextEffectFlags(absentAttr
.GetTextEffectFlags() | (~attr
.GetTextEffectFlags() & 0xFFFF));
12878 long forbiddenFlags
= clashingAttr
.GetFlags()|absentAttr
.GetFlags();
12880 // If different font size units are being used, this is a clash.
12881 if (((attr
.GetFlags() & wxTEXT_ATTR_FONT_SIZE
) | (currentStyle
.GetFlags() & wxTEXT_ATTR_FONT_SIZE
)) == wxTEXT_ATTR_FONT_SIZE
)
12883 currentStyle
.SetFontSize(0);
12884 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_SIZE
);
12885 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_SIZE
);
12889 if (attr
.HasFontPointSize() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_POINT_SIZE
))
12891 if (currentStyle
.HasFontPointSize())
12893 if (currentStyle
.GetFontSize() != attr
.GetFontSize())
12895 // Clash of attr - mark as such
12896 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE
);
12897 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE
);
12901 currentStyle
.SetFontSize(attr
.GetFontSize());
12903 else if (!attr
.HasFontPointSize() && currentStyle
.HasFontPointSize())
12905 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE
);
12906 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE
);
12909 if (attr
.HasFontPixelSize() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_PIXEL_SIZE
))
12911 if (currentStyle
.HasFontPixelSize())
12913 if (currentStyle
.GetFontSize() != attr
.GetFontSize())
12915 // Clash of attr - mark as such
12916 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE
);
12917 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE
);
12921 currentStyle
.SetFontPixelSize(attr
.GetFontSize());
12923 else if (!attr
.HasFontPixelSize() && currentStyle
.HasFontPixelSize())
12925 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE
);
12926 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE
);
12930 if (attr
.HasFontItalic() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_ITALIC
))
12932 if (currentStyle
.HasFontItalic())
12934 if (currentStyle
.GetFontStyle() != attr
.GetFontStyle())
12936 // Clash of attr - mark as such
12937 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_ITALIC
);
12938 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC
);
12942 currentStyle
.SetFontStyle(attr
.GetFontStyle());
12944 else if (!attr
.HasFontItalic() && currentStyle
.HasFontItalic())
12946 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_ITALIC
);
12947 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC
);
12950 if (attr
.HasFontFamily() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_FAMILY
))
12952 if (currentStyle
.HasFontFamily())
12954 if (currentStyle
.GetFontFamily() != attr
.GetFontFamily())
12956 // Clash of attr - mark as such
12957 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FAMILY
);
12958 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY
);
12962 currentStyle
.SetFontFamily(attr
.GetFontFamily());
12964 else if (!attr
.HasFontFamily() && currentStyle
.HasFontFamily())
12966 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FAMILY
);
12967 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY
);
12970 if (attr
.HasFontWeight() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_WEIGHT
))
12972 if (currentStyle
.HasFontWeight())
12974 if (currentStyle
.GetFontWeight() != attr
.GetFontWeight())
12976 // Clash of attr - mark as such
12977 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_WEIGHT
);
12978 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT
);
12982 currentStyle
.SetFontWeight(attr
.GetFontWeight());
12984 else if (!attr
.HasFontWeight() && currentStyle
.HasFontWeight())
12986 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_WEIGHT
);
12987 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT
);
12990 if (attr
.HasFontFaceName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_FACE
))
12992 if (currentStyle
.HasFontFaceName())
12994 wxString
faceName1(currentStyle
.GetFontFaceName());
12995 wxString
faceName2(attr
.GetFontFaceName());
12997 if (faceName1
!= faceName2
)
12999 // Clash of attr - mark as such
13000 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FACE
);
13001 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FACE
);
13005 currentStyle
.SetFontFaceName(attr
.GetFontFaceName());
13007 else if (!attr
.HasFontFaceName() && currentStyle
.HasFontFaceName())
13009 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FACE
);
13010 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FACE
);
13013 if (attr
.HasFontUnderlined() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_UNDERLINE
))
13015 if (currentStyle
.HasFontUnderlined())
13017 if (currentStyle
.GetFontUnderlined() != attr
.GetFontUnderlined())
13019 // Clash of attr - mark as such
13020 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
13021 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
13025 currentStyle
.SetFontUnderlined(attr
.GetFontUnderlined());
13027 else if (!attr
.HasFontUnderlined() && currentStyle
.HasFontUnderlined())
13029 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
13030 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
13033 if (attr
.HasFontStrikethrough() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_STRIKETHROUGH
))
13035 if (currentStyle
.HasFontStrikethrough())
13037 if (currentStyle
.GetFontStrikethrough() != attr
.GetFontStrikethrough())
13039 // Clash of attr - mark as such
13040 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH
);
13041 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH
);
13045 currentStyle
.SetFontStrikethrough(attr
.GetFontStrikethrough());
13047 else if (!attr
.HasFontStrikethrough() && currentStyle
.HasFontStrikethrough())
13049 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH
);
13050 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH
);
13053 if (attr
.HasTextColour() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_TEXT_COLOUR
))
13055 if (currentStyle
.HasTextColour())
13057 if (currentStyle
.GetTextColour() != attr
.GetTextColour())
13059 // Clash of attr - mark as such
13060 clashingAttr
.AddFlag(wxTEXT_ATTR_TEXT_COLOUR
);
13061 currentStyle
.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR
);
13065 currentStyle
.SetTextColour(attr
.GetTextColour());
13067 else if (!attr
.HasTextColour() && currentStyle
.HasTextColour())
13069 clashingAttr
.AddFlag(wxTEXT_ATTR_TEXT_COLOUR
);
13070 currentStyle
.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR
);
13073 if (attr
.HasBackgroundColour() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BACKGROUND_COLOUR
))
13075 if (currentStyle
.HasBackgroundColour())
13077 if (currentStyle
.GetBackgroundColour() != attr
.GetBackgroundColour())
13079 // Clash of attr - mark as such
13080 clashingAttr
.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
13081 currentStyle
.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
13085 currentStyle
.SetBackgroundColour(attr
.GetBackgroundColour());
13087 else if (!attr
.HasBackgroundColour() && currentStyle
.HasBackgroundColour())
13089 clashingAttr
.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
13090 currentStyle
.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
13093 if (attr
.HasAlignment() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_ALIGNMENT
))
13095 if (currentStyle
.HasAlignment())
13097 if (currentStyle
.GetAlignment() != attr
.GetAlignment())
13099 // Clash of attr - mark as such
13100 clashingAttr
.AddFlag(wxTEXT_ATTR_ALIGNMENT
);
13101 currentStyle
.RemoveFlag(wxTEXT_ATTR_ALIGNMENT
);
13105 currentStyle
.SetAlignment(attr
.GetAlignment());
13107 else if (!attr
.HasAlignment() && currentStyle
.HasAlignment())
13109 clashingAttr
.AddFlag(wxTEXT_ATTR_ALIGNMENT
);
13110 currentStyle
.RemoveFlag(wxTEXT_ATTR_ALIGNMENT
);
13113 if (attr
.HasTabs() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_TABS
))
13115 if (currentStyle
.HasTabs())
13117 if (!wxRichTextTabsEq(currentStyle
.GetTabs(), attr
.GetTabs()))
13119 // Clash of attr - mark as such
13120 clashingAttr
.AddFlag(wxTEXT_ATTR_TABS
);
13121 currentStyle
.RemoveFlag(wxTEXT_ATTR_TABS
);
13125 currentStyle
.SetTabs(attr
.GetTabs());
13127 else if (!attr
.HasTabs() && currentStyle
.HasTabs())
13129 clashingAttr
.AddFlag(wxTEXT_ATTR_TABS
);
13130 currentStyle
.RemoveFlag(wxTEXT_ATTR_TABS
);
13133 if (attr
.HasLeftIndent() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LEFT_INDENT
))
13135 if (currentStyle
.HasLeftIndent())
13137 if (currentStyle
.GetLeftIndent() != attr
.GetLeftIndent() || currentStyle
.GetLeftSubIndent() != attr
.GetLeftSubIndent())
13139 // Clash of attr - mark as such
13140 clashingAttr
.AddFlag(wxTEXT_ATTR_LEFT_INDENT
);
13141 currentStyle
.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT
);
13145 currentStyle
.SetLeftIndent(attr
.GetLeftIndent(), attr
.GetLeftSubIndent());
13147 else if (!attr
.HasLeftIndent() && currentStyle
.HasLeftIndent())
13149 clashingAttr
.AddFlag(wxTEXT_ATTR_LEFT_INDENT
);
13150 currentStyle
.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT
);
13153 if (attr
.HasRightIndent() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_RIGHT_INDENT
))
13155 if (currentStyle
.HasRightIndent())
13157 if (currentStyle
.GetRightIndent() != attr
.GetRightIndent())
13159 // Clash of attr - mark as such
13160 clashingAttr
.AddFlag(wxTEXT_ATTR_RIGHT_INDENT
);
13161 currentStyle
.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT
);
13165 currentStyle
.SetRightIndent(attr
.GetRightIndent());
13167 else if (!attr
.HasRightIndent() && currentStyle
.HasRightIndent())
13169 clashingAttr
.AddFlag(wxTEXT_ATTR_RIGHT_INDENT
);
13170 currentStyle
.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT
);
13173 if (attr
.HasParagraphSpacingAfter() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARA_SPACING_AFTER
))
13175 if (currentStyle
.HasParagraphSpacingAfter())
13177 if (currentStyle
.GetParagraphSpacingAfter() != attr
.GetParagraphSpacingAfter())
13179 // Clash of attr - mark as such
13180 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
13181 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
13185 currentStyle
.SetParagraphSpacingAfter(attr
.GetParagraphSpacingAfter());
13187 else if (!attr
.HasParagraphSpacingAfter() && currentStyle
.HasParagraphSpacingAfter())
13189 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
13190 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
13193 if (attr
.HasParagraphSpacingBefore() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARA_SPACING_BEFORE
))
13195 if (currentStyle
.HasParagraphSpacingBefore())
13197 if (currentStyle
.GetParagraphSpacingBefore() != attr
.GetParagraphSpacingBefore())
13199 // Clash of attr - mark as such
13200 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
13201 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
13205 currentStyle
.SetParagraphSpacingBefore(attr
.GetParagraphSpacingBefore());
13207 else if (!attr
.HasParagraphSpacingBefore() && currentStyle
.HasParagraphSpacingBefore())
13209 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
13210 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
13213 if (attr
.HasLineSpacing() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LINE_SPACING
))
13215 if (currentStyle
.HasLineSpacing())
13217 if (currentStyle
.GetLineSpacing() != attr
.GetLineSpacing())
13219 // Clash of attr - mark as such
13220 clashingAttr
.AddFlag(wxTEXT_ATTR_LINE_SPACING
);
13221 currentStyle
.RemoveFlag(wxTEXT_ATTR_LINE_SPACING
);
13225 currentStyle
.SetLineSpacing(attr
.GetLineSpacing());
13227 else if (!attr
.HasLineSpacing() && currentStyle
.HasLineSpacing())
13229 clashingAttr
.AddFlag(wxTEXT_ATTR_LINE_SPACING
);
13230 currentStyle
.RemoveFlag(wxTEXT_ATTR_LINE_SPACING
);
13233 if (attr
.HasCharacterStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_CHARACTER_STYLE_NAME
))
13235 if (currentStyle
.HasCharacterStyleName())
13237 if (currentStyle
.GetCharacterStyleName() != attr
.GetCharacterStyleName())
13239 // Clash of attr - mark as such
13240 clashingAttr
.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
13241 currentStyle
.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
13245 currentStyle
.SetCharacterStyleName(attr
.GetCharacterStyleName());
13247 else if (!attr
.HasCharacterStyleName() && currentStyle
.HasCharacterStyleName())
13249 clashingAttr
.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
13250 currentStyle
.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
13253 if (attr
.HasParagraphStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
))
13255 if (currentStyle
.HasParagraphStyleName())
13257 if (currentStyle
.GetParagraphStyleName() != attr
.GetParagraphStyleName())
13259 // Clash of attr - mark as such
13260 clashingAttr
.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
13261 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
13265 currentStyle
.SetParagraphStyleName(attr
.GetParagraphStyleName());
13267 else if (!attr
.HasParagraphStyleName() && currentStyle
.HasParagraphStyleName())
13269 clashingAttr
.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
13270 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
13273 if (attr
.HasListStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LIST_STYLE_NAME
))
13275 if (currentStyle
.HasListStyleName())
13277 if (currentStyle
.GetListStyleName() != attr
.GetListStyleName())
13279 // Clash of attr - mark as such
13280 clashingAttr
.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
13281 currentStyle
.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
13285 currentStyle
.SetListStyleName(attr
.GetListStyleName());
13287 else if (!attr
.HasListStyleName() && currentStyle
.HasListStyleName())
13289 clashingAttr
.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
13290 currentStyle
.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
13293 if (attr
.HasBulletStyle() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_STYLE
))
13295 if (currentStyle
.HasBulletStyle())
13297 if (currentStyle
.GetBulletStyle() != attr
.GetBulletStyle())
13299 // Clash of attr - mark as such
13300 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_STYLE
);
13301 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE
);
13305 currentStyle
.SetBulletStyle(attr
.GetBulletStyle());
13307 else if (!attr
.HasBulletStyle() && currentStyle
.HasBulletStyle())
13309 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_STYLE
);
13310 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE
);
13313 if (attr
.HasBulletNumber() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_NUMBER
))
13315 if (currentStyle
.HasBulletNumber())
13317 if (currentStyle
.GetBulletNumber() != attr
.GetBulletNumber())
13319 // Clash of attr - mark as such
13320 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NUMBER
);
13321 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER
);
13325 currentStyle
.SetBulletNumber(attr
.GetBulletNumber());
13327 else if (!attr
.HasBulletNumber() && currentStyle
.HasBulletNumber())
13329 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NUMBER
);
13330 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER
);
13333 if (attr
.HasBulletText() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_TEXT
))
13335 if (currentStyle
.HasBulletText())
13337 if (currentStyle
.GetBulletText() != attr
.GetBulletText())
13339 // Clash of attr - mark as such
13340 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_TEXT
);
13341 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT
);
13346 currentStyle
.SetBulletText(attr
.GetBulletText());
13347 currentStyle
.SetBulletFont(attr
.GetBulletFont());
13350 else if (!attr
.HasBulletText() && currentStyle
.HasBulletText())
13352 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_TEXT
);
13353 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT
);
13356 if (attr
.HasBulletName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_NAME
))
13358 if (currentStyle
.HasBulletName())
13360 if (currentStyle
.GetBulletName() != attr
.GetBulletName())
13362 // Clash of attr - mark as such
13363 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NAME
);
13364 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NAME
);
13369 currentStyle
.SetBulletName(attr
.GetBulletName());
13372 else if (!attr
.HasBulletName() && currentStyle
.HasBulletName())
13374 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NAME
);
13375 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NAME
);
13378 if (attr
.HasURL() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_URL
))
13380 if (currentStyle
.HasURL())
13382 if (currentStyle
.GetURL() != attr
.GetURL())
13384 // Clash of attr - mark as such
13385 clashingAttr
.AddFlag(wxTEXT_ATTR_URL
);
13386 currentStyle
.RemoveFlag(wxTEXT_ATTR_URL
);
13391 currentStyle
.SetURL(attr
.GetURL());
13394 else if (!attr
.HasURL() && currentStyle
.HasURL())
13396 clashingAttr
.AddFlag(wxTEXT_ATTR_URL
);
13397 currentStyle
.RemoveFlag(wxTEXT_ATTR_URL
);
13400 if (attr
.HasTextEffects() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_EFFECTS
))
13402 if (currentStyle
.HasTextEffects())
13404 // We need to find the bits in the new attr that are different:
13405 // just look at those bits that are specified by the new attr.
13407 // We need to remove the bits and flags that are not common between current attr
13408 // and new attr. In so doing we need to take account of the styles absent from one or more of the
13409 // previous styles.
13411 int currentRelevantTextEffects
= currentStyle
.GetTextEffects() & attr
.GetTextEffectFlags();
13412 int newRelevantTextEffects
= attr
.GetTextEffects() & attr
.GetTextEffectFlags();
13414 if (currentRelevantTextEffects
!= newRelevantTextEffects
)
13416 // Find the text effects that were different, using XOR
13417 int differentEffects
= currentRelevantTextEffects
^ newRelevantTextEffects
;
13419 // Clash of attr - mark as such
13420 clashingAttr
.SetTextEffectFlags(clashingAttr
.GetTextEffectFlags() | differentEffects
);
13421 currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~differentEffects
);
13426 currentStyle
.SetTextEffects(attr
.GetTextEffects());
13427 currentStyle
.SetTextEffectFlags(attr
.GetTextEffectFlags());
13430 // Mask out the flags and values that cannot be common because they were absent in one or more objecrs
13431 // that we've looked at so far
13432 currentStyle
.SetTextEffects(currentStyle
.GetTextEffects() & ~absentAttr
.GetTextEffectFlags());
13433 currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~absentAttr
.GetTextEffectFlags());
13435 if (currentStyle
.GetTextEffectFlags() == 0)
13436 currentStyle
.RemoveFlag(wxTEXT_ATTR_EFFECTS
);
13438 else if (!attr
.HasTextEffects() && currentStyle
.HasTextEffects())
13440 clashingAttr
.AddFlag(wxTEXT_ATTR_EFFECTS
);
13441 currentStyle
.RemoveFlag(wxTEXT_ATTR_EFFECTS
);
13444 if (attr
.HasOutlineLevel() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_OUTLINE_LEVEL
))
13446 if (currentStyle
.HasOutlineLevel())
13448 if (currentStyle
.GetOutlineLevel() != attr
.GetOutlineLevel())
13450 // Clash of attr - mark as such
13451 clashingAttr
.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
13452 currentStyle
.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
13456 currentStyle
.SetOutlineLevel(attr
.GetOutlineLevel());
13458 else if (!attr
.HasOutlineLevel() && currentStyle
.HasOutlineLevel())
13460 clashingAttr
.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
13461 currentStyle
.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
13465 WX_DEFINE_OBJARRAY(wxRichTextVariantArray
);
13468 WX_DEFINE_OBJARRAY(wxRichTextAttrArray
);
13470 IMPLEMENT_DYNAMIC_CLASS(wxRichTextProperties
, wxObject
)
13472 bool wxRichTextProperties::operator==(const wxRichTextProperties
& props
) const
13474 if (m_properties
.GetCount() != props
.GetCount())
13478 for (i
= 0; i
< m_properties
.GetCount(); i
++)
13480 const wxVariant
& var1
= m_properties
[i
];
13481 int idx
= props
.Find(var1
.GetName());
13484 const wxVariant
& var2
= props
.m_properties
[idx
];
13485 if (!(var1
== var2
))
13492 wxArrayString
wxRichTextProperties::GetPropertyNames() const
13496 for (i
= 0; i
< m_properties
.GetCount(); i
++)
13498 arr
.Add(m_properties
[i
].GetName());
13503 int wxRichTextProperties::Find(const wxString
& name
) const
13506 for (i
= 0; i
< m_properties
.GetCount(); i
++)
13508 if (m_properties
[i
].GetName() == name
)
13514 bool wxRichTextProperties::Remove(const wxString
& name
)
13516 int idx
= Find(name
);
13519 m_properties
.RemoveAt(idx
);
13526 wxVariant
* wxRichTextProperties::FindOrCreateProperty(const wxString
& name
)
13528 int idx
= Find(name
);
13529 if (idx
== wxNOT_FOUND
)
13530 SetProperty(name
, wxString());
13532 if (idx
!= wxNOT_FOUND
)
13534 return & (*this)[idx
];
13540 const wxVariant
& wxRichTextProperties::GetProperty(const wxString
& name
) const
13542 static const wxVariant nullVariant
;
13543 int idx
= Find(name
);
13545 return m_properties
[idx
];
13547 return nullVariant
;
13550 wxString
wxRichTextProperties::GetPropertyString(const wxString
& name
) const
13552 return GetProperty(name
).GetString();
13555 long wxRichTextProperties::GetPropertyLong(const wxString
& name
) const
13557 return GetProperty(name
).GetLong();
13560 bool wxRichTextProperties::GetPropertyBool(const wxString
& name
) const
13562 return GetProperty(name
).GetBool();
13565 double wxRichTextProperties::GetPropertyDouble(const wxString
& name
) const
13567 return GetProperty(name
).GetDouble();
13570 void wxRichTextProperties::SetProperty(const wxVariant
& variant
)
13572 wxASSERT(!variant
.GetName().IsEmpty());
13574 int idx
= Find(variant
.GetName());
13577 m_properties
.Add(variant
);
13579 m_properties
[idx
] = variant
;
13582 void wxRichTextProperties::SetProperty(const wxString
& name
, const wxVariant
& variant
)
13584 int idx
= Find(name
);
13585 wxVariant
var(variant
);
13589 m_properties
.Add(var
);
13591 m_properties
[idx
] = var
;
13594 void wxRichTextProperties::SetProperty(const wxString
& name
, const wxString
& value
)
13596 SetProperty(name
, wxVariant(value
, name
));
13599 void wxRichTextProperties::SetProperty(const wxString
& name
, long value
)
13601 SetProperty(name
, wxVariant(value
, name
));
13604 void wxRichTextProperties::SetProperty(const wxString
& name
, double value
)
13606 SetProperty(name
, wxVariant(value
, name
));
13609 void wxRichTextProperties::SetProperty(const wxString
& name
, bool value
)
13611 SetProperty(name
, wxVariant(value
, name
));
13614 void wxRichTextProperties::RemoveProperties(const wxRichTextProperties
& properties
)
13617 for (i
= 0; i
< properties
.GetCount(); i
++)
13619 wxString name
= properties
.GetProperties()[i
].GetName();
13620 if (HasProperty(name
))
13625 void wxRichTextProperties::MergeProperties(const wxRichTextProperties
& properties
)
13628 for (i
= 0; i
< properties
.GetCount(); i
++)
13630 SetProperty(properties
.GetProperties()[i
]);
13634 wxRichTextObject
* wxRichTextObjectAddress::GetObject(wxRichTextParagraphLayoutBox
* topLevelContainer
) const
13636 if (m_address
.GetCount() == 0)
13637 return topLevelContainer
;
13639 wxRichTextCompositeObject
* p
= topLevelContainer
;
13641 while (p
&& i
< m_address
.GetCount())
13643 int pos
= m_address
[i
];
13644 wxASSERT(pos
>= 0 && pos
< (int) p
->GetChildren().GetCount());
13645 if (pos
< 0 || pos
>= (int) p
->GetChildren().GetCount())
13648 wxRichTextObject
* p1
= p
->GetChild(pos
);
13649 if (i
== (m_address
.GetCount()-1))
13652 p
= wxDynamicCast(p1
, wxRichTextCompositeObject
);
13658 bool wxRichTextObjectAddress::Create(wxRichTextParagraphLayoutBox
* topLevelContainer
, wxRichTextObject
* obj
)
13662 if (topLevelContainer
== obj
)
13665 wxRichTextObject
* o
= obj
;
13668 wxRichTextCompositeObject
* p
= wxDynamicCast(o
->GetParent(), wxRichTextCompositeObject
);
13672 int pos
= p
->GetChildren().IndexOf(o
);
13676 m_address
.Insert(pos
, 0);
13678 if (p
== topLevelContainer
)
13687 bool wxRichTextSelection::operator==(const wxRichTextSelection
& sel
) const
13689 if (m_container
!= sel
.m_container
)
13691 if (m_ranges
.GetCount() != sel
.m_ranges
.GetCount())
13694 for (i
= 0; i
< m_ranges
.GetCount(); i
++)
13695 if (!(m_ranges
[i
] == sel
.m_ranges
[i
]))
13700 // Get the selections appropriate to the specified object, if any; returns wxRICHTEXT_NO_SELECTION if none
13701 // or none at the level of the object's container.
13702 wxRichTextRangeArray
wxRichTextSelection::GetSelectionForObject(wxRichTextObject
* obj
) const
13706 wxRichTextParagraphLayoutBox
* container
= obj
->GetParentContainer();
13708 if (container
== m_container
)
13711 container
= obj
->GetContainer();
13714 if (container
->GetParent())
13716 // If we found that our object's container is within the range of
13717 // a selection higher up, then assume the whole original object
13718 // is also selected.
13719 wxRichTextParagraphLayoutBox
* parentContainer
= container
->GetParentContainer();
13720 if (parentContainer
== m_container
)
13722 if (WithinSelection(container
->GetRange().GetStart(), m_ranges
))
13724 wxRichTextRangeArray ranges
;
13725 ranges
.Add(obj
->GetRange());
13730 container
= parentContainer
;
13739 return wxRichTextRangeArray();
13742 // Is the given position within the selection?
13743 bool wxRichTextSelection::WithinSelection(long pos
, wxRichTextObject
* obj
) const
13749 wxRichTextRangeArray selectionRanges
= GetSelectionForObject(obj
);
13750 return WithinSelection(pos
, selectionRanges
);
13754 // Is the given position within the selection range?
13755 bool wxRichTextSelection::WithinSelection(long pos
, const wxRichTextRangeArray
& ranges
)
13758 for (i
= 0; i
< ranges
.GetCount(); i
++)
13760 const wxRichTextRange
& range
= ranges
[i
];
13761 if (pos
>= range
.GetStart() && pos
<= range
.GetEnd())
13767 // Is the given range completely within the selection range?
13768 bool wxRichTextSelection::WithinSelection(const wxRichTextRange
& range
, const wxRichTextRangeArray
& ranges
)
13771 for (i
= 0; i
< ranges
.GetCount(); i
++)
13773 const wxRichTextRange
& eachRange
= ranges
[i
];
13774 if (range
.IsWithin(eachRange
))
13780 IMPLEMENT_CLASS(wxRichTextDrawingHandler
, wxObject
)
13781 IMPLEMENT_CLASS(wxRichTextDrawingContext
, wxObject
)
13783 wxRichTextDrawingContext::wxRichTextDrawingContext(wxRichTextBuffer
* buffer
)
13787 if (m_buffer
&& m_buffer
->GetRichTextCtrl())
13788 EnableVirtualAttributes(m_buffer
->GetRichTextCtrl()->GetVirtualAttributesEnabled());
13791 bool wxRichTextDrawingContext::HasVirtualAttributes(wxRichTextObject
* obj
) const
13793 if (!GetVirtualAttributesEnabled())
13796 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
13799 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13800 if (handler
->HasVirtualAttributes(obj
))
13803 node
= node
->GetNext();
13808 wxRichTextAttr
wxRichTextDrawingContext::GetVirtualAttributes(wxRichTextObject
* obj
) const
13810 wxRichTextAttr attr
;
13811 if (!GetVirtualAttributesEnabled())
13814 // We apply all handlers, so we can may combine several different attributes
13815 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
13818 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13819 if (handler
->HasVirtualAttributes(obj
))
13821 bool success
= handler
->GetVirtualAttributes(attr
, obj
);
13823 wxUnusedVar(success
);
13826 node
= node
->GetNext();
13831 bool wxRichTextDrawingContext::ApplyVirtualAttributes(wxRichTextAttr
& attr
, wxRichTextObject
* obj
) const
13833 if (!GetVirtualAttributesEnabled())
13836 if (HasVirtualAttributes(obj
))
13838 wxRichTextAttr
a(GetVirtualAttributes(obj
));
13846 int wxRichTextDrawingContext::GetVirtualSubobjectAttributesCount(wxRichTextObject
* obj
) const
13848 if (!GetVirtualAttributesEnabled())
13851 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
13854 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13855 int count
= handler
->GetVirtualSubobjectAttributesCount(obj
);
13859 node
= node
->GetNext();
13864 int wxRichTextDrawingContext::GetVirtualSubobjectAttributes(wxRichTextObject
* obj
, wxArrayInt
& positions
, wxRichTextAttrArray
& attributes
) const
13866 if (!GetVirtualAttributesEnabled())
13869 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
13872 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13873 if (handler
->GetVirtualSubobjectAttributes(obj
, positions
, attributes
))
13874 return positions
.GetCount();
13876 node
= node
->GetNext();
13881 bool wxRichTextDrawingContext::HasVirtualText(const wxRichTextPlainText
* obj
) const
13883 if (!GetVirtualAttributesEnabled())
13886 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
13889 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13890 if (handler
->HasVirtualText(obj
))
13893 node
= node
->GetNext();
13898 bool wxRichTextDrawingContext::GetVirtualText(const wxRichTextPlainText
* obj
, wxString
& text
) const
13900 if (!GetVirtualAttributesEnabled())
13903 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
13906 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13907 if (handler
->GetVirtualText(obj
, text
))
13910 node
= node
->GetNext();
13915 /// Adds a handler to the end
13916 void wxRichTextBuffer::AddDrawingHandler(wxRichTextDrawingHandler
*handler
)
13918 sm_drawingHandlers
.Append(handler
);
13921 /// Inserts a handler at the front
13922 void wxRichTextBuffer::InsertDrawingHandler(wxRichTextDrawingHandler
*handler
)
13924 sm_drawingHandlers
.Insert( handler
);
13927 /// Removes a handler
13928 bool wxRichTextBuffer::RemoveDrawingHandler(const wxString
& name
)
13930 wxRichTextDrawingHandler
*handler
= FindDrawingHandler(name
);
13933 sm_drawingHandlers
.DeleteObject(handler
);
13941 wxRichTextDrawingHandler
* wxRichTextBuffer::FindDrawingHandler(const wxString
& name
)
13943 wxList::compatibility_iterator node
= sm_drawingHandlers
.GetFirst();
13946 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13947 if (handler
->GetName().Lower() == name
.Lower()) return handler
;
13949 node
= node
->GetNext();
13954 void wxRichTextBuffer::CleanUpDrawingHandlers()
13956 wxList::compatibility_iterator node
= sm_drawingHandlers
.GetFirst();
13959 wxRichTextDrawingHandler
* handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13960 wxList::compatibility_iterator next
= node
->GetNext();
13965 sm_drawingHandlers
.Clear();
13968 void wxRichTextBuffer::AddFieldType(wxRichTextFieldType
*fieldType
)
13970 sm_fieldTypes
[fieldType
->GetName()] = fieldType
;
13973 bool wxRichTextBuffer::RemoveFieldType(const wxString
& name
)
13975 wxRichTextFieldTypeHashMap::iterator it
= sm_fieldTypes
.find(name
);
13976 if (it
== sm_fieldTypes
.end())
13980 wxRichTextFieldType
* fieldType
= it
->second
;
13981 sm_fieldTypes
.erase(it
);
13987 wxRichTextFieldType
*wxRichTextBuffer::FindFieldType(const wxString
& name
)
13989 wxRichTextFieldTypeHashMap::iterator it
= sm_fieldTypes
.find(name
);
13990 if (it
== sm_fieldTypes
.end())
13996 void wxRichTextBuffer::CleanUpFieldTypes()
13998 wxRichTextFieldTypeHashMap::iterator it
;
13999 for( it
= sm_fieldTypes
.begin(); it
!= sm_fieldTypes
.end(); ++it
)
14001 wxRichTextFieldType
* fieldType
= it
->second
;
14005 sm_fieldTypes
.clear();