1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/richtext/richtextbuffer.cpp
3 // Purpose: Buffer for wxRichTextCtrl
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // For compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
21 #include "wx/richtext/richtextbuffer.h"
27 #include "wx/dataobj.h"
28 #include "wx/module.h"
31 #include "wx/settings.h"
32 #include "wx/filename.h"
33 #include "wx/clipbrd.h"
34 #include "wx/wfstream.h"
35 #include "wx/mstream.h"
36 #include "wx/sstream.h"
37 #include "wx/textfile.h"
38 #include "wx/hashmap.h"
39 #include "wx/dynarray.h"
41 #include "wx/richtext/richtextctrl.h"
42 #include "wx/richtext/richtextstyles.h"
43 #include "wx/richtext/richtextimagedlg.h"
44 #include "wx/richtext/richtextsizepage.h"
45 #include "wx/richtext/richtextxml.h"
47 #include "wx/listimpl.cpp"
48 #include "wx/arrimpl.cpp"
50 WX_DEFINE_LIST(wxRichTextObjectList
)
51 WX_DEFINE_LIST(wxRichTextLineList
)
53 // Switch off if the platform doesn't like it for some reason
54 #define wxRICHTEXT_USE_OPTIMIZED_DRAWING 1
56 // Use GetPartialTextExtents for platforms that support it natively
57 #define wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS 1
59 const wxChar wxRichTextLineBreakChar
= (wxChar
) 29;
61 // Helper classes for floating layout
62 struct wxRichTextFloatRectMap
64 wxRichTextFloatRectMap(int sY
, int eY
, int w
, wxRichTextObject
* obj
)
74 wxRichTextObject
* anchor
;
77 WX_DEFINE_SORTED_ARRAY(wxRichTextFloatRectMap
*, wxRichTextFloatRectMapArray
);
79 int wxRichTextFloatRectMapCmp(wxRichTextFloatRectMap
* r1
, wxRichTextFloatRectMap
* r2
)
81 return r1
->startY
- r2
->startY
;
84 class wxRichTextFloatCollector
87 wxRichTextFloatCollector(const wxRect
& availableRect
);
88 ~wxRichTextFloatCollector();
90 // Collect the floating objects info in the given paragraph
91 void CollectFloat(wxRichTextParagraph
* para
);
92 void CollectFloat(wxRichTextParagraph
* para
, wxRichTextObject
* floating
);
94 // Return the last paragraph we collected
95 wxRichTextParagraph
* LastParagraph();
97 // Given the start y position and the height of the line,
98 // find out how wide the line can be
99 wxRect
GetAvailableRect(int startY
, int endY
);
101 // Given a floating box, find its fit position
102 int GetFitPosition(int direction
, int start
, int height
) const;
103 int GetFitPosition(const wxRichTextFloatRectMapArray
& array
, int start
, int height
) const;
105 // Find the last y position
106 int GetLastRectBottom();
108 // Draw the floats inside a rect
109 void Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
);
111 // HitTest the floats
112 int HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, int flags
);
114 // Get floating object count
115 int GetFloatingObjectCount() const { return m_left
.GetCount() + m_right
.GetCount(); }
117 // Get floating objects
118 bool GetFloatingObjects(wxRichTextObjectList
& objects
) const;
121 bool DeleteFloat(wxRichTextObject
* obj
);
123 // Do we have this float already?
124 bool HasFloat(wxRichTextObject
* obj
);
126 bool HasFloats() const { return m_left
.GetCount() >0 || m_right
.GetCount() > 0; }
128 static int SearchAdjacentRect(const wxRichTextFloatRectMapArray
& array
, int point
);
130 static int GetWidthFromFloatRect(const wxRichTextFloatRectMapArray
& array
, int index
, int startY
, int endY
);
132 static void FreeFloatRectMapArray(wxRichTextFloatRectMapArray
& array
);
134 static void DrawFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
);
136 static int HitTestFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, int flags
);
139 wxRichTextFloatRectMapArray m_left
;
140 wxRichTextFloatRectMapArray m_right
;
142 wxRect m_availableRect
;
143 wxRichTextParagraph
* m_para
;
147 bool wxRichTextFloatCollector::DeleteFloat(wxRichTextObject
* obj
)
150 for (i
= 0; i
< m_left
.GetCount(); i
++)
152 if (m_left
[i
]->anchor
== obj
)
158 for (i
= 0; i
< m_right
.GetCount(); i
++)
160 if (m_right
[i
]->anchor
== obj
)
169 // Do we have this float already?
170 bool wxRichTextFloatCollector::HasFloat(wxRichTextObject
* obj
)
173 for (i
= 0; i
< m_left
.GetCount(); i
++)
175 if (m_left
[i
]->anchor
== obj
)
180 for (i
= 0; i
< m_right
.GetCount(); i
++)
182 if (m_right
[i
]->anchor
== obj
)
190 // Get floating objects
191 bool wxRichTextFloatCollector::GetFloatingObjects(wxRichTextObjectList
& objects
) const
194 for (i
= 0; i
< m_left
.GetCount(); i
++)
195 objects
.Append(m_left
[i
]->anchor
);
196 for (i
= 0; i
< m_right
.GetCount(); i
++)
197 objects
.Append(m_right
[i
]->anchor
);
203 * Binary search helper function
204 * The argument point is the Y coordinate, and this fuction
205 * always return the floating rect that contain this coordinate
206 * or under this coordinate.
208 int wxRichTextFloatCollector::SearchAdjacentRect(const wxRichTextFloatRectMapArray
& array
, int point
)
210 int end
= array
.GetCount() - 1;
223 int mid
= (start
+ end
) / 2;
224 if (array
[mid
]->startY
<= point
&& array
[mid
]->endY
>= point
)
226 else if (array
[mid
]->startY
> point
)
231 else if (array
[mid
]->endY
< point
)
241 int wxRichTextFloatCollector::GetWidthFromFloatRect(const wxRichTextFloatRectMapArray
& array
, int index
, int startY
, int endY
)
244 int len
= array
.GetCount();
246 wxASSERT(index
>= 0 && index
< len
);
248 if (array
[index
]->startY
< startY
&& array
[index
]->endY
> startY
)
249 ret
= ret
< array
[index
]->width
? array
[index
]->width
: ret
;
250 while (index
< len
&& array
[index
]->startY
<= endY
)
252 ret
= ret
< array
[index
]->width
? array
[index
]->width
: ret
;
259 wxRichTextFloatCollector::wxRichTextFloatCollector(const wxRect
& rect
) : m_left(wxRichTextFloatRectMapCmp
), m_right(wxRichTextFloatRectMapCmp
)
261 m_availableRect
= rect
;
265 void wxRichTextFloatCollector::FreeFloatRectMapArray(wxRichTextFloatRectMapArray
& array
)
267 int len
= array
.GetCount();
268 for (int i
= 0; i
< len
; i
++)
272 wxRichTextFloatCollector::~wxRichTextFloatCollector()
274 FreeFloatRectMapArray(m_left
);
275 FreeFloatRectMapArray(m_right
);
278 int wxRichTextFloatCollector::GetFitPosition(const wxRichTextFloatRectMapArray
& array
, int start
, int height
) const
280 if (array
.GetCount() == 0)
283 int i
= SearchAdjacentRect(array
, start
);
285 while (i
< (int) array
.GetCount())
287 if (array
[i
]->startY
- last
>= height
)
289 last
= array
[i
]->endY
;
296 int wxRichTextFloatCollector::GetFitPosition(int direction
, int start
, int height
) const
298 if (direction
== wxTEXT_BOX_ATTR_FLOAT_LEFT
)
299 return GetFitPosition(m_left
, start
, height
);
300 else if (direction
== wxTEXT_BOX_ATTR_FLOAT_RIGHT
)
301 return GetFitPosition(m_right
, start
, height
);
304 wxASSERT("Never should be here");
309 // Adds a floating image to the float collector.
310 // The actual positioning is done by wxRichTextParagraph::LayoutFloat.
311 void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph
* para
, wxRichTextObject
* floating
)
313 int direction
= floating
->GetFloatDirection();
315 wxPoint pos
= floating
->GetPosition();
316 wxSize size
= floating
->GetCachedSize();
317 wxRichTextFloatRectMap
*map
= new wxRichTextFloatRectMap(pos
.y
, pos
.y
+ size
.y
, size
.x
, floating
);
320 case wxTEXT_BOX_ATTR_FLOAT_NONE
:
323 case wxTEXT_BOX_ATTR_FLOAT_LEFT
:
324 // Just a not-enough simple assertion
325 wxASSERT (m_left
.Index(map
) == wxNOT_FOUND
);
328 case wxTEXT_BOX_ATTR_FLOAT_RIGHT
:
329 wxASSERT (m_right
.Index(map
) == wxNOT_FOUND
);
334 wxASSERT("Unrecognised float attribute.");
340 void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph
* para
)
342 wxRichTextObjectList::compatibility_iterator node
= para
->GetChildren().GetFirst();
345 wxRichTextObject
* floating
= node
->GetData();
347 if (floating
->IsFloating())
349 CollectFloat(para
, floating
);
352 node
= node
->GetNext();
358 wxRichTextParagraph
* wxRichTextFloatCollector::LastParagraph()
363 wxRect
wxRichTextFloatCollector::GetAvailableRect(int startY
, int endY
)
365 int widthLeft
= 0, widthRight
= 0;
366 if (m_left
.GetCount() != 0)
368 int i
= SearchAdjacentRect(m_left
, startY
);
369 if (i
< (int) m_left
.GetCount())
370 widthLeft
= GetWidthFromFloatRect(m_left
, i
, startY
, endY
);
372 if (m_right
.GetCount() != 0)
374 int j
= SearchAdjacentRect(m_right
, startY
);
375 if (j
< (int) m_right
.GetCount())
376 widthRight
= GetWidthFromFloatRect(m_right
, j
, startY
, endY
);
379 // TODO: actually we want to use the actual image positions to find the
380 // available remaining space, since the image might not be right up against
381 // the left or right edge of the container.
382 return wxRect(widthLeft
+ m_availableRect
.x
, 0, m_availableRect
.width
- widthLeft
- widthRight
, 0);
385 int wxRichTextFloatCollector::GetLastRectBottom()
388 int len
= m_left
.GetCount();
390 ret
= ret
> m_left
[len
-1]->endY
? ret
: m_left
[len
-1]->endY
;
392 len
= m_right
.GetCount();
394 ret
= ret
> m_right
[len
-1]->endY
? ret
: m_right
[len
-1]->endY
;
400 void wxRichTextFloatCollector::DrawFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& WXUNUSED(range
), const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
403 int end
= rect
.y
+ rect
.height
;
405 i
= SearchAdjacentRect(array
, start
);
406 if (i
< 0 || i
>= (int) array
.GetCount())
408 j
= SearchAdjacentRect(array
, end
);
409 if (j
< 0 || j
>= (int) array
.GetCount())
410 j
= array
.GetCount() - 1;
413 wxRichTextObject
* obj
= array
[i
]->anchor
;
414 wxRichTextRange r
= obj
->GetRange();
415 obj
->Draw(dc
, context
, r
, selection
, wxRect(obj
->GetPosition(), obj
->GetCachedSize()), descent
, style
);
420 void wxRichTextFloatCollector::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
422 if (m_left
.GetCount() > 0)
423 DrawFloat(m_left
, dc
, context
, range
, selection
, rect
, descent
, style
);
424 if (m_right
.GetCount() > 0)
425 DrawFloat(m_right
, dc
, context
, range
, selection
, rect
, descent
, style
);
428 int wxRichTextFloatCollector::HitTestFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& WXUNUSED(dc
), wxRichTextDrawingContext
& WXUNUSED(context
), const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, int WXUNUSED(flags
))
431 if (array
.GetCount() == 0)
432 return wxRICHTEXT_HITTEST_NONE
;
433 i
= SearchAdjacentRect(array
, pt
.y
);
434 if (i
< 0 || i
>= (int) array
.GetCount())
435 return wxRICHTEXT_HITTEST_NONE
;
436 if (!array
[i
]->anchor
->IsShown())
437 return wxRICHTEXT_HITTEST_NONE
;
439 wxPoint point
= array
[i
]->anchor
->GetPosition();
440 wxSize size
= array
[i
]->anchor
->GetCachedSize();
441 if (point
.x
<= pt
.x
&& point
.x
+ size
.x
>= pt
.x
442 && point
.y
<= pt
.y
&& point
.y
+ size
.y
>= pt
.y
)
444 textPosition
= array
[i
]->anchor
->GetRange().GetStart();
445 * obj
= array
[i
]->anchor
;
446 if (pt
.x
> (pt
.x
+ pt
.x
+ size
.x
) / 2)
447 return wxRICHTEXT_HITTEST_BEFORE
;
449 return wxRICHTEXT_HITTEST_AFTER
;
452 return wxRICHTEXT_HITTEST_NONE
;
455 int wxRichTextFloatCollector::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, int flags
)
457 int ret
= HitTestFloat(m_left
, dc
, context
, pt
, textPosition
, obj
, flags
);
458 if (ret
== wxRICHTEXT_HITTEST_NONE
)
460 ret
= HitTestFloat(m_right
, dc
, context
, pt
, textPosition
, obj
, flags
);
465 // Helpers for efficiency
466 inline void wxCheckSetFont(wxDC
& dc
, const wxFont
& font
)
471 inline void wxCheckSetPen(wxDC
& dc
, const wxPen
& pen
)
473 const wxPen
& pen1
= dc
.GetPen();
474 if (pen1
.IsOk() && pen
.IsOk())
476 if (pen1
.GetWidth() == pen
.GetWidth() &&
477 pen1
.GetStyle() == pen
.GetStyle() &&
478 pen1
.GetColour() == pen
.GetColour())
484 inline void wxCheckSetBrush(wxDC
& dc
, const wxBrush
& brush
)
486 const wxBrush
& brush1
= dc
.GetBrush();
487 if (brush1
.IsOk() && brush
.IsOk())
489 if (brush1
.GetStyle() == brush
.GetStyle() &&
490 brush1
.GetColour() == brush
.GetColour())
498 * This is the base for drawable objects.
501 IMPLEMENT_CLASS(wxRichTextObject
, wxObject
)
503 wxRichTextObject::wxRichTextObject(wxRichTextObject
* parent
)
511 wxRichTextObject::~wxRichTextObject()
515 void wxRichTextObject::Dereference()
523 void wxRichTextObject::Copy(const wxRichTextObject
& obj
)
526 m_maxSize
= obj
.m_maxSize
;
527 m_minSize
= obj
.m_minSize
;
529 m_range
= obj
.m_range
;
530 m_ownRange
= obj
.m_ownRange
;
531 m_attributes
= obj
.m_attributes
;
532 m_properties
= obj
.m_properties
;
533 m_descent
= obj
.m_descent
;
537 // Get/set the top-level container of this object.
538 wxRichTextParagraphLayoutBox
* wxRichTextObject::GetContainer() const
540 const wxRichTextObject
* p
= this;
545 return wxDynamicCast(p
, wxRichTextParagraphLayoutBox
);
552 void wxRichTextObject::SetMargins(int margin
)
554 SetMargins(margin
, margin
, margin
, margin
);
557 void wxRichTextObject::SetMargins(int leftMargin
, int rightMargin
, int topMargin
, int bottomMargin
)
559 GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().SetValue(leftMargin
, wxTEXT_ATTR_UNITS_PIXELS
);
560 GetAttributes().GetTextBoxAttr().GetMargins().GetRight().SetValue(rightMargin
, wxTEXT_ATTR_UNITS_PIXELS
);
561 GetAttributes().GetTextBoxAttr().GetMargins().GetTop().SetValue(topMargin
, wxTEXT_ATTR_UNITS_PIXELS
);
562 GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().SetValue(bottomMargin
, wxTEXT_ATTR_UNITS_PIXELS
);
565 int wxRichTextObject::GetLeftMargin() const
567 return GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().GetValue();
570 int wxRichTextObject::GetRightMargin() const
572 return GetAttributes().GetTextBoxAttr().GetMargins().GetRight().GetValue();
575 int wxRichTextObject::GetTopMargin() const
577 return GetAttributes().GetTextBoxAttr().GetMargins().GetTop().GetValue();
580 int wxRichTextObject::GetBottomMargin() const
582 return GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().GetValue();
585 // Calculate the available content space in the given rectangle, given the
586 // margins, border and padding specified in the object's attributes.
587 wxRect
wxRichTextObject::GetAvailableContentArea(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& outerRect
) const
589 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
590 marginRect
= outerRect
;
591 wxRichTextAttr
attr(GetAttributes());
592 context
.ApplyVirtualAttributes(attr
, (wxRichTextObject
*) this);
593 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
597 // Invalidate the buffer. With no argument, invalidates whole buffer.
598 void wxRichTextObject::Invalidate(const wxRichTextRange
& invalidRange
)
600 if (invalidRange
!= wxRICHTEXT_NONE
)
602 // If this is a floating object, size may not be recalculated
603 // after floats have been collected in an early stage of Layout.
604 // So avoid resetting the cache for floating objects during layout.
605 if (!IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode())
606 SetCachedSize(wxDefaultSize
);
607 SetMaxSize(wxDefaultSize
);
608 SetMinSize(wxDefaultSize
);
612 // Convert units in tenths of a millimetre to device units
613 int wxRichTextObject::ConvertTenthsMMToPixels(wxDC
& dc
, int units
) const
618 scale
= GetBuffer()->GetScale() / GetBuffer()->GetDimensionScale();
619 int p
= ConvertTenthsMMToPixels(dc
.GetPPI().x
, units
, scale
);
624 // Convert units in tenths of a millimetre to device units
625 int wxRichTextObject::ConvertTenthsMMToPixels(int ppi
, int units
, double scale
)
627 // There are ppi pixels in 254.1 "1/10 mm"
629 double pixels
= ((double) units
* (double)ppi
) / 254.1;
633 // If the result is very small, make it at least one pixel in size.
634 if (pixels
== 0 && units
> 0)
640 // Convert units in pixels to tenths of a millimetre
641 int wxRichTextObject::ConvertPixelsToTenthsMM(wxDC
& dc
, int pixels
) const
646 scale
= GetBuffer()->GetScale();
648 return ConvertPixelsToTenthsMM(dc
.GetPPI().x
, p
, scale
);
651 int wxRichTextObject::ConvertPixelsToTenthsMM(int ppi
, int pixels
, double scale
)
653 // There are ppi pixels in 254.1 "1/10 mm"
655 double p
= double(pixels
);
660 int units
= int( p
* 254.1 / (double) ppi
);
664 // Draw the borders and background for the given rectangle and attributes.
665 // Width and height are taken to be the outer margin size, not the content.
666 bool wxRichTextObject::DrawBoxAttributes(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& attr
, const wxRect
& boxRect
, int flags
)
668 // Assume boxRect is the area around the content
669 wxRect marginRect
= boxRect
;
670 wxRect contentRect
, borderRect
, paddingRect
, outlineRect
;
672 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
674 // Margin is transparent. Draw background from margin.
675 if (attr
.HasBackgroundColour() || (flags
& wxRICHTEXT_DRAW_SELECTED
))
678 if (flags
& wxRICHTEXT_DRAW_SELECTED
)
680 // TODO: get selection colour from control?
681 colour
= wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
);
684 colour
= attr
.GetBackgroundColour();
687 wxBrush
brush(colour
);
691 dc
.DrawRectangle(borderRect
);
694 if (flags
& wxRICHTEXT_DRAW_GUIDELINES
)
696 wxRichTextAttr editBorderAttr
= attr
;
697 // TODO: make guideline colour configurable
698 editBorderAttr
.GetTextBoxAttr().GetBorder().SetColour(*wxLIGHT_GREY
);
699 editBorderAttr
.GetTextBoxAttr().GetBorder().SetWidth(1, wxTEXT_ATTR_UNITS_PIXELS
);
700 editBorderAttr
.GetTextBoxAttr().GetBorder().SetStyle(wxTEXT_BOX_ATTR_BORDER_SOLID
);
702 DrawBorder(dc
, buffer
, editBorderAttr
.GetTextBoxAttr().GetBorder(), borderRect
, flags
);
705 if (attr
.GetTextBoxAttr().GetBorder().IsValid())
706 DrawBorder(dc
, buffer
, attr
.GetTextBoxAttr().GetBorder(), borderRect
);
708 if (attr
.GetTextBoxAttr().GetOutline().IsValid())
709 DrawBorder(dc
, buffer
, attr
.GetTextBoxAttr().GetOutline(), outlineRect
);
715 bool wxRichTextObject::DrawBorder(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxTextAttrBorders
& attr
, const wxRect
& rect
, int WXUNUSED(flags
))
717 int borderLeft
= 0, borderRight
= 0, borderTop
= 0, borderBottom
= 0;
718 wxTextAttrDimensionConverter
converter(dc
, buffer
? buffer
->GetScale() : 1.0);
720 if (attr
.GetLeft().IsValid() && attr
.GetLeft().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
722 borderLeft
= converter
.GetPixels(attr
.GetLeft().GetWidth());
723 wxColour
col(attr
.GetLeft().GetColour());
725 // If pen width is > 1, resorts to a solid rectangle.
728 int penStyle
= wxSOLID
;
729 if (attr
.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
731 else if (attr
.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
732 penStyle
= wxLONG_DASH
;
733 wxPen
pen(col
, 1, penStyle
);
735 dc
.DrawLine(rect
.x
, rect
.y
, rect
.x
, rect
.y
+ rect
.height
);
738 else if (borderLeft
> 1)
744 dc
.DrawRectangle(rect
.x
, rect
.y
, borderLeft
, rect
.height
);
748 if (attr
.GetRight().IsValid() && attr
.GetRight().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
750 borderRight
= converter
.GetPixels(attr
.GetRight().GetWidth());
752 wxColour
col(attr
.GetRight().GetColour());
754 // If pen width is > 1, resorts to a solid rectangle.
755 if (borderRight
== 1)
757 int penStyle
= wxSOLID
;
758 if (attr
.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
760 else if (attr
.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
761 penStyle
= wxLONG_DASH
;
762 wxPen
pen(col
, 1, penStyle
);
764 dc
.DrawLine(rect
.x
+ rect
.width
, rect
.y
, rect
.x
+ rect
.width
, rect
.y
+ rect
.height
+ 1);
767 else if (borderRight
> 1)
773 dc
.DrawRectangle(rect
.x
+ rect
.width
- borderRight
, rect
.y
, borderRight
, rect
.height
);
777 if (attr
.GetTop().IsValid() && attr
.GetTop().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
779 borderTop
= converter
.GetPixels(attr
.GetTop().GetWidth());
781 wxColour
col(attr
.GetTop().GetColour());
783 // If pen width is > 1, resorts to a solid rectangle.
786 int penStyle
= wxSOLID
;
787 if (attr
.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
789 else if (attr
.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
790 penStyle
= wxLONG_DASH
;
791 wxPen
pen(col
, 1, penStyle
);
793 dc
.DrawLine(rect
.x
, rect
.y
, rect
.x
+ rect
.width
, rect
.y
);
796 else if (borderTop
> 1)
802 dc
.DrawRectangle(rect
.x
, rect
.y
, rect
.width
, borderTop
);
806 if (attr
.GetBottom().IsValid() && attr
.GetBottom().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
808 borderBottom
= converter
.GetPixels(attr
.GetBottom().GetWidth());
809 wxColour
col(attr
.GetBottom().GetColour());
811 // If pen width is > 1, resorts to a solid rectangle.
812 if (borderBottom
== 1)
814 int penStyle
= wxSOLID
;
815 if (attr
.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
817 else if (attr
.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
818 penStyle
= wxLONG_DASH
;
819 wxPen
pen(col
, 1, penStyle
);
821 dc
.DrawLine(rect
.x
, rect
.y
+ rect
.height
, rect
.x
+ rect
.width
, rect
.y
+ rect
.height
);
824 else if (borderBottom
> 1)
830 dc
.DrawRectangle(rect
.x
, rect
.y
+ rect
.height
- borderBottom
, rect
.width
, borderBottom
);
837 // Get the various rectangles of the box model in pixels. You can either specify contentRect (inner)
838 // or marginRect (outer), and the other must be the default rectangle (no width or height).
839 // Note that the outline doesn't affect the position of the rectangle, it's drawn in whatever space
842 // | Margin | Border | Padding | CONTENT | Padding | Border | Margin |
844 bool wxRichTextObject::GetBoxRects(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& attr
, wxRect
& marginRect
, wxRect
& borderRect
, wxRect
& contentRect
, wxRect
& paddingRect
, wxRect
& outlineRect
)
846 int borderLeft
= 0, borderRight
= 0, borderTop
= 0, borderBottom
= 0;
847 int outlineLeft
= 0, outlineRight
= 0, outlineTop
= 0, outlineBottom
= 0;
848 int paddingLeft
= 0, paddingRight
= 0, paddingTop
= 0, paddingBottom
= 0;
849 int marginLeft
= 0, marginRight
= 0, marginTop
= 0, marginBottom
= 0;
851 wxTextAttrDimensionConverter
converter(dc
, buffer
? buffer
->GetScale() : 1.0);
853 if (attr
.GetTextBoxAttr().GetMargins().GetLeft().IsValid())
854 marginLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetLeft());
855 if (attr
.GetTextBoxAttr().GetMargins().GetRight().IsValid())
856 marginRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetRight());
857 if (attr
.GetTextBoxAttr().GetMargins().GetTop().IsValid())
858 marginTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetTop());
859 if (attr
.GetTextBoxAttr().GetMargins().GetBottom().IsValid())
860 marginBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetBottom());
862 if (attr
.GetTextBoxAttr().GetBorder().GetLeft().GetWidth().IsValid())
863 borderLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetLeft().GetWidth());
864 if (attr
.GetTextBoxAttr().GetBorder().GetRight().GetWidth().IsValid())
865 borderRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetRight().GetWidth());
866 if (attr
.GetTextBoxAttr().GetBorder().GetTop().GetWidth().IsValid())
867 borderTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetTop().GetWidth());
868 if (attr
.GetTextBoxAttr().GetBorder().GetBottom().GetWidth().IsValid())
869 borderBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetBottom().GetWidth());
871 if (attr
.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
872 paddingLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetLeft());
873 if (attr
.GetTextBoxAttr().GetPadding().GetRight().IsValid())
874 paddingRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetRight());
875 if (attr
.GetTextBoxAttr().GetPadding().GetTop().IsValid())
876 paddingTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetTop());
877 if (attr
.GetTextBoxAttr().GetPadding().GetBottom().IsValid())
878 paddingBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetBottom());
880 if (attr
.GetTextBoxAttr().GetOutline().GetLeft().GetWidth().IsValid())
881 outlineLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetLeft().GetWidth());
882 if (attr
.GetTextBoxAttr().GetOutline().GetRight().GetWidth().IsValid())
883 outlineRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetRight().GetWidth());
884 if (attr
.GetTextBoxAttr().GetOutline().GetTop().GetWidth().IsValid())
885 outlineTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetTop().GetWidth());
886 if (attr
.GetTextBoxAttr().GetOutline().GetBottom().GetWidth().IsValid())
887 outlineBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetBottom().GetWidth());
889 int leftTotal
= marginLeft
+ borderLeft
+ paddingLeft
;
890 int rightTotal
= marginRight
+ borderRight
+ paddingRight
;
891 int topTotal
= marginTop
+ borderTop
+ paddingTop
;
892 int bottomTotal
= marginBottom
+ borderBottom
+ paddingBottom
;
894 if (marginRect
!= wxRect())
896 contentRect
.x
= marginRect
.x
+ leftTotal
;
897 contentRect
.y
= marginRect
.y
+ topTotal
;
898 contentRect
.width
= marginRect
.width
- (leftTotal
+ rightTotal
);
899 contentRect
.height
= marginRect
.height
- (topTotal
+ bottomTotal
);
903 marginRect
.x
= contentRect
.x
- leftTotal
;
904 marginRect
.y
= contentRect
.y
- topTotal
;
905 marginRect
.width
= contentRect
.width
+ (leftTotal
+ rightTotal
);
906 marginRect
.height
= contentRect
.height
+ (topTotal
+ bottomTotal
);
909 borderRect
.x
= marginRect
.x
+ marginLeft
;
910 borderRect
.y
= marginRect
.y
+ marginTop
;
911 borderRect
.width
= marginRect
.width
- (marginLeft
+ marginRight
);
912 borderRect
.height
= marginRect
.height
- (marginTop
+ marginBottom
);
914 paddingRect
.x
= marginRect
.x
+ marginLeft
+ borderLeft
;
915 paddingRect
.y
= marginRect
.y
+ marginTop
+ borderTop
;
916 paddingRect
.width
= marginRect
.width
- (marginLeft
+ marginRight
+ borderLeft
+ borderRight
);
917 paddingRect
.height
= marginRect
.height
- (marginTop
+ marginBottom
+ borderTop
+ borderBottom
);
919 // The outline is outside the margin and doesn't influence the overall box position or content size.
920 outlineRect
.x
= marginRect
.x
- outlineLeft
;
921 outlineRect
.y
= marginRect
.y
- outlineTop
;
922 outlineRect
.width
= marginRect
.width
+ (outlineLeft
+ outlineRight
);
923 outlineRect
.height
= marginRect
.height
+ (outlineTop
+ outlineBottom
);
928 // Get the total margin for the object in pixels, taking into account margin, padding and border size
929 bool wxRichTextObject::GetTotalMargin(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& attr
, int& leftMargin
, int& rightMargin
,
930 int& topMargin
, int& bottomMargin
)
932 // Assume boxRect is the area around the content
933 wxRect contentRect
, marginRect
, borderRect
, paddingRect
, outlineRect
;
934 marginRect
= wxRect(0, 0, 1000, 1000);
936 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
938 leftMargin
= contentRect
.GetLeft() - marginRect
.GetLeft();
939 rightMargin
= marginRect
.GetRight() - contentRect
.GetRight();
940 topMargin
= contentRect
.GetTop() - marginRect
.GetTop();
941 bottomMargin
= marginRect
.GetBottom() - contentRect
.GetBottom();
946 // Returns the rectangle which the child has available to it given restrictions specified in the
947 // child attribute, e.g. 50% width of the parent, 400 pixels, x position 20% of the parent, etc.
948 // availableContainerSpace might be a parent that the cell has to compute its width relative to.
949 // E.g. a cell that's 50% of its parent.
950 wxRect
wxRichTextObject::AdjustAvailableSpace(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& WXUNUSED(parentAttr
), const wxRichTextAttr
& childAttr
, const wxRect
& availableParentSpace
, const wxRect
& availableContainerSpace
)
952 wxRect rect
= availableParentSpace
;
955 scale
= buffer
->GetScale();
957 wxTextAttrDimensionConverter
converter(dc
, scale
, availableContainerSpace
.GetSize());
959 if (childAttr
.GetTextBoxAttr().GetWidth().IsValid())
960 rect
.width
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetWidth());
962 if (childAttr
.GetTextBoxAttr().GetHeight().IsValid())
963 rect
.height
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetHeight());
965 // Can specify either left or right for the position (we're assuming we can't
966 // set the left and right edges to effectively set the size. Would we want to do that?)
967 if (childAttr
.GetTextBoxAttr().GetPosition().GetLeft().IsValid())
969 rect
.x
= rect
.x
+ converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetLeft());
971 else if (childAttr
.GetTextBoxAttr().GetPosition().GetRight().IsValid())
973 int x
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetRight());
974 if (childAttr
.GetTextBoxAttr().GetPosition().GetRight().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE
)
975 rect
.x
= availableContainerSpace
.x
+ availableContainerSpace
.width
- rect
.width
;
980 if (childAttr
.GetTextBoxAttr().GetPosition().GetTop().IsValid())
982 rect
.y
= rect
.y
+ converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetTop());
984 else if (childAttr
.GetTextBoxAttr().GetPosition().GetBottom().IsValid())
986 int y
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetBottom());
987 if (childAttr
.GetTextBoxAttr().GetPosition().GetBottom().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE
)
988 rect
.y
= availableContainerSpace
.y
+ availableContainerSpace
.height
- rect
.height
;
993 if (rect
.GetWidth() > availableParentSpace
.GetWidth())
994 rect
.SetWidth(availableParentSpace
.GetWidth());
999 // Dump to output stream for debugging
1000 void wxRichTextObject::Dump(wxTextOutputStream
& stream
)
1002 stream
<< GetClassInfo()->GetClassName() << wxT("\n");
1003 stream
<< wxString::Format(wxT("Size: %d,%d. Position: %d,%d, Range: %ld,%ld"), m_size
.x
, m_size
.y
, m_pos
.x
, m_pos
.y
, m_range
.GetStart(), m_range
.GetEnd()) << wxT("\n");
1004 stream
<< wxString::Format(wxT("Text colour: %d,%d,%d."), (int) m_attributes
.GetTextColour().Red(), (int) m_attributes
.GetTextColour().Green(), (int) m_attributes
.GetTextColour().Blue()) << wxT("\n");
1007 // Gets the containing buffer
1008 wxRichTextBuffer
* wxRichTextObject::GetBuffer() const
1010 const wxRichTextObject
* obj
= this;
1011 while (obj
&& !wxDynamicCast(obj
, wxRichTextBuffer
))
1012 obj
= obj
->GetParent();
1013 return wxDynamicCast(obj
, wxRichTextBuffer
);
1016 // Get the absolute object position, by traversing up the child/parent hierarchy
1017 wxPoint
wxRichTextObject::GetAbsolutePosition() const
1019 wxPoint pt
= GetPosition();
1021 wxRichTextObject
* p
= GetParent();
1024 pt
= pt
+ p
->GetPosition();
1031 // Hit-testing: returns a flag indicating hit test details, plus
1032 // information about position
1033 int wxRichTextObject::HitTest(wxDC
& WXUNUSED(dc
), wxRichTextDrawingContext
& WXUNUSED(context
), const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int WXUNUSED(flags
))
1036 return wxRICHTEXT_HITTEST_NONE
;
1038 wxRect rect
= GetRect();
1039 if (pt
.x
>= rect
.x
&& pt
.x
< rect
.x
+ rect
.width
&&
1040 pt
.y
>= rect
.y
&& pt
.y
< rect
.y
+ rect
.height
)
1043 *contextObj
= GetParentContainer();
1044 textPosition
= GetRange().GetStart();
1045 return wxRICHTEXT_HITTEST_ON
;
1048 return wxRICHTEXT_HITTEST_NONE
;
1051 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1052 // lays out the object again using the maximum ('best') size
1053 bool wxRichTextObject::LayoutToBestSize(wxDC
& dc
, wxRichTextDrawingContext
& context
, wxRichTextBuffer
* buffer
,
1054 const wxRichTextAttr
& parentAttr
, const wxRichTextAttr
& attr
,
1055 const wxRect
& availableParentSpace
, const wxRect
& availableContainerSpace
,
1058 wxRect availableChildRect
= AdjustAvailableSpace(dc
, buffer
, parentAttr
, attr
, availableParentSpace
, availableContainerSpace
);
1059 wxRect originalAvailableRect
= availableChildRect
;
1060 Layout(dc
, context
, availableChildRect
, availableContainerSpace
, style
);
1062 wxSize maxSize
= GetMaxSize();
1064 // Don't ignore if maxSize.x is zero, since we need to redo the paragraph's lines
1066 if (!attr
.GetTextBoxAttr().GetWidth().IsValid() && maxSize
.x
< availableChildRect
.width
)
1068 // Redo the layout with a fixed, minimum size this time.
1069 Invalidate(wxRICHTEXT_ALL
);
1070 wxRichTextAttr
newAttr(attr
);
1071 newAttr
.GetTextBoxAttr().GetWidth().SetValue(maxSize
.x
, wxTEXT_ATTR_UNITS_PIXELS
);
1072 newAttr
.GetTextBoxAttr().GetWidth().SetPosition(wxTEXT_BOX_ATTR_POSITION_ABSOLUTE
);
1074 availableChildRect
= AdjustAvailableSpace(dc
, buffer
, parentAttr
, newAttr
, availableParentSpace
, availableContainerSpace
);
1076 // If a paragraph, align the whole paragraph.
1077 // Problem with this: if we're limited by a floating object, a line may be centered
1078 // w.r.t. the smaller resulting box rather than the actual available width.
1079 // FIXME: aligning whole paragraph not compatible with floating objects
1080 if (attr
.HasAlignment() && (!wxRichTextBuffer::GetFloatingLayoutMode() || (GetContainer()->GetFloatCollector() && !GetContainer()->GetFloatCollector()->HasFloats())))
1082 // centering, right-justification
1083 if (attr
.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE
)
1085 availableChildRect
.x
= (originalAvailableRect
.GetWidth() - availableChildRect
.GetWidth())/2 + availableChildRect
.x
;
1087 else if (attr
.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT
)
1089 availableChildRect
.x
= availableChildRect
.x
+ originalAvailableRect
.GetWidth() - availableChildRect
.GetWidth();
1093 Layout(dc
, context
, availableChildRect
, availableContainerSpace
, style
);
1107 // Move the object recursively, by adding the offset from old to new
1108 void wxRichTextObject::Move(const wxPoint
& pt
)
1115 * wxRichTextCompositeObject
1116 * This is the base for drawable objects.
1119 IMPLEMENT_CLASS(wxRichTextCompositeObject
, wxRichTextObject
)
1121 wxRichTextCompositeObject::wxRichTextCompositeObject(wxRichTextObject
* parent
):
1122 wxRichTextObject(parent
)
1126 wxRichTextCompositeObject::~wxRichTextCompositeObject()
1131 /// Get the nth child
1132 wxRichTextObject
* wxRichTextCompositeObject::GetChild(size_t n
) const
1134 wxASSERT ( n
< m_children
.GetCount() );
1136 return m_children
.Item(n
)->GetData();
1139 /// Append a child, returning the position
1140 size_t wxRichTextCompositeObject::AppendChild(wxRichTextObject
* child
)
1142 m_children
.Append(child
);
1143 child
->SetParent(this);
1144 return m_children
.GetCount() - 1;
1147 /// Insert the child in front of the given object, or at the beginning
1148 bool wxRichTextCompositeObject::InsertChild(wxRichTextObject
* child
, wxRichTextObject
* inFrontOf
)
1152 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(inFrontOf
);
1153 m_children
.Insert(node
, child
);
1156 m_children
.Insert(child
);
1157 child
->SetParent(this);
1162 /// Delete the child
1163 bool wxRichTextCompositeObject::RemoveChild(wxRichTextObject
* child
, bool deleteChild
)
1165 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(child
);
1168 wxRichTextObject
* obj
= node
->GetData();
1169 m_children
.Erase(node
);
1178 /// Delete all children
1179 bool wxRichTextCompositeObject::DeleteChildren()
1181 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1184 wxRichTextObjectList::compatibility_iterator oldNode
= node
;
1186 wxRichTextObject
* child
= node
->GetData();
1187 child
->Dereference(); // Only delete if reference count is zero
1189 node
= node
->GetNext();
1190 m_children
.Erase(oldNode
);
1196 /// Get the child count
1197 size_t wxRichTextCompositeObject::GetChildCount() const
1199 return m_children
.GetCount();
1203 void wxRichTextCompositeObject::Copy(const wxRichTextCompositeObject
& obj
)
1205 wxRichTextObject::Copy(obj
);
1209 wxRichTextObjectList::compatibility_iterator node
= obj
.m_children
.GetFirst();
1212 wxRichTextObject
* child
= node
->GetData();
1213 wxRichTextObject
* newChild
= child
->Clone();
1214 newChild
->SetParent(this);
1215 m_children
.Append(newChild
);
1217 node
= node
->GetNext();
1221 /// Hit-testing: returns a flag indicating hit test details, plus
1222 /// information about position
1223 int wxRichTextCompositeObject::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
1226 return wxRICHTEXT_HITTEST_NONE
;
1228 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1231 wxRichTextObject
* child
= node
->GetData();
1233 if (child
->IsShown() && child
->IsTopLevel() && (flags
& wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS
))
1235 // Just check if we hit the overall object
1236 int ret
= child
->wxRichTextObject::HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
1237 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
1240 else if (child
->IsShown())
1242 int ret
= child
->HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
1243 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
1247 node
= node
->GetNext();
1250 return wxRICHTEXT_HITTEST_NONE
;
1253 /// Finds the absolute position and row height for the given character position
1254 bool wxRichTextCompositeObject::FindPosition(wxDC
& dc
, wxRichTextDrawingContext
& context
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
1256 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1259 wxRichTextObject
* child
= node
->GetData();
1261 // Don't recurse if the child is a top-level object,
1262 // such as a text box, because the character position will no longer
1263 // apply. By definition, a top-level object has its own range of
1264 // character positions.
1265 if (!child
->IsTopLevel() && child
->FindPosition(dc
, context
, index
, pt
, height
, forceLineStart
))
1268 node
= node
->GetNext();
1275 void wxRichTextCompositeObject::CalculateRange(long start
, long& end
)
1277 long current
= start
;
1278 long lastEnd
= current
;
1286 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1289 wxRichTextObject
* child
= node
->GetData();
1292 child
->CalculateRange(current
, childEnd
);
1295 current
= childEnd
+ 1;
1297 node
= node
->GetNext();
1302 // A top-level object always has a range of size 1,
1303 // because its children don't count at this level.
1305 m_range
.SetRange(start
, start
);
1307 // An object with no children has zero length
1308 if (m_children
.GetCount() == 0)
1310 m_ownRange
.SetRange(0, lastEnd
);
1316 // An object with no children has zero length
1317 if (m_children
.GetCount() == 0)
1320 m_range
.SetRange(start
, end
);
1324 /// Delete range from layout.
1325 bool wxRichTextCompositeObject::DeleteRange(const wxRichTextRange
& range
)
1327 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1331 wxRichTextObject
* obj
= (wxRichTextObject
*) node
->GetData();
1332 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
1334 // Delete the range in each paragraph
1336 // When a chunk has been deleted, internally the content does not
1337 // now match the ranges.
1338 // However, so long as deletion is not done on the same object twice this is OK.
1339 // If you may delete content from the same object twice, recalculate
1340 // the ranges inbetween DeleteRange calls by calling CalculateRanges, and
1341 // adjust the range you're deleting accordingly.
1343 if (!obj
->GetRange().IsOutside(range
))
1345 // No need to delete within a top-level object; just removing this object will do fine
1346 if (!obj
->IsTopLevel())
1347 obj
->DeleteRange(range
);
1349 // Delete an empty object, or paragraph within this range.
1350 if (obj
->IsEmpty() ||
1351 (range
.GetStart() <= obj
->GetRange().GetStart() && range
.GetEnd() >= obj
->GetRange().GetEnd()))
1353 // An empty paragraph has length 1, so won't be deleted unless the
1354 // whole range is deleted.
1355 RemoveChild(obj
, true);
1365 /// Get any text in this object for the given range
1366 wxString
wxRichTextCompositeObject::GetTextForRange(const wxRichTextRange
& range
) const
1369 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1372 wxRichTextObject
* child
= node
->GetData();
1373 wxRichTextRange childRange
= range
;
1374 if (!child
->GetRange().IsOutside(range
))
1376 childRange
.LimitTo(child
->GetRange());
1378 wxString childText
= child
->GetTextForRange(childRange
);
1382 node
= node
->GetNext();
1388 /// Get the child object at the given character position
1389 wxRichTextObject
* wxRichTextCompositeObject::GetChildAtPosition(long pos
) const
1391 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1394 wxRichTextObject
* child
= node
->GetData();
1395 if (child
->GetRange().GetStart() == pos
)
1397 node
= node
->GetNext();
1402 /// Recursively merge all pieces that can be merged.
1403 bool wxRichTextCompositeObject::Defragment(wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
)
1405 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1408 wxRichTextObject
* child
= node
->GetData();
1409 if (range
== wxRICHTEXT_ALL
|| !child
->GetRange().IsOutside(range
))
1411 wxRichTextCompositeObject
* composite
= wxDynamicCast(child
, wxRichTextCompositeObject
);
1413 composite
->Defragment(context
);
1415 // Optimization: if there are no virtual attributes, we won't need to
1416 // to split objects in order to paint individually attributed chunks.
1417 // So only merge in this case.
1418 if (!context
.GetVirtualAttributesEnabled())
1420 if (node
->GetNext())
1422 wxRichTextObject
* nextChild
= node
->GetNext()->GetData();
1423 if (child
->CanMerge(nextChild
, context
) && child
->Merge(nextChild
, context
))
1425 nextChild
->Dereference();
1426 m_children
.Erase(node
->GetNext());
1429 node
= node
->GetNext();
1432 node
= node
->GetNext();
1436 // If we might have virtual attributes, we first see if we have to split
1437 // objects so that they may be painted with potential virtual attributes,
1438 // since text objects can only draw or measure with a single attributes object
1440 wxRichTextObject
* childAfterSplit
= child
;
1441 if (child
->CanSplit(context
))
1443 childAfterSplit
= child
->Split(context
);
1444 node
= m_children
.Find(childAfterSplit
);
1447 if (node
->GetNext())
1449 wxRichTextObject
* nextChild
= node
->GetNext()->GetData();
1450 wxRichTextObjectList::compatibility_iterator nextNode
= node
->GetNext();
1452 // First split child and nextChild so we have smaller fragments to merge.
1453 // Then Merge only has to test per-object virtual attributes
1454 // because for an object with all the same sub-object attributes,
1455 // then any general virtual attributes should be merged with sub-objects by
1456 // the implementation.
1458 wxRichTextObject
* nextChildAfterSplit
= nextChild
;
1460 if (nextChildAfterSplit
->CanSplit(context
))
1461 nextChildAfterSplit
= nextChild
->Split(context
);
1463 bool splitNextChild
= nextChild
!= nextChildAfterSplit
;
1465 // See if we can merge this new fragment with (perhaps the first part of) the next object.
1466 // Note that we use nextChild because if we had split nextChild, the first object always
1467 // remains (and further parts are appended). However we must use childAfterSplit since
1468 // it's the last part of a possibly split child.
1470 if (childAfterSplit
->CanMerge(nextChild
, context
) && childAfterSplit
->Merge(nextChild
, context
))
1472 nextChild
->Dereference();
1473 m_children
.Erase(node
->GetNext());
1475 // Don't set node -- we'll see if we can merge again with the next
1476 // child. UNLESS we split this or the next child, in which case we know we have to
1477 // move on to the end of the next child.
1479 node
= m_children
.Find(nextChildAfterSplit
);
1484 node
= m_children
.Find(nextChildAfterSplit
); // start from the last object in the split
1486 node
= node
->GetNext();
1490 node
= node
->GetNext();
1494 node
= node
->GetNext();
1497 // Delete any remaining empty objects, but leave at least one empty object per composite object.
1498 if (GetChildCount() > 1)
1500 node
= m_children
.GetFirst();
1503 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
1504 wxRichTextObject
* child
= node
->GetData();
1505 if (range
== wxRICHTEXT_ALL
|| !child
->GetRange().IsOutside(range
))
1507 if (child
->IsEmpty())
1509 child
->Dereference();
1510 m_children
.Erase(node
);
1515 node
= node
->GetNext();
1522 /// Dump to output stream for debugging
1523 void wxRichTextCompositeObject::Dump(wxTextOutputStream
& stream
)
1525 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1528 wxRichTextObject
* child
= node
->GetData();
1529 child
->Dump(stream
);
1530 node
= node
->GetNext();
1534 /// Get/set the object size for the given range. Returns false if the range
1535 /// is invalid for this object.
1536 bool wxRichTextCompositeObject::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, const wxPoint
& position
, const wxSize
& parentSize
, wxArrayInt
* partialExtents
) const
1538 if (!range
.IsWithin(GetRange()))
1543 wxArrayInt childExtents
;
1550 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1553 wxRichTextObject
* child
= node
->GetData();
1554 if (!child
->GetRange().IsOutside(range
))
1556 // Floating objects have a zero size within the paragraph.
1557 if (child
->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode())
1562 if (partialExtents
->GetCount() > 0)
1563 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
1567 partialExtents
->Add(0 /* zero size */ + lastSize
);
1574 wxRichTextRange rangeToUse
= range
;
1575 rangeToUse
.LimitTo(child
->GetRange());
1576 if (child
->IsTopLevel())
1577 rangeToUse
= child
->GetOwnRange();
1579 int childDescent
= 0;
1581 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we're already cached the size,
1582 // but it's only going to be used after caching has taken place.
1583 if ((flags
& wxRICHTEXT_HEIGHT_ONLY
) && child
->GetCachedSize().y
!= 0)
1585 childDescent
= child
->GetDescent();
1586 childSize
= child
->GetCachedSize();
1588 sz
.y
= wxMax(sz
.y
, childSize
.y
);
1589 sz
.x
+= childSize
.x
;
1590 descent
= wxMax(descent
, childDescent
);
1592 else if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, context
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
), parentSize
, p
))
1594 sz
.y
= wxMax(sz
.y
, childSize
.y
);
1595 sz
.x
+= childSize
.x
;
1596 descent
= wxMax(descent
, childDescent
);
1598 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange() || child
->IsTopLevel()))
1600 child
->SetCachedSize(childSize
);
1601 child
->SetDescent(childDescent
);
1607 if (partialExtents
->GetCount() > 0)
1608 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
1613 for (i
= 0; i
< childExtents
.GetCount(); i
++)
1615 partialExtents
->Add(childExtents
[i
] + lastSize
);
1625 node
= node
->GetNext();
1631 // Invalidate the buffer. With no argument, invalidates whole buffer.
1632 void wxRichTextCompositeObject::Invalidate(const wxRichTextRange
& invalidRange
)
1634 wxRichTextObject::Invalidate(invalidRange
);
1636 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1639 wxRichTextObject
* child
= node
->GetData();
1640 if (invalidRange
!= wxRICHTEXT_ALL
&& invalidRange
!= wxRICHTEXT_NONE
&& child
->GetRange().IsOutside(invalidRange
))
1644 else if (child
->IsTopLevel())
1646 if (wxRichTextBuffer::GetFloatingLayoutMode() && child
->IsFloating() && GetBuffer()->GetFloatCollector() && GetBuffer()->GetFloatCollector()->HasFloat(child
))
1648 // Don't invalidate subhierarchy if we've already been laid out
1652 if (invalidRange
== wxRICHTEXT_NONE
)
1653 child
->Invalidate(wxRICHTEXT_NONE
);
1655 child
->Invalidate(wxRICHTEXT_ALL
); // All children must be invalidated if within parent range
1659 child
->Invalidate(invalidRange
);
1660 node
= node
->GetNext();
1664 // Move the object recursively, by adding the offset from old to new
1665 void wxRichTextCompositeObject::Move(const wxPoint
& pt
)
1667 wxPoint oldPos
= GetPosition();
1669 wxPoint offset
= pt
- oldPos
;
1671 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1674 wxRichTextObject
* child
= node
->GetData();
1675 wxPoint childPos
= child
->GetPosition() + offset
;
1676 child
->Move(childPos
);
1677 node
= node
->GetNext();
1683 * wxRichTextParagraphLayoutBox
1684 * This box knows how to lay out paragraphs.
1687 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraphLayoutBox
, wxRichTextCompositeObject
)
1689 wxRichTextParagraphLayoutBox::wxRichTextParagraphLayoutBox(wxRichTextObject
* parent
):
1690 wxRichTextCompositeObject(parent
)
1695 wxRichTextParagraphLayoutBox::~wxRichTextParagraphLayoutBox()
1697 if (m_floatCollector
)
1699 delete m_floatCollector
;
1700 m_floatCollector
= NULL
;
1704 /// Initialize the object.
1705 void wxRichTextParagraphLayoutBox::Init()
1709 // For now, assume is the only box and has no initial size.
1710 m_range
= wxRichTextRange(0, -1);
1711 m_ownRange
= wxRichTextRange(0, -1);
1713 m_invalidRange
= wxRICHTEXT_ALL
;
1715 m_partialParagraph
= false;
1716 m_floatCollector
= NULL
;
1719 void wxRichTextParagraphLayoutBox::Clear()
1723 if (m_floatCollector
)
1724 delete m_floatCollector
;
1725 m_floatCollector
= NULL
;
1726 m_partialParagraph
= false;
1730 void wxRichTextParagraphLayoutBox::Copy(const wxRichTextParagraphLayoutBox
& obj
)
1734 wxRichTextCompositeObject::Copy(obj
);
1736 m_partialParagraph
= obj
.m_partialParagraph
;
1737 m_defaultAttributes
= obj
.m_defaultAttributes
;
1740 // Gather information about floating objects; only gather floats for those paragraphs that
1741 // will not be formatted again due to optimization, after which floats will be gathered per-paragraph
1743 bool wxRichTextParagraphLayoutBox::UpdateFloatingObjects(const wxRect
& availableRect
, wxRichTextObject
* untilObj
)
1745 if (m_floatCollector
!= NULL
)
1746 delete m_floatCollector
;
1747 m_floatCollector
= new wxRichTextFloatCollector(availableRect
);
1748 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1749 // Only gather floats up to the point we'll start formatting paragraphs.
1750 while (untilObj
&& node
&& node
->GetData() != untilObj
)
1752 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
1753 wxASSERT (child
!= NULL
);
1755 m_floatCollector
->CollectFloat(child
);
1756 node
= node
->GetNext();
1762 // Returns the style sheet associated with the overall buffer.
1763 wxRichTextStyleSheet
* wxRichTextParagraphLayoutBox::GetStyleSheet() const
1765 return GetBuffer() ? GetBuffer()->GetStyleSheet() : (wxRichTextStyleSheet
*) NULL
;
1768 // Get the number of floating objects at this level
1769 int wxRichTextParagraphLayoutBox::GetFloatingObjectCount() const
1771 if (m_floatCollector
)
1772 return m_floatCollector
->GetFloatingObjectCount();
1777 // Get a list of floating objects
1778 bool wxRichTextParagraphLayoutBox::GetFloatingObjects(wxRichTextObjectList
& objects
) const
1780 if (m_floatCollector
)
1782 return m_floatCollector
->GetFloatingObjects(objects
);
1789 void wxRichTextParagraphLayoutBox::UpdateRanges()
1793 start
= GetRange().GetStart();
1795 CalculateRange(start
, end
);
1799 int wxRichTextParagraphLayoutBox::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
1802 return wxRICHTEXT_HITTEST_NONE
;
1804 int ret
= wxRICHTEXT_HITTEST_NONE
;
1805 if (wxRichTextBuffer::GetFloatingLayoutMode() && m_floatCollector
&& (flags
& wxRICHTEXT_HITTEST_NO_FLOATING_OBJECTS
) == 0)
1806 ret
= m_floatCollector
->HitTest(dc
, context
, pt
, textPosition
, obj
, flags
);
1808 if (ret
== wxRICHTEXT_HITTEST_NONE
)
1809 return wxRichTextCompositeObject::HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
1817 /// Draw the floating objects
1818 void wxRichTextParagraphLayoutBox::DrawFloats(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
1820 if (wxRichTextBuffer::GetFloatingLayoutMode() && m_floatCollector
)
1821 m_floatCollector
->Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
1824 void wxRichTextParagraphLayoutBox::MoveAnchoredObjectToParagraph(wxRichTextParagraph
* from
, wxRichTextParagraph
* to
, wxRichTextObject
* obj
)
1829 from
->RemoveChild(obj
);
1830 to
->AppendChild(obj
);
1834 bool wxRichTextParagraphLayoutBox::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
1839 wxRect
thisRect(GetPosition(), GetCachedSize());
1841 wxRichTextAttr
attr(GetAttributes());
1842 context
.ApplyVirtualAttributes(attr
, this);
1845 if (selection
.IsValid() && GetParentContainer() != this && selection
.WithinSelection(GetRange().GetStart(), GetParentContainer()))
1846 flags
|= wxRICHTEXT_DRAW_SELECTED
;
1848 // Don't draw guidelines if at top level
1849 int theseFlags
= flags
;
1851 theseFlags
&= ~wxRICHTEXT_DRAW_GUIDELINES
;
1852 DrawBoxAttributes(dc
, GetBuffer(), attr
, thisRect
, theseFlags
);
1854 if (wxRichTextBuffer::GetFloatingLayoutMode())
1855 DrawFloats(dc
, context
, range
, selection
, rect
, descent
, style
);
1857 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1860 wxRichTextObject
* child
= node
->GetData();
1862 if (child
&& !child
->GetRange().IsOutside(range
))
1864 wxRect
childRect(child
->GetPosition(), child
->GetCachedSize());
1865 wxRichTextRange childRange
= range
;
1866 if (child
->IsTopLevel())
1868 childRange
= child
->GetOwnRange();
1871 if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) == 0) && childRect
.GetTop() > rect
.GetBottom())
1876 else if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) == 0) && childRect
.GetBottom() < rect
.GetTop())
1881 child
->Draw(dc
, context
, childRange
, selection
, rect
, descent
, style
);
1884 node
= node
->GetNext();
1889 /// Lay the item out
1890 bool wxRichTextParagraphLayoutBox::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
)
1892 SetPosition(rect
.GetPosition());
1897 wxRect availableSpace
;
1898 bool formatRect
= (style
& wxRICHTEXT_LAYOUT_SPECIFIED_RECT
) == wxRICHTEXT_LAYOUT_SPECIFIED_RECT
;
1900 wxRichTextAttr
attr(GetAttributes());
1901 context
.ApplyVirtualAttributes(attr
, this);
1903 // If only laying out a specific area, the passed rect has a different meaning:
1904 // the visible part of the buffer. This is used in wxRichTextCtrl::OnSize,
1905 // so that during a size, only the visible part will be relaid out, or
1906 // it would take too long causing flicker. As an approximation, we assume that
1907 // everything up to the start of the visible area is laid out correctly.
1910 wxRect
rect2(0, 0, rect
.width
, rect
.height
);
1911 availableSpace
= GetAvailableContentArea(dc
, context
, rect2
);
1913 // Invalidate the part of the buffer from the first visible line
1914 // to the end. If other parts of the buffer are currently invalid,
1915 // then they too will be taken into account if they are above
1916 // the visible point.
1918 wxRichTextLine
* line
= GetLineAtYPosition(rect
.y
);
1920 startPos
= line
->GetAbsoluteRange().GetStart();
1922 Invalidate(wxRichTextRange(startPos
, GetOwnRange().GetEnd()));
1926 availableSpace
= GetAvailableContentArea(dc
, context
, rect
);
1929 // Fix the width if we're at the top level
1931 attr
.GetTextBoxAttr().GetWidth().SetValue(rect
.GetWidth(), wxTEXT_ATTR_UNITS_PIXELS
);
1933 int leftMargin
, rightMargin
, topMargin
, bottomMargin
;
1934 wxRichTextObject::GetTotalMargin(dc
, GetBuffer(), attr
, leftMargin
, rightMargin
,
1935 topMargin
, bottomMargin
);
1940 // The maximum paragraph maximum width, so we can set the overall maximum width for this object
1941 int maxMaxWidth
= 0;
1943 // The maximum paragraph minimum width, so we can set the overall minimum width for this object
1944 int maxMinWidth
= 0;
1946 // If we have vertical alignment, we must recalculate everything.
1947 bool hasVerticalAlignment
= (attr
.GetTextBoxAttr().HasVerticalAlignment() &&
1948 (attr
.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP
));
1950 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1952 bool layoutAll
= true;
1954 // Get invalid range, rounding to paragraph start/end.
1955 wxRichTextRange invalidRange
= GetInvalidRange(true);
1957 if (invalidRange
== wxRICHTEXT_NONE
&& !formatRect
)
1960 if (invalidRange
== wxRICHTEXT_ALL
|| hasVerticalAlignment
)
1962 else // If we know what range is affected, start laying out from that point on.
1963 if (invalidRange
.GetStart() >= GetOwnRange().GetStart())
1965 wxRichTextParagraph
* firstParagraph
= GetParagraphAtPosition(invalidRange
.GetStart());
1968 wxRichTextObjectList::compatibility_iterator firstNode
= m_children
.Find(firstParagraph
);
1969 wxRichTextObjectList::compatibility_iterator previousNode
;
1971 previousNode
= firstNode
->GetPrevious();
1976 wxRichTextParagraph
* previousParagraph
= wxDynamicCast(previousNode
->GetData(), wxRichTextParagraph
);
1977 availableSpace
.y
= previousParagraph
->GetPosition().y
+ previousParagraph
->GetCachedSize().y
;
1980 // Now we're going to start iterating from the first affected paragraph.
1988 // Gather information about only those floating objects that will not be formatted,
1989 // after which floats will be gathered per-paragraph during layout.
1990 if (wxRichTextBuffer::GetFloatingLayoutMode())
1991 UpdateFloatingObjects(availableSpace
, node
? node
->GetData() : (wxRichTextObject
*) NULL
);
1993 // A way to force speedy rest-of-buffer layout (the 'else' below)
1994 bool forceQuickLayout
= false;
1996 // First get the size of the paragraphs we won't be laying out
1997 wxRichTextObjectList::compatibility_iterator n
= m_children
.GetFirst();
1998 while (n
&& n
!= node
)
2000 wxRichTextParagraph
* child
= wxDynamicCast(n
->GetData(), wxRichTextParagraph
);
2003 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
2004 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
2005 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
2012 // Assume this box only contains paragraphs
2014 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2015 // Unsure if this is needed
2016 // wxCHECK_MSG( child, false, wxT("Unknown object in layout") );
2018 if (child
&& child
->IsShown())
2020 // TODO: what if the child hasn't been laid out (e.g. involved in Undo) but still has 'old' lines
2021 if ( !forceQuickLayout
&&
2023 child
->GetLines().IsEmpty() ||
2024 !child
->GetRange().IsOutside(invalidRange
)) )
2026 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
2027 // lays out the object again using the minimum size
2028 child
->LayoutToBestSize(dc
, context
, GetBuffer(),
2029 attr
, child
->GetAttributes(), availableSpace
, rect
, style
&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT
);
2031 // Layout must set the cached size
2032 availableSpace
.y
+= child
->GetCachedSize().y
;
2033 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
2034 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
2035 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
2037 // If we're just formatting the visible part of the buffer,
2038 // and we're now past the bottom of the window, and we don't have any
2039 // floating objects (since they may cause wrapping to change for the rest of the
2040 // the buffer), start quick layout.
2041 if (!hasVerticalAlignment
&& formatRect
&& child
->GetPosition().y
> rect
.GetBottom() && GetFloatingObjectCount() == 0)
2042 forceQuickLayout
= true;
2046 // We're outside the immediately affected range, so now let's just
2047 // move everything up or down. This assumes that all the children have previously
2048 // been laid out and have wrapped line lists associated with them.
2049 // TODO: check all paragraphs before the affected range.
2051 int inc
= availableSpace
.y
- child
->GetPosition().y
;
2055 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2058 if (child
->GetLines().GetCount() == 0)
2060 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
2061 // lays out the object again using the minimum size
2062 child
->LayoutToBestSize(dc
, context
, GetBuffer(),
2063 attr
, child
->GetAttributes(), availableSpace
, rect
, style
&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT
);
2065 //child->Layout(dc, availableChildRect, style);
2068 child
->Move(wxPoint(child
->GetPosition().x
, child
->GetPosition().y
+ inc
));
2070 availableSpace
.y
+= child
->GetCachedSize().y
;
2071 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
2072 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
2073 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
2076 node
= node
->GetNext();
2082 node
= node
->GetNext();
2085 node
= m_children
.GetLast();
2086 if (node
&& node
->GetData()->IsShown())
2088 wxRichTextObject
* child
= node
->GetData();
2089 maxHeight
= child
->GetPosition().y
- (GetPosition().y
+ topMargin
) + child
->GetCachedSize().y
;
2092 maxHeight
= 0; // topMargin + bottomMargin;
2094 // Check the bottom edge of any floating object
2095 if (wxRichTextBuffer::GetFloatingLayoutMode() && GetFloatCollector() && GetFloatCollector()->HasFloats())
2097 int bottom
= GetFloatCollector()->GetLastRectBottom();
2098 if (bottom
> maxHeight
)
2102 if (attr
.GetTextBoxAttr().GetSize().GetWidth().IsValid())
2104 wxRect r
= AdjustAvailableSpace(dc
, GetBuffer(), wxRichTextAttr() /* not used */, attr
, parentRect
, parentRect
);
2105 int w
= r
.GetWidth();
2107 // Convert external to content rect
2108 w
= w
- leftMargin
- rightMargin
;
2109 maxWidth
= wxMax(maxWidth
, w
);
2110 maxMaxWidth
= wxMax(maxMaxWidth
, w
);
2114 // TODO: Make sure the layout box's position reflects
2115 // the position of the children, but without
2116 // breaking layout of a box within a paragraph.
2119 // TODO: (also in para layout) should set the
2120 // object's size to an absolute one if specified,
2121 // but if not specified, calculate it from content.
2123 // We need to add back the margins etc.
2125 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2126 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxWidth
, maxHeight
));
2127 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2128 SetCachedSize(marginRect
.GetSize());
2131 // The maximum size is the greatest of all maximum widths for all paragraphs.
2133 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2134 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxMaxWidth
, maxHeight
)); // Actually max height is a lie, we can't know it
2135 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2136 SetMaxSize(marginRect
.GetSize());
2139 // The minimum size is the greatest of all minimum widths for all paragraphs.
2141 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2142 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxMinWidth
, maxHeight
)); // Actually max height is a lie, we can't know it
2143 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2144 SetMinSize(marginRect
.GetSize());
2147 if (attr
.GetTextBoxAttr().HasVerticalAlignment() &&
2148 (attr
.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP
))
2151 int leftOverSpace
= availableSpace
.height
- topMargin
- bottomMargin
- maxHeight
;
2152 if (leftOverSpace
> 0)
2154 if (attr
.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_CENTRE
)
2156 yOffset
= (leftOverSpace
/2);
2158 else if (attr
.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_BOTTOM
)
2160 yOffset
= leftOverSpace
;
2164 // Move all the children to vertically align the content
2165 // This doesn't take into account floating objects, unfortunately.
2168 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2171 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2173 child
->Move(wxPoint(child
->GetPosition().x
, child
->GetPosition().y
+ yOffset
));
2175 node
= node
->GetNext();
2180 m_invalidRange
= wxRICHTEXT_NONE
;
2185 /// Get/set the size for the given range.
2186 bool wxRichTextParagraphLayoutBox::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, const wxPoint
& position
, const wxSize
& parentSize
, wxArrayInt
* WXUNUSED(partialExtents
)) const
2190 wxRichTextObjectList::compatibility_iterator startPara
= wxRichTextObjectList::compatibility_iterator();
2191 wxRichTextObjectList::compatibility_iterator endPara
= wxRichTextObjectList::compatibility_iterator();
2193 // First find the first paragraph whose starting position is within the range.
2194 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2197 // child is a paragraph
2198 wxRichTextObject
* child
= node
->GetData();
2199 const wxRichTextRange
& r
= child
->GetRange();
2201 if (r
.GetStart() <= range
.GetStart() && r
.GetEnd() >= range
.GetStart())
2207 node
= node
->GetNext();
2210 // Next find the last paragraph containing part of the range
2211 node
= m_children
.GetFirst();
2214 // child is a paragraph
2215 wxRichTextObject
* child
= node
->GetData();
2216 const wxRichTextRange
& r
= child
->GetRange();
2218 if (r
.GetStart() <= range
.GetEnd() && r
.GetEnd() >= range
.GetEnd())
2224 node
= node
->GetNext();
2227 if (!startPara
|| !endPara
)
2230 // Now we can add up the sizes
2231 for (node
= startPara
; node
; node
= node
->GetNext())
2233 // child is a paragraph
2234 wxRichTextObject
* child
= node
->GetData();
2235 const wxRichTextRange
& childRange
= child
->GetRange();
2236 wxRichTextRange rangeToFind
= range
;
2237 rangeToFind
.LimitTo(childRange
);
2239 if (child
->IsTopLevel())
2240 rangeToFind
= child
->GetOwnRange();
2244 int childDescent
= 0;
2245 child
->GetRangeSize(rangeToFind
, childSize
, childDescent
, dc
, context
, flags
, position
, parentSize
);
2247 descent
= wxMax(childDescent
, descent
);
2249 sz
.x
= wxMax(sz
.x
, childSize
.x
);
2250 sz
.y
+= childSize
.y
;
2252 if (node
== endPara
)
2261 /// Get the paragraph at the given position
2262 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphAtPosition(long pos
, bool caretPosition
) const
2267 // First find the first paragraph whose starting position is within the range.
2268 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2271 // child is a paragraph
2272 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2273 // wxASSERT (child != NULL);
2277 // Return first child in buffer if position is -1
2281 if (child
->GetRange().Contains(pos
))
2285 node
= node
->GetNext();
2290 /// Get the line at the given position
2291 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineAtPosition(long pos
, bool caretPosition
) const
2296 // First find the first paragraph whose starting position is within the range.
2297 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2300 wxRichTextObject
* obj
= (wxRichTextObject
*) node
->GetData();
2301 if (obj
->GetRange().Contains(pos
))
2303 // child is a paragraph
2304 wxRichTextParagraph
* child
= wxDynamicCast(obj
, wxRichTextParagraph
);
2305 // wxASSERT (child != NULL);
2309 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2312 wxRichTextLine
* line
= node2
->GetData();
2314 wxRichTextRange range
= line
->GetAbsoluteRange();
2316 if (range
.Contains(pos
) ||
2318 // If the position is end-of-paragraph, then return the last line of
2319 // of the paragraph.
2320 ((range
.GetEnd() == child
->GetRange().GetEnd()-1) && (pos
== child
->GetRange().GetEnd())))
2323 node2
= node2
->GetNext();
2328 node
= node
->GetNext();
2331 int lineCount
= GetLineCount();
2333 return GetLineForVisibleLineNumber(lineCount
-1);
2338 /// Get the line at the given y pixel position, or the last line.
2339 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineAtYPosition(int y
) const
2341 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2344 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2345 // wxASSERT (child != NULL);
2349 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2352 wxRichTextLine
* line
= node2
->GetData();
2354 wxRect
rect(line
->GetRect());
2356 if (y
<= rect
.GetBottom())
2359 node2
= node2
->GetNext();
2363 node
= node
->GetNext();
2367 int lineCount
= GetLineCount();
2369 return GetLineForVisibleLineNumber(lineCount
-1);
2374 /// Get the number of visible lines
2375 int wxRichTextParagraphLayoutBox::GetLineCount() const
2379 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2382 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2383 // wxASSERT (child != NULL);
2386 count
+= child
->GetLines().GetCount();
2388 node
= node
->GetNext();
2394 /// Get the paragraph for a given line
2395 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphForLine(wxRichTextLine
* line
) const
2397 return GetParagraphAtPosition(line
->GetAbsoluteRange().GetStart());
2400 /// Get the line size at the given position
2401 wxSize
wxRichTextParagraphLayoutBox::GetLineSizeAtPosition(long pos
, bool caretPosition
) const
2403 wxRichTextLine
* line
= GetLineAtPosition(pos
, caretPosition
);
2406 return line
->GetSize();
2409 return wxSize(0, 0);
2413 /// Convenience function to add a paragraph of text
2414 wxRichTextRange
wxRichTextParagraphLayoutBox::AddParagraph(const wxString
& text
, wxRichTextAttr
* paraStyle
)
2416 // Don't use the base style, just the default style, and the base style will
2417 // be combined at display time.
2418 // Divide into paragraph and character styles.
2420 wxRichTextAttr defaultCharStyle
;
2421 wxRichTextAttr defaultParaStyle
;
2423 // If the default style is a named paragraph style, don't apply any character formatting
2424 // to the initial text string.
2425 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2427 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2429 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2432 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2434 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2435 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2437 wxRichTextParagraph
* para
= new wxRichTextParagraph(text
, this, pStyle
, cStyle
);
2438 para
->GetAttributes().GetTextBoxAttr().Reset();
2444 return para
->GetRange();
2447 /// Adds multiple paragraphs, based on newlines.
2448 wxRichTextRange
wxRichTextParagraphLayoutBox::AddParagraphs(const wxString
& text
, wxRichTextAttr
* paraStyle
)
2450 // Don't use the base style, just the default style, and the base style will
2451 // be combined at display time.
2452 // Divide into paragraph and character styles.
2454 wxRichTextAttr defaultCharStyle
;
2455 wxRichTextAttr defaultParaStyle
;
2457 // If the default style is a named paragraph style, don't apply any character formatting
2458 // to the initial text string.
2459 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2461 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2463 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2466 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2468 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2469 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2471 wxRichTextParagraph
* firstPara
= NULL
;
2472 wxRichTextParagraph
* lastPara
= NULL
;
2474 wxRichTextRange
range(-1, -1);
2477 size_t len
= text
.length();
2479 wxRichTextParagraph
* para
= new wxRichTextParagraph(wxEmptyString
, this, pStyle
, cStyle
);
2480 para
->GetAttributes().GetTextBoxAttr().Reset();
2489 wxChar ch
= text
[i
];
2490 if (ch
== wxT('\n') || ch
== wxT('\r'))
2494 wxRichTextPlainText
* plainText
= (wxRichTextPlainText
*) para
->GetChildren().GetFirst()->GetData();
2495 plainText
->SetText(line
);
2497 para
= new wxRichTextParagraph(wxEmptyString
, this, pStyle
, cStyle
);
2498 para
->GetAttributes().GetTextBoxAttr().Reset();
2503 line
= wxEmptyString
;
2514 wxRichTextPlainText
* plainText
= (wxRichTextPlainText
*) para
->GetChildren().GetFirst()->GetData();
2515 plainText
->SetText(line
);
2520 return wxRichTextRange(firstPara
->GetRange().GetStart(), lastPara
->GetRange().GetEnd());
2523 /// Convenience function to add an image
2524 wxRichTextRange
wxRichTextParagraphLayoutBox::AddImage(const wxImage
& image
, wxRichTextAttr
* paraStyle
)
2526 // Don't use the base style, just the default style, and the base style will
2527 // be combined at display time.
2528 // Divide into paragraph and character styles.
2530 wxRichTextAttr defaultCharStyle
;
2531 wxRichTextAttr defaultParaStyle
;
2533 // If the default style is a named paragraph style, don't apply any character formatting
2534 // to the initial text string.
2535 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2537 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2539 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2542 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2544 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2545 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2547 wxRichTextParagraph
* para
= new wxRichTextParagraph(this, pStyle
);
2548 para
->GetAttributes().GetTextBoxAttr().Reset();
2550 para
->AppendChild(new wxRichTextImage(image
, this, cStyle
));
2554 return para
->GetRange();
2558 /// Insert fragment into this box at the given position. If partialParagraph is true,
2559 /// it is assumed that the last (or only) paragraph is just a piece of data with no paragraph
2562 bool wxRichTextParagraphLayoutBox::InsertFragment(long position
, wxRichTextParagraphLayoutBox
& fragment
)
2564 // First, find the first paragraph whose starting position is within the range.
2565 wxRichTextParagraph
* para
= GetParagraphAtPosition(position
);
2568 wxRichTextAttr originalAttr
= para
->GetAttributes();
2570 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(para
);
2572 // Now split at this position, returning the object to insert the new
2573 // ones in front of.
2574 wxRichTextObject
* nextObject
= para
->SplitAt(position
);
2576 // Special case: partial paragraph, just one paragraph. Might be a small amount of
2577 // text, for example, so let's optimize.
2579 if (fragment
.GetPartialParagraph() && fragment
.GetChildren().GetCount() == 1)
2581 // Add the first para to this para...
2582 wxRichTextObjectList::compatibility_iterator firstParaNode
= fragment
.GetChildren().GetFirst();
2586 // Iterate through the fragment paragraph inserting the content into this paragraph.
2587 wxRichTextParagraph
* firstPara
= wxDynamicCast(firstParaNode
->GetData(), wxRichTextParagraph
);
2588 wxASSERT (firstPara
!= NULL
);
2590 wxRichTextObjectList::compatibility_iterator objectNode
= firstPara
->GetChildren().GetFirst();
2593 wxRichTextObject
* newObj
= objectNode
->GetData()->Clone();
2598 para
->AppendChild(newObj
);
2602 // Insert before nextObject
2603 para
->InsertChild(newObj
, nextObject
);
2606 objectNode
= objectNode
->GetNext();
2613 // Procedure for inserting a fragment consisting of a number of
2616 // 1. Remove and save the content that's after the insertion point, for adding
2617 // back once we've added the fragment.
2618 // 2. Add the content from the first fragment paragraph to the current
2620 // 3. Add remaining fragment paragraphs after the current paragraph.
2621 // 4. Add back the saved content from the first paragraph. If partialParagraph
2622 // is true, add it to the last paragraph added and not a new one.
2624 // 1. Remove and save objects after split point.
2625 wxList savedObjects
;
2627 para
->MoveToList(nextObject
, savedObjects
);
2629 // 2. Add the content from the 1st fragment paragraph.
2630 wxRichTextObjectList::compatibility_iterator firstParaNode
= fragment
.GetChildren().GetFirst();
2634 wxRichTextParagraph
* firstPara
= wxDynamicCast(firstParaNode
->GetData(), wxRichTextParagraph
);
2635 wxASSERT(firstPara
!= NULL
);
2637 if (!(fragment
.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
))
2638 para
->SetAttributes(firstPara
->GetAttributes());
2640 // Save empty paragraph attributes for appending later
2641 // These are character attributes deliberately set for a new paragraph. Without this,
2642 // we couldn't pass default attributes when appending a new paragraph.
2643 wxRichTextAttr emptyParagraphAttributes
;
2645 wxRichTextObjectList::compatibility_iterator objectNode
= firstPara
->GetChildren().GetFirst();
2647 if (objectNode
&& firstPara
->GetChildren().GetCount() == 1 && objectNode
->GetData()->IsEmpty())
2648 emptyParagraphAttributes
= objectNode
->GetData()->GetAttributes();
2652 wxRichTextObject
* newObj
= objectNode
->GetData()->Clone();
2655 para
->AppendChild(newObj
);
2657 objectNode
= objectNode
->GetNext();
2660 // 3. Add remaining fragment paragraphs after the current paragraph.
2661 wxRichTextObjectList::compatibility_iterator nextParagraphNode
= node
->GetNext();
2662 wxRichTextObject
* nextParagraph
= NULL
;
2663 if (nextParagraphNode
)
2664 nextParagraph
= nextParagraphNode
->GetData();
2666 wxRichTextObjectList::compatibility_iterator i
= fragment
.GetChildren().GetFirst()->GetNext();
2667 wxRichTextParagraph
* finalPara
= para
;
2669 bool needExtraPara
= (!i
|| !fragment
.GetPartialParagraph());
2671 // If there was only one paragraph, we need to insert a new one.
2674 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2675 wxASSERT( para
!= NULL
);
2677 finalPara
= (wxRichTextParagraph
*) para
->Clone();
2680 InsertChild(finalPara
, nextParagraph
);
2682 AppendChild(finalPara
);
2687 // If there was only one paragraph, or we have full paragraphs in our fragment,
2688 // we need to insert a new one.
2691 finalPara
= new wxRichTextParagraph
;
2694 InsertChild(finalPara
, nextParagraph
);
2696 AppendChild(finalPara
);
2699 // 4. Add back the remaining content.
2703 finalPara
->MoveFromList(savedObjects
);
2705 // Ensure there's at least one object
2706 if (finalPara
->GetChildCount() == 0)
2708 wxRichTextPlainText
* text
= new wxRichTextPlainText(wxEmptyString
);
2709 text
->SetAttributes(emptyParagraphAttributes
);
2711 finalPara
->AppendChild(text
);
2715 if ((fragment
.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
) && firstPara
)
2716 finalPara
->SetAttributes(firstPara
->GetAttributes());
2717 else if (finalPara
&& finalPara
!= para
)
2718 finalPara
->SetAttributes(originalAttr
);
2726 wxRichTextObjectList::compatibility_iterator i
= fragment
.GetChildren().GetFirst();
2729 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2730 wxASSERT( para
!= NULL
);
2732 AppendChild(para
->Clone());
2741 /// Make a copy of the fragment corresponding to the given range, putting it in 'fragment'.
2742 /// If there was an incomplete paragraph at the end, partialParagraph is set to true.
2743 bool wxRichTextParagraphLayoutBox::CopyFragment(const wxRichTextRange
& range
, wxRichTextParagraphLayoutBox
& fragment
)
2745 wxRichTextObjectList::compatibility_iterator i
= GetChildren().GetFirst();
2748 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2749 wxASSERT( para
!= NULL
);
2751 if (!para
->GetRange().IsOutside(range
))
2753 fragment
.AppendChild(para
->Clone());
2758 // Now top and tail the first and last paragraphs in our new fragment (which might be the same).
2759 if (!fragment
.IsEmpty())
2761 wxRichTextParagraph
* firstPara
= wxDynamicCast(fragment
.GetChildren().GetFirst()->GetData(), wxRichTextParagraph
);
2762 wxASSERT( firstPara
!= NULL
);
2764 wxRichTextParagraph
* lastPara
= wxDynamicCast(fragment
.GetChildren().GetLast()->GetData(), wxRichTextParagraph
);
2765 wxASSERT( lastPara
!= NULL
);
2767 if (!firstPara
|| !lastPara
)
2770 bool isFragment
= (range
.GetEnd() < lastPara
->GetRange().GetEnd());
2772 long firstPos
= firstPara
->GetRange().GetStart();
2774 // Adjust for renumbering from zero
2775 wxRichTextRange
topTailRange(range
.GetStart() - firstPos
, range
.GetEnd() - firstPos
);
2778 fragment
.CalculateRange(0, end
);
2780 // Chop off the start of the paragraph
2781 if (topTailRange
.GetStart() > 0)
2783 wxRichTextRange
r(0, topTailRange
.GetStart()-1);
2784 firstPara
->DeleteRange(r
);
2786 // Make sure the numbering is correct
2787 fragment
.CalculateRange(0, end
);
2789 // Now, we've deleted some positions, so adjust the range
2791 topTailRange
.SetStart(range
.GetLength());
2792 topTailRange
.SetEnd(fragment
.GetOwnRange().GetEnd());
2796 topTailRange
.SetStart(range
.GetLength());
2797 topTailRange
.SetEnd(fragment
.GetOwnRange().GetEnd());
2800 if (topTailRange
.GetStart() < lastPara
->GetRange().GetEnd())
2802 lastPara
->DeleteRange(topTailRange
);
2804 // Make sure the numbering is correct
2806 fragment
.CalculateRange(0, end
);
2808 // We only have part of a paragraph at the end
2809 fragment
.SetPartialParagraph(true);
2813 // We have a partial paragraph (don't save last new paragraph marker)
2814 // or complete paragraph
2815 fragment
.SetPartialParagraph(isFragment
);
2822 /// Given a position, get the number of the visible line (potentially many to a paragraph),
2823 /// starting from zero at the start of the buffer.
2824 long wxRichTextParagraphLayoutBox::GetVisibleLineNumber(long pos
, bool caretPosition
, bool startOfLine
) const
2831 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2834 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2835 // wxASSERT( child != NULL );
2839 if (child
->GetRange().Contains(pos
))
2841 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2844 wxRichTextLine
* line
= node2
->GetData();
2845 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
2847 if (lineRange
.Contains(pos
) || pos
== lineRange
.GetStart())
2849 // If the caret is displayed at the end of the previous wrapped line,
2850 // we want to return the line it's _displayed_ at (not the actual line
2851 // containing the position).
2852 if (lineRange
.GetStart() == pos
&& !startOfLine
&& child
->GetRange().GetStart() != pos
)
2853 return lineCount
- 1;
2860 node2
= node2
->GetNext();
2862 // If we didn't find it in the lines, it must be
2863 // the last position of the paragraph. So return the last line.
2867 lineCount
+= child
->GetLines().GetCount();
2870 node
= node
->GetNext();
2877 /// Given a line number, get the corresponding wxRichTextLine object.
2878 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineForVisibleLineNumber(long lineNumber
) const
2882 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2885 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2886 // wxASSERT(child != NULL);
2890 if (lineNumber
< (int) (child
->GetLines().GetCount() + lineCount
))
2892 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2895 wxRichTextLine
* line
= node2
->GetData();
2897 if (lineCount
== lineNumber
)
2902 node2
= node2
->GetNext();
2906 lineCount
+= child
->GetLines().GetCount();
2909 node
= node
->GetNext();
2916 /// Delete range from layout.
2917 bool wxRichTextParagraphLayoutBox::DeleteRange(const wxRichTextRange
& range
)
2919 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2921 wxRichTextParagraph
* firstPara
= NULL
;
2924 wxRichTextParagraph
* obj
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2925 // wxASSERT (obj != NULL);
2927 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
2931 // Delete the range in each paragraph
2933 if (!obj
->GetRange().IsOutside(range
))
2935 // Deletes the content of this object within the given range
2936 obj
->DeleteRange(range
);
2938 wxRichTextRange thisRange
= obj
->GetRange();
2939 wxRichTextAttr thisAttr
= obj
->GetAttributes();
2941 // If the whole paragraph is within the range to delete,
2942 // delete the whole thing.
2943 if (range
.GetStart() <= thisRange
.GetStart() && range
.GetEnd() >= thisRange
.GetEnd())
2945 // Delete the whole object
2946 RemoveChild(obj
, true);
2949 else if (!firstPara
)
2952 // If the range includes the paragraph end, we need to join this
2953 // and the next paragraph.
2954 if (range
.GetEnd() <= thisRange
.GetEnd())
2956 // We need to move the objects from the next paragraph
2957 // to this paragraph
2959 wxRichTextParagraph
* nextParagraph
= NULL
;
2960 if ((range
.GetEnd() < thisRange
.GetEnd()) && obj
)
2961 nextParagraph
= obj
;
2964 // We're ending at the end of the paragraph, so merge the _next_ paragraph.
2966 nextParagraph
= wxDynamicCast(next
->GetData(), wxRichTextParagraph
);
2969 bool applyFinalParagraphStyle
= firstPara
&& nextParagraph
&& nextParagraph
!= firstPara
;
2971 wxRichTextAttr nextParaAttr
;
2972 if (applyFinalParagraphStyle
)
2974 // Special case when deleting the end of a paragraph - use _this_ paragraph's style,
2975 // not the next one.
2976 if (range
.GetStart() == range
.GetEnd() && range
.GetStart() == thisRange
.GetEnd())
2977 nextParaAttr
= thisAttr
;
2979 nextParaAttr
= nextParagraph
->GetAttributes();
2982 if (firstPara
&& nextParagraph
&& firstPara
!= nextParagraph
)
2984 // Move the objects to the previous para
2985 wxRichTextObjectList::compatibility_iterator node1
= nextParagraph
->GetChildren().GetFirst();
2989 wxRichTextObject
* obj1
= node1
->GetData();
2991 firstPara
->AppendChild(obj1
);
2993 wxRichTextObjectList::compatibility_iterator next1
= node1
->GetNext();
2994 nextParagraph
->GetChildren().Erase(node1
);
2999 // Delete the paragraph
3000 RemoveChild(nextParagraph
, true);
3003 // Avoid empty paragraphs
3004 if (firstPara
&& firstPara
->GetChildren().GetCount() == 0)
3006 wxRichTextPlainText
* text
= new wxRichTextPlainText(wxEmptyString
);
3007 firstPara
->AppendChild(text
);
3010 if (applyFinalParagraphStyle
)
3011 firstPara
->SetAttributes(nextParaAttr
);
3024 /// Get any text in this object for the given range
3025 wxString
wxRichTextParagraphLayoutBox::GetTextForRange(const wxRichTextRange
& range
) const
3029 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3032 wxRichTextObject
* child
= node
->GetData();
3033 if (!child
->GetRange().IsOutside(range
))
3035 wxRichTextRange childRange
= range
;
3036 childRange
.LimitTo(child
->GetRange());
3038 wxString childText
= child
->GetTextForRange(childRange
);
3042 if ((childRange
.GetEnd() == child
->GetRange().GetEnd()) && node
->GetNext())
3047 node
= node
->GetNext();
3053 /// Get all the text
3054 wxString
wxRichTextParagraphLayoutBox::GetText() const
3056 return GetTextForRange(GetOwnRange());
3059 /// Get the paragraph by number
3060 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphAtLine(long paragraphNumber
) const
3062 if ((size_t) paragraphNumber
>= GetChildCount())
3065 return (wxRichTextParagraph
*) GetChild((size_t) paragraphNumber
);
3068 /// Get the length of the paragraph
3069 int wxRichTextParagraphLayoutBox::GetParagraphLength(long paragraphNumber
) const
3071 wxRichTextParagraph
* para
= GetParagraphAtLine(paragraphNumber
);
3073 return para
->GetRange().GetLength() - 1; // don't include newline
3078 /// Get the text of the paragraph
3079 wxString
wxRichTextParagraphLayoutBox::GetParagraphText(long paragraphNumber
) const
3081 wxRichTextParagraph
* para
= GetParagraphAtLine(paragraphNumber
);
3083 return para
->GetTextForRange(para
->GetRange());
3085 return wxEmptyString
;
3088 /// Convert zero-based line column and paragraph number to a position.
3089 long wxRichTextParagraphLayoutBox::XYToPosition(long x
, long y
) const
3091 wxRichTextParagraph
* para
= GetParagraphAtLine(y
);
3094 return para
->GetRange().GetStart() + x
;
3100 /// Convert zero-based position to line column and paragraph number
3101 bool wxRichTextParagraphLayoutBox::PositionToXY(long pos
, long* x
, long* y
) const
3103 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
);
3107 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3110 wxRichTextObject
* child
= node
->GetData();
3114 node
= node
->GetNext();
3118 *x
= pos
- para
->GetRange().GetStart();
3126 /// Get the leaf object in a paragraph at this position.
3127 /// Given a line number, get the corresponding wxRichTextLine object.
3128 wxRichTextObject
* wxRichTextParagraphLayoutBox::GetLeafObjectAtPosition(long position
) const
3130 wxRichTextParagraph
* para
= GetParagraphAtPosition(position
);
3133 wxRichTextObjectList::compatibility_iterator node
= para
->GetChildren().GetFirst();
3137 wxRichTextObject
* child
= node
->GetData();
3138 if (child
->GetRange().Contains(position
))
3141 node
= node
->GetNext();
3143 if (position
== para
->GetRange().GetEnd() && para
->GetChildCount() > 0)
3144 return para
->GetChildren().GetLast()->GetData();
3149 /// Set character or paragraph text attributes: apply character styles only to immediate text nodes
3150 bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange
& range
, const wxRichTextAttr
& style
, int flags
)
3152 bool characterStyle
= false;
3153 bool paragraphStyle
= false;
3155 if (style
.IsCharacterStyle())
3156 characterStyle
= true;
3157 if (style
.IsParagraphStyle())
3158 paragraphStyle
= true;
3160 wxRichTextBuffer
* buffer
= GetBuffer();
3162 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
3163 bool applyMinimal
= ((flags
& wxRICHTEXT_SETSTYLE_OPTIMIZE
) != 0);
3164 bool parasOnly
= ((flags
& wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY
) != 0);
3165 bool charactersOnly
= ((flags
& wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY
) != 0);
3166 bool resetExistingStyle
= ((flags
& wxRICHTEXT_SETSTYLE_RESET
) != 0);
3167 bool removeStyle
= ((flags
& wxRICHTEXT_SETSTYLE_REMOVE
) != 0);
3169 // Apply paragraph style first, if any
3170 wxRichTextAttr
wholeStyle(style
);
3172 if (!removeStyle
&& wholeStyle
.HasParagraphStyleName() && buffer
->GetStyleSheet())
3174 wxRichTextParagraphStyleDefinition
* def
= buffer
->GetStyleSheet()->FindParagraphStyle(wholeStyle
.GetParagraphStyleName());
3176 wxRichTextApplyStyle(wholeStyle
, def
->GetStyleMergedWithBase(buffer
->GetStyleSheet()));
3179 // Limit the attributes to be set to the content to only character attributes.
3180 wxRichTextAttr
characterAttributes(wholeStyle
);
3181 characterAttributes
.SetFlags(characterAttributes
.GetFlags() & (wxTEXT_ATTR_CHARACTER
));
3183 if (!removeStyle
&& characterAttributes
.HasCharacterStyleName() && buffer
->GetStyleSheet())
3185 wxRichTextCharacterStyleDefinition
* def
= buffer
->GetStyleSheet()->FindCharacterStyle(characterAttributes
.GetCharacterStyleName());
3187 wxRichTextApplyStyle(characterAttributes
, def
->GetStyleMergedWithBase(buffer
->GetStyleSheet()));
3190 // If we are associated with a control, make undoable; otherwise, apply immediately
3193 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3195 wxRichTextAction
* action
= NULL
;
3197 if (haveControl
&& withUndo
)
3199 action
= new wxRichTextAction(NULL
, _("Change Style"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
3200 action
->SetRange(range
);
3201 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3204 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3207 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3208 // wxASSERT (para != NULL);
3210 if (para
&& para
->GetChildCount() > 0)
3212 // Stop searching if we're beyond the range of interest
3213 if (para
->GetRange().GetStart() > range
.GetEnd())
3216 if (!para
->GetRange().IsOutside(range
))
3218 // We'll be using a copy of the paragraph to make style changes,
3219 // not updating the buffer directly.
3220 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3222 if (haveControl
&& withUndo
)
3224 newPara
= new wxRichTextParagraph(*para
);
3225 action
->GetNewParagraphs().AppendChild(newPara
);
3227 // Also store the old ones for Undo
3228 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3233 // If we're specifying paragraphs only, then we really mean character formatting
3234 // to be included in the paragraph style
3235 if ((paragraphStyle
|| parasOnly
) && !charactersOnly
)
3239 // Removes the given style from the paragraph
3240 wxRichTextRemoveStyle(newPara
->GetAttributes(), style
);
3242 else if (resetExistingStyle
)
3243 newPara
->GetAttributes() = wholeStyle
;
3248 // Only apply attributes that will make a difference to the combined
3249 // style as seen on the display
3250 wxRichTextAttr
combinedAttr(para
->GetCombinedAttributes(true));
3251 wxRichTextApplyStyle(newPara
->GetAttributes(), wholeStyle
, & combinedAttr
);
3254 wxRichTextApplyStyle(newPara
->GetAttributes(), wholeStyle
);
3258 // When applying paragraph styles dynamically, don't change the text objects' attributes
3259 // since they will computed as needed. Only apply the character styling if it's _only_
3260 // character styling. This policy is subject to change and might be put under user control.
3262 // Hm. we might well be applying a mix of paragraph and character styles, in which
3263 // case we _do_ want to apply character styles regardless of what para styles are set.
3264 // But if we're applying a paragraph style, which has some character attributes, but
3265 // we only want the paragraphs to hold this character style, then we _don't_ want to
3266 // apply the character style. So we need to be able to choose.
3268 if (!parasOnly
&& (characterStyle
|charactersOnly
) && range
.GetStart() != newPara
->GetRange().GetEnd())
3270 wxRichTextRange
childRange(range
);
3271 childRange
.LimitTo(newPara
->GetRange());
3273 // Find the starting position and if necessary split it so
3274 // we can start applying a different style.
3275 // TODO: check that the style actually changes or is different
3276 // from style outside of range
3277 wxRichTextObject
* firstObject
wxDUMMY_INITIALIZE(NULL
);
3278 wxRichTextObject
* lastObject
wxDUMMY_INITIALIZE(NULL
);
3280 if (childRange
.GetStart() == newPara
->GetRange().GetStart())
3281 firstObject
= newPara
->GetChildren().GetFirst()->GetData();
3283 firstObject
= newPara
->SplitAt(range
.GetStart());
3285 // Increment by 1 because we're apply the style one _after_ the split point
3286 long splitPoint
= childRange
.GetEnd();
3287 if (splitPoint
!= newPara
->GetRange().GetEnd())
3291 if (splitPoint
== newPara
->GetRange().GetEnd())
3292 lastObject
= newPara
->GetChildren().GetLast()->GetData();
3294 // lastObject is set as a side-effect of splitting. It's
3295 // returned as the object before the new object.
3296 (void) newPara
->SplitAt(splitPoint
, & lastObject
);
3298 wxASSERT(firstObject
!= NULL
);
3299 wxASSERT(lastObject
!= NULL
);
3301 if (!firstObject
|| !lastObject
)
3304 wxRichTextObjectList::compatibility_iterator firstNode
= newPara
->GetChildren().Find(firstObject
);
3305 wxRichTextObjectList::compatibility_iterator lastNode
= newPara
->GetChildren().Find(lastObject
);
3307 wxASSERT(firstNode
);
3310 wxRichTextObjectList::compatibility_iterator node2
= firstNode
;
3314 wxRichTextObject
* child
= node2
->GetData();
3318 // Removes the given style from the paragraph
3319 wxRichTextRemoveStyle(child
->GetAttributes(), style
);
3321 else if (resetExistingStyle
)
3322 child
->GetAttributes() = characterAttributes
;
3327 // Only apply attributes that will make a difference to the combined
3328 // style as seen on the display
3329 wxRichTextAttr
combinedAttr(newPara
->GetCombinedAttributes(child
->GetAttributes(), true));
3330 wxRichTextApplyStyle(child
->GetAttributes(), characterAttributes
, & combinedAttr
);
3333 wxRichTextApplyStyle(child
->GetAttributes(), characterAttributes
);
3336 if (node2
== lastNode
)
3339 node2
= node2
->GetNext();
3345 node
= node
->GetNext();
3348 // Do action, or delay it until end of batch.
3349 if (haveControl
&& withUndo
)
3350 buffer
->SubmitAction(action
);
3355 // Just change the attributes for this single object.
3356 void wxRichTextParagraphLayoutBox::SetStyle(wxRichTextObject
* obj
, const wxRichTextAttr
& textAttr
, int flags
)
3358 wxRichTextBuffer
* buffer
= GetBuffer();
3359 bool withUndo
= flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
;
3360 bool resetExistingStyle
= ((flags
& wxRICHTEXT_SETSTYLE_RESET
) != 0);
3361 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3363 wxRichTextAction
*action
= NULL
;
3364 wxRichTextAttr newAttr
= obj
->GetAttributes();
3365 if (resetExistingStyle
)
3368 newAttr
.Apply(textAttr
);
3370 if (haveControl
&& withUndo
)
3372 action
= new wxRichTextAction(NULL
, _("Change Object Style"), wxRICHTEXT_CHANGE_ATTRIBUTES
, buffer
, obj
->GetContainer(), buffer
->GetRichTextCtrl());
3373 action
->SetRange(obj
->GetRange().FromInternal());
3374 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3375 action
->MakeObject(obj
);
3377 action
->GetAttributes() = newAttr
;
3380 obj
->GetAttributes() = newAttr
;
3382 if (haveControl
&& withUndo
)
3383 buffer
->SubmitAction(action
);
3386 /// Get the text attributes for this position.
3387 bool wxRichTextParagraphLayoutBox::GetStyle(long position
, wxRichTextAttr
& style
)
3389 return DoGetStyle(position
, style
, true);
3392 bool wxRichTextParagraphLayoutBox::GetUncombinedStyle(long position
, wxRichTextAttr
& style
)
3394 return DoGetStyle(position
, style
, false);
3397 /// Implementation helper for GetStyle. If combineStyles is true, combine base, paragraph and
3398 /// context attributes.
3399 bool wxRichTextParagraphLayoutBox::DoGetStyle(long position
, wxRichTextAttr
& style
, bool combineStyles
)
3401 wxRichTextObject
* obj
wxDUMMY_INITIALIZE(NULL
);
3403 if (style
.IsParagraphStyle())
3405 obj
= GetParagraphAtPosition(position
);
3410 // Start with the base style
3411 style
= GetAttributes();
3412 style
.GetTextBoxAttr().Reset();
3414 // Apply the paragraph style
3415 wxRichTextApplyStyle(style
, obj
->GetAttributes());
3418 style
= obj
->GetAttributes();
3425 obj
= GetLeafObjectAtPosition(position
);
3430 wxRichTextParagraph
* para
= wxDynamicCast(obj
->GetParent(), wxRichTextParagraph
);
3431 style
= para
? para
->GetCombinedAttributes(obj
->GetAttributes()) : obj
->GetAttributes();
3434 style
= obj
->GetAttributes();
3442 static bool wxHasStyle(long flags
, long style
)
3444 return (flags
& style
) != 0;
3447 /// Combines 'style' with 'currentStyle' for the purpose of summarising the attributes of a range of
3449 bool wxRichTextParagraphLayoutBox::CollectStyle(wxRichTextAttr
& currentStyle
, const wxRichTextAttr
& style
, wxRichTextAttr
& clashingAttr
, wxRichTextAttr
& absentAttr
)
3451 currentStyle
.CollectCommonAttributes(style
, clashingAttr
, absentAttr
);
3456 /// Get the combined style for a range - if any attribute is different within the range,
3457 /// that attribute is not present within the flags.
3458 /// *** Note that this is not recursive, and so assumes that content inside a paragraph is not itself
3460 bool wxRichTextParagraphLayoutBox::GetStyleForRange(const wxRichTextRange
& range
, wxRichTextAttr
& style
)
3462 style
= wxRichTextAttr();
3464 wxRichTextAttr clashingAttrPara
, clashingAttrChar
;
3465 wxRichTextAttr absentAttrPara
, absentAttrChar
;
3467 wxRichTextObjectList::compatibility_iterator node
= GetChildren().GetFirst();
3470 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3471 if (para
&& !(para
->GetRange().GetStart() > range
.GetEnd() || para
->GetRange().GetEnd() < range
.GetStart()))
3473 if (para
->GetChildren().GetCount() == 0)
3475 wxRichTextAttr paraStyle
= para
->GetCombinedAttributes(true /* use box attributes */);
3477 CollectStyle(style
, paraStyle
, clashingAttrPara
, absentAttrPara
);
3481 wxRichTextRange
paraRange(para
->GetRange());
3482 paraRange
.LimitTo(range
);
3484 // First collect paragraph attributes only
3485 wxRichTextAttr paraStyle
= para
->GetCombinedAttributes();
3486 paraStyle
.SetFlags(paraStyle
.GetFlags() & wxTEXT_ATTR_PARAGRAPH
);
3487 CollectStyle(style
, paraStyle
, clashingAttrPara
, absentAttrPara
);
3489 wxRichTextObjectList::compatibility_iterator childNode
= para
->GetChildren().GetFirst();
3493 wxRichTextObject
* child
= childNode
->GetData();
3494 if (!(child
->GetRange().GetStart() > range
.GetEnd() || child
->GetRange().GetEnd() < range
.GetStart()))
3496 wxRichTextAttr childStyle
= para
->GetCombinedAttributes(child
->GetAttributes(), true /* include box attributes */);
3498 // Now collect character attributes only
3499 childStyle
.SetFlags(childStyle
.GetFlags() & wxTEXT_ATTR_CHARACTER
);
3501 CollectStyle(style
, childStyle
, clashingAttrChar
, absentAttrChar
);
3504 childNode
= childNode
->GetNext();
3508 node
= node
->GetNext();
3513 /// Set default style
3514 bool wxRichTextParagraphLayoutBox::SetDefaultStyle(const wxRichTextAttr
& style
)
3516 m_defaultAttributes
= style
;
3520 /// Test if this whole range has character attributes of the specified kind. If any
3521 /// of the attributes are different within the range, the test fails. You
3522 /// can use this to implement, for example, bold button updating. style must have
3523 /// flags indicating which attributes are of interest.
3524 bool wxRichTextParagraphLayoutBox::HasCharacterAttributes(const wxRichTextRange
& range
, const wxRichTextAttr
& style
) const
3527 int matchingCount
= 0;
3529 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3532 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3533 // wxASSERT (para != NULL);
3537 // Stop searching if we're beyond the range of interest
3538 if (para
->GetRange().GetStart() > range
.GetEnd())
3539 return foundCount
== matchingCount
&& foundCount
!= 0;
3541 if (!para
->GetRange().IsOutside(range
))
3543 wxRichTextObjectList::compatibility_iterator node2
= para
->GetChildren().GetFirst();
3547 wxRichTextObject
* child
= node2
->GetData();
3548 // Allow for empty string if no buffer
3549 wxRichTextRange childRange
= child
->GetRange();
3550 if (childRange
.GetLength() == 0 && GetRange().GetLength() == 1)
3551 childRange
.SetEnd(childRange
.GetEnd()+1);
3553 if (!childRange
.IsOutside(range
) && wxDynamicCast(child
, wxRichTextPlainText
))
3556 wxRichTextAttr textAttr
= para
->GetCombinedAttributes(child
->GetAttributes());
3558 if (textAttr
.EqPartial(style
, false /* strong test - attributes must be valid in both objects */))
3562 node2
= node2
->GetNext();
3567 node
= node
->GetNext();
3570 return foundCount
== matchingCount
&& foundCount
!= 0;
3573 /// Test if this whole range has paragraph attributes of the specified kind. If any
3574 /// of the attributes are different within the range, the test fails. You
3575 /// can use this to implement, for example, centering button updating. style must have
3576 /// flags indicating which attributes are of interest.
3577 bool wxRichTextParagraphLayoutBox::HasParagraphAttributes(const wxRichTextRange
& range
, const wxRichTextAttr
& style
) const
3580 int matchingCount
= 0;
3582 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3585 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3586 // wxASSERT (para != NULL);
3590 // Stop searching if we're beyond the range of interest
3591 if (para
->GetRange().GetStart() > range
.GetEnd())
3592 return foundCount
== matchingCount
&& foundCount
!= 0;
3594 if (!para
->GetRange().IsOutside(range
))
3596 wxRichTextAttr textAttr
= GetAttributes();
3597 // Apply the paragraph style
3598 wxRichTextApplyStyle(textAttr
, para
->GetAttributes());
3601 if (textAttr
.EqPartial(style
, false /* strong test */))
3606 node
= node
->GetNext();
3608 return foundCount
== matchingCount
&& foundCount
!= 0;
3611 void wxRichTextParagraphLayoutBox::PrepareContent(wxRichTextParagraphLayoutBox
& container
)
3613 wxRichTextBuffer
* buffer
= GetBuffer();
3614 if (buffer
&& buffer
->GetRichTextCtrl())
3615 buffer
->GetRichTextCtrl()->PrepareContent(container
);
3618 /// Set character or paragraph properties
3619 bool wxRichTextParagraphLayoutBox::SetProperties(const wxRichTextRange
& range
, const wxRichTextProperties
& properties
, int flags
)
3621 wxRichTextBuffer
* buffer
= GetBuffer();
3623 bool withUndo
= ((flags
& wxRICHTEXT_SETPROPERTIES_WITH_UNDO
) != 0);
3624 bool parasOnly
= ((flags
& wxRICHTEXT_SETPROPERTIES_PARAGRAPHS_ONLY
) != 0);
3625 bool charactersOnly
= ((flags
& wxRICHTEXT_SETPROPERTIES_CHARACTERS_ONLY
) != 0);
3626 bool resetExistingProperties
= ((flags
& wxRICHTEXT_SETPROPERTIES_RESET
) != 0);
3627 bool removeProperties
= ((flags
& wxRICHTEXT_SETPROPERTIES_REMOVE
) != 0);
3629 // If we are associated with a control, make undoable; otherwise, apply immediately
3632 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3634 wxRichTextAction
* action
= NULL
;
3636 if (haveControl
&& withUndo
)
3638 action
= new wxRichTextAction(NULL
, _("Change Properties"), wxRICHTEXT_CHANGE_PROPERTIES
, buffer
, this, buffer
->GetRichTextCtrl());
3639 action
->SetRange(range
);
3640 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3643 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3646 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3647 // wxASSERT (para != NULL);
3649 if (para
&& para
->GetChildCount() > 0)
3651 // Stop searching if we're beyond the range of interest
3652 if (para
->GetRange().GetStart() > range
.GetEnd())
3655 if (!para
->GetRange().IsOutside(range
))
3657 // We'll be using a copy of the paragraph to make style changes,
3658 // not updating the buffer directly.
3659 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3661 if (haveControl
&& withUndo
)
3663 newPara
= new wxRichTextParagraph(*para
);
3664 action
->GetNewParagraphs().AppendChild(newPara
);
3666 // Also store the old ones for Undo
3667 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3674 if (removeProperties
)
3676 // Removes the given style from the paragraph
3678 newPara
->GetProperties().RemoveProperties(properties
);
3680 else if (resetExistingProperties
)
3681 newPara
->GetProperties() = properties
;
3683 newPara
->GetProperties().MergeProperties(properties
);
3686 // When applying paragraph styles dynamically, don't change the text objects' attributes
3687 // since they will computed as needed. Only apply the character styling if it's _only_
3688 // character styling. This policy is subject to change and might be put under user control.
3690 // Hm. we might well be applying a mix of paragraph and character styles, in which
3691 // case we _do_ want to apply character styles regardless of what para styles are set.
3692 // But if we're applying a paragraph style, which has some character attributes, but
3693 // we only want the paragraphs to hold this character style, then we _don't_ want to
3694 // apply the character style. So we need to be able to choose.
3696 if (!parasOnly
&& charactersOnly
&& range
.GetStart() != newPara
->GetRange().GetEnd())
3698 wxRichTextRange
childRange(range
);
3699 childRange
.LimitTo(newPara
->GetRange());
3701 // Find the starting position and if necessary split it so
3702 // we can start applying different properties.
3703 // TODO: check that the properties actually change or are different
3704 // from properties outside of range
3705 wxRichTextObject
* firstObject
wxDUMMY_INITIALIZE(NULL
);
3706 wxRichTextObject
* lastObject
wxDUMMY_INITIALIZE(NULL
);
3708 if (childRange
.GetStart() == newPara
->GetRange().GetStart())
3709 firstObject
= newPara
->GetChildren().GetFirst()->GetData();
3711 firstObject
= newPara
->SplitAt(range
.GetStart());
3713 // Increment by 1 because we're apply the style one _after_ the split point
3714 long splitPoint
= childRange
.GetEnd();
3715 if (splitPoint
!= newPara
->GetRange().GetEnd())
3719 if (splitPoint
== newPara
->GetRange().GetEnd())
3720 lastObject
= newPara
->GetChildren().GetLast()->GetData();
3722 // lastObject is set as a side-effect of splitting. It's
3723 // returned as the object before the new object.
3724 (void) newPara
->SplitAt(splitPoint
, & lastObject
);
3726 wxASSERT(firstObject
!= NULL
);
3727 wxASSERT(lastObject
!= NULL
);
3729 if (!firstObject
|| !lastObject
)
3732 wxRichTextObjectList::compatibility_iterator firstNode
= newPara
->GetChildren().Find(firstObject
);
3733 wxRichTextObjectList::compatibility_iterator lastNode
= newPara
->GetChildren().Find(lastObject
);
3735 wxASSERT(firstNode
);
3738 wxRichTextObjectList::compatibility_iterator node2
= firstNode
;
3742 wxRichTextObject
* child
= node2
->GetData();
3744 if (removeProperties
)
3746 // Removes the given properties from the paragraph
3747 child
->GetProperties().RemoveProperties(properties
);
3749 else if (resetExistingProperties
)
3750 child
->GetProperties() = properties
;
3753 child
->GetProperties().MergeProperties(properties
);
3756 if (node2
== lastNode
)
3759 node2
= node2
->GetNext();
3765 node
= node
->GetNext();
3768 // Do action, or delay it until end of batch.
3769 if (haveControl
&& withUndo
)
3770 buffer
->SubmitAction(action
);
3775 void wxRichTextParagraphLayoutBox::Reset()
3779 wxRichTextBuffer
* buffer
= GetBuffer();
3780 if (buffer
&& buffer
->GetRichTextCtrl())
3782 wxRichTextEvent
event(wxEVT_COMMAND_RICHTEXT_BUFFER_RESET
, buffer
->GetRichTextCtrl()->GetId());
3783 event
.SetEventObject(buffer
->GetRichTextCtrl());
3784 event
.SetContainer(this);
3786 buffer
->SendEvent(event
, true);
3789 AddParagraph(wxEmptyString
);
3791 PrepareContent(*this);
3793 InvalidateHierarchy(wxRICHTEXT_ALL
);
3796 /// Invalidate the buffer. With no argument, invalidates whole buffer.
3797 void wxRichTextParagraphLayoutBox::Invalidate(const wxRichTextRange
& invalidRange
)
3799 wxRichTextCompositeObject::Invalidate(invalidRange
);
3801 DoInvalidate(invalidRange
);
3804 // Do the (in)validation for this object only
3805 void wxRichTextParagraphLayoutBox::DoInvalidate(const wxRichTextRange
& invalidRange
)
3807 if (invalidRange
== wxRICHTEXT_ALL
)
3809 m_invalidRange
= wxRICHTEXT_ALL
;
3811 // Already invalidating everything
3812 else if (m_invalidRange
== wxRICHTEXT_ALL
)
3817 if ((invalidRange
.GetStart() < m_invalidRange
.GetStart()) || m_invalidRange
.GetStart() == -1)
3818 m_invalidRange
.SetStart(invalidRange
.GetStart());
3819 if (invalidRange
.GetEnd() > m_invalidRange
.GetEnd())
3820 m_invalidRange
.SetEnd(invalidRange
.GetEnd());
3824 // Do the (in)validation both up and down the hierarchy
3825 void wxRichTextParagraphLayoutBox::InvalidateHierarchy(const wxRichTextRange
& invalidRange
)
3827 Invalidate(invalidRange
);
3829 if (invalidRange
!= wxRICHTEXT_NONE
)
3831 // Now go up the hierarchy
3832 wxRichTextObject
* thisObj
= this;
3833 wxRichTextObject
* p
= GetParent();
3836 wxRichTextParagraphLayoutBox
* l
= wxDynamicCast(p
, wxRichTextParagraphLayoutBox
);
3838 l
->DoInvalidate(thisObj
->GetRange());
3846 /// Get invalid range, rounding to entire paragraphs if argument is true.
3847 wxRichTextRange
wxRichTextParagraphLayoutBox::GetInvalidRange(bool wholeParagraphs
) const
3849 if (m_invalidRange
== wxRICHTEXT_ALL
|| m_invalidRange
== wxRICHTEXT_NONE
)
3850 return m_invalidRange
;
3852 wxRichTextRange range
= m_invalidRange
;
3854 if (wholeParagraphs
)
3856 wxRichTextParagraph
* para1
= GetParagraphAtPosition(range
.GetStart());
3858 range
.SetStart(para1
->GetRange().GetStart());
3860 // FIXME: be more intelligent about this. Check if we have floating objects
3861 // before the end of the range. But it's not clear how we can in general
3862 // tell where it's safe to stop laying out.
3863 // Anyway, this code is central to efficiency when laying in floating mode.
3864 if (!wxRichTextBuffer::GetFloatingLayoutMode())
3866 wxRichTextParagraph
* para2
= GetParagraphAtPosition(range
.GetEnd());
3868 range
.SetEnd(para2
->GetRange().GetEnd());
3871 // Floating layout means that all children should be laid out,
3872 // because we can't tell how the whole buffer will be affected.
3873 range
.SetEnd(GetOwnRange().GetEnd());
3878 /// Apply the style sheet to the buffer, for example if the styles have changed.
3879 bool wxRichTextParagraphLayoutBox::ApplyStyleSheet(wxRichTextStyleSheet
* styleSheet
)
3881 wxASSERT(styleSheet
!= NULL
);
3887 wxRichTextAttr
attr(GetBasicStyle());
3888 if (GetBasicStyle().HasParagraphStyleName())
3890 wxRichTextParagraphStyleDefinition
* paraDef
= styleSheet
->FindParagraphStyle(GetBasicStyle().GetParagraphStyleName());
3893 attr
.Apply(paraDef
->GetStyleMergedWithBase(styleSheet
));
3894 SetBasicStyle(attr
);
3899 if (GetBasicStyle().HasCharacterStyleName())
3901 wxRichTextCharacterStyleDefinition
* charDef
= styleSheet
->FindCharacterStyle(GetBasicStyle().GetCharacterStyleName());
3904 attr
.Apply(charDef
->GetStyleMergedWithBase(styleSheet
));
3905 SetBasicStyle(attr
);
3910 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3913 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3914 // wxASSERT (para != NULL);
3918 // Combine paragraph and list styles. If there is a list style in the original attributes,
3919 // the current indentation overrides anything else and is used to find the item indentation.
3920 // Also, for applying paragraph styles, consider having 2 modes: (1) we merge with what we have,
3921 // thereby taking into account all user changes, (2) reset the style completely (except for indentation/list
3922 // exception as above).
3923 // Problem: when changing from one list style to another, there's a danger that the level info will get lost.
3924 // So when changing a list style interactively, could retrieve level based on current style, then
3925 // set appropriate indent and apply new style.
3929 if (para
->GetAttributes().HasOutlineLevel())
3930 outline
= para
->GetAttributes().GetOutlineLevel();
3931 if (para
->GetAttributes().HasBulletNumber())
3932 num
= para
->GetAttributes().GetBulletNumber();
3934 if (!para
->GetAttributes().GetParagraphStyleName().IsEmpty() && !para
->GetAttributes().GetListStyleName().IsEmpty())
3936 int currentIndent
= para
->GetAttributes().GetLeftIndent();
3938 wxRichTextParagraphStyleDefinition
* paraDef
= styleSheet
->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
3939 wxRichTextListStyleDefinition
* listDef
= styleSheet
->FindListStyle(para
->GetAttributes().GetListStyleName());
3940 if (paraDef
&& !listDef
)
3942 para
->GetAttributes() = paraDef
->GetStyleMergedWithBase(styleSheet
);
3945 else if (listDef
&& !paraDef
)
3947 // Set overall style defined for the list style definition
3948 para
->GetAttributes() = listDef
->GetStyleMergedWithBase(styleSheet
);
3950 // Apply the style for this level
3951 wxRichTextApplyStyle(para
->GetAttributes(), * listDef
->GetLevelAttributes(listDef
->FindLevelForIndent(currentIndent
)));
3954 else if (listDef
&& paraDef
)
3956 // Combines overall list style, style for level, and paragraph style
3957 para
->GetAttributes() = listDef
->CombineWithParagraphStyle(currentIndent
, paraDef
->GetStyleMergedWithBase(styleSheet
));
3961 else if (para
->GetAttributes().GetParagraphStyleName().IsEmpty() && !para
->GetAttributes().GetListStyleName().IsEmpty())
3963 int currentIndent
= para
->GetAttributes().GetLeftIndent();
3965 wxRichTextListStyleDefinition
* listDef
= styleSheet
->FindListStyle(para
->GetAttributes().GetListStyleName());
3967 // Overall list definition style
3968 para
->GetAttributes() = listDef
->GetStyleMergedWithBase(styleSheet
);
3970 // Style for this level
3971 wxRichTextApplyStyle(para
->GetAttributes(), * listDef
->GetLevelAttributes(listDef
->FindLevelForIndent(currentIndent
)));
3975 else if (!para
->GetAttributes().GetParagraphStyleName().IsEmpty() && para
->GetAttributes().GetListStyleName().IsEmpty())
3977 wxRichTextParagraphStyleDefinition
* def
= styleSheet
->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
3980 para
->GetAttributes() = def
->GetStyleMergedWithBase(styleSheet
);
3986 para
->GetAttributes().SetOutlineLevel(outline
);
3988 para
->GetAttributes().SetBulletNumber(num
);
3991 node
= node
->GetNext();
3993 return foundCount
!= 0;
3997 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
3999 wxRichTextBuffer
* buffer
= GetBuffer();
4000 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
4002 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
4003 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
4004 bool specifyLevel
= ((flags
& wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL
) != 0);
4005 bool renumber
= ((flags
& wxRICHTEXT_SETSTYLE_RENUMBER
) != 0);
4007 // Current number, if numbering
4010 wxASSERT (!specifyLevel
|| (specifyLevel
&& (specifiedLevel
>= 0)));
4012 // If we are associated with a control, make undoable; otherwise, apply immediately
4015 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
4017 wxRichTextAction
* action
= NULL
;
4019 if (haveControl
&& withUndo
)
4021 action
= new wxRichTextAction(NULL
, _("Change List Style"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
4022 action
->SetRange(range
);
4023 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
4026 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
4029 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
4030 // wxASSERT (para != NULL);
4032 if (para
&& para
->GetChildCount() > 0)
4034 // Stop searching if we're beyond the range of interest
4035 if (para
->GetRange().GetStart() > range
.GetEnd())
4038 if (!para
->GetRange().IsOutside(range
))
4040 // We'll be using a copy of the paragraph to make style changes,
4041 // not updating the buffer directly.
4042 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
4044 if (haveControl
&& withUndo
)
4046 newPara
= new wxRichTextParagraph(*para
);
4047 action
->GetNewParagraphs().AppendChild(newPara
);
4049 // Also store the old ones for Undo
4050 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
4057 int thisIndent
= newPara
->GetAttributes().GetLeftIndent();
4058 int thisLevel
= specifyLevel
? specifiedLevel
: def
->FindLevelForIndent(thisIndent
);
4060 // How is numbering going to work?
4061 // If we are renumbering, or numbering for the first time, we need to keep
4062 // track of the number for each level. But we might be simply applying a different
4064 // In Word, applying a style to several paragraphs, even if at different levels,
4065 // reverts the level back to the same one. So we could do the same here.
4066 // Renumbering will need to be done when we promote/demote a paragraph.
4068 // Apply the overall list style, and item style for this level
4069 wxRichTextAttr
listStyle(def
->GetCombinedStyleForLevel(thisLevel
, styleSheet
));
4070 wxRichTextApplyStyle(newPara
->GetAttributes(), listStyle
);
4072 // Now we need to do numbering
4073 // Preserve the existing list item continuation bullet style, if any
4074 if (para
->GetAttributes().HasBulletStyle() && (para
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
))
4075 newPara
->GetAttributes().SetBulletStyle(newPara
->GetAttributes().GetBulletStyle()|wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
);
4080 newPara
->GetAttributes().SetBulletNumber(n
);
4086 else if (!newPara
->GetAttributes().GetListStyleName().IsEmpty())
4088 // if def is NULL, remove list style, applying any associated paragraph style
4089 // to restore the attributes
4091 newPara
->GetAttributes().SetListStyleName(wxEmptyString
);
4092 newPara
->GetAttributes().SetLeftIndent(0, 0);
4093 newPara
->GetAttributes().SetBulletText(wxEmptyString
);
4094 newPara
->GetAttributes().SetBulletStyle(0);
4096 // Eliminate the main list-related attributes
4097 newPara
->GetAttributes().SetFlags(newPara
->GetAttributes().GetFlags() & ~wxTEXT_ATTR_LEFT_INDENT
& ~wxTEXT_ATTR_BULLET_STYLE
& ~wxTEXT_ATTR_BULLET_NUMBER
& ~wxTEXT_ATTR_BULLET_TEXT
& wxTEXT_ATTR_LIST_STYLE_NAME
);
4099 if (styleSheet
&& !newPara
->GetAttributes().GetParagraphStyleName().IsEmpty())
4101 wxRichTextParagraphStyleDefinition
* def
= styleSheet
->FindParagraphStyle(newPara
->GetAttributes().GetParagraphStyleName());
4104 newPara
->GetAttributes() = def
->GetStyleMergedWithBase(styleSheet
);
4111 node
= node
->GetNext();
4114 // Do action, or delay it until end of batch.
4115 if (haveControl
&& withUndo
)
4116 buffer
->SubmitAction(action
);
4121 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
)
4123 wxRichTextBuffer
* buffer
= GetBuffer();
4124 if (buffer
&& buffer
->GetStyleSheet())
4126 wxRichTextListStyleDefinition
* def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4128 return SetListStyle(range
, def
, flags
, startFrom
, specifiedLevel
);
4133 /// Clear list for given range
4134 bool wxRichTextParagraphLayoutBox::ClearListStyle(const wxRichTextRange
& range
, int flags
)
4136 return SetListStyle(range
, NULL
, flags
);
4139 /// Number/renumber any list elements in the given range
4140 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
4142 return DoNumberList(range
, range
, 0, def
, flags
, startFrom
, specifiedLevel
);
4145 /// Number/renumber any list elements in the given range. Also do promotion or demotion of items, if specified
4146 bool wxRichTextParagraphLayoutBox::DoNumberList(const wxRichTextRange
& range
, const wxRichTextRange
& promotionRange
, int promoteBy
,
4147 wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
4149 wxRichTextBuffer
* buffer
= GetBuffer();
4150 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
4152 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
4153 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
4155 bool specifyLevel
= ((flags
& wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL
) != 0);
4158 bool renumber
= ((flags
& wxRICHTEXT_SETSTYLE_RENUMBER
) != 0);
4160 // Max number of levels
4161 const int maxLevels
= 10;
4163 // The level we're looking at now
4164 int currentLevel
= -1;
4166 // The item number for each level
4167 int levels
[maxLevels
];
4170 // Reset all numbering
4171 for (i
= 0; i
< maxLevels
; i
++)
4173 if (startFrom
!= -1)
4174 levels
[i
] = startFrom
-1;
4175 else if (renumber
) // start again
4178 levels
[i
] = -1; // start from the number we found, if any
4182 wxASSERT(!specifyLevel
|| (specifyLevel
&& (specifiedLevel
>= 0)));
4185 // If we are associated with a control, make undoable; otherwise, apply immediately
4188 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
4190 wxRichTextAction
* action
= NULL
;
4192 if (haveControl
&& withUndo
)
4194 action
= new wxRichTextAction(NULL
, _("Renumber List"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
4195 action
->SetRange(range
);
4196 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
4199 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
4202 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
4203 // wxASSERT (para != NULL);
4205 if (para
&& para
->GetChildCount() > 0)
4207 // Stop searching if we're beyond the range of interest
4208 if (para
->GetRange().GetStart() > range
.GetEnd())
4211 if (!para
->GetRange().IsOutside(range
))
4213 // We'll be using a copy of the paragraph to make style changes,
4214 // not updating the buffer directly.
4215 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
4217 if (haveControl
&& withUndo
)
4219 newPara
= new wxRichTextParagraph(*para
);
4220 action
->GetNewParagraphs().AppendChild(newPara
);
4222 // Also store the old ones for Undo
4223 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
4228 wxRichTextListStyleDefinition
* defToUse
= def
;
4231 if (styleSheet
&& !newPara
->GetAttributes().GetListStyleName().IsEmpty())
4232 defToUse
= styleSheet
->FindListStyle(newPara
->GetAttributes().GetListStyleName());
4237 int thisIndent
= newPara
->GetAttributes().GetLeftIndent();
4238 int thisLevel
= defToUse
->FindLevelForIndent(thisIndent
);
4240 // If we've specified a level to apply to all, change the level.
4241 if (specifiedLevel
!= -1)
4242 thisLevel
= specifiedLevel
;
4244 // Do promotion if specified
4245 if ((promoteBy
!= 0) && !para
->GetRange().IsOutside(promotionRange
))
4247 thisLevel
= thisLevel
- promoteBy
;
4254 // Apply the overall list style, and item style for this level
4255 wxRichTextAttr
listStyle(defToUse
->GetCombinedStyleForLevel(thisLevel
, styleSheet
));
4256 wxRichTextApplyStyle(newPara
->GetAttributes(), listStyle
);
4258 // Preserve the existing list item continuation bullet style, if any
4259 if (para
->GetAttributes().HasBulletStyle() && (para
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
))
4260 newPara
->GetAttributes().SetBulletStyle(newPara
->GetAttributes().GetBulletStyle()|wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
);
4262 // OK, we've (re)applied the style, now let's get the numbering right.
4264 if (currentLevel
== -1)
4265 currentLevel
= thisLevel
;
4267 // Same level as before, do nothing except increment level's number afterwards
4268 if (currentLevel
== thisLevel
)
4271 // A deeper level: start renumbering all levels after current level
4272 else if (thisLevel
> currentLevel
)
4274 for (i
= currentLevel
+1; i
<= thisLevel
; i
++)
4278 currentLevel
= thisLevel
;
4280 else if (thisLevel
< currentLevel
)
4282 currentLevel
= thisLevel
;
4285 // Use the current numbering if -1 and we have a bullet number already
4286 if (levels
[currentLevel
] == -1)
4288 if (newPara
->GetAttributes().HasBulletNumber())
4289 levels
[currentLevel
] = newPara
->GetAttributes().GetBulletNumber();
4291 levels
[currentLevel
] = 1;
4295 if (!(para
->GetAttributes().HasBulletStyle() && (para
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
)))
4296 levels
[currentLevel
] ++;
4299 newPara
->GetAttributes().SetBulletNumber(levels
[currentLevel
]);
4301 // Create the bullet text if an outline list
4302 if (listStyle
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
)
4305 for (i
= 0; i
<= currentLevel
; i
++)
4307 if (!text
.IsEmpty())
4309 text
+= wxString::Format(wxT("%d"), levels
[i
]);
4311 newPara
->GetAttributes().SetBulletText(text
);
4317 node
= node
->GetNext();
4320 // Do action, or delay it until end of batch.
4321 if (haveControl
&& withUndo
)
4322 buffer
->SubmitAction(action
);
4327 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
)
4329 wxRichTextBuffer
* buffer
= GetBuffer();
4330 if (buffer
->GetStyleSheet())
4332 wxRichTextListStyleDefinition
* def
= NULL
;
4333 if (!defName
.IsEmpty())
4334 def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4335 return NumberList(range
, def
, flags
, startFrom
, specifiedLevel
);
4340 /// Promote the list items within the given range. promoteBy can be a positive or negative number, e.g. 1 or -1
4341 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy
, const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int specifiedLevel
)
4344 // One strategy is to first work out the range within which renumbering must occur. Then could pass these two ranges
4345 // to NumberList with a flag indicating promotion is required within one of the ranges.
4346 // Find first and last paragraphs in range. Then for first, calculate new indentation and look back until we find
4347 // a paragraph that either has no list style, or has one that is different or whose indentation is less.
4348 // We start renumbering from the para after that different para we found. We specify that the numbering of that
4349 // list position will start from 1.
4350 // Similarly, we look after the last para in the promote range for an indentation that is less (or no list style).
4351 // We can end the renumbering at this point.
4353 // For now, only renumber within the promotion range.
4355 return DoNumberList(range
, range
, promoteBy
, def
, flags
, 1, specifiedLevel
);
4358 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy
, const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int specifiedLevel
)
4360 wxRichTextBuffer
* buffer
= GetBuffer();
4361 if (buffer
->GetStyleSheet())
4363 wxRichTextListStyleDefinition
* def
= NULL
;
4364 if (!defName
.IsEmpty())
4365 def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4366 return PromoteList(promoteBy
, range
, def
, flags
, specifiedLevel
);
4371 /// Fills in the attributes for numbering a paragraph after previousParagraph. It also finds the
4372 /// position of the paragraph that it had to start looking from.
4373 bool wxRichTextParagraphLayoutBox::FindNextParagraphNumber(wxRichTextParagraph
* previousParagraph
, wxRichTextAttr
& attr
) const
4375 // TODO: add GetNextChild/GetPreviousChild to composite
4376 // Search for a paragraph that isn't a continuation paragraph (no bullet)
4377 while (previousParagraph
&& previousParagraph
->GetAttributes().HasBulletStyle() && previousParagraph
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
)
4379 wxRichTextObjectList::compatibility_iterator node
= ((wxRichTextCompositeObject
*) previousParagraph
->GetParent())->GetChildren().Find(previousParagraph
);
4382 node
= node
->GetPrevious();
4384 previousParagraph
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
4386 previousParagraph
= NULL
;
4389 previousParagraph
= NULL
;
4392 if (!previousParagraph
|| !previousParagraph
->GetAttributes().HasFlag(wxTEXT_ATTR_BULLET_STYLE
) || previousParagraph
->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
)
4395 wxRichTextBuffer
* buffer
= GetBuffer();
4396 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
4397 if (styleSheet
&& !previousParagraph
->GetAttributes().GetListStyleName().IsEmpty())
4399 wxRichTextListStyleDefinition
* def
= styleSheet
->FindListStyle(previousParagraph
->GetAttributes().GetListStyleName());
4402 // int thisIndent = previousParagraph->GetAttributes().GetLeftIndent();
4403 // int thisLevel = def->FindLevelForIndent(thisIndent);
4405 bool isOutline
= (previousParagraph
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
) != 0;
4407 attr
.SetFlags(previousParagraph
->GetAttributes().GetFlags() & (wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_BULLET_NUMBER
|wxTEXT_ATTR_BULLET_TEXT
|wxTEXT_ATTR_BULLET_NAME
));
4408 if (previousParagraph
->GetAttributes().HasBulletName())
4409 attr
.SetBulletName(previousParagraph
->GetAttributes().GetBulletName());
4410 attr
.SetBulletStyle(previousParagraph
->GetAttributes().GetBulletStyle());
4411 attr
.SetListStyleName(previousParagraph
->GetAttributes().GetListStyleName());
4413 int nextNumber
= previousParagraph
->GetAttributes().GetBulletNumber() + 1;
4414 attr
.SetBulletNumber(nextNumber
);
4418 wxString text
= previousParagraph
->GetAttributes().GetBulletText();
4419 if (!text
.IsEmpty())
4421 int pos
= text
.Find(wxT('.'), true);
4422 if (pos
!= wxNOT_FOUND
)
4424 text
= text
.Mid(0, text
.Length() - pos
- 1);
4427 text
= wxEmptyString
;
4428 if (!text
.IsEmpty())
4430 text
+= wxString::Format(wxT("%d"), nextNumber
);
4431 attr
.SetBulletText(text
);
4445 * wxRichTextParagraph
4446 * This object represents a single paragraph (or in a straight text editor, a line).
4449 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraph
, wxRichTextCompositeObject
)
4451 wxArrayInt
wxRichTextParagraph::sm_defaultTabs
;
4453 wxRichTextParagraph::wxRichTextParagraph(wxRichTextObject
* parent
, wxRichTextAttr
* style
):
4454 wxRichTextCompositeObject(parent
)
4457 SetAttributes(*style
);
4460 wxRichTextParagraph::wxRichTextParagraph(const wxString
& text
, wxRichTextObject
* parent
, wxRichTextAttr
* paraStyle
, wxRichTextAttr
* charStyle
):
4461 wxRichTextCompositeObject(parent
)
4464 SetAttributes(*paraStyle
);
4466 AppendChild(new wxRichTextPlainText(text
, this, charStyle
));
4469 wxRichTextParagraph::~wxRichTextParagraph()
4475 bool wxRichTextParagraph::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int WXUNUSED(descent
), int style
)
4480 // Currently we don't merge these attributes with the parent, but we
4481 // should consider whether we should (e.g. if we set a border colour
4482 // for all paragraphs). But generally box attributes are likely to be
4483 // different for different objects.
4484 wxRect paraRect
= GetRect();
4485 wxRichTextAttr attr
= GetCombinedAttributes();
4486 context
.ApplyVirtualAttributes(attr
, this);
4488 DrawBoxAttributes(dc
, GetBuffer(), attr
, paraRect
);
4490 // Draw the bullet, if any
4491 if ((attr
.GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
) == 0 && (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
) == 0)
4493 if (attr
.GetLeftSubIndent() != 0)
4495 int spaceBeforePara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingBefore());
4496 int leftIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftIndent());
4498 wxRichTextAttr
bulletAttr(attr
);
4500 // Combine with the font of the first piece of content, if one is specified
4501 if (GetChildren().GetCount() > 0)
4503 wxRichTextObject
* firstObj
= (wxRichTextObject
*) GetChildren().GetFirst()->GetData();
4504 if (!firstObj
->IsFloatable() && firstObj
->GetAttributes().HasFont())
4506 wxRichTextApplyStyle(bulletAttr
, firstObj
->GetAttributes());
4510 // Get line height from first line, if any
4511 wxRichTextLine
* line
= m_cachedLines
.GetFirst() ? (wxRichTextLine
* ) m_cachedLines
.GetFirst()->GetData() : NULL
;
4514 int lineHeight
wxDUMMY_INITIALIZE(0);
4517 lineHeight
= line
->GetSize().y
;
4518 linePos
= line
->GetPosition() + GetPosition();
4523 if (bulletAttr
.HasFont() && GetBuffer())
4524 font
= GetBuffer()->GetFontTable().FindFont(bulletAttr
);
4526 font
= (*wxNORMAL_FONT
);
4528 wxCheckSetFont(dc
, font
);
4530 lineHeight
= dc
.GetCharHeight();
4531 linePos
= GetPosition();
4532 linePos
.y
+= spaceBeforePara
;
4535 wxRect
bulletRect(GetPosition().x
+ leftIndent
, linePos
.y
, linePos
.x
- (GetPosition().x
+ leftIndent
), lineHeight
);
4537 if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
)
4539 if (wxRichTextBuffer::GetRenderer())
4540 wxRichTextBuffer::GetRenderer()->DrawBitmapBullet(this, dc
, bulletAttr
, bulletRect
);
4542 else if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_STANDARD
)
4544 if (wxRichTextBuffer::GetRenderer())
4545 wxRichTextBuffer::GetRenderer()->DrawStandardBullet(this, dc
, bulletAttr
, bulletRect
);
4549 wxString bulletText
= GetBulletText();
4551 if (!bulletText
.empty() && wxRichTextBuffer::GetRenderer())
4552 wxRichTextBuffer::GetRenderer()->DrawTextBullet(this, dc
, bulletAttr
, bulletRect
, bulletText
);
4557 // Draw the range for each line, one object at a time.
4559 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
4562 wxRichTextLine
* line
= node
->GetData();
4563 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
4565 // Lines are specified relative to the paragraph
4567 wxPoint linePosition
= line
->GetPosition() + GetPosition();
4569 // Don't draw if off the screen
4570 if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) != 0) || ((linePosition
.y
+ line
->GetSize().y
) >= rect
.y
&& linePosition
.y
<= rect
.y
+ rect
.height
))
4572 wxPoint objectPosition
= linePosition
;
4573 int maxDescent
= line
->GetDescent();
4575 // Loop through objects until we get to the one within range
4576 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
4581 wxRichTextObject
* child
= node2
->GetData();
4583 if ((!child
->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && child
->GetRange().GetLength() > 0 && !child
->GetRange().IsOutside(lineRange
) && !lineRange
.IsOutside(range
))
4585 // Draw this part of the line at the correct position
4586 wxRichTextRange
objectRange(child
->GetRange());
4587 objectRange
.LimitTo(lineRange
);
4590 if (child
->IsTopLevel())
4592 objectSize
= child
->GetCachedSize();
4593 objectRange
= child
->GetOwnRange();
4597 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING && wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4598 if (i
< (int) line
->GetObjectSizes().GetCount())
4600 objectSize
.x
= line
->GetObjectSizes()[(size_t) i
];
4606 child
->GetRangeSize(objectRange
, objectSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, objectPosition
);
4610 // Use the child object's width, but the whole line's height
4611 wxRect
childRect(objectPosition
, wxSize(objectSize
.x
, line
->GetSize().y
));
4612 child
->Draw(dc
, context
, objectRange
, selection
, childRect
, maxDescent
, style
);
4614 objectPosition
.x
+= objectSize
.x
;
4617 else if (child
->GetRange().GetStart() > lineRange
.GetEnd())
4618 // Can break out of inner loop now since we've passed this line's range
4621 node2
= node2
->GetNext();
4625 node
= node
->GetNext();
4631 // Get the range width using partial extents calculated for the whole paragraph.
4632 static int wxRichTextGetRangeWidth(const wxRichTextParagraph
& para
, const wxRichTextRange
& range
, const wxArrayInt
& partialExtents
)
4634 wxASSERT(partialExtents
.GetCount() >= (size_t) range
.GetLength());
4636 if (partialExtents
.GetCount() < (size_t) range
.GetLength())
4639 int leftMostPos
= 0;
4640 if (range
.GetStart() - para
.GetRange().GetStart() > 0)
4641 leftMostPos
= partialExtents
[range
.GetStart() - para
.GetRange().GetStart() - 1];
4643 int rightMostPos
= partialExtents
[range
.GetEnd() - para
.GetRange().GetStart()];
4645 int w
= rightMostPos
- leftMostPos
;
4650 /// Lay the item out
4651 bool wxRichTextParagraph::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
)
4653 // Deal with floating objects firstly before the normal layout
4654 wxRichTextBuffer
* buffer
= GetBuffer();
4657 wxRichTextFloatCollector
* collector
= GetContainer()->GetFloatCollector();
4659 if (wxRichTextBuffer::GetFloatingLayoutMode())
4661 wxASSERT(collector
!= NULL
);
4663 LayoutFloat(dc
, context
, rect
, parentRect
, style
, collector
);
4666 wxRichTextAttr attr
= GetCombinedAttributes();
4667 context
.ApplyVirtualAttributes(attr
, this);
4671 // Increase the size of the paragraph due to spacing
4672 int spaceBeforePara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingBefore());
4673 int spaceAfterPara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingAfter());
4674 int leftIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftIndent());
4675 int leftSubIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftSubIndent());
4676 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
4678 int lineSpacing
= 0;
4680 // Let's assume line spacing of 10 is normal, 15 is 1.5, 20 is 2, etc.
4681 if (attr
.HasLineSpacing() && attr
.GetLineSpacing() > 0 && attr
.HasFont())
4683 wxFont
font(buffer
->GetFontTable().FindFont(attr
));
4686 wxCheckSetFont(dc
, font
);
4687 lineSpacing
= (int) (double(dc
.GetCharHeight()) * (double(attr
.GetLineSpacing())/10.0 - 1.0));
4691 // Start position for each line relative to the paragraph
4692 int startPositionFirstLine
= leftIndent
;
4693 int startPositionSubsequentLines
= leftIndent
+ leftSubIndent
;
4695 // If we have a bullet in this paragraph, the start position for the first line's text
4696 // is actually leftIndent + leftSubIndent.
4697 if (attr
.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE
)
4698 startPositionFirstLine
= startPositionSubsequentLines
;
4700 long lastEndPos
= GetRange().GetStart()-1;
4701 long lastCompletedEndPos
= lastEndPos
;
4703 int currentWidth
= 0;
4704 SetPosition(rect
.GetPosition());
4706 wxPoint
currentPosition(0, spaceBeforePara
); // We will calculate lines relative to paragraph
4709 int maxHeight
= currentPosition
.y
;
4714 int lineDescent
= 0;
4716 wxRichTextObjectList::compatibility_iterator node
;
4718 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4720 wxArrayInt partialExtents
;
4723 int paraDescent
= 0;
4725 // This calculates the partial text extents
4726 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, rect
.GetPosition(), parentRect
.GetSize(), & partialExtents
);
4728 node
= m_children
.GetFirst();
4731 wxRichTextObject
* child
= node
->GetData();
4733 //child->SetCachedSize(wxDefaultSize);
4734 child
->Layout(dc
, context
, rect
, style
);
4736 node
= node
->GetNext();
4742 // We may need to go back to a previous child, in which case create the new line,
4743 // find the child corresponding to the start position of the string, and
4746 wxRect availableRect
;
4748 node
= m_children
.GetFirst();
4751 wxRichTextObject
* child
= node
->GetData();
4753 // If floating, ignore. We already laid out floats.
4754 // Also ignore if empty object, except if we haven't got any
4756 if ((child
->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode())
4757 || !child
->IsShown() || (child
->GetRange().GetLength() == 0 && maxHeight
> spaceBeforePara
)
4760 node
= node
->GetNext();
4764 // If this is e.g. a composite text box, it will need to be laid out itself.
4765 // But if just a text fragment or image, for example, this will
4766 // do nothing. NB: won't we need to set the position after layout?
4767 // since for example if position is dependent on vertical line size, we
4768 // can't tell the position until the size is determined. So possibly introduce
4769 // another layout phase.
4771 // We may only be looking at part of a child, if we searched back for wrapping
4772 // and found a suitable point some way into the child. So get the size for the fragment
4775 long nextBreakPos
= GetFirstLineBreakPosition(lastEndPos
+1);
4776 long lastPosToUse
= child
->GetRange().GetEnd();
4777 bool lineBreakInThisObject
= (nextBreakPos
> -1 && nextBreakPos
<= child
->GetRange().GetEnd());
4779 if (lineBreakInThisObject
)
4780 lastPosToUse
= nextBreakPos
;
4783 int childDescent
= 0;
4785 int startOffset
= (lineCount
== 0 ? startPositionFirstLine
: startPositionSubsequentLines
);
4786 availableRect
= wxRect(rect
.x
+ startOffset
, rect
.y
+ currentPosition
.y
,
4787 rect
.width
- startOffset
- rightIndent
, rect
.height
);
4789 if (child
->IsTopLevel())
4791 wxSize oldSize
= child
->GetCachedSize();
4793 child
->Invalidate(wxRICHTEXT_ALL
);
4794 child
->SetPosition(wxPoint(0, 0));
4796 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4797 // lays out the object again using the minimum size
4798 // The position will be determined by its location in its line,
4799 // and not by the child's actual position.
4800 child
->LayoutToBestSize(dc
, context
, buffer
,
4801 attr
, child
->GetAttributes(), availableRect
, parentRect
, style
);
4803 if (oldSize
!= child
->GetCachedSize())
4805 partialExtents
.Clear();
4807 // Recalculate the partial text extents since the child object changed size
4808 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), parentRect
.GetSize(), & partialExtents
);
4812 // Problem: we need to layout composites here for which we need the available width,
4813 // but we can't get the available width without using the float collector which
4814 // needs to know the object height.
4816 if ((nextBreakPos
== -1) && (lastEndPos
== child
->GetRange().GetStart() - 1)) // i.e. we want to get the whole thing
4818 childSize
= child
->GetCachedSize();
4819 childDescent
= child
->GetDescent();
4823 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4824 // Get height only, then the width using the partial extents
4825 GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
, wxPoint(0,0), parentRect
.GetSize());
4826 childSize
.x
= wxRichTextGetRangeWidth(*this, wxRichTextRange(lastEndPos
+1, lastPosToUse
), partialExtents
);
4828 GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, rect
.GetPosition(), parentRect
.GetSize());
4833 int loopIterations
= 0;
4835 // If there are nested objects that need to lay themselves out, we have to do this in a
4836 // loop because the height of the object may well depend on the available width.
4837 // And because of floating object positioning, the available width depends on the
4838 // height of the object and whether it will clash with the floating objects.
4839 // So, we see whether the available width changes due to the presence of floating images.
4840 // If it does, then we'll use the new restricted width to find the object height again.
4841 // If this causes another restriction in the available width, we'll try again, until
4842 // either we lose patience or the available width settles down.
4847 wxRect oldAvailableRect
= availableRect
;
4849 // Available width depends on the floating objects and the line height.
4850 // Note: the floating objects may be placed vertically along the two sides of
4851 // buffer, so we may have different available line widths with different
4852 // [startY, endY]. So, we can't determine how wide the available
4853 // space is until we know the exact line height.
4854 if (childDescent
== 0)
4856 lineHeight
= wxMax(lineHeight
, childSize
.y
);
4857 lineDescent
= maxDescent
;
4858 lineAscent
= maxAscent
;
4862 lineDescent
= wxMax(childDescent
, maxDescent
);
4863 lineAscent
= wxMax(childSize
.y
-childDescent
, maxAscent
);
4865 lineHeight
= wxMax(lineHeight
, (lineDescent
+ lineAscent
));
4867 if (wxRichTextBuffer::GetFloatingLayoutMode() && collector
)
4869 wxRect floatAvailableRect
= collector
->GetAvailableRect(rect
.y
+ currentPosition
.y
, rect
.y
+ currentPosition
.y
+ lineHeight
);
4871 // Adjust availableRect to the space that is available when taking floating objects into account.
4873 if (floatAvailableRect
.x
+ startOffset
> availableRect
.x
)
4875 int newX
= floatAvailableRect
.x
+ startOffset
;
4876 int newW
= availableRect
.width
- (newX
- availableRect
.x
);
4877 availableRect
.x
= newX
;
4878 availableRect
.width
= newW
;
4881 if (floatAvailableRect
.width
< availableRect
.width
)
4882 availableRect
.width
= floatAvailableRect
.width
;
4885 currentPosition
.x
= availableRect
.x
- rect
.x
;
4887 if (child
->IsTopLevel() && loopIterations
<= 20)
4889 if (availableRect
!= oldAvailableRect
)
4891 wxSize oldSize
= child
->GetCachedSize();
4893 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4894 // lays out the object again using the minimum size
4895 child
->Invalidate(wxRICHTEXT_ALL
);
4896 child
->LayoutToBestSize(dc
, context
, buffer
,
4897 attr
, child
->GetAttributes(), availableRect
, parentRect
.GetSize(), style
);
4898 childSize
= child
->GetCachedSize();
4899 childDescent
= child
->GetDescent();
4901 if (oldSize
!= child
->GetCachedSize())
4903 partialExtents
.Clear();
4905 // Recalculate the partial text extents since the child object changed size
4906 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), parentRect
.GetSize(), & partialExtents
);
4909 // Go around the loop finding the available rect for the given floating objects
4919 if (child
->IsTopLevel())
4921 // We can move it to the correct position at this point
4922 // TODO: probably need to add margin
4923 child
->Move(GetPosition() + wxPoint(currentWidth
+ (wxMax(leftIndent
, leftIndent
+ leftSubIndent
)), currentPosition
.y
));
4927 // 1) There was a line break BEFORE the natural break
4928 // 2) There was a line break AFTER the natural break
4929 // 3) It's the last line
4930 // 4) The child still fits (carry on) - 'else' clause
4932 if ((lineBreakInThisObject
&& (childSize
.x
+ currentWidth
<= availableRect
.width
))
4934 (childSize
.x
+ currentWidth
> availableRect
.width
)
4937 ((childSize
.x
+ currentWidth
<= availableRect
.width
) && !node
->GetNext())
4941 long wrapPosition
= 0;
4942 if ((childSize
.x
+ currentWidth
<= availableRect
.width
) && !node
->GetNext() && !lineBreakInThisObject
)
4943 wrapPosition
= child
->GetRange().GetEnd();
4946 // Find a place to wrap. This may walk back to previous children,
4947 // for example if a word spans several objects.
4948 // Note: one object must contains only one wxTextAtrr, so the line height will not
4949 // change inside one object. Thus, we can pass the remain line width to the
4950 // FindWrapPosition function.
4951 if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos
+1, child
->GetRange().GetEnd()), dc
, context
, availableRect
.width
, wrapPosition
, & partialExtents
))
4953 // If the function failed, just cut it off at the end of this child.
4954 wrapPosition
= child
->GetRange().GetEnd();
4957 // FindWrapPosition can still return a value that will put us in an endless wrapping loop
4958 if (wrapPosition
<= lastCompletedEndPos
)
4959 wrapPosition
= wxMax(lastCompletedEndPos
+1,child
->GetRange().GetEnd());
4961 // Line end position shouldn't be the same as the end, or greater.
4962 if (wrapPosition
>= GetRange().GetEnd())
4963 wrapPosition
= wxMax(0, GetRange().GetEnd()-1);
4965 // wxLogDebug(wxT("Split at %ld"), wrapPosition);
4967 // Let's find the actual size of the current line now
4969 wxRichTextRange
actualRange(lastCompletedEndPos
+1, wrapPosition
);
4973 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4974 if (!child
->IsEmpty())
4976 // Get height only, then the width using the partial extents
4977 GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
, wxPoint(0,0), parentRect
.GetSize());
4978 actualSize
.x
= wxRichTextGetRangeWidth(*this, actualRange
, partialExtents
);
4982 GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, wxPoint(0,0), parentRect
.GetSize());
4984 currentWidth
= actualSize
.x
;
4986 // The descent for the whole line at this point, is the correct max descent
4987 maxDescent
= childDescent
;
4989 maxAscent
= actualSize
.y
-childDescent
;
4991 // lineHeight is given by the height for the whole line, since it will
4992 // take into account ascend/descend.
4993 lineHeight
= actualSize
.y
;
4995 if (lineHeight
== 0 && buffer
)
4997 wxFont
font(buffer
->GetFontTable().FindFont(attr
));
4998 wxCheckSetFont(dc
, font
);
4999 lineHeight
= dc
.GetCharHeight();
5002 if (maxDescent
== 0)
5005 dc
.GetTextExtent(wxT("X"), & w
, &h
, & maxDescent
);
5009 wxRichTextLine
* line
= AllocateLine(lineCount
);
5011 // Set relative range so we won't have to change line ranges when paragraphs are moved
5012 line
->SetRange(wxRichTextRange(actualRange
.GetStart() - GetRange().GetStart(), actualRange
.GetEnd() - GetRange().GetStart()));
5013 line
->SetPosition(currentPosition
);
5014 line
->SetSize(wxSize(currentWidth
, lineHeight
));
5015 line
->SetDescent(maxDescent
);
5017 maxHeight
= currentPosition
.y
+ lineHeight
;
5019 // Now move down a line. TODO: add margins, spacing
5020 currentPosition
.y
+= lineHeight
;
5021 currentPosition
.y
+= lineSpacing
;
5024 maxWidth
= wxMax(maxWidth
, currentWidth
+startOffset
);
5029 // TODO: account for zero-length objects
5030 // wxASSERT(wrapPosition > lastCompletedEndPos);
5032 lastEndPos
= wrapPosition
;
5033 lastCompletedEndPos
= lastEndPos
;
5037 if (wrapPosition
< GetRange().GetEnd()-1)
5039 // May need to set the node back to a previous one, due to searching back in wrapping
5040 wxRichTextObject
* childAfterWrapPosition
= FindObjectAtPosition(wrapPosition
+1);
5041 if (childAfterWrapPosition
)
5042 node
= m_children
.Find(childAfterWrapPosition
);
5044 node
= node
->GetNext();
5047 node
= node
->GetNext();
5049 // Apply paragraph styles such as alignment to the wrapped line
5050 ApplyParagraphStyle(line
, attr
, availableRect
, dc
);
5054 // We still fit, so don't add a line, and keep going
5055 currentWidth
+= childSize
.x
;
5057 if (childDescent
== 0)
5059 // An object with a zero descend value wants to take up the whole
5060 // height regardless of baseline
5061 lineHeight
= wxMax(lineHeight
, childSize
.y
);
5065 maxDescent
= wxMax(childDescent
, maxDescent
);
5066 maxAscent
= wxMax(childSize
.y
-childDescent
, maxAscent
);
5069 lineHeight
= wxMax(lineHeight
, (maxDescent
+ maxAscent
));
5071 maxWidth
= wxMax(maxWidth
, currentWidth
+startOffset
);
5072 lastEndPos
= child
->GetRange().GetEnd();
5074 node
= node
->GetNext();
5078 //wxASSERT(!(lastCompletedEndPos != -1 && lastCompletedEndPos < GetRange().GetEnd()-1));
5080 // Add the last line - it's the current pos -> last para pos
5081 // Substract -1 because the last position is always the end-paragraph position.
5082 if (lastCompletedEndPos
<= GetRange().GetEnd()-1)
5084 currentPosition
.x
= (lineCount
== 0 ? startPositionFirstLine
: startPositionSubsequentLines
);
5086 wxRichTextLine
* line
= AllocateLine(lineCount
);
5088 wxRichTextRange
actualRange(lastCompletedEndPos
+1, GetRange().GetEnd()-1);
5090 // Set relative range so we won't have to change line ranges when paragraphs are moved
5091 line
->SetRange(wxRichTextRange(actualRange
.GetStart() - GetRange().GetStart(), actualRange
.GetEnd() - GetRange().GetStart()));
5093 line
->SetPosition(currentPosition
);
5095 if (lineHeight
== 0 && buffer
)
5097 wxFont
font(buffer
->GetFontTable().FindFont(attr
));
5098 wxCheckSetFont(dc
, font
);
5099 lineHeight
= dc
.GetCharHeight();
5102 if (maxDescent
== 0)
5105 dc
.GetTextExtent(wxT("X"), & w
, &h
, & maxDescent
);
5108 line
->SetSize(wxSize(currentWidth
, lineHeight
));
5109 line
->SetDescent(maxDescent
);
5110 currentPosition
.y
+= lineHeight
;
5111 currentPosition
.y
+= lineSpacing
;
5114 // Apply paragraph styles such as alignment to the wrapped line
5115 ApplyParagraphStyle(line
, attr
, availableRect
, dc
);
5118 // Remove remaining unused line objects, if any
5119 ClearUnusedLines(lineCount
);
5121 // We need to add back the margins etc.
5123 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
5124 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxWidth
, currentPosition
.y
+ spaceAfterPara
));
5125 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
5126 SetCachedSize(marginRect
.GetSize());
5129 // The maximum size is the length of the paragraph stretched out into a line.
5130 // So if there were a single word, or an image, or a fixed-size text box, the object could be shrunk around
5131 // this size. TODO: take into account line breaks.
5133 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
5134 contentRect
= wxRect(wxPoint(0, 0), wxSize(paraSize
.x
+ wxMax(leftIndent
, leftIndent
+ leftSubIndent
) + rightIndent
, currentPosition
.y
+ spaceAfterPara
));
5135 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
5136 SetMaxSize(marginRect
.GetSize());
5139 // Find the greatest minimum size. Currently we only look at non-text objects,
5140 // which isn't ideal but it would be slow to find the maximum word width to
5141 // use as the minimum.
5144 node
= m_children
.GetFirst();
5147 wxRichTextObject
* child
= node
->GetData();
5149 // If floating, ignore. We already laid out floats.
5150 // Also ignore if empty object, except if we haven't got any
5152 if ((!child
->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && child
->GetRange().GetLength() != 0 && !wxDynamicCast(child
, wxRichTextPlainText
))
5154 if (child
->GetCachedSize().x
> minWidth
)
5155 minWidth
= child
->GetMinSize().x
;
5157 node
= node
->GetNext();
5160 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
5161 contentRect
= wxRect(wxPoint(0, 0), wxSize(minWidth
, currentPosition
.y
+ spaceAfterPara
));
5162 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
5163 SetMinSize(marginRect
.GetSize());
5166 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5167 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
5168 // Use the text extents to calculate the size of each fragment in each line
5169 wxRichTextLineList::compatibility_iterator lineNode
= m_cachedLines
.GetFirst();
5172 wxRichTextLine
* line
= lineNode
->GetData();
5173 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5175 // Loop through objects until we get to the one within range
5176 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
5180 wxRichTextObject
* child
= node2
->GetData();
5182 if (child
->GetRange().GetLength() > 0 && !child
->GetRange().IsOutside(lineRange
))
5184 wxRichTextRange rangeToUse
= lineRange
;
5185 rangeToUse
.LimitTo(child
->GetRange());
5187 // Find the size of the child from the text extents, and store in an array
5188 // for drawing later
5190 if (rangeToUse
.GetStart() > GetRange().GetStart())
5191 left
= partialExtents
[(rangeToUse
.GetStart()-1) - GetRange().GetStart()];
5192 int right
= partialExtents
[rangeToUse
.GetEnd() - GetRange().GetStart()];
5193 int sz
= right
- left
;
5194 line
->GetObjectSizes().Add(sz
);
5196 else if (child
->GetRange().GetStart() > lineRange
.GetEnd())
5197 // Can break out of inner loop now since we've passed this line's range
5200 node2
= node2
->GetNext();
5203 lineNode
= lineNode
->GetNext();
5211 /// Apply paragraph styles, such as centering, to wrapped lines
5212 /// TODO: take into account box attributes, possibly
5213 void wxRichTextParagraph::ApplyParagraphStyle(wxRichTextLine
* line
, const wxRichTextAttr
& attr
, const wxRect
& rect
, wxDC
& dc
)
5215 if (!attr
.HasAlignment())
5218 wxPoint pos
= line
->GetPosition();
5219 wxPoint originalPos
= pos
;
5220 wxSize size
= line
->GetSize();
5222 // centering, right-justification
5223 if (attr
.HasAlignment() && attr
.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE
)
5225 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
5226 pos
.x
= (rect
.GetWidth() - rightIndent
- size
.x
)/2 + pos
.x
;
5227 line
->SetPosition(pos
);
5229 else if (attr
.HasAlignment() && attr
.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT
)
5231 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
5232 pos
.x
= pos
.x
+ rect
.GetWidth() - size
.x
- rightIndent
;
5233 line
->SetPosition(pos
);
5236 if (pos
!= originalPos
)
5238 wxPoint inc
= pos
- originalPos
;
5240 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5244 wxRichTextObject
* child
= node
->GetData();
5245 if (child
->IsTopLevel() && !child
->GetRange().IsOutside(line
->GetAbsoluteRange()))
5246 child
->Move(child
->GetPosition() + inc
);
5248 node
= node
->GetNext();
5253 /// Insert text at the given position
5254 bool wxRichTextParagraph::InsertText(long pos
, const wxString
& text
)
5256 wxRichTextObject
* childToUse
= NULL
;
5257 wxRichTextObjectList::compatibility_iterator nodeToUse
= wxRichTextObjectList::compatibility_iterator();
5259 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5262 wxRichTextObject
* child
= node
->GetData();
5263 if (child
->GetRange().Contains(pos
) && child
->GetRange().GetLength() > 0)
5270 node
= node
->GetNext();
5275 wxRichTextPlainText
* textObject
= wxDynamicCast(childToUse
, wxRichTextPlainText
);
5278 int posInString
= pos
- textObject
->GetRange().GetStart();
5280 wxString newText
= textObject
->GetText().Mid(0, posInString
) +
5281 text
+ textObject
->GetText().Mid(posInString
);
5282 textObject
->SetText(newText
);
5284 int textLength
= text
.length();
5286 textObject
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart(),
5287 textObject
->GetRange().GetEnd() + textLength
));
5289 // Increment the end range of subsequent fragments in this paragraph.
5290 // We'll set the paragraph range itself at a higher level.
5292 wxRichTextObjectList::compatibility_iterator node
= nodeToUse
->GetNext();
5295 wxRichTextObject
* child
= node
->GetData();
5296 child
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart() + textLength
,
5297 textObject
->GetRange().GetEnd() + textLength
));
5299 node
= node
->GetNext();
5306 // TODO: if not a text object, insert at closest position, e.g. in front of it
5312 // Don't pass parent initially to suppress auto-setting of parent range.
5313 // We'll do that at a higher level.
5314 wxRichTextPlainText
* textObject
= new wxRichTextPlainText(text
, this);
5316 AppendChild(textObject
);
5323 void wxRichTextParagraph::Copy(const wxRichTextParagraph
& obj
)
5325 wxRichTextCompositeObject::Copy(obj
);
5328 /// Clear the cached lines
5329 void wxRichTextParagraph::ClearLines()
5331 WX_CLEAR_LIST(wxRichTextLineList
, m_cachedLines
);
5334 /// Get/set the object size for the given range. Returns false if the range
5335 /// is invalid for this object.
5336 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
5338 if (!range
.IsWithin(GetRange()))
5341 if (flags
& wxRICHTEXT_UNFORMATTED
)
5343 // Just use unformatted data, assume no line breaks
5346 wxArrayInt childExtents
;
5355 int maxLineHeight
= 0;
5357 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5360 wxRichTextObject
* child
= node
->GetData();
5361 if (!child
->GetRange().IsOutside(range
))
5363 // Floating objects have a zero size within the paragraph.
5364 if (child
->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode())
5369 if (partialExtents
->GetCount() > 0)
5370 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5374 partialExtents
->Add(0 /* zero size */ + lastSize
);
5381 wxRichTextRange rangeToUse
= range
;
5382 rangeToUse
.LimitTo(child
->GetRange());
5383 int childDescent
= 0;
5385 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we've already cached the size,
5386 // but it's only going to be used after caching has taken place.
5387 if ((flags
& wxRICHTEXT_HEIGHT_ONLY
) && child
->GetCachedSize().y
!= 0)
5389 childDescent
= child
->GetDescent();
5390 childSize
= child
->GetCachedSize();
5392 if (childDescent
== 0)
5394 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5398 maxDescent
= wxMax(maxDescent
, childDescent
);
5399 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5402 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5404 sz
.y
= wxMax(sz
.y
, maxLineHeight
);
5405 sz
.x
+= childSize
.x
;
5406 descent
= maxDescent
;
5408 else if (child
->IsTopLevel())
5410 childDescent
= child
->GetDescent();
5411 childSize
= child
->GetCachedSize();
5413 if (childDescent
== 0)
5415 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5419 maxDescent
= wxMax(maxDescent
, childDescent
);
5420 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5423 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5425 sz
.y
= wxMax(sz
.y
, maxLineHeight
);
5426 sz
.x
+= childSize
.x
;
5427 descent
= maxDescent
;
5429 // FIXME: this won't change the original values.
5430 // Should we be calling GetRangeSize above instead of using cached values?
5432 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange()))
5434 child
->SetCachedSize(childSize
);
5435 child
->SetDescent(childDescent
);
5442 if (partialExtents
->GetCount() > 0)
5443 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5447 partialExtents
->Add(childSize
.x
+ lastSize
);
5450 else if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, context
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
), parentSize
, p
))
5452 if (childDescent
== 0)
5454 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5458 maxDescent
= wxMax(maxDescent
, childDescent
);
5459 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5462 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5464 sz
.y
= wxMax(sz
.y
, maxLineHeight
);
5465 sz
.x
+= childSize
.x
;
5466 descent
= maxDescent
;
5468 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange()))
5470 child
->SetCachedSize(childSize
);
5471 child
->SetDescent(childDescent
);
5477 if (partialExtents
->GetCount() > 0)
5478 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5483 for (i
= 0; i
< childExtents
.GetCount(); i
++)
5485 partialExtents
->Add(childExtents
[i
] + lastSize
);
5495 node
= node
->GetNext();
5501 // Use formatted data, with line breaks
5504 // We're going to loop through each line, and then for each line,
5505 // call GetRangeSize for the fragment that comprises that line.
5506 // Only we have to do that multiple times within the line, because
5507 // the line may be broken into pieces. For now ignore line break commands
5508 // (so we can assume that getting the unformatted size for a fragment
5509 // within a line is the actual size)
5511 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5514 wxRichTextLine
* line
= node
->GetData();
5515 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5516 if (!lineRange
.IsOutside(range
))
5520 int maxLineHeight
= 0;
5521 int maxLineWidth
= 0;
5523 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
5526 wxRichTextObject
* child
= node2
->GetData();
5528 if ((!child
->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && !child
->GetRange().IsOutside(lineRange
))
5530 wxRichTextRange rangeToUse
= lineRange
;
5531 rangeToUse
.LimitTo(child
->GetRange());
5532 if (child
->IsTopLevel())
5533 rangeToUse
= child
->GetOwnRange();
5536 int childDescent
= 0;
5537 if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, context
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
), parentSize
))
5539 if (childDescent
== 0)
5541 // Assume that if descent is zero, this child can occupy the full line height
5542 // and does not need space for the line's maximum descent. So we influence
5543 // the overall max line height only.
5544 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5548 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5549 maxDescent
= wxMax(maxAscent
, childDescent
);
5551 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5552 maxLineWidth
+= childSize
.x
;
5556 node2
= node2
->GetNext();
5559 descent
= wxMax(descent
, maxDescent
);
5561 // Increase size by a line (TODO: paragraph spacing)
5562 sz
.y
+= maxLineHeight
;
5563 sz
.x
= wxMax(sz
.x
, maxLineWidth
);
5565 node
= node
->GetNext();
5572 /// Finds the absolute position and row height for the given character position
5573 bool wxRichTextParagraph::FindPosition(wxDC
& dc
, wxRichTextDrawingContext
& context
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
5577 wxRichTextLine
* line
= ((wxRichTextParagraphLayoutBox
*)GetParent())->GetLineAtPosition(0);
5579 *height
= line
->GetSize().y
;
5581 *height
= dc
.GetCharHeight();
5583 // -1 means 'the start of the buffer'.
5586 pt
= pt
+ line
->GetPosition();
5591 // The final position in a paragraph is taken to mean the position
5592 // at the start of the next paragraph.
5593 if (index
== GetRange().GetEnd())
5595 wxRichTextParagraphLayoutBox
* parent
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
5596 wxASSERT( parent
!= NULL
);
5598 // Find the height at the next paragraph, if any
5599 wxRichTextLine
* line
= parent
->GetLineAtPosition(index
+ 1);
5602 *height
= line
->GetSize().y
;
5603 pt
= line
->GetAbsolutePosition();
5607 *height
= dc
.GetCharHeight();
5608 int indent
= ConvertTenthsMMToPixels(dc
, m_attributes
.GetLeftIndent());
5609 pt
= wxPoint(indent
, GetCachedSize().y
);
5615 if (index
< GetRange().GetStart() || index
> GetRange().GetEnd())
5618 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5621 wxRichTextLine
* line
= node
->GetData();
5622 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5623 if (index
>= lineRange
.GetStart() && index
<= lineRange
.GetEnd())
5625 // If this is the last point in the line, and we're forcing the
5626 // returned value to be the start of the next line, do the required
5628 if (index
== lineRange
.GetEnd() && forceLineStart
)
5630 if (node
->GetNext())
5632 wxRichTextLine
* nextLine
= node
->GetNext()->GetData();
5633 *height
= nextLine
->GetSize().y
;
5634 pt
= nextLine
->GetAbsolutePosition();
5639 pt
.y
= line
->GetPosition().y
+ GetPosition().y
;
5641 wxRichTextRange
r(lineRange
.GetStart(), index
);
5645 // We find the size of the line up to this point,
5646 // then we can add this size to the line start position and
5647 // paragraph start position to find the actual position.
5649 if (GetRangeSize(r
, rangeSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, line
->GetPosition()+ GetPosition()))
5651 pt
.x
= line
->GetPosition().x
+ GetPosition().x
+ rangeSize
.x
;
5652 *height
= line
->GetSize().y
;
5659 node
= node
->GetNext();
5665 /// Hit-testing: returns a flag indicating hit test details, plus
5666 /// information about position
5667 int wxRichTextParagraph::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
5670 return wxRICHTEXT_HITTEST_NONE
;
5672 // If we're in the top-level container, then we can return
5673 // a suitable hit test code even if the point is outside the container area,
5674 // so that we can position the caret sensibly even if we don't
5675 // click on valid content. If we're not at the top-level, and the point
5676 // is not within this paragraph object, then we don't want to stop more
5677 // precise hit-testing from working prematurely, so return immediately.
5678 // NEW STRATEGY: use the parent boundary to test whether we're in the
5679 // right region, not the paragraph, since the paragraph may be positioned
5680 // some way in from where the user clicks.
5683 wxRichTextObject
* tempObj
, *tempContextObj
;
5684 if (GetParent() && GetParent()->wxRichTextObject::HitTest(dc
, context
, pt
, tmpPos
, & tempObj
, & tempContextObj
, flags
) == wxRICHTEXT_HITTEST_NONE
)
5685 return wxRICHTEXT_HITTEST_NONE
;
5688 wxRichTextObjectList::compatibility_iterator objNode
= m_children
.GetFirst();
5691 wxRichTextObject
* child
= objNode
->GetData();
5692 // Don't recurse if we have wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS,
5693 // and also, if this seems composite but actually is marked as atomic,
5695 if (child
->IsTopLevel() && ((flags
& wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS
) == 0) &&
5696 (! (((flags
& wxRICHTEXT_HITTEST_HONOUR_ATOMIC
) != 0) && child
->IsAtomic())))
5699 int hitTest
= child
->HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
);
5700 if (hitTest
!= wxRICHTEXT_HITTEST_NONE
)
5705 objNode
= objNode
->GetNext();
5708 wxPoint paraPos
= GetPosition();
5710 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5713 wxRichTextLine
* line
= node
->GetData();
5714 wxPoint linePos
= paraPos
+ line
->GetPosition();
5715 wxSize lineSize
= line
->GetSize();
5716 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5718 if (pt
.y
<= linePos
.y
+ lineSize
.y
)
5720 if (pt
.x
< linePos
.x
)
5722 textPosition
= lineRange
.GetStart();
5723 *obj
= FindObjectAtPosition(textPosition
);
5724 *contextObj
= GetContainer();
5725 return wxRICHTEXT_HITTEST_BEFORE
|wxRICHTEXT_HITTEST_OUTSIDE
;
5727 else if (pt
.x
>= (linePos
.x
+ lineSize
.x
))
5729 textPosition
= lineRange
.GetEnd();
5730 *obj
= FindObjectAtPosition(textPosition
);
5731 *contextObj
= GetContainer();
5732 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
;
5736 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5737 wxArrayInt partialExtents
;
5742 // This calculates the partial text extents
5743 GetRangeSize(lineRange
, paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, linePos
, wxDefaultSize
, & partialExtents
);
5745 int lastX
= linePos
.x
;
5747 for (i
= 0; i
< partialExtents
.GetCount(); i
++)
5749 int nextX
= partialExtents
[i
] + linePos
.x
;
5751 if (pt
.x
>= lastX
&& pt
.x
<= nextX
)
5753 textPosition
= i
+ lineRange
.GetStart(); // minus 1?
5755 *obj
= FindObjectAtPosition(textPosition
);
5756 *contextObj
= GetContainer();
5758 // So now we know it's between i-1 and i.
5759 // Let's see if we can be more precise about
5760 // which side of the position it's on.
5762 int midPoint
= (nextX
+ lastX
)/2;
5763 if (pt
.x
>= midPoint
)
5764 return wxRICHTEXT_HITTEST_AFTER
;
5766 return wxRICHTEXT_HITTEST_BEFORE
;
5773 int lastX
= linePos
.x
;
5774 for (i
= lineRange
.GetStart(); i
<= lineRange
.GetEnd(); i
++)
5779 wxRichTextRange
rangeToUse(lineRange
.GetStart(), i
);
5781 GetRangeSize(rangeToUse
, childSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, linePos
);
5783 int nextX
= childSize
.x
+ linePos
.x
;
5785 if (pt
.x
>= lastX
&& pt
.x
<= nextX
)
5789 *obj
= FindObjectAtPosition(textPosition
);
5790 *contextObj
= GetContainer();
5792 // So now we know it's between i-1 and i.
5793 // Let's see if we can be more precise about
5794 // which side of the position it's on.
5796 int midPoint
= (nextX
+ lastX
)/2;
5797 if (pt
.x
>= midPoint
)
5798 return wxRICHTEXT_HITTEST_AFTER
;
5800 return wxRICHTEXT_HITTEST_BEFORE
;
5811 node
= node
->GetNext();
5814 return wxRICHTEXT_HITTEST_NONE
;
5817 /// Split an object at this position if necessary, and return
5818 /// the previous object, or NULL if inserting at beginning.
5819 wxRichTextObject
* wxRichTextParagraph::SplitAt(long pos
, wxRichTextObject
** previousObject
)
5821 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5824 wxRichTextObject
* child
= node
->GetData();
5826 if (pos
== child
->GetRange().GetStart())
5830 if (node
->GetPrevious())
5831 *previousObject
= node
->GetPrevious()->GetData();
5833 *previousObject
= NULL
;
5839 if (child
->GetRange().Contains(pos
))
5841 // This should create a new object, transferring part of
5842 // the content to the old object and the rest to the new object.
5843 wxRichTextObject
* newObject
= child
->DoSplit(pos
);
5845 // If we couldn't split this object, just insert in front of it.
5848 // Maybe this is an empty string, try the next one
5853 // Insert the new object after 'child'
5854 if (node
->GetNext())
5855 m_children
.Insert(node
->GetNext(), newObject
);
5857 m_children
.Append(newObject
);
5858 newObject
->SetParent(this);
5861 *previousObject
= child
;
5867 node
= node
->GetNext();
5870 *previousObject
= NULL
;
5874 /// Move content to a list from obj on
5875 void wxRichTextParagraph::MoveToList(wxRichTextObject
* obj
, wxList
& list
)
5877 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(obj
);
5880 wxRichTextObject
* child
= node
->GetData();
5883 wxRichTextObjectList::compatibility_iterator oldNode
= node
;
5885 node
= node
->GetNext();
5887 m_children
.DeleteNode(oldNode
);
5891 /// Add content back from list
5892 void wxRichTextParagraph::MoveFromList(wxList
& list
)
5894 for (wxList::compatibility_iterator node
= list
.GetFirst(); node
; node
= node
->GetNext())
5896 AppendChild((wxRichTextObject
*) node
->GetData());
5901 void wxRichTextParagraph::CalculateRange(long start
, long& end
)
5903 wxRichTextCompositeObject::CalculateRange(start
, end
);
5905 // Add one for end of paragraph
5908 m_range
.SetRange(start
, end
);
5911 /// Find the object at the given position
5912 wxRichTextObject
* wxRichTextParagraph::FindObjectAtPosition(long position
)
5914 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5917 wxRichTextObject
* obj
= node
->GetData();
5918 if (obj
->GetRange().Contains(position
) ||
5919 obj
->GetRange().GetStart() == position
||
5920 obj
->GetRange().GetEnd() == position
)
5923 node
= node
->GetNext();
5928 /// Get the plain text searching from the start or end of the range.
5929 /// The resulting string may be shorter than the range given.
5930 bool wxRichTextParagraph::GetContiguousPlainText(wxString
& text
, const wxRichTextRange
& range
, bool fromStart
)
5932 text
= wxEmptyString
;
5936 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5939 wxRichTextObject
* obj
= node
->GetData();
5940 if (!obj
->GetRange().IsOutside(range
))
5942 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5945 text
+= textObj
->GetTextForRange(range
);
5953 node
= node
->GetNext();
5958 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetLast();
5961 wxRichTextObject
* obj
= node
->GetData();
5962 if (!obj
->GetRange().IsOutside(range
))
5964 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5967 text
= textObj
->GetTextForRange(range
) + text
;
5971 text
= wxT(" ") + text
;
5975 node
= node
->GetPrevious();
5982 /// Find a suitable wrap position.
5983 bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange
& range
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int availableSpace
, long& wrapPosition
, wxArrayInt
* partialExtents
)
5985 if (range
.GetLength() <= 0)
5988 // Find the first position where the line exceeds the available space.
5990 long breakPosition
= range
.GetEnd();
5992 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5993 if (partialExtents
&& partialExtents
->GetCount() >= (size_t) (GetRange().GetLength()-1)) // the final position in a paragraph is the newline
5997 if (range
.GetStart() > GetRange().GetStart())
5998 widthBefore
= (*partialExtents
)[range
.GetStart() - GetRange().GetStart() - 1];
6003 for (i
= (size_t) range
.GetStart(); i
<= (size_t) range
.GetEnd(); i
++)
6005 int widthFromStartOfThisRange
= (*partialExtents
)[i
- GetRange().GetStart()] - widthBefore
;
6007 if (widthFromStartOfThisRange
> availableSpace
)
6009 breakPosition
= i
-1;
6017 // Binary chop for speed
6018 long minPos
= range
.GetStart();
6019 long maxPos
= range
.GetEnd();
6022 if (minPos
== maxPos
)
6025 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
6027 if (sz
.x
> availableSpace
)
6028 breakPosition
= minPos
- 1;
6031 else if ((maxPos
- minPos
) == 1)
6034 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
6036 if (sz
.x
> availableSpace
)
6037 breakPosition
= minPos
- 1;
6040 GetRangeSize(wxRichTextRange(range
.GetStart(), maxPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
6041 if (sz
.x
> availableSpace
)
6042 breakPosition
= maxPos
-1;
6048 long nextPos
= minPos
+ ((maxPos
- minPos
) / 2);
6051 GetRangeSize(wxRichTextRange(range
.GetStart(), nextPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
6053 if (sz
.x
> availableSpace
)
6065 // Now we know the last position on the line.
6066 // Let's try to find a word break.
6069 if (GetContiguousPlainText(plainText
, wxRichTextRange(range
.GetStart(), breakPosition
), false))
6071 int newLinePos
= plainText
.Find(wxRichTextLineBreakChar
);
6072 if (newLinePos
!= wxNOT_FOUND
)
6074 breakPosition
= wxMax(0, range
.GetStart() + newLinePos
);
6078 int spacePos
= plainText
.Find(wxT(' '), true);
6079 int tabPos
= plainText
.Find(wxT('\t'), true);
6080 int pos
= wxMax(spacePos
, tabPos
);
6081 if (pos
!= wxNOT_FOUND
)
6083 int positionsFromEndOfString
= plainText
.length() - pos
- 1;
6084 breakPosition
= breakPosition
- positionsFromEndOfString
;
6089 wrapPosition
= breakPosition
;
6094 /// Get the bullet text for this paragraph.
6095 wxString
wxRichTextParagraph::GetBulletText()
6097 if (GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
||
6098 (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
))
6099 return wxEmptyString
;
6101 int number
= GetAttributes().GetBulletNumber();
6104 if ((GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ARABIC
) || (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
))
6106 text
.Printf(wxT("%d"), number
);
6108 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_UPPER
)
6110 // TODO: Unicode, and also check if number > 26
6111 text
.Printf(wxT("%c"), (wxChar
) (number
+64));
6113 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_LOWER
)
6115 // TODO: Unicode, and also check if number > 26
6116 text
.Printf(wxT("%c"), (wxChar
) (number
+96));
6118 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER
)
6120 text
= wxRichTextDecimalToRoman(number
);
6122 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER
)
6124 text
= wxRichTextDecimalToRoman(number
);
6127 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
)
6129 text
= GetAttributes().GetBulletText();
6132 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
)
6134 // The outline style relies on the text being computed statically,
6135 // since it depends on other levels points (e.g. 1.2.1.1). So normally the bullet text
6136 // should be stored in the attributes; if not, just use the number for this
6137 // level, as previously computed.
6138 if (!GetAttributes().GetBulletText().IsEmpty())
6139 text
= GetAttributes().GetBulletText();
6142 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PARENTHESES
)
6144 text
= wxT("(") + text
+ wxT(")");
6146 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_RIGHT_PARENTHESIS
)
6148 text
= text
+ wxT(")");
6151 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PERIOD
)
6159 /// Allocate or reuse a line object
6160 wxRichTextLine
* wxRichTextParagraph::AllocateLine(int pos
)
6162 if (pos
< (int) m_cachedLines
.GetCount())
6164 wxRichTextLine
* line
= m_cachedLines
.Item(pos
)->GetData();
6170 wxRichTextLine
* line
= new wxRichTextLine(this);
6171 m_cachedLines
.Append(line
);
6176 /// Clear remaining unused line objects, if any
6177 bool wxRichTextParagraph::ClearUnusedLines(int lineCount
)
6179 int cachedLineCount
= m_cachedLines
.GetCount();
6180 if ((int) cachedLineCount
> lineCount
)
6182 for (int i
= 0; i
< (int) (cachedLineCount
- lineCount
); i
++)
6184 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetLast();
6185 wxRichTextLine
* line
= node
->GetData();
6186 m_cachedLines
.Erase(node
);
6193 /// Get combined attributes of the base style, paragraph style and character style. We use this to dynamically
6194 /// retrieve the actual style.
6195 wxRichTextAttr
wxRichTextParagraph::GetCombinedAttributes(const wxRichTextAttr
& contentStyle
, bool includingBoxAttr
) const
6197 wxRichTextAttr attr
;
6198 wxRichTextParagraphLayoutBox
* buf
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
6201 attr
= buf
->GetBasicStyle();
6202 if (!includingBoxAttr
)
6204 attr
.GetTextBoxAttr().Reset();
6205 // The background colour will be painted by the container, and we don't
6206 // want to unnecessarily overwrite the background when we're drawing text
6207 // because this may erase the guideline (which appears just under the text
6208 // if there's no padding).
6209 attr
.SetFlags(attr
.GetFlags() & ~wxTEXT_ATTR_BACKGROUND_COLOUR
);
6211 wxRichTextApplyStyle(attr
, GetAttributes());
6214 attr
= GetAttributes();
6216 wxRichTextApplyStyle(attr
, contentStyle
);
6220 /// Get combined attributes of the base style and paragraph style.
6221 wxRichTextAttr
wxRichTextParagraph::GetCombinedAttributes(bool includingBoxAttr
) const
6223 wxRichTextAttr attr
;
6224 wxRichTextParagraphLayoutBox
* buf
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
6227 attr
= buf
->GetBasicStyle();
6228 if (!includingBoxAttr
)
6229 attr
.GetTextBoxAttr().Reset();
6230 wxRichTextApplyStyle(attr
, GetAttributes());
6233 attr
= GetAttributes();
6238 // Create default tabstop array
6239 void wxRichTextParagraph::InitDefaultTabs()
6241 // create a default tab list at 10 mm each.
6242 for (int i
= 0; i
< 20; ++i
)
6244 sm_defaultTabs
.Add(i
*100);
6248 // Clear default tabstop array
6249 void wxRichTextParagraph::ClearDefaultTabs()
6251 sm_defaultTabs
.Clear();
6254 void wxRichTextParagraph::LayoutFloat(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
, wxRichTextFloatCollector
* floatCollector
)
6256 wxRichTextObjectList::compatibility_iterator node
= GetChildren().GetFirst();
6259 wxRichTextObject
* anchored
= node
->GetData();
6260 if (anchored
&& anchored
->IsFloating() && !floatCollector
->HasFloat(anchored
))
6263 wxRichTextAttr
parentAttr(GetAttributes());
6264 context
.ApplyVirtualAttributes(parentAttr
, this);
6267 wxRect availableSpace
= GetParent()->GetAvailableContentArea(dc
, context
, rect
);
6269 anchored
->LayoutToBestSize(dc
, context
, GetBuffer(),
6270 parentAttr
, anchored
->GetAttributes(),
6271 parentRect
, availableSpace
,
6273 wxSize size
= anchored
->GetCachedSize();
6277 anchored
->GetRangeSize(anchored
->GetRange(), size
, descent
, dc
, context
, style
);
6281 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().IsValid())
6283 offsetY
= anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetValue();
6284 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
6286 offsetY
= ConvertTenthsMMToPixels(dc
, offsetY
);
6290 int pos
= floatCollector
->GetFitPosition(anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode(), rect
.y
+ offsetY
, size
.y
);
6292 /* Update the offset */
6293 int newOffsetY
= pos
- rect
.y
;
6294 if (newOffsetY
!= offsetY
)
6296 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
6297 newOffsetY
= ConvertPixelsToTenthsMM(dc
, newOffsetY
);
6298 anchored
->GetAttributes().GetTextBoxAttr().GetTop().SetValue(newOffsetY
);
6301 if (anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT
)
6303 else if (anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT
)
6304 x
= rect
.x
+ rect
.width
- size
.x
;
6306 //anchored->SetPosition(wxPoint(x, pos));
6307 anchored
->Move(wxPoint(x
, pos
)); // should move children
6308 anchored
->SetCachedSize(size
);
6309 floatCollector
->CollectFloat(this, anchored
);
6312 node
= node
->GetNext();
6316 // Get the first position from pos that has a line break character.
6317 long wxRichTextParagraph::GetFirstLineBreakPosition(long pos
)
6319 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
6322 wxRichTextObject
* obj
= node
->GetData();
6323 if (pos
>= obj
->GetRange().GetStart() && pos
<= obj
->GetRange().GetEnd())
6325 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
6328 long breakPos
= textObj
->GetFirstLineBreakPosition(pos
);
6333 node
= node
->GetNext();
6340 * This object represents a line in a paragraph, and stores
6341 * offsets from the start of the paragraph representing the
6342 * start and end positions of the line.
6345 wxRichTextLine::wxRichTextLine(wxRichTextParagraph
* parent
)
6351 void wxRichTextLine::Init(wxRichTextParagraph
* parent
)
6354 m_range
.SetRange(-1, -1);
6355 m_pos
= wxPoint(0, 0);
6356 m_size
= wxSize(0, 0);
6358 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6359 m_objectSizes
.Clear();
6364 void wxRichTextLine::Copy(const wxRichTextLine
& obj
)
6366 m_range
= obj
.m_range
;
6367 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6368 m_objectSizes
= obj
.m_objectSizes
;
6372 /// Get the absolute object position
6373 wxPoint
wxRichTextLine::GetAbsolutePosition() const
6375 return m_parent
->GetPosition() + m_pos
;
6378 /// Get the absolute range
6379 wxRichTextRange
wxRichTextLine::GetAbsoluteRange() const
6381 wxRichTextRange
range(m_range
.GetStart() + m_parent
->GetRange().GetStart(), 0);
6382 range
.SetEnd(range
.GetStart() + m_range
.GetLength()-1);
6387 * wxRichTextPlainText
6388 * This object represents a single piece of text.
6391 IMPLEMENT_DYNAMIC_CLASS(wxRichTextPlainText
, wxRichTextObject
)
6393 wxRichTextPlainText::wxRichTextPlainText(const wxString
& text
, wxRichTextObject
* parent
, wxRichTextAttr
* style
):
6394 wxRichTextObject(parent
)
6397 SetAttributes(*style
);
6402 #define USE_KERNING_FIX 1
6404 // If insufficient tabs are defined, this is the tab width used
6405 #define WIDTH_FOR_DEFAULT_TABS 50
6408 bool wxRichTextPlainText::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int WXUNUSED(style
))
6410 wxRichTextParagraph
* para
= wxDynamicCast(GetParent(), wxRichTextParagraph
);
6411 wxASSERT (para
!= NULL
);
6413 wxRichTextAttr
textAttr(para
? para
->GetCombinedAttributes(GetAttributes(), false /* no box attributes */) : GetAttributes());
6414 context
.ApplyVirtualAttributes(textAttr
, this);
6416 // Let's make the assumption for now that for content in a paragraph, including
6417 // text, we never have a discontinuous selection. So we only deal with a
6419 wxRichTextRange selectionRange
;
6420 if (selection
.IsValid())
6422 wxRichTextRangeArray selectionRanges
= selection
.GetSelectionForObject(this);
6423 if (selectionRanges
.GetCount() > 0)
6424 selectionRange
= selectionRanges
[0];
6426 selectionRange
= wxRICHTEXT_NO_SELECTION
;
6429 selectionRange
= wxRICHTEXT_NO_SELECTION
;
6431 int offset
= GetRange().GetStart();
6433 wxString str
= m_text
;
6434 if (context
.HasVirtualText(this))
6436 if (!context
.GetVirtualText(this, str
) || str
.Length() != m_text
.Length())
6440 // Replace line break characters with spaces
6441 wxString toRemove
= wxRichTextLineBreakChar
;
6442 str
.Replace(toRemove
, wxT(" "));
6443 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS
|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS
)))
6446 long len
= range
.GetLength();
6447 wxString stringChunk
= str
.Mid(range
.GetStart() - offset
, (size_t) len
);
6449 // Test for the optimized situations where all is selected, or none
6452 wxFont
textFont(GetBuffer()->GetFontTable().FindFont(textAttr
));
6453 wxCheckSetFont(dc
, textFont
);
6454 int charHeight
= dc
.GetCharHeight();
6457 if ( textFont
.IsOk() )
6459 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SMALL_CAPITALS
))
6461 textFont
.SetPointSize((int) (textFont
.GetPointSize()*0.75));
6462 wxCheckSetFont(dc
, textFont
);
6463 charHeight
= dc
.GetCharHeight();
6466 if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
) )
6468 if (textFont
.IsUsingSizeInPixels())
6470 double size
= static_cast<double>(textFont
.GetPixelSize().y
) / wxSCRIPT_MUL_FACTOR
;
6471 textFont
.SetPixelSize(wxSize(0, static_cast<int>(size
)));
6477 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6478 textFont
.SetPointSize(static_cast<int>(size
));
6482 wxCheckSetFont(dc
, textFont
);
6484 else if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) )
6486 if (textFont
.IsUsingSizeInPixels())
6488 double size
= static_cast<double>(textFont
.GetPixelSize().y
) / wxSCRIPT_MUL_FACTOR
;
6489 textFont
.SetPixelSize(wxSize(0, static_cast<int>(size
)));
6491 int sub_height
= static_cast<int>(static_cast<double>(charHeight
) / wxSCRIPT_MUL_FACTOR
);
6492 y
= rect
.y
+ (rect
.height
- sub_height
+ (descent
- m_descent
));
6496 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6497 textFont
.SetPointSize(static_cast<int>(size
));
6499 int sub_height
= static_cast<int>(static_cast<double>(charHeight
) / wxSCRIPT_MUL_FACTOR
);
6500 y
= rect
.y
+ (rect
.height
- sub_height
+ (descent
- m_descent
));
6502 wxCheckSetFont(dc
, textFont
);
6507 y
= rect
.y
+ (rect
.height
- charHeight
- (descent
- m_descent
));
6513 y
= rect
.y
+ (rect
.height
- charHeight
- (descent
- m_descent
));
6516 // TODO: new selection code
6518 // (a) All selected.
6519 if (selectionRange
.GetStart() <= range
.GetStart() && selectionRange
.GetEnd() >= range
.GetEnd())
6521 DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, true);
6523 // (b) None selected.
6524 else if (selectionRange
.GetEnd() < range
.GetStart() || selectionRange
.GetStart() > range
.GetEnd())
6526 // Draw all unselected
6527 DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, false);
6531 // (c) Part selected, part not
6532 // Let's draw unselected chunk, selected chunk, then unselected chunk.
6534 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6536 // 1. Initial unselected chunk, if any, up until start of selection.
6537 if (selectionRange
.GetStart() > range
.GetStart() && selectionRange
.GetStart() <= range
.GetEnd())
6539 int r1
= range
.GetStart();
6540 int s1
= selectionRange
.GetStart()-1;
6541 int fragmentLen
= s1
- r1
+ 1;
6542 if (fragmentLen
< 0)
6544 wxLogDebug(wxT("Mid(%d, %d"), (int)(r1
- offset
), (int)fragmentLen
);
6546 wxString stringFragment
= str
.Mid(r1
- offset
, fragmentLen
);
6548 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false);
6551 if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
)
6553 // Compensate for kerning difference
6554 wxString
stringFragment2(str
.Mid(r1
- offset
, fragmentLen
+1));
6555 wxString
stringFragment3(str
.Mid(r1
- offset
+ fragmentLen
, 1));
6557 wxCoord w1
, h1
, w2
, h2
, w3
, h3
;
6558 dc
.GetTextExtent(stringFragment
, & w1
, & h1
);
6559 dc
.GetTextExtent(stringFragment2
, & w2
, & h2
);
6560 dc
.GetTextExtent(stringFragment3
, & w3
, & h3
);
6562 int kerningDiff
= (w1
+ w3
) - w2
;
6563 x
= x
- kerningDiff
;
6568 // 2. Selected chunk, if any.
6569 if (selectionRange
.GetEnd() >= range
.GetStart())
6571 int s1
= wxMax(selectionRange
.GetStart(), range
.GetStart());
6572 int s2
= wxMin(selectionRange
.GetEnd(), range
.GetEnd());
6574 int fragmentLen
= s2
- s1
+ 1;
6575 if (fragmentLen
< 0)
6577 wxLogDebug(wxT("Mid(%d, %d"), (int)(s1
- offset
), (int)fragmentLen
);
6579 wxString stringFragment
= str
.Mid(s1
- offset
, fragmentLen
);
6581 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, true);
6584 if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
)
6586 // Compensate for kerning difference
6587 wxString
stringFragment2(str
.Mid(s1
- offset
, fragmentLen
+1));
6588 wxString
stringFragment3(str
.Mid(s1
- offset
+ fragmentLen
, 1));
6590 wxCoord w1
, h1
, w2
, h2
, w3
, h3
;
6591 dc
.GetTextExtent(stringFragment
, & w1
, & h1
);
6592 dc
.GetTextExtent(stringFragment2
, & w2
, & h2
);
6593 dc
.GetTextExtent(stringFragment3
, & w3
, & h3
);
6595 int kerningDiff
= (w1
+ w3
) - w2
;
6596 x
= x
- kerningDiff
;
6601 // 3. Remaining unselected chunk, if any
6602 if (selectionRange
.GetEnd() < range
.GetEnd())
6604 int s2
= wxMin(selectionRange
.GetEnd()+1, range
.GetEnd());
6605 int r2
= range
.GetEnd();
6607 int fragmentLen
= r2
- s2
+ 1;
6608 if (fragmentLen
< 0)
6610 wxLogDebug(wxT("Mid(%d, %d"), (int)(s2
- offset
), (int)fragmentLen
);
6612 wxString stringFragment
= str
.Mid(s2
- offset
, fragmentLen
);
6614 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false);
6621 bool wxRichTextPlainText::DrawTabbedString(wxDC
& dc
, const wxRichTextAttr
& attr
, const wxRect
& rect
,wxString
& str
, wxCoord
& x
, wxCoord
& y
, bool selected
)
6623 bool hasTabs
= (str
.Find(wxT('\t')) != wxNOT_FOUND
);
6625 wxArrayInt tabArray
;
6629 if (attr
.GetTabs().IsEmpty())
6630 tabArray
= wxRichTextParagraph::GetDefaultTabs();
6632 tabArray
= attr
.GetTabs();
6633 tabCount
= tabArray
.GetCount();
6635 for (int i
= 0; i
< tabCount
; ++i
)
6637 int pos
= tabArray
[i
];
6638 pos
= ConvertTenthsMMToPixels(dc
, pos
);
6645 int nextTabPos
= -1;
6651 wxColour
highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
));
6652 wxColour
highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
));
6654 wxCheckSetBrush(dc
, wxBrush(highlightColour
));
6655 wxCheckSetPen(dc
, wxPen(highlightColour
));
6656 dc
.SetTextForeground(highlightTextColour
);
6657 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6661 dc
.SetTextForeground(attr
.GetTextColour());
6663 if (attr
.HasFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
) && attr
.GetBackgroundColour().IsOk())
6665 dc
.SetBackgroundMode(wxBRUSHSTYLE_SOLID
);
6666 dc
.SetTextBackground(attr
.GetBackgroundColour());
6669 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6672 wxCoord x_orig
= GetParent()->GetPosition().x
;
6675 // the string has a tab
6676 // break up the string at the Tab
6677 wxString stringChunk
= str
.BeforeFirst(wxT('\t'));
6678 str
= str
.AfterFirst(wxT('\t'));
6679 dc
.GetTextExtent(stringChunk
, & w
, & h
);
6681 bool not_found
= true;
6682 for (int i
= 0; i
< tabCount
&& not_found
; ++i
)
6684 nextTabPos
= tabArray
.Item(i
) + x_orig
;
6686 // Find the next tab position.
6687 // Even if we're at the end of the tab array, we must still draw the chunk.
6689 if (nextTabPos
> tabPos
|| (i
== (tabCount
- 1)))
6691 if (nextTabPos
<= tabPos
)
6693 int defaultTabWidth
= ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
);
6694 nextTabPos
= tabPos
+ defaultTabWidth
;
6701 wxRect
selRect(x
, rect
.y
, w
, rect
.GetHeight());
6702 dc
.DrawRectangle(selRect
);
6704 dc
.DrawText(stringChunk
, x
, y
);
6706 if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
))
6708 wxPen oldPen
= dc
.GetPen();
6709 wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1));
6710 dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5));
6711 wxCheckSetPen(dc
, oldPen
);
6717 hasTabs
= (str
.Find(wxT('\t')) != wxNOT_FOUND
);
6722 dc
.GetTextExtent(str
, & w
, & h
);
6725 wxRect
selRect(x
, rect
.y
, w
, rect
.GetHeight());
6726 dc
.DrawRectangle(selRect
);
6728 dc
.DrawText(str
, x
, y
);
6730 if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
))
6732 wxPen oldPen
= dc
.GetPen();
6733 wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1));
6734 dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5));
6735 wxCheckSetPen(dc
, oldPen
);
6744 /// Lay the item out
6745 bool wxRichTextPlainText::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& WXUNUSED(rect
), const wxRect
& WXUNUSED(parentRect
), int WXUNUSED(style
))
6747 // Only lay out if we haven't already cached the size
6749 GetRangeSize(GetRange(), m_size
, m_descent
, dc
, context
, 0, wxPoint(0, 0));
6751 // Eventually we want to have a reasonable estimate of minimum size.
6752 m_minSize
= wxSize(0, 0);
6757 void wxRichTextPlainText::Copy(const wxRichTextPlainText
& obj
)
6759 wxRichTextObject::Copy(obj
);
6761 m_text
= obj
.m_text
;
6764 /// Get/set the object size for the given range. Returns false if the range
6765 /// is invalid for this object.
6766 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
6768 if (!range
.IsWithin(GetRange()))
6771 wxRichTextParagraph
* para
= wxDynamicCast(GetParent(), wxRichTextParagraph
);
6772 wxASSERT (para
!= NULL
);
6774 int relativeX
= position
.x
- GetParent()->GetPosition().x
;
6776 wxRichTextAttr
textAttr(para
? para
->GetCombinedAttributes(GetAttributes()) : GetAttributes());
6777 context
.ApplyVirtualAttributes(textAttr
, (wxRichTextObject
*) this);
6779 // Always assume unformatted text, since at this level we have no knowledge
6780 // of line breaks - and we don't need it, since we'll calculate size within
6781 // formatted text by doing it in chunks according to the line ranges
6783 bool bScript(false);
6784 wxFont
font(GetBuffer()->GetFontTable().FindFont(textAttr
));
6787 if ( textAttr
.HasTextEffects() && ( (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
)
6788 || (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) ) )
6790 wxFont textFont
= font
;
6791 if (textFont
.IsUsingSizeInPixels())
6793 double size
= static_cast<double>(textFont
.GetPixelSize().y
) / wxSCRIPT_MUL_FACTOR
;
6794 textFont
.SetPixelSize(wxSize(0, static_cast<int>(size
)));
6798 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6799 textFont
.SetPointSize(static_cast<int>(size
));
6801 wxCheckSetFont(dc
, textFont
);
6804 else if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SMALL_CAPITALS
))
6806 wxFont textFont
= font
;
6807 textFont
.SetPointSize((int) (textFont
.GetPointSize()*0.75));
6808 wxCheckSetFont(dc
, textFont
);
6813 wxCheckSetFont(dc
, font
);
6817 bool haveDescent
= false;
6818 int startPos
= range
.GetStart() - GetRange().GetStart();
6819 long len
= range
.GetLength();
6821 wxString
str(m_text
);
6822 if (context
.HasVirtualText(this))
6824 if (!context
.GetVirtualText(this, str
) || str
.Length() != m_text
.Length())
6828 wxString toReplace
= wxRichTextLineBreakChar
;
6829 str
.Replace(toReplace
, wxT(" "));
6831 wxString stringChunk
= str
.Mid(startPos
, (size_t) len
);
6833 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS
|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS
)))
6834 stringChunk
.MakeUpper();
6838 if (stringChunk
.Find(wxT('\t')) != wxNOT_FOUND
)
6840 // the string has a tab
6841 wxArrayInt tabArray
;
6842 if (textAttr
.GetTabs().IsEmpty())
6843 tabArray
= wxRichTextParagraph::GetDefaultTabs();
6845 tabArray
= textAttr
.GetTabs();
6847 int tabCount
= tabArray
.GetCount();
6849 for (int i
= 0; i
< tabCount
; ++i
)
6851 int pos
= tabArray
[i
];
6852 pos
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, pos
);
6856 int nextTabPos
= -1;
6858 while (stringChunk
.Find(wxT('\t')) >= 0)
6860 int absoluteWidth
= 0;
6862 // the string has a tab
6863 // break up the string at the Tab
6864 wxString stringFragment
= stringChunk
.BeforeFirst(wxT('\t'));
6865 stringChunk
= stringChunk
.AfterFirst(wxT('\t'));
6870 if (partialExtents
->GetCount() > 0)
6871 oldWidth
= (*partialExtents
)[partialExtents
->GetCount()-1];
6875 // Add these partial extents
6877 dc
.GetPartialTextExtents(stringFragment
, p
);
6879 for (j
= 0; j
< p
.GetCount(); j
++)
6880 partialExtents
->Add(oldWidth
+ p
[j
]);
6882 if (partialExtents
->GetCount() > 0)
6883 absoluteWidth
= (*partialExtents
)[(*partialExtents
).GetCount()-1] + relativeX
;
6885 absoluteWidth
= relativeX
;
6889 dc
.GetTextExtent(stringFragment
, & w
, & h
);
6891 absoluteWidth
= width
+ relativeX
;
6895 bool notFound
= true;
6896 for (int i
= 0; i
< tabCount
&& notFound
; ++i
)
6898 nextTabPos
= tabArray
.Item(i
);
6900 // Find the next tab position.
6901 // Even if we're at the end of the tab array, we must still process the chunk.
6903 if (nextTabPos
> absoluteWidth
|| (i
== (tabCount
- 1)))
6905 if (nextTabPos
<= absoluteWidth
)
6907 int defaultTabWidth
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
);
6908 nextTabPos
= absoluteWidth
+ defaultTabWidth
;
6912 width
= nextTabPos
- relativeX
;
6915 partialExtents
->Add(width
);
6921 if (!stringChunk
.IsEmpty())
6926 if (partialExtents
->GetCount() > 0)
6927 oldWidth
= (*partialExtents
)[partialExtents
->GetCount()-1];
6931 // Add these partial extents
6933 dc
.GetPartialTextExtents(stringChunk
, p
);
6935 for (j
= 0; j
< p
.GetCount(); j
++)
6936 partialExtents
->Add(oldWidth
+ p
[j
]);
6940 dc
.GetTextExtent(stringChunk
, & w
, & h
, & descent
);
6948 int charHeight
= dc
.GetCharHeight();
6949 if ((*partialExtents
).GetCount() > 0)
6950 w
= (*partialExtents
)[partialExtents
->GetCount()-1];
6953 size
= wxSize(w
, charHeight
);
6957 size
= wxSize(width
, dc
.GetCharHeight());
6961 dc
.GetTextExtent(wxT("X"), & w
, & h
, & descent
);
6969 /// Do a split, returning an object containing the second part, and setting
6970 /// the first part in 'this'.
6971 wxRichTextObject
* wxRichTextPlainText::DoSplit(long pos
)
6973 long index
= pos
- GetRange().GetStart();
6975 if (index
< 0 || index
>= (int) m_text
.length())
6978 wxString firstPart
= m_text
.Mid(0, index
);
6979 wxString secondPart
= m_text
.Mid(index
);
6983 wxRichTextPlainText
* newObject
= new wxRichTextPlainText(secondPart
);
6984 newObject
->SetAttributes(GetAttributes());
6985 newObject
->SetProperties(GetProperties());
6987 newObject
->SetRange(wxRichTextRange(pos
, GetRange().GetEnd()));
6988 GetRange().SetEnd(pos
-1);
6994 void wxRichTextPlainText::CalculateRange(long start
, long& end
)
6996 end
= start
+ m_text
.length() - 1;
6997 m_range
.SetRange(start
, end
);
7001 bool wxRichTextPlainText::DeleteRange(const wxRichTextRange
& range
)
7003 wxRichTextRange r
= range
;
7005 r
.LimitTo(GetRange());
7007 if (r
.GetStart() == GetRange().GetStart() && r
.GetEnd() == GetRange().GetEnd())
7013 long startIndex
= r
.GetStart() - GetRange().GetStart();
7014 long len
= r
.GetLength();
7016 m_text
= m_text
.Mid(0, startIndex
) + m_text
.Mid(startIndex
+len
);
7020 /// Get text for the given range.
7021 wxString
wxRichTextPlainText::GetTextForRange(const wxRichTextRange
& range
) const
7023 wxRichTextRange r
= range
;
7025 r
.LimitTo(GetRange());
7027 long startIndex
= r
.GetStart() - GetRange().GetStart();
7028 long len
= r
.GetLength();
7030 return m_text
.Mid(startIndex
, len
);
7033 /// Returns true if this object can merge itself with the given one.
7034 bool wxRichTextPlainText::CanMerge(wxRichTextObject
* object
, wxRichTextDrawingContext
& context
) const
7037 if (!context
.GetVirtualAttributesEnabled())
7039 return object
->GetClassInfo() == wxCLASSINFO(wxRichTextPlainText
) &&
7040 (m_text
.empty() || (wxTextAttrEq(GetAttributes(), object
->GetAttributes()) && m_properties
== object
->GetProperties()));
7044 wxRichTextPlainText
* otherObj
= wxDynamicCast(object
, wxRichTextPlainText
);
7045 if (!otherObj
|| m_text
.empty())
7048 if (!wxTextAttrEq(GetAttributes(), object
->GetAttributes()) || !(m_properties
== object
->GetProperties()))
7051 // Check if differing virtual attributes makes it impossible to merge
7054 bool hasVirtualAttr1
= context
.HasVirtualAttributes((wxRichTextObject
*) this);
7055 bool hasVirtualAttr2
= context
.HasVirtualAttributes((wxRichTextObject
*) object
);
7056 if (!hasVirtualAttr1
&& !hasVirtualAttr2
)
7058 else if (hasVirtualAttr1
!= hasVirtualAttr2
)
7062 wxRichTextAttr virtualAttr1
= context
.GetVirtualAttributes((wxRichTextObject
*) this);
7063 wxRichTextAttr virtualAttr2
= context
.GetVirtualAttributes((wxRichTextObject
*) object
);
7064 return virtualAttr1
== virtualAttr2
;
7069 /// Returns true if this object merged itself with the given one.
7070 /// The calling code will then delete the given object.
7071 bool wxRichTextPlainText::Merge(wxRichTextObject
* object
, wxRichTextDrawingContext
& WXUNUSED(context
))
7073 wxRichTextPlainText
* textObject
= wxDynamicCast(object
, wxRichTextPlainText
);
7074 wxASSERT( textObject
!= NULL
);
7078 m_text
+= textObject
->GetText();
7079 wxRichTextApplyStyle(m_attributes
, textObject
->GetAttributes());
7086 bool wxRichTextPlainText::CanSplit(wxRichTextDrawingContext
& context
) const
7088 // If this object has any virtual attributes at all, whether for the whole object
7089 // or individual ones, we should try splitting it by calling Split.
7090 // Must be more than one character in order to be able to split.
7091 return m_text
.Length() > 1 && context
.HasVirtualAttributes((wxRichTextObject
*) this);
7094 wxRichTextObject
* wxRichTextPlainText::Split(wxRichTextDrawingContext
& context
)
7096 int count
= context
.GetVirtualSubobjectAttributesCount(this);
7097 if (count
> 0 && GetParent())
7099 wxRichTextCompositeObject
* parent
= wxDynamicCast(GetParent(), wxRichTextCompositeObject
);
7100 wxRichTextObjectList::compatibility_iterator node
= parent
->GetChildren().Find(this);
7103 const wxRichTextAttr emptyAttr
;
7104 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
7106 wxArrayInt positions
;
7107 wxRichTextAttrArray attributes
;
7108 if (context
.GetVirtualSubobjectAttributes(this, positions
, attributes
) && positions
.GetCount() > 0)
7110 wxASSERT(positions
.GetCount() == attributes
.GetCount());
7112 // We will gather up runs of text with the same virtual attributes
7114 int len
= m_text
.Length();
7117 // runStart and runEnd represent the accumulated run with a consistent attribute
7118 // that hasn't yet been appended
7121 wxRichTextAttr currentAttr
;
7122 wxString text
= m_text
;
7123 wxRichTextPlainText
* lastPlainText
= this;
7125 for (i
= 0; i
< (int) positions
.GetCount(); i
++)
7127 int pos
= positions
[i
];
7128 wxASSERT(pos
>= 0 && pos
< len
);
7129 if (pos
>= 0 && pos
< len
)
7131 const wxRichTextAttr
& attr
= attributes
[i
];
7138 // Check if there was a gap from the last known attribute and this.
7139 // In that case, we need to do something with the span of non-attributed text.
7140 else if ((pos
-1) > runEnd
)
7144 // We hadn't processed anything previously, so the previous run is from the text start
7145 // to just before this position. The current attribute remains empty.
7151 // If the previous attribute matches the gap's attribute (i.e., no attributes)
7152 // then just extend the run.
7153 if (currentAttr
.IsDefault())
7159 // We need to add an object, or reuse the existing one.
7162 lastPlainText
= this;
7163 SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7167 wxRichTextPlainText
* obj
= new wxRichTextPlainText
;
7168 lastPlainText
= obj
;
7169 obj
->SetAttributes(GetAttributes());
7170 obj
->SetProperties(GetProperties());
7171 obj
->SetParent(parent
);
7173 obj
->SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7175 parent
->GetChildren().Insert(next
, obj
);
7177 parent
->GetChildren().Append(obj
);
7180 runStart
= runEnd
+1;
7183 currentAttr
= emptyAttr
;
7188 wxASSERT(runEnd
== pos
-1);
7190 // Now we only have to deal with the previous run
7191 if (currentAttr
== attr
)
7193 // If we still have the same attributes, then we
7194 // simply increase the run size.
7201 // We need to add an object, or reuse the existing one.
7204 lastPlainText
= this;
7205 SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7209 wxRichTextPlainText
* obj
= new wxRichTextPlainText
;
7210 lastPlainText
= obj
;
7211 obj
->SetAttributes(GetAttributes());
7212 obj
->SetProperties(GetProperties());
7213 obj
->SetParent(parent
);
7215 obj
->SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7217 parent
->GetChildren().Insert(next
, obj
);
7219 parent
->GetChildren().Append(obj
);
7231 // We may still have a run to add, and possibly a no-attribute text fragment after that.
7232 // If the whole string was already a single attribute (the run covers the whole string), don't split.
7233 if ((runStart
!= -1) && !(runStart
== 0 && runEnd
== (len
-1)))
7235 // If the current attribute is empty, merge the run with the next fragment
7236 // which by definition (because it's not specified) has empty attributes.
7237 if (currentAttr
.IsDefault())
7240 if (runEnd
< (len
-1))
7242 // We need to add an object, or reuse the existing one.
7245 lastPlainText
= this;
7246 SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7250 wxRichTextPlainText
* obj
= new wxRichTextPlainText
;
7251 lastPlainText
= obj
;
7252 obj
->SetAttributes(GetAttributes());
7253 obj
->SetProperties(GetProperties());
7254 obj
->SetParent(parent
);
7256 obj
->SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7258 parent
->GetChildren().Insert(next
, obj
);
7260 parent
->GetChildren().Append(obj
);
7263 runStart
= runEnd
+1;
7267 // Now the last, non-attributed fragment at the end, if any
7268 if ((runStart
< len
) && !(runStart
== 0 && runEnd
== (len
-1)))
7270 wxASSERT(runStart
!= 0);
7272 wxRichTextPlainText
* obj
= new wxRichTextPlainText
;
7273 obj
->SetAttributes(GetAttributes());
7274 obj
->SetProperties(GetProperties());
7275 obj
->SetParent(parent
);
7277 obj
->SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7279 parent
->GetChildren().Insert(next
, obj
);
7281 parent
->GetChildren().Append(obj
);
7283 lastPlainText
= obj
;
7287 return lastPlainText
;
7294 /// Dump to output stream for debugging
7295 void wxRichTextPlainText::Dump(wxTextOutputStream
& stream
)
7297 wxRichTextObject::Dump(stream
);
7298 stream
<< m_text
<< wxT("\n");
7301 /// Get the first position from pos that has a line break character.
7302 long wxRichTextPlainText::GetFirstLineBreakPosition(long pos
)
7305 int len
= m_text
.length();
7306 int startPos
= pos
- m_range
.GetStart();
7307 for (i
= startPos
; i
< len
; i
++)
7309 wxChar ch
= m_text
[i
];
7310 if (ch
== wxRichTextLineBreakChar
)
7312 return i
+ m_range
.GetStart();
7320 * This is a kind of box, used to represent the whole buffer
7323 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBuffer
, wxRichTextParagraphLayoutBox
)
7325 wxList
wxRichTextBuffer::sm_handlers
;
7326 wxList
wxRichTextBuffer::sm_drawingHandlers
;
7327 wxRichTextFieldTypeHashMap
wxRichTextBuffer::sm_fieldTypes
;
7328 wxRichTextRenderer
* wxRichTextBuffer::sm_renderer
= NULL
;
7329 int wxRichTextBuffer::sm_bulletRightMargin
= 20;
7330 float wxRichTextBuffer::sm_bulletProportion
= (float) 0.3;
7331 bool wxRichTextBuffer::sm_floatingLayoutMode
= true;
7334 void wxRichTextBuffer::Init()
7336 m_commandProcessor
= new wxCommandProcessor
;
7337 m_styleSheet
= NULL
;
7339 m_batchedCommandDepth
= 0;
7340 m_batchedCommand
= NULL
;
7344 m_dimensionScale
= 1.0;
7350 wxRichTextBuffer::~wxRichTextBuffer()
7352 delete m_commandProcessor
;
7353 delete m_batchedCommand
;
7356 ClearEventHandlers();
7359 void wxRichTextBuffer::ResetAndClearCommands()
7363 GetCommandProcessor()->ClearCommands();
7366 Invalidate(wxRICHTEXT_ALL
);
7369 void wxRichTextBuffer::Copy(const wxRichTextBuffer
& obj
)
7371 wxRichTextParagraphLayoutBox::Copy(obj
);
7373 m_styleSheet
= obj
.m_styleSheet
;
7374 m_modified
= obj
.m_modified
;
7375 m_batchedCommandDepth
= 0;
7376 if (m_batchedCommand
)
7377 delete m_batchedCommand
;
7378 m_batchedCommand
= NULL
;
7379 m_suppressUndo
= obj
.m_suppressUndo
;
7380 m_invalidRange
= obj
.m_invalidRange
;
7381 m_dimensionScale
= obj
.m_dimensionScale
;
7382 m_fontScale
= obj
.m_fontScale
;
7385 /// Push style sheet to top of stack
7386 bool wxRichTextBuffer::PushStyleSheet(wxRichTextStyleSheet
* styleSheet
)
7389 styleSheet
->InsertSheet(m_styleSheet
);
7391 SetStyleSheet(styleSheet
);
7396 /// Pop style sheet from top of stack
7397 wxRichTextStyleSheet
* wxRichTextBuffer::PopStyleSheet()
7401 wxRichTextStyleSheet
* oldSheet
= m_styleSheet
;
7402 m_styleSheet
= oldSheet
->GetNextSheet();
7411 /// Submit command to insert paragraphs
7412 bool wxRichTextBuffer::InsertParagraphsWithUndo(long pos
, const wxRichTextParagraphLayoutBox
& paragraphs
, wxRichTextCtrl
* ctrl
, int flags
)
7414 return ctrl
->GetFocusObject()->InsertParagraphsWithUndo(this, pos
, paragraphs
, ctrl
, flags
);
7417 /// Submit command to insert paragraphs
7418 bool wxRichTextParagraphLayoutBox::InsertParagraphsWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxRichTextParagraphLayoutBox
& paragraphs
, wxRichTextCtrl
* ctrl
, int WXUNUSED(flags
))
7420 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7422 action
->GetNewParagraphs() = paragraphs
;
7424 action
->SetPosition(pos
);
7426 wxRichTextRange range
= wxRichTextRange(pos
, pos
+ paragraphs
.GetOwnRange().GetEnd() - 1);
7427 if (!paragraphs
.GetPartialParagraph())
7428 range
.SetEnd(range
.GetEnd()+1);
7430 // Set the range we'll need to delete in Undo
7431 action
->SetRange(range
);
7433 buffer
->SubmitAction(action
);
7438 /// Submit command to insert the given text
7439 bool wxRichTextBuffer::InsertTextWithUndo(long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
, int flags
)
7441 return ctrl
->GetFocusObject()->InsertTextWithUndo(this, pos
, text
, ctrl
, flags
);
7444 /// Submit command to insert the given text
7445 bool wxRichTextParagraphLayoutBox::InsertTextWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
, int flags
)
7447 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7449 wxRichTextAttr
* p
= NULL
;
7450 wxRichTextAttr paraAttr
;
7451 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7453 // Get appropriate paragraph style
7454 paraAttr
= GetStyleForNewParagraph(buffer
, pos
, false, false);
7455 if (!paraAttr
.IsDefault())
7459 action
->GetNewParagraphs().AddParagraphs(text
, p
);
7461 int length
= action
->GetNewParagraphs().GetOwnRange().GetLength();
7463 if (!text
.empty() && text
.Last() != wxT('\n'))
7465 // Don't count the newline when undoing
7467 action
->GetNewParagraphs().SetPartialParagraph(true);
7469 else if (!text
.empty() && text
.Last() == wxT('\n'))
7472 action
->SetPosition(pos
);
7474 // Set the range we'll need to delete in Undo
7475 action
->SetRange(wxRichTextRange(pos
, pos
+ length
- 1));
7477 buffer
->SubmitAction(action
);
7482 /// Submit command to insert the given text
7483 bool wxRichTextBuffer::InsertNewlineWithUndo(long pos
, wxRichTextCtrl
* ctrl
, int flags
)
7485 return ctrl
->GetFocusObject()->InsertNewlineWithUndo(this, pos
, ctrl
, flags
);
7488 /// Submit command to insert the given text
7489 bool wxRichTextParagraphLayoutBox::InsertNewlineWithUndo(wxRichTextBuffer
* buffer
, long pos
, wxRichTextCtrl
* ctrl
, int flags
)
7491 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7493 wxRichTextAttr
* p
= NULL
;
7494 wxRichTextAttr paraAttr
;
7495 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7497 paraAttr
= GetStyleForNewParagraph(buffer
, pos
, false, true /* look for next paragraph style */);
7498 if (!paraAttr
.IsDefault())
7502 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7503 // Don't include box attributes such as margins
7504 attr
.GetTextBoxAttr().Reset();
7506 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(wxEmptyString
, this, & attr
);
7507 action
->GetNewParagraphs().AppendChild(newPara
);
7508 action
->GetNewParagraphs().UpdateRanges();
7509 action
->GetNewParagraphs().SetPartialParagraph(false);
7510 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
, false);
7514 newPara
->SetAttributes(*p
);
7516 if (flags
& wxRICHTEXT_INSERT_INTERACTIVE
)
7518 if (para
&& para
->GetRange().GetEnd() == pos
)
7521 // Now see if we need to number the paragraph.
7522 if (newPara
->GetAttributes().HasBulletNumber())
7524 wxRichTextAttr numberingAttr
;
7525 if (FindNextParagraphNumber(para
, numberingAttr
))
7526 wxRichTextApplyStyle(newPara
->GetAttributes(), (const wxRichTextAttr
&) numberingAttr
);
7530 action
->SetPosition(pos
);
7532 // Use the default character style
7533 if (!buffer
->GetDefaultStyle().IsDefault() && newPara
->GetChildren().GetFirst())
7535 // Check whether the default style merely reflects the paragraph/basic style,
7536 // in which case don't apply it.
7537 wxRichTextAttr
defaultStyle(buffer
->GetDefaultStyle());
7538 defaultStyle
.GetTextBoxAttr().Reset();
7539 wxRichTextAttr toApply
;
7542 wxRichTextAttr combinedAttr
= para
->GetCombinedAttributes(true /* include box attributes */);
7543 wxRichTextAttr newAttr
;
7544 // This filters out attributes that are accounted for by the current
7545 // paragraph/basic style
7546 wxRichTextApplyStyle(toApply
, defaultStyle
, & combinedAttr
);
7549 toApply
= defaultStyle
;
7551 if (!toApply
.IsDefault())
7552 newPara
->GetChildren().GetFirst()->GetData()->SetAttributes(toApply
);
7555 // Set the range we'll need to delete in Undo
7556 action
->SetRange(wxRichTextRange(pos1
, pos1
));
7558 buffer
->SubmitAction(action
);
7563 /// Submit command to insert the given image
7564 bool wxRichTextBuffer::InsertImageWithUndo(long pos
, const wxRichTextImageBlock
& imageBlock
, wxRichTextCtrl
* ctrl
, int flags
,
7565 const wxRichTextAttr
& textAttr
)
7567 return ctrl
->GetFocusObject()->InsertImageWithUndo(this, pos
, imageBlock
, ctrl
, flags
, textAttr
);
7570 /// Submit command to insert the given image
7571 bool wxRichTextParagraphLayoutBox::InsertImageWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxRichTextImageBlock
& imageBlock
,
7572 wxRichTextCtrl
* ctrl
, int flags
,
7573 const wxRichTextAttr
& textAttr
)
7575 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7577 wxRichTextAttr
* p
= NULL
;
7578 wxRichTextAttr paraAttr
;
7579 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7581 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
7582 if (!paraAttr
.IsDefault())
7586 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7588 // Don't include box attributes such as margins
7589 attr
.GetTextBoxAttr().Reset();
7591 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7593 newPara
->SetAttributes(*p
);
7595 wxRichTextImage
* imageObject
= new wxRichTextImage(imageBlock
, newPara
);
7596 newPara
->AppendChild(imageObject
);
7597 imageObject
->SetAttributes(textAttr
);
7598 action
->GetNewParagraphs().AppendChild(newPara
);
7599 action
->GetNewParagraphs().UpdateRanges();
7601 action
->GetNewParagraphs().SetPartialParagraph(true);
7603 action
->SetPosition(pos
);
7605 // Set the range we'll need to delete in Undo
7606 action
->SetRange(wxRichTextRange(pos
, pos
));
7608 buffer
->SubmitAction(action
);
7613 // Insert an object with no change of it
7614 wxRichTextObject
* wxRichTextBuffer::InsertObjectWithUndo(long pos
, wxRichTextObject
*object
, wxRichTextCtrl
* ctrl
, int flags
)
7616 return ctrl
->GetFocusObject()->InsertObjectWithUndo(this, pos
, object
, ctrl
, flags
);
7619 // Insert an object with no change of it
7620 wxRichTextObject
* wxRichTextParagraphLayoutBox::InsertObjectWithUndo(wxRichTextBuffer
* buffer
, long pos
, wxRichTextObject
*object
, wxRichTextCtrl
* ctrl
, int flags
)
7622 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Object"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7624 wxRichTextAttr
* p
= NULL
;
7625 wxRichTextAttr paraAttr
;
7626 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7628 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
7629 if (!paraAttr
.IsDefault())
7633 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7635 // Don't include box attributes such as margins
7636 attr
.GetTextBoxAttr().Reset();
7638 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7640 newPara
->SetAttributes(*p
);
7642 newPara
->AppendChild(object
);
7643 action
->GetNewParagraphs().AppendChild(newPara
);
7644 action
->GetNewParagraphs().UpdateRanges();
7646 action
->GetNewParagraphs().SetPartialParagraph(true);
7648 action
->SetPosition(pos
);
7650 // Set the range we'll need to delete in Undo
7651 action
->SetRange(wxRichTextRange(pos
, pos
));
7653 buffer
->SubmitAction(action
);
7655 wxRichTextObject
* obj
= GetLeafObjectAtPosition(pos
);
7659 wxRichTextField
* wxRichTextParagraphLayoutBox::InsertFieldWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxString
& fieldType
,
7660 const wxRichTextProperties
& properties
,
7661 wxRichTextCtrl
* ctrl
, int flags
,
7662 const wxRichTextAttr
& textAttr
)
7664 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Field"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7666 wxRichTextAttr
* p
= NULL
;
7667 wxRichTextAttr paraAttr
;
7668 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7670 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
7671 if (!paraAttr
.IsDefault())
7675 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7677 // Don't include box attributes such as margins
7678 attr
.GetTextBoxAttr().Reset();
7680 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7682 newPara
->SetAttributes(*p
);
7684 wxRichTextField
* fieldObject
= new wxRichTextField();
7685 fieldObject
->wxRichTextObject::SetProperties(properties
);
7686 fieldObject
->SetFieldType(fieldType
);
7687 fieldObject
->SetAttributes(textAttr
);
7688 newPara
->AppendChild(fieldObject
);
7689 action
->GetNewParagraphs().AppendChild(newPara
);
7690 action
->GetNewParagraphs().UpdateRanges();
7691 action
->GetNewParagraphs().SetPartialParagraph(true);
7692 action
->SetPosition(pos
);
7694 // Set the range we'll need to delete in Undo
7695 action
->SetRange(wxRichTextRange(pos
, pos
));
7697 buffer
->SubmitAction(action
);
7699 wxRichTextField
* obj
= wxDynamicCast(GetLeafObjectAtPosition(pos
), wxRichTextField
);
7703 /// Get the style that is appropriate for a new paragraph at this position.
7704 /// If the previous paragraph has a paragraph style name, look up the next-paragraph
7706 wxRichTextAttr
wxRichTextParagraphLayoutBox::GetStyleForNewParagraph(wxRichTextBuffer
* buffer
, long pos
, bool caretPosition
, bool lookUpNewParaStyle
) const
7708 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
, caretPosition
);
7711 wxRichTextAttr attr
;
7712 bool foundAttributes
= false;
7714 // Look for a matching paragraph style
7715 if (lookUpNewParaStyle
&& !para
->GetAttributes().GetParagraphStyleName().IsEmpty() && buffer
->GetStyleSheet())
7717 wxRichTextParagraphStyleDefinition
* paraDef
= buffer
->GetStyleSheet()->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
7720 // If we're not at the end of the paragraph, then we apply THIS style, and not the designated next style.
7721 if (para
->GetRange().GetEnd() == pos
&& !paraDef
->GetNextStyle().IsEmpty())
7723 wxRichTextParagraphStyleDefinition
* nextParaDef
= buffer
->GetStyleSheet()->FindParagraphStyle(paraDef
->GetNextStyle());
7726 foundAttributes
= true;
7727 attr
= nextParaDef
->GetStyleMergedWithBase(buffer
->GetStyleSheet());
7731 // If we didn't find the 'next style', use this style instead.
7732 if (!foundAttributes
)
7734 foundAttributes
= true;
7735 attr
= paraDef
->GetStyleMergedWithBase(buffer
->GetStyleSheet());
7740 // Also apply list style if present
7741 if (lookUpNewParaStyle
&& !para
->GetAttributes().GetListStyleName().IsEmpty() && buffer
->GetStyleSheet())
7743 wxRichTextListStyleDefinition
* listDef
= buffer
->GetStyleSheet()->FindListStyle(para
->GetAttributes().GetListStyleName());
7746 int thisIndent
= para
->GetAttributes().GetLeftIndent();
7747 int thisLevel
= para
->GetAttributes().HasOutlineLevel() ? para
->GetAttributes().GetOutlineLevel() : listDef
->FindLevelForIndent(thisIndent
);
7749 // Apply the overall list style, and item style for this level
7750 wxRichTextAttr
listStyle(listDef
->GetCombinedStyleForLevel(thisLevel
, buffer
->GetStyleSheet()));
7751 wxRichTextApplyStyle(attr
, listStyle
);
7752 attr
.SetOutlineLevel(thisLevel
);
7753 if (para
->GetAttributes().HasBulletNumber())
7754 attr
.SetBulletNumber(para
->GetAttributes().GetBulletNumber());
7758 if (!foundAttributes
)
7760 attr
= para
->GetAttributes();
7761 int flags
= attr
.GetFlags();
7763 // Eliminate character styles
7764 flags
&= ( (~ wxTEXT_ATTR_FONT
) |
7765 (~ wxTEXT_ATTR_TEXT_COLOUR
) |
7766 (~ wxTEXT_ATTR_BACKGROUND_COLOUR
) );
7767 attr
.SetFlags(flags
);
7773 return wxRichTextAttr();
7776 /// Submit command to delete this range
7777 bool wxRichTextBuffer::DeleteRangeWithUndo(const wxRichTextRange
& range
, wxRichTextCtrl
* ctrl
)
7779 return ctrl
->GetFocusObject()->DeleteRangeWithUndo(range
, ctrl
, this);
7782 /// Submit command to delete this range
7783 bool wxRichTextParagraphLayoutBox::DeleteRangeWithUndo(const wxRichTextRange
& range
, wxRichTextCtrl
* ctrl
, wxRichTextBuffer
* buffer
)
7785 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Delete"), wxRICHTEXT_DELETE
, buffer
, this, ctrl
);
7787 action
->SetPosition(ctrl
->GetCaretPosition());
7789 // Set the range to delete
7790 action
->SetRange(range
);
7792 // Copy the fragment that we'll need to restore in Undo
7793 CopyFragment(range
, action
->GetOldParagraphs());
7795 // See if we're deleting a paragraph marker, in which case we need to
7796 // make a note not to copy the attributes from the 2nd paragraph to the 1st.
7797 if (range
.GetStart() == range
.GetEnd())
7799 wxRichTextParagraph
* para
= GetParagraphAtPosition(range
.GetStart());
7800 if (para
&& para
->GetRange().GetEnd() == range
.GetEnd())
7802 wxRichTextParagraph
* nextPara
= GetParagraphAtPosition(range
.GetStart()+1);
7803 if (nextPara
&& nextPara
!= para
)
7805 action
->GetOldParagraphs().GetChildren().GetFirst()->GetData()->SetAttributes(nextPara
->GetAttributes());
7806 action
->GetOldParagraphs().GetAttributes().SetFlags(action
->GetOldParagraphs().GetAttributes().GetFlags() | wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
);
7811 buffer
->SubmitAction(action
);
7816 /// Collapse undo/redo commands
7817 bool wxRichTextBuffer::BeginBatchUndo(const wxString
& cmdName
)
7819 if (m_batchedCommandDepth
== 0)
7821 wxASSERT(m_batchedCommand
== NULL
);
7822 if (m_batchedCommand
)
7824 GetCommandProcessor()->Store(m_batchedCommand
);
7826 m_batchedCommand
= new wxRichTextCommand(cmdName
);
7829 m_batchedCommandDepth
++;
7834 /// Collapse undo/redo commands
7835 bool wxRichTextBuffer::EndBatchUndo()
7837 m_batchedCommandDepth
--;
7839 wxASSERT(m_batchedCommandDepth
>= 0);
7840 wxASSERT(m_batchedCommand
!= NULL
);
7842 if (m_batchedCommandDepth
== 0)
7844 GetCommandProcessor()->Store(m_batchedCommand
);
7845 m_batchedCommand
= NULL
;
7851 /// Submit immediately, or delay according to whether collapsing is on
7852 bool wxRichTextBuffer::SubmitAction(wxRichTextAction
* action
)
7854 if (action
&& !action
->GetNewParagraphs().IsEmpty())
7855 PrepareContent(action
->GetNewParagraphs());
7857 if (BatchingUndo() && m_batchedCommand
&& !SuppressingUndo())
7859 wxRichTextCommand
* cmd
= new wxRichTextCommand(action
->GetName());
7860 cmd
->AddAction(action
);
7862 cmd
->GetActions().Clear();
7865 m_batchedCommand
->AddAction(action
);
7869 wxRichTextCommand
* cmd
= new wxRichTextCommand(action
->GetName());
7870 cmd
->AddAction(action
);
7872 // Only store it if we're not suppressing undo.
7873 return GetCommandProcessor()->Submit(cmd
, !SuppressingUndo());
7879 /// Begin suppressing undo/redo commands.
7880 bool wxRichTextBuffer::BeginSuppressUndo()
7887 /// End suppressing undo/redo commands.
7888 bool wxRichTextBuffer::EndSuppressUndo()
7895 /// Begin using a style
7896 bool wxRichTextBuffer::BeginStyle(const wxRichTextAttr
& style
)
7898 wxRichTextAttr
newStyle(GetDefaultStyle());
7899 newStyle
.GetTextBoxAttr().Reset();
7901 // Save the old default style
7902 m_attributeStack
.Append((wxObject
*) new wxRichTextAttr(newStyle
));
7904 wxRichTextApplyStyle(newStyle
, style
);
7905 newStyle
.SetFlags(style
.GetFlags()|newStyle
.GetFlags());
7907 SetDefaultStyle(newStyle
);
7913 bool wxRichTextBuffer::EndStyle()
7915 if (!m_attributeStack
.GetFirst())
7917 wxLogDebug(_("Too many EndStyle calls!"));
7921 wxList::compatibility_iterator node
= m_attributeStack
.GetLast();
7922 wxRichTextAttr
* attr
= (wxRichTextAttr
*)node
->GetData();
7923 m_attributeStack
.Erase(node
);
7925 SetDefaultStyle(*attr
);
7932 bool wxRichTextBuffer::EndAllStyles()
7934 while (m_attributeStack
.GetCount() != 0)
7939 /// Clear the style stack
7940 void wxRichTextBuffer::ClearStyleStack()
7942 for (wxList::compatibility_iterator node
= m_attributeStack
.GetFirst(); node
; node
= node
->GetNext())
7943 delete (wxRichTextAttr
*) node
->GetData();
7944 m_attributeStack
.Clear();
7947 /// Begin using bold
7948 bool wxRichTextBuffer::BeginBold()
7950 wxRichTextAttr attr
;
7951 attr
.SetFontWeight(wxFONTWEIGHT_BOLD
);
7953 return BeginStyle(attr
);
7956 /// Begin using italic
7957 bool wxRichTextBuffer::BeginItalic()
7959 wxRichTextAttr attr
;
7960 attr
.SetFontStyle(wxFONTSTYLE_ITALIC
);
7962 return BeginStyle(attr
);
7965 /// Begin using underline
7966 bool wxRichTextBuffer::BeginUnderline()
7968 wxRichTextAttr attr
;
7969 attr
.SetFontUnderlined(true);
7971 return BeginStyle(attr
);
7974 /// Begin using point size
7975 bool wxRichTextBuffer::BeginFontSize(int pointSize
)
7977 wxRichTextAttr attr
;
7978 attr
.SetFontSize(pointSize
);
7980 return BeginStyle(attr
);
7983 /// Begin using this font
7984 bool wxRichTextBuffer::BeginFont(const wxFont
& font
)
7986 wxRichTextAttr attr
;
7989 return BeginStyle(attr
);
7992 /// Begin using this colour
7993 bool wxRichTextBuffer::BeginTextColour(const wxColour
& colour
)
7995 wxRichTextAttr attr
;
7996 attr
.SetFlags(wxTEXT_ATTR_TEXT_COLOUR
);
7997 attr
.SetTextColour(colour
);
7999 return BeginStyle(attr
);
8002 /// Begin using alignment
8003 bool wxRichTextBuffer::BeginAlignment(wxTextAttrAlignment alignment
)
8005 wxRichTextAttr attr
;
8006 attr
.SetFlags(wxTEXT_ATTR_ALIGNMENT
);
8007 attr
.SetAlignment(alignment
);
8009 return BeginStyle(attr
);
8012 /// Begin left indent
8013 bool wxRichTextBuffer::BeginLeftIndent(int leftIndent
, int leftSubIndent
)
8015 wxRichTextAttr attr
;
8016 attr
.SetFlags(wxTEXT_ATTR_LEFT_INDENT
);
8017 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
8019 return BeginStyle(attr
);
8022 /// Begin right indent
8023 bool wxRichTextBuffer::BeginRightIndent(int rightIndent
)
8025 wxRichTextAttr attr
;
8026 attr
.SetFlags(wxTEXT_ATTR_RIGHT_INDENT
);
8027 attr
.SetRightIndent(rightIndent
);
8029 return BeginStyle(attr
);
8032 /// Begin paragraph spacing
8033 bool wxRichTextBuffer::BeginParagraphSpacing(int before
, int after
)
8037 flags
|= wxTEXT_ATTR_PARA_SPACING_BEFORE
;
8039 flags
|= wxTEXT_ATTR_PARA_SPACING_AFTER
;
8041 wxRichTextAttr attr
;
8042 attr
.SetFlags(flags
);
8043 attr
.SetParagraphSpacingBefore(before
);
8044 attr
.SetParagraphSpacingAfter(after
);
8046 return BeginStyle(attr
);
8049 /// Begin line spacing
8050 bool wxRichTextBuffer::BeginLineSpacing(int lineSpacing
)
8052 wxRichTextAttr attr
;
8053 attr
.SetFlags(wxTEXT_ATTR_LINE_SPACING
);
8054 attr
.SetLineSpacing(lineSpacing
);
8056 return BeginStyle(attr
);
8059 /// Begin numbered bullet
8060 bool wxRichTextBuffer::BeginNumberedBullet(int bulletNumber
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
8062 wxRichTextAttr attr
;
8063 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
8064 attr
.SetBulletStyle(bulletStyle
);
8065 attr
.SetBulletNumber(bulletNumber
);
8066 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
8068 return BeginStyle(attr
);
8071 /// Begin symbol bullet
8072 bool wxRichTextBuffer::BeginSymbolBullet(const wxString
& symbol
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
8074 wxRichTextAttr attr
;
8075 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
8076 attr
.SetBulletStyle(bulletStyle
);
8077 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
8078 attr
.SetBulletText(symbol
);
8080 return BeginStyle(attr
);
8083 /// Begin standard bullet
8084 bool wxRichTextBuffer::BeginStandardBullet(const wxString
& bulletName
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
8086 wxRichTextAttr attr
;
8087 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
8088 attr
.SetBulletStyle(bulletStyle
);
8089 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
8090 attr
.SetBulletName(bulletName
);
8092 return BeginStyle(attr
);
8095 /// Begin named character style
8096 bool wxRichTextBuffer::BeginCharacterStyle(const wxString
& characterStyle
)
8098 if (GetStyleSheet())
8100 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->FindCharacterStyle(characterStyle
);
8103 wxRichTextAttr attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
8104 return BeginStyle(attr
);
8110 /// Begin named paragraph style
8111 bool wxRichTextBuffer::BeginParagraphStyle(const wxString
& paragraphStyle
)
8113 if (GetStyleSheet())
8115 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(paragraphStyle
);
8118 wxRichTextAttr attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
8119 return BeginStyle(attr
);
8125 /// Begin named list style
8126 bool wxRichTextBuffer::BeginListStyle(const wxString
& listStyle
, int level
, int number
)
8128 if (GetStyleSheet())
8130 wxRichTextListStyleDefinition
* def
= GetStyleSheet()->FindListStyle(listStyle
);
8133 wxRichTextAttr
attr(def
->GetCombinedStyleForLevel(level
));
8135 attr
.SetBulletNumber(number
);
8137 return BeginStyle(attr
);
8144 bool wxRichTextBuffer::BeginURL(const wxString
& url
, const wxString
& characterStyle
)
8146 wxRichTextAttr attr
;
8148 if (!characterStyle
.IsEmpty() && GetStyleSheet())
8150 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->FindCharacterStyle(characterStyle
);
8153 attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
8158 return BeginStyle(attr
);
8161 /// Adds a handler to the end
8162 void wxRichTextBuffer::AddHandler(wxRichTextFileHandler
*handler
)
8164 sm_handlers
.Append(handler
);
8167 /// Inserts a handler at the front
8168 void wxRichTextBuffer::InsertHandler(wxRichTextFileHandler
*handler
)
8170 sm_handlers
.Insert( handler
);
8173 /// Removes a handler
8174 bool wxRichTextBuffer::RemoveHandler(const wxString
& name
)
8176 wxRichTextFileHandler
*handler
= FindHandler(name
);
8179 sm_handlers
.DeleteObject(handler
);
8187 /// Finds a handler by filename or, if supplied, type
8188 wxRichTextFileHandler
*wxRichTextBuffer::FindHandlerFilenameOrType(const wxString
& filename
,
8189 wxRichTextFileType imageType
)
8191 if (imageType
!= wxRICHTEXT_TYPE_ANY
)
8192 return FindHandler(imageType
);
8193 else if (!filename
.IsEmpty())
8195 wxString path
, file
, ext
;
8196 wxFileName::SplitPath(filename
, & path
, & file
, & ext
);
8197 return FindHandler(ext
, imageType
);
8204 /// Finds a handler by name
8205 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(const wxString
& name
)
8207 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
8210 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
8211 if (handler
->GetName().Lower() == name
.Lower()) return handler
;
8213 node
= node
->GetNext();
8218 /// Finds a handler by extension and type
8219 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(const wxString
& extension
, wxRichTextFileType type
)
8221 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
8224 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
8225 if ( handler
->GetExtension().Lower() == extension
.Lower() &&
8226 (type
== wxRICHTEXT_TYPE_ANY
|| handler
->GetType() == type
) )
8228 node
= node
->GetNext();
8233 /// Finds a handler by type
8234 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(wxRichTextFileType type
)
8236 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
8239 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
8240 if (handler
->GetType() == type
) return handler
;
8241 node
= node
->GetNext();
8246 void wxRichTextBuffer::InitStandardHandlers()
8248 if (!FindHandler(wxRICHTEXT_TYPE_TEXT
))
8249 AddHandler(new wxRichTextPlainTextHandler
);
8252 void wxRichTextBuffer::CleanUpHandlers()
8254 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
8257 wxRichTextFileHandler
* handler
= (wxRichTextFileHandler
*)node
->GetData();
8258 wxList::compatibility_iterator next
= node
->GetNext();
8263 sm_handlers
.Clear();
8266 wxString
wxRichTextBuffer::GetExtWildcard(bool combine
, bool save
, wxArrayInt
* types
)
8273 wxList::compatibility_iterator node
= GetHandlers().GetFirst();
8277 wxRichTextFileHandler
* handler
= (wxRichTextFileHandler
*) node
->GetData();
8278 if (handler
->IsVisible() && ((save
&& handler
->CanSave()) || (!save
&& handler
->CanLoad())))
8283 wildcard
+= wxT(";");
8284 wildcard
+= wxT("*.") + handler
->GetExtension();
8289 wildcard
+= wxT("|");
8290 wildcard
+= handler
->GetName();
8291 wildcard
+= wxT(" ");
8292 wildcard
+= _("files");
8293 wildcard
+= wxT(" (*.");
8294 wildcard
+= handler
->GetExtension();
8295 wildcard
+= wxT(")|*.");
8296 wildcard
+= handler
->GetExtension();
8298 types
->Add(handler
->GetType());
8303 node
= node
->GetNext();
8307 wildcard
= wxT("(") + wildcard
+ wxT(")|") + wildcard
;
8312 bool wxRichTextBuffer::LoadFile(const wxString
& filename
, wxRichTextFileType type
)
8314 wxRichTextFileHandler
* handler
= FindHandlerFilenameOrType(filename
, type
);
8317 SetDefaultStyle(wxRichTextAttr());
8318 handler
->SetFlags(GetHandlerFlags());
8319 bool success
= handler
->LoadFile(this, filename
);
8320 Invalidate(wxRICHTEXT_ALL
);
8328 bool wxRichTextBuffer::SaveFile(const wxString
& filename
, wxRichTextFileType type
)
8330 wxRichTextFileHandler
* handler
= FindHandlerFilenameOrType(filename
, type
);
8333 handler
->SetFlags(GetHandlerFlags());
8334 return handler
->SaveFile(this, filename
);
8340 /// Load from a stream
8341 bool wxRichTextBuffer::LoadFile(wxInputStream
& stream
, wxRichTextFileType type
)
8343 wxRichTextFileHandler
* handler
= FindHandler(type
);
8346 SetDefaultStyle(wxRichTextAttr());
8347 handler
->SetFlags(GetHandlerFlags());
8348 bool success
= handler
->LoadFile(this, stream
);
8349 Invalidate(wxRICHTEXT_ALL
);
8356 /// Save to a stream
8357 bool wxRichTextBuffer::SaveFile(wxOutputStream
& stream
, wxRichTextFileType type
)
8359 wxRichTextFileHandler
* handler
= FindHandler(type
);
8362 handler
->SetFlags(GetHandlerFlags());
8363 return handler
->SaveFile(this, stream
);
8369 /// Copy the range to the clipboard
8370 bool wxRichTextBuffer::CopyToClipboard(const wxRichTextRange
& range
)
8372 bool success
= false;
8373 wxRichTextParagraphLayoutBox
* container
= this;
8374 if (GetRichTextCtrl())
8375 container
= GetRichTextCtrl()->GetFocusObject();
8377 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
8379 if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open())
8381 wxTheClipboard
->Clear();
8383 // Add composite object
8385 wxDataObjectComposite
* compositeObject
= new wxDataObjectComposite();
8388 wxString text
= container
->GetTextForRange(range
);
8391 text
= wxTextFile::Translate(text
, wxTextFileType_Dos
);
8394 compositeObject
->Add(new wxTextDataObject(text
), false /* not preferred */);
8397 // Add rich text buffer data object. This needs the XML handler to be present.
8399 if (FindHandler(wxRICHTEXT_TYPE_XML
))
8401 wxRichTextBuffer
* richTextBuf
= new wxRichTextBuffer
;
8402 container
->CopyFragment(range
, *richTextBuf
);
8404 compositeObject
->Add(new wxRichTextBufferDataObject(richTextBuf
), true /* preferred */);
8407 if (wxTheClipboard
->SetData(compositeObject
))
8410 wxTheClipboard
->Close();
8419 /// Paste the clipboard content to the buffer
8420 bool wxRichTextBuffer::PasteFromClipboard(long position
)
8422 bool success
= false;
8423 wxRichTextParagraphLayoutBox
* container
= this;
8424 if (GetRichTextCtrl())
8425 container
= GetRichTextCtrl()->GetFocusObject();
8427 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
8428 if (CanPasteFromClipboard())
8430 if (wxTheClipboard
->Open())
8432 if (wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())))
8434 wxRichTextBufferDataObject data
;
8435 wxTheClipboard
->GetData(data
);
8436 wxRichTextBuffer
* richTextBuffer
= data
.GetRichTextBuffer();
8439 container
->InsertParagraphsWithUndo(this, position
+1, *richTextBuffer
, GetRichTextCtrl(), 0);
8440 if (GetRichTextCtrl())
8441 GetRichTextCtrl()->ShowPosition(position
+ richTextBuffer
->GetOwnRange().GetEnd());
8442 delete richTextBuffer
;
8445 else if (wxTheClipboard
->IsSupported(wxDF_TEXT
)
8447 || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
)
8451 wxTextDataObject data
;
8452 wxTheClipboard
->GetData(data
);
8453 wxString
text(data
.GetText());
8456 text2
.Alloc(text
.Length()+1);
8458 for (i
= 0; i
< text
.Length(); i
++)
8460 wxChar ch
= text
[i
];
8461 if (ch
!= wxT('\r'))
8465 wxString text2
= text
;
8467 container
->InsertTextWithUndo(this, position
+1, text2
, GetRichTextCtrl(), wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
);
8469 if (GetRichTextCtrl())
8470 GetRichTextCtrl()->ShowPosition(position
+ text2
.Length());
8474 else if (wxTheClipboard
->IsSupported(wxDF_BITMAP
))
8476 wxBitmapDataObject data
;
8477 wxTheClipboard
->GetData(data
);
8478 wxBitmap
bitmap(data
.GetBitmap());
8479 wxImage
image(bitmap
.ConvertToImage());
8481 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, this, container
, GetRichTextCtrl(), false);
8483 action
->GetNewParagraphs().AddImage(image
);
8485 if (action
->GetNewParagraphs().GetChildCount() == 1)
8486 action
->GetNewParagraphs().SetPartialParagraph(true);
8488 action
->SetPosition(position
+1);
8490 // Set the range we'll need to delete in Undo
8491 action
->SetRange(wxRichTextRange(position
+1, position
+1));
8493 SubmitAction(action
);
8497 wxTheClipboard
->Close();
8501 wxUnusedVar(position
);
8506 /// Can we paste from the clipboard?
8507 bool wxRichTextBuffer::CanPasteFromClipboard() const
8509 bool canPaste
= false;
8510 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
8511 if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open())
8513 if (wxTheClipboard
->IsSupported(wxDF_TEXT
)
8515 || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
)
8517 || wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())) ||
8518 wxTheClipboard
->IsSupported(wxDF_BITMAP
))
8522 wxTheClipboard
->Close();
8528 /// Dumps contents of buffer for debugging purposes
8529 void wxRichTextBuffer::Dump()
8533 wxStringOutputStream
stream(& text
);
8534 wxTextOutputStream
textStream(stream
);
8541 /// Add an event handler
8542 bool wxRichTextBuffer::AddEventHandler(wxEvtHandler
* handler
)
8544 m_eventHandlers
.Append(handler
);
8548 /// Remove an event handler
8549 bool wxRichTextBuffer::RemoveEventHandler(wxEvtHandler
* handler
, bool deleteHandler
)
8551 wxList::compatibility_iterator node
= m_eventHandlers
.Find(handler
);
8554 m_eventHandlers
.Erase(node
);
8564 /// Clear event handlers
8565 void wxRichTextBuffer::ClearEventHandlers()
8567 m_eventHandlers
.Clear();
8570 /// Send event to event handlers. If sendToAll is true, will send to all event handlers,
8571 /// otherwise will stop at the first successful one.
8572 bool wxRichTextBuffer::SendEvent(wxEvent
& event
, bool sendToAll
)
8574 bool success
= false;
8575 for (wxList::compatibility_iterator node
= m_eventHandlers
.GetFirst(); node
; node
= node
->GetNext())
8577 wxEvtHandler
* handler
= (wxEvtHandler
*) node
->GetData();
8578 if (handler
->ProcessEvent(event
))
8588 /// Set style sheet and notify of the change
8589 bool wxRichTextBuffer::SetStyleSheetAndNotify(wxRichTextStyleSheet
* sheet
)
8591 wxRichTextStyleSheet
* oldSheet
= GetStyleSheet();
8593 wxWindowID winid
= wxID_ANY
;
8594 if (GetRichTextCtrl())
8595 winid
= GetRichTextCtrl()->GetId();
8597 wxRichTextEvent
event(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACING
, winid
);
8598 event
.SetEventObject(GetRichTextCtrl());
8599 event
.SetContainer(GetRichTextCtrl()->GetFocusObject());
8600 event
.SetOldStyleSheet(oldSheet
);
8601 event
.SetNewStyleSheet(sheet
);
8604 if (SendEvent(event
) && !event
.IsAllowed())
8606 if (sheet
!= oldSheet
)
8612 if (oldSheet
&& oldSheet
!= sheet
)
8615 SetStyleSheet(sheet
);
8617 event
.SetEventType(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACED
);
8618 event
.SetOldStyleSheet(NULL
);
8621 return SendEvent(event
);
8624 /// Set renderer, deleting old one
8625 void wxRichTextBuffer::SetRenderer(wxRichTextRenderer
* renderer
)
8629 sm_renderer
= renderer
;
8632 /// Hit-testing: returns a flag indicating hit test details, plus
8633 /// information about position
8634 int wxRichTextBuffer::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
8636 int ret
= wxRichTextParagraphLayoutBox::HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
8637 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
8643 textPosition
= m_ownRange
.GetEnd()-1;
8646 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
;
8650 void wxRichTextBuffer::SetFontScale(double fontScale
)
8652 m_fontScale
= fontScale
;
8653 m_fontTable
.SetFontScale(fontScale
);
8656 void wxRichTextBuffer::SetDimensionScale(double dimScale
)
8658 m_dimensionScale
= dimScale
;
8661 bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxRichTextAttr
& bulletAttr
, const wxRect
& rect
)
8663 if (bulletAttr
.GetTextColour().IsOk())
8665 wxCheckSetPen(dc
, wxPen(bulletAttr
.GetTextColour()));
8666 wxCheckSetBrush(dc
, wxBrush(bulletAttr
.GetTextColour()));
8670 wxCheckSetPen(dc
, *wxBLACK_PEN
);
8671 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
8675 if (bulletAttr
.HasFont())
8677 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(bulletAttr
);
8680 font
= (*wxNORMAL_FONT
);
8682 wxCheckSetFont(dc
, font
);
8684 int charHeight
= dc
.GetCharHeight();
8686 int bulletWidth
= (int) (((float) charHeight
) * wxRichTextBuffer::GetBulletProportion());
8687 int bulletHeight
= bulletWidth
;
8691 // Calculate the top position of the character (as opposed to the whole line height)
8692 int y
= rect
.y
+ (rect
.height
- charHeight
);
8694 // Calculate where the bullet should be positioned
8695 y
= y
+ (charHeight
+1)/2 - (bulletHeight
+1)/2;
8697 // The margin between a bullet and text.
8698 int margin
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer::GetBulletRightMargin());
8700 if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
)
8701 x
= rect
.x
+ rect
.width
- bulletWidth
- margin
;
8702 else if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
)
8703 x
= x
+ (rect
.width
)/2 - bulletWidth
/2;
8705 if (bulletAttr
.GetBulletName() == wxT("standard/square"))
8707 dc
.DrawRectangle(x
, y
, bulletWidth
, bulletHeight
);
8709 else if (bulletAttr
.GetBulletName() == wxT("standard/diamond"))
8712 pts
[0].x
= x
; pts
[0].y
= y
+ bulletHeight
/2;
8713 pts
[1].x
= x
+ bulletWidth
/2; pts
[1].y
= y
;
8714 pts
[2].x
= x
+ bulletWidth
; pts
[2].y
= y
+ bulletHeight
/2;
8715 pts
[3].x
= x
+ bulletWidth
/2; pts
[3].y
= y
+ bulletHeight
;
8717 dc
.DrawPolygon(4, pts
);
8719 else if (bulletAttr
.GetBulletName() == wxT("standard/triangle"))
8722 pts
[0].x
= x
; pts
[0].y
= y
;
8723 pts
[1].x
= x
+ bulletWidth
; pts
[1].y
= y
+ bulletHeight
/2;
8724 pts
[2].x
= x
; pts
[2].y
= y
+ bulletHeight
;
8726 dc
.DrawPolygon(3, pts
);
8728 else if (bulletAttr
.GetBulletName() == wxT("standard/circle-outline"))
8730 wxCheckSetBrush(dc
, *wxTRANSPARENT_BRUSH
);
8731 dc
.DrawEllipse(x
, y
, bulletWidth
, bulletHeight
);
8733 else // "standard/circle", and catch-all
8735 dc
.DrawEllipse(x
, y
, bulletWidth
, bulletHeight
);
8741 bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxRichTextAttr
& attr
, const wxRect
& rect
, const wxString
& text
)
8746 if ((attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
) && !attr
.GetBulletFont().IsEmpty() && attr
.HasFont())
8748 wxRichTextAttr fontAttr
;
8749 if (attr
.HasFontPixelSize())
8750 fontAttr
.SetFontPixelSize(attr
.GetFontSize());
8752 fontAttr
.SetFontPointSize(attr
.GetFontSize());
8753 fontAttr
.SetFontStyle(attr
.GetFontStyle());
8754 fontAttr
.SetFontWeight(attr
.GetFontWeight());
8755 fontAttr
.SetFontUnderlined(attr
.GetFontUnderlined());
8756 fontAttr
.SetFontFaceName(attr
.GetBulletFont());
8757 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(fontAttr
);
8759 else if (attr
.HasFont())
8760 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(attr
);
8762 font
= (*wxNORMAL_FONT
);
8764 wxCheckSetFont(dc
, font
);
8766 if (attr
.GetTextColour().IsOk())
8767 dc
.SetTextForeground(attr
.GetTextColour());
8769 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
8771 int charHeight
= dc
.GetCharHeight();
8773 dc
.GetTextExtent(text
, & tw
, & th
);
8777 // Calculate the top position of the character (as opposed to the whole line height)
8778 int y
= rect
.y
+ (rect
.height
- charHeight
);
8780 // The margin between a bullet and text.
8781 int margin
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer::GetBulletRightMargin());
8783 if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
)
8784 x
= (rect
.x
+ rect
.width
) - tw
- margin
;
8785 else if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
)
8786 x
= x
+ (rect
.width
)/2 - tw
/2;
8788 dc
.DrawText(text
, x
, y
);
8796 bool wxRichTextStdRenderer::DrawBitmapBullet(wxRichTextParagraph
* WXUNUSED(paragraph
), wxDC
& WXUNUSED(dc
), const wxRichTextAttr
& WXUNUSED(attr
), const wxRect
& WXUNUSED(rect
))
8798 // Currently unimplemented. The intention is to store bitmaps by name in a media store associated
8799 // with the buffer. The store will allow retrieval from memory, disk or other means.
8803 /// Enumerate the standard bullet names currently supported
8804 bool wxRichTextStdRenderer::EnumerateStandardBulletNames(wxArrayString
& bulletNames
)
8806 bulletNames
.Add(wxTRANSLATE("standard/circle"));
8807 bulletNames
.Add(wxTRANSLATE("standard/circle-outline"));
8808 bulletNames
.Add(wxTRANSLATE("standard/square"));
8809 bulletNames
.Add(wxTRANSLATE("standard/diamond"));
8810 bulletNames
.Add(wxTRANSLATE("standard/triangle"));
8819 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBox
, wxRichTextParagraphLayoutBox
)
8821 wxRichTextBox::wxRichTextBox(wxRichTextObject
* parent
):
8822 wxRichTextParagraphLayoutBox(parent
)
8827 bool wxRichTextBox::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8832 // TODO: if the active object in the control, draw an indication.
8833 // We need to add the concept of active object, and not just focus object,
8834 // so we can apply commands (properties, delete, ...) to objects such as text boxes and images.
8835 // Ultimately we would like to be able to interactively resize an active object
8836 // using drag handles.
8837 return wxRichTextParagraphLayoutBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8841 void wxRichTextBox::Copy(const wxRichTextBox
& obj
)
8843 wxRichTextParagraphLayoutBox::Copy(obj
);
8846 // Edit properties via a GUI
8847 bool wxRichTextBox::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8849 wxRichTextObjectPropertiesDialog
boxDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Box Properties"));
8850 boxDlg
.SetAttributes(GetAttributes());
8852 if (boxDlg
.ShowModal() == wxID_OK
)
8854 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
8855 // indeterminate in the object.
8856 boxDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
8867 IMPLEMENT_DYNAMIC_CLASS(wxRichTextField
, wxRichTextParagraphLayoutBox
)
8869 wxRichTextField::wxRichTextField(const wxString
& fieldType
, wxRichTextObject
* parent
):
8870 wxRichTextParagraphLayoutBox(parent
)
8872 SetFieldType(fieldType
);
8876 bool wxRichTextField::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8881 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8882 if (fieldType
&& fieldType
->Draw(this, dc
, context
, range
, selection
, rect
, descent
, style
))
8885 // Fallback; but don't draw guidelines.
8886 style
&= ~wxRICHTEXT_DRAW_GUIDELINES
;
8887 return wxRichTextParagraphLayoutBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8890 bool wxRichTextField::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
)
8892 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8893 if (fieldType
&& fieldType
->Layout(this, dc
, context
, rect
, parentRect
, style
))
8897 return wxRichTextParagraphLayoutBox::Layout(dc
, context
, rect
, parentRect
, style
);
8900 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
8902 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8904 return fieldType
->GetRangeSize((wxRichTextField
*) this, range
, size
, descent
, dc
, context
, flags
, position
, parentSize
, partialExtents
);
8906 return wxRichTextParagraphLayoutBox::GetRangeSize(range
, size
, descent
, dc
, context
, flags
, position
, parentSize
, partialExtents
);
8910 void wxRichTextField::CalculateRange(long start
, long& end
)
8913 wxRichTextParagraphLayoutBox::CalculateRange(start
, end
);
8915 wxRichTextObject::CalculateRange(start
, end
);
8919 void wxRichTextField::Copy(const wxRichTextField
& obj
)
8921 wxRichTextParagraphLayoutBox::Copy(obj
);
8923 UpdateField(GetBuffer());
8926 // Edit properties via a GUI
8927 bool wxRichTextField::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8929 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8931 return fieldType
->EditProperties(this, parent
, buffer
);
8936 bool wxRichTextField::CanEditProperties() const
8938 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8940 return fieldType
->CanEditProperties((wxRichTextField
*) this);
8945 wxString
wxRichTextField::GetPropertiesMenuLabel() const
8947 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8949 return fieldType
->GetPropertiesMenuLabel((wxRichTextField
*) this);
8951 return wxEmptyString
;
8954 bool wxRichTextField::UpdateField(wxRichTextBuffer
* buffer
)
8956 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8958 return fieldType
->UpdateField(buffer
, (wxRichTextField
*) this);
8963 bool wxRichTextField::IsTopLevel() const
8965 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8967 return fieldType
->IsTopLevel((wxRichTextField
*) this);
8972 IMPLEMENT_CLASS(wxRichTextFieldType
, wxObject
)
8974 IMPLEMENT_CLASS(wxRichTextFieldTypeStandard
, wxRichTextFieldType
)
8976 wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString
& name
, const wxString
& label
, int displayStyle
)
8982 SetDisplayStyle(displayStyle
);
8985 wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString
& name
, const wxBitmap
& bitmap
, int displayStyle
)
8991 SetDisplayStyle(displayStyle
);
8994 void wxRichTextFieldTypeStandard::Init()
8996 m_displayStyle
= wxRICHTEXT_FIELD_STYLE_RECTANGLE
;
8997 m_font
= wxFont(6, wxFONTFAMILY_SWISS
, wxFONTSTYLE_NORMAL
, wxFONTWEIGHT_NORMAL
);
8998 m_textColour
= *wxWHITE
;
8999 m_borderColour
= *wxBLACK
;
9000 m_backgroundColour
= *wxBLACK
;
9001 m_verticalPadding
= 1;
9002 m_horizontalPadding
= 3;
9003 m_horizontalMargin
= 2;
9004 m_verticalMargin
= 0;
9007 void wxRichTextFieldTypeStandard::Copy(const wxRichTextFieldTypeStandard
& field
)
9009 wxRichTextFieldType::Copy(field
);
9011 m_label
= field
.m_label
;
9012 m_displayStyle
= field
.m_displayStyle
;
9013 m_font
= field
.m_font
;
9014 m_textColour
= field
.m_textColour
;
9015 m_borderColour
= field
.m_borderColour
;
9016 m_backgroundColour
= field
.m_backgroundColour
;
9017 m_verticalPadding
= field
.m_verticalPadding
;
9018 m_horizontalPadding
= field
.m_horizontalPadding
;
9019 m_horizontalMargin
= field
.m_horizontalMargin
;
9020 m_bitmap
= field
.m_bitmap
;
9023 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
))
9025 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_COMPOSITE
)
9026 return false; // USe default composite drawing
9027 else // if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_RECTANGLE || m_displayStyle == wxRICHTEXT_FIELD_STYLE_NOBORDER)
9031 wxPen
borderPen(m_borderColour
, 1, wxSOLID
);
9032 wxBrush
backgroundBrush(m_backgroundColour
);
9033 wxColour
textColour(m_textColour
);
9035 if (selection
.WithinSelection(obj
->GetRange().GetStart(), obj
))
9037 wxColour
highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
));
9038 wxColour
highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
));
9040 borderPen
= wxPen(highlightTextColour
, 1, wxSOLID
);
9041 backgroundBrush
= wxBrush(highlightColour
);
9043 wxCheckSetBrush(dc
, backgroundBrush
);
9044 wxCheckSetPen(dc
, wxPen(highlightColour
, 1, wxSOLID
));
9045 dc
.DrawRectangle(rect
);
9048 if (m_displayStyle
!= wxRICHTEXT_FIELD_STYLE_NO_BORDER
)
9051 // objectRect is the area where the content is drawn, after margins around it have been taken into account
9052 wxRect objectRect
= wxRect(wxPoint(rect
.x
+ m_horizontalMargin
, rect
.y
+ wxMax(0, rect
.height
- descent
- obj
->GetCachedSize().y
)),
9053 wxSize(obj
->GetCachedSize().x
- 2*m_horizontalMargin
- borderSize
, obj
->GetCachedSize().y
));
9055 // clientArea is where the text is actually written
9056 wxRect clientArea
= objectRect
;
9058 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_RECTANGLE
)
9060 dc
.SetPen(borderPen
);
9061 dc
.SetBrush(backgroundBrush
);
9062 dc
.DrawRoundedRectangle(objectRect
, 4.0);
9064 else if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_START_TAG
)
9066 int arrowLength
= objectRect
.height
/2;
9067 clientArea
.width
-= (arrowLength
- m_horizontalPadding
);
9070 pts
[0].x
= objectRect
.x
; pts
[0].y
= objectRect
.y
;
9071 pts
[1].x
= objectRect
.x
+ objectRect
.width
- arrowLength
; pts
[1].y
= objectRect
.y
;
9072 pts
[2].x
= objectRect
.x
+ objectRect
.width
; pts
[2].y
= objectRect
.y
+ (objectRect
.height
/2);
9073 pts
[3].x
= objectRect
.x
+ objectRect
.width
- arrowLength
; pts
[3].y
= objectRect
.y
+ objectRect
.height
;
9074 pts
[4].x
= objectRect
.x
; pts
[4].y
= objectRect
.y
+ objectRect
.height
;
9075 dc
.SetPen(borderPen
);
9076 dc
.SetBrush(backgroundBrush
);
9077 dc
.DrawPolygon(5, pts
);
9079 else if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_END_TAG
)
9081 int arrowLength
= objectRect
.height
/2;
9082 clientArea
.width
-= (arrowLength
- m_horizontalPadding
);
9083 clientArea
.x
+= (arrowLength
- m_horizontalPadding
);
9086 pts
[0].x
= objectRect
.x
+ objectRect
.width
; pts
[0].y
= objectRect
.y
;
9087 pts
[1].x
= objectRect
.x
+ arrowLength
; pts
[1].y
= objectRect
.y
;
9088 pts
[2].x
= objectRect
.x
; pts
[2].y
= objectRect
.y
+ (objectRect
.height
/2);
9089 pts
[3].x
= objectRect
.x
+ arrowLength
; pts
[3].y
= objectRect
.y
+ objectRect
.height
;
9090 pts
[4].x
= objectRect
.x
+ objectRect
.width
; pts
[4].y
= objectRect
.y
+ objectRect
.height
;
9091 dc
.SetPen(borderPen
);
9092 dc
.SetBrush(backgroundBrush
);
9093 dc
.DrawPolygon(5, pts
);
9096 if (m_bitmap
.IsOk())
9098 int x
= clientArea
.x
+ (clientArea
.width
- m_bitmap
.GetWidth())/2;
9099 int y
= clientArea
.y
+ m_verticalPadding
;
9100 dc
.DrawBitmap(m_bitmap
, x
, y
, true);
9102 if (selection
.WithinSelection(obj
->GetRange().GetStart(), obj
))
9104 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
9105 wxCheckSetPen(dc
, *wxBLACK_PEN
);
9106 dc
.SetLogicalFunction(wxINVERT
);
9107 dc
.DrawRectangle(wxRect(x
, y
, m_bitmap
.GetWidth(), m_bitmap
.GetHeight()));
9108 dc
.SetLogicalFunction(wxCOPY
);
9113 wxString
label(m_label
);
9114 if (label
.IsEmpty())
9116 int w
, h
, maxDescent
;
9118 dc
.GetTextExtent(m_label
, & w
, &h
, & maxDescent
);
9119 dc
.SetTextForeground(textColour
);
9121 int x
= clientArea
.x
+ (clientArea
.width
- w
)/2;
9122 int y
= clientArea
.y
+ (clientArea
.height
- (h
- maxDescent
))/2;
9123 dc
.DrawText(m_label
, x
, y
);
9130 bool wxRichTextFieldTypeStandard::Layout(wxRichTextField
* obj
, wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& WXUNUSED(rect
), const wxRect
& WXUNUSED(parentRect
), int style
)
9132 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_COMPOSITE
)
9133 return false; // USe default composite layout
9135 wxSize size
= GetSize(obj
, dc
, context
, style
);
9136 obj
->SetCachedSize(size
);
9137 obj
->SetMinSize(size
);
9138 obj
->SetMaxSize(size
);
9142 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
9144 if (IsTopLevel(obj
))
9145 return obj
->wxRichTextParagraphLayoutBox::GetRangeSize(range
, size
, descent
, dc
, context
, flags
, position
, parentSize
);
9148 wxSize sz
= GetSize(obj
, dc
, context
, 0);
9152 if (partialExtents
->GetCount() > 0)
9153 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
9156 partialExtents
->Add(lastSize
+ sz
.x
);
9163 wxSize
wxRichTextFieldTypeStandard::GetSize(wxRichTextField
* WXUNUSED(obj
), wxDC
& dc
, wxRichTextDrawingContext
& WXUNUSED(context
), int WXUNUSED(style
)) const
9166 int w
= 0, h
= 0, maxDescent
= 0;
9169 if (m_bitmap
.IsOk())
9171 w
= m_bitmap
.GetWidth();
9172 h
= m_bitmap
.GetHeight();
9174 sz
= wxSize(w
+ m_horizontalMargin
*2, h
+ m_verticalMargin
*2);
9178 wxString
label(m_label
);
9179 if (label
.IsEmpty())
9182 dc
.GetTextExtent(label
, & w
, &h
, & maxDescent
);
9184 sz
= wxSize(w
+ m_horizontalPadding
*2 + m_horizontalMargin
*2, h
+ m_verticalPadding
*2 + m_verticalMargin
*2);
9187 if (m_displayStyle
!= wxRICHTEXT_FIELD_STYLE_NO_BORDER
)
9189 sz
.x
+= borderSize
*2;
9190 sz
.y
+= borderSize
*2;
9193 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_START_TAG
|| m_displayStyle
== wxRICHTEXT_FIELD_STYLE_END_TAG
)
9195 // Add space for the arrow
9196 sz
.x
+= (sz
.y
/2 - m_horizontalPadding
);
9202 IMPLEMENT_DYNAMIC_CLASS(wxRichTextCell
, wxRichTextBox
)
9204 wxRichTextCell::wxRichTextCell(wxRichTextObject
* parent
):
9205 wxRichTextBox(parent
)
9210 bool wxRichTextCell::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
9212 return wxRichTextBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
9216 void wxRichTextCell::Copy(const wxRichTextCell
& obj
)
9218 wxRichTextBox::Copy(obj
);
9221 // Edit properties via a GUI
9222 bool wxRichTextCell::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
9224 // We need to gather common attributes for all selected cells.
9226 wxRichTextTable
* table
= wxDynamicCast(GetParent(), wxRichTextTable
);
9227 bool multipleCells
= false;
9228 wxRichTextAttr attr
;
9230 if (table
&& buffer
&& buffer
->GetRichTextCtrl() && buffer
->GetRichTextCtrl()->GetSelection().IsValid() &&
9231 buffer
->GetRichTextCtrl()->GetSelection().GetContainer() == GetParent())
9233 wxRichTextAttr clashingAttr
, absentAttr
;
9234 const wxRichTextSelection
& sel
= buffer
->GetRichTextCtrl()->GetSelection();
9236 int selectedCellCount
= 0;
9237 for (i
= 0; i
< sel
.GetCount(); i
++)
9239 const wxRichTextRange
& range
= sel
[i
];
9240 wxRichTextCell
* cell
= table
->GetCell(range
.GetStart());
9243 wxRichTextAttr cellStyle
= cell
->GetAttributes();
9245 CollectStyle(attr
, cellStyle
, clashingAttr
, absentAttr
);
9247 selectedCellCount
++;
9250 multipleCells
= selectedCellCount
> 1;
9254 attr
= GetAttributes();
9259 caption
= _("Multiple Cell Properties");
9261 caption
= _("Cell Properties");
9263 wxRichTextObjectPropertiesDialog
cellDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, caption
);
9264 cellDlg
.SetAttributes(attr
);
9266 wxRichTextSizePage
* sizePage
= wxDynamicCast(cellDlg
.FindPage(wxCLASSINFO(wxRichTextSizePage
)), wxRichTextSizePage
);
9269 // We don't want position and floating controls for a cell.
9270 sizePage
->ShowPositionControls(false);
9271 sizePage
->ShowFloatingControls(false);
9274 if (cellDlg
.ShowModal() == wxID_OK
)
9278 const wxRichTextSelection
& sel
= buffer
->GetRichTextCtrl()->GetSelection();
9279 // Apply the style; we interpret indeterminate attributes as 'don't touch this attribute'
9280 // since it may represent clashing attributes across multiple objects.
9281 table
->SetCellStyle(sel
, attr
);
9284 // For a single object, indeterminate attributes set by the user should be reflected in the
9285 // actual object style, so pass the wxRICHTEXT_SETSTYLE_RESET flag to assign
9286 // the style directly instead of applying (which ignores indeterminate attributes,
9287 // leaving them as they were).
9288 cellDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
9295 WX_DEFINE_OBJARRAY(wxRichTextObjectPtrArrayArray
)
9297 IMPLEMENT_DYNAMIC_CLASS(wxRichTextTable
, wxRichTextBox
)
9299 wxRichTextTable::wxRichTextTable(wxRichTextObject
* parent
): wxRichTextBox(parent
)
9305 // Draws the object.
9306 bool wxRichTextTable::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
9308 return wxRichTextBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
9311 WX_DECLARE_OBJARRAY(wxRect
, wxRichTextRectArray
);
9312 WX_DEFINE_OBJARRAY(wxRichTextRectArray
);
9314 // Lays the object out. rect is the space available for layout. Often it will
9315 // be the specified overall space for this object, if trying to constrain
9316 // layout to a particular size, or it could be the total space available in the
9317 // parent. rect is the overall size, so we must subtract margins and padding.
9318 // to get the actual available space.
9319 bool wxRichTextTable::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& WXUNUSED(parentRect
), int style
)
9321 SetPosition(rect
.GetPosition());
9323 // TODO: the meaty bit. Calculate sizes of all cells and rows. Try to use
9324 // minimum size if within alloted size, then divide up remaining size
9325 // between rows/cols.
9328 wxRichTextBuffer
* buffer
= GetBuffer();
9329 if (buffer
) scale
= buffer
->GetScale();
9331 wxRect availableSpace
= GetAvailableContentArea(dc
, context
, rect
);
9332 wxTextAttrDimensionConverter
converter(dc
, scale
, availableSpace
.GetSize());
9334 wxRichTextAttr
attr(GetAttributes());
9335 context
.ApplyVirtualAttributes(attr
, this);
9337 // If we have no fixed table size, and assuming we're not pushed for
9338 // space, then we don't have to try to stretch the table to fit the contents.
9339 bool stretchToFitTableWidth
= false;
9341 int tableWidth
= rect
.width
;
9342 if (attr
.GetTextBoxAttr().GetWidth().IsValid())
9344 tableWidth
= converter
.GetPixels(attr
.GetTextBoxAttr().GetWidth());
9346 // Fixed table width, so we do want to stretch columns out if necessary.
9347 stretchToFitTableWidth
= true;
9349 // Shouldn't be able to exceed the size passed to this function
9350 tableWidth
= wxMin(rect
.width
, tableWidth
);
9353 // Get internal padding
9354 int paddingLeft
= 0, paddingTop
= 0;
9355 if (attr
.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
9356 paddingLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetLeft());
9357 if (attr
.GetTextBoxAttr().GetPadding().GetTop().IsValid())
9358 paddingTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetTop());
9360 // Assume that left and top padding are also used for inter-cell padding.
9361 int paddingX
= paddingLeft
;
9362 int paddingY
= paddingTop
;
9364 int totalLeftMargin
= 0, totalRightMargin
= 0, totalTopMargin
= 0, totalBottomMargin
= 0;
9365 GetTotalMargin(dc
, buffer
, attr
, totalLeftMargin
, totalRightMargin
, totalTopMargin
, totalBottomMargin
);
9367 // Internal table width - the area for content
9368 int internalTableWidth
= tableWidth
- totalLeftMargin
- totalRightMargin
;
9370 int rowCount
= m_cells
.GetCount();
9371 if (m_colCount
== 0 || rowCount
== 0)
9373 wxRect
overallRect(rect
.x
, rect
.y
, totalLeftMargin
+ totalRightMargin
, totalTopMargin
+ totalBottomMargin
);
9374 SetCachedSize(overallRect
.GetSize());
9376 // Zero content size
9377 SetMinSize(overallRect
.GetSize());
9378 SetMaxSize(GetMinSize());
9382 // The final calculated widths
9383 wxArrayInt colWidths
;
9384 colWidths
.Add(0, m_colCount
);
9386 wxArrayInt absoluteColWidths
;
9387 absoluteColWidths
.Add(0, m_colCount
);
9389 wxArrayInt percentageColWidths
;
9390 percentageColWidths
.Add(0, m_colCount
);
9391 // wxArrayInt percentageColWidthsSpanning(m_colCount);
9392 // These are only relevant when the first column contains spanning information.
9393 // wxArrayInt columnSpans(m_colCount); // Each contains 1 for non-spanning cell, > 1 for spanning cell.
9394 wxArrayInt maxColWidths
;
9395 maxColWidths
.Add(0, m_colCount
);
9396 wxArrayInt minColWidths
;
9397 minColWidths
.Add(0, m_colCount
);
9399 wxSize
tableSize(tableWidth
, 0);
9403 for (i
= 0; i
< m_colCount
; i
++)
9405 absoluteColWidths
[i
] = 0;
9406 // absoluteColWidthsSpanning[i] = 0;
9407 percentageColWidths
[i
] = -1;
9408 // percentageColWidthsSpanning[i] = -1;
9410 maxColWidths
[i
] = 0;
9411 minColWidths
[i
] = 0;
9412 // columnSpans[i] = 1;
9415 // (0) Determine which cells are visible according to spans
9417 // __________________
9422 // |------------------|
9423 // |__________________| 4
9425 // To calculate cell visibility:
9426 // First find all spanning cells. Build an array of span records with start x, y and end x, y.
9427 // Then for each cell, test whether we're within one of those cells, and unless we're at the start of
9428 // that cell, hide the cell.
9430 // We can also use this array to match the size of spanning cells to the grid. Or just do
9431 // this when we iterate through all cells.
9433 // 0.1: add spanning cells to an array
9434 wxRichTextRectArray rectArray
;
9435 for (j
= 0; j
< m_rowCount
; j
++)
9437 for (i
= 0; i
< m_colCount
; i
++)
9439 wxRichTextBox
* cell
= GetCell(j
, i
);
9440 int colSpan
= 1, rowSpan
= 1;
9441 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9442 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9443 if (cell
->GetProperties().HasProperty(wxT("rowspan")))
9444 rowSpan
= cell
->GetProperties().GetPropertyLong(wxT("rowspan"));
9445 if (colSpan
> 1 || rowSpan
> 1)
9447 rectArray
.Add(wxRect(i
, j
, colSpan
, rowSpan
));
9451 // 0.2: find which cells are subsumed by a spanning cell
9452 for (j
= 0; j
< m_rowCount
; j
++)
9454 for (i
= 0; i
< m_colCount
; i
++)
9456 wxRichTextBox
* cell
= GetCell(j
, i
);
9457 if (rectArray
.GetCount() == 0)
9463 int colSpan
= 1, rowSpan
= 1;
9464 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9465 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9466 if (cell
->GetProperties().HasProperty(wxT("rowspan")))
9467 rowSpan
= cell
->GetProperties().GetPropertyLong(wxT("rowspan"));
9468 if (colSpan
> 1 || rowSpan
> 1)
9470 // Assume all spanning cells are shown
9476 for (k
= 0; k
< (int) rectArray
.GetCount(); k
++)
9478 if (rectArray
[k
].Contains(wxPoint(i
, j
)))
9490 // TODO: find the first spanned cell in each row that spans the most columns and doesn't
9491 // overlap with a spanned cell starting at a previous column position.
9492 // This means we need to keep an array of rects so we can check. However
9493 // it does also mean that some spans simply may not be taken into account
9494 // where there are different spans happening on different rows. In these cases,
9495 // they will simply be as wide as their constituent columns.
9497 // (1) Do an initial layout for all cells to get minimum and maximum size, and get
9498 // the absolute or percentage width of each column.
9500 for (j
= 0; j
< m_rowCount
; j
++)
9502 // First get the overall margins so we can calculate percentage widths based on
9503 // the available content space for all cells on the row
9505 int overallRowContentMargin
= 0;
9506 int visibleCellCount
= 0;
9508 for (i
= 0; i
< m_colCount
; i
++)
9510 wxRichTextBox
* cell
= GetCell(j
, i
);
9511 if (cell
->IsShown())
9513 int cellTotalLeftMargin
= 0, cellTotalRightMargin
= 0, cellTotalTopMargin
= 0, cellTotalBottomMargin
= 0;
9514 GetTotalMargin(dc
, buffer
, cell
->GetAttributes(), cellTotalLeftMargin
, cellTotalRightMargin
, cellTotalTopMargin
, cellTotalBottomMargin
);
9516 overallRowContentMargin
+= (cellTotalLeftMargin
+ cellTotalRightMargin
);
9517 visibleCellCount
++;
9521 // Add in inter-cell padding
9522 overallRowContentMargin
+= ((visibleCellCount
-1) * paddingX
);
9524 int rowContentWidth
= internalTableWidth
- overallRowContentMargin
;
9525 wxSize
rowTableSize(rowContentWidth
, 0);
9526 wxTextAttrDimensionConverter
converter(dc
, scale
, rowTableSize
);
9528 for (i
= 0; i
< m_colCount
; i
++)
9530 wxRichTextBox
* cell
= GetCell(j
, i
);
9531 if (cell
->IsShown())
9534 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9535 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9537 // Lay out cell to find min/max widths
9538 cell
->Invalidate(wxRICHTEXT_ALL
);
9539 cell
->Layout(dc
, context
, availableSpace
, availableSpace
, style
);
9543 int absoluteCellWidth
= -1;
9544 int percentageCellWidth
= -1;
9546 // I think we need to calculate percentages from the internal table size,
9547 // minus the padding between cells which we'll need to calculate from the
9548 // (number of VISIBLE cells - 1)*paddingX. Then percentages that add up to 100%
9549 // will add up to 100%. In CSS, the width specifies the cell's content rect width,
9550 // so if we want to conform to that we'll need to add in the overall cell margins.
9551 // However, this will make it difficult to specify percentages that add up to
9552 // 100% and still fit within the table width.
9553 // Let's say two cells have 50% width. They have 10 pixels of overall margin each.
9554 // The table content rect is 500 pixels and the inter-cell padding is 20 pixels.
9555 // If we're using internal content size for the width, we would calculate the
9556 // the overall cell width for n cells as:
9557 // (500 - 20*(n-1) - overallCellMargin1 - overallCellMargin2 - ...) * percentage / 100
9558 // + thisOverallCellMargin
9559 // = 500 - 20 - 10 - 10) * 0.5 + 10 = 240 pixels overall cell width.
9560 // Adding this back, we get 240 + 240 + 20 = 500 pixels.
9562 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9564 int w
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetWidth());
9565 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
9567 percentageCellWidth
= w
;
9571 absoluteCellWidth
= w
;
9573 // Override absolute width with minimum width if necessary
9574 if (cell
->GetMinSize().x
> 0 && absoluteCellWidth
!=1 && cell
->GetMinSize().x
> absoluteCellWidth
)
9575 absoluteCellWidth
= cell
->GetMinSize().x
;
9578 if (absoluteCellWidth
!= -1)
9580 if (absoluteCellWidth
> absoluteColWidths
[i
])
9581 absoluteColWidths
[i
] = absoluteCellWidth
;
9584 if (percentageCellWidth
!= -1)
9586 if (percentageCellWidth
> percentageColWidths
[i
])
9587 percentageColWidths
[i
] = percentageCellWidth
;
9590 if (colSpan
== 1 && cell
->GetMinSize().x
&& cell
->GetMinSize().x
> minColWidths
[i
])
9591 minColWidths
[i
] = cell
->GetMinSize().x
;
9592 if (colSpan
== 1 && cell
->GetMaxSize().x
&& cell
->GetMaxSize().x
> maxColWidths
[i
])
9593 maxColWidths
[i
] = cell
->GetMaxSize().x
;
9599 // (2) Allocate initial column widths from minimum widths, absolute values and proportions
9600 // TODO: simply merge this into (1).
9601 for (i
= 0; i
< m_colCount
; i
++)
9603 if (absoluteColWidths
[i
] > 0)
9605 colWidths
[i
] = absoluteColWidths
[i
];
9607 else if (percentageColWidths
[i
] > 0)
9609 colWidths
[i
] = percentageColWidths
[i
];
9611 // This is rubbish - we calculated the absolute widths from percentages, so
9612 // we can't do it again here.
9613 //colWidths[i] = (int) (double(percentageColWidths[i]) * double(tableWidth) / 100.0 + 0.5);
9617 // (3) Process absolute or proportional widths of spanning columns,
9618 // now that we know what our fixed column widths are going to be.
9619 // Spanned cells will try to adjust columns so the span will fit.
9620 // Even existing fixed column widths can be expanded if necessary.
9621 // Actually, currently fixed columns widths aren't adjusted; instead,
9622 // the algorithm favours earlier rows and adjusts unspecified column widths
9623 // the first time only. After that, we can't know whether the column has been
9624 // specified explicitly or not. (We could make a note if necessary.)
9625 for (j
= 0; j
< m_rowCount
; j
++)
9627 // First get the overall margins so we can calculate percentage widths based on
9628 // the available content space for all cells on the row
9630 int overallRowContentMargin
= 0;
9631 int visibleCellCount
= 0;
9633 for (i
= 0; i
< m_colCount
; i
++)
9635 wxRichTextBox
* cell
= GetCell(j
, i
);
9636 if (cell
->IsShown())
9638 int cellTotalLeftMargin
= 0, cellTotalRightMargin
= 0, cellTotalTopMargin
= 0, cellTotalBottomMargin
= 0;
9639 GetTotalMargin(dc
, buffer
, cell
->GetAttributes(), cellTotalLeftMargin
, cellTotalRightMargin
, cellTotalTopMargin
, cellTotalBottomMargin
);
9641 overallRowContentMargin
+= (cellTotalLeftMargin
+ cellTotalRightMargin
);
9642 visibleCellCount
++;
9646 // Add in inter-cell padding
9647 overallRowContentMargin
+= ((visibleCellCount
-1) * paddingX
);
9649 int rowContentWidth
= internalTableWidth
- overallRowContentMargin
;
9650 wxSize
rowTableSize(rowContentWidth
, 0);
9651 wxTextAttrDimensionConverter
converter(dc
, scale
, rowTableSize
);
9653 for (i
= 0; i
< m_colCount
; i
++)
9655 wxRichTextBox
* cell
= GetCell(j
, i
);
9656 if (cell
->IsShown())
9659 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9660 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9664 int spans
= wxMin(colSpan
, m_colCount
- i
);
9668 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9670 cellWidth
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetWidth());
9671 // Override absolute width with minimum width if necessary
9672 if (cell
->GetMinSize().x
> 0 && cellWidth
!=1 && cell
->GetMinSize().x
> cellWidth
)
9673 cellWidth
= cell
->GetMinSize().x
;
9677 // Do we want to do this? It's the only chance we get to
9678 // use the cell's min/max sizes, so we need to work out
9679 // how we're going to balance the unspecified spanning cell
9680 // width with the possibility more-constrained constituent cell widths.
9681 // Say there's a tiny bitmap giving it a max width of 10 pixels. We
9682 // don't want to constraint all the spanned columns to fit into this cell.
9683 // OK, let's say that if any of the constituent columns don't fit,
9684 // then we simply stop constraining the columns; instead, we'll just fit the spanning
9685 // cells to the columns later.
9686 cellWidth
= cell
->GetMinSize().x
;
9687 if (cell
->GetMaxSize().x
> cellWidth
)
9688 cellWidth
= cell
->GetMaxSize().x
;
9691 // Subtract the padding between cells
9692 int spanningWidth
= cellWidth
;
9693 spanningWidth
-= paddingX
* (spans
-1);
9695 if (spanningWidth
> 0)
9697 // Now share the spanning width between columns within that span
9698 // TODO: take into account min widths of columns within the span
9699 int spanningWidthLeft
= spanningWidth
;
9700 int stretchColCount
= 0;
9701 for (k
= i
; k
< (i
+spans
); k
++)
9703 if (colWidths
[k
] > 0) // absolute or proportional width has been specified
9704 spanningWidthLeft
-= colWidths
[k
];
9708 // Now divide what's left between the remaining columns
9710 if (stretchColCount
> 0)
9711 colShare
= spanningWidthLeft
/ stretchColCount
;
9712 int colShareRemainder
= spanningWidthLeft
- (colShare
* stretchColCount
);
9714 // If fixed-width columns are currently too big, then we'll later
9715 // stretch the spanned cell to fit.
9717 if (spanningWidthLeft
> 0)
9719 for (k
= i
; k
< (i
+spans
); k
++)
9721 if (colWidths
[k
] <= 0) // absolute or proportional width has not been specified
9723 int newWidth
= colShare
;
9724 if (k
== (i
+spans
-1))
9725 newWidth
+= colShareRemainder
; // ensure all pixels are filled
9726 colWidths
[k
] = newWidth
;
9737 // (4) Next, share any remaining space out between columns that have not yet been calculated.
9738 // TODO: take into account min widths of columns within the span
9739 int tableWidthMinusPadding
= internalTableWidth
- (m_colCount
-1)*paddingX
;
9740 int widthLeft
= tableWidthMinusPadding
;
9741 int stretchColCount
= 0;
9742 for (i
= 0; i
< m_colCount
; i
++)
9744 // TODO: we need to take into account min widths.
9745 // Subtract min width from width left, then
9746 // add the colShare to the min width
9747 if (colWidths
[i
] > 0) // absolute or proportional width has been specified
9748 widthLeft
-= colWidths
[i
];
9751 if (minColWidths
[i
] > 0)
9752 widthLeft
-= minColWidths
[i
];
9758 // Now divide what's left between the remaining columns
9760 if (stretchColCount
> 0)
9761 colShare
= widthLeft
/ stretchColCount
;
9762 int colShareRemainder
= widthLeft
- (colShare
* stretchColCount
);
9764 // Check we don't have enough space, in which case shrink all columns, overriding
9765 // any absolute/proportional widths
9766 // TODO: actually we would like to divide up the shrinkage according to size.
9767 // How do we calculate the proportions that will achieve this?
9768 // Could first choose an arbitrary value for stretching cells, and then calculate
9769 // factors to multiply each width by.
9770 // TODO: want to record this fact and pass to an iteration that tries e.g. min widths
9771 if (widthLeft
< 0 || (stretchToFitTableWidth
&& (stretchColCount
== 0)))
9773 colShare
= tableWidthMinusPadding
/ m_colCount
;
9774 colShareRemainder
= tableWidthMinusPadding
- (colShare
* m_colCount
);
9775 for (i
= 0; i
< m_colCount
; i
++)
9778 minColWidths
[i
] = 0;
9782 // We have to adjust the columns if either we need to shrink the
9783 // table to fit the parent/table width, or we explicitly set the
9784 // table width and need to stretch out the table.
9785 if (widthLeft
< 0 || stretchToFitTableWidth
)
9787 for (i
= 0; i
< m_colCount
; i
++)
9789 if (colWidths
[i
] <= 0) // absolute or proportional width has not been specified
9791 if (minColWidths
[i
] > 0)
9792 colWidths
[i
] = minColWidths
[i
] + colShare
;
9794 colWidths
[i
] = colShare
;
9795 if (i
== (m_colCount
-1))
9796 colWidths
[i
] += colShareRemainder
; // ensure all pixels are filled
9801 // TODO: if spanned cells have no specified or max width, make them the
9802 // as big as the columns they span. Do this for all spanned cells in all
9803 // rows, of course. Size any spanned cells left over at the end - even if they
9804 // have width > 0, make sure they're limited to the appropriate column edge.
9808 Sort out confusion between content width
9809 and overall width later. For now, assume we specify overall width.
9811 So, now we've laid out the table to fit into the given space
9812 and have used specified widths and minimum widths.
9814 Now we need to consider how we will try to take maximum width into account.
9818 // (??) TODO: take max width into account
9820 // (6) Lay out all cells again with the current values
9823 int y
= availableSpace
.y
;
9824 for (j
= 0; j
< m_rowCount
; j
++)
9826 int x
= availableSpace
.x
; // TODO: take into account centering etc.
9827 int maxCellHeight
= 0;
9828 int maxSpecifiedCellHeight
= 0;
9830 wxArrayInt actualWidths
;
9831 actualWidths
.Add(0, m_colCount
);
9833 wxTextAttrDimensionConverter
converter(dc
, scale
);
9834 for (i
= 0; i
< m_colCount
; i
++)
9836 wxRichTextCell
* cell
= GetCell(j
, i
);
9837 if (cell
->IsShown())
9839 // Get max specified cell height
9840 // Don't handle percentages for height
9841 if (cell
->GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && cell
->GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() != wxTEXT_ATTR_UNITS_PERCENTAGE
)
9843 int h
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetHeight());
9844 if (h
> maxSpecifiedCellHeight
)
9845 maxSpecifiedCellHeight
= h
;
9848 if (colWidths
[i
] > 0) // absolute or proportional width has been specified
9851 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9852 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9854 wxRect availableCellSpace
;
9856 // TODO: take into acount spans
9859 // Calculate the size of this spanning cell from its constituent columns
9861 int spans
= wxMin(colSpan
, m_colCount
- i
);
9862 for (k
= i
; k
< spans
; k
++)
9868 availableCellSpace
= wxRect(x
, y
, xx
, -1);
9871 availableCellSpace
= wxRect(x
, y
, colWidths
[i
], -1);
9873 // Store actual width so we can force cell to be the appropriate width on the final loop
9874 actualWidths
[i
] = availableCellSpace
.GetWidth();
9877 cell
->Invalidate(wxRICHTEXT_ALL
);
9878 cell
->Layout(dc
, context
, availableCellSpace
, availableSpace
, style
);
9880 // TODO: use GetCachedSize().x to compute 'natural' size
9882 x
+= (availableCellSpace
.GetWidth() + paddingX
);
9883 if (cell
->GetCachedSize().y
> maxCellHeight
)
9884 maxCellHeight
= cell
->GetCachedSize().y
;
9889 maxCellHeight
= wxMax(maxCellHeight
, maxSpecifiedCellHeight
);
9891 for (i
= 0; i
< m_colCount
; i
++)
9893 wxRichTextCell
* cell
= GetCell(j
, i
);
9894 if (cell
->IsShown())
9896 wxRect availableCellSpace
= wxRect(cell
->GetPosition(), wxSize(actualWidths
[i
], maxCellHeight
));
9897 // Lay out cell with new height
9898 cell
->Invalidate(wxRICHTEXT_ALL
);
9899 cell
->Layout(dc
, context
, availableCellSpace
, availableSpace
, style
);
9901 // Make sure the cell size really is the appropriate size,
9902 // not the calculated box size
9903 cell
->SetCachedSize(wxSize(actualWidths
[i
], maxCellHeight
));
9905 maxRight
= wxMax(maxRight
, cell
->GetPosition().x
+ cell
->GetCachedSize().x
);
9910 if (j
< (m_rowCount
-1))
9914 // We need to add back the margins etc.
9916 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
9917 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxRight
- availableSpace
.x
, y
- availableSpace
.y
));
9918 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
9919 SetCachedSize(marginRect
.GetSize());
9922 // TODO: calculate max size
9924 SetMaxSize(GetCachedSize());
9927 // TODO: calculate min size
9929 SetMinSize(GetCachedSize());
9932 // TODO: currently we use either a fixed table width or the parent's size.
9933 // We also want to be able to calculate the table width from its content,
9934 // whether using fixed column widths or cell content min/max width.
9935 // Probably need a boolean flag to say whether we need to stretch cells
9936 // to fit the table width, or to simply use min/max cell widths. The
9937 // trouble with this is that if cell widths are not specified, they
9938 // will be tiny; we could use arbitrary defaults but this seems unsatisfactory.
9939 // Anyway, ignoring that problem, we probably need to factor layout into a function
9940 // that can can calculate the maximum unconstrained layout in case table size is
9941 // not specified. Then LayoutToBestSize() can choose to use either parent size to
9942 // constrain Layout(), or the previously-calculated max size to constraint layout.
9947 // Finds the absolute position and row height for the given character position
9948 bool wxRichTextTable::FindPosition(wxDC
& dc
, wxRichTextDrawingContext
& context
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
9950 wxRichTextCell
* child
= GetCell(index
+1);
9953 // Find the position at the start of the child cell, since the table doesn't
9954 // have any caret position of its own.
9955 return child
->FindPosition(dc
, context
, -1, pt
, height
, forceLineStart
);
9961 // Get the cell at the given character position (in the range of the table).
9962 wxRichTextCell
* wxRichTextTable::GetCell(long pos
) const
9964 int row
= 0, col
= 0;
9965 if (GetCellRowColumnPosition(pos
, row
, col
))
9967 return GetCell(row
, col
);
9973 // Get the row/column for a given character position
9974 bool wxRichTextTable::GetCellRowColumnPosition(long pos
, int& row
, int& col
) const
9976 if (m_colCount
== 0 || m_rowCount
== 0)
9979 row
= (int) (pos
/ m_colCount
);
9980 col
= pos
- (row
* m_colCount
);
9982 wxASSERT(row
< m_rowCount
&& col
< m_colCount
);
9984 if (row
< m_rowCount
&& col
< m_colCount
)
9990 // Calculate range, taking row/cell ordering into account instead of relying
9991 // on list ordering.
9992 void wxRichTextTable::CalculateRange(long start
, long& end
)
9994 long current
= start
;
9995 long lastEnd
= current
;
10004 for (i
= 0; i
< m_rowCount
; i
++)
10006 for (j
= 0; j
< m_colCount
; j
++)
10008 wxRichTextCell
* child
= GetCell(i
, j
);
10013 child
->CalculateRange(current
, childEnd
);
10015 lastEnd
= childEnd
;
10016 current
= childEnd
+ 1;
10021 // A top-level object always has a range of size 1,
10022 // because its children don't count at this level.
10024 m_range
.SetRange(start
, start
);
10026 // An object with no children has zero length
10027 if (m_children
.GetCount() == 0)
10029 m_ownRange
.SetRange(0, lastEnd
);
10032 // Gets the range size.
10033 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
10035 return wxRichTextBox::GetRangeSize(range
, size
, descent
, dc
, context
, flags
, position
, parentSize
, partialExtents
);
10038 // Deletes content in the given range.
10039 bool wxRichTextTable::DeleteRange(const wxRichTextRange
& WXUNUSED(range
))
10041 // TODO: implement deletion of cells
10045 // Gets any text in this object for the given range.
10046 wxString
wxRichTextTable::GetTextForRange(const wxRichTextRange
& range
) const
10048 return wxRichTextBox::GetTextForRange(range
);
10051 // Copies this object.
10052 void wxRichTextTable::Copy(const wxRichTextTable
& obj
)
10054 wxRichTextBox::Copy(obj
);
10058 m_rowCount
= obj
.m_rowCount
;
10059 m_colCount
= obj
.m_colCount
;
10061 m_cells
.Add(wxRichTextObjectPtrArray(), m_rowCount
);
10064 for (i
= 0; i
< m_rowCount
; i
++)
10066 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
10067 for (j
= 0; j
< m_colCount
; j
++)
10069 wxRichTextCell
* cell
= wxDynamicCast(obj
.GetCell(i
, j
)->Clone(), wxRichTextCell
);
10072 colArray
.Add(cell
);
10077 void wxRichTextTable::ClearTable()
10083 bool wxRichTextTable::CreateTable(int rows
, int cols
)
10090 m_cells
.Add(wxRichTextObjectPtrArray(), rows
);
10093 for (i
= 0; i
< rows
; i
++)
10095 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
10096 for (j
= 0; j
< cols
; j
++)
10098 wxRichTextCell
* cell
= new wxRichTextCell
;
10100 cell
->AddParagraph(wxEmptyString
);
10102 colArray
.Add(cell
);
10109 wxRichTextCell
* wxRichTextTable::GetCell(int row
, int col
) const
10111 wxASSERT(row
< m_rowCount
);
10112 wxASSERT(col
< m_colCount
);
10114 if (row
< m_rowCount
&& col
< m_colCount
)
10116 wxRichTextObjectPtrArray
& colArray
= m_cells
[row
];
10117 wxRichTextObject
* obj
= colArray
[col
];
10118 return wxDynamicCast(obj
, wxRichTextCell
);
10124 // Returns a selection object specifying the selections between start and end character positions.
10125 // For example, a table would deduce what cells (of range length 1) are selected when dragging across the table.
10126 wxRichTextSelection
wxRichTextTable::GetSelection(long start
, long end
) const
10128 wxRichTextSelection selection
;
10129 selection
.SetContainer((wxRichTextTable
*) this);
10138 wxASSERT( start
>= 0 && end
< (m_colCount
* m_rowCount
));
10140 if (end
>= (m_colCount
* m_rowCount
))
10143 // We need to find the rectangle of cells that is described by the rectangle
10144 // with start, end as the diagonal. Make sure we don't add cells that are
10145 // not currenty visible because they are overlapped by spanning cells.
10147 --------------------------
10148 | 0 | 1 | 2 | 3 | 4 |
10149 --------------------------
10150 | 5 | 6 | 7 | 8 | 9 |
10151 --------------------------
10152 | 10 | 11 | 12 | 13 | 14 |
10153 --------------------------
10154 | 15 | 16 | 17 | 18 | 19 |
10155 --------------------------
10157 Let's say we select 6 -> 18.
10159 Left and right edge cols of rectangle are 1 and 3 inclusive. Find least/greatest to find
10160 which is left and which is right.
10162 Top and bottom edge rows are 1 and 3 inclusive. Again, find least/greatest to find top and bottom.
10164 Now go through rows from 1 to 3 and only add cells that are (a) within above column range
10170 int leftCol
= start
- m_colCount
* int(start
/m_colCount
);
10171 int rightCol
= end
- m_colCount
* int(end
/m_colCount
);
10173 int topRow
= int(start
/m_colCount
);
10174 int bottomRow
= int(end
/m_colCount
);
10176 if (leftCol
> rightCol
)
10178 int tmp
= rightCol
;
10179 rightCol
= leftCol
;
10183 if (topRow
> bottomRow
)
10185 int tmp
= bottomRow
;
10186 bottomRow
= topRow
;
10191 for (i
= topRow
; i
<= bottomRow
; i
++)
10193 for (j
= leftCol
; j
<= rightCol
; j
++)
10195 wxRichTextCell
* cell
= GetCell(i
, j
);
10196 if (cell
&& cell
->IsShown())
10197 selection
.Add(cell
->GetRange());
10204 // Sets the attributes for the cells specified by the selection.
10205 bool wxRichTextTable::SetCellStyle(const wxRichTextSelection
& selection
, const wxRichTextAttr
& style
, int flags
)
10207 if (selection
.GetContainer() != this)
10210 wxRichTextBuffer
* buffer
= GetBuffer();
10211 bool haveControl
= (buffer
&& buffer
->GetRichTextCtrl() != NULL
);
10212 bool withUndo
= haveControl
&& ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
10215 buffer
->BeginBatchUndo(_("Set Cell Style"));
10217 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
10220 wxRichTextCell
* cell
= wxDynamicCast(node
->GetData(), wxRichTextCell
);
10221 if (cell
&& selection
.WithinSelection(cell
->GetRange().GetStart()))
10222 SetStyle(cell
, style
, flags
);
10223 node
= node
->GetNext();
10226 // Do action, or delay it until end of batch.
10228 buffer
->EndBatchUndo();
10233 bool wxRichTextTable::DeleteRows(int startRow
, int noRows
)
10235 wxASSERT((startRow
+ noRows
) < m_rowCount
);
10236 if ((startRow
+ noRows
) >= m_rowCount
)
10240 for (i
= startRow
; i
< (startRow
+noRows
); i
++)
10242 wxRichTextObjectPtrArray
& colArray
= m_cells
[startRow
];
10243 for (j
= 0; j
< (int) colArray
.GetCount(); j
++)
10245 wxRichTextObject
* cell
= colArray
[j
];
10246 RemoveChild(cell
, true);
10249 // Keep deleting at the same position, since we move all
10251 m_cells
.RemoveAt(startRow
);
10254 m_rowCount
= m_rowCount
- noRows
;
10259 bool wxRichTextTable::DeleteColumns(int startCol
, int noCols
)
10261 wxASSERT((startCol
+ noCols
) < m_colCount
);
10262 if ((startCol
+ noCols
) >= m_colCount
)
10265 bool deleteRows
= (noCols
== m_colCount
);
10268 for (i
= 0; i
< m_rowCount
; i
++)
10270 wxRichTextObjectPtrArray
& colArray
= m_cells
[deleteRows
? 0 : i
];
10271 for (j
= startCol
; j
< (startCol
+noCols
); j
++)
10273 wxRichTextObject
* cell
= colArray
[j
];
10274 RemoveChild(cell
, true);
10278 m_cells
.RemoveAt(0);
10283 m_colCount
= m_colCount
- noCols
;
10288 bool wxRichTextTable::AddRows(int startRow
, int noRows
, const wxRichTextAttr
& attr
)
10290 wxASSERT(startRow
<= m_rowCount
);
10291 if (startRow
> m_rowCount
)
10295 for (i
= 0; i
< noRows
; i
++)
10298 if (startRow
== m_rowCount
)
10300 m_cells
.Add(wxRichTextObjectPtrArray());
10301 idx
= m_cells
.GetCount() - 1;
10305 m_cells
.Insert(wxRichTextObjectPtrArray(), startRow
+i
);
10309 wxRichTextObjectPtrArray
& colArray
= m_cells
[idx
];
10310 for (j
= 0; j
< m_colCount
; j
++)
10312 wxRichTextCell
* cell
= new wxRichTextCell
;
10313 cell
->GetAttributes() = attr
;
10316 colArray
.Add(cell
);
10320 m_rowCount
= m_rowCount
+ noRows
;
10324 bool wxRichTextTable::AddColumns(int startCol
, int noCols
, const wxRichTextAttr
& attr
)
10326 wxASSERT(startCol
<= m_colCount
);
10327 if (startCol
> m_colCount
)
10331 for (i
= 0; i
< m_rowCount
; i
++)
10333 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
10334 for (j
= 0; j
< noCols
; j
++)
10336 wxRichTextCell
* cell
= new wxRichTextCell
;
10337 cell
->GetAttributes() = attr
;
10341 if (startCol
== m_colCount
)
10342 colArray
.Add(cell
);
10344 colArray
.Insert(cell
, startCol
+j
);
10348 m_colCount
= m_colCount
+ noCols
;
10353 // Edit properties via a GUI
10354 bool wxRichTextTable::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
10356 wxRichTextObjectPropertiesDialog
boxDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Table Properties"));
10357 boxDlg
.SetAttributes(GetAttributes());
10359 if (boxDlg
.ShowModal() == wxID_OK
)
10361 boxDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
10369 * Module to initialise and clean up handlers
10372 class wxRichTextModule
: public wxModule
10374 DECLARE_DYNAMIC_CLASS(wxRichTextModule
)
10376 wxRichTextModule() {}
10379 wxRichTextBuffer::SetRenderer(new wxRichTextStdRenderer
);
10380 wxRichTextBuffer::InitStandardHandlers();
10381 wxRichTextParagraph::InitDefaultTabs();
10383 wxRichTextXMLHandler::RegisterNodeName(wxT("text"), wxT("wxRichTextPlainText"));
10384 wxRichTextXMLHandler::RegisterNodeName(wxT("symbol"), wxT("wxRichTextPlainText"));
10385 wxRichTextXMLHandler::RegisterNodeName(wxT("image"), wxT("wxRichTextImage"));
10386 wxRichTextXMLHandler::RegisterNodeName(wxT("paragraph"), wxT("wxRichTextParagraph"));
10387 wxRichTextXMLHandler::RegisterNodeName(wxT("paragraphlayout"), wxT("wxRichTextParagraphLayoutBox"));
10388 wxRichTextXMLHandler::RegisterNodeName(wxT("textbox"), wxT("wxRichTextBox"));
10389 wxRichTextXMLHandler::RegisterNodeName(wxT("cell"), wxT("wxRichTextCell"));
10390 wxRichTextXMLHandler::RegisterNodeName(wxT("table"), wxT("wxRichTextTable"));
10391 wxRichTextXMLHandler::RegisterNodeName(wxT("field"), wxT("wxRichTextField"));
10397 wxRichTextBuffer::CleanUpHandlers();
10398 wxRichTextBuffer::CleanUpDrawingHandlers();
10399 wxRichTextBuffer::CleanUpFieldTypes();
10400 wxRichTextXMLHandler::ClearNodeToClassMap();
10401 wxRichTextDecimalToRoman(-1);
10402 wxRichTextParagraph::ClearDefaultTabs();
10403 wxRichTextCtrl::ClearAvailableFontNames();
10404 wxRichTextBuffer::SetRenderer(NULL
);
10408 IMPLEMENT_DYNAMIC_CLASS(wxRichTextModule
, wxModule
)
10411 // If the richtext lib is dynamically loaded after the app has already started
10412 // (such as from wxPython) then the built-in module system will not init this
10413 // module. Provide this function to do it manually.
10414 void wxRichTextModuleInit()
10416 wxModule
* module = new wxRichTextModule
;
10418 wxModule::RegisterModule(module);
10423 * Commands for undo/redo
10427 wxRichTextCommand::wxRichTextCommand(const wxString
& name
, wxRichTextCommandId id
, wxRichTextBuffer
* buffer
,
10428 wxRichTextParagraphLayoutBox
* container
, wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
): wxCommand(true, name
)
10430 /* wxRichTextAction* action = */ new wxRichTextAction(this, name
, id
, buffer
, container
, ctrl
, ignoreFirstTime
);
10433 wxRichTextCommand::wxRichTextCommand(const wxString
& name
): wxCommand(true, name
)
10437 wxRichTextCommand::~wxRichTextCommand()
10442 void wxRichTextCommand::AddAction(wxRichTextAction
* action
)
10444 if (!m_actions
.Member(action
))
10445 m_actions
.Append(action
);
10448 bool wxRichTextCommand::Do()
10450 for (wxList::compatibility_iterator node
= m_actions
.GetFirst(); node
; node
= node
->GetNext())
10452 wxRichTextAction
* action
= (wxRichTextAction
*) node
->GetData();
10459 bool wxRichTextCommand::Undo()
10461 for (wxList::compatibility_iterator node
= m_actions
.GetLast(); node
; node
= node
->GetPrevious())
10463 wxRichTextAction
* action
= (wxRichTextAction
*) node
->GetData();
10470 void wxRichTextCommand::ClearActions()
10472 WX_CLEAR_LIST(wxList
, m_actions
);
10476 * Individual action
10480 wxRichTextAction::wxRichTextAction(wxRichTextCommand
* cmd
, const wxString
& name
, wxRichTextCommandId id
,
10481 wxRichTextBuffer
* buffer
, wxRichTextParagraphLayoutBox
* container
,
10482 wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
)
10486 m_containerAddress
.Create(buffer
, container
);
10487 m_ignoreThis
= ignoreFirstTime
;
10492 m_newParagraphs
.SetDefaultStyle(buffer
->GetDefaultStyle());
10493 m_newParagraphs
.SetBasicStyle(buffer
->GetBasicStyle());
10495 cmd
->AddAction(this);
10498 wxRichTextAction::~wxRichTextAction()
10504 // Returns the container that this action refers to, using the container address and top-level buffer.
10505 wxRichTextParagraphLayoutBox
* wxRichTextAction::GetContainer() const
10507 wxRichTextParagraphLayoutBox
* container
= wxDynamicCast(GetContainerAddress().GetObject(m_buffer
), wxRichTextParagraphLayoutBox
);
10512 void wxRichTextAction::CalculateRefreshOptimizations(wxArrayInt
& optimizationLineCharPositions
, wxArrayInt
& optimizationLineYPositions
)
10514 // Store a list of line start character and y positions so we can figure out which area
10515 // we need to refresh
10517 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10518 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10519 wxASSERT(container
!= NULL
);
10523 // NOTE: we're assuming that the buffer is laid out correctly at this point.
10524 // If we had several actions, which only invalidate and leave layout until the
10525 // paint handler is called, then this might not be true. So we may need to switch
10526 // optimisation on only when we're simply adding text and not simultaneously
10527 // deleting a selection, for example. Or, we make sure the buffer is laid out correctly
10528 // first, but of course this means we'll be doing it twice.
10529 if (!m_buffer
->IsDirty() && m_ctrl
) // can only do optimisation if the buffer is already laid out correctly
10531 wxSize clientSize
= m_ctrl
->GetUnscaledSize(m_ctrl
->GetClientSize());
10532 wxPoint firstVisiblePt
= m_ctrl
->GetUnscaledPoint(m_ctrl
->GetFirstVisiblePoint());
10533 int lastY
= firstVisiblePt
.y
+ clientSize
.y
;
10535 wxRichTextParagraph
* para
= container
->GetParagraphAtPosition(GetRange().GetStart());
10536 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(para
);
10539 wxRichTextParagraph
* child
= (wxRichTextParagraph
*) node
->GetData();
10540 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
10543 wxRichTextLine
* line
= node2
->GetData();
10544 wxPoint pt
= line
->GetAbsolutePosition();
10545 wxRichTextRange range
= line
->GetAbsoluteRange();
10549 node2
= wxRichTextLineList::compatibility_iterator();
10550 node
= wxRichTextObjectList::compatibility_iterator();
10552 else if (range
.GetStart() > GetPosition() && pt
.y
>= firstVisiblePt
.y
)
10554 optimizationLineCharPositions
.Add(range
.GetStart());
10555 optimizationLineYPositions
.Add(pt
.y
);
10559 node2
= node2
->GetNext();
10563 node
= node
->GetNext();
10569 bool wxRichTextAction::Do()
10571 m_buffer
->Modify(true);
10573 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10574 wxASSERT(container
!= NULL
);
10580 case wxRICHTEXT_INSERT
:
10582 // Store a list of line start character and y positions so we can figure out which area
10583 // we need to refresh
10584 wxArrayInt optimizationLineCharPositions
;
10585 wxArrayInt optimizationLineYPositions
;
10587 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10588 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
10591 container
->InsertFragment(GetRange().GetStart(), m_newParagraphs
);
10592 container
->UpdateRanges();
10594 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10595 // Layout() would stop prematurely at the top level.
10596 container
->InvalidateHierarchy(wxRichTextRange(wxMax(0, GetRange().GetStart()-1), GetRange().GetEnd()));
10598 long newCaretPosition
= GetPosition() + m_newParagraphs
.GetOwnRange().GetLength();
10600 // Character position to caret position
10601 newCaretPosition
--;
10603 // Don't take into account the last newline
10604 if (m_newParagraphs
.GetPartialParagraph())
10605 newCaretPosition
--;
10607 if (m_newParagraphs
.GetChildren().GetCount() > 1)
10609 wxRichTextObject
* p
= (wxRichTextObject
*) m_newParagraphs
.GetChildren().GetLast()->GetData();
10610 if (p
->GetRange().GetLength() == 1)
10611 newCaretPosition
--;
10614 newCaretPosition
= wxMin(newCaretPosition
, (container
->GetOwnRange().GetEnd()-1));
10616 UpdateAppearance(newCaretPosition
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */);
10618 wxRichTextEvent
cmdEvent(
10619 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED
,
10620 m_ctrl
? m_ctrl
->GetId() : -1);
10621 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10622 cmdEvent
.SetRange(GetRange());
10623 cmdEvent
.SetPosition(GetRange().GetStart());
10624 cmdEvent
.SetContainer(container
);
10626 m_buffer
->SendEvent(cmdEvent
);
10630 case wxRICHTEXT_DELETE
:
10632 wxArrayInt optimizationLineCharPositions
;
10633 wxArrayInt optimizationLineYPositions
;
10635 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10636 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
10639 container
->DeleteRange(GetRange());
10640 container
->UpdateRanges();
10641 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10642 // Layout() would stop prematurely at the top level.
10643 container
->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
10645 long caretPos
= GetRange().GetStart()-1;
10646 if (caretPos
>= container
->GetOwnRange().GetEnd())
10649 UpdateAppearance(caretPos
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */);
10651 wxRichTextEvent
cmdEvent(
10652 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED
,
10653 m_ctrl
? m_ctrl
->GetId() : -1);
10654 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10655 cmdEvent
.SetRange(GetRange());
10656 cmdEvent
.SetPosition(GetRange().GetStart());
10657 cmdEvent
.SetContainer(container
);
10659 m_buffer
->SendEvent(cmdEvent
);
10663 case wxRICHTEXT_CHANGE_STYLE
:
10664 case wxRICHTEXT_CHANGE_PROPERTIES
:
10666 ApplyParagraphs(GetNewParagraphs());
10668 // Invalidate the whole buffer if there were floating objects
10669 if (wxRichTextBuffer::GetFloatingLayoutMode() && container
->GetFloatingObjectCount() > 0)
10670 m_buffer
->InvalidateHierarchy(wxRICHTEXT_ALL
);
10673 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10674 // Layout() would stop prematurely at the top level.
10675 container
->InvalidateHierarchy(GetRange());
10678 UpdateAppearance(GetPosition());
10680 wxRichTextEvent
cmdEvent(
10681 m_cmdId
== wxRICHTEXT_CHANGE_STYLE
? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
: wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED
,
10682 m_ctrl
? m_ctrl
->GetId() : -1);
10683 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10684 cmdEvent
.SetRange(GetRange());
10685 cmdEvent
.SetPosition(GetRange().GetStart());
10686 cmdEvent
.SetContainer(container
);
10688 m_buffer
->SendEvent(cmdEvent
);
10692 case wxRICHTEXT_CHANGE_ATTRIBUTES
:
10694 wxRichTextObject
* obj
= m_objectAddress
.GetObject(m_buffer
); // container->GetChildAtPosition(GetRange().GetStart());
10697 wxRichTextAttr oldAttr
= obj
->GetAttributes();
10698 obj
->GetAttributes() = m_attributes
;
10699 m_attributes
= oldAttr
;
10702 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10703 // Layout() would stop prematurely at the top level.
10704 // Invalidate the whole buffer if there were floating objects
10705 if (wxRichTextBuffer::GetFloatingLayoutMode() && container
->GetFloatingObjectCount() > 0)
10706 m_buffer
->InvalidateHierarchy(wxRICHTEXT_ALL
);
10708 container
->InvalidateHierarchy(GetRange());
10710 UpdateAppearance(GetPosition());
10712 wxRichTextEvent
cmdEvent(
10713 wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
,
10714 m_ctrl
? m_ctrl
->GetId() : -1);
10715 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10716 cmdEvent
.SetRange(GetRange());
10717 cmdEvent
.SetPosition(GetRange().GetStart());
10718 cmdEvent
.SetContainer(container
);
10720 m_buffer
->SendEvent(cmdEvent
);
10724 case wxRICHTEXT_CHANGE_OBJECT
:
10726 wxRichTextObject
* obj
= m_objectAddress
.GetObject(m_buffer
);
10727 // wxRichTextObject* obj = container->GetChildAtPosition(GetRange().GetStart());
10728 if (obj
&& m_object
)
10730 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(obj
);
10733 wxRichTextObject
* obj
= node
->GetData();
10734 node
->SetData(m_object
);
10739 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10740 // Layout() would stop prematurely at the top level.
10741 // Invalidate the whole buffer if there were floating objects
10742 if (wxRichTextBuffer::GetFloatingLayoutMode() && container
->GetFloatingObjectCount() > 0)
10743 m_buffer
->InvalidateHierarchy(wxRICHTEXT_ALL
);
10745 container
->InvalidateHierarchy(GetRange());
10747 UpdateAppearance(GetPosition());
10749 // TODO: send new kind of modification event
10760 bool wxRichTextAction::Undo()
10762 m_buffer
->Modify(true);
10764 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10765 wxASSERT(container
!= NULL
);
10771 case wxRICHTEXT_INSERT
:
10773 wxArrayInt optimizationLineCharPositions
;
10774 wxArrayInt optimizationLineYPositions
;
10776 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10777 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
10780 container
->DeleteRange(GetRange());
10781 container
->UpdateRanges();
10783 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10784 // Layout() would stop prematurely at the top level.
10785 container
->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
10787 long newCaretPosition
= GetPosition() - 1;
10789 UpdateAppearance(newCaretPosition
, true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */);
10791 wxRichTextEvent
cmdEvent(
10792 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED
,
10793 m_ctrl
? m_ctrl
->GetId() : -1);
10794 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10795 cmdEvent
.SetRange(GetRange());
10796 cmdEvent
.SetPosition(GetRange().GetStart());
10797 cmdEvent
.SetContainer(container
);
10799 m_buffer
->SendEvent(cmdEvent
);
10803 case wxRICHTEXT_DELETE
:
10805 wxArrayInt optimizationLineCharPositions
;
10806 wxArrayInt optimizationLineYPositions
;
10808 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10809 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
10812 container
->InsertFragment(GetRange().GetStart(), m_oldParagraphs
);
10813 container
->UpdateRanges();
10815 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10816 // Layout() would stop prematurely at the top level.
10817 container
->InvalidateHierarchy(GetRange());
10819 UpdateAppearance(GetPosition(), true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */);
10821 wxRichTextEvent
cmdEvent(
10822 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED
,
10823 m_ctrl
? m_ctrl
->GetId() : -1);
10824 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10825 cmdEvent
.SetRange(GetRange());
10826 cmdEvent
.SetPosition(GetRange().GetStart());
10827 cmdEvent
.SetContainer(container
);
10829 m_buffer
->SendEvent(cmdEvent
);
10833 case wxRICHTEXT_CHANGE_STYLE
:
10834 case wxRICHTEXT_CHANGE_PROPERTIES
:
10836 ApplyParagraphs(GetOldParagraphs());
10837 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10838 // Layout() would stop prematurely at the top level.
10839 container
->InvalidateHierarchy(GetRange());
10841 UpdateAppearance(GetPosition());
10843 wxRichTextEvent
cmdEvent(
10844 m_cmdId
== wxRICHTEXT_CHANGE_STYLE
? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
: wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED
,
10845 m_ctrl
? m_ctrl
->GetId() : -1);
10846 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10847 cmdEvent
.SetRange(GetRange());
10848 cmdEvent
.SetPosition(GetRange().GetStart());
10849 cmdEvent
.SetContainer(container
);
10851 m_buffer
->SendEvent(cmdEvent
);
10855 case wxRICHTEXT_CHANGE_ATTRIBUTES
:
10856 case wxRICHTEXT_CHANGE_OBJECT
:
10867 /// Update the control appearance
10868 void wxRichTextAction::UpdateAppearance(long caretPosition
, bool sendUpdateEvent
, wxArrayInt
* optimizationLineCharPositions
, wxArrayInt
* optimizationLineYPositions
, bool isDoCmd
)
10870 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10871 wxASSERT(container
!= NULL
);
10877 m_ctrl
->SetFocusObject(container
);
10878 m_ctrl
->SetCaretPosition(caretPosition
);
10880 if (!m_ctrl
->IsFrozen())
10882 wxRect containerRect
= container
->GetRect();
10884 m_ctrl
->LayoutContent();
10886 // Refresh everything if there were floating objects or the container changed size
10887 // (we can't yet optimize in these cases, since more complex interaction with other content occurs)
10888 if ((wxRichTextBuffer::GetFloatingLayoutMode() && container
->GetFloatingObjectCount() > 0) || (container
->GetParent() && containerRect
!= container
->GetRect()))
10890 m_ctrl
->Refresh(false);
10894 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10895 // Find refresh rectangle if we are in a position to optimise refresh
10896 if ((m_cmdId
== wxRICHTEXT_INSERT
|| m_cmdId
== wxRICHTEXT_DELETE
) && optimizationLineCharPositions
)
10900 wxSize clientSize
= m_ctrl
->GetUnscaledSize(m_ctrl
->GetClientSize());
10901 wxPoint firstVisiblePt
= m_ctrl
->GetUnscaledPoint(m_ctrl
->GetFirstVisiblePoint());
10903 // Start/end positions
10905 int lastY
= firstVisiblePt
.y
+ clientSize
.y
;
10907 bool foundEnd
= false;
10909 // position offset - how many characters were inserted
10910 int positionOffset
= GetRange().GetLength();
10912 // Determine whether this is Do or Undo, and adjust positionOffset accordingly
10913 if ((m_cmdId
== wxRICHTEXT_DELETE
&& isDoCmd
) || (m_cmdId
== wxRICHTEXT_INSERT
&& !isDoCmd
))
10914 positionOffset
= - positionOffset
;
10916 // find the first line which is being drawn at the same position as it was
10917 // before. Since we're talking about a simple insertion, we can assume
10918 // that the rest of the window does not need to be redrawn.
10920 wxRichTextParagraph
* para
= container
->GetParagraphAtPosition(GetPosition());
10921 // Since we support floating layout, we should redraw the whole para instead of just
10922 // the first line touching the invalid range.
10925 firstY
= para
->GetPosition().y
;
10928 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(para
);
10931 wxRichTextParagraph
* child
= (wxRichTextParagraph
*) node
->GetData();
10932 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
10935 wxRichTextLine
* line
= node2
->GetData();
10936 wxPoint pt
= line
->GetAbsolutePosition();
10937 wxRichTextRange range
= line
->GetAbsoluteRange();
10939 // we want to find the first line that is in the same position
10940 // as before. This will mean we're at the end of the changed text.
10942 if (pt
.y
> lastY
) // going past the end of the window, no more info
10944 node2
= wxRichTextLineList::compatibility_iterator();
10945 node
= wxRichTextObjectList::compatibility_iterator();
10947 // Detect last line in the buffer
10948 else if (!node2
->GetNext() && para
->GetRange().Contains(container
->GetOwnRange().GetEnd()))
10950 // If deleting text, make sure we refresh below as well as above
10951 if (positionOffset
>= 0)
10954 lastY
= pt
.y
+ line
->GetSize().y
;
10957 node2
= wxRichTextLineList::compatibility_iterator();
10958 node
= wxRichTextObjectList::compatibility_iterator();
10964 // search for this line being at the same position as before
10965 for (i
= 0; i
< optimizationLineCharPositions
->GetCount(); i
++)
10967 if (((*optimizationLineCharPositions
)[i
] + positionOffset
== range
.GetStart()) &&
10968 ((*optimizationLineYPositions
)[i
] == pt
.y
))
10970 // Stop, we're now the same as we were
10975 node2
= wxRichTextLineList::compatibility_iterator();
10976 node
= wxRichTextObjectList::compatibility_iterator();
10984 node2
= node2
->GetNext();
10988 node
= node
->GetNext();
10991 firstY
= wxMax(firstVisiblePt
.y
, firstY
);
10993 lastY
= firstVisiblePt
.y
+ clientSize
.y
;
10995 // Convert to device coordinates
10996 wxRect
rect(m_ctrl
->GetPhysicalPoint(m_ctrl
->GetScaledPoint(wxPoint(firstVisiblePt
.x
, firstY
))), m_ctrl
->GetScaledSize(wxSize(clientSize
.x
, lastY
- firstY
)));
10997 m_ctrl
->RefreshRect(rect
);
11001 m_ctrl
->Refresh(false);
11003 m_ctrl
->PositionCaret();
11005 // This causes styles to persist when doing programmatic
11006 // content creation except when Freeze/Thaw is used, so
11007 // disable this and check for the consequences.
11008 // m_ctrl->SetDefaultStyleToCursorStyle();
11010 if (sendUpdateEvent
)
11011 wxTextCtrl::SendTextUpdatedEvent(m_ctrl
);
11016 /// Replace the buffer paragraphs with the new ones.
11017 void wxRichTextAction::ApplyParagraphs(const wxRichTextParagraphLayoutBox
& fragment
)
11019 wxRichTextParagraphLayoutBox
* container
= GetContainer();
11020 wxASSERT(container
!= NULL
);
11024 wxRichTextObjectList::compatibility_iterator node
= fragment
.GetChildren().GetFirst();
11027 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
11028 wxASSERT (para
!= NULL
);
11030 // We'll replace the existing paragraph by finding the paragraph at this position,
11031 // delete its node data, and setting a copy as the new node data.
11032 // TODO: make more efficient by simply swapping old and new paragraph objects.
11034 wxRichTextParagraph
* existingPara
= container
->GetParagraphAtPosition(para
->GetRange().GetStart());
11037 wxRichTextObjectList::compatibility_iterator bufferParaNode
= container
->GetChildren().Find(existingPara
);
11038 if (bufferParaNode
)
11040 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(*para
);
11041 newPara
->SetParent(container
);
11043 bufferParaNode
->SetData(newPara
);
11045 delete existingPara
;
11049 node
= node
->GetNext();
11056 * This stores beginning and end positions for a range of data.
11059 WX_DEFINE_OBJARRAY(wxRichTextRangeArray
);
11061 /// Limit this range to be within 'range'
11062 bool wxRichTextRange::LimitTo(const wxRichTextRange
& range
)
11064 if (m_start
< range
.m_start
)
11065 m_start
= range
.m_start
;
11067 if (m_end
> range
.m_end
)
11068 m_end
= range
.m_end
;
11074 * wxRichTextImage implementation
11075 * This object represents an image.
11078 IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage
, wxRichTextObject
)
11080 wxRichTextImage::wxRichTextImage(const wxImage
& image
, wxRichTextObject
* parent
, wxRichTextAttr
* charStyle
):
11081 wxRichTextObject(parent
)
11084 m_imageBlock
.MakeImageBlockDefaultQuality(image
, wxBITMAP_TYPE_PNG
);
11086 SetAttributes(*charStyle
);
11089 wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock
& imageBlock
, wxRichTextObject
* parent
, wxRichTextAttr
* charStyle
):
11090 wxRichTextObject(parent
)
11093 m_imageBlock
= imageBlock
;
11095 SetAttributes(*charStyle
);
11098 wxRichTextImage::~wxRichTextImage()
11102 void wxRichTextImage::Init()
11104 m_originalImageSize
= wxSize(-1, -1);
11107 /// Create a cached image at the required size
11108 bool wxRichTextImage::LoadImageCache(wxDC
& dc
, bool resetCache
, const wxSize
& parentSize
)
11110 if (!m_imageBlock
.IsOk())
11113 // If we have an original image size, use that to compute the cached bitmap size
11114 // instead of loading the image each time. This way we can avoid loading
11115 // the image so long as the new cached bitmap size hasn't changed.
11118 if (resetCache
|| m_originalImageSize
.GetWidth() <= 0 || m_originalImageSize
.GetHeight() <= 0)
11120 m_imageCache
= wxNullBitmap
;
11122 m_imageBlock
.Load(image
);
11126 m_originalImageSize
= wxSize(image
.GetWidth(), image
.GetHeight());
11129 int width
= m_originalImageSize
.GetWidth();
11130 int height
= m_originalImageSize
.GetHeight();
11132 int parentWidth
= 0;
11133 int parentHeight
= 0;
11136 int maxHeight
= -1;
11138 wxSize sz
= parentSize
;
11139 if (sz
== wxDefaultSize
)
11141 if (GetParent() && GetParent()->GetParent())
11142 sz
= GetParent()->GetParent()->GetCachedSize();
11145 if (sz
!= wxDefaultSize
)
11147 wxRichTextBuffer
* buffer
= GetBuffer();
11150 // Find the actual space available when margin is taken into account
11151 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
11152 marginRect
= wxRect(0, 0, sz
.x
, sz
.y
);
11153 if (GetParent() && GetParent()->GetParent())
11155 buffer
->GetBoxRects(dc
, buffer
, GetParent()->GetParent()->GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
11156 sz
= contentRect
.GetSize();
11159 // Use a minimum size to stop images becoming very small
11160 parentWidth
= wxMax(100, sz
.GetWidth());
11161 parentHeight
= wxMax(100, sz
.GetHeight());
11163 if (buffer
->GetRichTextCtrl())
11164 // Start with a maximum width of the control size, even if not specified by the content,
11165 // to minimize the amount of picture overlapping the right-hand side
11166 maxWidth
= parentWidth
;
11170 if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetWidth().GetValue() > 0)
11172 if (parentWidth
> 0 && GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
11173 width
= (int) ((GetAttributes().GetTextBoxAttr().GetWidth().GetValue() * parentWidth
)/100.0);
11174 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
11175 width
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetWidth().GetValue());
11176 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
11177 width
= GetAttributes().GetTextBoxAttr().GetWidth().GetValue();
11180 // Limit to max width
11182 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() > 0)
11186 if (parentWidth
> 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
11187 mw
= (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() * parentWidth
)/100.0);
11188 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
11189 mw
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue());
11190 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
11191 mw
= GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue();
11193 // If we already have a smaller max width due to the constraints of the control size,
11194 // don't use the larger max width.
11195 if (mw
!= -1 && ((maxWidth
== -1) || (mw
< maxWidth
)))
11199 if (maxWidth
> 0 && width
> maxWidth
)
11202 // Preserve the aspect ratio
11203 if (width
!= m_originalImageSize
.GetWidth())
11204 height
= (int) (float(m_originalImageSize
.GetHeight()) * (float(width
)/float(m_originalImageSize
.GetWidth())));
11206 if (GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetHeight().GetValue() > 0)
11208 if (parentHeight
> 0 && GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
11209 height
= (int) ((GetAttributes().GetTextBoxAttr().GetHeight().GetValue() * parentHeight
)/100.0);
11210 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
11211 height
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetHeight().GetValue());
11212 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
11213 height
= GetAttributes().GetTextBoxAttr().GetHeight().GetValue();
11215 // Preserve the aspect ratio
11216 if (height
!= m_originalImageSize
.GetHeight())
11217 width
= (int) (float(m_originalImageSize
.GetWidth()) * (float(height
)/float(m_originalImageSize
.GetHeight())));
11220 // Limit to max height
11222 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() > 0)
11224 if (parentHeight
> 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
11225 maxHeight
= (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() * parentHeight
)/100.0);
11226 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
11227 maxHeight
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue());
11228 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
11229 maxHeight
= GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue();
11232 if (maxHeight
> 0 && height
> maxHeight
)
11234 height
= maxHeight
;
11236 // Preserve the aspect ratio
11237 if (height
!= m_originalImageSize
.GetHeight())
11238 width
= (int) (float(m_originalImageSize
.GetWidth()) * (float(height
)/float(m_originalImageSize
.GetHeight())));
11241 // Prevent the use of zero size
11242 width
= wxMax(1, width
);
11243 height
= wxMax(1, height
);
11245 if (m_imageCache
.IsOk() && m_imageCache
.GetWidth() == width
&& m_imageCache
.GetHeight() == height
)
11247 // Do nothing, we didn't need to change the image cache
11253 m_imageBlock
.Load(image
);
11258 if (image
.GetWidth() == width
&& image
.GetHeight() == height
)
11259 m_imageCache
= wxBitmap(image
);
11262 // If the original width and height is small, e.g. 400 or below,
11263 // scale up and then down to improve image quality. This can make
11264 // a big difference, with not much performance hit.
11265 int upscaleThreshold
= 400;
11267 if (image
.GetWidth() <= upscaleThreshold
|| image
.GetHeight() <= upscaleThreshold
)
11269 img
= image
.Scale(image
.GetWidth()*2, image
.GetHeight()*2);
11270 img
.Rescale(width
, height
, wxIMAGE_QUALITY_HIGH
);
11273 img
= image
.Scale(width
, height
, wxIMAGE_QUALITY_HIGH
);
11274 m_imageCache
= wxBitmap(img
);
11278 return m_imageCache
.IsOk();
11282 bool wxRichTextImage::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& WXUNUSED(range
), const wxRichTextSelection
& selection
, const wxRect
& rect
, int WXUNUSED(descent
), int WXUNUSED(style
))
11287 // Don't need cached size AFAIK
11288 // wxSize size = GetCachedSize();
11289 if (!LoadImageCache(dc
))
11292 wxRichTextAttr
attr(GetAttributes());
11293 context
.ApplyVirtualAttributes(attr
, this);
11295 DrawBoxAttributes(dc
, GetBuffer(), attr
, wxRect(rect
.GetPosition(), GetCachedSize()));
11297 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
11298 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
11299 marginRect
= rect
; // outer rectangle, will calculate contentRect
11300 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
11302 dc
.DrawBitmap(m_imageCache
, contentRect
.x
, contentRect
.y
, true);
11304 if (selection
.WithinSelection(GetRange().GetStart(), this))
11306 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
11307 wxCheckSetPen(dc
, *wxBLACK_PEN
);
11308 dc
.SetLogicalFunction(wxINVERT
);
11309 dc
.DrawRectangle(contentRect
);
11310 dc
.SetLogicalFunction(wxCOPY
);
11316 /// Lay the item out
11317 bool wxRichTextImage::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& WXUNUSED(parentRect
), int WXUNUSED(style
))
11319 if (!LoadImageCache(dc
))
11322 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
11323 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
11324 contentRect
= wxRect(wxPoint(0,0), imageSize
);
11326 wxRichTextAttr
attr(GetAttributes());
11327 context
.ApplyVirtualAttributes(attr
, this);
11329 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
11331 wxSize overallSize
= marginRect
.GetSize();
11333 SetCachedSize(overallSize
);
11334 SetMaxSize(overallSize
);
11335 SetMinSize(overallSize
);
11336 SetPosition(rect
.GetPosition());
11341 /// Get/set the object size for the given range. Returns false if the range
11342 /// is invalid for this object.
11343 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
11345 if (!range
.IsWithin(GetRange()))
11348 if (!((wxRichTextImage
*)this)->LoadImageCache(dc
, false, parentSize
))
11350 size
.x
= 0; size
.y
= 0;
11351 if (partialExtents
)
11352 partialExtents
->Add(0);
11356 wxRichTextAttr
attr(GetAttributes());
11357 context
.ApplyVirtualAttributes(attr
, (wxRichTextObject
*) this);
11359 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
11360 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
11361 contentRect
= wxRect(wxPoint(0,0), imageSize
);
11362 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
11364 wxSize overallSize
= marginRect
.GetSize();
11366 if (partialExtents
)
11367 partialExtents
->Add(overallSize
.x
);
11369 size
= overallSize
;
11374 // Get the 'natural' size for an object. For an image, it would be the
11376 wxTextAttrSize
wxRichTextImage::GetNaturalSize() const
11378 wxTextAttrSize size
;
11379 if (GetImageCache().IsOk())
11381 size
.SetWidth(GetImageCache().GetWidth(), wxTEXT_ATTR_UNITS_PIXELS
);
11382 size
.SetHeight(GetImageCache().GetHeight(), wxTEXT_ATTR_UNITS_PIXELS
);
11389 void wxRichTextImage::Copy(const wxRichTextImage
& obj
)
11391 wxRichTextObject::Copy(obj
);
11393 m_imageBlock
= obj
.m_imageBlock
;
11394 m_originalImageSize
= obj
.m_originalImageSize
;
11397 /// Edit properties via a GUI
11398 bool wxRichTextImage::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
11400 wxRichTextObjectPropertiesDialog
imageDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Picture Properties"));
11401 imageDlg
.SetAttributes(GetAttributes());
11403 if (imageDlg
.ShowModal() == wxID_OK
)
11405 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
11406 // indeterminate in the object.
11407 imageDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
11419 /// Compare two attribute objects
11420 bool wxTextAttrEq(const wxRichTextAttr
& attr1
, const wxRichTextAttr
& attr2
)
11422 return (attr1
== attr2
);
11426 bool wxRichTextTabsEq(const wxArrayInt
& tabs1
, const wxArrayInt
& tabs2
)
11428 if (tabs1
.GetCount() != tabs2
.GetCount())
11432 for (i
= 0; i
< tabs1
.GetCount(); i
++)
11434 if (tabs1
[i
] != tabs2
[i
])
11440 bool wxRichTextApplyStyle(wxRichTextAttr
& destStyle
, const wxRichTextAttr
& style
, wxRichTextAttr
* compareWith
)
11442 return destStyle
.Apply(style
, compareWith
);
11445 // Remove attributes
11446 bool wxRichTextRemoveStyle(wxRichTextAttr
& destStyle
, const wxRichTextAttr
& style
)
11448 return destStyle
.RemoveStyle(style
);
11451 /// Combine two bitlists, specifying the bits of interest with separate flags.
11452 bool wxRichTextCombineBitlists(int& valueA
, int valueB
, int& flagsA
, int flagsB
)
11454 return wxRichTextAttr::CombineBitlists(valueA
, valueB
, flagsA
, flagsB
);
11457 /// Compare two bitlists
11458 bool wxRichTextBitlistsEqPartial(int valueA
, int valueB
, int flags
)
11460 return wxRichTextAttr::BitlistsEqPartial(valueA
, valueB
, flags
);
11463 /// Split into paragraph and character styles
11464 bool wxRichTextSplitParaCharStyles(const wxRichTextAttr
& style
, wxRichTextAttr
& parStyle
, wxRichTextAttr
& charStyle
)
11466 return wxRichTextAttr::SplitParaCharStyles(style
, parStyle
, charStyle
);
11469 /// Convert a decimal to Roman numerals
11470 wxString
wxRichTextDecimalToRoman(long n
)
11472 static wxArrayInt decimalNumbers
;
11473 static wxArrayString romanNumbers
;
11478 decimalNumbers
.Clear();
11479 romanNumbers
.Clear();
11480 return wxEmptyString
;
11483 if (decimalNumbers
.GetCount() == 0)
11485 #define wxRichTextAddDecRom(n, r) decimalNumbers.Add(n); romanNumbers.Add(r);
11487 wxRichTextAddDecRom(1000, wxT("M"));
11488 wxRichTextAddDecRom(900, wxT("CM"));
11489 wxRichTextAddDecRom(500, wxT("D"));
11490 wxRichTextAddDecRom(400, wxT("CD"));
11491 wxRichTextAddDecRom(100, wxT("C"));
11492 wxRichTextAddDecRom(90, wxT("XC"));
11493 wxRichTextAddDecRom(50, wxT("L"));
11494 wxRichTextAddDecRom(40, wxT("XL"));
11495 wxRichTextAddDecRom(10, wxT("X"));
11496 wxRichTextAddDecRom(9, wxT("IX"));
11497 wxRichTextAddDecRom(5, wxT("V"));
11498 wxRichTextAddDecRom(4, wxT("IV"));
11499 wxRichTextAddDecRom(1, wxT("I"));
11505 while (n
> 0 && i
< 13)
11507 if (n
>= decimalNumbers
[i
])
11509 n
-= decimalNumbers
[i
];
11510 roman
+= romanNumbers
[i
];
11517 if (roman
.IsEmpty())
11523 * wxRichTextFileHandler
11524 * Base class for file handlers
11527 IMPLEMENT_CLASS(wxRichTextFileHandler
, wxObject
)
11529 #if wxUSE_FFILE && wxUSE_STREAMS
11530 bool wxRichTextFileHandler::LoadFile(wxRichTextBuffer
*buffer
, const wxString
& filename
)
11532 wxFFileInputStream
stream(filename
);
11534 return LoadFile(buffer
, stream
);
11539 bool wxRichTextFileHandler::SaveFile(wxRichTextBuffer
*buffer
, const wxString
& filename
)
11541 wxFFileOutputStream
stream(filename
);
11543 return SaveFile(buffer
, stream
);
11547 #endif // wxUSE_FFILE && wxUSE_STREAMS
11549 /// Can we handle this filename (if using files)? By default, checks the extension.
11550 bool wxRichTextFileHandler::CanHandle(const wxString
& filename
) const
11552 wxString path
, file
, ext
;
11553 wxFileName::SplitPath(filename
, & path
, & file
, & ext
);
11555 return (ext
.Lower() == GetExtension());
11559 * wxRichTextTextHandler
11560 * Plain text handler
11563 IMPLEMENT_CLASS(wxRichTextPlainTextHandler
, wxRichTextFileHandler
)
11566 bool wxRichTextPlainTextHandler::DoLoadFile(wxRichTextBuffer
*buffer
, wxInputStream
& stream
)
11568 if (!stream
.IsOk())
11574 while (!stream
.Eof())
11576 int ch
= stream
.GetC();
11580 if (ch
== 10 && lastCh
!= 13)
11583 if (ch
> 0 && ch
!= 10)
11590 buffer
->ResetAndClearCommands();
11592 buffer
->AddParagraphs(str
);
11593 buffer
->UpdateRanges();
11598 bool wxRichTextPlainTextHandler::DoSaveFile(wxRichTextBuffer
*buffer
, wxOutputStream
& stream
)
11600 if (!stream
.IsOk())
11603 wxString text
= buffer
->GetText();
11605 wxString newLine
= wxRichTextLineBreakChar
;
11606 text
.Replace(newLine
, wxT("\n"));
11608 wxCharBuffer buf
= text
.ToAscii();
11610 stream
.Write((const char*) buf
, text
.length());
11613 #endif // wxUSE_STREAMS
11616 * Stores information about an image, in binary in-memory form
11619 wxRichTextImageBlock::wxRichTextImageBlock()
11624 wxRichTextImageBlock::wxRichTextImageBlock(const wxRichTextImageBlock
& block
):wxObject()
11630 wxRichTextImageBlock::~wxRichTextImageBlock()
11635 void wxRichTextImageBlock::Init()
11639 m_imageType
= wxBITMAP_TYPE_INVALID
;
11642 void wxRichTextImageBlock::Clear()
11646 m_imageType
= wxBITMAP_TYPE_INVALID
;
11650 // Load the original image into a memory block.
11651 // If the image is not a JPEG, we must convert it into a JPEG
11652 // to conserve space.
11653 // If it's not a JPEG we can make use of 'image', already scaled, so we don't have to
11654 // load the image a 2nd time.
11656 bool wxRichTextImageBlock::MakeImageBlock(const wxString
& filename
, wxBitmapType imageType
,
11657 wxImage
& image
, bool convertToJPEG
)
11659 m_imageType
= imageType
;
11661 wxString
filenameToRead(filename
);
11662 bool removeFile
= false;
11664 if (imageType
== wxBITMAP_TYPE_INVALID
)
11665 return false; // Could not determine image type
11667 if ((imageType
!= wxBITMAP_TYPE_JPEG
) && convertToJPEG
)
11669 wxString tempFile
=
11670 wxFileName::CreateTempFileName(_("image"));
11672 wxASSERT(!tempFile
.IsEmpty());
11674 image
.SaveFile(tempFile
, wxBITMAP_TYPE_JPEG
);
11675 filenameToRead
= tempFile
;
11678 m_imageType
= wxBITMAP_TYPE_JPEG
;
11681 if (!file
.Open(filenameToRead
))
11684 m_dataSize
= (size_t) file
.Length();
11689 m_data
= ReadBlock(filenameToRead
, m_dataSize
);
11692 wxRemoveFile(filenameToRead
);
11694 return (m_data
!= NULL
);
11697 // Make an image block from the wxImage in the given
11699 bool wxRichTextImageBlock::MakeImageBlock(wxImage
& image
, wxBitmapType imageType
, int quality
)
11701 image
.SetOption(wxT("quality"), quality
);
11703 if (imageType
== wxBITMAP_TYPE_INVALID
)
11704 return false; // Could not determine image type
11706 return DoMakeImageBlock(image
, imageType
);
11709 // Uses a const wxImage for efficiency, but can't set quality (only relevant for JPEG)
11710 bool wxRichTextImageBlock::MakeImageBlockDefaultQuality(const wxImage
& image
, wxBitmapType imageType
)
11712 if (imageType
== wxBITMAP_TYPE_INVALID
)
11713 return false; // Could not determine image type
11715 return DoMakeImageBlock(image
, imageType
);
11718 // Makes the image block
11719 bool wxRichTextImageBlock::DoMakeImageBlock(const wxImage
& image
, wxBitmapType imageType
)
11721 wxMemoryOutputStream memStream
;
11722 if (!image
.SaveFile(memStream
, imageType
))
11727 unsigned char* block
= new unsigned char[memStream
.GetSize()];
11735 m_imageType
= imageType
;
11736 m_dataSize
= memStream
.GetSize();
11738 memStream
.CopyTo(m_data
, m_dataSize
);
11740 return (m_data
!= NULL
);
11744 bool wxRichTextImageBlock::Write(const wxString
& filename
)
11746 return WriteBlock(filename
, m_data
, m_dataSize
);
11749 void wxRichTextImageBlock::Copy(const wxRichTextImageBlock
& block
)
11751 m_imageType
= block
.m_imageType
;
11753 m_dataSize
= block
.m_dataSize
;
11754 if (m_dataSize
== 0)
11757 m_data
= new unsigned char[m_dataSize
];
11759 for (i
= 0; i
< m_dataSize
; i
++)
11760 m_data
[i
] = block
.m_data
[i
];
11764 void wxRichTextImageBlock::operator=(const wxRichTextImageBlock
& block
)
11769 // Load a wxImage from the block
11770 bool wxRichTextImageBlock::Load(wxImage
& image
)
11775 // Read in the image.
11777 wxMemoryInputStream
mstream(m_data
, m_dataSize
);
11778 bool success
= image
.LoadFile(mstream
, GetImageType());
11780 wxString tempFile
= wxFileName::CreateTempFileName(_("image"));
11781 wxASSERT(!tempFile
.IsEmpty());
11783 if (!WriteBlock(tempFile
, m_data
, m_dataSize
))
11787 success
= image
.LoadFile(tempFile
, GetImageType());
11788 wxRemoveFile(tempFile
);
11794 // Write data in hex to a stream
11795 bool wxRichTextImageBlock::WriteHex(wxOutputStream
& stream
)
11797 if (m_dataSize
== 0)
11800 int bufSize
= 100000;
11801 if (int(2*m_dataSize
) < bufSize
)
11802 bufSize
= 2*m_dataSize
;
11803 char* buf
= new char[bufSize
+1];
11805 int left
= m_dataSize
;
11810 if (left
*2 > bufSize
)
11812 n
= bufSize
; left
-= (bufSize
/2);
11816 n
= left
*2; left
= 0;
11820 for (i
= 0; i
< (n
/2); i
++)
11822 wxDecToHex(m_data
[j
], b
, b
+1);
11827 stream
.Write((const char*) buf
, n
);
11833 // Read data in hex from a stream
11834 bool wxRichTextImageBlock::ReadHex(wxInputStream
& stream
, int length
, wxBitmapType imageType
)
11836 int dataSize
= length
/2;
11841 // create a null terminated temporary string:
11845 m_data
= new unsigned char[dataSize
];
11847 for (i
= 0; i
< dataSize
; i
++)
11849 str
[0] = (char)stream
.GetC();
11850 str
[1] = (char)stream
.GetC();
11852 m_data
[i
] = (unsigned char)wxHexToDec(str
);
11855 m_dataSize
= dataSize
;
11856 m_imageType
= imageType
;
11861 // Allocate and read from stream as a block of memory
11862 unsigned char* wxRichTextImageBlock::ReadBlock(wxInputStream
& stream
, size_t size
)
11864 unsigned char* block
= new unsigned char[size
];
11868 stream
.Read(block
, size
);
11873 unsigned char* wxRichTextImageBlock::ReadBlock(const wxString
& filename
, size_t size
)
11875 wxFileInputStream
stream(filename
);
11876 if (!stream
.IsOk())
11879 return ReadBlock(stream
, size
);
11882 // Write memory block to stream
11883 bool wxRichTextImageBlock::WriteBlock(wxOutputStream
& stream
, unsigned char* block
, size_t size
)
11885 stream
.Write((void*) block
, size
);
11886 return stream
.IsOk();
11890 // Write memory block to file
11891 bool wxRichTextImageBlock::WriteBlock(const wxString
& filename
, unsigned char* block
, size_t size
)
11893 wxFileOutputStream
outStream(filename
);
11894 if (!outStream
.IsOk())
11897 return WriteBlock(outStream
, block
, size
);
11900 // Gets the extension for the block's type
11901 wxString
wxRichTextImageBlock::GetExtension() const
11903 wxImageHandler
* handler
= wxImage::FindHandler(GetImageType());
11905 return handler
->GetExtension();
11907 return wxEmptyString
;
11913 * The data object for a wxRichTextBuffer
11916 const wxChar
*wxRichTextBufferDataObject::ms_richTextBufferFormatId
= wxT("wxRichText");
11918 wxRichTextBufferDataObject::wxRichTextBufferDataObject(wxRichTextBuffer
* richTextBuffer
)
11920 m_richTextBuffer
= richTextBuffer
;
11922 // this string should uniquely identify our format, but is otherwise
11924 m_formatRichTextBuffer
.SetId(GetRichTextBufferFormatId());
11926 SetFormat(m_formatRichTextBuffer
);
11929 wxRichTextBufferDataObject::~wxRichTextBufferDataObject()
11931 delete m_richTextBuffer
;
11934 // after a call to this function, the richTextBuffer is owned by the caller and it
11935 // is responsible for deleting it!
11936 wxRichTextBuffer
* wxRichTextBufferDataObject::GetRichTextBuffer()
11938 wxRichTextBuffer
* richTextBuffer
= m_richTextBuffer
;
11939 m_richTextBuffer
= NULL
;
11941 return richTextBuffer
;
11944 wxDataFormat
wxRichTextBufferDataObject::GetPreferredFormat(Direction
WXUNUSED(dir
)) const
11946 return m_formatRichTextBuffer
;
11949 size_t wxRichTextBufferDataObject::GetDataSize() const
11951 if (!m_richTextBuffer
)
11957 wxStringOutputStream
stream(& bufXML
);
11958 if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
))
11960 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
11966 wxCharBuffer buffer
= bufXML
.mb_str(wxConvUTF8
);
11967 return strlen(buffer
) + 1;
11969 return bufXML
.Length()+1;
11973 bool wxRichTextBufferDataObject::GetDataHere(void *pBuf
) const
11975 if (!pBuf
|| !m_richTextBuffer
)
11981 wxStringOutputStream
stream(& bufXML
);
11982 if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
))
11984 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
11990 wxCharBuffer buffer
= bufXML
.mb_str(wxConvUTF8
);
11991 size_t len
= strlen(buffer
);
11992 memcpy((char*) pBuf
, (const char*) buffer
, len
);
11993 ((char*) pBuf
)[len
] = 0;
11995 size_t len
= bufXML
.Length();
11996 memcpy((char*) pBuf
, (const char*) bufXML
.c_str(), len
);
11997 ((char*) pBuf
)[len
] = 0;
12003 bool wxRichTextBufferDataObject::SetData(size_t WXUNUSED(len
), const void *buf
)
12005 wxDELETE(m_richTextBuffer
);
12007 wxString
bufXML((const char*) buf
, wxConvUTF8
);
12009 m_richTextBuffer
= new wxRichTextBuffer
;
12011 wxStringInputStream
stream(bufXML
);
12012 if (!m_richTextBuffer
->LoadFile(stream
, wxRICHTEXT_TYPE_XML
))
12014 wxLogError(wxT("Could not read the buffer from an XML stream.\nYou may have forgotten to add the XML file handler."));
12016 wxDELETE(m_richTextBuffer
);
12028 * wxRichTextFontTable
12029 * Manages quick access to a pool of fonts for rendering rich text
12032 WX_DECLARE_STRING_HASH_MAP_WITH_DECL(wxFont
, wxRichTextFontTableHashMap
, class WXDLLIMPEXP_RICHTEXT
);
12034 class wxRichTextFontTableData
: public wxObjectRefData
12037 wxRichTextFontTableData() {}
12039 wxFont
FindFont(const wxRichTextAttr
& fontSpec
, double fontScale
);
12041 wxRichTextFontTableHashMap m_hashMap
;
12044 wxFont
wxRichTextFontTableData::FindFont(const wxRichTextAttr
& fontSpec
, double fontScale
)
12046 wxString
facename(fontSpec
.GetFontFaceName());
12048 int fontSize
= fontSpec
.GetFontSize();
12049 if (fontScale
!= 1.0)
12050 fontSize
= (int) ((double(fontSize
) * fontScale
) + 0.5);
12053 if (fontSpec
.HasFontPixelSize() && !fontSpec
.HasFontPointSize())
12057 wxString spec
= wxString::Format(wxT("%d-%s-%d-%d-%d-%d-%s-%d"),
12058 fontSize
, units
.c_str(), fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), (int) fontSpec
.GetFontUnderlined(), (int) fontSpec
.GetFontStrikethrough(),
12059 facename
.c_str(), (int) fontSpec
.GetFontEncoding());
12061 wxRichTextFontTableHashMap::iterator entry
= m_hashMap
.find(spec
);
12062 if ( entry
== m_hashMap
.end() )
12064 if (fontSpec
.HasFontPixelSize() && !fontSpec
.HasFontPointSize())
12066 wxFont
font(wxSize(0, fontSize
), wxFONTFAMILY_DEFAULT
, fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), fontSpec
.GetFontUnderlined(), facename
);
12067 if (fontSpec
.HasFontStrikethrough() && fontSpec
.GetFontStrikethrough())
12068 font
.SetStrikethrough(true);
12069 m_hashMap
[spec
] = font
;
12074 wxFont
font(fontSize
, wxFONTFAMILY_DEFAULT
, fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), fontSpec
.GetFontUnderlined(), facename
.c_str());
12075 if (fontSpec
.HasFontStrikethrough() && fontSpec
.GetFontStrikethrough())
12076 font
.SetStrikethrough(true);
12078 m_hashMap
[spec
] = font
;
12084 return entry
->second
;
12088 IMPLEMENT_DYNAMIC_CLASS(wxRichTextFontTable
, wxObject
)
12090 wxRichTextFontTable::wxRichTextFontTable()
12092 m_refData
= new wxRichTextFontTableData
;
12096 wxRichTextFontTable::wxRichTextFontTable(const wxRichTextFontTable
& table
)
12102 wxRichTextFontTable::~wxRichTextFontTable()
12107 bool wxRichTextFontTable::operator == (const wxRichTextFontTable
& table
) const
12109 return (m_refData
== table
.m_refData
);
12112 void wxRichTextFontTable::operator= (const wxRichTextFontTable
& table
)
12115 m_fontScale
= table
.m_fontScale
;
12118 wxFont
wxRichTextFontTable::FindFont(const wxRichTextAttr
& fontSpec
)
12120 wxRichTextFontTableData
* data
= (wxRichTextFontTableData
*) m_refData
;
12122 return data
->FindFont(fontSpec
, m_fontScale
);
12127 void wxRichTextFontTable::Clear()
12129 wxRichTextFontTableData
* data
= (wxRichTextFontTableData
*) m_refData
;
12131 data
->m_hashMap
.clear();
12134 void wxRichTextFontTable::SetFontScale(double fontScale
)
12136 if (fontScale
!= m_fontScale
)
12138 m_fontScale
= fontScale
;
12143 void wxTextBoxAttr::Reset()
12146 m_floatMode
= wxTEXT_BOX_ATTR_FLOAT_NONE
;
12147 m_clearMode
= wxTEXT_BOX_ATTR_CLEAR_NONE
;
12148 m_collapseMode
= wxTEXT_BOX_ATTR_COLLAPSE_NONE
;
12149 m_verticalAlignment
= wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_NONE
;
12150 m_boxStyleName
= wxEmptyString
;
12154 m_position
.Reset();
12165 bool wxTextBoxAttr::operator== (const wxTextBoxAttr
& attr
) const
12168 m_flags
== attr
.m_flags
&&
12169 m_floatMode
== attr
.m_floatMode
&&
12170 m_clearMode
== attr
.m_clearMode
&&
12171 m_collapseMode
== attr
.m_collapseMode
&&
12172 m_verticalAlignment
== attr
.m_verticalAlignment
&&
12174 m_margins
== attr
.m_margins
&&
12175 m_padding
== attr
.m_padding
&&
12176 m_position
== attr
.m_position
&&
12178 m_size
== attr
.m_size
&&
12179 m_minSize
== attr
.m_minSize
&&
12180 m_maxSize
== attr
.m_maxSize
&&
12182 m_border
== attr
.m_border
&&
12183 m_outline
== attr
.m_outline
&&
12185 m_boxStyleName
== attr
.m_boxStyleName
12189 // Partial equality test
12190 bool wxTextBoxAttr::EqPartial(const wxTextBoxAttr
& attr
, bool weakTest
) const
12193 ((!HasFloatMode() && attr
.HasFloatMode()) ||
12194 (!HasClearMode() && attr
.HasClearMode()) ||
12195 (!HasCollapseBorders() && attr
.HasCollapseBorders()) ||
12196 (!HasVerticalAlignment() && attr
.HasVerticalAlignment()) ||
12197 (!HasBoxStyleName() && attr
.HasBoxStyleName())))
12201 if (attr
.HasFloatMode() && HasFloatMode() && (GetFloatMode() != attr
.GetFloatMode()))
12204 if (attr
.HasClearMode() && HasClearMode() && (GetClearMode() != attr
.GetClearMode()))
12207 if (attr
.HasCollapseBorders() && HasCollapseBorders() && (attr
.GetCollapseBorders() != GetCollapseBorders()))
12210 if (attr
.HasVerticalAlignment() && HasVerticalAlignment() && (attr
.GetVerticalAlignment() != GetVerticalAlignment()))
12213 if (attr
.HasBoxStyleName() && HasBoxStyleName() && (attr
.GetBoxStyleName() != GetBoxStyleName()))
12218 if (!m_position
.EqPartial(attr
.m_position
, weakTest
))
12223 if (!m_size
.EqPartial(attr
.m_size
, weakTest
))
12225 if (!m_minSize
.EqPartial(attr
.m_minSize
, weakTest
))
12227 if (!m_maxSize
.EqPartial(attr
.m_maxSize
, weakTest
))
12232 if (!m_margins
.EqPartial(attr
.m_margins
, weakTest
))
12237 if (!m_padding
.EqPartial(attr
.m_padding
, weakTest
))
12242 if (!GetBorder().EqPartial(attr
.GetBorder(), weakTest
))
12247 if (!GetOutline().EqPartial(attr
.GetOutline(), weakTest
))
12253 // Merges the given attributes. If compareWith
12254 // is non-NULL, then it will be used to mask out those attributes that are the same in style
12255 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
12256 bool wxTextBoxAttr::Apply(const wxTextBoxAttr
& attr
, const wxTextBoxAttr
* compareWith
)
12258 if (attr
.HasFloatMode())
12260 if (!(compareWith
&& compareWith
->HasFloatMode() && compareWith
->GetFloatMode() == attr
.GetFloatMode()))
12261 SetFloatMode(attr
.GetFloatMode());
12264 if (attr
.HasClearMode())
12266 if (!(compareWith
&& compareWith
->HasClearMode() && compareWith
->GetClearMode() == attr
.GetClearMode()))
12267 SetClearMode(attr
.GetClearMode());
12270 if (attr
.HasCollapseBorders())
12272 if (!(compareWith
&& compareWith
->HasCollapseBorders() && compareWith
->GetCollapseBorders() == attr
.GetCollapseBorders()))
12273 SetCollapseBorders(attr
.GetCollapseBorders());
12276 if (attr
.HasVerticalAlignment())
12278 if (!(compareWith
&& compareWith
->HasVerticalAlignment() && compareWith
->GetVerticalAlignment() == attr
.GetVerticalAlignment()))
12279 SetVerticalAlignment(attr
.GetVerticalAlignment());
12282 if (attr
.HasBoxStyleName())
12284 if (!(compareWith
&& compareWith
->HasBoxStyleName() && compareWith
->GetBoxStyleName() == attr
.GetBoxStyleName()))
12285 SetBoxStyleName(attr
.GetBoxStyleName());
12288 m_margins
.Apply(attr
.m_margins
, compareWith
? (& attr
.m_margins
) : (const wxTextAttrDimensions
*) NULL
);
12289 m_padding
.Apply(attr
.m_padding
, compareWith
? (& attr
.m_padding
) : (const wxTextAttrDimensions
*) NULL
);
12290 m_position
.Apply(attr
.m_position
, compareWith
? (& attr
.m_position
) : (const wxTextAttrDimensions
*) NULL
);
12292 m_size
.Apply(attr
.m_size
, compareWith
? (& attr
.m_size
) : (const wxTextAttrSize
*) NULL
);
12293 m_minSize
.Apply(attr
.m_minSize
, compareWith
? (& attr
.m_minSize
) : (const wxTextAttrSize
*) NULL
);
12294 m_maxSize
.Apply(attr
.m_maxSize
, compareWith
? (& attr
.m_maxSize
) : (const wxTextAttrSize
*) NULL
);
12296 m_border
.Apply(attr
.m_border
, compareWith
? (& attr
.m_border
) : (const wxTextAttrBorders
*) NULL
);
12297 m_outline
.Apply(attr
.m_outline
, compareWith
? (& attr
.m_outline
) : (const wxTextAttrBorders
*) NULL
);
12302 // Remove specified attributes from this object
12303 bool wxTextBoxAttr::RemoveStyle(const wxTextBoxAttr
& attr
)
12305 if (attr
.HasFloatMode())
12306 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT
);
12308 if (attr
.HasClearMode())
12309 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR
);
12311 if (attr
.HasCollapseBorders())
12312 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
12314 if (attr
.HasVerticalAlignment())
12315 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
12317 if (attr
.HasBoxStyleName())
12319 SetBoxStyleName(wxEmptyString
);
12320 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
12323 m_margins
.RemoveStyle(attr
.m_margins
);
12324 m_padding
.RemoveStyle(attr
.m_padding
);
12325 m_position
.RemoveStyle(attr
.m_position
);
12327 m_size
.RemoveStyle(attr
.m_size
);
12328 m_minSize
.RemoveStyle(attr
.m_minSize
);
12329 m_maxSize
.RemoveStyle(attr
.m_maxSize
);
12331 m_border
.RemoveStyle(attr
.m_border
);
12332 m_outline
.RemoveStyle(attr
.m_outline
);
12337 // Collects the attributes that are common to a range of content, building up a note of
12338 // which attributes are absent in some objects and which clash in some objects.
12339 void wxTextBoxAttr::CollectCommonAttributes(const wxTextBoxAttr
& attr
, wxTextBoxAttr
& clashingAttr
, wxTextBoxAttr
& absentAttr
)
12341 if (attr
.HasFloatMode())
12343 if (!clashingAttr
.HasFloatMode() && !absentAttr
.HasFloatMode())
12345 if (HasFloatMode())
12347 if (GetFloatMode() != attr
.GetFloatMode())
12349 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_FLOAT
);
12350 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT
);
12354 SetFloatMode(attr
.GetFloatMode());
12358 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_FLOAT
);
12360 if (attr
.HasClearMode())
12362 if (!clashingAttr
.HasClearMode() && !absentAttr
.HasClearMode())
12364 if (HasClearMode())
12366 if (GetClearMode() != attr
.GetClearMode())
12368 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_CLEAR
);
12369 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR
);
12373 SetClearMode(attr
.GetClearMode());
12377 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_CLEAR
);
12379 if (attr
.HasCollapseBorders())
12381 if (!clashingAttr
.HasCollapseBorders() && !absentAttr
.HasCollapseBorders())
12383 if (HasCollapseBorders())
12385 if (GetCollapseBorders() != attr
.GetCollapseBorders())
12387 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
12388 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
12392 SetCollapseBorders(attr
.GetCollapseBorders());
12396 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
12398 if (attr
.HasVerticalAlignment())
12400 if (!clashingAttr
.HasVerticalAlignment() && !absentAttr
.HasVerticalAlignment())
12402 if (HasVerticalAlignment())
12404 if (GetVerticalAlignment() != attr
.GetVerticalAlignment())
12406 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
12407 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
12411 SetVerticalAlignment(attr
.GetVerticalAlignment());
12415 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
12417 if (attr
.HasBoxStyleName())
12419 if (!clashingAttr
.HasBoxStyleName() && !absentAttr
.HasBoxStyleName())
12421 if (HasBoxStyleName())
12423 if (GetBoxStyleName() != attr
.GetBoxStyleName())
12425 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
12426 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
12430 SetBoxStyleName(attr
.GetBoxStyleName());
12434 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
12436 m_margins
.CollectCommonAttributes(attr
.m_margins
, clashingAttr
.m_margins
, absentAttr
.m_margins
);
12437 m_padding
.CollectCommonAttributes(attr
.m_padding
, clashingAttr
.m_padding
, absentAttr
.m_padding
);
12438 m_position
.CollectCommonAttributes(attr
.m_position
, clashingAttr
.m_position
, absentAttr
.m_position
);
12440 m_size
.CollectCommonAttributes(attr
.m_size
, clashingAttr
.m_size
, absentAttr
.m_size
);
12441 m_minSize
.CollectCommonAttributes(attr
.m_minSize
, clashingAttr
.m_minSize
, absentAttr
.m_minSize
);
12442 m_maxSize
.CollectCommonAttributes(attr
.m_maxSize
, clashingAttr
.m_maxSize
, absentAttr
.m_maxSize
);
12444 m_border
.CollectCommonAttributes(attr
.m_border
, clashingAttr
.m_border
, absentAttr
.m_border
);
12445 m_outline
.CollectCommonAttributes(attr
.m_outline
, clashingAttr
.m_outline
, absentAttr
.m_outline
);
12448 bool wxTextBoxAttr::IsDefault() const
12450 return GetFlags() == 0 && !m_border
.IsValid() && !m_outline
.IsValid() &&
12451 !m_size
.IsValid() && !m_minSize
.IsValid() && !m_maxSize
.IsValid() &&
12452 !m_position
.IsValid() && !m_padding
.IsValid() && !m_margins
.IsValid();
12457 void wxRichTextAttr::Copy(const wxRichTextAttr
& attr
)
12459 wxTextAttr::Copy(attr
);
12461 m_textBoxAttr
= attr
.m_textBoxAttr
;
12464 bool wxRichTextAttr::operator==(const wxRichTextAttr
& attr
) const
12466 if (!(wxTextAttr::operator==(attr
)))
12469 return (m_textBoxAttr
== attr
.m_textBoxAttr
);
12472 // Partial equality test
12473 bool wxRichTextAttr::EqPartial(const wxRichTextAttr
& attr
, bool weakTest
) const
12475 if (!(wxTextAttr::EqPartial(attr
, weakTest
)))
12478 return m_textBoxAttr
.EqPartial(attr
.m_textBoxAttr
, weakTest
);
12481 // Merges the given attributes. If compareWith
12482 // is non-NULL, then it will be used to mask out those attributes that are the same in style
12483 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
12484 bool wxRichTextAttr::Apply(const wxRichTextAttr
& style
, const wxRichTextAttr
* compareWith
)
12486 wxTextAttr::Apply(style
, compareWith
);
12488 return m_textBoxAttr
.Apply(style
.m_textBoxAttr
, compareWith
? (& compareWith
->m_textBoxAttr
) : (const wxTextBoxAttr
*) NULL
);
12491 // Remove specified attributes from this object
12492 bool wxRichTextAttr::RemoveStyle(const wxRichTextAttr
& attr
)
12494 wxTextAttr::RemoveStyle(*this, attr
);
12496 return m_textBoxAttr
.RemoveStyle(attr
.m_textBoxAttr
);
12499 // Collects the attributes that are common to a range of content, building up a note of
12500 // which attributes are absent in some objects and which clash in some objects.
12501 void wxRichTextAttr::CollectCommonAttributes(const wxRichTextAttr
& attr
, wxRichTextAttr
& clashingAttr
, wxRichTextAttr
& absentAttr
)
12503 wxTextAttrCollectCommonAttributes(*this, attr
, clashingAttr
, absentAttr
);
12505 m_textBoxAttr
.CollectCommonAttributes(attr
.m_textBoxAttr
, clashingAttr
.m_textBoxAttr
, absentAttr
.m_textBoxAttr
);
12508 // Partial equality test
12509 bool wxTextAttrBorder::EqPartial(const wxTextAttrBorder
& border
, bool weakTest
) const
12512 ((!HasStyle() && border
.HasStyle()) ||
12513 (!HasColour() && border
.HasColour()) ||
12514 (!HasWidth() && border
.HasWidth())))
12519 if (border
.HasStyle() && HasStyle() && (border
.GetStyle() != GetStyle()))
12522 if (border
.HasColour() && HasColour() && (border
.GetColourLong() != GetColourLong()))
12525 if (border
.HasWidth() && HasWidth() && !(border
.GetWidth() == GetWidth()))
12531 // Apply border to 'this', but not if the same as compareWith
12532 bool wxTextAttrBorder::Apply(const wxTextAttrBorder
& border
, const wxTextAttrBorder
* compareWith
)
12534 if (border
.HasStyle())
12536 if (!(compareWith
&& (border
.GetStyle() == compareWith
->GetStyle())))
12537 SetStyle(border
.GetStyle());
12539 if (border
.HasColour())
12541 if (!(compareWith
&& (border
.GetColourLong() == compareWith
->GetColourLong())))
12542 SetColour(border
.GetColourLong());
12544 if (border
.HasWidth())
12546 if (!(compareWith
&& (border
.GetWidth() == compareWith
->GetWidth())))
12547 SetWidth(border
.GetWidth());
12553 // Remove specified attributes from this object
12554 bool wxTextAttrBorder::RemoveStyle(const wxTextAttrBorder
& attr
)
12556 if (attr
.HasStyle() && HasStyle())
12557 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_STYLE
);
12558 if (attr
.HasColour() && HasColour())
12559 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_COLOUR
);
12560 if (attr
.HasWidth() && HasWidth())
12561 m_borderWidth
.Reset();
12566 // Collects the attributes that are common to a range of content, building up a note of
12567 // which attributes are absent in some objects and which clash in some objects.
12568 void wxTextAttrBorder::CollectCommonAttributes(const wxTextAttrBorder
& attr
, wxTextAttrBorder
& clashingAttr
, wxTextAttrBorder
& absentAttr
)
12570 if (attr
.HasStyle())
12572 if (!clashingAttr
.HasStyle() && !absentAttr
.HasStyle())
12576 if (GetStyle() != attr
.GetStyle())
12578 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
12579 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
12583 SetStyle(attr
.GetStyle());
12587 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
12589 if (attr
.HasColour())
12591 if (!clashingAttr
.HasColour() && !absentAttr
.HasColour())
12595 if (GetColour() != attr
.GetColour())
12597 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
12598 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
12602 SetColour(attr
.GetColourLong());
12606 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
12608 m_borderWidth
.CollectCommonAttributes(attr
.m_borderWidth
, clashingAttr
.m_borderWidth
, absentAttr
.m_borderWidth
);
12611 // Partial equality test
12612 bool wxTextAttrBorders::EqPartial(const wxTextAttrBorders
& borders
, bool weakTest
) const
12614 return m_left
.EqPartial(borders
.m_left
, weakTest
) && m_right
.EqPartial(borders
.m_right
, weakTest
) &&
12615 m_top
.EqPartial(borders
.m_top
, weakTest
) && m_bottom
.EqPartial(borders
.m_bottom
, weakTest
);
12618 // Apply border to 'this', but not if the same as compareWith
12619 bool wxTextAttrBorders::Apply(const wxTextAttrBorders
& borders
, const wxTextAttrBorders
* compareWith
)
12621 m_left
.Apply(borders
.m_left
, compareWith
? (& compareWith
->m_left
) : (const wxTextAttrBorder
*) NULL
);
12622 m_right
.Apply(borders
.m_right
, compareWith
? (& compareWith
->m_right
) : (const wxTextAttrBorder
*) NULL
);
12623 m_top
.Apply(borders
.m_top
, compareWith
? (& compareWith
->m_top
) : (const wxTextAttrBorder
*) NULL
);
12624 m_bottom
.Apply(borders
.m_bottom
, compareWith
? (& compareWith
->m_bottom
) : (const wxTextAttrBorder
*) NULL
);
12628 // Remove specified attributes from this object
12629 bool wxTextAttrBorders::RemoveStyle(const wxTextAttrBorders
& attr
)
12631 m_left
.RemoveStyle(attr
.m_left
);
12632 m_right
.RemoveStyle(attr
.m_right
);
12633 m_top
.RemoveStyle(attr
.m_top
);
12634 m_bottom
.RemoveStyle(attr
.m_bottom
);
12638 // Collects the attributes that are common to a range of content, building up a note of
12639 // which attributes are absent in some objects and which clash in some objects.
12640 void wxTextAttrBorders::CollectCommonAttributes(const wxTextAttrBorders
& attr
, wxTextAttrBorders
& clashingAttr
, wxTextAttrBorders
& absentAttr
)
12642 m_left
.CollectCommonAttributes(attr
.m_left
, clashingAttr
.m_left
, absentAttr
.m_left
);
12643 m_right
.CollectCommonAttributes(attr
.m_right
, clashingAttr
.m_right
, absentAttr
.m_right
);
12644 m_top
.CollectCommonAttributes(attr
.m_top
, clashingAttr
.m_top
, absentAttr
.m_top
);
12645 m_bottom
.CollectCommonAttributes(attr
.m_bottom
, clashingAttr
.m_bottom
, absentAttr
.m_bottom
);
12648 // Set style of all borders
12649 void wxTextAttrBorders::SetStyle(int style
)
12651 m_left
.SetStyle(style
);
12652 m_right
.SetStyle(style
);
12653 m_top
.SetStyle(style
);
12654 m_bottom
.SetStyle(style
);
12657 // Set colour of all borders
12658 void wxTextAttrBorders::SetColour(unsigned long colour
)
12660 m_left
.SetColour(colour
);
12661 m_right
.SetColour(colour
);
12662 m_top
.SetColour(colour
);
12663 m_bottom
.SetColour(colour
);
12666 void wxTextAttrBorders::SetColour(const wxColour
& colour
)
12668 m_left
.SetColour(colour
);
12669 m_right
.SetColour(colour
);
12670 m_top
.SetColour(colour
);
12671 m_bottom
.SetColour(colour
);
12674 // Set width of all borders
12675 void wxTextAttrBorders::SetWidth(const wxTextAttrDimension
& width
)
12677 m_left
.SetWidth(width
);
12678 m_right
.SetWidth(width
);
12679 m_top
.SetWidth(width
);
12680 m_bottom
.SetWidth(width
);
12683 // Partial equality test
12684 bool wxTextAttrDimension::EqPartial(const wxTextAttrDimension
& dim
, bool weakTest
) const
12686 if (!weakTest
&& !IsValid() && dim
.IsValid())
12689 if (dim
.IsValid() && IsValid() && !((*this) == dim
))
12695 bool wxTextAttrDimension::Apply(const wxTextAttrDimension
& dim
, const wxTextAttrDimension
* compareWith
)
12699 if (!(compareWith
&& dim
== (*compareWith
)))
12706 // Collects the attributes that are common to a range of content, building up a note of
12707 // which attributes are absent in some objects and which clash in some objects.
12708 void wxTextAttrDimension::CollectCommonAttributes(const wxTextAttrDimension
& attr
, wxTextAttrDimension
& clashingAttr
, wxTextAttrDimension
& absentAttr
)
12710 if (attr
.IsValid())
12712 if (!clashingAttr
.IsValid() && !absentAttr
.IsValid())
12716 if (!((*this) == attr
))
12718 clashingAttr
.SetValid(true);
12727 absentAttr
.SetValid(true);
12730 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(wxDC
& dc
, double scale
, const wxSize
& parentSize
)
12732 m_ppi
= dc
.GetPPI().x
; m_scale
= scale
; m_parentSize
= parentSize
;
12735 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(int ppi
, double scale
, const wxSize
& parentSize
)
12737 m_ppi
= ppi
; m_scale
= scale
; m_parentSize
= parentSize
;
12740 int wxTextAttrDimensionConverter::ConvertTenthsMMToPixels(int units
) const
12742 return wxRichTextObject::ConvertTenthsMMToPixels(m_ppi
, units
, m_scale
);
12745 int wxTextAttrDimensionConverter::ConvertPixelsToTenthsMM(int pixels
) const
12747 return wxRichTextObject::ConvertPixelsToTenthsMM(m_ppi
, pixels
, m_scale
);
12750 int wxTextAttrDimensionConverter::GetPixels(const wxTextAttrDimension
& dim
, int direction
) const
12752 if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
12753 return ConvertTenthsMMToPixels(dim
.GetValue());
12754 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
12755 return dim
.GetValue();
12756 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
12758 wxASSERT(m_parentSize
!= wxDefaultSize
);
12759 if (direction
== wxHORIZONTAL
)
12760 return (int) (double(m_parentSize
.x
) * double(dim
.GetValue()) / 100.0);
12762 return (int) (double(m_parentSize
.y
) * double(dim
.GetValue()) / 100.0);
12771 int wxTextAttrDimensionConverter::GetTenthsMM(const wxTextAttrDimension
& dim
) const
12773 if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
12774 return dim
.GetValue();
12775 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
12776 return ConvertPixelsToTenthsMM(dim
.GetValue());
12784 // Partial equality test
12785 bool wxTextAttrDimensions::EqPartial(const wxTextAttrDimensions
& dims
, bool weakTest
) const
12787 if (!m_left
.EqPartial(dims
.m_left
, weakTest
))
12790 if (!m_right
.EqPartial(dims
.m_right
, weakTest
))
12793 if (!m_top
.EqPartial(dims
.m_top
, weakTest
))
12796 if (!m_bottom
.EqPartial(dims
.m_bottom
, weakTest
))
12802 // Apply border to 'this', but not if the same as compareWith
12803 bool wxTextAttrDimensions::Apply(const wxTextAttrDimensions
& dims
, const wxTextAttrDimensions
* compareWith
)
12805 m_left
.Apply(dims
.m_left
, compareWith
? (& compareWith
->m_left
) : (const wxTextAttrDimension
*) NULL
);
12806 m_right
.Apply(dims
.m_right
, compareWith
? (& compareWith
->m_right
): (const wxTextAttrDimension
*) NULL
);
12807 m_top
.Apply(dims
.m_top
, compareWith
? (& compareWith
->m_top
): (const wxTextAttrDimension
*) NULL
);
12808 m_bottom
.Apply(dims
.m_bottom
, compareWith
? (& compareWith
->m_bottom
): (const wxTextAttrDimension
*) NULL
);
12813 // Remove specified attributes from this object
12814 bool wxTextAttrDimensions::RemoveStyle(const wxTextAttrDimensions
& attr
)
12816 if (attr
.m_left
.IsValid())
12818 if (attr
.m_right
.IsValid())
12820 if (attr
.m_top
.IsValid())
12822 if (attr
.m_bottom
.IsValid())
12828 // Collects the attributes that are common to a range of content, building up a note of
12829 // which attributes are absent in some objects and which clash in some objects.
12830 void wxTextAttrDimensions::CollectCommonAttributes(const wxTextAttrDimensions
& attr
, wxTextAttrDimensions
& clashingAttr
, wxTextAttrDimensions
& absentAttr
)
12832 m_left
.CollectCommonAttributes(attr
.m_left
, clashingAttr
.m_left
, absentAttr
.m_left
);
12833 m_right
.CollectCommonAttributes(attr
.m_right
, clashingAttr
.m_right
, absentAttr
.m_right
);
12834 m_top
.CollectCommonAttributes(attr
.m_top
, clashingAttr
.m_top
, absentAttr
.m_top
);
12835 m_bottom
.CollectCommonAttributes(attr
.m_bottom
, clashingAttr
.m_bottom
, absentAttr
.m_bottom
);
12838 // Partial equality test
12839 bool wxTextAttrSize::EqPartial(const wxTextAttrSize
& size
, bool weakTest
) const
12841 if (!m_width
.EqPartial(size
.m_width
, weakTest
))
12844 if (!m_height
.EqPartial(size
.m_height
, weakTest
))
12850 // Apply border to 'this', but not if the same as compareWith
12851 bool wxTextAttrSize::Apply(const wxTextAttrSize
& size
, const wxTextAttrSize
* compareWith
)
12853 m_width
.Apply(size
.m_width
, compareWith
? (& compareWith
->m_width
) : (const wxTextAttrDimension
*) NULL
);
12854 m_height
.Apply(size
.m_height
, compareWith
? (& compareWith
->m_height
): (const wxTextAttrDimension
*) NULL
);
12859 // Remove specified attributes from this object
12860 bool wxTextAttrSize::RemoveStyle(const wxTextAttrSize
& attr
)
12862 if (attr
.m_width
.IsValid())
12864 if (attr
.m_height
.IsValid())
12870 // Collects the attributes that are common to a range of content, building up a note of
12871 // which attributes are absent in some objects and which clash in some objects.
12872 void wxTextAttrSize::CollectCommonAttributes(const wxTextAttrSize
& attr
, wxTextAttrSize
& clashingAttr
, wxTextAttrSize
& absentAttr
)
12874 m_width
.CollectCommonAttributes(attr
.m_width
, clashingAttr
.m_width
, absentAttr
.m_width
);
12875 m_height
.CollectCommonAttributes(attr
.m_height
, clashingAttr
.m_height
, absentAttr
.m_height
);
12878 // Collects the attributes that are common to a range of content, building up a note of
12879 // which attributes are absent in some objects and which clash in some objects.
12880 void wxTextAttrCollectCommonAttributes(wxTextAttr
& currentStyle
, const wxTextAttr
& attr
, wxTextAttr
& clashingAttr
, wxTextAttr
& absentAttr
)
12882 absentAttr
.SetFlags(absentAttr
.GetFlags() | (~attr
.GetFlags() & wxTEXT_ATTR_ALL
));
12883 absentAttr
.SetTextEffectFlags(absentAttr
.GetTextEffectFlags() | (~attr
.GetTextEffectFlags() & 0xFFFF));
12885 long forbiddenFlags
= clashingAttr
.GetFlags()|absentAttr
.GetFlags();
12887 // If different font size units are being used, this is a clash.
12888 if (((attr
.GetFlags() & wxTEXT_ATTR_FONT_SIZE
) | (currentStyle
.GetFlags() & wxTEXT_ATTR_FONT_SIZE
)) == wxTEXT_ATTR_FONT_SIZE
)
12890 currentStyle
.SetFontSize(0);
12891 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_SIZE
);
12892 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_SIZE
);
12896 if (attr
.HasFontPointSize() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_POINT_SIZE
))
12898 if (currentStyle
.HasFontPointSize())
12900 if (currentStyle
.GetFontSize() != attr
.GetFontSize())
12902 // Clash of attr - mark as such
12903 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE
);
12904 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE
);
12908 currentStyle
.SetFontSize(attr
.GetFontSize());
12910 else if (!attr
.HasFontPointSize() && currentStyle
.HasFontPointSize())
12912 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE
);
12913 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE
);
12916 if (attr
.HasFontPixelSize() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_PIXEL_SIZE
))
12918 if (currentStyle
.HasFontPixelSize())
12920 if (currentStyle
.GetFontSize() != attr
.GetFontSize())
12922 // Clash of attr - mark as such
12923 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE
);
12924 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE
);
12928 currentStyle
.SetFontPixelSize(attr
.GetFontSize());
12930 else if (!attr
.HasFontPixelSize() && currentStyle
.HasFontPixelSize())
12932 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE
);
12933 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE
);
12937 if (attr
.HasFontItalic() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_ITALIC
))
12939 if (currentStyle
.HasFontItalic())
12941 if (currentStyle
.GetFontStyle() != attr
.GetFontStyle())
12943 // Clash of attr - mark as such
12944 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_ITALIC
);
12945 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC
);
12949 currentStyle
.SetFontStyle(attr
.GetFontStyle());
12951 else if (!attr
.HasFontItalic() && currentStyle
.HasFontItalic())
12953 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_ITALIC
);
12954 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC
);
12957 if (attr
.HasFontFamily() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_FAMILY
))
12959 if (currentStyle
.HasFontFamily())
12961 if (currentStyle
.GetFontFamily() != attr
.GetFontFamily())
12963 // Clash of attr - mark as such
12964 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FAMILY
);
12965 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY
);
12969 currentStyle
.SetFontFamily(attr
.GetFontFamily());
12971 else if (!attr
.HasFontFamily() && currentStyle
.HasFontFamily())
12973 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FAMILY
);
12974 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY
);
12977 if (attr
.HasFontWeight() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_WEIGHT
))
12979 if (currentStyle
.HasFontWeight())
12981 if (currentStyle
.GetFontWeight() != attr
.GetFontWeight())
12983 // Clash of attr - mark as such
12984 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_WEIGHT
);
12985 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT
);
12989 currentStyle
.SetFontWeight(attr
.GetFontWeight());
12991 else if (!attr
.HasFontWeight() && currentStyle
.HasFontWeight())
12993 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_WEIGHT
);
12994 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT
);
12997 if (attr
.HasFontFaceName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_FACE
))
12999 if (currentStyle
.HasFontFaceName())
13001 wxString
faceName1(currentStyle
.GetFontFaceName());
13002 wxString
faceName2(attr
.GetFontFaceName());
13004 if (faceName1
!= faceName2
)
13006 // Clash of attr - mark as such
13007 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FACE
);
13008 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FACE
);
13012 currentStyle
.SetFontFaceName(attr
.GetFontFaceName());
13014 else if (!attr
.HasFontFaceName() && currentStyle
.HasFontFaceName())
13016 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FACE
);
13017 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FACE
);
13020 if (attr
.HasFontUnderlined() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_UNDERLINE
))
13022 if (currentStyle
.HasFontUnderlined())
13024 if (currentStyle
.GetFontUnderlined() != attr
.GetFontUnderlined())
13026 // Clash of attr - mark as such
13027 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
13028 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
13032 currentStyle
.SetFontUnderlined(attr
.GetFontUnderlined());
13034 else if (!attr
.HasFontUnderlined() && currentStyle
.HasFontUnderlined())
13036 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
13037 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
13040 if (attr
.HasFontStrikethrough() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_STRIKETHROUGH
))
13042 if (currentStyle
.HasFontStrikethrough())
13044 if (currentStyle
.GetFontStrikethrough() != attr
.GetFontStrikethrough())
13046 // Clash of attr - mark as such
13047 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH
);
13048 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH
);
13052 currentStyle
.SetFontStrikethrough(attr
.GetFontStrikethrough());
13054 else if (!attr
.HasFontStrikethrough() && currentStyle
.HasFontStrikethrough())
13056 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH
);
13057 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH
);
13060 if (attr
.HasTextColour() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_TEXT_COLOUR
))
13062 if (currentStyle
.HasTextColour())
13064 if (currentStyle
.GetTextColour() != attr
.GetTextColour())
13066 // Clash of attr - mark as such
13067 clashingAttr
.AddFlag(wxTEXT_ATTR_TEXT_COLOUR
);
13068 currentStyle
.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR
);
13072 currentStyle
.SetTextColour(attr
.GetTextColour());
13074 else if (!attr
.HasTextColour() && currentStyle
.HasTextColour())
13076 clashingAttr
.AddFlag(wxTEXT_ATTR_TEXT_COLOUR
);
13077 currentStyle
.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR
);
13080 if (attr
.HasBackgroundColour() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BACKGROUND_COLOUR
))
13082 if (currentStyle
.HasBackgroundColour())
13084 if (currentStyle
.GetBackgroundColour() != attr
.GetBackgroundColour())
13086 // Clash of attr - mark as such
13087 clashingAttr
.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
13088 currentStyle
.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
13092 currentStyle
.SetBackgroundColour(attr
.GetBackgroundColour());
13094 else if (!attr
.HasBackgroundColour() && currentStyle
.HasBackgroundColour())
13096 clashingAttr
.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
13097 currentStyle
.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
13100 if (attr
.HasAlignment() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_ALIGNMENT
))
13102 if (currentStyle
.HasAlignment())
13104 if (currentStyle
.GetAlignment() != attr
.GetAlignment())
13106 // Clash of attr - mark as such
13107 clashingAttr
.AddFlag(wxTEXT_ATTR_ALIGNMENT
);
13108 currentStyle
.RemoveFlag(wxTEXT_ATTR_ALIGNMENT
);
13112 currentStyle
.SetAlignment(attr
.GetAlignment());
13114 else if (!attr
.HasAlignment() && currentStyle
.HasAlignment())
13116 clashingAttr
.AddFlag(wxTEXT_ATTR_ALIGNMENT
);
13117 currentStyle
.RemoveFlag(wxTEXT_ATTR_ALIGNMENT
);
13120 if (attr
.HasTabs() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_TABS
))
13122 if (currentStyle
.HasTabs())
13124 if (!wxRichTextTabsEq(currentStyle
.GetTabs(), attr
.GetTabs()))
13126 // Clash of attr - mark as such
13127 clashingAttr
.AddFlag(wxTEXT_ATTR_TABS
);
13128 currentStyle
.RemoveFlag(wxTEXT_ATTR_TABS
);
13132 currentStyle
.SetTabs(attr
.GetTabs());
13134 else if (!attr
.HasTabs() && currentStyle
.HasTabs())
13136 clashingAttr
.AddFlag(wxTEXT_ATTR_TABS
);
13137 currentStyle
.RemoveFlag(wxTEXT_ATTR_TABS
);
13140 if (attr
.HasLeftIndent() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LEFT_INDENT
))
13142 if (currentStyle
.HasLeftIndent())
13144 if (currentStyle
.GetLeftIndent() != attr
.GetLeftIndent() || currentStyle
.GetLeftSubIndent() != attr
.GetLeftSubIndent())
13146 // Clash of attr - mark as such
13147 clashingAttr
.AddFlag(wxTEXT_ATTR_LEFT_INDENT
);
13148 currentStyle
.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT
);
13152 currentStyle
.SetLeftIndent(attr
.GetLeftIndent(), attr
.GetLeftSubIndent());
13154 else if (!attr
.HasLeftIndent() && currentStyle
.HasLeftIndent())
13156 clashingAttr
.AddFlag(wxTEXT_ATTR_LEFT_INDENT
);
13157 currentStyle
.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT
);
13160 if (attr
.HasRightIndent() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_RIGHT_INDENT
))
13162 if (currentStyle
.HasRightIndent())
13164 if (currentStyle
.GetRightIndent() != attr
.GetRightIndent())
13166 // Clash of attr - mark as such
13167 clashingAttr
.AddFlag(wxTEXT_ATTR_RIGHT_INDENT
);
13168 currentStyle
.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT
);
13172 currentStyle
.SetRightIndent(attr
.GetRightIndent());
13174 else if (!attr
.HasRightIndent() && currentStyle
.HasRightIndent())
13176 clashingAttr
.AddFlag(wxTEXT_ATTR_RIGHT_INDENT
);
13177 currentStyle
.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT
);
13180 if (attr
.HasParagraphSpacingAfter() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARA_SPACING_AFTER
))
13182 if (currentStyle
.HasParagraphSpacingAfter())
13184 if (currentStyle
.GetParagraphSpacingAfter() != attr
.GetParagraphSpacingAfter())
13186 // Clash of attr - mark as such
13187 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
13188 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
13192 currentStyle
.SetParagraphSpacingAfter(attr
.GetParagraphSpacingAfter());
13194 else if (!attr
.HasParagraphSpacingAfter() && currentStyle
.HasParagraphSpacingAfter())
13196 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
13197 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
13200 if (attr
.HasParagraphSpacingBefore() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARA_SPACING_BEFORE
))
13202 if (currentStyle
.HasParagraphSpacingBefore())
13204 if (currentStyle
.GetParagraphSpacingBefore() != attr
.GetParagraphSpacingBefore())
13206 // Clash of attr - mark as such
13207 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
13208 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
13212 currentStyle
.SetParagraphSpacingBefore(attr
.GetParagraphSpacingBefore());
13214 else if (!attr
.HasParagraphSpacingBefore() && currentStyle
.HasParagraphSpacingBefore())
13216 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
13217 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
13220 if (attr
.HasLineSpacing() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LINE_SPACING
))
13222 if (currentStyle
.HasLineSpacing())
13224 if (currentStyle
.GetLineSpacing() != attr
.GetLineSpacing())
13226 // Clash of attr - mark as such
13227 clashingAttr
.AddFlag(wxTEXT_ATTR_LINE_SPACING
);
13228 currentStyle
.RemoveFlag(wxTEXT_ATTR_LINE_SPACING
);
13232 currentStyle
.SetLineSpacing(attr
.GetLineSpacing());
13234 else if (!attr
.HasLineSpacing() && currentStyle
.HasLineSpacing())
13236 clashingAttr
.AddFlag(wxTEXT_ATTR_LINE_SPACING
);
13237 currentStyle
.RemoveFlag(wxTEXT_ATTR_LINE_SPACING
);
13240 if (attr
.HasCharacterStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_CHARACTER_STYLE_NAME
))
13242 if (currentStyle
.HasCharacterStyleName())
13244 if (currentStyle
.GetCharacterStyleName() != attr
.GetCharacterStyleName())
13246 // Clash of attr - mark as such
13247 clashingAttr
.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
13248 currentStyle
.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
13252 currentStyle
.SetCharacterStyleName(attr
.GetCharacterStyleName());
13254 else if (!attr
.HasCharacterStyleName() && currentStyle
.HasCharacterStyleName())
13256 clashingAttr
.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
13257 currentStyle
.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
13260 if (attr
.HasParagraphStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
))
13262 if (currentStyle
.HasParagraphStyleName())
13264 if (currentStyle
.GetParagraphStyleName() != attr
.GetParagraphStyleName())
13266 // Clash of attr - mark as such
13267 clashingAttr
.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
13268 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
13272 currentStyle
.SetParagraphStyleName(attr
.GetParagraphStyleName());
13274 else if (!attr
.HasParagraphStyleName() && currentStyle
.HasParagraphStyleName())
13276 clashingAttr
.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
13277 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
13280 if (attr
.HasListStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LIST_STYLE_NAME
))
13282 if (currentStyle
.HasListStyleName())
13284 if (currentStyle
.GetListStyleName() != attr
.GetListStyleName())
13286 // Clash of attr - mark as such
13287 clashingAttr
.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
13288 currentStyle
.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
13292 currentStyle
.SetListStyleName(attr
.GetListStyleName());
13294 else if (!attr
.HasListStyleName() && currentStyle
.HasListStyleName())
13296 clashingAttr
.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
13297 currentStyle
.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
13300 if (attr
.HasBulletStyle() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_STYLE
))
13302 if (currentStyle
.HasBulletStyle())
13304 if (currentStyle
.GetBulletStyle() != attr
.GetBulletStyle())
13306 // Clash of attr - mark as such
13307 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_STYLE
);
13308 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE
);
13312 currentStyle
.SetBulletStyle(attr
.GetBulletStyle());
13314 else if (!attr
.HasBulletStyle() && currentStyle
.HasBulletStyle())
13316 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_STYLE
);
13317 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE
);
13320 if (attr
.HasBulletNumber() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_NUMBER
))
13322 if (currentStyle
.HasBulletNumber())
13324 if (currentStyle
.GetBulletNumber() != attr
.GetBulletNumber())
13326 // Clash of attr - mark as such
13327 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NUMBER
);
13328 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER
);
13332 currentStyle
.SetBulletNumber(attr
.GetBulletNumber());
13334 else if (!attr
.HasBulletNumber() && currentStyle
.HasBulletNumber())
13336 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NUMBER
);
13337 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER
);
13340 if (attr
.HasBulletText() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_TEXT
))
13342 if (currentStyle
.HasBulletText())
13344 if (currentStyle
.GetBulletText() != attr
.GetBulletText())
13346 // Clash of attr - mark as such
13347 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_TEXT
);
13348 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT
);
13353 currentStyle
.SetBulletText(attr
.GetBulletText());
13354 currentStyle
.SetBulletFont(attr
.GetBulletFont());
13357 else if (!attr
.HasBulletText() && currentStyle
.HasBulletText())
13359 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_TEXT
);
13360 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT
);
13363 if (attr
.HasBulletName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_NAME
))
13365 if (currentStyle
.HasBulletName())
13367 if (currentStyle
.GetBulletName() != attr
.GetBulletName())
13369 // Clash of attr - mark as such
13370 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NAME
);
13371 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NAME
);
13376 currentStyle
.SetBulletName(attr
.GetBulletName());
13379 else if (!attr
.HasBulletName() && currentStyle
.HasBulletName())
13381 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NAME
);
13382 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NAME
);
13385 if (attr
.HasURL() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_URL
))
13387 if (currentStyle
.HasURL())
13389 if (currentStyle
.GetURL() != attr
.GetURL())
13391 // Clash of attr - mark as such
13392 clashingAttr
.AddFlag(wxTEXT_ATTR_URL
);
13393 currentStyle
.RemoveFlag(wxTEXT_ATTR_URL
);
13398 currentStyle
.SetURL(attr
.GetURL());
13401 else if (!attr
.HasURL() && currentStyle
.HasURL())
13403 clashingAttr
.AddFlag(wxTEXT_ATTR_URL
);
13404 currentStyle
.RemoveFlag(wxTEXT_ATTR_URL
);
13407 if (attr
.HasTextEffects() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_EFFECTS
))
13409 if (currentStyle
.HasTextEffects())
13411 // We need to find the bits in the new attr that are different:
13412 // just look at those bits that are specified by the new attr.
13414 // We need to remove the bits and flags that are not common between current attr
13415 // and new attr. In so doing we need to take account of the styles absent from one or more of the
13416 // previous styles.
13418 int currentRelevantTextEffects
= currentStyle
.GetTextEffects() & attr
.GetTextEffectFlags();
13419 int newRelevantTextEffects
= attr
.GetTextEffects() & attr
.GetTextEffectFlags();
13421 if (currentRelevantTextEffects
!= newRelevantTextEffects
)
13423 // Find the text effects that were different, using XOR
13424 int differentEffects
= currentRelevantTextEffects
^ newRelevantTextEffects
;
13426 // Clash of attr - mark as such
13427 clashingAttr
.SetTextEffectFlags(clashingAttr
.GetTextEffectFlags() | differentEffects
);
13428 currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~differentEffects
);
13433 currentStyle
.SetTextEffects(attr
.GetTextEffects());
13434 currentStyle
.SetTextEffectFlags(attr
.GetTextEffectFlags());
13437 // Mask out the flags and values that cannot be common because they were absent in one or more objecrs
13438 // that we've looked at so far
13439 currentStyle
.SetTextEffects(currentStyle
.GetTextEffects() & ~absentAttr
.GetTextEffectFlags());
13440 currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~absentAttr
.GetTextEffectFlags());
13442 if (currentStyle
.GetTextEffectFlags() == 0)
13443 currentStyle
.RemoveFlag(wxTEXT_ATTR_EFFECTS
);
13445 else if (!attr
.HasTextEffects() && currentStyle
.HasTextEffects())
13447 clashingAttr
.AddFlag(wxTEXT_ATTR_EFFECTS
);
13448 currentStyle
.RemoveFlag(wxTEXT_ATTR_EFFECTS
);
13451 if (attr
.HasOutlineLevel() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_OUTLINE_LEVEL
))
13453 if (currentStyle
.HasOutlineLevel())
13455 if (currentStyle
.GetOutlineLevel() != attr
.GetOutlineLevel())
13457 // Clash of attr - mark as such
13458 clashingAttr
.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
13459 currentStyle
.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
13463 currentStyle
.SetOutlineLevel(attr
.GetOutlineLevel());
13465 else if (!attr
.HasOutlineLevel() && currentStyle
.HasOutlineLevel())
13467 clashingAttr
.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
13468 currentStyle
.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
13472 WX_DEFINE_OBJARRAY(wxRichTextVariantArray
);
13475 WX_DEFINE_OBJARRAY(wxRichTextAttrArray
);
13477 IMPLEMENT_DYNAMIC_CLASS(wxRichTextProperties
, wxObject
)
13479 bool wxRichTextProperties::operator==(const wxRichTextProperties
& props
) const
13481 if (m_properties
.GetCount() != props
.GetCount())
13485 for (i
= 0; i
< m_properties
.GetCount(); i
++)
13487 const wxVariant
& var1
= m_properties
[i
];
13488 int idx
= props
.Find(var1
.GetName());
13491 const wxVariant
& var2
= props
.m_properties
[idx
];
13492 if (!(var1
== var2
))
13499 wxArrayString
wxRichTextProperties::GetPropertyNames() const
13503 for (i
= 0; i
< m_properties
.GetCount(); i
++)
13505 arr
.Add(m_properties
[i
].GetName());
13510 int wxRichTextProperties::Find(const wxString
& name
) const
13513 for (i
= 0; i
< m_properties
.GetCount(); i
++)
13515 if (m_properties
[i
].GetName() == name
)
13521 bool wxRichTextProperties::Remove(const wxString
& name
)
13523 int idx
= Find(name
);
13526 m_properties
.RemoveAt(idx
);
13533 wxVariant
* wxRichTextProperties::FindOrCreateProperty(const wxString
& name
)
13535 int idx
= Find(name
);
13536 if (idx
== wxNOT_FOUND
)
13537 SetProperty(name
, wxString());
13539 if (idx
!= wxNOT_FOUND
)
13541 return & (*this)[idx
];
13547 const wxVariant
& wxRichTextProperties::GetProperty(const wxString
& name
) const
13549 static const wxVariant nullVariant
;
13550 int idx
= Find(name
);
13552 return m_properties
[idx
];
13554 return nullVariant
;
13557 wxString
wxRichTextProperties::GetPropertyString(const wxString
& name
) const
13559 return GetProperty(name
).GetString();
13562 long wxRichTextProperties::GetPropertyLong(const wxString
& name
) const
13564 return GetProperty(name
).GetLong();
13567 bool wxRichTextProperties::GetPropertyBool(const wxString
& name
) const
13569 return GetProperty(name
).GetBool();
13572 double wxRichTextProperties::GetPropertyDouble(const wxString
& name
) const
13574 return GetProperty(name
).GetDouble();
13577 void wxRichTextProperties::SetProperty(const wxVariant
& variant
)
13579 wxASSERT(!variant
.GetName().IsEmpty());
13581 int idx
= Find(variant
.GetName());
13584 m_properties
.Add(variant
);
13586 m_properties
[idx
] = variant
;
13589 void wxRichTextProperties::SetProperty(const wxString
& name
, const wxVariant
& variant
)
13591 int idx
= Find(name
);
13592 wxVariant
var(variant
);
13596 m_properties
.Add(var
);
13598 m_properties
[idx
] = var
;
13601 void wxRichTextProperties::SetProperty(const wxString
& name
, const wxString
& value
)
13603 SetProperty(name
, wxVariant(value
, name
));
13606 void wxRichTextProperties::SetProperty(const wxString
& name
, long value
)
13608 SetProperty(name
, wxVariant(value
, name
));
13611 void wxRichTextProperties::SetProperty(const wxString
& name
, double value
)
13613 SetProperty(name
, wxVariant(value
, name
));
13616 void wxRichTextProperties::SetProperty(const wxString
& name
, bool value
)
13618 SetProperty(name
, wxVariant(value
, name
));
13621 void wxRichTextProperties::RemoveProperties(const wxRichTextProperties
& properties
)
13624 for (i
= 0; i
< properties
.GetCount(); i
++)
13626 wxString name
= properties
.GetProperties()[i
].GetName();
13627 if (HasProperty(name
))
13632 void wxRichTextProperties::MergeProperties(const wxRichTextProperties
& properties
)
13635 for (i
= 0; i
< properties
.GetCount(); i
++)
13637 SetProperty(properties
.GetProperties()[i
]);
13641 wxRichTextObject
* wxRichTextObjectAddress::GetObject(wxRichTextParagraphLayoutBox
* topLevelContainer
) const
13643 if (m_address
.GetCount() == 0)
13644 return topLevelContainer
;
13646 wxRichTextCompositeObject
* p
= topLevelContainer
;
13648 while (p
&& i
< m_address
.GetCount())
13650 int pos
= m_address
[i
];
13651 wxASSERT(pos
>= 0 && pos
< (int) p
->GetChildren().GetCount());
13652 if (pos
< 0 || pos
>= (int) p
->GetChildren().GetCount())
13655 wxRichTextObject
* p1
= p
->GetChild(pos
);
13656 if (i
== (m_address
.GetCount()-1))
13659 p
= wxDynamicCast(p1
, wxRichTextCompositeObject
);
13665 bool wxRichTextObjectAddress::Create(wxRichTextParagraphLayoutBox
* topLevelContainer
, wxRichTextObject
* obj
)
13669 if (topLevelContainer
== obj
)
13672 wxRichTextObject
* o
= obj
;
13675 wxRichTextCompositeObject
* p
= wxDynamicCast(o
->GetParent(), wxRichTextCompositeObject
);
13679 int pos
= p
->GetChildren().IndexOf(o
);
13683 m_address
.Insert(pos
, 0);
13685 if (p
== topLevelContainer
)
13694 bool wxRichTextSelection::operator==(const wxRichTextSelection
& sel
) const
13696 if (m_container
!= sel
.m_container
)
13698 if (m_ranges
.GetCount() != sel
.m_ranges
.GetCount())
13701 for (i
= 0; i
< m_ranges
.GetCount(); i
++)
13702 if (!(m_ranges
[i
] == sel
.m_ranges
[i
]))
13707 // Get the selections appropriate to the specified object, if any; returns wxRICHTEXT_NO_SELECTION if none
13708 // or none at the level of the object's container.
13709 wxRichTextRangeArray
wxRichTextSelection::GetSelectionForObject(wxRichTextObject
* obj
) const
13713 wxRichTextParagraphLayoutBox
* container
= obj
->GetParentContainer();
13715 if (container
== m_container
)
13718 container
= obj
->GetContainer();
13721 if (container
->GetParent())
13723 // If we found that our object's container is within the range of
13724 // a selection higher up, then assume the whole original object
13725 // is also selected.
13726 wxRichTextParagraphLayoutBox
* parentContainer
= container
->GetParentContainer();
13727 if (parentContainer
== m_container
)
13729 if (WithinSelection(container
->GetRange().GetStart(), m_ranges
))
13731 wxRichTextRangeArray ranges
;
13732 ranges
.Add(obj
->GetRange());
13737 container
= parentContainer
;
13746 return wxRichTextRangeArray();
13749 // Is the given position within the selection?
13750 bool wxRichTextSelection::WithinSelection(long pos
, wxRichTextObject
* obj
) const
13756 wxRichTextRangeArray selectionRanges
= GetSelectionForObject(obj
);
13757 return WithinSelection(pos
, selectionRanges
);
13761 // Is the given position within the selection range?
13762 bool wxRichTextSelection::WithinSelection(long pos
, const wxRichTextRangeArray
& ranges
)
13765 for (i
= 0; i
< ranges
.GetCount(); i
++)
13767 const wxRichTextRange
& range
= ranges
[i
];
13768 if (pos
>= range
.GetStart() && pos
<= range
.GetEnd())
13774 // Is the given range completely within the selection range?
13775 bool wxRichTextSelection::WithinSelection(const wxRichTextRange
& range
, const wxRichTextRangeArray
& ranges
)
13778 for (i
= 0; i
< ranges
.GetCount(); i
++)
13780 const wxRichTextRange
& eachRange
= ranges
[i
];
13781 if (range
.IsWithin(eachRange
))
13787 IMPLEMENT_CLASS(wxRichTextDrawingHandler
, wxObject
)
13788 IMPLEMENT_CLASS(wxRichTextDrawingContext
, wxObject
)
13790 wxRichTextDrawingContext::wxRichTextDrawingContext(wxRichTextBuffer
* buffer
)
13794 if (m_buffer
&& m_buffer
->GetRichTextCtrl())
13795 EnableVirtualAttributes(m_buffer
->GetRichTextCtrl()->GetVirtualAttributesEnabled());
13798 bool wxRichTextDrawingContext::HasVirtualAttributes(wxRichTextObject
* obj
) const
13800 if (!GetVirtualAttributesEnabled())
13803 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
13806 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13807 if (handler
->HasVirtualAttributes(obj
))
13810 node
= node
->GetNext();
13815 wxRichTextAttr
wxRichTextDrawingContext::GetVirtualAttributes(wxRichTextObject
* obj
) const
13817 wxRichTextAttr attr
;
13818 if (!GetVirtualAttributesEnabled())
13821 // We apply all handlers, so we can may combine several different attributes
13822 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
13825 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13826 if (handler
->HasVirtualAttributes(obj
))
13828 bool success
= handler
->GetVirtualAttributes(attr
, obj
);
13830 wxUnusedVar(success
);
13833 node
= node
->GetNext();
13838 bool wxRichTextDrawingContext::ApplyVirtualAttributes(wxRichTextAttr
& attr
, wxRichTextObject
* obj
) const
13840 if (!GetVirtualAttributesEnabled())
13843 if (HasVirtualAttributes(obj
))
13845 wxRichTextAttr
a(GetVirtualAttributes(obj
));
13853 int wxRichTextDrawingContext::GetVirtualSubobjectAttributesCount(wxRichTextObject
* obj
) const
13855 if (!GetVirtualAttributesEnabled())
13858 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
13861 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13862 int count
= handler
->GetVirtualSubobjectAttributesCount(obj
);
13866 node
= node
->GetNext();
13871 int wxRichTextDrawingContext::GetVirtualSubobjectAttributes(wxRichTextObject
* obj
, wxArrayInt
& positions
, wxRichTextAttrArray
& attributes
) const
13873 if (!GetVirtualAttributesEnabled())
13876 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
13879 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13880 if (handler
->GetVirtualSubobjectAttributes(obj
, positions
, attributes
))
13881 return positions
.GetCount();
13883 node
= node
->GetNext();
13888 bool wxRichTextDrawingContext::HasVirtualText(const wxRichTextPlainText
* obj
) const
13890 if (!GetVirtualAttributesEnabled())
13893 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
13896 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13897 if (handler
->HasVirtualText(obj
))
13900 node
= node
->GetNext();
13905 bool wxRichTextDrawingContext::GetVirtualText(const wxRichTextPlainText
* obj
, wxString
& text
) const
13907 if (!GetVirtualAttributesEnabled())
13910 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
13913 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13914 if (handler
->GetVirtualText(obj
, text
))
13917 node
= node
->GetNext();
13922 /// Adds a handler to the end
13923 void wxRichTextBuffer::AddDrawingHandler(wxRichTextDrawingHandler
*handler
)
13925 sm_drawingHandlers
.Append(handler
);
13928 /// Inserts a handler at the front
13929 void wxRichTextBuffer::InsertDrawingHandler(wxRichTextDrawingHandler
*handler
)
13931 sm_drawingHandlers
.Insert( handler
);
13934 /// Removes a handler
13935 bool wxRichTextBuffer::RemoveDrawingHandler(const wxString
& name
)
13937 wxRichTextDrawingHandler
*handler
= FindDrawingHandler(name
);
13940 sm_drawingHandlers
.DeleteObject(handler
);
13948 wxRichTextDrawingHandler
* wxRichTextBuffer::FindDrawingHandler(const wxString
& name
)
13950 wxList::compatibility_iterator node
= sm_drawingHandlers
.GetFirst();
13953 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13954 if (handler
->GetName().Lower() == name
.Lower()) return handler
;
13956 node
= node
->GetNext();
13961 void wxRichTextBuffer::CleanUpDrawingHandlers()
13963 wxList::compatibility_iterator node
= sm_drawingHandlers
.GetFirst();
13966 wxRichTextDrawingHandler
* handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13967 wxList::compatibility_iterator next
= node
->GetNext();
13972 sm_drawingHandlers
.Clear();
13975 void wxRichTextBuffer::AddFieldType(wxRichTextFieldType
*fieldType
)
13977 sm_fieldTypes
[fieldType
->GetName()] = fieldType
;
13980 bool wxRichTextBuffer::RemoveFieldType(const wxString
& name
)
13982 wxRichTextFieldTypeHashMap::iterator it
= sm_fieldTypes
.find(name
);
13983 if (it
== sm_fieldTypes
.end())
13987 wxRichTextFieldType
* fieldType
= it
->second
;
13988 sm_fieldTypes
.erase(it
);
13994 wxRichTextFieldType
*wxRichTextBuffer::FindFieldType(const wxString
& name
)
13996 wxRichTextFieldTypeHashMap::iterator it
= sm_fieldTypes
.find(name
);
13997 if (it
== sm_fieldTypes
.end())
14003 void wxRichTextBuffer::CleanUpFieldTypes()
14005 wxRichTextFieldTypeHashMap::iterator it
;
14006 for( it
= sm_fieldTypes
.begin(); it
!= sm_fieldTypes
.end(); ++it
)
14008 wxRichTextFieldType
* fieldType
= it
->second
;
14012 sm_fieldTypes
.clear();