1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/richtext/richtextbuffer.cpp
3 // Purpose: Buffer for wxRichTextCtrl
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // For compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
21 #include "wx/richtext/richtextbuffer.h"
27 #include "wx/dataobj.h"
28 #include "wx/module.h"
31 #include "wx/settings.h"
32 #include "wx/filename.h"
33 #include "wx/clipbrd.h"
34 #include "wx/wfstream.h"
35 #include "wx/mstream.h"
36 #include "wx/sstream.h"
37 #include "wx/textfile.h"
38 #include "wx/hashmap.h"
39 #include "wx/dynarray.h"
41 #include "wx/richtext/richtextctrl.h"
42 #include "wx/richtext/richtextstyles.h"
43 #include "wx/richtext/richtextimagedlg.h"
44 #include "wx/richtext/richtextsizepage.h"
45 #include "wx/richtext/richtextxml.h"
47 #include "wx/listimpl.cpp"
48 #include "wx/arrimpl.cpp"
50 WX_DEFINE_LIST(wxRichTextObjectList
)
51 WX_DEFINE_LIST(wxRichTextLineList
)
53 // Switch off if the platform doesn't like it for some reason
54 #define wxRICHTEXT_USE_OPTIMIZED_DRAWING 1
56 // Use GetPartialTextExtents for platforms that support it natively
57 #define wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS 1
59 const wxChar wxRichTextLineBreakChar
= (wxChar
) 29;
61 // Helper classes for floating layout
62 struct wxRichTextFloatRectMap
64 wxRichTextFloatRectMap(int sY
, int eY
, int w
, wxRichTextObject
* obj
)
74 wxRichTextObject
* anchor
;
77 WX_DEFINE_SORTED_ARRAY(wxRichTextFloatRectMap
*, wxRichTextFloatRectMapArray
);
79 int wxRichTextFloatRectMapCmp(wxRichTextFloatRectMap
* r1
, wxRichTextFloatRectMap
* r2
)
81 return r1
->startY
- r2
->startY
;
84 class wxRichTextFloatCollector
87 wxRichTextFloatCollector(const wxRect
& availableRect
);
88 ~wxRichTextFloatCollector();
90 // Collect the floating objects info in the given paragraph
91 void CollectFloat(wxRichTextParagraph
* para
);
92 void CollectFloat(wxRichTextParagraph
* para
, wxRichTextObject
* floating
);
94 // Return the last paragraph we collected
95 wxRichTextParagraph
* LastParagraph();
97 // Given the start y position and the height of the line,
98 // find out how wide the line can be
99 wxRect
GetAvailableRect(int startY
, int endY
);
101 // Given a floating box, find its fit position
102 int GetFitPosition(int direction
, int start
, int height
) const;
103 int GetFitPosition(const wxRichTextFloatRectMapArray
& array
, int start
, int height
) const;
105 // Find the last y position
106 int GetLastRectBottom();
108 // Draw the floats inside a rect
109 void Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
);
111 // HitTest the floats
112 int HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, int flags
);
114 // Get floating object count
115 int GetFloatingObjectCount() const { return m_left
.GetCount() + m_right
.GetCount(); }
117 // Get floating objects
118 bool GetFloatingObjects(wxRichTextObjectList
& objects
) const;
121 bool DeleteFloat(wxRichTextObject
* obj
);
123 // Do we have this float already?
124 bool HasFloat(wxRichTextObject
* obj
);
126 bool HasFloats() const { return m_left
.GetCount() >0 || m_right
.GetCount() > 0; }
128 static int SearchAdjacentRect(const wxRichTextFloatRectMapArray
& array
, int point
);
130 static int GetWidthFromFloatRect(const wxRichTextFloatRectMapArray
& array
, int index
, int startY
, int endY
);
132 static void FreeFloatRectMapArray(wxRichTextFloatRectMapArray
& array
);
134 static void DrawFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
);
136 static int HitTestFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, int flags
);
139 wxRichTextFloatRectMapArray m_left
;
140 wxRichTextFloatRectMapArray m_right
;
142 wxRect m_availableRect
;
143 wxRichTextParagraph
* m_para
;
147 bool wxRichTextFloatCollector::DeleteFloat(wxRichTextObject
* obj
)
150 for (i
= 0; i
< m_left
.GetCount(); i
++)
152 if (m_left
[i
]->anchor
== obj
)
158 for (i
= 0; i
< m_right
.GetCount(); i
++)
160 if (m_right
[i
]->anchor
== obj
)
169 // Do we have this float already?
170 bool wxRichTextFloatCollector::HasFloat(wxRichTextObject
* obj
)
173 for (i
= 0; i
< m_left
.GetCount(); i
++)
175 if (m_left
[i
]->anchor
== obj
)
180 for (i
= 0; i
< m_right
.GetCount(); i
++)
182 if (m_right
[i
]->anchor
== obj
)
190 // Get floating objects
191 bool wxRichTextFloatCollector::GetFloatingObjects(wxRichTextObjectList
& objects
) const
194 for (i
= 0; i
< m_left
.GetCount(); i
++)
195 objects
.Append(m_left
[i
]->anchor
);
196 for (i
= 0; i
< m_right
.GetCount(); i
++)
197 objects
.Append(m_right
[i
]->anchor
);
203 * Binary search helper function
204 * The argument point is the Y coordinate, and this fuction
205 * always return the floating rect that contain this coordinate
206 * or under this coordinate.
208 int wxRichTextFloatCollector::SearchAdjacentRect(const wxRichTextFloatRectMapArray
& array
, int point
)
210 int end
= array
.GetCount() - 1;
223 int mid
= (start
+ end
) / 2;
224 if (array
[mid
]->startY
<= point
&& array
[mid
]->endY
>= point
)
226 else if (array
[mid
]->startY
> point
)
231 else if (array
[mid
]->endY
< point
)
241 int wxRichTextFloatCollector::GetWidthFromFloatRect(const wxRichTextFloatRectMapArray
& array
, int index
, int startY
, int endY
)
244 int len
= array
.GetCount();
246 wxASSERT(index
>= 0 && index
< len
);
248 if (array
[index
]->startY
< startY
&& array
[index
]->endY
> startY
)
249 ret
= ret
< array
[index
]->width
? array
[index
]->width
: ret
;
250 while (index
< len
&& array
[index
]->startY
<= endY
)
252 ret
= ret
< array
[index
]->width
? array
[index
]->width
: ret
;
259 wxRichTextFloatCollector::wxRichTextFloatCollector(const wxRect
& rect
) : m_left(wxRichTextFloatRectMapCmp
), m_right(wxRichTextFloatRectMapCmp
)
261 m_availableRect
= rect
;
265 void wxRichTextFloatCollector::FreeFloatRectMapArray(wxRichTextFloatRectMapArray
& array
)
267 int len
= array
.GetCount();
268 for (int i
= 0; i
< len
; i
++)
272 wxRichTextFloatCollector::~wxRichTextFloatCollector()
274 FreeFloatRectMapArray(m_left
);
275 FreeFloatRectMapArray(m_right
);
278 int wxRichTextFloatCollector::GetFitPosition(const wxRichTextFloatRectMapArray
& array
, int start
, int height
) const
280 if (array
.GetCount() == 0)
283 int i
= SearchAdjacentRect(array
, start
);
285 while (i
< (int) array
.GetCount())
287 if (array
[i
]->startY
- last
>= height
)
289 last
= array
[i
]->endY
;
296 int wxRichTextFloatCollector::GetFitPosition(int direction
, int start
, int height
) const
298 if (direction
== wxTEXT_BOX_ATTR_FLOAT_LEFT
)
299 return GetFitPosition(m_left
, start
, height
);
300 else if (direction
== wxTEXT_BOX_ATTR_FLOAT_RIGHT
)
301 return GetFitPosition(m_right
, start
, height
);
304 wxASSERT("Never should be here");
309 // Adds a floating image to the float collector.
310 // The actual positioning is done by wxRichTextParagraph::LayoutFloat.
311 void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph
* para
, wxRichTextObject
* floating
)
313 int direction
= floating
->GetFloatDirection();
315 wxPoint pos
= floating
->GetPosition();
316 wxSize size
= floating
->GetCachedSize();
317 wxRichTextFloatRectMap
*map
= new wxRichTextFloatRectMap(pos
.y
, pos
.y
+ size
.y
, size
.x
, floating
);
320 case wxTEXT_BOX_ATTR_FLOAT_NONE
:
323 case wxTEXT_BOX_ATTR_FLOAT_LEFT
:
324 // Just a not-enough simple assertion
325 wxASSERT (m_left
.Index(map
) == wxNOT_FOUND
);
328 case wxTEXT_BOX_ATTR_FLOAT_RIGHT
:
329 wxASSERT (m_right
.Index(map
) == wxNOT_FOUND
);
334 wxASSERT("Unrecognised float attribute.");
340 void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph
* para
)
342 wxRichTextObjectList::compatibility_iterator node
= para
->GetChildren().GetFirst();
345 wxRichTextObject
* floating
= node
->GetData();
347 if (floating
->IsFloating())
349 CollectFloat(para
, floating
);
352 node
= node
->GetNext();
358 wxRichTextParagraph
* wxRichTextFloatCollector::LastParagraph()
363 wxRect
wxRichTextFloatCollector::GetAvailableRect(int startY
, int endY
)
365 int widthLeft
= 0, widthRight
= 0;
366 if (m_left
.GetCount() != 0)
368 int i
= SearchAdjacentRect(m_left
, startY
);
369 if (i
< (int) m_left
.GetCount())
370 widthLeft
= GetWidthFromFloatRect(m_left
, i
, startY
, endY
);
372 if (m_right
.GetCount() != 0)
374 int j
= SearchAdjacentRect(m_right
, startY
);
375 if (j
< (int) m_right
.GetCount())
376 widthRight
= GetWidthFromFloatRect(m_right
, j
, startY
, endY
);
379 // TODO: actually we want to use the actual image positions to find the
380 // available remaining space, since the image might not be right up against
381 // the left or right edge of the container.
382 return wxRect(widthLeft
+ m_availableRect
.x
, 0, m_availableRect
.width
- widthLeft
- widthRight
, 0);
385 int wxRichTextFloatCollector::GetLastRectBottom()
388 int len
= m_left
.GetCount();
390 ret
= ret
> m_left
[len
-1]->endY
? ret
: m_left
[len
-1]->endY
;
392 len
= m_right
.GetCount();
394 ret
= ret
> m_right
[len
-1]->endY
? ret
: m_right
[len
-1]->endY
;
400 void wxRichTextFloatCollector::DrawFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& WXUNUSED(range
), const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
403 int end
= rect
.y
+ rect
.height
;
405 i
= SearchAdjacentRect(array
, start
);
406 if (i
< 0 || i
>= (int) array
.GetCount())
408 j
= SearchAdjacentRect(array
, end
);
409 if (j
< 0 || j
>= (int) array
.GetCount())
410 j
= array
.GetCount() - 1;
413 wxRichTextObject
* obj
= array
[i
]->anchor
;
414 wxRichTextRange r
= obj
->GetRange();
415 obj
->Draw(dc
, context
, r
, selection
, wxRect(obj
->GetPosition(), obj
->GetCachedSize()), descent
, style
);
420 void wxRichTextFloatCollector::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
422 if (m_left
.GetCount() > 0)
423 DrawFloat(m_left
, dc
, context
, range
, selection
, rect
, descent
, style
);
424 if (m_right
.GetCount() > 0)
425 DrawFloat(m_right
, dc
, context
, range
, selection
, rect
, descent
, style
);
428 int wxRichTextFloatCollector::HitTestFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& WXUNUSED(dc
), wxRichTextDrawingContext
& WXUNUSED(context
), const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, int WXUNUSED(flags
))
431 if (array
.GetCount() == 0)
432 return wxRICHTEXT_HITTEST_NONE
;
433 i
= SearchAdjacentRect(array
, pt
.y
);
434 if (i
< 0 || i
>= (int) array
.GetCount())
435 return wxRICHTEXT_HITTEST_NONE
;
436 if (!array
[i
]->anchor
->IsShown())
437 return wxRICHTEXT_HITTEST_NONE
;
439 wxPoint point
= array
[i
]->anchor
->GetPosition();
440 wxSize size
= array
[i
]->anchor
->GetCachedSize();
441 if (point
.x
<= pt
.x
&& point
.x
+ size
.x
>= pt
.x
442 && point
.y
<= pt
.y
&& point
.y
+ size
.y
>= pt
.y
)
444 textPosition
= array
[i
]->anchor
->GetRange().GetStart();
445 * obj
= array
[i
]->anchor
;
446 if (pt
.x
> (pt
.x
+ pt
.x
+ size
.x
) / 2)
447 return wxRICHTEXT_HITTEST_BEFORE
;
449 return wxRICHTEXT_HITTEST_AFTER
;
452 return wxRICHTEXT_HITTEST_NONE
;
455 int wxRichTextFloatCollector::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, int flags
)
457 int ret
= HitTestFloat(m_left
, dc
, context
, pt
, textPosition
, obj
, flags
);
458 if (ret
== wxRICHTEXT_HITTEST_NONE
)
460 ret
= HitTestFloat(m_right
, dc
, context
, pt
, textPosition
, obj
, flags
);
465 // Helpers for efficiency
466 inline void wxCheckSetFont(wxDC
& dc
, const wxFont
& font
)
471 inline void wxCheckSetPen(wxDC
& dc
, const wxPen
& pen
)
473 const wxPen
& pen1
= dc
.GetPen();
474 if (pen1
.IsOk() && pen
.IsOk())
476 if (pen1
.GetWidth() == pen
.GetWidth() &&
477 pen1
.GetStyle() == pen
.GetStyle() &&
478 pen1
.GetColour() == pen
.GetColour())
484 inline void wxCheckSetBrush(wxDC
& dc
, const wxBrush
& brush
)
486 const wxBrush
& brush1
= dc
.GetBrush();
487 if (brush1
.IsOk() && brush
.IsOk())
489 if (brush1
.GetStyle() == brush
.GetStyle() &&
490 brush1
.GetColour() == brush
.GetColour())
498 * This is the base for drawable objects.
501 IMPLEMENT_CLASS(wxRichTextObject
, wxObject
)
503 wxRichTextObject::wxRichTextObject(wxRichTextObject
* parent
)
511 wxRichTextObject::~wxRichTextObject()
515 void wxRichTextObject::Dereference()
523 void wxRichTextObject::Copy(const wxRichTextObject
& obj
)
526 m_maxSize
= obj
.m_maxSize
;
527 m_minSize
= obj
.m_minSize
;
529 m_range
= obj
.m_range
;
530 m_ownRange
= obj
.m_ownRange
;
531 m_attributes
= obj
.m_attributes
;
532 m_properties
= obj
.m_properties
;
533 m_descent
= obj
.m_descent
;
537 // Get/set the top-level container of this object.
538 wxRichTextParagraphLayoutBox
* wxRichTextObject::GetContainer() const
540 const wxRichTextObject
* p
= this;
545 return wxDynamicCast(p
, wxRichTextParagraphLayoutBox
);
552 void wxRichTextObject::SetMargins(int margin
)
554 SetMargins(margin
, margin
, margin
, margin
);
557 void wxRichTextObject::SetMargins(int leftMargin
, int rightMargin
, int topMargin
, int bottomMargin
)
559 GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().SetValue(leftMargin
, wxTEXT_ATTR_UNITS_PIXELS
);
560 GetAttributes().GetTextBoxAttr().GetMargins().GetRight().SetValue(rightMargin
, wxTEXT_ATTR_UNITS_PIXELS
);
561 GetAttributes().GetTextBoxAttr().GetMargins().GetTop().SetValue(topMargin
, wxTEXT_ATTR_UNITS_PIXELS
);
562 GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().SetValue(bottomMargin
, wxTEXT_ATTR_UNITS_PIXELS
);
565 int wxRichTextObject::GetLeftMargin() const
567 return GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().GetValue();
570 int wxRichTextObject::GetRightMargin() const
572 return GetAttributes().GetTextBoxAttr().GetMargins().GetRight().GetValue();
575 int wxRichTextObject::GetTopMargin() const
577 return GetAttributes().GetTextBoxAttr().GetMargins().GetTop().GetValue();
580 int wxRichTextObject::GetBottomMargin() const
582 return GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().GetValue();
585 // Calculate the available content space in the given rectangle, given the
586 // margins, border and padding specified in the object's attributes.
587 wxRect
wxRichTextObject::GetAvailableContentArea(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& outerRect
) const
589 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
590 marginRect
= outerRect
;
591 wxRichTextAttr
attr(GetAttributes());
592 context
.ApplyVirtualAttributes(attr
, (wxRichTextObject
*) this);
593 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
597 // Invalidate the buffer. With no argument, invalidates whole buffer.
598 void wxRichTextObject::Invalidate(const wxRichTextRange
& invalidRange
)
600 if (invalidRange
!= wxRICHTEXT_NONE
)
602 // If this is a floating object, size may not be recalculated
603 // after floats have been collected in an early stage of Layout.
604 // So avoid resetting the cache for floating objects during layout.
605 if (!IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode())
606 SetCachedSize(wxDefaultSize
);
607 SetMaxSize(wxDefaultSize
);
608 SetMinSize(wxDefaultSize
);
612 // Convert units in tenths of a millimetre to device units
613 int wxRichTextObject::ConvertTenthsMMToPixels(wxDC
& dc
, int units
) const
618 scale
= GetBuffer()->GetScale() / GetBuffer()->GetDimensionScale();
619 int p
= ConvertTenthsMMToPixels(dc
.GetPPI().x
, units
, scale
);
624 // Convert units in tenths of a millimetre to device units
625 int wxRichTextObject::ConvertTenthsMMToPixels(int ppi
, int units
, double scale
)
627 // There are ppi pixels in 254.1 "1/10 mm"
629 double pixels
= ((double) units
* (double)ppi
) / 254.1;
633 // If the result is very small, make it at least one pixel in size.
634 if (pixels
== 0 && units
> 0)
640 // Convert units in pixels to tenths of a millimetre
641 int wxRichTextObject::ConvertPixelsToTenthsMM(wxDC
& dc
, int pixels
) const
646 scale
= GetBuffer()->GetScale();
648 return ConvertPixelsToTenthsMM(dc
.GetPPI().x
, p
, scale
);
651 int wxRichTextObject::ConvertPixelsToTenthsMM(int ppi
, int pixels
, double scale
)
653 // There are ppi pixels in 254.1 "1/10 mm"
655 double p
= double(pixels
);
660 int units
= int( p
* 254.1 / (double) ppi
);
664 // Draw the borders and background for the given rectangle and attributes.
665 // Width and height are taken to be the outer margin size, not the content.
666 bool wxRichTextObject::DrawBoxAttributes(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& attr
, const wxRect
& boxRect
, int flags
)
668 // Assume boxRect is the area around the content
669 wxRect marginRect
= boxRect
;
670 wxRect contentRect
, borderRect
, paddingRect
, outlineRect
;
672 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
674 // Margin is transparent. Draw background from margin.
675 if (attr
.HasBackgroundColour() || (flags
& wxRICHTEXT_DRAW_SELECTED
))
678 if (flags
& wxRICHTEXT_DRAW_SELECTED
)
680 // TODO: get selection colour from control?
681 colour
= wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
);
684 colour
= attr
.GetBackgroundColour();
687 wxBrush
brush(colour
);
691 dc
.DrawRectangle(borderRect
);
694 if (flags
& wxRICHTEXT_DRAW_GUIDELINES
)
696 wxRichTextAttr editBorderAttr
= attr
;
697 // TODO: make guideline colour configurable
698 editBorderAttr
.GetTextBoxAttr().GetBorder().SetColour(*wxLIGHT_GREY
);
699 editBorderAttr
.GetTextBoxAttr().GetBorder().SetWidth(1, wxTEXT_ATTR_UNITS_PIXELS
);
700 editBorderAttr
.GetTextBoxAttr().GetBorder().SetStyle(wxTEXT_BOX_ATTR_BORDER_SOLID
);
702 DrawBorder(dc
, buffer
, editBorderAttr
.GetTextBoxAttr().GetBorder(), borderRect
, flags
);
705 if (attr
.GetTextBoxAttr().GetBorder().IsValid())
706 DrawBorder(dc
, buffer
, attr
.GetTextBoxAttr().GetBorder(), borderRect
);
708 if (attr
.GetTextBoxAttr().GetOutline().IsValid())
709 DrawBorder(dc
, buffer
, attr
.GetTextBoxAttr().GetOutline(), outlineRect
);
715 bool wxRichTextObject::DrawBorder(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxTextAttrBorders
& attr
, const wxRect
& rect
, int WXUNUSED(flags
))
717 int borderLeft
= 0, borderRight
= 0, borderTop
= 0, borderBottom
= 0;
718 wxTextAttrDimensionConverter
converter(dc
, buffer
? buffer
->GetScale() : 1.0);
720 if (attr
.GetLeft().IsValid() && attr
.GetLeft().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
722 borderLeft
= converter
.GetPixels(attr
.GetLeft().GetWidth());
723 wxColour
col(attr
.GetLeft().GetColour());
725 // If pen width is > 1, resorts to a solid rectangle.
728 int penStyle
= wxSOLID
;
729 if (attr
.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
731 else if (attr
.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
732 penStyle
= wxLONG_DASH
;
733 wxPen
pen(col
, 1, penStyle
);
735 dc
.DrawLine(rect
.x
, rect
.y
, rect
.x
, rect
.y
+ rect
.height
);
738 else if (borderLeft
> 1)
744 dc
.DrawRectangle(rect
.x
, rect
.y
, borderLeft
, rect
.height
);
748 if (attr
.GetRight().IsValid() && attr
.GetRight().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
750 borderRight
= converter
.GetPixels(attr
.GetRight().GetWidth());
752 wxColour
col(attr
.GetRight().GetColour());
754 // If pen width is > 1, resorts to a solid rectangle.
755 if (borderRight
== 1)
757 int penStyle
= wxSOLID
;
758 if (attr
.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
760 else if (attr
.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
761 penStyle
= wxLONG_DASH
;
762 wxPen
pen(col
, 1, penStyle
);
764 dc
.DrawLine(rect
.x
+ rect
.width
, rect
.y
, rect
.x
+ rect
.width
, rect
.y
+ rect
.height
+ 1);
767 else if (borderRight
> 1)
773 dc
.DrawRectangle(rect
.x
+ rect
.width
- borderRight
, rect
.y
, borderRight
, rect
.height
);
777 if (attr
.GetTop().IsValid() && attr
.GetTop().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
779 borderTop
= converter
.GetPixels(attr
.GetTop().GetWidth());
781 wxColour
col(attr
.GetTop().GetColour());
783 // If pen width is > 1, resorts to a solid rectangle.
786 int penStyle
= wxSOLID
;
787 if (attr
.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
789 else if (attr
.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
790 penStyle
= wxLONG_DASH
;
791 wxPen
pen(col
, 1, penStyle
);
793 dc
.DrawLine(rect
.x
, rect
.y
, rect
.x
+ rect
.width
, rect
.y
);
796 else if (borderTop
> 1)
802 dc
.DrawRectangle(rect
.x
, rect
.y
, rect
.width
, borderTop
);
806 if (attr
.GetBottom().IsValid() && attr
.GetBottom().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
808 borderBottom
= converter
.GetPixels(attr
.GetBottom().GetWidth());
809 wxColour
col(attr
.GetBottom().GetColour());
811 // If pen width is > 1, resorts to a solid rectangle.
812 if (borderBottom
== 1)
814 int penStyle
= wxSOLID
;
815 if (attr
.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
817 else if (attr
.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
818 penStyle
= wxLONG_DASH
;
819 wxPen
pen(col
, 1, penStyle
);
821 dc
.DrawLine(rect
.x
, rect
.y
+ rect
.height
, rect
.x
+ rect
.width
, rect
.y
+ rect
.height
);
824 else if (borderBottom
> 1)
830 dc
.DrawRectangle(rect
.x
, rect
.y
+ rect
.height
- borderBottom
, rect
.width
, borderBottom
);
837 // Get the various rectangles of the box model in pixels. You can either specify contentRect (inner)
838 // or marginRect (outer), and the other must be the default rectangle (no width or height).
839 // Note that the outline doesn't affect the position of the rectangle, it's drawn in whatever space
842 // | Margin | Border | Padding | CONTENT | Padding | Border | Margin |
844 bool wxRichTextObject::GetBoxRects(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& attr
, wxRect
& marginRect
, wxRect
& borderRect
, wxRect
& contentRect
, wxRect
& paddingRect
, wxRect
& outlineRect
)
846 int borderLeft
= 0, borderRight
= 0, borderTop
= 0, borderBottom
= 0;
847 int outlineLeft
= 0, outlineRight
= 0, outlineTop
= 0, outlineBottom
= 0;
848 int paddingLeft
= 0, paddingRight
= 0, paddingTop
= 0, paddingBottom
= 0;
849 int marginLeft
= 0, marginRight
= 0, marginTop
= 0, marginBottom
= 0;
851 wxTextAttrDimensionConverter
converter(dc
, buffer
? buffer
->GetScale() : 1.0);
853 if (attr
.GetTextBoxAttr().GetMargins().GetLeft().IsValid())
854 marginLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetLeft());
855 if (attr
.GetTextBoxAttr().GetMargins().GetRight().IsValid())
856 marginRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetRight());
857 if (attr
.GetTextBoxAttr().GetMargins().GetTop().IsValid())
858 marginTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetTop());
859 if (attr
.GetTextBoxAttr().GetMargins().GetBottom().IsValid())
860 marginBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetBottom());
862 if (attr
.GetTextBoxAttr().GetBorder().GetLeft().GetWidth().IsValid())
863 borderLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetLeft().GetWidth());
864 if (attr
.GetTextBoxAttr().GetBorder().GetRight().GetWidth().IsValid())
865 borderRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetRight().GetWidth());
866 if (attr
.GetTextBoxAttr().GetBorder().GetTop().GetWidth().IsValid())
867 borderTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetTop().GetWidth());
868 if (attr
.GetTextBoxAttr().GetBorder().GetBottom().GetWidth().IsValid())
869 borderBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetBottom().GetWidth());
871 if (attr
.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
872 paddingLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetLeft());
873 if (attr
.GetTextBoxAttr().GetPadding().GetRight().IsValid())
874 paddingRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetRight());
875 if (attr
.GetTextBoxAttr().GetPadding().GetTop().IsValid())
876 paddingTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetTop());
877 if (attr
.GetTextBoxAttr().GetPadding().GetBottom().IsValid())
878 paddingBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetBottom());
880 if (attr
.GetTextBoxAttr().GetOutline().GetLeft().GetWidth().IsValid())
881 outlineLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetLeft().GetWidth());
882 if (attr
.GetTextBoxAttr().GetOutline().GetRight().GetWidth().IsValid())
883 outlineRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetRight().GetWidth());
884 if (attr
.GetTextBoxAttr().GetOutline().GetTop().GetWidth().IsValid())
885 outlineTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetTop().GetWidth());
886 if (attr
.GetTextBoxAttr().GetOutline().GetBottom().GetWidth().IsValid())
887 outlineBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetBottom().GetWidth());
889 int leftTotal
= marginLeft
+ borderLeft
+ paddingLeft
;
890 int rightTotal
= marginRight
+ borderRight
+ paddingRight
;
891 int topTotal
= marginTop
+ borderTop
+ paddingTop
;
892 int bottomTotal
= marginBottom
+ borderBottom
+ paddingBottom
;
894 if (marginRect
!= wxRect())
896 contentRect
.x
= marginRect
.x
+ leftTotal
;
897 contentRect
.y
= marginRect
.y
+ topTotal
;
898 contentRect
.width
= marginRect
.width
- (leftTotal
+ rightTotal
);
899 contentRect
.height
= marginRect
.height
- (topTotal
+ bottomTotal
);
903 marginRect
.x
= contentRect
.x
- leftTotal
;
904 marginRect
.y
= contentRect
.y
- topTotal
;
905 marginRect
.width
= contentRect
.width
+ (leftTotal
+ rightTotal
);
906 marginRect
.height
= contentRect
.height
+ (topTotal
+ bottomTotal
);
909 borderRect
.x
= marginRect
.x
+ marginLeft
;
910 borderRect
.y
= marginRect
.y
+ marginTop
;
911 borderRect
.width
= marginRect
.width
- (marginLeft
+ marginRight
);
912 borderRect
.height
= marginRect
.height
- (marginTop
+ marginBottom
);
914 paddingRect
.x
= marginRect
.x
+ marginLeft
+ borderLeft
;
915 paddingRect
.y
= marginRect
.y
+ marginTop
+ borderTop
;
916 paddingRect
.width
= marginRect
.width
- (marginLeft
+ marginRight
+ borderLeft
+ borderRight
);
917 paddingRect
.height
= marginRect
.height
- (marginTop
+ marginBottom
+ borderTop
+ borderBottom
);
919 // The outline is outside the margin and doesn't influence the overall box position or content size.
920 outlineRect
.x
= marginRect
.x
- outlineLeft
;
921 outlineRect
.y
= marginRect
.y
- outlineTop
;
922 outlineRect
.width
= marginRect
.width
+ (outlineLeft
+ outlineRight
);
923 outlineRect
.height
= marginRect
.height
+ (outlineTop
+ outlineBottom
);
928 // Get the total margin for the object in pixels, taking into account margin, padding and border size
929 bool wxRichTextObject::GetTotalMargin(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& attr
, int& leftMargin
, int& rightMargin
,
930 int& topMargin
, int& bottomMargin
)
932 // Assume boxRect is the area around the content
933 wxRect contentRect
, marginRect
, borderRect
, paddingRect
, outlineRect
;
934 marginRect
= wxRect(0, 0, 1000, 1000);
936 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
938 leftMargin
= contentRect
.GetLeft() - marginRect
.GetLeft();
939 rightMargin
= marginRect
.GetRight() - contentRect
.GetRight();
940 topMargin
= contentRect
.GetTop() - marginRect
.GetTop();
941 bottomMargin
= marginRect
.GetBottom() - contentRect
.GetBottom();
946 // Returns the rectangle which the child has available to it given restrictions specified in the
947 // child attribute, e.g. 50% width of the parent, 400 pixels, x position 20% of the parent, etc.
948 // availableContainerSpace might be a parent that the cell has to compute its width relative to.
949 // E.g. a cell that's 50% of its parent.
950 wxRect
wxRichTextObject::AdjustAvailableSpace(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& WXUNUSED(parentAttr
), const wxRichTextAttr
& childAttr
, const wxRect
& availableParentSpace
, const wxRect
& availableContainerSpace
)
952 wxRect rect
= availableParentSpace
;
955 scale
= buffer
->GetScale();
957 wxTextAttrDimensionConverter
converter(dc
, scale
, availableContainerSpace
.GetSize());
959 if (childAttr
.GetTextBoxAttr().GetWidth().IsValid())
960 rect
.width
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetWidth());
962 if (childAttr
.GetTextBoxAttr().GetHeight().IsValid())
963 rect
.height
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetHeight());
965 // Can specify either left or right for the position (we're assuming we can't
966 // set the left and right edges to effectively set the size. Would we want to do that?)
967 if (childAttr
.GetTextBoxAttr().GetPosition().GetLeft().IsValid())
969 rect
.x
= rect
.x
+ converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetLeft());
971 else if (childAttr
.GetTextBoxAttr().GetPosition().GetRight().IsValid())
973 int x
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetRight());
974 if (childAttr
.GetTextBoxAttr().GetPosition().GetRight().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE
)
975 rect
.x
= availableContainerSpace
.x
+ availableContainerSpace
.width
- rect
.width
;
980 if (childAttr
.GetTextBoxAttr().GetPosition().GetTop().IsValid())
982 rect
.y
= rect
.y
+ converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetTop());
984 else if (childAttr
.GetTextBoxAttr().GetPosition().GetBottom().IsValid())
986 int y
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetBottom());
987 if (childAttr
.GetTextBoxAttr().GetPosition().GetBottom().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE
)
988 rect
.y
= availableContainerSpace
.y
+ availableContainerSpace
.height
- rect
.height
;
993 if (rect
.GetWidth() > availableParentSpace
.GetWidth())
994 rect
.SetWidth(availableParentSpace
.GetWidth());
999 // Dump to output stream for debugging
1000 void wxRichTextObject::Dump(wxTextOutputStream
& stream
)
1002 stream
<< GetClassInfo()->GetClassName() << wxT("\n");
1003 stream
<< wxString::Format(wxT("Size: %d,%d. Position: %d,%d, Range: %ld,%ld"), m_size
.x
, m_size
.y
, m_pos
.x
, m_pos
.y
, m_range
.GetStart(), m_range
.GetEnd()) << wxT("\n");
1004 stream
<< wxString::Format(wxT("Text colour: %d,%d,%d."), (int) m_attributes
.GetTextColour().Red(), (int) m_attributes
.GetTextColour().Green(), (int) m_attributes
.GetTextColour().Blue()) << wxT("\n");
1007 // Gets the containing buffer
1008 wxRichTextBuffer
* wxRichTextObject::GetBuffer() const
1010 const wxRichTextObject
* obj
= this;
1011 while (obj
&& !wxDynamicCast(obj
, wxRichTextBuffer
))
1012 obj
= obj
->GetParent();
1013 return wxDynamicCast(obj
, wxRichTextBuffer
);
1016 // Get the absolute object position, by traversing up the child/parent hierarchy
1017 wxPoint
wxRichTextObject::GetAbsolutePosition() const
1019 wxPoint pt
= GetPosition();
1021 wxRichTextObject
* p
= GetParent();
1024 pt
= pt
+ p
->GetPosition();
1031 // Hit-testing: returns a flag indicating hit test details, plus
1032 // information about position
1033 int wxRichTextObject::HitTest(wxDC
& WXUNUSED(dc
), wxRichTextDrawingContext
& WXUNUSED(context
), const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int WXUNUSED(flags
))
1036 return wxRICHTEXT_HITTEST_NONE
;
1038 wxRect rect
= GetRect();
1039 if (pt
.x
>= rect
.x
&& pt
.x
< rect
.x
+ rect
.width
&&
1040 pt
.y
>= rect
.y
&& pt
.y
< rect
.y
+ rect
.height
)
1043 *contextObj
= GetParentContainer();
1044 textPosition
= GetRange().GetStart();
1045 return wxRICHTEXT_HITTEST_ON
;
1048 return wxRICHTEXT_HITTEST_NONE
;
1051 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1052 // lays out the object again using the maximum ('best') size
1053 bool wxRichTextObject::LayoutToBestSize(wxDC
& dc
, wxRichTextDrawingContext
& context
, wxRichTextBuffer
* buffer
,
1054 const wxRichTextAttr
& parentAttr
, const wxRichTextAttr
& attr
,
1055 const wxRect
& availableParentSpace
, const wxRect
& availableContainerSpace
,
1058 wxRect availableChildRect
= AdjustAvailableSpace(dc
, buffer
, parentAttr
, attr
, availableParentSpace
, availableContainerSpace
);
1059 wxRect originalAvailableRect
= availableChildRect
;
1060 Layout(dc
, context
, availableChildRect
, availableContainerSpace
, style
);
1062 wxSize maxSize
= GetMaxSize();
1064 // Don't ignore if maxSize.x is zero, since we need to redo the paragraph's lines
1066 if (!attr
.GetTextBoxAttr().GetWidth().IsValid() && maxSize
.x
< availableChildRect
.width
)
1068 // Redo the layout with a fixed, minimum size this time.
1069 Invalidate(wxRICHTEXT_ALL
);
1070 wxRichTextAttr
newAttr(attr
);
1071 newAttr
.GetTextBoxAttr().GetWidth().SetValue(maxSize
.x
, wxTEXT_ATTR_UNITS_PIXELS
);
1072 newAttr
.GetTextBoxAttr().GetWidth().SetPosition(wxTEXT_BOX_ATTR_POSITION_ABSOLUTE
);
1074 availableChildRect
= AdjustAvailableSpace(dc
, buffer
, parentAttr
, newAttr
, availableParentSpace
, availableContainerSpace
);
1076 // If a paragraph, align the whole paragraph.
1077 // Problem with this: if we're limited by a floating object, a line may be centered
1078 // w.r.t. the smaller resulting box rather than the actual available width.
1079 // FIXME: aligning whole paragraph not compatible with floating objects
1080 if (attr
.HasAlignment() && (!wxRichTextBuffer::GetFloatingLayoutMode() || (GetContainer()->GetFloatCollector() && !GetContainer()->GetFloatCollector()->HasFloats())))
1082 // centering, right-justification
1083 if (attr
.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE
)
1085 availableChildRect
.x
= (originalAvailableRect
.GetWidth() - availableChildRect
.GetWidth())/2 + availableChildRect
.x
;
1087 else if (attr
.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT
)
1089 availableChildRect
.x
= availableChildRect
.x
+ originalAvailableRect
.GetWidth() - availableChildRect
.GetWidth();
1093 Layout(dc
, context
, availableChildRect
, availableContainerSpace
, style
);
1107 // Move the object recursively, by adding the offset from old to new
1108 void wxRichTextObject::Move(const wxPoint
& pt
)
1115 * wxRichTextCompositeObject
1116 * This is the base for drawable objects.
1119 IMPLEMENT_CLASS(wxRichTextCompositeObject
, wxRichTextObject
)
1121 wxRichTextCompositeObject::wxRichTextCompositeObject(wxRichTextObject
* parent
):
1122 wxRichTextObject(parent
)
1126 wxRichTextCompositeObject::~wxRichTextCompositeObject()
1131 /// Get the nth child
1132 wxRichTextObject
* wxRichTextCompositeObject::GetChild(size_t n
) const
1134 wxASSERT ( n
< m_children
.GetCount() );
1136 return m_children
.Item(n
)->GetData();
1139 /// Append a child, returning the position
1140 size_t wxRichTextCompositeObject::AppendChild(wxRichTextObject
* child
)
1142 m_children
.Append(child
);
1143 child
->SetParent(this);
1144 return m_children
.GetCount() - 1;
1147 /// Insert the child in front of the given object, or at the beginning
1148 bool wxRichTextCompositeObject::InsertChild(wxRichTextObject
* child
, wxRichTextObject
* inFrontOf
)
1152 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(inFrontOf
);
1153 m_children
.Insert(node
, child
);
1156 m_children
.Insert(child
);
1157 child
->SetParent(this);
1162 /// Delete the child
1163 bool wxRichTextCompositeObject::RemoveChild(wxRichTextObject
* child
, bool deleteChild
)
1165 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(child
);
1168 wxRichTextObject
* obj
= node
->GetData();
1169 m_children
.Erase(node
);
1178 /// Delete all children
1179 bool wxRichTextCompositeObject::DeleteChildren()
1181 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1184 wxRichTextObjectList::compatibility_iterator oldNode
= node
;
1186 wxRichTextObject
* child
= node
->GetData();
1187 child
->Dereference(); // Only delete if reference count is zero
1189 node
= node
->GetNext();
1190 m_children
.Erase(oldNode
);
1196 /// Get the child count
1197 size_t wxRichTextCompositeObject::GetChildCount() const
1199 return m_children
.GetCount();
1203 void wxRichTextCompositeObject::Copy(const wxRichTextCompositeObject
& obj
)
1205 wxRichTextObject::Copy(obj
);
1209 wxRichTextObjectList::compatibility_iterator node
= obj
.m_children
.GetFirst();
1212 wxRichTextObject
* child
= node
->GetData();
1213 wxRichTextObject
* newChild
= child
->Clone();
1214 newChild
->SetParent(this);
1215 m_children
.Append(newChild
);
1217 node
= node
->GetNext();
1221 /// Hit-testing: returns a flag indicating hit test details, plus
1222 /// information about position
1223 int wxRichTextCompositeObject::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
1226 return wxRICHTEXT_HITTEST_NONE
;
1228 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1231 wxRichTextObject
* child
= node
->GetData();
1233 if (child
->IsShown() && child
->IsTopLevel() && (flags
& wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS
))
1235 // Just check if we hit the overall object
1236 int ret
= child
->wxRichTextObject::HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
1237 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
1240 else if (child
->IsShown())
1242 int ret
= child
->HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
1243 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
1247 node
= node
->GetNext();
1250 return wxRICHTEXT_HITTEST_NONE
;
1253 /// Finds the absolute position and row height for the given character position
1254 bool wxRichTextCompositeObject::FindPosition(wxDC
& dc
, wxRichTextDrawingContext
& context
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
1256 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1259 wxRichTextObject
* child
= node
->GetData();
1261 // Don't recurse if the child is a top-level object,
1262 // such as a text box, because the character position will no longer
1263 // apply. By definition, a top-level object has its own range of
1264 // character positions.
1265 if (!child
->IsTopLevel() && child
->FindPosition(dc
, context
, index
, pt
, height
, forceLineStart
))
1268 node
= node
->GetNext();
1275 void wxRichTextCompositeObject::CalculateRange(long start
, long& end
)
1277 long current
= start
;
1278 long lastEnd
= current
;
1286 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1289 wxRichTextObject
* child
= node
->GetData();
1292 child
->CalculateRange(current
, childEnd
);
1295 current
= childEnd
+ 1;
1297 node
= node
->GetNext();
1302 // A top-level object always has a range of size 1,
1303 // because its children don't count at this level.
1305 m_range
.SetRange(start
, start
);
1307 // An object with no children has zero length
1308 if (m_children
.GetCount() == 0)
1310 m_ownRange
.SetRange(0, lastEnd
);
1316 // An object with no children has zero length
1317 if (m_children
.GetCount() == 0)
1320 m_range
.SetRange(start
, end
);
1324 /// Delete range from layout.
1325 bool wxRichTextCompositeObject::DeleteRange(const wxRichTextRange
& range
)
1327 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1331 wxRichTextObject
* obj
= (wxRichTextObject
*) node
->GetData();
1332 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
1334 // Delete the range in each paragraph
1336 // When a chunk has been deleted, internally the content does not
1337 // now match the ranges.
1338 // However, so long as deletion is not done on the same object twice this is OK.
1339 // If you may delete content from the same object twice, recalculate
1340 // the ranges inbetween DeleteRange calls by calling CalculateRanges, and
1341 // adjust the range you're deleting accordingly.
1343 if (!obj
->GetRange().IsOutside(range
))
1345 // No need to delete within a top-level object; just removing this object will do fine
1346 if (!obj
->IsTopLevel())
1347 obj
->DeleteRange(range
);
1349 // Delete an empty object, or paragraph within this range.
1350 if (obj
->IsEmpty() ||
1351 (range
.GetStart() <= obj
->GetRange().GetStart() && range
.GetEnd() >= obj
->GetRange().GetEnd()))
1353 // An empty paragraph has length 1, so won't be deleted unless the
1354 // whole range is deleted.
1355 RemoveChild(obj
, true);
1365 /// Get any text in this object for the given range
1366 wxString
wxRichTextCompositeObject::GetTextForRange(const wxRichTextRange
& range
) const
1369 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1372 wxRichTextObject
* child
= node
->GetData();
1373 wxRichTextRange childRange
= range
;
1374 if (!child
->GetRange().IsOutside(range
))
1376 childRange
.LimitTo(child
->GetRange());
1378 wxString childText
= child
->GetTextForRange(childRange
);
1382 node
= node
->GetNext();
1388 /// Get the child object at the given character position
1389 wxRichTextObject
* wxRichTextCompositeObject::GetChildAtPosition(long pos
) const
1391 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1394 wxRichTextObject
* child
= node
->GetData();
1395 if (child
->GetRange().GetStart() == pos
)
1397 node
= node
->GetNext();
1402 /// Recursively merge all pieces that can be merged.
1403 bool wxRichTextCompositeObject::Defragment(wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
)
1405 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1408 wxRichTextObject
* child
= node
->GetData();
1409 if (range
== wxRICHTEXT_ALL
|| !child
->GetRange().IsOutside(range
))
1411 wxRichTextCompositeObject
* composite
= wxDynamicCast(child
, wxRichTextCompositeObject
);
1413 composite
->Defragment(context
);
1415 // Optimization: if there are no virtual attributes, we won't need to
1416 // to split objects in order to paint individually attributed chunks.
1417 // So only merge in this case.
1418 if (!context
.GetVirtualAttributesEnabled())
1420 if (node
->GetNext())
1422 wxRichTextObject
* nextChild
= node
->GetNext()->GetData();
1423 if (child
->CanMerge(nextChild
, context
) && child
->Merge(nextChild
, context
))
1425 nextChild
->Dereference();
1426 m_children
.Erase(node
->GetNext());
1429 node
= node
->GetNext();
1432 node
= node
->GetNext();
1436 // If we might have virtual attributes, we first see if we have to split
1437 // objects so that they may be painted with potential virtual attributes,
1438 // since text objects can only draw or measure with a single attributes object
1440 wxRichTextObject
* childAfterSplit
= child
;
1441 if (child
->CanSplit(context
))
1443 childAfterSplit
= child
->Split(context
);
1444 node
= m_children
.Find(childAfterSplit
);
1447 if (node
->GetNext())
1449 wxRichTextObject
* nextChild
= node
->GetNext()->GetData();
1451 // First split child and nextChild so we have smaller fragments to merge.
1452 // Then Merge only has to test per-object virtual attributes
1453 // because for an object with all the same sub-object attributes,
1454 // then any general virtual attributes should be merged with sub-objects by
1455 // the implementation.
1457 wxRichTextObject
* nextChildAfterSplit
= nextChild
;
1459 if (nextChildAfterSplit
->CanSplit(context
))
1460 nextChildAfterSplit
= nextChild
->Split(context
);
1462 bool splitNextChild
= nextChild
!= nextChildAfterSplit
;
1464 // See if we can merge this new fragment with (perhaps the first part of) the next object.
1465 // Note that we use nextChild because if we had split nextChild, the first object always
1466 // remains (and further parts are appended). However we must use childAfterSplit since
1467 // it's the last part of a possibly split child.
1469 if (childAfterSplit
->CanMerge(nextChild
, context
) && childAfterSplit
->Merge(nextChild
, context
))
1471 nextChild
->Dereference();
1472 m_children
.Erase(node
->GetNext());
1474 // Don't set node -- we'll see if we can merge again with the next
1475 // child. UNLESS we split this or the next child, in which case we know we have to
1476 // move on to the end of the next child.
1478 node
= m_children
.Find(nextChildAfterSplit
);
1483 node
= m_children
.Find(nextChildAfterSplit
); // start from the last object in the split
1485 node
= node
->GetNext();
1489 node
= node
->GetNext();
1493 node
= node
->GetNext();
1496 // Delete any remaining empty objects, but leave at least one empty object per composite object.
1497 if (GetChildCount() > 1)
1499 node
= m_children
.GetFirst();
1502 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
1503 wxRichTextObject
* child
= node
->GetData();
1504 if (range
== wxRICHTEXT_ALL
|| !child
->GetRange().IsOutside(range
))
1506 if (child
->IsEmpty())
1508 child
->Dereference();
1509 m_children
.Erase(node
);
1514 node
= node
->GetNext();
1521 /// Dump to output stream for debugging
1522 void wxRichTextCompositeObject::Dump(wxTextOutputStream
& stream
)
1524 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1527 wxRichTextObject
* child
= node
->GetData();
1528 child
->Dump(stream
);
1529 node
= node
->GetNext();
1533 /// Get/set the object size for the given range. Returns false if the range
1534 /// is invalid for this object.
1535 bool wxRichTextCompositeObject::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, const wxPoint
& position
, const wxSize
& parentSize
, wxArrayInt
* partialExtents
) const
1537 if (!range
.IsWithin(GetRange()))
1542 wxArrayInt childExtents
;
1549 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1552 wxRichTextObject
* child
= node
->GetData();
1553 if (!child
->GetRange().IsOutside(range
))
1555 // Floating objects have a zero size within the paragraph.
1556 if (child
->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode())
1561 if (partialExtents
->GetCount() > 0)
1562 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
1566 partialExtents
->Add(0 /* zero size */ + lastSize
);
1573 wxRichTextRange rangeToUse
= range
;
1574 rangeToUse
.LimitTo(child
->GetRange());
1575 if (child
->IsTopLevel())
1576 rangeToUse
= child
->GetOwnRange();
1578 int childDescent
= 0;
1580 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we're already cached the size,
1581 // but it's only going to be used after caching has taken place.
1582 if ((flags
& wxRICHTEXT_HEIGHT_ONLY
) && child
->GetCachedSize().y
!= 0)
1584 childDescent
= child
->GetDescent();
1585 childSize
= child
->GetCachedSize();
1587 sz
.y
= wxMax(sz
.y
, childSize
.y
);
1588 sz
.x
+= childSize
.x
;
1589 descent
= wxMax(descent
, childDescent
);
1591 else if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, context
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
), parentSize
, p
))
1593 sz
.y
= wxMax(sz
.y
, childSize
.y
);
1594 sz
.x
+= childSize
.x
;
1595 descent
= wxMax(descent
, childDescent
);
1597 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange() || child
->IsTopLevel()))
1599 child
->SetCachedSize(childSize
);
1600 child
->SetDescent(childDescent
);
1606 if (partialExtents
->GetCount() > 0)
1607 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
1612 for (i
= 0; i
< childExtents
.GetCount(); i
++)
1614 partialExtents
->Add(childExtents
[i
] + lastSize
);
1624 node
= node
->GetNext();
1630 // Invalidate the buffer. With no argument, invalidates whole buffer.
1631 void wxRichTextCompositeObject::Invalidate(const wxRichTextRange
& invalidRange
)
1633 wxRichTextObject::Invalidate(invalidRange
);
1635 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1638 wxRichTextObject
* child
= node
->GetData();
1639 if (invalidRange
!= wxRICHTEXT_ALL
&& invalidRange
!= wxRICHTEXT_NONE
&& child
->GetRange().IsOutside(invalidRange
))
1643 else if (child
->IsTopLevel())
1645 if (wxRichTextBuffer::GetFloatingLayoutMode() && child
->IsFloating() && GetBuffer()->GetFloatCollector() && GetBuffer()->GetFloatCollector()->HasFloat(child
))
1647 // Don't invalidate subhierarchy if we've already been laid out
1651 if (invalidRange
== wxRICHTEXT_NONE
)
1652 child
->Invalidate(wxRICHTEXT_NONE
);
1654 child
->Invalidate(wxRICHTEXT_ALL
); // All children must be invalidated if within parent range
1658 child
->Invalidate(invalidRange
);
1659 node
= node
->GetNext();
1663 // Move the object recursively, by adding the offset from old to new
1664 void wxRichTextCompositeObject::Move(const wxPoint
& pt
)
1666 wxPoint oldPos
= GetPosition();
1668 wxPoint offset
= pt
- oldPos
;
1670 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1673 wxRichTextObject
* child
= node
->GetData();
1674 wxPoint childPos
= child
->GetPosition() + offset
;
1675 child
->Move(childPos
);
1676 node
= node
->GetNext();
1682 * wxRichTextParagraphLayoutBox
1683 * This box knows how to lay out paragraphs.
1686 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraphLayoutBox
, wxRichTextCompositeObject
)
1688 wxRichTextParagraphLayoutBox::wxRichTextParagraphLayoutBox(wxRichTextObject
* parent
):
1689 wxRichTextCompositeObject(parent
)
1694 wxRichTextParagraphLayoutBox::~wxRichTextParagraphLayoutBox()
1696 if (m_floatCollector
)
1698 delete m_floatCollector
;
1699 m_floatCollector
= NULL
;
1703 /// Initialize the object.
1704 void wxRichTextParagraphLayoutBox::Init()
1708 // For now, assume is the only box and has no initial size.
1709 m_range
= wxRichTextRange(0, -1);
1710 m_ownRange
= wxRichTextRange(0, -1);
1712 m_invalidRange
= wxRICHTEXT_ALL
;
1714 m_partialParagraph
= false;
1715 m_floatCollector
= NULL
;
1718 void wxRichTextParagraphLayoutBox::Clear()
1722 if (m_floatCollector
)
1723 delete m_floatCollector
;
1724 m_floatCollector
= NULL
;
1725 m_partialParagraph
= false;
1729 void wxRichTextParagraphLayoutBox::Copy(const wxRichTextParagraphLayoutBox
& obj
)
1733 wxRichTextCompositeObject::Copy(obj
);
1735 m_partialParagraph
= obj
.m_partialParagraph
;
1736 m_defaultAttributes
= obj
.m_defaultAttributes
;
1739 // Gather information about floating objects; only gather floats for those paragraphs that
1740 // will not be formatted again due to optimization, after which floats will be gathered per-paragraph
1742 bool wxRichTextParagraphLayoutBox::UpdateFloatingObjects(const wxRect
& availableRect
, wxRichTextObject
* untilObj
)
1744 if (m_floatCollector
!= NULL
)
1745 delete m_floatCollector
;
1746 m_floatCollector
= new wxRichTextFloatCollector(availableRect
);
1747 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1748 // Only gather floats up to the point we'll start formatting paragraphs.
1749 while (untilObj
&& node
&& node
->GetData() != untilObj
)
1751 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
1752 wxASSERT (child
!= NULL
);
1754 m_floatCollector
->CollectFloat(child
);
1755 node
= node
->GetNext();
1761 // Returns the style sheet associated with the overall buffer.
1762 wxRichTextStyleSheet
* wxRichTextParagraphLayoutBox::GetStyleSheet() const
1764 return GetBuffer() ? GetBuffer()->GetStyleSheet() : (wxRichTextStyleSheet
*) NULL
;
1767 // Get the number of floating objects at this level
1768 int wxRichTextParagraphLayoutBox::GetFloatingObjectCount() const
1770 if (m_floatCollector
)
1771 return m_floatCollector
->GetFloatingObjectCount();
1776 // Get a list of floating objects
1777 bool wxRichTextParagraphLayoutBox::GetFloatingObjects(wxRichTextObjectList
& objects
) const
1779 if (m_floatCollector
)
1781 return m_floatCollector
->GetFloatingObjects(objects
);
1788 void wxRichTextParagraphLayoutBox::UpdateRanges()
1792 start
= GetRange().GetStart();
1794 CalculateRange(start
, end
);
1798 int wxRichTextParagraphLayoutBox::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
1801 return wxRICHTEXT_HITTEST_NONE
;
1803 int ret
= wxRICHTEXT_HITTEST_NONE
;
1804 if (wxRichTextBuffer::GetFloatingLayoutMode() && m_floatCollector
&& (flags
& wxRICHTEXT_HITTEST_NO_FLOATING_OBJECTS
) == 0)
1805 ret
= m_floatCollector
->HitTest(dc
, context
, pt
, textPosition
, obj
, flags
);
1807 if (ret
== wxRICHTEXT_HITTEST_NONE
)
1808 return wxRichTextCompositeObject::HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
1816 /// Draw the floating objects
1817 void wxRichTextParagraphLayoutBox::DrawFloats(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
1819 if (wxRichTextBuffer::GetFloatingLayoutMode() && m_floatCollector
)
1820 m_floatCollector
->Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
1823 void wxRichTextParagraphLayoutBox::MoveAnchoredObjectToParagraph(wxRichTextParagraph
* from
, wxRichTextParagraph
* to
, wxRichTextObject
* obj
)
1828 from
->RemoveChild(obj
);
1829 to
->AppendChild(obj
);
1833 bool wxRichTextParagraphLayoutBox::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
1838 wxRect
thisRect(GetPosition(), GetCachedSize());
1840 wxRichTextAttr
attr(GetAttributes());
1841 context
.ApplyVirtualAttributes(attr
, this);
1844 if (selection
.IsValid() && GetParentContainer() != this && selection
.WithinSelection(GetRange().GetStart(), GetParentContainer()))
1845 flags
|= wxRICHTEXT_DRAW_SELECTED
;
1847 // Don't draw guidelines if at top level
1848 int theseFlags
= flags
;
1850 theseFlags
&= ~wxRICHTEXT_DRAW_GUIDELINES
;
1851 DrawBoxAttributes(dc
, GetBuffer(), attr
, thisRect
, theseFlags
);
1853 if (wxRichTextBuffer::GetFloatingLayoutMode())
1854 DrawFloats(dc
, context
, range
, selection
, rect
, descent
, style
);
1856 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1859 wxRichTextObject
* child
= node
->GetData();
1861 if (child
&& !child
->GetRange().IsOutside(range
))
1863 wxRect
childRect(child
->GetPosition(), child
->GetCachedSize());
1864 wxRichTextRange childRange
= range
;
1865 if (child
->IsTopLevel())
1867 childRange
= child
->GetOwnRange();
1870 if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) == 0) && childRect
.GetTop() > rect
.GetBottom())
1875 else if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) == 0) && childRect
.GetBottom() < rect
.GetTop())
1880 child
->Draw(dc
, context
, childRange
, selection
, rect
, descent
, style
);
1883 node
= node
->GetNext();
1888 /// Lay the item out
1889 bool wxRichTextParagraphLayoutBox::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
)
1891 SetPosition(rect
.GetPosition());
1896 wxRect availableSpace
;
1897 bool formatRect
= (style
& wxRICHTEXT_LAYOUT_SPECIFIED_RECT
) == wxRICHTEXT_LAYOUT_SPECIFIED_RECT
;
1899 wxRichTextAttr
attr(GetAttributes());
1900 context
.ApplyVirtualAttributes(attr
, this);
1902 // If only laying out a specific area, the passed rect has a different meaning:
1903 // the visible part of the buffer. This is used in wxRichTextCtrl::OnSize,
1904 // so that during a size, only the visible part will be relaid out, or
1905 // it would take too long causing flicker. As an approximation, we assume that
1906 // everything up to the start of the visible area is laid out correctly.
1909 wxRect
rect2(0, 0, rect
.width
, rect
.height
);
1910 availableSpace
= GetAvailableContentArea(dc
, context
, rect2
);
1912 // Invalidate the part of the buffer from the first visible line
1913 // to the end. If other parts of the buffer are currently invalid,
1914 // then they too will be taken into account if they are above
1915 // the visible point.
1917 wxRichTextLine
* line
= GetLineAtYPosition(rect
.y
);
1919 startPos
= line
->GetAbsoluteRange().GetStart();
1921 Invalidate(wxRichTextRange(startPos
, GetOwnRange().GetEnd()));
1925 availableSpace
= GetAvailableContentArea(dc
, context
, rect
);
1928 // Fix the width if we're at the top level
1930 attr
.GetTextBoxAttr().GetWidth().SetValue(rect
.GetWidth(), wxTEXT_ATTR_UNITS_PIXELS
);
1932 int leftMargin
, rightMargin
, topMargin
, bottomMargin
;
1933 wxRichTextObject::GetTotalMargin(dc
, GetBuffer(), attr
, leftMargin
, rightMargin
,
1934 topMargin
, bottomMargin
);
1939 // The maximum paragraph maximum width, so we can set the overall maximum width for this object
1940 int maxMaxWidth
= 0;
1942 // The maximum paragraph minimum width, so we can set the overall minimum width for this object
1943 int maxMinWidth
= 0;
1945 // If we have vertical alignment, we must recalculate everything.
1946 bool hasVerticalAlignment
= (attr
.GetTextBoxAttr().HasVerticalAlignment() &&
1947 (attr
.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP
));
1949 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1951 bool layoutAll
= true;
1953 // Get invalid range, rounding to paragraph start/end.
1954 wxRichTextRange invalidRange
= GetInvalidRange(true);
1956 if (invalidRange
== wxRICHTEXT_NONE
&& !formatRect
)
1959 if (invalidRange
== wxRICHTEXT_ALL
|| hasVerticalAlignment
)
1961 else // If we know what range is affected, start laying out from that point on.
1962 if (invalidRange
.GetStart() >= GetOwnRange().GetStart())
1964 wxRichTextParagraph
* firstParagraph
= GetParagraphAtPosition(invalidRange
.GetStart());
1967 wxRichTextObjectList::compatibility_iterator firstNode
= m_children
.Find(firstParagraph
);
1968 wxRichTextObjectList::compatibility_iterator previousNode
;
1970 previousNode
= firstNode
->GetPrevious();
1975 wxRichTextParagraph
* previousParagraph
= wxDynamicCast(previousNode
->GetData(), wxRichTextParagraph
);
1976 availableSpace
.y
= previousParagraph
->GetPosition().y
+ previousParagraph
->GetCachedSize().y
;
1979 // Now we're going to start iterating from the first affected paragraph.
1987 // Gather information about only those floating objects that will not be formatted,
1988 // after which floats will be gathered per-paragraph during layout.
1989 if (wxRichTextBuffer::GetFloatingLayoutMode())
1990 UpdateFloatingObjects(availableSpace
, node
? node
->GetData() : (wxRichTextObject
*) NULL
);
1992 // A way to force speedy rest-of-buffer layout (the 'else' below)
1993 bool forceQuickLayout
= false;
1995 // First get the size of the paragraphs we won't be laying out
1996 wxRichTextObjectList::compatibility_iterator n
= m_children
.GetFirst();
1997 while (n
&& n
!= node
)
1999 wxRichTextParagraph
* child
= wxDynamicCast(n
->GetData(), wxRichTextParagraph
);
2002 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
2003 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
2004 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
2011 // Assume this box only contains paragraphs
2013 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2014 // Unsure if this is needed
2015 // wxCHECK_MSG( child, false, wxT("Unknown object in layout") );
2017 if (child
&& child
->IsShown())
2019 // TODO: what if the child hasn't been laid out (e.g. involved in Undo) but still has 'old' lines
2020 if ( !forceQuickLayout
&&
2022 child
->GetLines().IsEmpty() ||
2023 !child
->GetRange().IsOutside(invalidRange
)) )
2025 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
2026 // lays out the object again using the minimum size
2027 child
->LayoutToBestSize(dc
, context
, GetBuffer(),
2028 attr
, child
->GetAttributes(), availableSpace
, rect
, style
&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT
);
2030 // Layout must set the cached size
2031 availableSpace
.y
+= child
->GetCachedSize().y
;
2032 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
2033 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
2034 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
2036 // If we're just formatting the visible part of the buffer,
2037 // and we're now past the bottom of the window, and we don't have any
2038 // floating objects (since they may cause wrapping to change for the rest of the
2039 // the buffer), start quick layout.
2040 if (!hasVerticalAlignment
&& formatRect
&& child
->GetPosition().y
> rect
.GetBottom() && GetFloatingObjectCount() == 0)
2041 forceQuickLayout
= true;
2045 // We're outside the immediately affected range, so now let's just
2046 // move everything up or down. This assumes that all the children have previously
2047 // been laid out and have wrapped line lists associated with them.
2048 // TODO: check all paragraphs before the affected range.
2050 int inc
= availableSpace
.y
- child
->GetPosition().y
;
2054 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2057 if (child
->GetLines().GetCount() == 0)
2059 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
2060 // lays out the object again using the minimum size
2061 child
->LayoutToBestSize(dc
, context
, GetBuffer(),
2062 attr
, child
->GetAttributes(), availableSpace
, rect
, style
&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT
);
2064 //child->Layout(dc, availableChildRect, style);
2067 child
->Move(wxPoint(child
->GetPosition().x
, child
->GetPosition().y
+ inc
));
2069 availableSpace
.y
+= child
->GetCachedSize().y
;
2070 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
2071 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
2072 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
2075 node
= node
->GetNext();
2081 node
= node
->GetNext();
2084 node
= m_children
.GetLast();
2085 if (node
&& node
->GetData()->IsShown())
2087 wxRichTextObject
* child
= node
->GetData();
2088 maxHeight
= child
->GetPosition().y
- (GetPosition().y
+ topMargin
) + child
->GetCachedSize().y
;
2091 maxHeight
= 0; // topMargin + bottomMargin;
2093 // Check the bottom edge of any floating object
2094 if (wxRichTextBuffer::GetFloatingLayoutMode() && GetFloatCollector() && GetFloatCollector()->HasFloats())
2096 int bottom
= GetFloatCollector()->GetLastRectBottom();
2097 if (bottom
> maxHeight
)
2101 if (attr
.GetTextBoxAttr().GetSize().GetWidth().IsValid())
2103 wxRect r
= AdjustAvailableSpace(dc
, GetBuffer(), wxRichTextAttr() /* not used */, attr
, parentRect
, parentRect
);
2104 int w
= r
.GetWidth();
2106 // Convert external to content rect
2107 w
= w
- leftMargin
- rightMargin
;
2108 maxWidth
= wxMax(maxWidth
, w
);
2109 maxMaxWidth
= wxMax(maxMaxWidth
, w
);
2113 // TODO: Make sure the layout box's position reflects
2114 // the position of the children, but without
2115 // breaking layout of a box within a paragraph.
2118 // TODO: (also in para layout) should set the
2119 // object's size to an absolute one if specified,
2120 // but if not specified, calculate it from content.
2122 // We need to add back the margins etc.
2124 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2125 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxWidth
, maxHeight
));
2126 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2127 SetCachedSize(marginRect
.GetSize());
2130 // The maximum size is the greatest of all maximum widths for all paragraphs.
2132 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2133 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxMaxWidth
, maxHeight
)); // Actually max height is a lie, we can't know it
2134 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2135 SetMaxSize(marginRect
.GetSize());
2138 // The minimum size is the greatest of all minimum widths for all paragraphs.
2140 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2141 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxMinWidth
, maxHeight
)); // Actually max height is a lie, we can't know it
2142 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2143 SetMinSize(marginRect
.GetSize());
2146 if (attr
.GetTextBoxAttr().HasVerticalAlignment() &&
2147 (attr
.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP
))
2150 int leftOverSpace
= availableSpace
.height
- topMargin
- bottomMargin
- maxHeight
;
2151 if (leftOverSpace
> 0)
2153 if (attr
.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_CENTRE
)
2155 yOffset
= (leftOverSpace
/2);
2157 else if (attr
.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_BOTTOM
)
2159 yOffset
= leftOverSpace
;
2163 // Move all the children to vertically align the content
2164 // This doesn't take into account floating objects, unfortunately.
2167 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2170 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2172 child
->Move(wxPoint(child
->GetPosition().x
, child
->GetPosition().y
+ yOffset
));
2174 node
= node
->GetNext();
2179 m_invalidRange
= wxRICHTEXT_NONE
;
2184 /// Get/set the size for the given range.
2185 bool wxRichTextParagraphLayoutBox::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, const wxPoint
& position
, const wxSize
& parentSize
, wxArrayInt
* WXUNUSED(partialExtents
)) const
2189 wxRichTextObjectList::compatibility_iterator startPara
= wxRichTextObjectList::compatibility_iterator();
2190 wxRichTextObjectList::compatibility_iterator endPara
= wxRichTextObjectList::compatibility_iterator();
2192 // First find the first paragraph whose starting position is within the range.
2193 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2196 // child is a paragraph
2197 wxRichTextObject
* child
= node
->GetData();
2198 const wxRichTextRange
& r
= child
->GetRange();
2200 if (r
.GetStart() <= range
.GetStart() && r
.GetEnd() >= range
.GetStart())
2206 node
= node
->GetNext();
2209 // Next find the last paragraph containing part of the range
2210 node
= m_children
.GetFirst();
2213 // child is a paragraph
2214 wxRichTextObject
* child
= node
->GetData();
2215 const wxRichTextRange
& r
= child
->GetRange();
2217 if (r
.GetStart() <= range
.GetEnd() && r
.GetEnd() >= range
.GetEnd())
2223 node
= node
->GetNext();
2226 if (!startPara
|| !endPara
)
2229 // Now we can add up the sizes
2230 for (node
= startPara
; node
; node
= node
->GetNext())
2232 // child is a paragraph
2233 wxRichTextObject
* child
= node
->GetData();
2234 const wxRichTextRange
& childRange
= child
->GetRange();
2235 wxRichTextRange rangeToFind
= range
;
2236 rangeToFind
.LimitTo(childRange
);
2238 if (child
->IsTopLevel())
2239 rangeToFind
= child
->GetOwnRange();
2243 int childDescent
= 0;
2244 child
->GetRangeSize(rangeToFind
, childSize
, childDescent
, dc
, context
, flags
, position
, parentSize
);
2246 descent
= wxMax(childDescent
, descent
);
2248 sz
.x
= wxMax(sz
.x
, childSize
.x
);
2249 sz
.y
+= childSize
.y
;
2251 if (node
== endPara
)
2260 /// Get the paragraph at the given position
2261 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphAtPosition(long pos
, bool caretPosition
) const
2266 // First find the first paragraph whose starting position is within the range.
2267 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2270 // child is a paragraph
2271 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2272 // wxASSERT (child != NULL);
2276 // Return first child in buffer if position is -1
2280 if (child
->GetRange().Contains(pos
))
2284 node
= node
->GetNext();
2289 /// Get the line at the given position
2290 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineAtPosition(long pos
, bool caretPosition
) const
2295 // First find the first paragraph whose starting position is within the range.
2296 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2299 wxRichTextObject
* obj
= (wxRichTextObject
*) node
->GetData();
2300 if (obj
->GetRange().Contains(pos
))
2302 // child is a paragraph
2303 wxRichTextParagraph
* child
= wxDynamicCast(obj
, wxRichTextParagraph
);
2304 // wxASSERT (child != NULL);
2308 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2311 wxRichTextLine
* line
= node2
->GetData();
2313 wxRichTextRange range
= line
->GetAbsoluteRange();
2315 if (range
.Contains(pos
) ||
2317 // If the position is end-of-paragraph, then return the last line of
2318 // of the paragraph.
2319 ((range
.GetEnd() == child
->GetRange().GetEnd()-1) && (pos
== child
->GetRange().GetEnd())))
2322 node2
= node2
->GetNext();
2327 node
= node
->GetNext();
2330 int lineCount
= GetLineCount();
2332 return GetLineForVisibleLineNumber(lineCount
-1);
2337 /// Get the line at the given y pixel position, or the last line.
2338 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineAtYPosition(int y
) const
2340 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2343 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2344 // wxASSERT (child != NULL);
2348 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2351 wxRichTextLine
* line
= node2
->GetData();
2353 wxRect
rect(line
->GetRect());
2355 if (y
<= rect
.GetBottom())
2358 node2
= node2
->GetNext();
2362 node
= node
->GetNext();
2366 int lineCount
= GetLineCount();
2368 return GetLineForVisibleLineNumber(lineCount
-1);
2373 /// Get the number of visible lines
2374 int wxRichTextParagraphLayoutBox::GetLineCount() const
2378 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2381 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2382 // wxASSERT (child != NULL);
2385 count
+= child
->GetLines().GetCount();
2387 node
= node
->GetNext();
2393 /// Get the paragraph for a given line
2394 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphForLine(wxRichTextLine
* line
) const
2396 return GetParagraphAtPosition(line
->GetAbsoluteRange().GetStart());
2399 /// Get the line size at the given position
2400 wxSize
wxRichTextParagraphLayoutBox::GetLineSizeAtPosition(long pos
, bool caretPosition
) const
2402 wxRichTextLine
* line
= GetLineAtPosition(pos
, caretPosition
);
2405 return line
->GetSize();
2408 return wxSize(0, 0);
2412 /// Convenience function to add a paragraph of text
2413 wxRichTextRange
wxRichTextParagraphLayoutBox::AddParagraph(const wxString
& text
, wxRichTextAttr
* paraStyle
)
2415 // Don't use the base style, just the default style, and the base style will
2416 // be combined at display time.
2417 // Divide into paragraph and character styles.
2419 wxRichTextAttr defaultCharStyle
;
2420 wxRichTextAttr defaultParaStyle
;
2422 // If the default style is a named paragraph style, don't apply any character formatting
2423 // to the initial text string.
2424 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2426 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2428 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2431 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2433 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2434 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2436 wxRichTextParagraph
* para
= new wxRichTextParagraph(text
, this, pStyle
, cStyle
);
2437 para
->GetAttributes().GetTextBoxAttr().Reset();
2443 return para
->GetRange();
2446 /// Adds multiple paragraphs, based on newlines.
2447 wxRichTextRange
wxRichTextParagraphLayoutBox::AddParagraphs(const wxString
& text
, wxRichTextAttr
* paraStyle
)
2449 // Don't use the base style, just the default style, and the base style will
2450 // be combined at display time.
2451 // Divide into paragraph and character styles.
2453 wxRichTextAttr defaultCharStyle
;
2454 wxRichTextAttr defaultParaStyle
;
2456 // If the default style is a named paragraph style, don't apply any character formatting
2457 // to the initial text string.
2458 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2460 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2462 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2465 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2467 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2468 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2470 wxRichTextParagraph
* firstPara
= NULL
;
2471 wxRichTextParagraph
* lastPara
= NULL
;
2473 wxRichTextRange
range(-1, -1);
2476 size_t len
= text
.length();
2478 wxRichTextParagraph
* para
= new wxRichTextParagraph(wxEmptyString
, this, pStyle
, cStyle
);
2479 para
->GetAttributes().GetTextBoxAttr().Reset();
2488 wxChar ch
= text
[i
];
2489 if (ch
== wxT('\n') || ch
== wxT('\r'))
2493 wxRichTextPlainText
* plainText
= (wxRichTextPlainText
*) para
->GetChildren().GetFirst()->GetData();
2494 plainText
->SetText(line
);
2496 para
= new wxRichTextParagraph(wxEmptyString
, this, pStyle
, cStyle
);
2497 para
->GetAttributes().GetTextBoxAttr().Reset();
2502 line
= wxEmptyString
;
2513 wxRichTextPlainText
* plainText
= (wxRichTextPlainText
*) para
->GetChildren().GetFirst()->GetData();
2514 plainText
->SetText(line
);
2519 return wxRichTextRange(firstPara
->GetRange().GetStart(), lastPara
->GetRange().GetEnd());
2522 /// Convenience function to add an image
2523 wxRichTextRange
wxRichTextParagraphLayoutBox::AddImage(const wxImage
& image
, wxRichTextAttr
* paraStyle
)
2525 // Don't use the base style, just the default style, and the base style will
2526 // be combined at display time.
2527 // Divide into paragraph and character styles.
2529 wxRichTextAttr defaultCharStyle
;
2530 wxRichTextAttr defaultParaStyle
;
2532 // If the default style is a named paragraph style, don't apply any character formatting
2533 // to the initial text string.
2534 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2536 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2538 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2541 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2543 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2544 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2546 wxRichTextParagraph
* para
= new wxRichTextParagraph(this, pStyle
);
2547 para
->GetAttributes().GetTextBoxAttr().Reset();
2549 para
->AppendChild(new wxRichTextImage(image
, this, cStyle
));
2553 return para
->GetRange();
2557 /// Insert fragment into this box at the given position. If partialParagraph is true,
2558 /// it is assumed that the last (or only) paragraph is just a piece of data with no paragraph
2561 bool wxRichTextParagraphLayoutBox::InsertFragment(long position
, wxRichTextParagraphLayoutBox
& fragment
)
2563 // First, find the first paragraph whose starting position is within the range.
2564 wxRichTextParagraph
* para
= GetParagraphAtPosition(position
);
2567 wxRichTextAttr originalAttr
= para
->GetAttributes();
2569 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(para
);
2571 // Now split at this position, returning the object to insert the new
2572 // ones in front of.
2573 wxRichTextObject
* nextObject
= para
->SplitAt(position
);
2575 // Special case: partial paragraph, just one paragraph. Might be a small amount of
2576 // text, for example, so let's optimize.
2578 if (fragment
.GetPartialParagraph() && fragment
.GetChildren().GetCount() == 1)
2580 // Add the first para to this para...
2581 wxRichTextObjectList::compatibility_iterator firstParaNode
= fragment
.GetChildren().GetFirst();
2585 // Iterate through the fragment paragraph inserting the content into this paragraph.
2586 wxRichTextParagraph
* firstPara
= wxDynamicCast(firstParaNode
->GetData(), wxRichTextParagraph
);
2587 wxASSERT (firstPara
!= NULL
);
2589 wxRichTextObjectList::compatibility_iterator objectNode
= firstPara
->GetChildren().GetFirst();
2592 wxRichTextObject
* newObj
= objectNode
->GetData()->Clone();
2597 para
->AppendChild(newObj
);
2601 // Insert before nextObject
2602 para
->InsertChild(newObj
, nextObject
);
2605 objectNode
= objectNode
->GetNext();
2612 // Procedure for inserting a fragment consisting of a number of
2615 // 1. Remove and save the content that's after the insertion point, for adding
2616 // back once we've added the fragment.
2617 // 2. Add the content from the first fragment paragraph to the current
2619 // 3. Add remaining fragment paragraphs after the current paragraph.
2620 // 4. Add back the saved content from the first paragraph. If partialParagraph
2621 // is true, add it to the last paragraph added and not a new one.
2623 // 1. Remove and save objects after split point.
2624 wxList savedObjects
;
2626 para
->MoveToList(nextObject
, savedObjects
);
2628 // 2. Add the content from the 1st fragment paragraph.
2629 wxRichTextObjectList::compatibility_iterator firstParaNode
= fragment
.GetChildren().GetFirst();
2633 wxRichTextParagraph
* firstPara
= wxDynamicCast(firstParaNode
->GetData(), wxRichTextParagraph
);
2634 wxASSERT(firstPara
!= NULL
);
2636 if (!(fragment
.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
))
2637 para
->SetAttributes(firstPara
->GetAttributes());
2639 // Save empty paragraph attributes for appending later
2640 // These are character attributes deliberately set for a new paragraph. Without this,
2641 // we couldn't pass default attributes when appending a new paragraph.
2642 wxRichTextAttr emptyParagraphAttributes
;
2644 wxRichTextObjectList::compatibility_iterator objectNode
= firstPara
->GetChildren().GetFirst();
2646 if (objectNode
&& firstPara
->GetChildren().GetCount() == 1 && objectNode
->GetData()->IsEmpty())
2647 emptyParagraphAttributes
= objectNode
->GetData()->GetAttributes();
2651 wxRichTextObject
* newObj
= objectNode
->GetData()->Clone();
2654 para
->AppendChild(newObj
);
2656 objectNode
= objectNode
->GetNext();
2659 // 3. Add remaining fragment paragraphs after the current paragraph.
2660 wxRichTextObjectList::compatibility_iterator nextParagraphNode
= node
->GetNext();
2661 wxRichTextObject
* nextParagraph
= NULL
;
2662 if (nextParagraphNode
)
2663 nextParagraph
= nextParagraphNode
->GetData();
2665 wxRichTextObjectList::compatibility_iterator i
= fragment
.GetChildren().GetFirst()->GetNext();
2666 wxRichTextParagraph
* finalPara
= para
;
2668 bool needExtraPara
= (!i
|| !fragment
.GetPartialParagraph());
2670 // If there was only one paragraph, we need to insert a new one.
2673 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2674 wxASSERT( para
!= NULL
);
2676 finalPara
= (wxRichTextParagraph
*) para
->Clone();
2679 InsertChild(finalPara
, nextParagraph
);
2681 AppendChild(finalPara
);
2686 // If there was only one paragraph, or we have full paragraphs in our fragment,
2687 // we need to insert a new one.
2690 finalPara
= new wxRichTextParagraph
;
2693 InsertChild(finalPara
, nextParagraph
);
2695 AppendChild(finalPara
);
2698 // 4. Add back the remaining content.
2702 finalPara
->MoveFromList(savedObjects
);
2704 // Ensure there's at least one object
2705 if (finalPara
->GetChildCount() == 0)
2707 wxRichTextPlainText
* text
= new wxRichTextPlainText(wxEmptyString
);
2708 text
->SetAttributes(emptyParagraphAttributes
);
2710 finalPara
->AppendChild(text
);
2714 if ((fragment
.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
) && firstPara
)
2715 finalPara
->SetAttributes(firstPara
->GetAttributes());
2716 else if (finalPara
&& finalPara
!= para
)
2717 finalPara
->SetAttributes(originalAttr
);
2725 wxRichTextObjectList::compatibility_iterator i
= fragment
.GetChildren().GetFirst();
2728 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2729 wxASSERT( para
!= NULL
);
2731 AppendChild(para
->Clone());
2740 /// Make a copy of the fragment corresponding to the given range, putting it in 'fragment'.
2741 /// If there was an incomplete paragraph at the end, partialParagraph is set to true.
2742 bool wxRichTextParagraphLayoutBox::CopyFragment(const wxRichTextRange
& range
, wxRichTextParagraphLayoutBox
& fragment
)
2744 wxRichTextObjectList::compatibility_iterator i
= GetChildren().GetFirst();
2747 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2748 wxASSERT( para
!= NULL
);
2750 if (!para
->GetRange().IsOutside(range
))
2752 fragment
.AppendChild(para
->Clone());
2757 // Now top and tail the first and last paragraphs in our new fragment (which might be the same).
2758 if (!fragment
.IsEmpty())
2760 wxRichTextParagraph
* firstPara
= wxDynamicCast(fragment
.GetChildren().GetFirst()->GetData(), wxRichTextParagraph
);
2761 wxASSERT( firstPara
!= NULL
);
2763 wxRichTextParagraph
* lastPara
= wxDynamicCast(fragment
.GetChildren().GetLast()->GetData(), wxRichTextParagraph
);
2764 wxASSERT( lastPara
!= NULL
);
2766 if (!firstPara
|| !lastPara
)
2769 bool isFragment
= (range
.GetEnd() < lastPara
->GetRange().GetEnd());
2771 long firstPos
= firstPara
->GetRange().GetStart();
2773 // Adjust for renumbering from zero
2774 wxRichTextRange
topTailRange(range
.GetStart() - firstPos
, range
.GetEnd() - firstPos
);
2777 fragment
.CalculateRange(0, end
);
2779 // Chop off the start of the paragraph
2780 if (topTailRange
.GetStart() > 0)
2782 wxRichTextRange
r(0, topTailRange
.GetStart()-1);
2783 firstPara
->DeleteRange(r
);
2785 // Make sure the numbering is correct
2786 fragment
.CalculateRange(0, end
);
2788 // Now, we've deleted some positions, so adjust the range
2790 topTailRange
.SetStart(range
.GetLength());
2791 topTailRange
.SetEnd(fragment
.GetOwnRange().GetEnd());
2795 topTailRange
.SetStart(range
.GetLength());
2796 topTailRange
.SetEnd(fragment
.GetOwnRange().GetEnd());
2799 if (topTailRange
.GetStart() < lastPara
->GetRange().GetEnd())
2801 lastPara
->DeleteRange(topTailRange
);
2803 // Make sure the numbering is correct
2805 fragment
.CalculateRange(0, end
);
2807 // We only have part of a paragraph at the end
2808 fragment
.SetPartialParagraph(true);
2812 // We have a partial paragraph (don't save last new paragraph marker)
2813 // or complete paragraph
2814 fragment
.SetPartialParagraph(isFragment
);
2821 /// Given a position, get the number of the visible line (potentially many to a paragraph),
2822 /// starting from zero at the start of the buffer.
2823 long wxRichTextParagraphLayoutBox::GetVisibleLineNumber(long pos
, bool caretPosition
, bool startOfLine
) const
2830 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2833 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2834 // wxASSERT( child != NULL );
2838 if (child
->GetRange().Contains(pos
))
2840 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2843 wxRichTextLine
* line
= node2
->GetData();
2844 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
2846 if (lineRange
.Contains(pos
) || pos
== lineRange
.GetStart())
2848 // If the caret is displayed at the end of the previous wrapped line,
2849 // we want to return the line it's _displayed_ at (not the actual line
2850 // containing the position).
2851 if (lineRange
.GetStart() == pos
&& !startOfLine
&& child
->GetRange().GetStart() != pos
)
2852 return lineCount
- 1;
2859 node2
= node2
->GetNext();
2861 // If we didn't find it in the lines, it must be
2862 // the last position of the paragraph. So return the last line.
2866 lineCount
+= child
->GetLines().GetCount();
2869 node
= node
->GetNext();
2876 /// Given a line number, get the corresponding wxRichTextLine object.
2877 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineForVisibleLineNumber(long lineNumber
) const
2881 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2884 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2885 // wxASSERT(child != NULL);
2889 if (lineNumber
< (int) (child
->GetLines().GetCount() + lineCount
))
2891 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2894 wxRichTextLine
* line
= node2
->GetData();
2896 if (lineCount
== lineNumber
)
2901 node2
= node2
->GetNext();
2905 lineCount
+= child
->GetLines().GetCount();
2908 node
= node
->GetNext();
2915 /// Delete range from layout.
2916 bool wxRichTextParagraphLayoutBox::DeleteRange(const wxRichTextRange
& range
)
2918 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2920 wxRichTextParagraph
* firstPara
= NULL
;
2923 wxRichTextParagraph
* obj
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2924 // wxASSERT (obj != NULL);
2926 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
2930 // Delete the range in each paragraph
2932 if (!obj
->GetRange().IsOutside(range
))
2934 // Deletes the content of this object within the given range
2935 obj
->DeleteRange(range
);
2937 wxRichTextRange thisRange
= obj
->GetRange();
2938 wxRichTextAttr thisAttr
= obj
->GetAttributes();
2940 // If the whole paragraph is within the range to delete,
2941 // delete the whole thing.
2942 if (range
.GetStart() <= thisRange
.GetStart() && range
.GetEnd() >= thisRange
.GetEnd())
2944 // Delete the whole object
2945 RemoveChild(obj
, true);
2948 else if (!firstPara
)
2951 // If the range includes the paragraph end, we need to join this
2952 // and the next paragraph.
2953 if (range
.GetEnd() <= thisRange
.GetEnd())
2955 // We need to move the objects from the next paragraph
2956 // to this paragraph
2958 wxRichTextParagraph
* nextParagraph
= NULL
;
2959 if ((range
.GetEnd() < thisRange
.GetEnd()) && obj
)
2960 nextParagraph
= obj
;
2963 // We're ending at the end of the paragraph, so merge the _next_ paragraph.
2965 nextParagraph
= wxDynamicCast(next
->GetData(), wxRichTextParagraph
);
2968 bool applyFinalParagraphStyle
= firstPara
&& nextParagraph
&& nextParagraph
!= firstPara
;
2970 wxRichTextAttr nextParaAttr
;
2971 if (applyFinalParagraphStyle
)
2973 // Special case when deleting the end of a paragraph - use _this_ paragraph's style,
2974 // not the next one.
2975 if (range
.GetStart() == range
.GetEnd() && range
.GetStart() == thisRange
.GetEnd())
2976 nextParaAttr
= thisAttr
;
2978 nextParaAttr
= nextParagraph
->GetAttributes();
2981 if (firstPara
&& nextParagraph
&& firstPara
!= nextParagraph
)
2983 // Move the objects to the previous para
2984 wxRichTextObjectList::compatibility_iterator node1
= nextParagraph
->GetChildren().GetFirst();
2988 wxRichTextObject
* obj1
= node1
->GetData();
2990 firstPara
->AppendChild(obj1
);
2992 wxRichTextObjectList::compatibility_iterator next1
= node1
->GetNext();
2993 nextParagraph
->GetChildren().Erase(node1
);
2998 // Delete the paragraph
2999 RemoveChild(nextParagraph
, true);
3002 // Avoid empty paragraphs
3003 if (firstPara
&& firstPara
->GetChildren().GetCount() == 0)
3005 wxRichTextPlainText
* text
= new wxRichTextPlainText(wxEmptyString
);
3006 firstPara
->AppendChild(text
);
3009 if (applyFinalParagraphStyle
)
3010 firstPara
->SetAttributes(nextParaAttr
);
3023 /// Get any text in this object for the given range
3024 wxString
wxRichTextParagraphLayoutBox::GetTextForRange(const wxRichTextRange
& range
) const
3028 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3031 wxRichTextObject
* child
= node
->GetData();
3032 if (!child
->GetRange().IsOutside(range
))
3034 wxRichTextRange childRange
= range
;
3035 childRange
.LimitTo(child
->GetRange());
3037 wxString childText
= child
->GetTextForRange(childRange
);
3041 if ((childRange
.GetEnd() == child
->GetRange().GetEnd()) && node
->GetNext())
3046 node
= node
->GetNext();
3052 /// Get all the text
3053 wxString
wxRichTextParagraphLayoutBox::GetText() const
3055 return GetTextForRange(GetOwnRange());
3058 /// Get the paragraph by number
3059 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphAtLine(long paragraphNumber
) const
3061 if ((size_t) paragraphNumber
>= GetChildCount())
3064 return (wxRichTextParagraph
*) GetChild((size_t) paragraphNumber
);
3067 /// Get the length of the paragraph
3068 int wxRichTextParagraphLayoutBox::GetParagraphLength(long paragraphNumber
) const
3070 wxRichTextParagraph
* para
= GetParagraphAtLine(paragraphNumber
);
3072 return para
->GetRange().GetLength() - 1; // don't include newline
3077 /// Get the text of the paragraph
3078 wxString
wxRichTextParagraphLayoutBox::GetParagraphText(long paragraphNumber
) const
3080 wxRichTextParagraph
* para
= GetParagraphAtLine(paragraphNumber
);
3082 return para
->GetTextForRange(para
->GetRange());
3084 return wxEmptyString
;
3087 /// Convert zero-based line column and paragraph number to a position.
3088 long wxRichTextParagraphLayoutBox::XYToPosition(long x
, long y
) const
3090 wxRichTextParagraph
* para
= GetParagraphAtLine(y
);
3093 return para
->GetRange().GetStart() + x
;
3099 /// Convert zero-based position to line column and paragraph number
3100 bool wxRichTextParagraphLayoutBox::PositionToXY(long pos
, long* x
, long* y
) const
3102 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
);
3106 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3109 wxRichTextObject
* child
= node
->GetData();
3113 node
= node
->GetNext();
3117 *x
= pos
- para
->GetRange().GetStart();
3125 /// Get the leaf object in a paragraph at this position.
3126 /// Given a line number, get the corresponding wxRichTextLine object.
3127 wxRichTextObject
* wxRichTextParagraphLayoutBox::GetLeafObjectAtPosition(long position
) const
3129 wxRichTextParagraph
* para
= GetParagraphAtPosition(position
);
3132 wxRichTextObjectList::compatibility_iterator node
= para
->GetChildren().GetFirst();
3136 wxRichTextObject
* child
= node
->GetData();
3137 if (child
->GetRange().Contains(position
))
3140 node
= node
->GetNext();
3142 if (position
== para
->GetRange().GetEnd() && para
->GetChildCount() > 0)
3143 return para
->GetChildren().GetLast()->GetData();
3148 /// Set character or paragraph text attributes: apply character styles only to immediate text nodes
3149 bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange
& range
, const wxRichTextAttr
& style
, int flags
)
3151 bool characterStyle
= false;
3152 bool paragraphStyle
= false;
3154 if (style
.IsCharacterStyle())
3155 characterStyle
= true;
3156 if (style
.IsParagraphStyle())
3157 paragraphStyle
= true;
3159 wxRichTextBuffer
* buffer
= GetBuffer();
3161 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
3162 bool applyMinimal
= ((flags
& wxRICHTEXT_SETSTYLE_OPTIMIZE
) != 0);
3163 bool parasOnly
= ((flags
& wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY
) != 0);
3164 bool charactersOnly
= ((flags
& wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY
) != 0);
3165 bool resetExistingStyle
= ((flags
& wxRICHTEXT_SETSTYLE_RESET
) != 0);
3166 bool removeStyle
= ((flags
& wxRICHTEXT_SETSTYLE_REMOVE
) != 0);
3168 // Apply paragraph style first, if any
3169 wxRichTextAttr
wholeStyle(style
);
3171 if (!removeStyle
&& wholeStyle
.HasParagraphStyleName() && buffer
->GetStyleSheet())
3173 wxRichTextParagraphStyleDefinition
* def
= buffer
->GetStyleSheet()->FindParagraphStyle(wholeStyle
.GetParagraphStyleName());
3175 wxRichTextApplyStyle(wholeStyle
, def
->GetStyleMergedWithBase(buffer
->GetStyleSheet()));
3178 // Limit the attributes to be set to the content to only character attributes.
3179 wxRichTextAttr
characterAttributes(wholeStyle
);
3180 characterAttributes
.SetFlags(characterAttributes
.GetFlags() & (wxTEXT_ATTR_CHARACTER
));
3182 if (!removeStyle
&& characterAttributes
.HasCharacterStyleName() && buffer
->GetStyleSheet())
3184 wxRichTextCharacterStyleDefinition
* def
= buffer
->GetStyleSheet()->FindCharacterStyle(characterAttributes
.GetCharacterStyleName());
3186 wxRichTextApplyStyle(characterAttributes
, def
->GetStyleMergedWithBase(buffer
->GetStyleSheet()));
3189 // If we are associated with a control, make undoable; otherwise, apply immediately
3192 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3194 wxRichTextAction
* action
= NULL
;
3196 if (haveControl
&& withUndo
)
3198 action
= new wxRichTextAction(NULL
, _("Change Style"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
3199 action
->SetRange(range
);
3200 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3203 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3206 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3207 // wxASSERT (para != NULL);
3209 if (para
&& para
->GetChildCount() > 0)
3211 // Stop searching if we're beyond the range of interest
3212 if (para
->GetRange().GetStart() > range
.GetEnd())
3215 if (!para
->GetRange().IsOutside(range
))
3217 // We'll be using a copy of the paragraph to make style changes,
3218 // not updating the buffer directly.
3219 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3221 if (haveControl
&& withUndo
)
3223 newPara
= new wxRichTextParagraph(*para
);
3224 action
->GetNewParagraphs().AppendChild(newPara
);
3226 // Also store the old ones for Undo
3227 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3232 // If we're specifying paragraphs only, then we really mean character formatting
3233 // to be included in the paragraph style
3234 if ((paragraphStyle
|| parasOnly
) && !charactersOnly
)
3238 // Removes the given style from the paragraph
3239 wxRichTextRemoveStyle(newPara
->GetAttributes(), style
);
3241 else if (resetExistingStyle
)
3242 newPara
->GetAttributes() = wholeStyle
;
3247 // Only apply attributes that will make a difference to the combined
3248 // style as seen on the display
3249 wxRichTextAttr
combinedAttr(para
->GetCombinedAttributes(true));
3250 wxRichTextApplyStyle(newPara
->GetAttributes(), wholeStyle
, & combinedAttr
);
3253 wxRichTextApplyStyle(newPara
->GetAttributes(), wholeStyle
);
3257 // When applying paragraph styles dynamically, don't change the text objects' attributes
3258 // since they will computed as needed. Only apply the character styling if it's _only_
3259 // character styling. This policy is subject to change and might be put under user control.
3261 // Hm. we might well be applying a mix of paragraph and character styles, in which
3262 // case we _do_ want to apply character styles regardless of what para styles are set.
3263 // But if we're applying a paragraph style, which has some character attributes, but
3264 // we only want the paragraphs to hold this character style, then we _don't_ want to
3265 // apply the character style. So we need to be able to choose.
3267 if (!parasOnly
&& (characterStyle
|charactersOnly
) && range
.GetStart() != newPara
->GetRange().GetEnd())
3269 wxRichTextRange
childRange(range
);
3270 childRange
.LimitTo(newPara
->GetRange());
3272 // Find the starting position and if necessary split it so
3273 // we can start applying a different style.
3274 // TODO: check that the style actually changes or is different
3275 // from style outside of range
3276 wxRichTextObject
* firstObject
wxDUMMY_INITIALIZE(NULL
);
3277 wxRichTextObject
* lastObject
wxDUMMY_INITIALIZE(NULL
);
3279 if (childRange
.GetStart() == newPara
->GetRange().GetStart())
3280 firstObject
= newPara
->GetChildren().GetFirst()->GetData();
3282 firstObject
= newPara
->SplitAt(range
.GetStart());
3284 // Increment by 1 because we're apply the style one _after_ the split point
3285 long splitPoint
= childRange
.GetEnd();
3286 if (splitPoint
!= newPara
->GetRange().GetEnd())
3290 if (splitPoint
== newPara
->GetRange().GetEnd())
3291 lastObject
= newPara
->GetChildren().GetLast()->GetData();
3293 // lastObject is set as a side-effect of splitting. It's
3294 // returned as the object before the new object.
3295 (void) newPara
->SplitAt(splitPoint
, & lastObject
);
3297 wxASSERT(firstObject
!= NULL
);
3298 wxASSERT(lastObject
!= NULL
);
3300 if (!firstObject
|| !lastObject
)
3303 wxRichTextObjectList::compatibility_iterator firstNode
= newPara
->GetChildren().Find(firstObject
);
3304 wxRichTextObjectList::compatibility_iterator lastNode
= newPara
->GetChildren().Find(lastObject
);
3306 wxASSERT(firstNode
);
3309 wxRichTextObjectList::compatibility_iterator node2
= firstNode
;
3313 wxRichTextObject
* child
= node2
->GetData();
3317 // Removes the given style from the paragraph
3318 wxRichTextRemoveStyle(child
->GetAttributes(), style
);
3320 else if (resetExistingStyle
)
3322 // Preserve the URL as it's not really a formatting style but a property of the object
3324 if (child
->GetAttributes().HasURL() && !characterAttributes
.HasURL())
3325 url
= child
->GetAttributes().GetURL();
3327 child
->GetAttributes() = characterAttributes
;
3330 child
->GetAttributes().SetURL(url
);
3336 // Only apply attributes that will make a difference to the combined
3337 // style as seen on the display
3338 wxRichTextAttr
combinedAttr(newPara
->GetCombinedAttributes(child
->GetAttributes(), true));
3339 wxRichTextApplyStyle(child
->GetAttributes(), characterAttributes
, & combinedAttr
);
3342 wxRichTextApplyStyle(child
->GetAttributes(), characterAttributes
);
3345 if (node2
== lastNode
)
3348 node2
= node2
->GetNext();
3354 node
= node
->GetNext();
3357 // Do action, or delay it until end of batch.
3358 if (haveControl
&& withUndo
)
3359 buffer
->SubmitAction(action
);
3364 // Just change the attributes for this single object.
3365 void wxRichTextParagraphLayoutBox::SetStyle(wxRichTextObject
* obj
, const wxRichTextAttr
& textAttr
, int flags
)
3367 wxRichTextBuffer
* buffer
= GetBuffer();
3368 bool withUndo
= flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
;
3369 bool resetExistingStyle
= ((flags
& wxRICHTEXT_SETSTYLE_RESET
) != 0);
3370 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3372 wxRichTextAction
*action
= NULL
;
3373 wxRichTextAttr newAttr
= obj
->GetAttributes();
3374 if (resetExistingStyle
)
3377 newAttr
.Apply(textAttr
);
3379 if (haveControl
&& withUndo
)
3381 action
= new wxRichTextAction(NULL
, _("Change Object Style"), wxRICHTEXT_CHANGE_ATTRIBUTES
, buffer
, obj
->GetContainer(), buffer
->GetRichTextCtrl());
3382 action
->SetRange(obj
->GetRange().FromInternal());
3383 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3384 action
->MakeObject(obj
);
3386 action
->GetAttributes() = newAttr
;
3389 obj
->GetAttributes() = newAttr
;
3391 if (haveControl
&& withUndo
)
3392 buffer
->SubmitAction(action
);
3395 /// Get the text attributes for this position.
3396 bool wxRichTextParagraphLayoutBox::GetStyle(long position
, wxRichTextAttr
& style
)
3398 return DoGetStyle(position
, style
, true);
3401 bool wxRichTextParagraphLayoutBox::GetUncombinedStyle(long position
, wxRichTextAttr
& style
)
3403 return DoGetStyle(position
, style
, false);
3406 /// Implementation helper for GetStyle. If combineStyles is true, combine base, paragraph and
3407 /// context attributes.
3408 bool wxRichTextParagraphLayoutBox::DoGetStyle(long position
, wxRichTextAttr
& style
, bool combineStyles
)
3410 wxRichTextObject
* obj
wxDUMMY_INITIALIZE(NULL
);
3412 if (style
.IsParagraphStyle())
3414 obj
= GetParagraphAtPosition(position
);
3419 // Start with the base style
3420 style
= GetAttributes();
3421 style
.GetTextBoxAttr().Reset();
3423 // Apply the paragraph style
3424 wxRichTextApplyStyle(style
, obj
->GetAttributes());
3427 style
= obj
->GetAttributes();
3434 obj
= GetLeafObjectAtPosition(position
);
3439 wxRichTextParagraph
* para
= wxDynamicCast(obj
->GetParent(), wxRichTextParagraph
);
3440 style
= para
? para
->GetCombinedAttributes(obj
->GetAttributes()) : obj
->GetAttributes();
3443 style
= obj
->GetAttributes();
3451 static bool wxHasStyle(long flags
, long style
)
3453 return (flags
& style
) != 0;
3456 /// Combines 'style' with 'currentStyle' for the purpose of summarising the attributes of a range of
3458 bool wxRichTextParagraphLayoutBox::CollectStyle(wxRichTextAttr
& currentStyle
, const wxRichTextAttr
& style
, wxRichTextAttr
& clashingAttr
, wxRichTextAttr
& absentAttr
)
3460 currentStyle
.CollectCommonAttributes(style
, clashingAttr
, absentAttr
);
3465 /// Get the combined style for a range - if any attribute is different within the range,
3466 /// that attribute is not present within the flags.
3467 /// *** Note that this is not recursive, and so assumes that content inside a paragraph is not itself
3469 bool wxRichTextParagraphLayoutBox::GetStyleForRange(const wxRichTextRange
& range
, wxRichTextAttr
& style
)
3471 style
= wxRichTextAttr();
3473 wxRichTextAttr clashingAttrPara
, clashingAttrChar
;
3474 wxRichTextAttr absentAttrPara
, absentAttrChar
;
3476 wxRichTextObjectList::compatibility_iterator node
= GetChildren().GetFirst();
3479 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3480 if (para
&& !(para
->GetRange().GetStart() > range
.GetEnd() || para
->GetRange().GetEnd() < range
.GetStart()))
3482 if (para
->GetChildren().GetCount() == 0)
3484 wxRichTextAttr paraStyle
= para
->GetCombinedAttributes(true /* use box attributes */);
3486 CollectStyle(style
, paraStyle
, clashingAttrPara
, absentAttrPara
);
3490 wxRichTextRange
paraRange(para
->GetRange());
3491 paraRange
.LimitTo(range
);
3493 // First collect paragraph attributes only
3494 wxRichTextAttr paraStyle
= para
->GetCombinedAttributes();
3495 paraStyle
.SetFlags(paraStyle
.GetFlags() & wxTEXT_ATTR_PARAGRAPH
);
3496 CollectStyle(style
, paraStyle
, clashingAttrPara
, absentAttrPara
);
3498 wxRichTextObjectList::compatibility_iterator childNode
= para
->GetChildren().GetFirst();
3502 wxRichTextObject
* child
= childNode
->GetData();
3503 if (!(child
->GetRange().GetStart() > range
.GetEnd() || child
->GetRange().GetEnd() < range
.GetStart()))
3505 wxRichTextAttr childStyle
= para
->GetCombinedAttributes(child
->GetAttributes(), true /* include box attributes */);
3507 // Now collect character attributes only
3508 childStyle
.SetFlags(childStyle
.GetFlags() & wxTEXT_ATTR_CHARACTER
);
3510 CollectStyle(style
, childStyle
, clashingAttrChar
, absentAttrChar
);
3513 childNode
= childNode
->GetNext();
3517 node
= node
->GetNext();
3522 /// Set default style
3523 bool wxRichTextParagraphLayoutBox::SetDefaultStyle(const wxRichTextAttr
& style
)
3525 m_defaultAttributes
= style
;
3529 /// Test if this whole range has character attributes of the specified kind. If any
3530 /// of the attributes are different within the range, the test fails. You
3531 /// can use this to implement, for example, bold button updating. style must have
3532 /// flags indicating which attributes are of interest.
3533 bool wxRichTextParagraphLayoutBox::HasCharacterAttributes(const wxRichTextRange
& range
, const wxRichTextAttr
& style
) const
3536 int matchingCount
= 0;
3538 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3541 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3542 // wxASSERT (para != NULL);
3546 // Stop searching if we're beyond the range of interest
3547 if (para
->GetRange().GetStart() > range
.GetEnd())
3548 return foundCount
== matchingCount
&& foundCount
!= 0;
3550 if (!para
->GetRange().IsOutside(range
))
3552 wxRichTextObjectList::compatibility_iterator node2
= para
->GetChildren().GetFirst();
3556 wxRichTextObject
* child
= node2
->GetData();
3557 // Allow for empty string if no buffer
3558 wxRichTextRange childRange
= child
->GetRange();
3559 if (childRange
.GetLength() == 0 && GetRange().GetLength() == 1)
3560 childRange
.SetEnd(childRange
.GetEnd()+1);
3562 if (!childRange
.IsOutside(range
) && wxDynamicCast(child
, wxRichTextPlainText
))
3565 wxRichTextAttr textAttr
= para
->GetCombinedAttributes(child
->GetAttributes());
3567 if (textAttr
.EqPartial(style
, false /* strong test - attributes must be valid in both objects */))
3571 node2
= node2
->GetNext();
3576 node
= node
->GetNext();
3579 return foundCount
== matchingCount
&& foundCount
!= 0;
3582 /// Test if this whole range has paragraph attributes of the specified kind. If any
3583 /// of the attributes are different within the range, the test fails. You
3584 /// can use this to implement, for example, centering button updating. style must have
3585 /// flags indicating which attributes are of interest.
3586 bool wxRichTextParagraphLayoutBox::HasParagraphAttributes(const wxRichTextRange
& range
, const wxRichTextAttr
& style
) const
3589 int matchingCount
= 0;
3591 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3594 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3595 // wxASSERT (para != NULL);
3599 // Stop searching if we're beyond the range of interest
3600 if (para
->GetRange().GetStart() > range
.GetEnd())
3601 return foundCount
== matchingCount
&& foundCount
!= 0;
3603 if (!para
->GetRange().IsOutside(range
))
3605 wxRichTextAttr textAttr
= GetAttributes();
3606 // Apply the paragraph style
3607 wxRichTextApplyStyle(textAttr
, para
->GetAttributes());
3610 if (textAttr
.EqPartial(style
, false /* strong test */))
3615 node
= node
->GetNext();
3617 return foundCount
== matchingCount
&& foundCount
!= 0;
3620 void wxRichTextParagraphLayoutBox::PrepareContent(wxRichTextParagraphLayoutBox
& container
)
3622 wxRichTextBuffer
* buffer
= GetBuffer();
3623 if (buffer
&& buffer
->GetRichTextCtrl())
3624 buffer
->GetRichTextCtrl()->PrepareContent(container
);
3627 /// Set character or paragraph properties
3628 bool wxRichTextParagraphLayoutBox::SetProperties(const wxRichTextRange
& range
, const wxRichTextProperties
& properties
, int flags
)
3630 wxRichTextBuffer
* buffer
= GetBuffer();
3632 bool withUndo
= ((flags
& wxRICHTEXT_SETPROPERTIES_WITH_UNDO
) != 0);
3633 bool parasOnly
= ((flags
& wxRICHTEXT_SETPROPERTIES_PARAGRAPHS_ONLY
) != 0);
3634 bool charactersOnly
= ((flags
& wxRICHTEXT_SETPROPERTIES_CHARACTERS_ONLY
) != 0);
3635 bool resetExistingProperties
= ((flags
& wxRICHTEXT_SETPROPERTIES_RESET
) != 0);
3636 bool removeProperties
= ((flags
& wxRICHTEXT_SETPROPERTIES_REMOVE
) != 0);
3638 // If we are associated with a control, make undoable; otherwise, apply immediately
3641 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3643 wxRichTextAction
* action
= NULL
;
3645 if (haveControl
&& withUndo
)
3647 action
= new wxRichTextAction(NULL
, _("Change Properties"), wxRICHTEXT_CHANGE_PROPERTIES
, buffer
, this, buffer
->GetRichTextCtrl());
3648 action
->SetRange(range
);
3649 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3652 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3655 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3656 // wxASSERT (para != NULL);
3658 if (para
&& para
->GetChildCount() > 0)
3660 // Stop searching if we're beyond the range of interest
3661 if (para
->GetRange().GetStart() > range
.GetEnd())
3664 if (!para
->GetRange().IsOutside(range
))
3666 // We'll be using a copy of the paragraph to make style changes,
3667 // not updating the buffer directly.
3668 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3670 if (haveControl
&& withUndo
)
3672 newPara
= new wxRichTextParagraph(*para
);
3673 action
->GetNewParagraphs().AppendChild(newPara
);
3675 // Also store the old ones for Undo
3676 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3683 if (removeProperties
)
3685 // Removes the given style from the paragraph
3687 newPara
->GetProperties().RemoveProperties(properties
);
3689 else if (resetExistingProperties
)
3690 newPara
->GetProperties() = properties
;
3692 newPara
->GetProperties().MergeProperties(properties
);
3695 // When applying paragraph styles dynamically, don't change the text objects' attributes
3696 // since they will computed as needed. Only apply the character styling if it's _only_
3697 // character styling. This policy is subject to change and might be put under user control.
3699 // Hm. we might well be applying a mix of paragraph and character styles, in which
3700 // case we _do_ want to apply character styles regardless of what para styles are set.
3701 // But if we're applying a paragraph style, which has some character attributes, but
3702 // we only want the paragraphs to hold this character style, then we _don't_ want to
3703 // apply the character style. So we need to be able to choose.
3705 if (!parasOnly
&& charactersOnly
&& range
.GetStart() != newPara
->GetRange().GetEnd())
3707 wxRichTextRange
childRange(range
);
3708 childRange
.LimitTo(newPara
->GetRange());
3710 // Find the starting position and if necessary split it so
3711 // we can start applying different properties.
3712 // TODO: check that the properties actually change or are different
3713 // from properties outside of range
3714 wxRichTextObject
* firstObject
wxDUMMY_INITIALIZE(NULL
);
3715 wxRichTextObject
* lastObject
wxDUMMY_INITIALIZE(NULL
);
3717 if (childRange
.GetStart() == newPara
->GetRange().GetStart())
3718 firstObject
= newPara
->GetChildren().GetFirst()->GetData();
3720 firstObject
= newPara
->SplitAt(range
.GetStart());
3722 // Increment by 1 because we're apply the style one _after_ the split point
3723 long splitPoint
= childRange
.GetEnd();
3724 if (splitPoint
!= newPara
->GetRange().GetEnd())
3728 if (splitPoint
== newPara
->GetRange().GetEnd())
3729 lastObject
= newPara
->GetChildren().GetLast()->GetData();
3731 // lastObject is set as a side-effect of splitting. It's
3732 // returned as the object before the new object.
3733 (void) newPara
->SplitAt(splitPoint
, & lastObject
);
3735 wxASSERT(firstObject
!= NULL
);
3736 wxASSERT(lastObject
!= NULL
);
3738 if (!firstObject
|| !lastObject
)
3741 wxRichTextObjectList::compatibility_iterator firstNode
= newPara
->GetChildren().Find(firstObject
);
3742 wxRichTextObjectList::compatibility_iterator lastNode
= newPara
->GetChildren().Find(lastObject
);
3744 wxASSERT(firstNode
);
3747 wxRichTextObjectList::compatibility_iterator node2
= firstNode
;
3751 wxRichTextObject
* child
= node2
->GetData();
3753 if (removeProperties
)
3755 // Removes the given properties from the paragraph
3756 child
->GetProperties().RemoveProperties(properties
);
3758 else if (resetExistingProperties
)
3759 child
->GetProperties() = properties
;
3762 child
->GetProperties().MergeProperties(properties
);
3765 if (node2
== lastNode
)
3768 node2
= node2
->GetNext();
3774 node
= node
->GetNext();
3777 // Do action, or delay it until end of batch.
3778 if (haveControl
&& withUndo
)
3779 buffer
->SubmitAction(action
);
3784 void wxRichTextParagraphLayoutBox::Reset()
3788 wxRichTextBuffer
* buffer
= GetBuffer();
3789 if (buffer
&& buffer
->GetRichTextCtrl())
3791 wxRichTextEvent
event(wxEVT_RICHTEXT_BUFFER_RESET
, buffer
->GetRichTextCtrl()->GetId());
3792 event
.SetEventObject(buffer
->GetRichTextCtrl());
3793 event
.SetContainer(this);
3795 buffer
->SendEvent(event
, true);
3798 AddParagraph(wxEmptyString
);
3800 PrepareContent(*this);
3802 InvalidateHierarchy(wxRICHTEXT_ALL
);
3805 /// Invalidate the buffer. With no argument, invalidates whole buffer.
3806 void wxRichTextParagraphLayoutBox::Invalidate(const wxRichTextRange
& invalidRange
)
3808 wxRichTextCompositeObject::Invalidate(invalidRange
);
3810 DoInvalidate(invalidRange
);
3813 // Do the (in)validation for this object only
3814 void wxRichTextParagraphLayoutBox::DoInvalidate(const wxRichTextRange
& invalidRange
)
3816 if (invalidRange
== wxRICHTEXT_ALL
)
3818 m_invalidRange
= wxRICHTEXT_ALL
;
3820 // Already invalidating everything
3821 else if (m_invalidRange
== wxRICHTEXT_ALL
)
3826 if ((invalidRange
.GetStart() < m_invalidRange
.GetStart()) || m_invalidRange
.GetStart() == -1)
3827 m_invalidRange
.SetStart(invalidRange
.GetStart());
3828 if (invalidRange
.GetEnd() > m_invalidRange
.GetEnd())
3829 m_invalidRange
.SetEnd(invalidRange
.GetEnd());
3833 // Do the (in)validation both up and down the hierarchy
3834 void wxRichTextParagraphLayoutBox::InvalidateHierarchy(const wxRichTextRange
& invalidRange
)
3836 Invalidate(invalidRange
);
3838 if (invalidRange
!= wxRICHTEXT_NONE
)
3840 // Now go up the hierarchy
3841 wxRichTextObject
* thisObj
= this;
3842 wxRichTextObject
* p
= GetParent();
3845 wxRichTextParagraphLayoutBox
* l
= wxDynamicCast(p
, wxRichTextParagraphLayoutBox
);
3847 l
->DoInvalidate(thisObj
->GetRange());
3855 /// Get invalid range, rounding to entire paragraphs if argument is true.
3856 wxRichTextRange
wxRichTextParagraphLayoutBox::GetInvalidRange(bool wholeParagraphs
) const
3858 if (m_invalidRange
== wxRICHTEXT_ALL
|| m_invalidRange
== wxRICHTEXT_NONE
)
3859 return m_invalidRange
;
3861 wxRichTextRange range
= m_invalidRange
;
3863 if (wholeParagraphs
)
3865 wxRichTextParagraph
* para1
= GetParagraphAtPosition(range
.GetStart());
3867 range
.SetStart(para1
->GetRange().GetStart());
3869 // FIXME: be more intelligent about this. Check if we have floating objects
3870 // before the end of the range. But it's not clear how we can in general
3871 // tell where it's safe to stop laying out.
3872 // Anyway, this code is central to efficiency when laying in floating mode.
3873 if (!wxRichTextBuffer::GetFloatingLayoutMode())
3875 wxRichTextParagraph
* para2
= GetParagraphAtPosition(range
.GetEnd());
3877 range
.SetEnd(para2
->GetRange().GetEnd());
3880 // Floating layout means that all children should be laid out,
3881 // because we can't tell how the whole buffer will be affected.
3882 range
.SetEnd(GetOwnRange().GetEnd());
3887 /// Apply the style sheet to the buffer, for example if the styles have changed.
3888 bool wxRichTextParagraphLayoutBox::ApplyStyleSheet(wxRichTextStyleSheet
* styleSheet
)
3890 wxASSERT(styleSheet
!= NULL
);
3896 wxRichTextAttr
attr(GetBasicStyle());
3897 if (GetBasicStyle().HasParagraphStyleName())
3899 wxRichTextParagraphStyleDefinition
* paraDef
= styleSheet
->FindParagraphStyle(GetBasicStyle().GetParagraphStyleName());
3902 attr
.Apply(paraDef
->GetStyleMergedWithBase(styleSheet
));
3903 SetBasicStyle(attr
);
3908 if (GetBasicStyle().HasCharacterStyleName())
3910 wxRichTextCharacterStyleDefinition
* charDef
= styleSheet
->FindCharacterStyle(GetBasicStyle().GetCharacterStyleName());
3913 attr
.Apply(charDef
->GetStyleMergedWithBase(styleSheet
));
3914 SetBasicStyle(attr
);
3919 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3922 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3923 // wxASSERT (para != NULL);
3927 // Combine paragraph and list styles. If there is a list style in the original attributes,
3928 // the current indentation overrides anything else and is used to find the item indentation.
3929 // Also, for applying paragraph styles, consider having 2 modes: (1) we merge with what we have,
3930 // thereby taking into account all user changes, (2) reset the style completely (except for indentation/list
3931 // exception as above).
3932 // Problem: when changing from one list style to another, there's a danger that the level info will get lost.
3933 // So when changing a list style interactively, could retrieve level based on current style, then
3934 // set appropriate indent and apply new style.
3938 if (para
->GetAttributes().HasOutlineLevel())
3939 outline
= para
->GetAttributes().GetOutlineLevel();
3940 if (para
->GetAttributes().HasBulletNumber())
3941 num
= para
->GetAttributes().GetBulletNumber();
3943 if (!para
->GetAttributes().GetParagraphStyleName().IsEmpty() && !para
->GetAttributes().GetListStyleName().IsEmpty())
3945 int currentIndent
= para
->GetAttributes().GetLeftIndent();
3947 wxRichTextParagraphStyleDefinition
* paraDef
= styleSheet
->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
3948 wxRichTextListStyleDefinition
* listDef
= styleSheet
->FindListStyle(para
->GetAttributes().GetListStyleName());
3949 if (paraDef
&& !listDef
)
3951 para
->GetAttributes() = paraDef
->GetStyleMergedWithBase(styleSheet
);
3954 else if (listDef
&& !paraDef
)
3956 // Set overall style defined for the list style definition
3957 para
->GetAttributes() = listDef
->GetStyleMergedWithBase(styleSheet
);
3959 // Apply the style for this level
3960 wxRichTextApplyStyle(para
->GetAttributes(), * listDef
->GetLevelAttributes(listDef
->FindLevelForIndent(currentIndent
)));
3963 else if (listDef
&& paraDef
)
3965 // Combines overall list style, style for level, and paragraph style
3966 para
->GetAttributes() = listDef
->CombineWithParagraphStyle(currentIndent
, paraDef
->GetStyleMergedWithBase(styleSheet
));
3970 else if (para
->GetAttributes().GetParagraphStyleName().IsEmpty() && !para
->GetAttributes().GetListStyleName().IsEmpty())
3972 int currentIndent
= para
->GetAttributes().GetLeftIndent();
3974 wxRichTextListStyleDefinition
* listDef
= styleSheet
->FindListStyle(para
->GetAttributes().GetListStyleName());
3976 // Overall list definition style
3977 para
->GetAttributes() = listDef
->GetStyleMergedWithBase(styleSheet
);
3979 // Style for this level
3980 wxRichTextApplyStyle(para
->GetAttributes(), * listDef
->GetLevelAttributes(listDef
->FindLevelForIndent(currentIndent
)));
3984 else if (!para
->GetAttributes().GetParagraphStyleName().IsEmpty() && para
->GetAttributes().GetListStyleName().IsEmpty())
3986 wxRichTextParagraphStyleDefinition
* def
= styleSheet
->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
3989 para
->GetAttributes() = def
->GetStyleMergedWithBase(styleSheet
);
3995 para
->GetAttributes().SetOutlineLevel(outline
);
3997 para
->GetAttributes().SetBulletNumber(num
);
4000 node
= node
->GetNext();
4002 return foundCount
!= 0;
4006 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
4008 wxRichTextBuffer
* buffer
= GetBuffer();
4009 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
4011 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
4012 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
4013 bool specifyLevel
= ((flags
& wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL
) != 0);
4014 bool renumber
= ((flags
& wxRICHTEXT_SETSTYLE_RENUMBER
) != 0);
4016 // Current number, if numbering
4019 wxASSERT (!specifyLevel
|| (specifyLevel
&& (specifiedLevel
>= 0)));
4021 // If we are associated with a control, make undoable; otherwise, apply immediately
4024 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
4026 wxRichTextAction
* action
= NULL
;
4028 if (haveControl
&& withUndo
)
4030 action
= new wxRichTextAction(NULL
, _("Change List Style"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
4031 action
->SetRange(range
);
4032 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
4035 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
4038 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
4039 // wxASSERT (para != NULL);
4041 if (para
&& para
->GetChildCount() > 0)
4043 // Stop searching if we're beyond the range of interest
4044 if (para
->GetRange().GetStart() > range
.GetEnd())
4047 if (!para
->GetRange().IsOutside(range
))
4049 // We'll be using a copy of the paragraph to make style changes,
4050 // not updating the buffer directly.
4051 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
4053 if (haveControl
&& withUndo
)
4055 newPara
= new wxRichTextParagraph(*para
);
4056 action
->GetNewParagraphs().AppendChild(newPara
);
4058 // Also store the old ones for Undo
4059 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
4066 int thisIndent
= newPara
->GetAttributes().GetLeftIndent();
4067 int thisLevel
= specifyLevel
? specifiedLevel
: def
->FindLevelForIndent(thisIndent
);
4069 // How is numbering going to work?
4070 // If we are renumbering, or numbering for the first time, we need to keep
4071 // track of the number for each level. But we might be simply applying a different
4073 // In Word, applying a style to several paragraphs, even if at different levels,
4074 // reverts the level back to the same one. So we could do the same here.
4075 // Renumbering will need to be done when we promote/demote a paragraph.
4077 // Apply the overall list style, and item style for this level
4078 wxRichTextAttr
listStyle(def
->GetCombinedStyleForLevel(thisLevel
, styleSheet
));
4079 wxRichTextApplyStyle(newPara
->GetAttributes(), listStyle
);
4081 // Now we need to do numbering
4082 // Preserve the existing list item continuation bullet style, if any
4083 if (para
->GetAttributes().HasBulletStyle() && (para
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
))
4084 newPara
->GetAttributes().SetBulletStyle(newPara
->GetAttributes().GetBulletStyle()|wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
);
4089 newPara
->GetAttributes().SetBulletNumber(n
);
4095 else if (!newPara
->GetAttributes().GetListStyleName().IsEmpty())
4097 // if def is NULL, remove list style, applying any associated paragraph style
4098 // to restore the attributes
4100 newPara
->GetAttributes().SetListStyleName(wxEmptyString
);
4101 newPara
->GetAttributes().SetLeftIndent(0, 0);
4102 newPara
->GetAttributes().SetBulletText(wxEmptyString
);
4103 newPara
->GetAttributes().SetBulletStyle(0);
4105 // Eliminate the main list-related attributes
4106 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
);
4108 if (styleSheet
&& !newPara
->GetAttributes().GetParagraphStyleName().IsEmpty())
4110 wxRichTextParagraphStyleDefinition
* def
= styleSheet
->FindParagraphStyle(newPara
->GetAttributes().GetParagraphStyleName());
4113 newPara
->GetAttributes() = def
->GetStyleMergedWithBase(styleSheet
);
4120 node
= node
->GetNext();
4123 // Do action, or delay it until end of batch.
4124 if (haveControl
&& withUndo
)
4125 buffer
->SubmitAction(action
);
4130 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
)
4132 wxRichTextBuffer
* buffer
= GetBuffer();
4133 if (buffer
&& buffer
->GetStyleSheet())
4135 wxRichTextListStyleDefinition
* def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4137 return SetListStyle(range
, def
, flags
, startFrom
, specifiedLevel
);
4142 /// Clear list for given range
4143 bool wxRichTextParagraphLayoutBox::ClearListStyle(const wxRichTextRange
& range
, int flags
)
4145 return SetListStyle(range
, NULL
, flags
);
4148 /// Number/renumber any list elements in the given range
4149 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
4151 return DoNumberList(range
, range
, 0, def
, flags
, startFrom
, specifiedLevel
);
4154 /// Number/renumber any list elements in the given range. Also do promotion or demotion of items, if specified
4155 bool wxRichTextParagraphLayoutBox::DoNumberList(const wxRichTextRange
& range
, const wxRichTextRange
& promotionRange
, int promoteBy
,
4156 wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
4158 wxRichTextBuffer
* buffer
= GetBuffer();
4159 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
4161 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
4162 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
4164 bool specifyLevel
= ((flags
& wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL
) != 0);
4167 bool renumber
= ((flags
& wxRICHTEXT_SETSTYLE_RENUMBER
) != 0);
4169 // Max number of levels
4170 const int maxLevels
= 10;
4172 // The level we're looking at now
4173 int currentLevel
= -1;
4175 // The item number for each level
4176 int levels
[maxLevels
];
4179 // Reset all numbering
4180 for (i
= 0; i
< maxLevels
; i
++)
4182 if (startFrom
!= -1)
4183 levels
[i
] = startFrom
-1;
4184 else if (renumber
) // start again
4187 levels
[i
] = -1; // start from the number we found, if any
4191 wxASSERT(!specifyLevel
|| (specifyLevel
&& (specifiedLevel
>= 0)));
4194 // If we are associated with a control, make undoable; otherwise, apply immediately
4197 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
4199 wxRichTextAction
* action
= NULL
;
4201 if (haveControl
&& withUndo
)
4203 action
= new wxRichTextAction(NULL
, _("Renumber List"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
4204 action
->SetRange(range
);
4205 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
4208 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
4211 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
4212 // wxASSERT (para != NULL);
4214 if (para
&& para
->GetChildCount() > 0)
4216 // Stop searching if we're beyond the range of interest
4217 if (para
->GetRange().GetStart() > range
.GetEnd())
4220 if (!para
->GetRange().IsOutside(range
))
4222 // We'll be using a copy of the paragraph to make style changes,
4223 // not updating the buffer directly.
4224 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
4226 if (haveControl
&& withUndo
)
4228 newPara
= new wxRichTextParagraph(*para
);
4229 action
->GetNewParagraphs().AppendChild(newPara
);
4231 // Also store the old ones for Undo
4232 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
4237 wxRichTextListStyleDefinition
* defToUse
= def
;
4240 if (styleSheet
&& !newPara
->GetAttributes().GetListStyleName().IsEmpty())
4241 defToUse
= styleSheet
->FindListStyle(newPara
->GetAttributes().GetListStyleName());
4246 int thisIndent
= newPara
->GetAttributes().GetLeftIndent();
4247 int thisLevel
= defToUse
->FindLevelForIndent(thisIndent
);
4249 // If we've specified a level to apply to all, change the level.
4250 if (specifiedLevel
!= -1)
4251 thisLevel
= specifiedLevel
;
4253 // Do promotion if specified
4254 if ((promoteBy
!= 0) && !para
->GetRange().IsOutside(promotionRange
))
4256 thisLevel
= thisLevel
- promoteBy
;
4263 // Apply the overall list style, and item style for this level
4264 wxRichTextAttr
listStyle(defToUse
->GetCombinedStyleForLevel(thisLevel
, styleSheet
));
4265 wxRichTextApplyStyle(newPara
->GetAttributes(), listStyle
);
4267 // Preserve the existing list item continuation bullet style, if any
4268 if (para
->GetAttributes().HasBulletStyle() && (para
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
))
4269 newPara
->GetAttributes().SetBulletStyle(newPara
->GetAttributes().GetBulletStyle()|wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
);
4271 // OK, we've (re)applied the style, now let's get the numbering right.
4273 if (currentLevel
== -1)
4274 currentLevel
= thisLevel
;
4276 // Same level as before, do nothing except increment level's number afterwards
4277 if (currentLevel
== thisLevel
)
4280 // A deeper level: start renumbering all levels after current level
4281 else if (thisLevel
> currentLevel
)
4283 for (i
= currentLevel
+1; i
<= thisLevel
; i
++)
4287 currentLevel
= thisLevel
;
4289 else if (thisLevel
< currentLevel
)
4291 currentLevel
= thisLevel
;
4294 // Use the current numbering if -1 and we have a bullet number already
4295 if (levels
[currentLevel
] == -1)
4297 if (newPara
->GetAttributes().HasBulletNumber())
4298 levels
[currentLevel
] = newPara
->GetAttributes().GetBulletNumber();
4300 levels
[currentLevel
] = 1;
4304 if (!(para
->GetAttributes().HasBulletStyle() && (para
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
)))
4305 levels
[currentLevel
] ++;
4308 newPara
->GetAttributes().SetBulletNumber(levels
[currentLevel
]);
4310 // Create the bullet text if an outline list
4311 if (listStyle
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
)
4314 for (i
= 0; i
<= currentLevel
; i
++)
4316 if (!text
.IsEmpty())
4318 text
+= wxString::Format(wxT("%d"), levels
[i
]);
4320 newPara
->GetAttributes().SetBulletText(text
);
4326 node
= node
->GetNext();
4329 // Do action, or delay it until end of batch.
4330 if (haveControl
&& withUndo
)
4331 buffer
->SubmitAction(action
);
4336 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
)
4338 wxRichTextBuffer
* buffer
= GetBuffer();
4339 if (buffer
->GetStyleSheet())
4341 wxRichTextListStyleDefinition
* def
= NULL
;
4342 if (!defName
.IsEmpty())
4343 def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4344 return NumberList(range
, def
, flags
, startFrom
, specifiedLevel
);
4349 /// Promote the list items within the given range. promoteBy can be a positive or negative number, e.g. 1 or -1
4350 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy
, const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int specifiedLevel
)
4353 // One strategy is to first work out the range within which renumbering must occur. Then could pass these two ranges
4354 // to NumberList with a flag indicating promotion is required within one of the ranges.
4355 // Find first and last paragraphs in range. Then for first, calculate new indentation and look back until we find
4356 // a paragraph that either has no list style, or has one that is different or whose indentation is less.
4357 // We start renumbering from the para after that different para we found. We specify that the numbering of that
4358 // list position will start from 1.
4359 // Similarly, we look after the last para in the promote range for an indentation that is less (or no list style).
4360 // We can end the renumbering at this point.
4362 // For now, only renumber within the promotion range.
4364 return DoNumberList(range
, range
, promoteBy
, def
, flags
, 1, specifiedLevel
);
4367 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy
, const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int specifiedLevel
)
4369 wxRichTextBuffer
* buffer
= GetBuffer();
4370 if (buffer
->GetStyleSheet())
4372 wxRichTextListStyleDefinition
* def
= NULL
;
4373 if (!defName
.IsEmpty())
4374 def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4375 return PromoteList(promoteBy
, range
, def
, flags
, specifiedLevel
);
4380 /// Fills in the attributes for numbering a paragraph after previousParagraph. It also finds the
4381 /// position of the paragraph that it had to start looking from.
4382 bool wxRichTextParagraphLayoutBox::FindNextParagraphNumber(wxRichTextParagraph
* previousParagraph
, wxRichTextAttr
& attr
) const
4384 // TODO: add GetNextChild/GetPreviousChild to composite
4385 // Search for a paragraph that isn't a continuation paragraph (no bullet)
4386 while (previousParagraph
&& previousParagraph
->GetAttributes().HasBulletStyle() && previousParagraph
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
)
4388 wxRichTextObjectList::compatibility_iterator node
= ((wxRichTextCompositeObject
*) previousParagraph
->GetParent())->GetChildren().Find(previousParagraph
);
4391 node
= node
->GetPrevious();
4393 previousParagraph
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
4395 previousParagraph
= NULL
;
4398 previousParagraph
= NULL
;
4401 if (!previousParagraph
|| !previousParagraph
->GetAttributes().HasFlag(wxTEXT_ATTR_BULLET_STYLE
) || previousParagraph
->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
)
4404 wxRichTextBuffer
* buffer
= GetBuffer();
4405 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
4406 if (styleSheet
&& !previousParagraph
->GetAttributes().GetListStyleName().IsEmpty())
4408 wxRichTextListStyleDefinition
* def
= styleSheet
->FindListStyle(previousParagraph
->GetAttributes().GetListStyleName());
4411 // int thisIndent = previousParagraph->GetAttributes().GetLeftIndent();
4412 // int thisLevel = def->FindLevelForIndent(thisIndent);
4414 bool isOutline
= (previousParagraph
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
) != 0;
4416 attr
.SetFlags(previousParagraph
->GetAttributes().GetFlags() & (wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_BULLET_NUMBER
|wxTEXT_ATTR_BULLET_TEXT
|wxTEXT_ATTR_BULLET_NAME
));
4417 if (previousParagraph
->GetAttributes().HasBulletName())
4418 attr
.SetBulletName(previousParagraph
->GetAttributes().GetBulletName());
4419 attr
.SetBulletStyle(previousParagraph
->GetAttributes().GetBulletStyle());
4420 attr
.SetListStyleName(previousParagraph
->GetAttributes().GetListStyleName());
4422 int nextNumber
= previousParagraph
->GetAttributes().GetBulletNumber() + 1;
4423 attr
.SetBulletNumber(nextNumber
);
4427 wxString text
= previousParagraph
->GetAttributes().GetBulletText();
4428 if (!text
.IsEmpty())
4430 int pos
= text
.Find(wxT('.'), true);
4431 if (pos
!= wxNOT_FOUND
)
4433 text
= text
.Mid(0, text
.Length() - pos
- 1);
4436 text
= wxEmptyString
;
4437 if (!text
.IsEmpty())
4439 text
+= wxString::Format(wxT("%d"), nextNumber
);
4440 attr
.SetBulletText(text
);
4454 * wxRichTextParagraph
4455 * This object represents a single paragraph (or in a straight text editor, a line).
4458 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraph
, wxRichTextCompositeObject
)
4460 wxArrayInt
wxRichTextParagraph::sm_defaultTabs
;
4462 wxRichTextParagraph::wxRichTextParagraph(wxRichTextObject
* parent
, wxRichTextAttr
* style
):
4463 wxRichTextCompositeObject(parent
)
4466 SetAttributes(*style
);
4469 wxRichTextParagraph::wxRichTextParagraph(const wxString
& text
, wxRichTextObject
* parent
, wxRichTextAttr
* paraStyle
, wxRichTextAttr
* charStyle
):
4470 wxRichTextCompositeObject(parent
)
4473 SetAttributes(*paraStyle
);
4475 AppendChild(new wxRichTextPlainText(text
, this, charStyle
));
4478 wxRichTextParagraph::~wxRichTextParagraph()
4484 bool wxRichTextParagraph::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int WXUNUSED(descent
), int style
)
4489 // Currently we don't merge these attributes with the parent, but we
4490 // should consider whether we should (e.g. if we set a border colour
4491 // for all paragraphs). But generally box attributes are likely to be
4492 // different for different objects.
4493 wxRect paraRect
= GetRect();
4494 wxRichTextAttr attr
= GetCombinedAttributes();
4495 context
.ApplyVirtualAttributes(attr
, this);
4497 DrawBoxAttributes(dc
, GetBuffer(), attr
, paraRect
);
4499 // Draw the bullet, if any
4500 if ((attr
.GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
) == 0 && (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
) == 0)
4502 if (attr
.GetLeftSubIndent() != 0)
4504 int spaceBeforePara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingBefore());
4505 int leftIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftIndent());
4507 wxRichTextAttr
bulletAttr(attr
);
4509 // Combine with the font of the first piece of content, if one is specified
4510 if (GetChildren().GetCount() > 0)
4512 wxRichTextObject
* firstObj
= (wxRichTextObject
*) GetChildren().GetFirst()->GetData();
4513 if (!firstObj
->IsFloatable() && firstObj
->GetAttributes().HasFont())
4515 wxRichTextApplyStyle(bulletAttr
, firstObj
->GetAttributes());
4519 // Get line height from first line, if any
4520 wxRichTextLine
* line
= m_cachedLines
.GetFirst() ? (wxRichTextLine
* ) m_cachedLines
.GetFirst()->GetData() : NULL
;
4523 int lineHeight
wxDUMMY_INITIALIZE(0);
4526 lineHeight
= line
->GetSize().y
;
4527 linePos
= line
->GetPosition() + GetPosition();
4532 if (bulletAttr
.HasFont() && GetBuffer())
4533 font
= GetBuffer()->GetFontTable().FindFont(bulletAttr
);
4535 font
= (*wxNORMAL_FONT
);
4537 wxCheckSetFont(dc
, font
);
4539 lineHeight
= dc
.GetCharHeight();
4540 linePos
= GetPosition();
4541 linePos
.y
+= spaceBeforePara
;
4544 wxRect
bulletRect(GetPosition().x
+ leftIndent
, linePos
.y
, linePos
.x
- (GetPosition().x
+ leftIndent
), lineHeight
);
4546 if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
)
4548 if (wxRichTextBuffer::GetRenderer())
4549 wxRichTextBuffer::GetRenderer()->DrawBitmapBullet(this, dc
, bulletAttr
, bulletRect
);
4551 else if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_STANDARD
)
4553 if (wxRichTextBuffer::GetRenderer())
4554 wxRichTextBuffer::GetRenderer()->DrawStandardBullet(this, dc
, bulletAttr
, bulletRect
);
4558 wxString bulletText
= GetBulletText();
4560 if (!bulletText
.empty() && wxRichTextBuffer::GetRenderer())
4561 wxRichTextBuffer::GetRenderer()->DrawTextBullet(this, dc
, bulletAttr
, bulletRect
, bulletText
);
4566 // Draw the range for each line, one object at a time.
4568 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
4571 wxRichTextLine
* line
= node
->GetData();
4572 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
4574 // Lines are specified relative to the paragraph
4576 wxPoint linePosition
= line
->GetPosition() + GetPosition();
4578 // Don't draw if off the screen
4579 if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) != 0) || ((linePosition
.y
+ line
->GetSize().y
) >= rect
.y
&& linePosition
.y
<= rect
.y
+ rect
.height
))
4581 wxPoint objectPosition
= linePosition
;
4582 int maxDescent
= line
->GetDescent();
4584 // Loop through objects until we get to the one within range
4585 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
4590 wxRichTextObject
* child
= node2
->GetData();
4592 if ((!child
->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && child
->GetRange().GetLength() > 0 && !child
->GetRange().IsOutside(lineRange
) && !lineRange
.IsOutside(range
))
4594 // Draw this part of the line at the correct position
4595 wxRichTextRange
objectRange(child
->GetRange());
4596 objectRange
.LimitTo(lineRange
);
4599 if (child
->IsTopLevel())
4601 objectSize
= child
->GetCachedSize();
4602 objectRange
= child
->GetOwnRange();
4606 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING && wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4607 if (i
< (int) line
->GetObjectSizes().GetCount())
4609 objectSize
.x
= line
->GetObjectSizes()[(size_t) i
];
4615 child
->GetRangeSize(objectRange
, objectSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, objectPosition
);
4619 // Use the child object's width, but the whole line's height
4620 wxRect
childRect(objectPosition
, wxSize(objectSize
.x
, line
->GetSize().y
));
4621 child
->Draw(dc
, context
, objectRange
, selection
, childRect
, maxDescent
, style
);
4623 objectPosition
.x
+= objectSize
.x
;
4626 else if (child
->GetRange().GetStart() > lineRange
.GetEnd())
4627 // Can break out of inner loop now since we've passed this line's range
4630 node2
= node2
->GetNext();
4634 node
= node
->GetNext();
4640 // Get the range width using partial extents calculated for the whole paragraph.
4641 static int wxRichTextGetRangeWidth(const wxRichTextParagraph
& para
, const wxRichTextRange
& range
, const wxArrayInt
& partialExtents
)
4643 wxASSERT(partialExtents
.GetCount() >= (size_t) range
.GetLength());
4645 if (partialExtents
.GetCount() < (size_t) range
.GetLength())
4648 int leftMostPos
= 0;
4649 if (range
.GetStart() - para
.GetRange().GetStart() > 0)
4650 leftMostPos
= partialExtents
[range
.GetStart() - para
.GetRange().GetStart() - 1];
4652 int rightMostPos
= partialExtents
[range
.GetEnd() - para
.GetRange().GetStart()];
4654 int w
= rightMostPos
- leftMostPos
;
4659 /// Lay the item out
4660 bool wxRichTextParagraph::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
)
4662 // Deal with floating objects firstly before the normal layout
4663 wxRichTextBuffer
* buffer
= GetBuffer();
4666 wxRichTextFloatCollector
* collector
= GetContainer()->GetFloatCollector();
4668 if (wxRichTextBuffer::GetFloatingLayoutMode())
4670 wxASSERT(collector
!= NULL
);
4672 LayoutFloat(dc
, context
, rect
, parentRect
, style
, collector
);
4675 wxRichTextAttr attr
= GetCombinedAttributes();
4676 context
.ApplyVirtualAttributes(attr
, this);
4680 // Increase the size of the paragraph due to spacing
4681 int spaceBeforePara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingBefore());
4682 int spaceAfterPara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingAfter());
4683 int leftIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftIndent());
4684 int leftSubIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftSubIndent());
4685 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
4687 int lineSpacing
= 0;
4689 // Let's assume line spacing of 10 is normal, 15 is 1.5, 20 is 2, etc.
4690 if (attr
.HasLineSpacing() && attr
.GetLineSpacing() > 0 && attr
.HasFont())
4692 wxFont
font(buffer
->GetFontTable().FindFont(attr
));
4695 wxCheckSetFont(dc
, font
);
4696 lineSpacing
= (int) (double(dc
.GetCharHeight()) * (double(attr
.GetLineSpacing())/10.0 - 1.0));
4700 // Start position for each line relative to the paragraph
4701 int startPositionFirstLine
= leftIndent
;
4702 int startPositionSubsequentLines
= leftIndent
+ leftSubIndent
;
4704 // If we have a bullet in this paragraph, the start position for the first line's text
4705 // is actually leftIndent + leftSubIndent.
4706 if (attr
.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE
)
4707 startPositionFirstLine
= startPositionSubsequentLines
;
4709 long lastEndPos
= GetRange().GetStart()-1;
4710 long lastCompletedEndPos
= lastEndPos
;
4712 int currentWidth
= 0;
4713 SetPosition(rect
.GetPosition());
4715 wxPoint
currentPosition(0, spaceBeforePara
); // We will calculate lines relative to paragraph
4718 int maxHeight
= currentPosition
.y
;
4723 int lineDescent
= 0;
4725 wxRichTextObjectList::compatibility_iterator node
;
4727 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4729 wxArrayInt partialExtents
;
4732 int paraDescent
= 0;
4734 // This calculates the partial text extents
4735 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, rect
.GetPosition(), parentRect
.GetSize(), & partialExtents
);
4737 node
= m_children
.GetFirst();
4740 wxRichTextObject
* child
= node
->GetData();
4742 //child->SetCachedSize(wxDefaultSize);
4743 child
->Layout(dc
, context
, rect
, style
);
4745 node
= node
->GetNext();
4751 // We may need to go back to a previous child, in which case create the new line,
4752 // find the child corresponding to the start position of the string, and
4755 wxRect availableRect
;
4757 node
= m_children
.GetFirst();
4760 wxRichTextObject
* child
= node
->GetData();
4762 // If floating, ignore. We already laid out floats.
4763 // Also ignore if empty object, except if we haven't got any
4765 if ((child
->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode())
4766 || !child
->IsShown() || (child
->GetRange().GetLength() == 0 && maxHeight
> spaceBeforePara
)
4769 node
= node
->GetNext();
4773 // If this is e.g. a composite text box, it will need to be laid out itself.
4774 // But if just a text fragment or image, for example, this will
4775 // do nothing. NB: won't we need to set the position after layout?
4776 // since for example if position is dependent on vertical line size, we
4777 // can't tell the position until the size is determined. So possibly introduce
4778 // another layout phase.
4780 // We may only be looking at part of a child, if we searched back for wrapping
4781 // and found a suitable point some way into the child. So get the size for the fragment
4784 long nextBreakPos
= GetFirstLineBreakPosition(lastEndPos
+1);
4785 long lastPosToUse
= child
->GetRange().GetEnd();
4786 bool lineBreakInThisObject
= (nextBreakPos
> -1 && nextBreakPos
<= child
->GetRange().GetEnd());
4788 if (lineBreakInThisObject
)
4789 lastPosToUse
= nextBreakPos
;
4792 int childDescent
= 0;
4794 int startOffset
= (lineCount
== 0 ? startPositionFirstLine
: startPositionSubsequentLines
);
4795 availableRect
= wxRect(rect
.x
+ startOffset
, rect
.y
+ currentPosition
.y
,
4796 rect
.width
- startOffset
- rightIndent
, rect
.height
);
4798 if (child
->IsTopLevel())
4800 wxSize oldSize
= child
->GetCachedSize();
4802 child
->Invalidate(wxRICHTEXT_ALL
);
4803 child
->SetPosition(wxPoint(0, 0));
4805 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4806 // lays out the object again using the minimum size
4807 // The position will be determined by its location in its line,
4808 // and not by the child's actual position.
4809 child
->LayoutToBestSize(dc
, context
, buffer
,
4810 attr
, child
->GetAttributes(), availableRect
, parentRect
, style
);
4812 if (oldSize
!= child
->GetCachedSize())
4814 partialExtents
.Clear();
4816 // Recalculate the partial text extents since the child object changed size
4817 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), parentRect
.GetSize(), & partialExtents
);
4821 // Problem: we need to layout composites here for which we need the available width,
4822 // but we can't get the available width without using the float collector which
4823 // needs to know the object height.
4825 if ((nextBreakPos
== -1) && (lastEndPos
== child
->GetRange().GetStart() - 1)) // i.e. we want to get the whole thing
4827 childSize
= child
->GetCachedSize();
4828 childDescent
= child
->GetDescent();
4832 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4833 // Get height only, then the width using the partial extents
4834 GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
, wxPoint(0,0), parentRect
.GetSize());
4835 childSize
.x
= wxRichTextGetRangeWidth(*this, wxRichTextRange(lastEndPos
+1, lastPosToUse
), partialExtents
);
4837 GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, rect
.GetPosition(), parentRect
.GetSize());
4842 int loopIterations
= 0;
4844 // If there are nested objects that need to lay themselves out, we have to do this in a
4845 // loop because the height of the object may well depend on the available width.
4846 // And because of floating object positioning, the available width depends on the
4847 // height of the object and whether it will clash with the floating objects.
4848 // So, we see whether the available width changes due to the presence of floating images.
4849 // If it does, then we'll use the new restricted width to find the object height again.
4850 // If this causes another restriction in the available width, we'll try again, until
4851 // either we lose patience or the available width settles down.
4856 wxRect oldAvailableRect
= availableRect
;
4858 // Available width depends on the floating objects and the line height.
4859 // Note: the floating objects may be placed vertically along the two sides of
4860 // buffer, so we may have different available line widths with different
4861 // [startY, endY]. So, we can't determine how wide the available
4862 // space is until we know the exact line height.
4863 if (childDescent
== 0)
4865 lineHeight
= wxMax(lineHeight
, childSize
.y
);
4866 lineDescent
= maxDescent
;
4867 lineAscent
= maxAscent
;
4871 lineDescent
= wxMax(childDescent
, maxDescent
);
4872 lineAscent
= wxMax(childSize
.y
-childDescent
, maxAscent
);
4874 lineHeight
= wxMax(lineHeight
, (lineDescent
+ lineAscent
));
4876 if (wxRichTextBuffer::GetFloatingLayoutMode() && collector
)
4878 wxRect floatAvailableRect
= collector
->GetAvailableRect(rect
.y
+ currentPosition
.y
, rect
.y
+ currentPosition
.y
+ lineHeight
);
4880 // Adjust availableRect to the space that is available when taking floating objects into account.
4882 if (floatAvailableRect
.x
+ startOffset
> availableRect
.x
)
4884 int newX
= floatAvailableRect
.x
+ startOffset
;
4885 int newW
= availableRect
.width
- (newX
- availableRect
.x
);
4886 availableRect
.x
= newX
;
4887 availableRect
.width
= newW
;
4890 if (floatAvailableRect
.width
< availableRect
.width
)
4891 availableRect
.width
= floatAvailableRect
.width
;
4894 currentPosition
.x
= availableRect
.x
- rect
.x
;
4896 if (child
->IsTopLevel() && loopIterations
<= 20)
4898 if (availableRect
!= oldAvailableRect
)
4900 wxSize oldSize
= child
->GetCachedSize();
4902 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4903 // lays out the object again using the minimum size
4904 child
->Invalidate(wxRICHTEXT_ALL
);
4905 child
->LayoutToBestSize(dc
, context
, buffer
,
4906 attr
, child
->GetAttributes(), availableRect
, parentRect
.GetSize(), style
);
4907 childSize
= child
->GetCachedSize();
4908 childDescent
= child
->GetDescent();
4910 if (oldSize
!= child
->GetCachedSize())
4912 partialExtents
.Clear();
4914 // Recalculate the partial text extents since the child object changed size
4915 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), parentRect
.GetSize(), & partialExtents
);
4918 // Go around the loop finding the available rect for the given floating objects
4928 if (child
->IsTopLevel())
4930 // We can move it to the correct position at this point
4931 // TODO: probably need to add margin
4932 child
->Move(GetPosition() + wxPoint(currentWidth
+ (wxMax(leftIndent
, leftIndent
+ leftSubIndent
)), currentPosition
.y
));
4936 // 1) There was a line break BEFORE the natural break
4937 // 2) There was a line break AFTER the natural break
4938 // 3) It's the last line
4939 // 4) The child still fits (carry on) - 'else' clause
4941 if ((lineBreakInThisObject
&& (childSize
.x
+ currentWidth
<= availableRect
.width
))
4943 (childSize
.x
+ currentWidth
> availableRect
.width
)
4946 ((childSize
.x
+ currentWidth
<= availableRect
.width
) && !node
->GetNext())
4950 long wrapPosition
= 0;
4951 if ((childSize
.x
+ currentWidth
<= availableRect
.width
) && !node
->GetNext() && !lineBreakInThisObject
)
4952 wrapPosition
= child
->GetRange().GetEnd();
4955 // Find a place to wrap. This may walk back to previous children,
4956 // for example if a word spans several objects.
4957 // Note: one object must contains only one wxTextAtrr, so the line height will not
4958 // change inside one object. Thus, we can pass the remain line width to the
4959 // FindWrapPosition function.
4960 if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos
+1, child
->GetRange().GetEnd()), dc
, context
, availableRect
.width
, wrapPosition
, & partialExtents
))
4962 // If the function failed, just cut it off at the end of this child.
4963 wrapPosition
= child
->GetRange().GetEnd();
4966 // FindWrapPosition can still return a value that will put us in an endless wrapping loop
4967 if (wrapPosition
<= lastCompletedEndPos
)
4968 wrapPosition
= wxMax(lastCompletedEndPos
+1,child
->GetRange().GetEnd());
4970 // Line end position shouldn't be the same as the end, or greater.
4971 if (wrapPosition
>= GetRange().GetEnd())
4972 wrapPosition
= wxMax(0, GetRange().GetEnd()-1);
4974 // wxLogDebug(wxT("Split at %ld"), wrapPosition);
4976 // Let's find the actual size of the current line now
4978 wxRichTextRange
actualRange(lastCompletedEndPos
+1, wrapPosition
);
4982 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4983 if (!child
->IsEmpty())
4985 // Get height only, then the width using the partial extents
4986 GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
, wxPoint(0,0), parentRect
.GetSize());
4987 actualSize
.x
= wxRichTextGetRangeWidth(*this, actualRange
, partialExtents
);
4991 GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, wxPoint(0,0), parentRect
.GetSize());
4993 currentWidth
= actualSize
.x
;
4995 // The descent for the whole line at this point, is the correct max descent
4996 maxDescent
= childDescent
;
4998 maxAscent
= actualSize
.y
-childDescent
;
5000 // lineHeight is given by the height for the whole line, since it will
5001 // take into account ascend/descend.
5002 lineHeight
= actualSize
.y
;
5004 if (lineHeight
== 0 && buffer
)
5006 wxFont
font(buffer
->GetFontTable().FindFont(attr
));
5007 wxCheckSetFont(dc
, font
);
5008 lineHeight
= dc
.GetCharHeight();
5011 if (maxDescent
== 0)
5014 dc
.GetTextExtent(wxT("X"), & w
, &h
, & maxDescent
);
5018 wxRichTextLine
* line
= AllocateLine(lineCount
);
5020 // Set relative range so we won't have to change line ranges when paragraphs are moved
5021 line
->SetRange(wxRichTextRange(actualRange
.GetStart() - GetRange().GetStart(), actualRange
.GetEnd() - GetRange().GetStart()));
5022 line
->SetPosition(currentPosition
);
5023 line
->SetSize(wxSize(currentWidth
, lineHeight
));
5024 line
->SetDescent(maxDescent
);
5026 maxHeight
= currentPosition
.y
+ lineHeight
;
5028 // Now move down a line. TODO: add margins, spacing
5029 currentPosition
.y
+= lineHeight
;
5030 currentPosition
.y
+= lineSpacing
;
5033 maxWidth
= wxMax(maxWidth
, currentWidth
+startOffset
);
5038 // TODO: account for zero-length objects
5039 // wxASSERT(wrapPosition > lastCompletedEndPos);
5041 lastEndPos
= wrapPosition
;
5042 lastCompletedEndPos
= lastEndPos
;
5046 if (wrapPosition
< GetRange().GetEnd()-1)
5048 // May need to set the node back to a previous one, due to searching back in wrapping
5049 wxRichTextObject
* childAfterWrapPosition
= FindObjectAtPosition(wrapPosition
+1);
5050 if (childAfterWrapPosition
)
5051 node
= m_children
.Find(childAfterWrapPosition
);
5053 node
= node
->GetNext();
5056 node
= node
->GetNext();
5058 // Apply paragraph styles such as alignment to the wrapped line
5059 ApplyParagraphStyle(line
, attr
, availableRect
, dc
);
5063 // We still fit, so don't add a line, and keep going
5064 currentWidth
+= childSize
.x
;
5066 if (childDescent
== 0)
5068 // An object with a zero descend value wants to take up the whole
5069 // height regardless of baseline
5070 lineHeight
= wxMax(lineHeight
, childSize
.y
);
5074 maxDescent
= wxMax(childDescent
, maxDescent
);
5075 maxAscent
= wxMax(childSize
.y
-childDescent
, maxAscent
);
5078 lineHeight
= wxMax(lineHeight
, (maxDescent
+ maxAscent
));
5080 maxWidth
= wxMax(maxWidth
, currentWidth
+startOffset
);
5081 lastEndPos
= child
->GetRange().GetEnd();
5083 node
= node
->GetNext();
5087 //wxASSERT(!(lastCompletedEndPos != -1 && lastCompletedEndPos < GetRange().GetEnd()-1));
5089 // Add the last line - it's the current pos -> last para pos
5090 // Substract -1 because the last position is always the end-paragraph position.
5091 if (lastCompletedEndPos
<= GetRange().GetEnd()-1)
5093 currentPosition
.x
= (lineCount
== 0 ? startPositionFirstLine
: startPositionSubsequentLines
);
5095 wxRichTextLine
* line
= AllocateLine(lineCount
);
5097 wxRichTextRange
actualRange(lastCompletedEndPos
+1, GetRange().GetEnd()-1);
5099 // Set relative range so we won't have to change line ranges when paragraphs are moved
5100 line
->SetRange(wxRichTextRange(actualRange
.GetStart() - GetRange().GetStart(), actualRange
.GetEnd() - GetRange().GetStart()));
5102 line
->SetPosition(currentPosition
);
5104 if (lineHeight
== 0 && buffer
)
5106 wxFont
font(buffer
->GetFontTable().FindFont(attr
));
5107 wxCheckSetFont(dc
, font
);
5108 lineHeight
= dc
.GetCharHeight();
5111 if (maxDescent
== 0)
5114 dc
.GetTextExtent(wxT("X"), & w
, &h
, & maxDescent
);
5117 line
->SetSize(wxSize(currentWidth
, lineHeight
));
5118 line
->SetDescent(maxDescent
);
5119 currentPosition
.y
+= lineHeight
;
5120 currentPosition
.y
+= lineSpacing
;
5123 // Apply paragraph styles such as alignment to the wrapped line
5124 ApplyParagraphStyle(line
, attr
, availableRect
, dc
);
5127 // Remove remaining unused line objects, if any
5128 ClearUnusedLines(lineCount
);
5130 // We need to add back the margins etc.
5132 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
5133 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxWidth
, currentPosition
.y
+ spaceAfterPara
));
5134 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
5135 SetCachedSize(marginRect
.GetSize());
5138 // The maximum size is the length of the paragraph stretched out into a line.
5139 // So if there were a single word, or an image, or a fixed-size text box, the object could be shrunk around
5140 // this size. TODO: take into account line breaks.
5142 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
5143 contentRect
= wxRect(wxPoint(0, 0), wxSize(paraSize
.x
+ wxMax(leftIndent
, leftIndent
+ leftSubIndent
) + rightIndent
, currentPosition
.y
+ spaceAfterPara
));
5144 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
5145 SetMaxSize(marginRect
.GetSize());
5148 // Find the greatest minimum size. Currently we only look at non-text objects,
5149 // which isn't ideal but it would be slow to find the maximum word width to
5150 // use as the minimum.
5153 node
= m_children
.GetFirst();
5156 wxRichTextObject
* child
= node
->GetData();
5158 // If floating, ignore. We already laid out floats.
5159 // Also ignore if empty object, except if we haven't got any
5161 if ((!child
->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && child
->GetRange().GetLength() != 0 && !wxDynamicCast(child
, wxRichTextPlainText
))
5163 if (child
->GetCachedSize().x
> minWidth
)
5164 minWidth
= child
->GetMinSize().x
;
5166 node
= node
->GetNext();
5169 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
5170 contentRect
= wxRect(wxPoint(0, 0), wxSize(minWidth
, currentPosition
.y
+ spaceAfterPara
));
5171 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
5172 SetMinSize(marginRect
.GetSize());
5175 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5176 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
5177 // Use the text extents to calculate the size of each fragment in each line
5178 wxRichTextLineList::compatibility_iterator lineNode
= m_cachedLines
.GetFirst();
5181 wxRichTextLine
* line
= lineNode
->GetData();
5182 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5184 // Loop through objects until we get to the one within range
5185 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
5189 wxRichTextObject
* child
= node2
->GetData();
5191 if (child
->GetRange().GetLength() > 0 && !child
->GetRange().IsOutside(lineRange
))
5193 wxRichTextRange rangeToUse
= lineRange
;
5194 rangeToUse
.LimitTo(child
->GetRange());
5196 // Find the size of the child from the text extents, and store in an array
5197 // for drawing later
5199 if (rangeToUse
.GetStart() > GetRange().GetStart())
5200 left
= partialExtents
[(rangeToUse
.GetStart()-1) - GetRange().GetStart()];
5201 int right
= partialExtents
[rangeToUse
.GetEnd() - GetRange().GetStart()];
5202 int sz
= right
- left
;
5203 line
->GetObjectSizes().Add(sz
);
5205 else if (child
->GetRange().GetStart() > lineRange
.GetEnd())
5206 // Can break out of inner loop now since we've passed this line's range
5209 node2
= node2
->GetNext();
5212 lineNode
= lineNode
->GetNext();
5220 /// Apply paragraph styles, such as centering, to wrapped lines
5221 /// TODO: take into account box attributes, possibly
5222 void wxRichTextParagraph::ApplyParagraphStyle(wxRichTextLine
* line
, const wxRichTextAttr
& attr
, const wxRect
& rect
, wxDC
& dc
)
5224 if (!attr
.HasAlignment())
5227 wxPoint pos
= line
->GetPosition();
5228 wxPoint originalPos
= pos
;
5229 wxSize size
= line
->GetSize();
5231 // centering, right-justification
5232 if (attr
.HasAlignment() && attr
.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE
)
5234 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
5235 pos
.x
= (rect
.GetWidth() - rightIndent
- size
.x
)/2 + pos
.x
;
5236 line
->SetPosition(pos
);
5238 else if (attr
.HasAlignment() && attr
.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT
)
5240 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
5241 pos
.x
= pos
.x
+ rect
.GetWidth() - size
.x
- rightIndent
;
5242 line
->SetPosition(pos
);
5245 if (pos
!= originalPos
)
5247 wxPoint inc
= pos
- originalPos
;
5249 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5253 wxRichTextObject
* child
= node
->GetData();
5254 if (child
->IsTopLevel() && !child
->GetRange().IsOutside(line
->GetAbsoluteRange()))
5255 child
->Move(child
->GetPosition() + inc
);
5257 node
= node
->GetNext();
5262 /// Insert text at the given position
5263 bool wxRichTextParagraph::InsertText(long pos
, const wxString
& text
)
5265 wxRichTextObject
* childToUse
= NULL
;
5266 wxRichTextObjectList::compatibility_iterator nodeToUse
= wxRichTextObjectList::compatibility_iterator();
5268 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5271 wxRichTextObject
* child
= node
->GetData();
5272 if (child
->GetRange().Contains(pos
) && child
->GetRange().GetLength() > 0)
5279 node
= node
->GetNext();
5284 wxRichTextPlainText
* textObject
= wxDynamicCast(childToUse
, wxRichTextPlainText
);
5287 int posInString
= pos
- textObject
->GetRange().GetStart();
5289 wxString newText
= textObject
->GetText().Mid(0, posInString
) +
5290 text
+ textObject
->GetText().Mid(posInString
);
5291 textObject
->SetText(newText
);
5293 int textLength
= text
.length();
5295 textObject
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart(),
5296 textObject
->GetRange().GetEnd() + textLength
));
5298 // Increment the end range of subsequent fragments in this paragraph.
5299 // We'll set the paragraph range itself at a higher level.
5301 wxRichTextObjectList::compatibility_iterator node
= nodeToUse
->GetNext();
5304 wxRichTextObject
* child
= node
->GetData();
5305 child
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart() + textLength
,
5306 textObject
->GetRange().GetEnd() + textLength
));
5308 node
= node
->GetNext();
5315 // TODO: if not a text object, insert at closest position, e.g. in front of it
5321 // Don't pass parent initially to suppress auto-setting of parent range.
5322 // We'll do that at a higher level.
5323 wxRichTextPlainText
* textObject
= new wxRichTextPlainText(text
, this);
5325 AppendChild(textObject
);
5332 void wxRichTextParagraph::Copy(const wxRichTextParagraph
& obj
)
5334 wxRichTextCompositeObject::Copy(obj
);
5337 /// Clear the cached lines
5338 void wxRichTextParagraph::ClearLines()
5340 WX_CLEAR_LIST(wxRichTextLineList
, m_cachedLines
);
5343 /// Get/set the object size for the given range. Returns false if the range
5344 /// is invalid for this object.
5345 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
5347 if (!range
.IsWithin(GetRange()))
5350 if (flags
& wxRICHTEXT_UNFORMATTED
)
5352 // Just use unformatted data, assume no line breaks
5355 wxArrayInt childExtents
;
5364 int maxLineHeight
= 0;
5366 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5369 wxRichTextObject
* child
= node
->GetData();
5370 if (!child
->GetRange().IsOutside(range
))
5372 // Floating objects have a zero size within the paragraph.
5373 if (child
->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode())
5378 if (partialExtents
->GetCount() > 0)
5379 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5383 partialExtents
->Add(0 /* zero size */ + lastSize
);
5390 wxRichTextRange rangeToUse
= range
;
5391 rangeToUse
.LimitTo(child
->GetRange());
5392 int childDescent
= 0;
5394 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we've already cached the size,
5395 // but it's only going to be used after caching has taken place.
5396 if ((flags
& wxRICHTEXT_HEIGHT_ONLY
) && child
->GetCachedSize().y
!= 0)
5398 childDescent
= child
->GetDescent();
5399 childSize
= child
->GetCachedSize();
5401 if (childDescent
== 0)
5403 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5407 maxDescent
= wxMax(maxDescent
, childDescent
);
5408 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5411 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5413 sz
.y
= wxMax(sz
.y
, maxLineHeight
);
5414 sz
.x
+= childSize
.x
;
5415 descent
= maxDescent
;
5417 else if (child
->IsTopLevel())
5419 childDescent
= child
->GetDescent();
5420 childSize
= child
->GetCachedSize();
5422 if (childDescent
== 0)
5424 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5428 maxDescent
= wxMax(maxDescent
, childDescent
);
5429 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5432 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5434 sz
.y
= wxMax(sz
.y
, maxLineHeight
);
5435 sz
.x
+= childSize
.x
;
5436 descent
= maxDescent
;
5438 // FIXME: this won't change the original values.
5439 // Should we be calling GetRangeSize above instead of using cached values?
5441 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange()))
5443 child
->SetCachedSize(childSize
);
5444 child
->SetDescent(childDescent
);
5451 if (partialExtents
->GetCount() > 0)
5452 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5456 partialExtents
->Add(childSize
.x
+ lastSize
);
5459 else if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, context
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
), parentSize
, p
))
5461 if (childDescent
== 0)
5463 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5467 maxDescent
= wxMax(maxDescent
, childDescent
);
5468 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5471 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5473 sz
.y
= wxMax(sz
.y
, maxLineHeight
);
5474 sz
.x
+= childSize
.x
;
5475 descent
= maxDescent
;
5477 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange()))
5479 child
->SetCachedSize(childSize
);
5480 child
->SetDescent(childDescent
);
5486 if (partialExtents
->GetCount() > 0)
5487 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5492 for (i
= 0; i
< childExtents
.GetCount(); i
++)
5494 partialExtents
->Add(childExtents
[i
] + lastSize
);
5504 node
= node
->GetNext();
5510 // Use formatted data, with line breaks
5513 // We're going to loop through each line, and then for each line,
5514 // call GetRangeSize for the fragment that comprises that line.
5515 // Only we have to do that multiple times within the line, because
5516 // the line may be broken into pieces. For now ignore line break commands
5517 // (so we can assume that getting the unformatted size for a fragment
5518 // within a line is the actual size)
5520 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5523 wxRichTextLine
* line
= node
->GetData();
5524 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5525 if (!lineRange
.IsOutside(range
))
5529 int maxLineHeight
= 0;
5530 int maxLineWidth
= 0;
5532 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
5535 wxRichTextObject
* child
= node2
->GetData();
5537 if ((!child
->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && !child
->GetRange().IsOutside(lineRange
))
5539 wxRichTextRange rangeToUse
= lineRange
;
5540 rangeToUse
.LimitTo(child
->GetRange());
5541 if (child
->IsTopLevel())
5542 rangeToUse
= child
->GetOwnRange();
5545 int childDescent
= 0;
5546 if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, context
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
), parentSize
))
5548 if (childDescent
== 0)
5550 // Assume that if descent is zero, this child can occupy the full line height
5551 // and does not need space for the line's maximum descent. So we influence
5552 // the overall max line height only.
5553 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5557 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5558 maxDescent
= wxMax(maxAscent
, childDescent
);
5560 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5561 maxLineWidth
+= childSize
.x
;
5565 node2
= node2
->GetNext();
5568 descent
= wxMax(descent
, maxDescent
);
5570 // Increase size by a line (TODO: paragraph spacing)
5571 sz
.y
+= maxLineHeight
;
5572 sz
.x
= wxMax(sz
.x
, maxLineWidth
);
5574 node
= node
->GetNext();
5581 /// Finds the absolute position and row height for the given character position
5582 bool wxRichTextParagraph::FindPosition(wxDC
& dc
, wxRichTextDrawingContext
& context
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
5586 wxRichTextLine
* line
= ((wxRichTextParagraphLayoutBox
*)GetParent())->GetLineAtPosition(0);
5588 *height
= line
->GetSize().y
;
5590 *height
= dc
.GetCharHeight();
5592 // -1 means 'the start of the buffer'.
5595 pt
= pt
+ line
->GetPosition();
5600 // The final position in a paragraph is taken to mean the position
5601 // at the start of the next paragraph.
5602 if (index
== GetRange().GetEnd())
5604 wxRichTextParagraphLayoutBox
* parent
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
5605 wxASSERT( parent
!= NULL
);
5607 // Find the height at the next paragraph, if any
5608 wxRichTextLine
* line
= parent
->GetLineAtPosition(index
+ 1);
5611 *height
= line
->GetSize().y
;
5612 pt
= line
->GetAbsolutePosition();
5616 *height
= dc
.GetCharHeight();
5617 int indent
= ConvertTenthsMMToPixels(dc
, m_attributes
.GetLeftIndent());
5618 pt
= wxPoint(indent
, GetCachedSize().y
);
5624 if (index
< GetRange().GetStart() || index
> GetRange().GetEnd())
5627 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5630 wxRichTextLine
* line
= node
->GetData();
5631 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5632 if (index
>= lineRange
.GetStart() && index
<= lineRange
.GetEnd())
5634 // If this is the last point in the line, and we're forcing the
5635 // returned value to be the start of the next line, do the required
5637 if (index
== lineRange
.GetEnd() && forceLineStart
)
5639 if (node
->GetNext())
5641 wxRichTextLine
* nextLine
= node
->GetNext()->GetData();
5642 *height
= nextLine
->GetSize().y
;
5643 pt
= nextLine
->GetAbsolutePosition();
5648 pt
.y
= line
->GetPosition().y
+ GetPosition().y
;
5650 wxRichTextRange
r(lineRange
.GetStart(), index
);
5654 // We find the size of the line up to this point,
5655 // then we can add this size to the line start position and
5656 // paragraph start position to find the actual position.
5658 if (GetRangeSize(r
, rangeSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, line
->GetPosition()+ GetPosition()))
5660 pt
.x
= line
->GetPosition().x
+ GetPosition().x
+ rangeSize
.x
;
5661 *height
= line
->GetSize().y
;
5668 node
= node
->GetNext();
5674 /// Hit-testing: returns a flag indicating hit test details, plus
5675 /// information about position
5676 int wxRichTextParagraph::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
5679 return wxRICHTEXT_HITTEST_NONE
;
5681 // If we're in the top-level container, then we can return
5682 // a suitable hit test code even if the point is outside the container area,
5683 // so that we can position the caret sensibly even if we don't
5684 // click on valid content. If we're not at the top-level, and the point
5685 // is not within this paragraph object, then we don't want to stop more
5686 // precise hit-testing from working prematurely, so return immediately.
5687 // NEW STRATEGY: use the parent boundary to test whether we're in the
5688 // right region, not the paragraph, since the paragraph may be positioned
5689 // some way in from where the user clicks.
5692 wxRichTextObject
* tempObj
, *tempContextObj
;
5693 if (GetParent() && GetParent()->wxRichTextObject::HitTest(dc
, context
, pt
, tmpPos
, & tempObj
, & tempContextObj
, flags
) == wxRICHTEXT_HITTEST_NONE
)
5694 return wxRICHTEXT_HITTEST_NONE
;
5697 wxRichTextObjectList::compatibility_iterator objNode
= m_children
.GetFirst();
5700 wxRichTextObject
* child
= objNode
->GetData();
5701 // Don't recurse if we have wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS,
5702 // and also, if this seems composite but actually is marked as atomic,
5704 if (child
->IsTopLevel() && ((flags
& wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS
) == 0) &&
5705 (! (((flags
& wxRICHTEXT_HITTEST_HONOUR_ATOMIC
) != 0) && child
->IsAtomic())))
5708 int hitTest
= child
->HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
);
5709 if (hitTest
!= wxRICHTEXT_HITTEST_NONE
)
5714 objNode
= objNode
->GetNext();
5717 wxPoint paraPos
= GetPosition();
5719 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5722 wxRichTextLine
* line
= node
->GetData();
5723 wxPoint linePos
= paraPos
+ line
->GetPosition();
5724 wxSize lineSize
= line
->GetSize();
5725 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5727 if (pt
.y
<= linePos
.y
+ lineSize
.y
)
5729 if (pt
.x
< linePos
.x
)
5731 textPosition
= lineRange
.GetStart();
5732 *obj
= FindObjectAtPosition(textPosition
);
5733 *contextObj
= GetContainer();
5734 return wxRICHTEXT_HITTEST_BEFORE
|wxRICHTEXT_HITTEST_OUTSIDE
;
5736 else if (pt
.x
>= (linePos
.x
+ lineSize
.x
))
5738 textPosition
= lineRange
.GetEnd();
5739 *obj
= FindObjectAtPosition(textPosition
);
5740 *contextObj
= GetContainer();
5741 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
;
5745 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5746 wxArrayInt partialExtents
;
5751 // This calculates the partial text extents
5752 GetRangeSize(lineRange
, paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, linePos
, wxDefaultSize
, & partialExtents
);
5754 int lastX
= linePos
.x
;
5756 for (i
= 0; i
< partialExtents
.GetCount(); i
++)
5758 int nextX
= partialExtents
[i
] + linePos
.x
;
5760 if (pt
.x
>= lastX
&& pt
.x
<= nextX
)
5762 textPosition
= i
+ lineRange
.GetStart(); // minus 1?
5764 *obj
= FindObjectAtPosition(textPosition
);
5765 *contextObj
= GetContainer();
5767 // So now we know it's between i-1 and i.
5768 // Let's see if we can be more precise about
5769 // which side of the position it's on.
5771 int midPoint
= (nextX
+ lastX
)/2;
5772 if (pt
.x
>= midPoint
)
5773 return wxRICHTEXT_HITTEST_AFTER
;
5775 return wxRICHTEXT_HITTEST_BEFORE
;
5782 int lastX
= linePos
.x
;
5783 for (i
= lineRange
.GetStart(); i
<= lineRange
.GetEnd(); i
++)
5788 wxRichTextRange
rangeToUse(lineRange
.GetStart(), i
);
5790 GetRangeSize(rangeToUse
, childSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, linePos
);
5792 int nextX
= childSize
.x
+ linePos
.x
;
5794 if (pt
.x
>= lastX
&& pt
.x
<= nextX
)
5798 *obj
= FindObjectAtPosition(textPosition
);
5799 *contextObj
= GetContainer();
5801 // So now we know it's between i-1 and i.
5802 // Let's see if we can be more precise about
5803 // which side of the position it's on.
5805 int midPoint
= (nextX
+ lastX
)/2;
5806 if (pt
.x
>= midPoint
)
5807 return wxRICHTEXT_HITTEST_AFTER
;
5809 return wxRICHTEXT_HITTEST_BEFORE
;
5820 node
= node
->GetNext();
5823 return wxRICHTEXT_HITTEST_NONE
;
5826 /// Split an object at this position if necessary, and return
5827 /// the previous object, or NULL if inserting at beginning.
5828 wxRichTextObject
* wxRichTextParagraph::SplitAt(long pos
, wxRichTextObject
** previousObject
)
5830 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5833 wxRichTextObject
* child
= node
->GetData();
5835 if (pos
== child
->GetRange().GetStart())
5839 if (node
->GetPrevious())
5840 *previousObject
= node
->GetPrevious()->GetData();
5842 *previousObject
= NULL
;
5848 if (child
->GetRange().Contains(pos
))
5850 // This should create a new object, transferring part of
5851 // the content to the old object and the rest to the new object.
5852 wxRichTextObject
* newObject
= child
->DoSplit(pos
);
5854 // If we couldn't split this object, just insert in front of it.
5857 // Maybe this is an empty string, try the next one
5862 // Insert the new object after 'child'
5863 if (node
->GetNext())
5864 m_children
.Insert(node
->GetNext(), newObject
);
5866 m_children
.Append(newObject
);
5867 newObject
->SetParent(this);
5870 *previousObject
= child
;
5876 node
= node
->GetNext();
5879 *previousObject
= NULL
;
5883 /// Move content to a list from obj on
5884 void wxRichTextParagraph::MoveToList(wxRichTextObject
* obj
, wxList
& list
)
5886 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(obj
);
5889 wxRichTextObject
* child
= node
->GetData();
5892 wxRichTextObjectList::compatibility_iterator oldNode
= node
;
5894 node
= node
->GetNext();
5896 m_children
.DeleteNode(oldNode
);
5900 /// Add content back from list
5901 void wxRichTextParagraph::MoveFromList(wxList
& list
)
5903 for (wxList::compatibility_iterator node
= list
.GetFirst(); node
; node
= node
->GetNext())
5905 AppendChild((wxRichTextObject
*) node
->GetData());
5910 void wxRichTextParagraph::CalculateRange(long start
, long& end
)
5912 wxRichTextCompositeObject::CalculateRange(start
, end
);
5914 // Add one for end of paragraph
5917 m_range
.SetRange(start
, end
);
5920 /// Find the object at the given position
5921 wxRichTextObject
* wxRichTextParagraph::FindObjectAtPosition(long position
)
5923 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5926 wxRichTextObject
* obj
= node
->GetData();
5927 if (obj
->GetRange().Contains(position
) ||
5928 obj
->GetRange().GetStart() == position
||
5929 obj
->GetRange().GetEnd() == position
)
5932 node
= node
->GetNext();
5937 /// Get the plain text searching from the start or end of the range.
5938 /// The resulting string may be shorter than the range given.
5939 bool wxRichTextParagraph::GetContiguousPlainText(wxString
& text
, const wxRichTextRange
& range
, bool fromStart
)
5941 text
= wxEmptyString
;
5945 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5948 wxRichTextObject
* obj
= node
->GetData();
5949 if (!obj
->GetRange().IsOutside(range
))
5951 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5954 text
+= textObj
->GetTextForRange(range
);
5962 node
= node
->GetNext();
5967 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetLast();
5970 wxRichTextObject
* obj
= node
->GetData();
5971 if (!obj
->GetRange().IsOutside(range
))
5973 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5976 text
= textObj
->GetTextForRange(range
) + text
;
5980 text
= wxT(" ") + text
;
5984 node
= node
->GetPrevious();
5991 /// Find a suitable wrap position.
5992 bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange
& range
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int availableSpace
, long& wrapPosition
, wxArrayInt
* partialExtents
)
5994 if (range
.GetLength() <= 0)
5997 // Find the first position where the line exceeds the available space.
5999 long breakPosition
= range
.GetEnd();
6001 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
6002 if (partialExtents
&& partialExtents
->GetCount() >= (size_t) (GetRange().GetLength()-1)) // the final position in a paragraph is the newline
6006 if (range
.GetStart() > GetRange().GetStart())
6007 widthBefore
= (*partialExtents
)[range
.GetStart() - GetRange().GetStart() - 1];
6012 for (i
= (size_t) range
.GetStart(); i
<= (size_t) range
.GetEnd(); i
++)
6014 int widthFromStartOfThisRange
= (*partialExtents
)[i
- GetRange().GetStart()] - widthBefore
;
6016 if (widthFromStartOfThisRange
> availableSpace
)
6018 breakPosition
= i
-1;
6026 // Binary chop for speed
6027 long minPos
= range
.GetStart();
6028 long maxPos
= range
.GetEnd();
6031 if (minPos
== maxPos
)
6034 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
6036 if (sz
.x
> availableSpace
)
6037 breakPosition
= minPos
- 1;
6040 else if ((maxPos
- minPos
) == 1)
6043 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
6045 if (sz
.x
> availableSpace
)
6046 breakPosition
= minPos
- 1;
6049 GetRangeSize(wxRichTextRange(range
.GetStart(), maxPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
6050 if (sz
.x
> availableSpace
)
6051 breakPosition
= maxPos
-1;
6057 long nextPos
= minPos
+ ((maxPos
- minPos
) / 2);
6060 GetRangeSize(wxRichTextRange(range
.GetStart(), nextPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
6062 if (sz
.x
> availableSpace
)
6074 // Now we know the last position on the line.
6075 // Let's try to find a word break.
6078 if (GetContiguousPlainText(plainText
, wxRichTextRange(range
.GetStart(), breakPosition
), false))
6080 int newLinePos
= plainText
.Find(wxRichTextLineBreakChar
);
6081 if (newLinePos
!= wxNOT_FOUND
)
6083 breakPosition
= wxMax(0, range
.GetStart() + newLinePos
);
6087 int spacePos
= plainText
.Find(wxT(' '), true);
6088 int tabPos
= plainText
.Find(wxT('\t'), true);
6089 int pos
= wxMax(spacePos
, tabPos
);
6090 if (pos
!= wxNOT_FOUND
)
6092 int positionsFromEndOfString
= plainText
.length() - pos
- 1;
6093 breakPosition
= breakPosition
- positionsFromEndOfString
;
6098 wrapPosition
= breakPosition
;
6103 /// Get the bullet text for this paragraph.
6104 wxString
wxRichTextParagraph::GetBulletText()
6106 if (GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
||
6107 (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
))
6108 return wxEmptyString
;
6110 int number
= GetAttributes().GetBulletNumber();
6113 if ((GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ARABIC
) || (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
))
6115 text
.Printf(wxT("%d"), number
);
6117 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_UPPER
)
6119 // TODO: Unicode, and also check if number > 26
6120 text
.Printf(wxT("%c"), (wxChar
) (number
+64));
6122 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_LOWER
)
6124 // TODO: Unicode, and also check if number > 26
6125 text
.Printf(wxT("%c"), (wxChar
) (number
+96));
6127 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER
)
6129 text
= wxRichTextDecimalToRoman(number
);
6131 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER
)
6133 text
= wxRichTextDecimalToRoman(number
);
6136 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
)
6138 text
= GetAttributes().GetBulletText();
6141 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
)
6143 // The outline style relies on the text being computed statically,
6144 // since it depends on other levels points (e.g. 1.2.1.1). So normally the bullet text
6145 // should be stored in the attributes; if not, just use the number for this
6146 // level, as previously computed.
6147 if (!GetAttributes().GetBulletText().IsEmpty())
6148 text
= GetAttributes().GetBulletText();
6151 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PARENTHESES
)
6153 text
= wxT("(") + text
+ wxT(")");
6155 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_RIGHT_PARENTHESIS
)
6157 text
= text
+ wxT(")");
6160 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PERIOD
)
6168 /// Allocate or reuse a line object
6169 wxRichTextLine
* wxRichTextParagraph::AllocateLine(int pos
)
6171 if (pos
< (int) m_cachedLines
.GetCount())
6173 wxRichTextLine
* line
= m_cachedLines
.Item(pos
)->GetData();
6179 wxRichTextLine
* line
= new wxRichTextLine(this);
6180 m_cachedLines
.Append(line
);
6185 /// Clear remaining unused line objects, if any
6186 bool wxRichTextParagraph::ClearUnusedLines(int lineCount
)
6188 int cachedLineCount
= m_cachedLines
.GetCount();
6189 if ((int) cachedLineCount
> lineCount
)
6191 for (int i
= 0; i
< (int) (cachedLineCount
- lineCount
); i
++)
6193 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetLast();
6194 wxRichTextLine
* line
= node
->GetData();
6195 m_cachedLines
.Erase(node
);
6202 /// Get combined attributes of the base style, paragraph style and character style. We use this to dynamically
6203 /// retrieve the actual style.
6204 wxRichTextAttr
wxRichTextParagraph::GetCombinedAttributes(const wxRichTextAttr
& contentStyle
, bool includingBoxAttr
) const
6206 wxRichTextAttr attr
;
6207 wxRichTextParagraphLayoutBox
* buf
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
6210 attr
= buf
->GetBasicStyle();
6211 if (!includingBoxAttr
)
6213 attr
.GetTextBoxAttr().Reset();
6214 // The background colour will be painted by the container, and we don't
6215 // want to unnecessarily overwrite the background when we're drawing text
6216 // because this may erase the guideline (which appears just under the text
6217 // if there's no padding).
6218 attr
.SetFlags(attr
.GetFlags() & ~wxTEXT_ATTR_BACKGROUND_COLOUR
);
6220 wxRichTextApplyStyle(attr
, GetAttributes());
6223 attr
= GetAttributes();
6225 wxRichTextApplyStyle(attr
, contentStyle
);
6229 /// Get combined attributes of the base style and paragraph style.
6230 wxRichTextAttr
wxRichTextParagraph::GetCombinedAttributes(bool includingBoxAttr
) const
6232 wxRichTextAttr attr
;
6233 wxRichTextParagraphLayoutBox
* buf
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
6236 attr
= buf
->GetBasicStyle();
6237 if (!includingBoxAttr
)
6238 attr
.GetTextBoxAttr().Reset();
6239 wxRichTextApplyStyle(attr
, GetAttributes());
6242 attr
= GetAttributes();
6247 // Create default tabstop array
6248 void wxRichTextParagraph::InitDefaultTabs()
6250 // create a default tab list at 10 mm each.
6251 for (int i
= 0; i
< 20; ++i
)
6253 sm_defaultTabs
.Add(i
*100);
6257 // Clear default tabstop array
6258 void wxRichTextParagraph::ClearDefaultTabs()
6260 sm_defaultTabs
.Clear();
6263 void wxRichTextParagraph::LayoutFloat(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
, wxRichTextFloatCollector
* floatCollector
)
6265 wxRichTextObjectList::compatibility_iterator node
= GetChildren().GetFirst();
6268 wxRichTextObject
* anchored
= node
->GetData();
6269 if (anchored
&& anchored
->IsFloating() && !floatCollector
->HasFloat(anchored
))
6272 wxRichTextAttr
parentAttr(GetAttributes());
6273 context
.ApplyVirtualAttributes(parentAttr
, this);
6276 wxRect availableSpace
= GetParent()->GetAvailableContentArea(dc
, context
, rect
);
6278 anchored
->LayoutToBestSize(dc
, context
, GetBuffer(),
6279 parentAttr
, anchored
->GetAttributes(),
6280 parentRect
, availableSpace
,
6282 wxSize size
= anchored
->GetCachedSize();
6286 anchored
->GetRangeSize(anchored
->GetRange(), size
, descent
, dc
, context
, style
);
6290 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().IsValid())
6292 offsetY
= anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetValue();
6293 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
6295 offsetY
= ConvertTenthsMMToPixels(dc
, offsetY
);
6299 int pos
= floatCollector
->GetFitPosition(anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode(), rect
.y
+ offsetY
, size
.y
);
6301 /* Update the offset */
6302 int newOffsetY
= pos
- rect
.y
;
6303 if (newOffsetY
!= offsetY
)
6305 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
6306 newOffsetY
= ConvertPixelsToTenthsMM(dc
, newOffsetY
);
6307 anchored
->GetAttributes().GetTextBoxAttr().GetTop().SetValue(newOffsetY
);
6310 if (anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT
)
6312 else if (anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT
)
6313 x
= rect
.x
+ rect
.width
- size
.x
;
6315 //anchored->SetPosition(wxPoint(x, pos));
6316 anchored
->Move(wxPoint(x
, pos
)); // should move children
6317 anchored
->SetCachedSize(size
);
6318 floatCollector
->CollectFloat(this, anchored
);
6321 node
= node
->GetNext();
6325 // Get the first position from pos that has a line break character.
6326 long wxRichTextParagraph::GetFirstLineBreakPosition(long pos
)
6328 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
6331 wxRichTextObject
* obj
= node
->GetData();
6332 if (pos
>= obj
->GetRange().GetStart() && pos
<= obj
->GetRange().GetEnd())
6334 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
6337 long breakPos
= textObj
->GetFirstLineBreakPosition(pos
);
6342 node
= node
->GetNext();
6349 * This object represents a line in a paragraph, and stores
6350 * offsets from the start of the paragraph representing the
6351 * start and end positions of the line.
6354 wxRichTextLine::wxRichTextLine(wxRichTextParagraph
* parent
)
6360 void wxRichTextLine::Init(wxRichTextParagraph
* parent
)
6363 m_range
.SetRange(-1, -1);
6364 m_pos
= wxPoint(0, 0);
6365 m_size
= wxSize(0, 0);
6367 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6368 m_objectSizes
.Clear();
6373 void wxRichTextLine::Copy(const wxRichTextLine
& obj
)
6375 m_range
= obj
.m_range
;
6376 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6377 m_objectSizes
= obj
.m_objectSizes
;
6381 /// Get the absolute object position
6382 wxPoint
wxRichTextLine::GetAbsolutePosition() const
6384 return m_parent
->GetPosition() + m_pos
;
6387 /// Get the absolute range
6388 wxRichTextRange
wxRichTextLine::GetAbsoluteRange() const
6390 wxRichTextRange
range(m_range
.GetStart() + m_parent
->GetRange().GetStart(), 0);
6391 range
.SetEnd(range
.GetStart() + m_range
.GetLength()-1);
6396 * wxRichTextPlainText
6397 * This object represents a single piece of text.
6400 IMPLEMENT_DYNAMIC_CLASS(wxRichTextPlainText
, wxRichTextObject
)
6402 wxRichTextPlainText::wxRichTextPlainText(const wxString
& text
, wxRichTextObject
* parent
, wxRichTextAttr
* style
):
6403 wxRichTextObject(parent
)
6406 SetAttributes(*style
);
6411 #define USE_KERNING_FIX 1
6413 // If insufficient tabs are defined, this is the tab width used
6414 #define WIDTH_FOR_DEFAULT_TABS 50
6417 bool wxRichTextPlainText::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int WXUNUSED(style
))
6419 wxRichTextParagraph
* para
= wxDynamicCast(GetParent(), wxRichTextParagraph
);
6420 wxASSERT (para
!= NULL
);
6422 wxRichTextAttr
textAttr(para
? para
->GetCombinedAttributes(GetAttributes(), false /* no box attributes */) : GetAttributes());
6423 context
.ApplyVirtualAttributes(textAttr
, this);
6425 // Let's make the assumption for now that for content in a paragraph, including
6426 // text, we never have a discontinuous selection. So we only deal with a
6428 wxRichTextRange selectionRange
;
6429 if (selection
.IsValid())
6431 wxRichTextRangeArray selectionRanges
= selection
.GetSelectionForObject(this);
6432 if (selectionRanges
.GetCount() > 0)
6433 selectionRange
= selectionRanges
[0];
6435 selectionRange
= wxRICHTEXT_NO_SELECTION
;
6438 selectionRange
= wxRICHTEXT_NO_SELECTION
;
6440 int offset
= GetRange().GetStart();
6442 wxString str
= m_text
;
6443 if (context
.HasVirtualText(this))
6445 if (!context
.GetVirtualText(this, str
) || str
.Length() != m_text
.Length())
6449 // Replace line break characters with spaces
6450 wxString toRemove
= wxRichTextLineBreakChar
;
6451 str
.Replace(toRemove
, wxT(" "));
6452 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS
|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS
)))
6455 long len
= range
.GetLength();
6456 wxString stringChunk
= str
.Mid(range
.GetStart() - offset
, (size_t) len
);
6458 // Test for the optimized situations where all is selected, or none
6461 wxFont
textFont(GetBuffer()->GetFontTable().FindFont(textAttr
));
6462 wxCheckSetFont(dc
, textFont
);
6463 int charHeight
= dc
.GetCharHeight();
6466 if ( textFont
.IsOk() )
6468 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SMALL_CAPITALS
))
6470 textFont
.SetPointSize((int) (textFont
.GetPointSize()*0.75));
6471 wxCheckSetFont(dc
, textFont
);
6472 charHeight
= dc
.GetCharHeight();
6475 if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
) )
6477 if (textFont
.IsUsingSizeInPixels())
6479 double size
= static_cast<double>(textFont
.GetPixelSize().y
) / wxSCRIPT_MUL_FACTOR
;
6480 textFont
.SetPixelSize(wxSize(0, static_cast<int>(size
)));
6486 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6487 textFont
.SetPointSize(static_cast<int>(size
));
6491 wxCheckSetFont(dc
, textFont
);
6493 else if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) )
6495 if (textFont
.IsUsingSizeInPixels())
6497 double size
= static_cast<double>(textFont
.GetPixelSize().y
) / wxSCRIPT_MUL_FACTOR
;
6498 textFont
.SetPixelSize(wxSize(0, static_cast<int>(size
)));
6500 int sub_height
= static_cast<int>(static_cast<double>(charHeight
) / wxSCRIPT_MUL_FACTOR
);
6501 y
= rect
.y
+ (rect
.height
- sub_height
+ (descent
- m_descent
));
6505 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6506 textFont
.SetPointSize(static_cast<int>(size
));
6508 int sub_height
= static_cast<int>(static_cast<double>(charHeight
) / wxSCRIPT_MUL_FACTOR
);
6509 y
= rect
.y
+ (rect
.height
- sub_height
+ (descent
- m_descent
));
6511 wxCheckSetFont(dc
, textFont
);
6516 y
= rect
.y
+ (rect
.height
- charHeight
- (descent
- m_descent
));
6522 y
= rect
.y
+ (rect
.height
- charHeight
- (descent
- m_descent
));
6525 // TODO: new selection code
6527 // (a) All selected.
6528 if (selectionRange
.GetStart() <= range
.GetStart() && selectionRange
.GetEnd() >= range
.GetEnd())
6530 DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, true);
6532 // (b) None selected.
6533 else if (selectionRange
.GetEnd() < range
.GetStart() || selectionRange
.GetStart() > range
.GetEnd())
6535 // Draw all unselected
6536 DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, false);
6540 // (c) Part selected, part not
6541 // Let's draw unselected chunk, selected chunk, then unselected chunk.
6543 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6545 // 1. Initial unselected chunk, if any, up until start of selection.
6546 if (selectionRange
.GetStart() > range
.GetStart() && selectionRange
.GetStart() <= range
.GetEnd())
6548 int r1
= range
.GetStart();
6549 int s1
= selectionRange
.GetStart()-1;
6550 int fragmentLen
= s1
- r1
+ 1;
6551 if (fragmentLen
< 0)
6553 wxLogDebug(wxT("Mid(%d, %d"), (int)(r1
- offset
), (int)fragmentLen
);
6555 wxString stringFragment
= str
.Mid(r1
- offset
, fragmentLen
);
6557 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false);
6560 if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
)
6562 // Compensate for kerning difference
6563 wxString
stringFragment2(str
.Mid(r1
- offset
, fragmentLen
+1));
6564 wxString
stringFragment3(str
.Mid(r1
- offset
+ fragmentLen
, 1));
6566 wxCoord w1
, h1
, w2
, h2
, w3
, h3
;
6567 dc
.GetTextExtent(stringFragment
, & w1
, & h1
);
6568 dc
.GetTextExtent(stringFragment2
, & w2
, & h2
);
6569 dc
.GetTextExtent(stringFragment3
, & w3
, & h3
);
6571 int kerningDiff
= (w1
+ w3
) - w2
;
6572 x
= x
- kerningDiff
;
6577 // 2. Selected chunk, if any.
6578 if (selectionRange
.GetEnd() >= range
.GetStart())
6580 int s1
= wxMax(selectionRange
.GetStart(), range
.GetStart());
6581 int s2
= wxMin(selectionRange
.GetEnd(), range
.GetEnd());
6583 int fragmentLen
= s2
- s1
+ 1;
6584 if (fragmentLen
< 0)
6586 wxLogDebug(wxT("Mid(%d, %d"), (int)(s1
- offset
), (int)fragmentLen
);
6588 wxString stringFragment
= str
.Mid(s1
- offset
, fragmentLen
);
6590 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, true);
6593 if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
)
6595 // Compensate for kerning difference
6596 wxString
stringFragment2(str
.Mid(s1
- offset
, fragmentLen
+1));
6597 wxString
stringFragment3(str
.Mid(s1
- offset
+ fragmentLen
, 1));
6599 wxCoord w1
, h1
, w2
, h2
, w3
, h3
;
6600 dc
.GetTextExtent(stringFragment
, & w1
, & h1
);
6601 dc
.GetTextExtent(stringFragment2
, & w2
, & h2
);
6602 dc
.GetTextExtent(stringFragment3
, & w3
, & h3
);
6604 int kerningDiff
= (w1
+ w3
) - w2
;
6605 x
= x
- kerningDiff
;
6610 // 3. Remaining unselected chunk, if any
6611 if (selectionRange
.GetEnd() < range
.GetEnd())
6613 int s2
= wxMin(selectionRange
.GetEnd()+1, range
.GetEnd());
6614 int r2
= range
.GetEnd();
6616 int fragmentLen
= r2
- s2
+ 1;
6617 if (fragmentLen
< 0)
6619 wxLogDebug(wxT("Mid(%d, %d"), (int)(s2
- offset
), (int)fragmentLen
);
6621 wxString stringFragment
= str
.Mid(s2
- offset
, fragmentLen
);
6623 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false);
6630 bool wxRichTextPlainText::DrawTabbedString(wxDC
& dc
, const wxRichTextAttr
& attr
, const wxRect
& rect
,wxString
& str
, wxCoord
& x
, wxCoord
& y
, bool selected
)
6632 bool hasTabs
= (str
.Find(wxT('\t')) != wxNOT_FOUND
);
6634 wxArrayInt tabArray
;
6638 if (attr
.GetTabs().IsEmpty())
6639 tabArray
= wxRichTextParagraph::GetDefaultTabs();
6641 tabArray
= attr
.GetTabs();
6642 tabCount
= tabArray
.GetCount();
6644 for (int i
= 0; i
< tabCount
; ++i
)
6646 int pos
= tabArray
[i
];
6647 pos
= ConvertTenthsMMToPixels(dc
, pos
);
6654 int nextTabPos
= -1;
6660 wxColour
highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
));
6661 wxColour
highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
));
6663 wxCheckSetBrush(dc
, wxBrush(highlightColour
));
6664 wxCheckSetPen(dc
, wxPen(highlightColour
));
6665 dc
.SetTextForeground(highlightTextColour
);
6666 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6670 dc
.SetTextForeground(attr
.GetTextColour());
6672 if (attr
.HasFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
) && attr
.GetBackgroundColour().IsOk())
6674 dc
.SetBackgroundMode(wxBRUSHSTYLE_SOLID
);
6675 dc
.SetTextBackground(attr
.GetBackgroundColour());
6678 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6681 wxCoord x_orig
= GetParent()->GetPosition().x
;
6684 // the string has a tab
6685 // break up the string at the Tab
6686 wxString stringChunk
= str
.BeforeFirst(wxT('\t'));
6687 str
= str
.AfterFirst(wxT('\t'));
6688 dc
.GetTextExtent(stringChunk
, & w
, & h
);
6690 bool not_found
= true;
6691 for (int i
= 0; i
< tabCount
&& not_found
; ++i
)
6693 nextTabPos
= tabArray
.Item(i
) + x_orig
;
6695 // Find the next tab position.
6696 // Even if we're at the end of the tab array, we must still draw the chunk.
6698 if (nextTabPos
> tabPos
|| (i
== (tabCount
- 1)))
6700 if (nextTabPos
<= tabPos
)
6702 int defaultTabWidth
= ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
);
6703 nextTabPos
= tabPos
+ defaultTabWidth
;
6710 wxRect
selRect(x
, rect
.y
, w
, rect
.GetHeight());
6711 dc
.DrawRectangle(selRect
);
6713 dc
.DrawText(stringChunk
, x
, y
);
6715 if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
))
6717 wxPen oldPen
= dc
.GetPen();
6718 wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1));
6719 dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5));
6720 wxCheckSetPen(dc
, oldPen
);
6726 hasTabs
= (str
.Find(wxT('\t')) != wxNOT_FOUND
);
6731 dc
.GetTextExtent(str
, & w
, & h
);
6734 wxRect
selRect(x
, rect
.y
, w
, rect
.GetHeight());
6735 dc
.DrawRectangle(selRect
);
6737 dc
.DrawText(str
, x
, y
);
6739 if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
))
6741 wxPen oldPen
= dc
.GetPen();
6742 wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1));
6743 dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5));
6744 wxCheckSetPen(dc
, oldPen
);
6753 /// Lay the item out
6754 bool wxRichTextPlainText::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& WXUNUSED(rect
), const wxRect
& WXUNUSED(parentRect
), int WXUNUSED(style
))
6756 // Only lay out if we haven't already cached the size
6758 GetRangeSize(GetRange(), m_size
, m_descent
, dc
, context
, 0, wxPoint(0, 0));
6760 // Eventually we want to have a reasonable estimate of minimum size.
6761 m_minSize
= wxSize(0, 0);
6766 void wxRichTextPlainText::Copy(const wxRichTextPlainText
& obj
)
6768 wxRichTextObject::Copy(obj
);
6770 m_text
= obj
.m_text
;
6773 /// Get/set the object size for the given range. Returns false if the range
6774 /// is invalid for this object.
6775 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
6777 if (!range
.IsWithin(GetRange()))
6780 wxRichTextParagraph
* para
= wxDynamicCast(GetParent(), wxRichTextParagraph
);
6781 wxASSERT (para
!= NULL
);
6783 int relativeX
= position
.x
- GetParent()->GetPosition().x
;
6785 wxRichTextAttr
textAttr(para
? para
->GetCombinedAttributes(GetAttributes()) : GetAttributes());
6786 context
.ApplyVirtualAttributes(textAttr
, (wxRichTextObject
*) this);
6788 // Always assume unformatted text, since at this level we have no knowledge
6789 // of line breaks - and we don't need it, since we'll calculate size within
6790 // formatted text by doing it in chunks according to the line ranges
6792 bool bScript(false);
6793 wxFont
font(GetBuffer()->GetFontTable().FindFont(textAttr
));
6796 if ( textAttr
.HasTextEffects() && ( (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
)
6797 || (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) ) )
6799 wxFont textFont
= font
;
6800 if (textFont
.IsUsingSizeInPixels())
6802 double size
= static_cast<double>(textFont
.GetPixelSize().y
) / wxSCRIPT_MUL_FACTOR
;
6803 textFont
.SetPixelSize(wxSize(0, static_cast<int>(size
)));
6807 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6808 textFont
.SetPointSize(static_cast<int>(size
));
6810 wxCheckSetFont(dc
, textFont
);
6813 else if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SMALL_CAPITALS
))
6815 wxFont textFont
= font
;
6816 textFont
.SetPointSize((int) (textFont
.GetPointSize()*0.75));
6817 wxCheckSetFont(dc
, textFont
);
6822 wxCheckSetFont(dc
, font
);
6826 bool haveDescent
= false;
6827 int startPos
= range
.GetStart() - GetRange().GetStart();
6828 long len
= range
.GetLength();
6830 wxString
str(m_text
);
6831 if (context
.HasVirtualText(this))
6833 if (!context
.GetVirtualText(this, str
) || str
.Length() != m_text
.Length())
6837 wxString toReplace
= wxRichTextLineBreakChar
;
6838 str
.Replace(toReplace
, wxT(" "));
6840 wxString stringChunk
= str
.Mid(startPos
, (size_t) len
);
6842 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS
|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS
)))
6843 stringChunk
.MakeUpper();
6847 if (stringChunk
.Find(wxT('\t')) != wxNOT_FOUND
)
6849 // the string has a tab
6850 wxArrayInt tabArray
;
6851 if (textAttr
.GetTabs().IsEmpty())
6852 tabArray
= wxRichTextParagraph::GetDefaultTabs();
6854 tabArray
= textAttr
.GetTabs();
6856 int tabCount
= tabArray
.GetCount();
6858 for (int i
= 0; i
< tabCount
; ++i
)
6860 int pos
= tabArray
[i
];
6861 pos
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, pos
);
6865 int nextTabPos
= -1;
6867 while (stringChunk
.Find(wxT('\t')) >= 0)
6869 int absoluteWidth
= 0;
6871 // the string has a tab
6872 // break up the string at the Tab
6873 wxString stringFragment
= stringChunk
.BeforeFirst(wxT('\t'));
6874 stringChunk
= stringChunk
.AfterFirst(wxT('\t'));
6879 if (partialExtents
->GetCount() > 0)
6880 oldWidth
= (*partialExtents
)[partialExtents
->GetCount()-1];
6884 // Add these partial extents
6886 dc
.GetPartialTextExtents(stringFragment
, p
);
6888 for (j
= 0; j
< p
.GetCount(); j
++)
6889 partialExtents
->Add(oldWidth
+ p
[j
]);
6891 if (partialExtents
->GetCount() > 0)
6892 absoluteWidth
= (*partialExtents
)[(*partialExtents
).GetCount()-1] + relativeX
;
6894 absoluteWidth
= relativeX
;
6898 dc
.GetTextExtent(stringFragment
, & w
, & h
);
6900 absoluteWidth
= width
+ relativeX
;
6904 bool notFound
= true;
6905 for (int i
= 0; i
< tabCount
&& notFound
; ++i
)
6907 nextTabPos
= tabArray
.Item(i
);
6909 // Find the next tab position.
6910 // Even if we're at the end of the tab array, we must still process the chunk.
6912 if (nextTabPos
> absoluteWidth
|| (i
== (tabCount
- 1)))
6914 if (nextTabPos
<= absoluteWidth
)
6916 int defaultTabWidth
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
);
6917 nextTabPos
= absoluteWidth
+ defaultTabWidth
;
6921 width
= nextTabPos
- relativeX
;
6924 partialExtents
->Add(width
);
6930 if (!stringChunk
.IsEmpty())
6935 if (partialExtents
->GetCount() > 0)
6936 oldWidth
= (*partialExtents
)[partialExtents
->GetCount()-1];
6940 // Add these partial extents
6942 dc
.GetPartialTextExtents(stringChunk
, p
);
6944 for (j
= 0; j
< p
.GetCount(); j
++)
6945 partialExtents
->Add(oldWidth
+ p
[j
]);
6949 dc
.GetTextExtent(stringChunk
, & w
, & h
, & descent
);
6957 int charHeight
= dc
.GetCharHeight();
6958 if ((*partialExtents
).GetCount() > 0)
6959 w
= (*partialExtents
)[partialExtents
->GetCount()-1];
6962 size
= wxSize(w
, charHeight
);
6966 size
= wxSize(width
, dc
.GetCharHeight());
6970 dc
.GetTextExtent(wxT("X"), & w
, & h
, & descent
);
6978 /// Do a split, returning an object containing the second part, and setting
6979 /// the first part in 'this'.
6980 wxRichTextObject
* wxRichTextPlainText::DoSplit(long pos
)
6982 long index
= pos
- GetRange().GetStart();
6984 if (index
< 0 || index
>= (int) m_text
.length())
6987 wxString firstPart
= m_text
.Mid(0, index
);
6988 wxString secondPart
= m_text
.Mid(index
);
6992 wxRichTextPlainText
* newObject
= new wxRichTextPlainText(secondPart
);
6993 newObject
->SetAttributes(GetAttributes());
6994 newObject
->SetProperties(GetProperties());
6996 newObject
->SetRange(wxRichTextRange(pos
, GetRange().GetEnd()));
6997 GetRange().SetEnd(pos
-1);
7003 void wxRichTextPlainText::CalculateRange(long start
, long& end
)
7005 end
= start
+ m_text
.length() - 1;
7006 m_range
.SetRange(start
, end
);
7010 bool wxRichTextPlainText::DeleteRange(const wxRichTextRange
& range
)
7012 wxRichTextRange r
= range
;
7014 r
.LimitTo(GetRange());
7016 if (r
.GetStart() == GetRange().GetStart() && r
.GetEnd() == GetRange().GetEnd())
7022 long startIndex
= r
.GetStart() - GetRange().GetStart();
7023 long len
= r
.GetLength();
7025 m_text
= m_text
.Mid(0, startIndex
) + m_text
.Mid(startIndex
+len
);
7029 /// Get text for the given range.
7030 wxString
wxRichTextPlainText::GetTextForRange(const wxRichTextRange
& range
) const
7032 wxRichTextRange r
= range
;
7034 r
.LimitTo(GetRange());
7036 long startIndex
= r
.GetStart() - GetRange().GetStart();
7037 long len
= r
.GetLength();
7039 return m_text
.Mid(startIndex
, len
);
7042 /// Returns true if this object can merge itself with the given one.
7043 bool wxRichTextPlainText::CanMerge(wxRichTextObject
* object
, wxRichTextDrawingContext
& context
) const
7046 if (!context
.GetVirtualAttributesEnabled())
7048 return object
->GetClassInfo() == wxCLASSINFO(wxRichTextPlainText
) &&
7049 (m_text
.empty() || (wxTextAttrEq(GetAttributes(), object
->GetAttributes()) && m_properties
== object
->GetProperties()));
7053 wxRichTextPlainText
* otherObj
= wxDynamicCast(object
, wxRichTextPlainText
);
7054 if (!otherObj
|| m_text
.empty())
7057 if (!wxTextAttrEq(GetAttributes(), object
->GetAttributes()) || !(m_properties
== object
->GetProperties()))
7060 // Check if differing virtual attributes makes it impossible to merge
7063 bool hasVirtualAttr1
= context
.HasVirtualAttributes((wxRichTextObject
*) this);
7064 bool hasVirtualAttr2
= context
.HasVirtualAttributes((wxRichTextObject
*) object
);
7065 if (!hasVirtualAttr1
&& !hasVirtualAttr2
)
7067 else if (hasVirtualAttr1
!= hasVirtualAttr2
)
7071 wxRichTextAttr virtualAttr1
= context
.GetVirtualAttributes((wxRichTextObject
*) this);
7072 wxRichTextAttr virtualAttr2
= context
.GetVirtualAttributes((wxRichTextObject
*) object
);
7073 return virtualAttr1
== virtualAttr2
;
7078 /// Returns true if this object merged itself with the given one.
7079 /// The calling code will then delete the given object.
7080 bool wxRichTextPlainText::Merge(wxRichTextObject
* object
, wxRichTextDrawingContext
& WXUNUSED(context
))
7082 wxRichTextPlainText
* textObject
= wxDynamicCast(object
, wxRichTextPlainText
);
7083 wxASSERT( textObject
!= NULL
);
7087 m_text
+= textObject
->GetText();
7088 wxRichTextApplyStyle(m_attributes
, textObject
->GetAttributes());
7095 bool wxRichTextPlainText::CanSplit(wxRichTextDrawingContext
& context
) const
7097 // If this object has any virtual attributes at all, whether for the whole object
7098 // or individual ones, we should try splitting it by calling Split.
7099 // Must be more than one character in order to be able to split.
7100 return m_text
.Length() > 1 && context
.HasVirtualAttributes((wxRichTextObject
*) this);
7103 wxRichTextObject
* wxRichTextPlainText::Split(wxRichTextDrawingContext
& context
)
7105 int count
= context
.GetVirtualSubobjectAttributesCount(this);
7106 if (count
> 0 && GetParent())
7108 wxRichTextCompositeObject
* parent
= wxDynamicCast(GetParent(), wxRichTextCompositeObject
);
7109 wxRichTextObjectList::compatibility_iterator node
= parent
->GetChildren().Find(this);
7112 const wxRichTextAttr emptyAttr
;
7113 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
7115 wxArrayInt positions
;
7116 wxRichTextAttrArray attributes
;
7117 if (context
.GetVirtualSubobjectAttributes(this, positions
, attributes
) && positions
.GetCount() > 0)
7119 wxASSERT(positions
.GetCount() == attributes
.GetCount());
7121 // We will gather up runs of text with the same virtual attributes
7123 int len
= m_text
.Length();
7126 // runStart and runEnd represent the accumulated run with a consistent attribute
7127 // that hasn't yet been appended
7130 wxRichTextAttr currentAttr
;
7131 wxString text
= m_text
;
7132 wxRichTextPlainText
* lastPlainText
= this;
7134 for (i
= 0; i
< (int) positions
.GetCount(); i
++)
7136 int pos
= positions
[i
];
7137 wxASSERT(pos
>= 0 && pos
< len
);
7138 if (pos
>= 0 && pos
< len
)
7140 const wxRichTextAttr
& attr
= attributes
[i
];
7147 // Check if there was a gap from the last known attribute and this.
7148 // In that case, we need to do something with the span of non-attributed text.
7149 else if ((pos
-1) > runEnd
)
7153 // We hadn't processed anything previously, so the previous run is from the text start
7154 // to just before this position. The current attribute remains empty.
7160 // If the previous attribute matches the gap's attribute (i.e., no attributes)
7161 // then just extend the run.
7162 if (currentAttr
.IsDefault())
7168 // We need to add an object, or reuse the existing one.
7171 lastPlainText
= this;
7172 SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7176 wxRichTextPlainText
* obj
= new wxRichTextPlainText
;
7177 lastPlainText
= obj
;
7178 obj
->SetAttributes(GetAttributes());
7179 obj
->SetProperties(GetProperties());
7180 obj
->SetParent(parent
);
7182 obj
->SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7184 parent
->GetChildren().Insert(next
, obj
);
7186 parent
->GetChildren().Append(obj
);
7189 runStart
= runEnd
+1;
7192 currentAttr
= emptyAttr
;
7197 wxASSERT(runEnd
== pos
-1);
7199 // Now we only have to deal with the previous run
7200 if (currentAttr
== attr
)
7202 // If we still have the same attributes, then we
7203 // simply increase the run size.
7210 // We need to add an object, or reuse the existing one.
7213 lastPlainText
= this;
7214 SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7218 wxRichTextPlainText
* obj
= new wxRichTextPlainText
;
7219 lastPlainText
= obj
;
7220 obj
->SetAttributes(GetAttributes());
7221 obj
->SetProperties(GetProperties());
7222 obj
->SetParent(parent
);
7224 obj
->SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7226 parent
->GetChildren().Insert(next
, obj
);
7228 parent
->GetChildren().Append(obj
);
7240 // We may still have a run to add, and possibly a no-attribute text fragment after that.
7241 // If the whole string was already a single attribute (the run covers the whole string), don't split.
7242 if ((runStart
!= -1) && !(runStart
== 0 && runEnd
== (len
-1)))
7244 // If the current attribute is empty, merge the run with the next fragment
7245 // which by definition (because it's not specified) has empty attributes.
7246 if (currentAttr
.IsDefault())
7249 if (runEnd
< (len
-1))
7251 // We need to add an object, or reuse the existing one.
7254 lastPlainText
= this;
7255 SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7259 wxRichTextPlainText
* obj
= new wxRichTextPlainText
;
7260 lastPlainText
= obj
;
7261 obj
->SetAttributes(GetAttributes());
7262 obj
->SetProperties(GetProperties());
7263 obj
->SetParent(parent
);
7265 obj
->SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7267 parent
->GetChildren().Insert(next
, obj
);
7269 parent
->GetChildren().Append(obj
);
7272 runStart
= runEnd
+1;
7276 // Now the last, non-attributed fragment at the end, if any
7277 if ((runStart
< len
) && !(runStart
== 0 && runEnd
== (len
-1)))
7279 wxASSERT(runStart
!= 0);
7281 wxRichTextPlainText
* obj
= new wxRichTextPlainText
;
7282 obj
->SetAttributes(GetAttributes());
7283 obj
->SetProperties(GetProperties());
7284 obj
->SetParent(parent
);
7286 obj
->SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7288 parent
->GetChildren().Insert(next
, obj
);
7290 parent
->GetChildren().Append(obj
);
7292 lastPlainText
= obj
;
7296 return lastPlainText
;
7303 /// Dump to output stream for debugging
7304 void wxRichTextPlainText::Dump(wxTextOutputStream
& stream
)
7306 wxRichTextObject::Dump(stream
);
7307 stream
<< m_text
<< wxT("\n");
7310 /// Get the first position from pos that has a line break character.
7311 long wxRichTextPlainText::GetFirstLineBreakPosition(long pos
)
7314 int len
= m_text
.length();
7315 int startPos
= pos
- m_range
.GetStart();
7316 for (i
= startPos
; i
< len
; i
++)
7318 wxChar ch
= m_text
[i
];
7319 if (ch
== wxRichTextLineBreakChar
)
7321 return i
+ m_range
.GetStart();
7329 * This is a kind of box, used to represent the whole buffer
7332 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBuffer
, wxRichTextParagraphLayoutBox
)
7334 wxList
wxRichTextBuffer::sm_handlers
;
7335 wxList
wxRichTextBuffer::sm_drawingHandlers
;
7336 wxRichTextFieldTypeHashMap
wxRichTextBuffer::sm_fieldTypes
;
7337 wxRichTextRenderer
* wxRichTextBuffer::sm_renderer
= NULL
;
7338 int wxRichTextBuffer::sm_bulletRightMargin
= 20;
7339 float wxRichTextBuffer::sm_bulletProportion
= (float) 0.3;
7340 bool wxRichTextBuffer::sm_floatingLayoutMode
= true;
7343 void wxRichTextBuffer::Init()
7345 m_commandProcessor
= new wxCommandProcessor
;
7346 m_styleSheet
= NULL
;
7348 m_batchedCommandDepth
= 0;
7349 m_batchedCommand
= NULL
;
7353 m_dimensionScale
= 1.0;
7359 wxRichTextBuffer::~wxRichTextBuffer()
7361 delete m_commandProcessor
;
7362 delete m_batchedCommand
;
7365 ClearEventHandlers();
7368 void wxRichTextBuffer::ResetAndClearCommands()
7372 GetCommandProcessor()->ClearCommands();
7375 Invalidate(wxRICHTEXT_ALL
);
7378 void wxRichTextBuffer::Copy(const wxRichTextBuffer
& obj
)
7380 wxRichTextParagraphLayoutBox::Copy(obj
);
7382 m_styleSheet
= obj
.m_styleSheet
;
7383 m_modified
= obj
.m_modified
;
7384 m_batchedCommandDepth
= 0;
7385 if (m_batchedCommand
)
7386 delete m_batchedCommand
;
7387 m_batchedCommand
= NULL
;
7388 m_suppressUndo
= obj
.m_suppressUndo
;
7389 m_invalidRange
= obj
.m_invalidRange
;
7390 m_dimensionScale
= obj
.m_dimensionScale
;
7391 m_fontScale
= obj
.m_fontScale
;
7394 /// Push style sheet to top of stack
7395 bool wxRichTextBuffer::PushStyleSheet(wxRichTextStyleSheet
* styleSheet
)
7398 styleSheet
->InsertSheet(m_styleSheet
);
7400 SetStyleSheet(styleSheet
);
7405 /// Pop style sheet from top of stack
7406 wxRichTextStyleSheet
* wxRichTextBuffer::PopStyleSheet()
7410 wxRichTextStyleSheet
* oldSheet
= m_styleSheet
;
7411 m_styleSheet
= oldSheet
->GetNextSheet();
7420 /// Submit command to insert paragraphs
7421 bool wxRichTextBuffer::InsertParagraphsWithUndo(long pos
, const wxRichTextParagraphLayoutBox
& paragraphs
, wxRichTextCtrl
* ctrl
, int flags
)
7423 return ctrl
->GetFocusObject()->InsertParagraphsWithUndo(this, pos
, paragraphs
, ctrl
, flags
);
7426 /// Submit command to insert paragraphs
7427 bool wxRichTextParagraphLayoutBox::InsertParagraphsWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxRichTextParagraphLayoutBox
& paragraphs
, wxRichTextCtrl
* ctrl
, int WXUNUSED(flags
))
7429 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7431 action
->GetNewParagraphs() = paragraphs
;
7433 action
->SetPosition(pos
);
7435 wxRichTextRange range
= wxRichTextRange(pos
, pos
+ paragraphs
.GetOwnRange().GetEnd() - 1);
7436 if (!paragraphs
.GetPartialParagraph())
7437 range
.SetEnd(range
.GetEnd()+1);
7439 // Set the range we'll need to delete in Undo
7440 action
->SetRange(range
);
7442 buffer
->SubmitAction(action
);
7447 /// Submit command to insert the given text
7448 bool wxRichTextBuffer::InsertTextWithUndo(long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
, int flags
)
7450 return ctrl
->GetFocusObject()->InsertTextWithUndo(this, pos
, text
, ctrl
, flags
);
7453 /// Submit command to insert the given text
7454 bool wxRichTextParagraphLayoutBox::InsertTextWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
, int flags
)
7456 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7458 wxRichTextAttr
* p
= NULL
;
7459 wxRichTextAttr paraAttr
;
7460 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7462 // Get appropriate paragraph style
7463 paraAttr
= GetStyleForNewParagraph(buffer
, pos
, false, false);
7464 if (!paraAttr
.IsDefault())
7468 action
->GetNewParagraphs().AddParagraphs(text
, p
);
7470 int length
= action
->GetNewParagraphs().GetOwnRange().GetLength();
7472 if (!text
.empty() && text
.Last() != wxT('\n'))
7474 // Don't count the newline when undoing
7476 action
->GetNewParagraphs().SetPartialParagraph(true);
7478 else if (!text
.empty() && text
.Last() == wxT('\n'))
7481 action
->SetPosition(pos
);
7483 // Set the range we'll need to delete in Undo
7484 action
->SetRange(wxRichTextRange(pos
, pos
+ length
- 1));
7486 buffer
->SubmitAction(action
);
7491 /// Submit command to insert the given text
7492 bool wxRichTextBuffer::InsertNewlineWithUndo(long pos
, wxRichTextCtrl
* ctrl
, int flags
)
7494 return ctrl
->GetFocusObject()->InsertNewlineWithUndo(this, pos
, ctrl
, flags
);
7497 /// Submit command to insert the given text
7498 bool wxRichTextParagraphLayoutBox::InsertNewlineWithUndo(wxRichTextBuffer
* buffer
, long pos
, wxRichTextCtrl
* ctrl
, int flags
)
7500 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7502 wxRichTextAttr
* p
= NULL
;
7503 wxRichTextAttr paraAttr
;
7504 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7506 paraAttr
= GetStyleForNewParagraph(buffer
, pos
, false, true /* look for next paragraph style */);
7507 if (!paraAttr
.IsDefault())
7511 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7512 // Don't include box attributes such as margins
7513 attr
.GetTextBoxAttr().Reset();
7515 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(wxEmptyString
, this, & attr
);
7516 action
->GetNewParagraphs().AppendChild(newPara
);
7517 action
->GetNewParagraphs().UpdateRanges();
7518 action
->GetNewParagraphs().SetPartialParagraph(false);
7519 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
, false);
7523 newPara
->SetAttributes(*p
);
7525 if (flags
& wxRICHTEXT_INSERT_INTERACTIVE
)
7527 if (para
&& para
->GetRange().GetEnd() == pos
)
7530 // Now see if we need to number the paragraph.
7531 if (newPara
->GetAttributes().HasBulletNumber())
7533 wxRichTextAttr numberingAttr
;
7534 if (FindNextParagraphNumber(para
, numberingAttr
))
7535 wxRichTextApplyStyle(newPara
->GetAttributes(), (const wxRichTextAttr
&) numberingAttr
);
7539 action
->SetPosition(pos
);
7541 // Use the default character style
7542 if (!buffer
->GetDefaultStyle().IsDefault() && newPara
->GetChildren().GetFirst())
7544 // Check whether the default style merely reflects the paragraph/basic style,
7545 // in which case don't apply it.
7546 wxRichTextAttr
defaultStyle(buffer
->GetDefaultStyle());
7547 defaultStyle
.GetTextBoxAttr().Reset();
7548 wxRichTextAttr toApply
;
7551 wxRichTextAttr combinedAttr
= para
->GetCombinedAttributes(true /* include box attributes */);
7552 wxRichTextAttr newAttr
;
7553 // This filters out attributes that are accounted for by the current
7554 // paragraph/basic style
7555 wxRichTextApplyStyle(toApply
, defaultStyle
, & combinedAttr
);
7558 toApply
= defaultStyle
;
7560 if (!toApply
.IsDefault())
7561 newPara
->GetChildren().GetFirst()->GetData()->SetAttributes(toApply
);
7564 // Set the range we'll need to delete in Undo
7565 action
->SetRange(wxRichTextRange(pos1
, pos1
));
7567 buffer
->SubmitAction(action
);
7572 /// Submit command to insert the given image
7573 bool wxRichTextBuffer::InsertImageWithUndo(long pos
, const wxRichTextImageBlock
& imageBlock
, wxRichTextCtrl
* ctrl
, int flags
,
7574 const wxRichTextAttr
& textAttr
)
7576 return ctrl
->GetFocusObject()->InsertImageWithUndo(this, pos
, imageBlock
, ctrl
, flags
, textAttr
);
7579 /// Submit command to insert the given image
7580 bool wxRichTextParagraphLayoutBox::InsertImageWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxRichTextImageBlock
& imageBlock
,
7581 wxRichTextCtrl
* ctrl
, int flags
,
7582 const wxRichTextAttr
& textAttr
)
7584 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7586 wxRichTextAttr
* p
= NULL
;
7587 wxRichTextAttr paraAttr
;
7588 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7590 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
7591 if (!paraAttr
.IsDefault())
7595 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7597 // Don't include box attributes such as margins
7598 attr
.GetTextBoxAttr().Reset();
7600 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7602 newPara
->SetAttributes(*p
);
7604 wxRichTextImage
* imageObject
= new wxRichTextImage(imageBlock
, newPara
);
7605 newPara
->AppendChild(imageObject
);
7606 imageObject
->SetAttributes(textAttr
);
7607 action
->GetNewParagraphs().AppendChild(newPara
);
7608 action
->GetNewParagraphs().UpdateRanges();
7610 action
->GetNewParagraphs().SetPartialParagraph(true);
7612 action
->SetPosition(pos
);
7614 // Set the range we'll need to delete in Undo
7615 action
->SetRange(wxRichTextRange(pos
, pos
));
7617 buffer
->SubmitAction(action
);
7622 // Insert an object with no change of it
7623 wxRichTextObject
* wxRichTextBuffer::InsertObjectWithUndo(long pos
, wxRichTextObject
*object
, wxRichTextCtrl
* ctrl
, int flags
)
7625 return ctrl
->GetFocusObject()->InsertObjectWithUndo(this, pos
, object
, ctrl
, flags
);
7628 // Insert an object with no change of it
7629 wxRichTextObject
* wxRichTextParagraphLayoutBox::InsertObjectWithUndo(wxRichTextBuffer
* buffer
, long pos
, wxRichTextObject
*object
, wxRichTextCtrl
* ctrl
, int flags
)
7631 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Object"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7633 wxRichTextAttr
* p
= NULL
;
7634 wxRichTextAttr paraAttr
;
7635 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7637 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
7638 if (!paraAttr
.IsDefault())
7642 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7644 // Don't include box attributes such as margins
7645 attr
.GetTextBoxAttr().Reset();
7647 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7649 newPara
->SetAttributes(*p
);
7651 newPara
->AppendChild(object
);
7652 action
->GetNewParagraphs().AppendChild(newPara
);
7653 action
->GetNewParagraphs().UpdateRanges();
7655 action
->GetNewParagraphs().SetPartialParagraph(true);
7657 action
->SetPosition(pos
);
7659 // Set the range we'll need to delete in Undo
7660 action
->SetRange(wxRichTextRange(pos
, pos
));
7662 buffer
->SubmitAction(action
);
7664 wxRichTextObject
* obj
= GetLeafObjectAtPosition(pos
);
7668 wxRichTextField
* wxRichTextParagraphLayoutBox::InsertFieldWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxString
& fieldType
,
7669 const wxRichTextProperties
& properties
,
7670 wxRichTextCtrl
* ctrl
, int flags
,
7671 const wxRichTextAttr
& textAttr
)
7673 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Field"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7675 wxRichTextAttr
* p
= NULL
;
7676 wxRichTextAttr paraAttr
;
7677 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7679 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
7680 if (!paraAttr
.IsDefault())
7684 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7686 // Don't include box attributes such as margins
7687 attr
.GetTextBoxAttr().Reset();
7689 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7691 newPara
->SetAttributes(*p
);
7693 wxRichTextField
* fieldObject
= new wxRichTextField();
7694 fieldObject
->wxRichTextObject::SetProperties(properties
);
7695 fieldObject
->SetFieldType(fieldType
);
7696 fieldObject
->SetAttributes(textAttr
);
7697 newPara
->AppendChild(fieldObject
);
7698 action
->GetNewParagraphs().AppendChild(newPara
);
7699 action
->GetNewParagraphs().UpdateRanges();
7700 action
->GetNewParagraphs().SetPartialParagraph(true);
7701 action
->SetPosition(pos
);
7703 // Set the range we'll need to delete in Undo
7704 action
->SetRange(wxRichTextRange(pos
, pos
));
7706 buffer
->SubmitAction(action
);
7708 wxRichTextField
* obj
= wxDynamicCast(GetLeafObjectAtPosition(pos
), wxRichTextField
);
7712 /// Get the style that is appropriate for a new paragraph at this position.
7713 /// If the previous paragraph has a paragraph style name, look up the next-paragraph
7715 wxRichTextAttr
wxRichTextParagraphLayoutBox::GetStyleForNewParagraph(wxRichTextBuffer
* buffer
, long pos
, bool caretPosition
, bool lookUpNewParaStyle
) const
7717 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
, caretPosition
);
7720 wxRichTextAttr attr
;
7721 bool foundAttributes
= false;
7723 // Look for a matching paragraph style
7724 if (lookUpNewParaStyle
&& !para
->GetAttributes().GetParagraphStyleName().IsEmpty() && buffer
->GetStyleSheet())
7726 wxRichTextParagraphStyleDefinition
* paraDef
= buffer
->GetStyleSheet()->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
7729 // If we're not at the end of the paragraph, then we apply THIS style, and not the designated next style.
7730 if (para
->GetRange().GetEnd() == pos
&& !paraDef
->GetNextStyle().IsEmpty())
7732 wxRichTextParagraphStyleDefinition
* nextParaDef
= buffer
->GetStyleSheet()->FindParagraphStyle(paraDef
->GetNextStyle());
7735 foundAttributes
= true;
7736 attr
= nextParaDef
->GetStyleMergedWithBase(buffer
->GetStyleSheet());
7740 // If we didn't find the 'next style', use this style instead.
7741 if (!foundAttributes
)
7743 foundAttributes
= true;
7744 attr
= paraDef
->GetStyleMergedWithBase(buffer
->GetStyleSheet());
7749 // Also apply list style if present
7750 if (lookUpNewParaStyle
&& !para
->GetAttributes().GetListStyleName().IsEmpty() && buffer
->GetStyleSheet())
7752 wxRichTextListStyleDefinition
* listDef
= buffer
->GetStyleSheet()->FindListStyle(para
->GetAttributes().GetListStyleName());
7755 int thisIndent
= para
->GetAttributes().GetLeftIndent();
7756 int thisLevel
= para
->GetAttributes().HasOutlineLevel() ? para
->GetAttributes().GetOutlineLevel() : listDef
->FindLevelForIndent(thisIndent
);
7758 // Apply the overall list style, and item style for this level
7759 wxRichTextAttr
listStyle(listDef
->GetCombinedStyleForLevel(thisLevel
, buffer
->GetStyleSheet()));
7760 wxRichTextApplyStyle(attr
, listStyle
);
7761 attr
.SetOutlineLevel(thisLevel
);
7762 if (para
->GetAttributes().HasBulletNumber())
7763 attr
.SetBulletNumber(para
->GetAttributes().GetBulletNumber());
7767 if (!foundAttributes
)
7769 attr
= para
->GetAttributes();
7770 int flags
= attr
.GetFlags();
7772 // Eliminate character styles
7773 flags
&= ( (~ wxTEXT_ATTR_FONT
) |
7774 (~ wxTEXT_ATTR_TEXT_COLOUR
) |
7775 (~ wxTEXT_ATTR_BACKGROUND_COLOUR
) );
7776 attr
.SetFlags(flags
);
7782 return wxRichTextAttr();
7785 /// Submit command to delete this range
7786 bool wxRichTextBuffer::DeleteRangeWithUndo(const wxRichTextRange
& range
, wxRichTextCtrl
* ctrl
)
7788 return ctrl
->GetFocusObject()->DeleteRangeWithUndo(range
, ctrl
, this);
7791 /// Submit command to delete this range
7792 bool wxRichTextParagraphLayoutBox::DeleteRangeWithUndo(const wxRichTextRange
& range
, wxRichTextCtrl
* ctrl
, wxRichTextBuffer
* buffer
)
7794 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Delete"), wxRICHTEXT_DELETE
, buffer
, this, ctrl
);
7796 action
->SetPosition(ctrl
->GetCaretPosition());
7798 // Set the range to delete
7799 action
->SetRange(range
);
7801 // Copy the fragment that we'll need to restore in Undo
7802 CopyFragment(range
, action
->GetOldParagraphs());
7804 // See if we're deleting a paragraph marker, in which case we need to
7805 // make a note not to copy the attributes from the 2nd paragraph to the 1st.
7806 if (range
.GetStart() == range
.GetEnd())
7808 wxRichTextParagraph
* para
= GetParagraphAtPosition(range
.GetStart());
7809 if (para
&& para
->GetRange().GetEnd() == range
.GetEnd())
7811 wxRichTextParagraph
* nextPara
= GetParagraphAtPosition(range
.GetStart()+1);
7812 if (nextPara
&& nextPara
!= para
)
7814 action
->GetOldParagraphs().GetChildren().GetFirst()->GetData()->SetAttributes(nextPara
->GetAttributes());
7815 action
->GetOldParagraphs().GetAttributes().SetFlags(action
->GetOldParagraphs().GetAttributes().GetFlags() | wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
);
7820 buffer
->SubmitAction(action
);
7825 /// Collapse undo/redo commands
7826 bool wxRichTextBuffer::BeginBatchUndo(const wxString
& cmdName
)
7828 if (m_batchedCommandDepth
== 0)
7830 wxASSERT(m_batchedCommand
== NULL
);
7831 if (m_batchedCommand
)
7833 GetCommandProcessor()->Store(m_batchedCommand
);
7835 m_batchedCommand
= new wxRichTextCommand(cmdName
);
7838 m_batchedCommandDepth
++;
7843 /// Collapse undo/redo commands
7844 bool wxRichTextBuffer::EndBatchUndo()
7846 m_batchedCommandDepth
--;
7848 wxASSERT(m_batchedCommandDepth
>= 0);
7849 wxASSERT(m_batchedCommand
!= NULL
);
7851 if (m_batchedCommandDepth
== 0)
7853 GetCommandProcessor()->Store(m_batchedCommand
);
7854 m_batchedCommand
= NULL
;
7860 /// Submit immediately, or delay according to whether collapsing is on
7861 bool wxRichTextBuffer::SubmitAction(wxRichTextAction
* action
)
7863 if (action
&& !action
->GetNewParagraphs().IsEmpty())
7864 PrepareContent(action
->GetNewParagraphs());
7866 if (BatchingUndo() && m_batchedCommand
&& !SuppressingUndo())
7868 wxRichTextCommand
* cmd
= new wxRichTextCommand(action
->GetName());
7869 cmd
->AddAction(action
);
7871 cmd
->GetActions().Clear();
7874 m_batchedCommand
->AddAction(action
);
7878 wxRichTextCommand
* cmd
= new wxRichTextCommand(action
->GetName());
7879 cmd
->AddAction(action
);
7881 // Only store it if we're not suppressing undo.
7882 return GetCommandProcessor()->Submit(cmd
, !SuppressingUndo());
7888 /// Begin suppressing undo/redo commands.
7889 bool wxRichTextBuffer::BeginSuppressUndo()
7896 /// End suppressing undo/redo commands.
7897 bool wxRichTextBuffer::EndSuppressUndo()
7904 /// Begin using a style
7905 bool wxRichTextBuffer::BeginStyle(const wxRichTextAttr
& style
)
7907 wxRichTextAttr
newStyle(GetDefaultStyle());
7908 newStyle
.GetTextBoxAttr().Reset();
7910 // Save the old default style
7911 m_attributeStack
.Append((wxObject
*) new wxRichTextAttr(newStyle
));
7913 wxRichTextApplyStyle(newStyle
, style
);
7914 newStyle
.SetFlags(style
.GetFlags()|newStyle
.GetFlags());
7916 SetDefaultStyle(newStyle
);
7922 bool wxRichTextBuffer::EndStyle()
7924 if (!m_attributeStack
.GetFirst())
7926 wxLogDebug(_("Too many EndStyle calls!"));
7930 wxList::compatibility_iterator node
= m_attributeStack
.GetLast();
7931 wxRichTextAttr
* attr
= (wxRichTextAttr
*)node
->GetData();
7932 m_attributeStack
.Erase(node
);
7934 SetDefaultStyle(*attr
);
7941 bool wxRichTextBuffer::EndAllStyles()
7943 while (m_attributeStack
.GetCount() != 0)
7948 /// Clear the style stack
7949 void wxRichTextBuffer::ClearStyleStack()
7951 for (wxList::compatibility_iterator node
= m_attributeStack
.GetFirst(); node
; node
= node
->GetNext())
7952 delete (wxRichTextAttr
*) node
->GetData();
7953 m_attributeStack
.Clear();
7956 /// Begin using bold
7957 bool wxRichTextBuffer::BeginBold()
7959 wxRichTextAttr attr
;
7960 attr
.SetFontWeight(wxFONTWEIGHT_BOLD
);
7962 return BeginStyle(attr
);
7965 /// Begin using italic
7966 bool wxRichTextBuffer::BeginItalic()
7968 wxRichTextAttr attr
;
7969 attr
.SetFontStyle(wxFONTSTYLE_ITALIC
);
7971 return BeginStyle(attr
);
7974 /// Begin using underline
7975 bool wxRichTextBuffer::BeginUnderline()
7977 wxRichTextAttr attr
;
7978 attr
.SetFontUnderlined(true);
7980 return BeginStyle(attr
);
7983 /// Begin using point size
7984 bool wxRichTextBuffer::BeginFontSize(int pointSize
)
7986 wxRichTextAttr attr
;
7987 attr
.SetFontSize(pointSize
);
7989 return BeginStyle(attr
);
7992 /// Begin using this font
7993 bool wxRichTextBuffer::BeginFont(const wxFont
& font
)
7995 wxRichTextAttr attr
;
7998 return BeginStyle(attr
);
8001 /// Begin using this colour
8002 bool wxRichTextBuffer::BeginTextColour(const wxColour
& colour
)
8004 wxRichTextAttr attr
;
8005 attr
.SetFlags(wxTEXT_ATTR_TEXT_COLOUR
);
8006 attr
.SetTextColour(colour
);
8008 return BeginStyle(attr
);
8011 /// Begin using alignment
8012 bool wxRichTextBuffer::BeginAlignment(wxTextAttrAlignment alignment
)
8014 wxRichTextAttr attr
;
8015 attr
.SetFlags(wxTEXT_ATTR_ALIGNMENT
);
8016 attr
.SetAlignment(alignment
);
8018 return BeginStyle(attr
);
8021 /// Begin left indent
8022 bool wxRichTextBuffer::BeginLeftIndent(int leftIndent
, int leftSubIndent
)
8024 wxRichTextAttr attr
;
8025 attr
.SetFlags(wxTEXT_ATTR_LEFT_INDENT
);
8026 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
8028 return BeginStyle(attr
);
8031 /// Begin right indent
8032 bool wxRichTextBuffer::BeginRightIndent(int rightIndent
)
8034 wxRichTextAttr attr
;
8035 attr
.SetFlags(wxTEXT_ATTR_RIGHT_INDENT
);
8036 attr
.SetRightIndent(rightIndent
);
8038 return BeginStyle(attr
);
8041 /// Begin paragraph spacing
8042 bool wxRichTextBuffer::BeginParagraphSpacing(int before
, int after
)
8046 flags
|= wxTEXT_ATTR_PARA_SPACING_BEFORE
;
8048 flags
|= wxTEXT_ATTR_PARA_SPACING_AFTER
;
8050 wxRichTextAttr attr
;
8051 attr
.SetFlags(flags
);
8052 attr
.SetParagraphSpacingBefore(before
);
8053 attr
.SetParagraphSpacingAfter(after
);
8055 return BeginStyle(attr
);
8058 /// Begin line spacing
8059 bool wxRichTextBuffer::BeginLineSpacing(int lineSpacing
)
8061 wxRichTextAttr attr
;
8062 attr
.SetFlags(wxTEXT_ATTR_LINE_SPACING
);
8063 attr
.SetLineSpacing(lineSpacing
);
8065 return BeginStyle(attr
);
8068 /// Begin numbered bullet
8069 bool wxRichTextBuffer::BeginNumberedBullet(int bulletNumber
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
8071 wxRichTextAttr attr
;
8072 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
8073 attr
.SetBulletStyle(bulletStyle
);
8074 attr
.SetBulletNumber(bulletNumber
);
8075 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
8077 return BeginStyle(attr
);
8080 /// Begin symbol bullet
8081 bool wxRichTextBuffer::BeginSymbolBullet(const wxString
& symbol
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
8083 wxRichTextAttr attr
;
8084 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
8085 attr
.SetBulletStyle(bulletStyle
);
8086 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
8087 attr
.SetBulletText(symbol
);
8089 return BeginStyle(attr
);
8092 /// Begin standard bullet
8093 bool wxRichTextBuffer::BeginStandardBullet(const wxString
& bulletName
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
8095 wxRichTextAttr attr
;
8096 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
8097 attr
.SetBulletStyle(bulletStyle
);
8098 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
8099 attr
.SetBulletName(bulletName
);
8101 return BeginStyle(attr
);
8104 /// Begin named character style
8105 bool wxRichTextBuffer::BeginCharacterStyle(const wxString
& characterStyle
)
8107 if (GetStyleSheet())
8109 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->FindCharacterStyle(characterStyle
);
8112 wxRichTextAttr attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
8113 return BeginStyle(attr
);
8119 /// Begin named paragraph style
8120 bool wxRichTextBuffer::BeginParagraphStyle(const wxString
& paragraphStyle
)
8122 if (GetStyleSheet())
8124 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(paragraphStyle
);
8127 wxRichTextAttr attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
8128 return BeginStyle(attr
);
8134 /// Begin named list style
8135 bool wxRichTextBuffer::BeginListStyle(const wxString
& listStyle
, int level
, int number
)
8137 if (GetStyleSheet())
8139 wxRichTextListStyleDefinition
* def
= GetStyleSheet()->FindListStyle(listStyle
);
8142 wxRichTextAttr
attr(def
->GetCombinedStyleForLevel(level
));
8144 attr
.SetBulletNumber(number
);
8146 return BeginStyle(attr
);
8153 bool wxRichTextBuffer::BeginURL(const wxString
& url
, const wxString
& characterStyle
)
8155 wxRichTextAttr attr
;
8157 if (!characterStyle
.IsEmpty() && GetStyleSheet())
8159 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->FindCharacterStyle(characterStyle
);
8162 attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
8167 return BeginStyle(attr
);
8170 /// Adds a handler to the end
8171 void wxRichTextBuffer::AddHandler(wxRichTextFileHandler
*handler
)
8173 sm_handlers
.Append(handler
);
8176 /// Inserts a handler at the front
8177 void wxRichTextBuffer::InsertHandler(wxRichTextFileHandler
*handler
)
8179 sm_handlers
.Insert( handler
);
8182 /// Removes a handler
8183 bool wxRichTextBuffer::RemoveHandler(const wxString
& name
)
8185 wxRichTextFileHandler
*handler
= FindHandler(name
);
8188 sm_handlers
.DeleteObject(handler
);
8196 /// Finds a handler by filename or, if supplied, type
8197 wxRichTextFileHandler
*wxRichTextBuffer::FindHandlerFilenameOrType(const wxString
& filename
,
8198 wxRichTextFileType imageType
)
8200 if (imageType
!= wxRICHTEXT_TYPE_ANY
)
8201 return FindHandler(imageType
);
8202 else if (!filename
.IsEmpty())
8204 wxString path
, file
, ext
;
8205 wxFileName::SplitPath(filename
, & path
, & file
, & ext
);
8206 return FindHandler(ext
, imageType
);
8213 /// Finds a handler by name
8214 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(const wxString
& name
)
8216 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
8219 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
8220 if (handler
->GetName().Lower() == name
.Lower()) return handler
;
8222 node
= node
->GetNext();
8227 /// Finds a handler by extension and type
8228 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(const wxString
& extension
, wxRichTextFileType type
)
8230 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
8233 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
8234 if ( handler
->GetExtension().Lower() == extension
.Lower() &&
8235 (type
== wxRICHTEXT_TYPE_ANY
|| handler
->GetType() == type
) )
8237 node
= node
->GetNext();
8242 /// Finds a handler by type
8243 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(wxRichTextFileType type
)
8245 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
8248 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
8249 if (handler
->GetType() == type
) return handler
;
8250 node
= node
->GetNext();
8255 void wxRichTextBuffer::InitStandardHandlers()
8257 if (!FindHandler(wxRICHTEXT_TYPE_TEXT
))
8258 AddHandler(new wxRichTextPlainTextHandler
);
8261 void wxRichTextBuffer::CleanUpHandlers()
8263 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
8266 wxRichTextFileHandler
* handler
= (wxRichTextFileHandler
*)node
->GetData();
8267 wxList::compatibility_iterator next
= node
->GetNext();
8272 sm_handlers
.Clear();
8275 wxString
wxRichTextBuffer::GetExtWildcard(bool combine
, bool save
, wxArrayInt
* types
)
8282 wxList::compatibility_iterator node
= GetHandlers().GetFirst();
8286 wxRichTextFileHandler
* handler
= (wxRichTextFileHandler
*) node
->GetData();
8287 if (handler
->IsVisible() && ((save
&& handler
->CanSave()) || (!save
&& handler
->CanLoad())))
8292 wildcard
+= wxT(";");
8293 wildcard
+= wxT("*.") + handler
->GetExtension();
8298 wildcard
+= wxT("|");
8299 wildcard
+= handler
->GetName();
8300 wildcard
+= wxT(" ");
8301 wildcard
+= _("files");
8302 wildcard
+= wxT(" (*.");
8303 wildcard
+= handler
->GetExtension();
8304 wildcard
+= wxT(")|*.");
8305 wildcard
+= handler
->GetExtension();
8307 types
->Add(handler
->GetType());
8312 node
= node
->GetNext();
8316 wildcard
= wxT("(") + wildcard
+ wxT(")|") + wildcard
;
8320 #if wxUSE_FFILE && wxUSE_STREAMS
8322 bool wxRichTextBuffer::LoadFile(const wxString
& filename
, wxRichTextFileType type
)
8324 wxRichTextFileHandler
* handler
= FindHandlerFilenameOrType(filename
, type
);
8327 SetDefaultStyle(wxRichTextAttr());
8328 handler
->SetFlags(GetHandlerFlags());
8329 bool success
= handler
->LoadFile(this, filename
);
8330 Invalidate(wxRICHTEXT_ALL
);
8338 bool wxRichTextBuffer::SaveFile(const wxString
& filename
, wxRichTextFileType type
)
8340 wxRichTextFileHandler
* handler
= FindHandlerFilenameOrType(filename
, type
);
8343 handler
->SetFlags(GetHandlerFlags());
8344 return handler
->SaveFile(this, filename
);
8349 #endif // wxUSE_FFILE && wxUSE_STREAMS
8352 /// Load from a stream
8353 bool wxRichTextBuffer::LoadFile(wxInputStream
& stream
, wxRichTextFileType type
)
8355 wxRichTextFileHandler
* handler
= FindHandler(type
);
8358 SetDefaultStyle(wxRichTextAttr());
8359 handler
->SetFlags(GetHandlerFlags());
8360 bool success
= handler
->LoadFile(this, stream
);
8361 Invalidate(wxRICHTEXT_ALL
);
8368 /// Save to a stream
8369 bool wxRichTextBuffer::SaveFile(wxOutputStream
& stream
, wxRichTextFileType type
)
8371 wxRichTextFileHandler
* handler
= FindHandler(type
);
8374 handler
->SetFlags(GetHandlerFlags());
8375 return handler
->SaveFile(this, stream
);
8380 #endif // wxUSE_STREAMS
8382 /// Copy the range to the clipboard
8383 bool wxRichTextBuffer::CopyToClipboard(const wxRichTextRange
& range
)
8385 bool success
= false;
8386 wxRichTextParagraphLayoutBox
* container
= this;
8387 if (GetRichTextCtrl())
8388 container
= GetRichTextCtrl()->GetFocusObject();
8390 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
8392 if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open())
8394 wxTheClipboard
->Clear();
8396 // Add composite object
8398 wxDataObjectComposite
* compositeObject
= new wxDataObjectComposite();
8401 wxString text
= container
->GetTextForRange(range
);
8404 text
= wxTextFile::Translate(text
, wxTextFileType_Dos
);
8407 compositeObject
->Add(new wxTextDataObject(text
), false /* not preferred */);
8410 // Add rich text buffer data object. This needs the XML handler to be present.
8412 if (FindHandler(wxRICHTEXT_TYPE_XML
))
8414 wxRichTextBuffer
* richTextBuf
= new wxRichTextBuffer
;
8415 container
->CopyFragment(range
, *richTextBuf
);
8417 compositeObject
->Add(new wxRichTextBufferDataObject(richTextBuf
), true /* preferred */);
8420 if (wxTheClipboard
->SetData(compositeObject
))
8423 wxTheClipboard
->Close();
8432 /// Paste the clipboard content to the buffer
8433 bool wxRichTextBuffer::PasteFromClipboard(long position
)
8435 bool success
= false;
8436 wxRichTextParagraphLayoutBox
* container
= this;
8437 if (GetRichTextCtrl())
8438 container
= GetRichTextCtrl()->GetFocusObject();
8440 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
8441 if (CanPasteFromClipboard())
8443 if (wxTheClipboard
->Open())
8445 if (wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())))
8447 wxRichTextBufferDataObject data
;
8448 wxTheClipboard
->GetData(data
);
8449 wxRichTextBuffer
* richTextBuffer
= data
.GetRichTextBuffer();
8452 container
->InsertParagraphsWithUndo(this, position
+1, *richTextBuffer
, GetRichTextCtrl(), 0);
8453 if (GetRichTextCtrl())
8454 GetRichTextCtrl()->ShowPosition(position
+ richTextBuffer
->GetOwnRange().GetEnd());
8455 delete richTextBuffer
;
8458 else if (wxTheClipboard
->IsSupported(wxDF_TEXT
)
8460 || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
)
8464 wxTextDataObject data
;
8465 wxTheClipboard
->GetData(data
);
8466 wxString
text(data
.GetText());
8469 text2
.Alloc(text
.Length()+1);
8471 for (i
= 0; i
< text
.Length(); i
++)
8473 wxChar ch
= text
[i
];
8474 if (ch
!= wxT('\r'))
8478 wxString text2
= text
;
8480 container
->InsertTextWithUndo(this, position
+1, text2
, GetRichTextCtrl(), wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
);
8482 if (GetRichTextCtrl())
8483 GetRichTextCtrl()->ShowPosition(position
+ text2
.Length());
8487 else if (wxTheClipboard
->IsSupported(wxDF_BITMAP
))
8489 wxBitmapDataObject data
;
8490 wxTheClipboard
->GetData(data
);
8491 wxBitmap
bitmap(data
.GetBitmap());
8492 wxImage
image(bitmap
.ConvertToImage());
8494 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, this, container
, GetRichTextCtrl(), false);
8496 action
->GetNewParagraphs().AddImage(image
);
8498 if (action
->GetNewParagraphs().GetChildCount() == 1)
8499 action
->GetNewParagraphs().SetPartialParagraph(true);
8501 action
->SetPosition(position
+1);
8503 // Set the range we'll need to delete in Undo
8504 action
->SetRange(wxRichTextRange(position
+1, position
+1));
8506 SubmitAction(action
);
8510 wxTheClipboard
->Close();
8514 wxUnusedVar(position
);
8519 /// Can we paste from the clipboard?
8520 bool wxRichTextBuffer::CanPasteFromClipboard() const
8522 bool canPaste
= false;
8523 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
8524 if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open())
8526 if (wxTheClipboard
->IsSupported(wxDF_TEXT
)
8528 || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
)
8530 || wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())) ||
8531 wxTheClipboard
->IsSupported(wxDF_BITMAP
))
8535 wxTheClipboard
->Close();
8541 /// Dumps contents of buffer for debugging purposes
8542 void wxRichTextBuffer::Dump()
8546 wxStringOutputStream
stream(& text
);
8547 wxTextOutputStream
textStream(stream
);
8554 /// Add an event handler
8555 bool wxRichTextBuffer::AddEventHandler(wxEvtHandler
* handler
)
8557 m_eventHandlers
.Append(handler
);
8561 /// Remove an event handler
8562 bool wxRichTextBuffer::RemoveEventHandler(wxEvtHandler
* handler
, bool deleteHandler
)
8564 wxList::compatibility_iterator node
= m_eventHandlers
.Find(handler
);
8567 m_eventHandlers
.Erase(node
);
8577 /// Clear event handlers
8578 void wxRichTextBuffer::ClearEventHandlers()
8580 m_eventHandlers
.Clear();
8583 /// Send event to event handlers. If sendToAll is true, will send to all event handlers,
8584 /// otherwise will stop at the first successful one.
8585 bool wxRichTextBuffer::SendEvent(wxEvent
& event
, bool sendToAll
)
8587 bool success
= false;
8588 for (wxList::compatibility_iterator node
= m_eventHandlers
.GetFirst(); node
; node
= node
->GetNext())
8590 wxEvtHandler
* handler
= (wxEvtHandler
*) node
->GetData();
8591 if (handler
->ProcessEvent(event
))
8601 /// Set style sheet and notify of the change
8602 bool wxRichTextBuffer::SetStyleSheetAndNotify(wxRichTextStyleSheet
* sheet
)
8604 wxRichTextStyleSheet
* oldSheet
= GetStyleSheet();
8606 wxWindowID winid
= wxID_ANY
;
8607 if (GetRichTextCtrl())
8608 winid
= GetRichTextCtrl()->GetId();
8610 wxRichTextEvent
event(wxEVT_RICHTEXT_STYLESHEET_REPLACING
, winid
);
8611 event
.SetEventObject(GetRichTextCtrl());
8612 event
.SetContainer(GetRichTextCtrl()->GetFocusObject());
8613 event
.SetOldStyleSheet(oldSheet
);
8614 event
.SetNewStyleSheet(sheet
);
8617 if (SendEvent(event
) && !event
.IsAllowed())
8619 if (sheet
!= oldSheet
)
8625 if (oldSheet
&& oldSheet
!= sheet
)
8628 SetStyleSheet(sheet
);
8630 event
.SetEventType(wxEVT_RICHTEXT_STYLESHEET_REPLACED
);
8631 event
.SetOldStyleSheet(NULL
);
8634 return SendEvent(event
);
8637 /// Set renderer, deleting old one
8638 void wxRichTextBuffer::SetRenderer(wxRichTextRenderer
* renderer
)
8642 sm_renderer
= renderer
;
8645 /// Hit-testing: returns a flag indicating hit test details, plus
8646 /// information about position
8647 int wxRichTextBuffer::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
8649 int ret
= wxRichTextParagraphLayoutBox::HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
8650 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
8656 textPosition
= m_ownRange
.GetEnd()-1;
8659 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
;
8663 void wxRichTextBuffer::SetFontScale(double fontScale
)
8665 m_fontScale
= fontScale
;
8666 m_fontTable
.SetFontScale(fontScale
);
8669 void wxRichTextBuffer::SetDimensionScale(double dimScale
)
8671 m_dimensionScale
= dimScale
;
8674 bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxRichTextAttr
& bulletAttr
, const wxRect
& rect
)
8676 if (bulletAttr
.GetTextColour().IsOk())
8678 wxCheckSetPen(dc
, wxPen(bulletAttr
.GetTextColour()));
8679 wxCheckSetBrush(dc
, wxBrush(bulletAttr
.GetTextColour()));
8683 wxCheckSetPen(dc
, *wxBLACK_PEN
);
8684 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
8688 if (bulletAttr
.HasFont())
8690 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(bulletAttr
);
8693 font
= (*wxNORMAL_FONT
);
8695 wxCheckSetFont(dc
, font
);
8697 int charHeight
= dc
.GetCharHeight();
8699 int bulletWidth
= (int) (((float) charHeight
) * wxRichTextBuffer::GetBulletProportion());
8700 int bulletHeight
= bulletWidth
;
8704 // Calculate the top position of the character (as opposed to the whole line height)
8705 int y
= rect
.y
+ (rect
.height
- charHeight
);
8707 // Calculate where the bullet should be positioned
8708 y
= y
+ (charHeight
+1)/2 - (bulletHeight
+1)/2;
8710 // The margin between a bullet and text.
8711 int margin
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer::GetBulletRightMargin());
8713 if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
)
8714 x
= rect
.x
+ rect
.width
- bulletWidth
- margin
;
8715 else if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
)
8716 x
= x
+ (rect
.width
)/2 - bulletWidth
/2;
8718 if (bulletAttr
.GetBulletName() == wxT("standard/square"))
8720 dc
.DrawRectangle(x
, y
, bulletWidth
, bulletHeight
);
8722 else if (bulletAttr
.GetBulletName() == wxT("standard/diamond"))
8725 pts
[0].x
= x
; pts
[0].y
= y
+ bulletHeight
/2;
8726 pts
[1].x
= x
+ bulletWidth
/2; pts
[1].y
= y
;
8727 pts
[2].x
= x
+ bulletWidth
; pts
[2].y
= y
+ bulletHeight
/2;
8728 pts
[3].x
= x
+ bulletWidth
/2; pts
[3].y
= y
+ bulletHeight
;
8730 dc
.DrawPolygon(4, pts
);
8732 else if (bulletAttr
.GetBulletName() == wxT("standard/triangle"))
8735 pts
[0].x
= x
; pts
[0].y
= y
;
8736 pts
[1].x
= x
+ bulletWidth
; pts
[1].y
= y
+ bulletHeight
/2;
8737 pts
[2].x
= x
; pts
[2].y
= y
+ bulletHeight
;
8739 dc
.DrawPolygon(3, pts
);
8741 else if (bulletAttr
.GetBulletName() == wxT("standard/circle-outline"))
8743 wxCheckSetBrush(dc
, *wxTRANSPARENT_BRUSH
);
8744 dc
.DrawEllipse(x
, y
, bulletWidth
, bulletHeight
);
8746 else // "standard/circle", and catch-all
8748 dc
.DrawEllipse(x
, y
, bulletWidth
, bulletHeight
);
8754 bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxRichTextAttr
& attr
, const wxRect
& rect
, const wxString
& text
)
8759 if ((attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
) && !attr
.GetBulletFont().IsEmpty() && attr
.HasFont())
8761 wxRichTextAttr fontAttr
;
8762 if (attr
.HasFontPixelSize())
8763 fontAttr
.SetFontPixelSize(attr
.GetFontSize());
8765 fontAttr
.SetFontPointSize(attr
.GetFontSize());
8766 fontAttr
.SetFontStyle(attr
.GetFontStyle());
8767 fontAttr
.SetFontWeight(attr
.GetFontWeight());
8768 fontAttr
.SetFontUnderlined(attr
.GetFontUnderlined());
8769 fontAttr
.SetFontFaceName(attr
.GetBulletFont());
8770 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(fontAttr
);
8772 else if (attr
.HasFont())
8773 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(attr
);
8775 font
= (*wxNORMAL_FONT
);
8777 wxCheckSetFont(dc
, font
);
8779 if (attr
.GetTextColour().IsOk())
8780 dc
.SetTextForeground(attr
.GetTextColour());
8782 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
8784 int charHeight
= dc
.GetCharHeight();
8786 dc
.GetTextExtent(text
, & tw
, & th
);
8790 // Calculate the top position of the character (as opposed to the whole line height)
8791 int y
= rect
.y
+ (rect
.height
- charHeight
);
8793 // The margin between a bullet and text.
8794 int margin
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer::GetBulletRightMargin());
8796 if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
)
8797 x
= (rect
.x
+ rect
.width
) - tw
- margin
;
8798 else if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
)
8799 x
= x
+ (rect
.width
)/2 - tw
/2;
8801 dc
.DrawText(text
, x
, y
);
8809 bool wxRichTextStdRenderer::DrawBitmapBullet(wxRichTextParagraph
* WXUNUSED(paragraph
), wxDC
& WXUNUSED(dc
), const wxRichTextAttr
& WXUNUSED(attr
), const wxRect
& WXUNUSED(rect
))
8811 // Currently unimplemented. The intention is to store bitmaps by name in a media store associated
8812 // with the buffer. The store will allow retrieval from memory, disk or other means.
8816 /// Enumerate the standard bullet names currently supported
8817 bool wxRichTextStdRenderer::EnumerateStandardBulletNames(wxArrayString
& bulletNames
)
8819 bulletNames
.Add(wxTRANSLATE("standard/circle"));
8820 bulletNames
.Add(wxTRANSLATE("standard/circle-outline"));
8821 bulletNames
.Add(wxTRANSLATE("standard/square"));
8822 bulletNames
.Add(wxTRANSLATE("standard/diamond"));
8823 bulletNames
.Add(wxTRANSLATE("standard/triangle"));
8832 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBox
, wxRichTextParagraphLayoutBox
)
8834 wxRichTextBox::wxRichTextBox(wxRichTextObject
* parent
):
8835 wxRichTextParagraphLayoutBox(parent
)
8840 bool wxRichTextBox::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8845 // TODO: if the active object in the control, draw an indication.
8846 // We need to add the concept of active object, and not just focus object,
8847 // so we can apply commands (properties, delete, ...) to objects such as text boxes and images.
8848 // Ultimately we would like to be able to interactively resize an active object
8849 // using drag handles.
8850 return wxRichTextParagraphLayoutBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8854 void wxRichTextBox::Copy(const wxRichTextBox
& obj
)
8856 wxRichTextParagraphLayoutBox::Copy(obj
);
8859 // Edit properties via a GUI
8860 bool wxRichTextBox::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8862 wxRichTextObjectPropertiesDialog
boxDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Box Properties"));
8863 boxDlg
.SetAttributes(GetAttributes());
8865 if (boxDlg
.ShowModal() == wxID_OK
)
8867 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
8868 // indeterminate in the object.
8869 boxDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
8880 IMPLEMENT_DYNAMIC_CLASS(wxRichTextField
, wxRichTextParagraphLayoutBox
)
8882 wxRichTextField::wxRichTextField(const wxString
& fieldType
, wxRichTextObject
* parent
):
8883 wxRichTextParagraphLayoutBox(parent
)
8885 SetFieldType(fieldType
);
8889 bool wxRichTextField::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8894 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8895 if (fieldType
&& fieldType
->Draw(this, dc
, context
, range
, selection
, rect
, descent
, style
))
8898 // Fallback; but don't draw guidelines.
8899 style
&= ~wxRICHTEXT_DRAW_GUIDELINES
;
8900 return wxRichTextParagraphLayoutBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8903 bool wxRichTextField::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
)
8905 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8906 if (fieldType
&& fieldType
->Layout(this, dc
, context
, rect
, parentRect
, style
))
8910 return wxRichTextParagraphLayoutBox::Layout(dc
, context
, rect
, parentRect
, style
);
8913 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
8915 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8917 return fieldType
->GetRangeSize((wxRichTextField
*) this, range
, size
, descent
, dc
, context
, flags
, position
, parentSize
, partialExtents
);
8919 return wxRichTextParagraphLayoutBox::GetRangeSize(range
, size
, descent
, dc
, context
, flags
, position
, parentSize
, partialExtents
);
8923 void wxRichTextField::CalculateRange(long start
, long& end
)
8926 wxRichTextParagraphLayoutBox::CalculateRange(start
, end
);
8928 wxRichTextObject::CalculateRange(start
, end
);
8932 void wxRichTextField::Copy(const wxRichTextField
& obj
)
8934 wxRichTextParagraphLayoutBox::Copy(obj
);
8936 UpdateField(GetBuffer());
8939 // Edit properties via a GUI
8940 bool wxRichTextField::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8942 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8944 return fieldType
->EditProperties(this, parent
, buffer
);
8949 bool wxRichTextField::CanEditProperties() const
8951 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8953 return fieldType
->CanEditProperties((wxRichTextField
*) this);
8958 wxString
wxRichTextField::GetPropertiesMenuLabel() const
8960 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8962 return fieldType
->GetPropertiesMenuLabel((wxRichTextField
*) this);
8964 return wxEmptyString
;
8967 bool wxRichTextField::UpdateField(wxRichTextBuffer
* buffer
)
8969 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8971 return fieldType
->UpdateField(buffer
, (wxRichTextField
*) this);
8976 bool wxRichTextField::IsTopLevel() const
8978 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8980 return fieldType
->IsTopLevel((wxRichTextField
*) this);
8985 IMPLEMENT_CLASS(wxRichTextFieldType
, wxObject
)
8987 IMPLEMENT_CLASS(wxRichTextFieldTypeStandard
, wxRichTextFieldType
)
8989 wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString
& name
, const wxString
& label
, int displayStyle
)
8995 SetDisplayStyle(displayStyle
);
8998 wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString
& name
, const wxBitmap
& bitmap
, int displayStyle
)
9004 SetDisplayStyle(displayStyle
);
9007 void wxRichTextFieldTypeStandard::Init()
9009 m_displayStyle
= wxRICHTEXT_FIELD_STYLE_RECTANGLE
;
9010 m_font
= wxFont(6, wxFONTFAMILY_SWISS
, wxFONTSTYLE_NORMAL
, wxFONTWEIGHT_NORMAL
);
9011 m_textColour
= *wxWHITE
;
9012 m_borderColour
= *wxBLACK
;
9013 m_backgroundColour
= *wxBLACK
;
9014 m_verticalPadding
= 1;
9015 m_horizontalPadding
= 3;
9016 m_horizontalMargin
= 2;
9017 m_verticalMargin
= 0;
9020 void wxRichTextFieldTypeStandard::Copy(const wxRichTextFieldTypeStandard
& field
)
9022 wxRichTextFieldType::Copy(field
);
9024 m_label
= field
.m_label
;
9025 m_displayStyle
= field
.m_displayStyle
;
9026 m_font
= field
.m_font
;
9027 m_textColour
= field
.m_textColour
;
9028 m_borderColour
= field
.m_borderColour
;
9029 m_backgroundColour
= field
.m_backgroundColour
;
9030 m_verticalPadding
= field
.m_verticalPadding
;
9031 m_horizontalPadding
= field
.m_horizontalPadding
;
9032 m_horizontalMargin
= field
.m_horizontalMargin
;
9033 m_bitmap
= field
.m_bitmap
;
9036 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
))
9038 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_COMPOSITE
)
9039 return false; // USe default composite drawing
9040 else // if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_RECTANGLE || m_displayStyle == wxRICHTEXT_FIELD_STYLE_NOBORDER)
9044 wxPen
borderPen(m_borderColour
, 1, wxSOLID
);
9045 wxBrush
backgroundBrush(m_backgroundColour
);
9046 wxColour
textColour(m_textColour
);
9048 if (selection
.WithinSelection(obj
->GetRange().GetStart(), obj
))
9050 wxColour
highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
));
9051 wxColour
highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
));
9053 borderPen
= wxPen(highlightTextColour
, 1, wxSOLID
);
9054 backgroundBrush
= wxBrush(highlightColour
);
9056 wxCheckSetBrush(dc
, backgroundBrush
);
9057 wxCheckSetPen(dc
, wxPen(highlightColour
, 1, wxSOLID
));
9058 dc
.DrawRectangle(rect
);
9061 if (m_displayStyle
!= wxRICHTEXT_FIELD_STYLE_NO_BORDER
)
9064 // objectRect is the area where the content is drawn, after margins around it have been taken into account
9065 wxRect objectRect
= wxRect(wxPoint(rect
.x
+ m_horizontalMargin
, rect
.y
+ wxMax(0, rect
.height
- descent
- obj
->GetCachedSize().y
)),
9066 wxSize(obj
->GetCachedSize().x
- 2*m_horizontalMargin
- borderSize
, obj
->GetCachedSize().y
));
9068 // clientArea is where the text is actually written
9069 wxRect clientArea
= objectRect
;
9071 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_RECTANGLE
)
9073 dc
.SetPen(borderPen
);
9074 dc
.SetBrush(backgroundBrush
);
9075 dc
.DrawRoundedRectangle(objectRect
, 4.0);
9077 else if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_START_TAG
)
9079 int arrowLength
= objectRect
.height
/2;
9080 clientArea
.width
-= (arrowLength
- m_horizontalPadding
);
9083 pts
[0].x
= objectRect
.x
; pts
[0].y
= objectRect
.y
;
9084 pts
[1].x
= objectRect
.x
+ objectRect
.width
- arrowLength
; pts
[1].y
= objectRect
.y
;
9085 pts
[2].x
= objectRect
.x
+ objectRect
.width
; pts
[2].y
= objectRect
.y
+ (objectRect
.height
/2);
9086 pts
[3].x
= objectRect
.x
+ objectRect
.width
- arrowLength
; pts
[3].y
= objectRect
.y
+ objectRect
.height
;
9087 pts
[4].x
= objectRect
.x
; pts
[4].y
= objectRect
.y
+ objectRect
.height
;
9088 dc
.SetPen(borderPen
);
9089 dc
.SetBrush(backgroundBrush
);
9090 dc
.DrawPolygon(5, pts
);
9092 else if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_END_TAG
)
9094 int arrowLength
= objectRect
.height
/2;
9095 clientArea
.width
-= (arrowLength
- m_horizontalPadding
);
9096 clientArea
.x
+= (arrowLength
- m_horizontalPadding
);
9099 pts
[0].x
= objectRect
.x
+ objectRect
.width
; pts
[0].y
= objectRect
.y
;
9100 pts
[1].x
= objectRect
.x
+ arrowLength
; pts
[1].y
= objectRect
.y
;
9101 pts
[2].x
= objectRect
.x
; pts
[2].y
= objectRect
.y
+ (objectRect
.height
/2);
9102 pts
[3].x
= objectRect
.x
+ arrowLength
; pts
[3].y
= objectRect
.y
+ objectRect
.height
;
9103 pts
[4].x
= objectRect
.x
+ objectRect
.width
; pts
[4].y
= objectRect
.y
+ objectRect
.height
;
9104 dc
.SetPen(borderPen
);
9105 dc
.SetBrush(backgroundBrush
);
9106 dc
.DrawPolygon(5, pts
);
9109 if (m_bitmap
.IsOk())
9111 int x
= clientArea
.x
+ (clientArea
.width
- m_bitmap
.GetWidth())/2;
9112 int y
= clientArea
.y
+ m_verticalPadding
;
9113 dc
.DrawBitmap(m_bitmap
, x
, y
, true);
9115 if (selection
.WithinSelection(obj
->GetRange().GetStart(), obj
))
9117 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
9118 wxCheckSetPen(dc
, *wxBLACK_PEN
);
9119 dc
.SetLogicalFunction(wxINVERT
);
9120 dc
.DrawRectangle(wxRect(x
, y
, m_bitmap
.GetWidth(), m_bitmap
.GetHeight()));
9121 dc
.SetLogicalFunction(wxCOPY
);
9126 wxString
label(m_label
);
9127 if (label
.IsEmpty())
9129 int w
, h
, maxDescent
;
9131 dc
.GetTextExtent(m_label
, & w
, &h
, & maxDescent
);
9132 dc
.SetTextForeground(textColour
);
9134 int x
= clientArea
.x
+ (clientArea
.width
- w
)/2;
9135 int y
= clientArea
.y
+ (clientArea
.height
- (h
- maxDescent
))/2;
9136 dc
.DrawText(m_label
, x
, y
);
9143 bool wxRichTextFieldTypeStandard::Layout(wxRichTextField
* obj
, wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& WXUNUSED(rect
), const wxRect
& WXUNUSED(parentRect
), int style
)
9145 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_COMPOSITE
)
9146 return false; // USe default composite layout
9148 wxSize size
= GetSize(obj
, dc
, context
, style
);
9149 obj
->SetCachedSize(size
);
9150 obj
->SetMinSize(size
);
9151 obj
->SetMaxSize(size
);
9155 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
9157 if (IsTopLevel(obj
))
9158 return obj
->wxRichTextParagraphLayoutBox::GetRangeSize(range
, size
, descent
, dc
, context
, flags
, position
, parentSize
);
9161 wxSize sz
= GetSize(obj
, dc
, context
, 0);
9165 if (partialExtents
->GetCount() > 0)
9166 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
9169 partialExtents
->Add(lastSize
+ sz
.x
);
9176 wxSize
wxRichTextFieldTypeStandard::GetSize(wxRichTextField
* WXUNUSED(obj
), wxDC
& dc
, wxRichTextDrawingContext
& WXUNUSED(context
), int WXUNUSED(style
)) const
9179 int w
= 0, h
= 0, maxDescent
= 0;
9182 if (m_bitmap
.IsOk())
9184 w
= m_bitmap
.GetWidth();
9185 h
= m_bitmap
.GetHeight();
9187 sz
= wxSize(w
+ m_horizontalMargin
*2, h
+ m_verticalMargin
*2);
9191 wxString
label(m_label
);
9192 if (label
.IsEmpty())
9195 dc
.GetTextExtent(label
, & w
, &h
, & maxDescent
);
9197 sz
= wxSize(w
+ m_horizontalPadding
*2 + m_horizontalMargin
*2, h
+ m_verticalPadding
*2 + m_verticalMargin
*2);
9200 if (m_displayStyle
!= wxRICHTEXT_FIELD_STYLE_NO_BORDER
)
9202 sz
.x
+= borderSize
*2;
9203 sz
.y
+= borderSize
*2;
9206 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_START_TAG
|| m_displayStyle
== wxRICHTEXT_FIELD_STYLE_END_TAG
)
9208 // Add space for the arrow
9209 sz
.x
+= (sz
.y
/2 - m_horizontalPadding
);
9215 IMPLEMENT_DYNAMIC_CLASS(wxRichTextCell
, wxRichTextBox
)
9217 wxRichTextCell::wxRichTextCell(wxRichTextObject
* parent
):
9218 wxRichTextBox(parent
)
9223 bool wxRichTextCell::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
9225 return wxRichTextBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
9229 void wxRichTextCell::Copy(const wxRichTextCell
& obj
)
9231 wxRichTextBox::Copy(obj
);
9234 // Edit properties via a GUI
9235 bool wxRichTextCell::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
9237 // We need to gather common attributes for all selected cells.
9239 wxRichTextTable
* table
= wxDynamicCast(GetParent(), wxRichTextTable
);
9240 bool multipleCells
= false;
9241 wxRichTextAttr attr
;
9243 if (table
&& buffer
&& buffer
->GetRichTextCtrl() && buffer
->GetRichTextCtrl()->GetSelection().IsValid() &&
9244 buffer
->GetRichTextCtrl()->GetSelection().GetContainer() == GetParent())
9246 wxRichTextAttr clashingAttr
, absentAttr
;
9247 const wxRichTextSelection
& sel
= buffer
->GetRichTextCtrl()->GetSelection();
9249 int selectedCellCount
= 0;
9250 for (i
= 0; i
< sel
.GetCount(); i
++)
9252 const wxRichTextRange
& range
= sel
[i
];
9253 wxRichTextCell
* cell
= table
->GetCell(range
.GetStart());
9256 wxRichTextAttr cellStyle
= cell
->GetAttributes();
9258 CollectStyle(attr
, cellStyle
, clashingAttr
, absentAttr
);
9260 selectedCellCount
++;
9263 multipleCells
= selectedCellCount
> 1;
9267 attr
= GetAttributes();
9272 caption
= _("Multiple Cell Properties");
9274 caption
= _("Cell Properties");
9276 wxRichTextObjectPropertiesDialog
cellDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, caption
);
9277 cellDlg
.SetAttributes(attr
);
9279 wxRichTextSizePage
* sizePage
= wxDynamicCast(cellDlg
.FindPage(wxCLASSINFO(wxRichTextSizePage
)), wxRichTextSizePage
);
9282 // We don't want position and floating controls for a cell.
9283 sizePage
->ShowPositionControls(false);
9284 sizePage
->ShowFloatingControls(false);
9287 if (cellDlg
.ShowModal() == wxID_OK
)
9291 const wxRichTextSelection
& sel
= buffer
->GetRichTextCtrl()->GetSelection();
9292 // Apply the style; we interpret indeterminate attributes as 'don't touch this attribute'
9293 // since it may represent clashing attributes across multiple objects.
9294 table
->SetCellStyle(sel
, attr
);
9297 // For a single object, indeterminate attributes set by the user should be reflected in the
9298 // actual object style, so pass the wxRICHTEXT_SETSTYLE_RESET flag to assign
9299 // the style directly instead of applying (which ignores indeterminate attributes,
9300 // leaving them as they were).
9301 cellDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
9308 // The next 2 methods return span values. Note that the default is 1, not 0
9309 int wxRichTextCell::GetColspan() const
9312 if (GetProperties().HasProperty(wxT("colspan")))
9314 span
= GetProperties().GetPropertyLong(wxT("colspan"));
9320 int wxRichTextCell::GetRowspan() const
9323 if (GetProperties().HasProperty(wxT("rowspan")))
9325 span
= GetProperties().GetPropertyLong(wxT("rowspan"));
9331 WX_DEFINE_OBJARRAY(wxRichTextObjectPtrArrayArray
)
9333 IMPLEMENT_DYNAMIC_CLASS(wxRichTextTable
, wxRichTextBox
)
9335 wxRichTextTable::wxRichTextTable(wxRichTextObject
* parent
): wxRichTextBox(parent
)
9341 // Draws the object.
9342 bool wxRichTextTable::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
9344 return wxRichTextBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
9347 WX_DECLARE_OBJARRAY(wxRect
, wxRichTextRectArray
);
9348 WX_DEFINE_OBJARRAY(wxRichTextRectArray
);
9351 // Helper function for Layout() that clears the space needed by a cell with rowspan > 1
9352 int GetRowspanDisplacement(const wxRichTextTable
* table
, int row
, int col
, int paddingX
, const wxArrayInt
& colWidths
)
9354 // If one or more cells above-left of this one has rowspan > 1, the affected cells below it
9355 // will have been hidden and have width 0. As a result they are ignored by the layout algorithm,
9356 // and all cells to their right are effectively shifted left. As a result there's no hole for
9357 // the spanning cell to fill.
9358 // So search back along the current row for hidden cells. However there's also the annoying issue of a
9359 // rowspanning cell that also has colspam. So we can't rely on the rowspanning cell being directly above
9360 // the first hidden one we come to. We also can't rely on a cell being hidden only by one type of span;
9361 // there's nothing to stop a cell being hidden by colspan, and then again hidden from above by rowspan.
9362 // The answer is to look above each hidden cell in turn, which I think covers all bases.
9364 for (int prevcol
= 0; prevcol
< col
; ++prevcol
)
9366 if (!table
->GetCell(row
, prevcol
)->IsShown())
9368 // We've found a hidden cell. If it's hidden because of colspan to its left, it's
9369 // already been taken into account; but not if there's a rowspanning cell above
9370 for (int prevrow
= row
-1; prevrow
>= 0; --prevrow
)
9372 wxRichTextCell
* cell
= table
->GetCell(prevrow
, prevcol
);
9373 if (cell
&& cell
->IsShown())
9375 int rowSpan
= cell
->GetRowspan();
9376 if (rowSpan
> 1 && rowSpan
> (row
-prevrow
))
9378 // There is a rowspanning cell above above the hidden one, so we need
9379 // to right-shift the index cell by this column's width. Furthermore,
9380 // if the cell also colspans, we need to shift by all affected columns
9381 for (int colSpan
= 0; colSpan
< cell
->GetColspan(); ++colSpan
)
9382 deltaX
+= (colWidths
[prevcol
+colSpan
] + paddingX
);
9392 // Helper function for Layout() that expands any cell with rowspan > 1
9393 void ExpandCellsWithRowspan(const wxRichTextTable
* table
, int paddingY
, int& bottomY
, wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& availableSpace
, int style
)
9395 // This is called when the table's cell layout is otherwise complete.
9396 // For any cell with rowspan > 1, expand downwards into the row(s) below.
9398 // Start by finding the current 'y' of the top of each row, plus the bottom of the available area for cells.
9399 // Deduce this from the top of a visible cell in the row below. (If none are visible, the row will be invisible anyway and can be ignored.)
9400 const int rowCount
= table
->GetRowCount();
9401 const int colCount
= table
->GetColumnCount();
9403 rowTops
.Add(0, rowCount
+1);
9405 for (row
= 0; row
< rowCount
; ++row
)
9407 for (int column
= 0; column
< colCount
; ++column
)
9409 wxRichTextCell
* cell
= table
->GetCell(row
, column
);
9410 if (cell
&& cell
->IsShown())
9412 rowTops
[row
] = cell
->GetPosition().y
;
9417 rowTops
[rowCount
] = bottomY
+ paddingY
; // The table bottom, which was passed to us
9419 bool needsRelay
= false;
9421 for (row
= 0; row
< rowCount
-1; ++row
) // -1 as the bottom row can't rowspan
9423 for (int col
= 0; col
< colCount
; ++col
)
9425 wxRichTextCell
* cell
= table
->GetCell(row
, col
);
9426 if (cell
&& cell
->IsShown())
9428 int span
= cell
->GetRowspan();
9431 span
= wxMin(span
, rowCount
-row
); // Don't try to span below the table!
9435 int availableHeight
= rowTops
[row
+span
] - rowTops
[row
] - paddingY
;
9436 wxSize newSize
= wxSize(cell
->GetCachedSize().GetWidth(), availableHeight
);
9437 wxRect availableCellSpace
= wxRect(cell
->GetPosition(), newSize
);
9438 cell
->Invalidate(wxRICHTEXT_ALL
);
9439 cell
->Layout(dc
, context
, availableCellSpace
, availableSpace
, style
);
9440 // Ensure there's room in the span to display its contents, else it'll overwrite lower rows
9441 int overhang
= cell
->GetCachedSize().GetHeight() - availableHeight
;
9442 cell
->SetCachedSize(newSize
);
9446 // There are 3 things to get right:
9447 // 1) The easiest is the rows below the span: they need to be downshifted by the overhang, and so does the table bottom itself
9448 // 2) The rows within the span, including the one holding this cell, need to be deepened by their share of the overhang
9449 // e.g. if rowspan == 3, each row should increase in depth by 1/3rd of the overhang.
9450 // 3) The cell with the rowspan shouldn't be touched in 2); its height will be set to the whole span later.
9451 int deltaY
= overhang
/ span
;
9452 int spare
= overhang
% span
;
9454 // Each row in the span needs to by deepened by its share of the overhang (give the first row any spare).
9455 // This is achieved by increasing the value stored in the following row's rowTops
9456 for (int spannedRows
= 0; spannedRows
< span
; ++spannedRows
)
9458 rowTops
[row
+spannedRows
+1] += ((deltaY
* (spannedRows
+1)) + (spannedRows
== 0 ? spare
:0));
9461 // Any rows below the span need shifting down
9462 for (int rowsBelow
= row
+ span
+1; rowsBelow
<= rowCount
; ++rowsBelow
)
9464 rowTops
[rowsBelow
] += overhang
;
9477 // There were overflowing rowspanning cells, so layout yet again to make the increased row depths show
9478 for (row
= 0; row
< rowCount
; ++row
)
9480 for (int col
= 0; col
< colCount
; ++col
)
9482 wxRichTextCell
* cell
= table
->GetCell(row
, col
);
9483 if (cell
&& cell
->IsShown())
9485 wxPoint
position(cell
->GetPosition().x
, rowTops
[row
]);
9487 // GetRowspan() will usually return 1, but may be greater
9488 wxSize
size(cell
->GetCachedSize().GetWidth(), rowTops
[row
+ cell
->GetRowspan()] - rowTops
[row
] - paddingY
);
9490 wxRect availableCellSpace
= wxRect(position
, size
);
9491 cell
->Invalidate(wxRICHTEXT_ALL
);
9492 cell
->Layout(dc
, context
, availableCellSpace
, availableSpace
, style
);
9493 cell
->SetCachedSize(size
);
9497 bottomY
= rowTops
[rowCount
] - paddingY
;
9501 // Lays the object out. rect is the space available for layout. Often it will
9502 // be the specified overall space for this object, if trying to constrain
9503 // layout to a particular size, or it could be the total space available in the
9504 // parent. rect is the overall size, so we must subtract margins and padding.
9505 // to get the actual available space.
9506 bool wxRichTextTable::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& WXUNUSED(parentRect
), int style
)
9508 SetPosition(rect
.GetPosition());
9510 // The meaty bit. Calculate sizes of all cells and rows. Try to use
9511 // minimum size if within alloted size, then divide up remaining size
9512 // between rows/cols.
9515 wxRichTextBuffer
* buffer
= GetBuffer();
9516 if (buffer
) scale
= buffer
->GetScale();
9518 wxRect availableSpace
= GetAvailableContentArea(dc
, context
, rect
);
9519 wxTextAttrDimensionConverter
converter(dc
, scale
, availableSpace
.GetSize());
9521 wxRichTextAttr
attr(GetAttributes());
9522 context
.ApplyVirtualAttributes(attr
, this);
9524 // If we have no fixed table size, and assuming we're not pushed for
9525 // space, then we don't have to try to stretch the table to fit the contents.
9526 bool stretchToFitTableWidth
= false;
9528 int tableWidth
= rect
.width
;
9529 if (attr
.GetTextBoxAttr().GetWidth().IsValid())
9531 tableWidth
= converter
.GetPixels(attr
.GetTextBoxAttr().GetWidth());
9533 // Fixed table width, so we do want to stretch columns out if necessary.
9534 stretchToFitTableWidth
= true;
9536 // Shouldn't be able to exceed the size passed to this function
9537 tableWidth
= wxMin(rect
.width
, tableWidth
);
9540 // Get internal padding
9541 int paddingLeft
= 0, paddingTop
= 0;
9542 if (attr
.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
9543 paddingLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetLeft());
9544 if (attr
.GetTextBoxAttr().GetPadding().GetTop().IsValid())
9545 paddingTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetTop());
9547 // Assume that left and top padding are also used for inter-cell padding.
9548 int paddingX
= paddingLeft
;
9549 int paddingY
= paddingTop
;
9551 int totalLeftMargin
= 0, totalRightMargin
= 0, totalTopMargin
= 0, totalBottomMargin
= 0;
9552 GetTotalMargin(dc
, buffer
, attr
, totalLeftMargin
, totalRightMargin
, totalTopMargin
, totalBottomMargin
);
9554 // Internal table width - the area for content
9555 int internalTableWidth
= tableWidth
- totalLeftMargin
- totalRightMargin
;
9557 int rowCount
= m_cells
.GetCount();
9558 if (m_colCount
== 0 || rowCount
== 0)
9560 wxRect
overallRect(rect
.x
, rect
.y
, totalLeftMargin
+ totalRightMargin
, totalTopMargin
+ totalBottomMargin
);
9561 SetCachedSize(overallRect
.GetSize());
9563 // Zero content size
9564 SetMinSize(overallRect
.GetSize());
9565 SetMaxSize(GetMinSize());
9569 // The final calculated widths
9570 wxArrayInt colWidths
;
9571 colWidths
.Add(0, m_colCount
);
9573 wxArrayInt absoluteColWidths
;
9574 absoluteColWidths
.Add(0, m_colCount
);
9576 wxArrayInt percentageColWidths
;
9577 percentageColWidths
.Add(0, m_colCount
);
9578 // wxArrayInt percentageColWidthsSpanning(m_colCount);
9579 // These are only relevant when the first column contains spanning information.
9580 // wxArrayInt columnSpans(m_colCount); // Each contains 1 for non-spanning cell, > 1 for spanning cell.
9581 wxArrayInt maxColWidths
;
9582 maxColWidths
.Add(0, m_colCount
);
9583 wxArrayInt minColWidths
;
9584 minColWidths
.Add(0, m_colCount
);
9586 wxSize
tableSize(tableWidth
, 0);
9590 for (i
= 0; i
< m_colCount
; i
++)
9592 absoluteColWidths
[i
] = 0;
9593 // absoluteColWidthsSpanning[i] = 0;
9594 percentageColWidths
[i
] = -1;
9595 // percentageColWidthsSpanning[i] = -1;
9597 maxColWidths
[i
] = 0;
9598 minColWidths
[i
] = 0;
9599 // columnSpans[i] = 1;
9602 // (0) Determine which cells are visible according to spans
9604 // __________________
9609 // |------------------|
9610 // |__________________| 4
9612 // To calculate cell visibility:
9613 // First find all spanning cells. Build an array of span records with start x, y and end x, y.
9614 // Then for each cell, test whether we're within one of those cells, and unless we're at the start of
9615 // that cell, hide the cell.
9617 // We can also use this array to match the size of spanning cells to the grid. Or just do
9618 // this when we iterate through all cells.
9620 // 0.1: add spanning cells to an array
9621 wxRichTextRectArray rectArray
;
9622 for (j
= 0; j
< m_rowCount
; j
++)
9624 for (i
= 0; i
< m_colCount
; i
++)
9626 wxRichTextCell
* cell
= GetCell(j
, i
);
9627 int colSpan
= cell
->GetColspan();
9628 int rowSpan
= cell
->GetRowspan();
9629 if (colSpan
> 1 || rowSpan
> 1)
9631 rectArray
.Add(wxRect(i
, j
, colSpan
, rowSpan
));
9635 // 0.2: find which cells are subsumed by a spanning cell
9636 for (j
= 0; j
< m_rowCount
; j
++)
9638 for (i
= 0; i
< m_colCount
; i
++)
9640 wxRichTextCell
* cell
= GetCell(j
, i
);
9641 if (rectArray
.GetCount() == 0)
9647 int colSpan
= cell
->GetColspan();
9648 int rowSpan
= cell
->GetRowspan();
9650 if (colSpan
> 1 || rowSpan
> 1)
9652 // Assume all spanning cells are shown
9658 for (k
= 0; k
< (int) rectArray
.GetCount(); k
++)
9660 if (rectArray
[k
].Contains(wxPoint(i
, j
)))
9672 // Find the first spanned cell in each row that spans the most columns and doesn't
9673 // overlap with a spanned cell starting at a previous column position.
9674 // This means we need to keep an array of rects so we can check. However
9675 // it does also mean that some spans simply may not be taken into account
9676 // where there are different spans happening on different rows. In these cases,
9677 // they will simply be as wide as their constituent columns.
9679 // (1) Do an initial layout for all cells to get minimum and maximum size, and get
9680 // the absolute or percentage width of each column.
9682 for (j
= 0; j
< m_rowCount
; j
++)
9684 // First get the overall margins so we can calculate percentage widths based on
9685 // the available content space for all cells on the row
9687 int overallRowContentMargin
= 0;
9688 int visibleCellCount
= 0;
9690 for (i
= 0; i
< m_colCount
; i
++)
9692 wxRichTextBox
* cell
= GetCell(j
, i
);
9693 if (cell
->IsShown())
9695 int cellTotalLeftMargin
= 0, cellTotalRightMargin
= 0, cellTotalTopMargin
= 0, cellTotalBottomMargin
= 0;
9696 GetTotalMargin(dc
, buffer
, cell
->GetAttributes(), cellTotalLeftMargin
, cellTotalRightMargin
, cellTotalTopMargin
, cellTotalBottomMargin
);
9698 overallRowContentMargin
+= (cellTotalLeftMargin
+ cellTotalRightMargin
);
9699 visibleCellCount
++;
9703 // Add in inter-cell padding
9704 overallRowContentMargin
+= ((visibleCellCount
-1) * paddingX
);
9706 int rowContentWidth
= internalTableWidth
- overallRowContentMargin
;
9707 wxSize
rowTableSize(rowContentWidth
, 0);
9708 wxTextAttrDimensionConverter
converter(dc
, scale
, rowTableSize
);
9710 for (i
= 0; i
< m_colCount
; i
++)
9712 wxRichTextCell
* cell
= GetCell(j
, i
);
9713 if (cell
->IsShown())
9715 int colSpan
= cell
->GetColspan();
9717 // Lay out cell to find min/max widths
9718 cell
->Invalidate(wxRICHTEXT_ALL
);
9719 cell
->Layout(dc
, context
, availableSpace
, availableSpace
, style
);
9723 int absoluteCellWidth
= -1;
9724 int percentageCellWidth
= -1;
9726 // I think we need to calculate percentages from the internal table size,
9727 // minus the padding between cells which we'll need to calculate from the
9728 // (number of VISIBLE cells - 1)*paddingX. Then percentages that add up to 100%
9729 // will add up to 100%. In CSS, the width specifies the cell's content rect width,
9730 // so if we want to conform to that we'll need to add in the overall cell margins.
9731 // However, this will make it difficult to specify percentages that add up to
9732 // 100% and still fit within the table width.
9733 // Let's say two cells have 50% width. They have 10 pixels of overall margin each.
9734 // The table content rect is 500 pixels and the inter-cell padding is 20 pixels.
9735 // If we're using internal content size for the width, we would calculate the
9736 // the overall cell width for n cells as:
9737 // (500 - 20*(n-1) - overallCellMargin1 - overallCellMargin2 - ...) * percentage / 100
9738 // + thisOverallCellMargin
9739 // = 500 - 20 - 10 - 10) * 0.5 + 10 = 240 pixels overall cell width.
9740 // Adding this back, we get 240 + 240 + 20 = 500 pixels.
9742 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9744 int w
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetWidth());
9745 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
9747 percentageCellWidth
= w
;
9751 absoluteCellWidth
= w
;
9753 // Override absolute width with minimum width if necessary
9754 if (cell
->GetMinSize().x
> 0 && absoluteCellWidth
!=1 && cell
->GetMinSize().x
> absoluteCellWidth
)
9755 absoluteCellWidth
= cell
->GetMinSize().x
;
9758 if (absoluteCellWidth
!= -1)
9760 if (absoluteCellWidth
> absoluteColWidths
[i
])
9761 absoluteColWidths
[i
] = absoluteCellWidth
;
9764 if (percentageCellWidth
!= -1)
9766 if (percentageCellWidth
> percentageColWidths
[i
])
9767 percentageColWidths
[i
] = percentageCellWidth
;
9770 if (colSpan
== 1 && cell
->GetMinSize().x
&& cell
->GetMinSize().x
> minColWidths
[i
])
9771 minColWidths
[i
] = cell
->GetMinSize().x
;
9772 if (colSpan
== 1 && cell
->GetMaxSize().x
&& cell
->GetMaxSize().x
> maxColWidths
[i
])
9773 maxColWidths
[i
] = cell
->GetMaxSize().x
;
9779 // (2) Allocate initial column widths from minimum widths, absolute values and proportions
9780 // TODO: simply merge this into (1).
9781 for (i
= 0; i
< m_colCount
; i
++)
9783 if (absoluteColWidths
[i
] > 0)
9785 colWidths
[i
] = absoluteColWidths
[i
];
9787 else if (percentageColWidths
[i
] > 0)
9789 colWidths
[i
] = percentageColWidths
[i
];
9791 // This is rubbish - we calculated the absolute widths from percentages, so
9792 // we can't do it again here.
9793 //colWidths[i] = (int) (double(percentageColWidths[i]) * double(tableWidth) / 100.0 + 0.5);
9797 // (3) Process absolute or proportional widths of spanning columns,
9798 // now that we know what our fixed column widths are going to be.
9799 // Spanned cells will try to adjust columns so the span will fit.
9800 // Even existing fixed column widths can be expanded if necessary.
9801 // Actually, currently fixed columns widths aren't adjusted; instead,
9802 // the algorithm favours earlier rows and adjusts unspecified column widths
9803 // the first time only. After that, we can't know whether the column has been
9804 // specified explicitly or not. (We could make a note if necessary.)
9805 for (j
= 0; j
< m_rowCount
; j
++)
9807 // First get the overall margins so we can calculate percentage widths based on
9808 // the available content space for all cells on the row
9810 int overallRowContentMargin
= 0;
9811 int visibleCellCount
= 0;
9813 for (i
= 0; i
< m_colCount
; i
++)
9815 wxRichTextBox
* cell
= GetCell(j
, i
);
9816 if (cell
->IsShown())
9818 int cellTotalLeftMargin
= 0, cellTotalRightMargin
= 0, cellTotalTopMargin
= 0, cellTotalBottomMargin
= 0;
9819 GetTotalMargin(dc
, buffer
, cell
->GetAttributes(), cellTotalLeftMargin
, cellTotalRightMargin
, cellTotalTopMargin
, cellTotalBottomMargin
);
9821 overallRowContentMargin
+= (cellTotalLeftMargin
+ cellTotalRightMargin
);
9822 visibleCellCount
++;
9826 // Add in inter-cell padding
9827 overallRowContentMargin
+= ((visibleCellCount
-1) * paddingX
);
9829 int rowContentWidth
= internalTableWidth
- overallRowContentMargin
;
9830 wxSize
rowTableSize(rowContentWidth
, 0);
9831 wxTextAttrDimensionConverter
converter(dc
, scale
, rowTableSize
);
9833 for (i
= 0; i
< m_colCount
; i
++)
9835 wxRichTextCell
* cell
= GetCell(j
, i
);
9836 if (cell
->IsShown())
9838 int colSpan
= cell
->GetColspan();
9841 int spans
= wxMin(colSpan
, m_colCount
- i
);
9845 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9847 cellWidth
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetWidth());
9848 // Override absolute width with minimum width if necessary
9849 if (cell
->GetMinSize().x
> 0 && cellWidth
!=1 && cell
->GetMinSize().x
> cellWidth
)
9850 cellWidth
= cell
->GetMinSize().x
;
9854 // Do we want to do this? It's the only chance we get to
9855 // use the cell's min/max sizes, so we need to work out
9856 // how we're going to balance the unspecified spanning cell
9857 // width with the possibility more-constrained constituent cell widths.
9858 // Say there's a tiny bitmap giving it a max width of 10 pixels. We
9859 // don't want to constraint all the spanned columns to fit into this cell.
9860 // OK, let's say that if any of the constituent columns don't fit,
9861 // then we simply stop constraining the columns; instead, we'll just fit the spanning
9862 // cells to the columns later.
9863 cellWidth
= cell
->GetMinSize().x
;
9864 if (cell
->GetMaxSize().x
> cellWidth
)
9865 cellWidth
= cell
->GetMaxSize().x
;
9868 // Subtract the padding between cells
9869 int spanningWidth
= cellWidth
;
9870 spanningWidth
-= paddingX
* (spans
-1);
9872 if (spanningWidth
> 0)
9874 // Now share the spanning width between columns within that span
9875 // TODO: take into account min widths of columns within the span
9876 int spanningWidthLeft
= spanningWidth
;
9877 int stretchColCount
= 0;
9878 for (k
= i
; k
< (i
+spans
); k
++)
9880 if (colWidths
[k
] > 0) // absolute or proportional width has been specified
9881 spanningWidthLeft
-= colWidths
[k
];
9885 // Now divide what's left between the remaining columns
9887 if (stretchColCount
> 0)
9888 colShare
= spanningWidthLeft
/ stretchColCount
;
9889 int colShareRemainder
= spanningWidthLeft
- (colShare
* stretchColCount
);
9891 // If fixed-width columns are currently too big, then we'll later
9892 // stretch the spanned cell to fit.
9894 if (spanningWidthLeft
> 0)
9896 for (k
= i
; k
< (i
+spans
); k
++)
9898 if (colWidths
[k
] <= 0) // absolute or proportional width has not been specified
9900 int newWidth
= colShare
;
9901 if (k
== (i
+spans
-1))
9902 newWidth
+= colShareRemainder
; // ensure all pixels are filled
9903 colWidths
[k
] = newWidth
;
9914 // (4) Next, share any remaining space out between columns that have not yet been calculated.
9915 // TODO: take into account min widths of columns within the span
9916 int tableWidthMinusPadding
= internalTableWidth
- (m_colCount
-1)*paddingX
;
9917 int widthLeft
= tableWidthMinusPadding
;
9918 int stretchColCount
= 0;
9919 for (i
= 0; i
< m_colCount
; i
++)
9921 // Subtract min width from width left, then
9922 // add the colShare to the min width
9923 if (colWidths
[i
] > 0) // absolute or proportional width has been specified
9924 widthLeft
-= colWidths
[i
];
9927 if (minColWidths
[i
] > 0)
9928 widthLeft
-= minColWidths
[i
];
9934 // Now divide what's left between the remaining columns
9936 if (stretchColCount
> 0)
9937 colShare
= widthLeft
/ stretchColCount
;
9938 int colShareRemainder
= widthLeft
- (colShare
* stretchColCount
);
9940 // Check we don't have enough space, in which case shrink all columns, overriding
9941 // any absolute/proportional widths
9942 // TODO: actually we would like to divide up the shrinkage according to size.
9943 // How do we calculate the proportions that will achieve this?
9944 // Could first choose an arbitrary value for stretching cells, and then calculate
9945 // factors to multiply each width by.
9946 // TODO: want to record this fact and pass to an iteration that tries e.g. min widths
9947 if (widthLeft
< 0 || (stretchToFitTableWidth
&& (stretchColCount
== 0)))
9949 colShare
= tableWidthMinusPadding
/ m_colCount
;
9950 colShareRemainder
= tableWidthMinusPadding
- (colShare
* m_colCount
);
9951 for (i
= 0; i
< m_colCount
; i
++)
9954 minColWidths
[i
] = 0;
9958 // We have to adjust the columns if either we need to shrink the
9959 // table to fit the parent/table width, or we explicitly set the
9960 // table width and need to stretch out the table.
9961 if (widthLeft
< 0 || stretchToFitTableWidth
)
9963 for (i
= 0; i
< m_colCount
; i
++)
9965 if (colWidths
[i
] <= 0) // absolute or proportional width has not been specified
9967 if (minColWidths
[i
] > 0)
9968 colWidths
[i
] = minColWidths
[i
] + colShare
;
9970 colWidths
[i
] = colShare
;
9971 if (i
== (m_colCount
-1))
9972 colWidths
[i
] += colShareRemainder
; // ensure all pixels are filled
9977 // TODO: if spanned cells have no specified or max width, make them the
9978 // as big as the columns they span. Do this for all spanned cells in all
9979 // rows, of course. Size any spanned cells left over at the end - even if they
9980 // have width > 0, make sure they're limited to the appropriate column edge.
9984 Sort out confusion between content width
9985 and overall width later. For now, assume we specify overall width.
9987 So, now we've laid out the table to fit into the given space
9988 and have used specified widths and minimum widths.
9990 Now we need to consider how we will try to take maximum width into account.
9994 // (??) TODO: take max width into account
9996 // (6) Lay out all cells again with the current values
9999 int y
= availableSpace
.y
;
10000 for (j
= 0; j
< m_rowCount
; j
++)
10002 int x
= availableSpace
.x
; // TODO: take into account centering etc.
10003 int maxCellHeight
= 0;
10004 int maxSpecifiedCellHeight
= 0;
10006 wxArrayInt actualWidths
;
10007 actualWidths
.Add(0, m_colCount
);
10009 wxTextAttrDimensionConverter
converter(dc
, scale
);
10010 for (i
= 0; i
< m_colCount
; i
++)
10012 wxRichTextCell
* cell
= GetCell(j
, i
);
10013 if (cell
->IsShown())
10015 // Get max specified cell height
10016 // Don't handle percentages for height
10017 if (cell
->GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && cell
->GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() != wxTEXT_ATTR_UNITS_PERCENTAGE
)
10019 int h
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetHeight());
10020 if (h
> maxSpecifiedCellHeight
)
10021 maxSpecifiedCellHeight
= h
;
10024 if (colWidths
[i
] > 0) // absolute or proportional width has been specified
10026 int colSpan
= cell
->GetColspan();
10027 wxRect availableCellSpace
;
10029 // Take into account spans
10032 // Calculate the size of this spanning cell from its constituent columns
10034 int spans
= wxMin(colSpan
, m_colCount
- i
);
10035 for (k
= i
; k
< (i
+spans
); k
++)
10039 xx
+= colWidths
[k
];
10041 availableCellSpace
= wxRect(x
, y
, xx
, -1);
10044 availableCellSpace
= wxRect(x
, y
, colWidths
[i
], -1);
10046 // Store actual width so we can force cell to be the appropriate width on the final loop
10047 actualWidths
[i
] = availableCellSpace
.GetWidth();
10049 // We now need to shift right by the width of any rowspanning cells above-left of us
10050 int deltaX
= GetRowspanDisplacement(this, j
, i
, paddingX
, colWidths
);
10051 availableCellSpace
.SetX(availableCellSpace
.GetX() + deltaX
);
10054 cell
->Invalidate(wxRICHTEXT_ALL
);
10055 cell
->Layout(dc
, context
, availableCellSpace
, availableSpace
, style
);
10057 // TODO: use GetCachedSize().x to compute 'natural' size
10059 x
+= (availableCellSpace
.GetWidth() + paddingX
);
10060 if ((cell
->GetCachedSize().y
> maxCellHeight
) && (cell
->GetRowspan() < 2))
10061 maxCellHeight
= cell
->GetCachedSize().y
;
10066 maxCellHeight
= wxMax(maxCellHeight
, maxSpecifiedCellHeight
);
10068 for (i
= 0; i
< m_colCount
; i
++)
10070 wxRichTextCell
* cell
= GetCell(j
, i
);
10071 if (cell
->IsShown())
10073 wxRect availableCellSpace
= wxRect(cell
->GetPosition(), wxSize(actualWidths
[i
], maxCellHeight
));
10074 // Lay out cell with new height
10075 cell
->Invalidate(wxRICHTEXT_ALL
);
10076 cell
->Layout(dc
, context
, availableCellSpace
, availableSpace
, style
);
10078 // Make sure the cell size really is the appropriate size,
10079 // not the calculated box size
10080 cell
->SetCachedSize(wxSize(actualWidths
[i
], maxCellHeight
));
10082 maxRight
= wxMax(maxRight
, cell
->GetPosition().x
+ cell
->GetCachedSize().x
);
10086 y
+= maxCellHeight
;
10087 if (j
< (m_rowCount
-1))
10091 // Finally we need to expand any cell with rowspan > 1. We couldn't earlier; lower rows' heights weren't known
10092 ExpandCellsWithRowspan(this, paddingY
, y
, dc
, context
, availableSpace
, style
);
10094 // We need to add back the margins etc.
10096 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
10097 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxRight
- availableSpace
.x
, y
- availableSpace
.y
));
10098 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
10099 SetCachedSize(marginRect
.GetSize());
10102 // TODO: calculate max size
10104 SetMaxSize(GetCachedSize());
10107 // TODO: calculate min size
10109 SetMinSize(GetCachedSize());
10112 // TODO: currently we use either a fixed table width or the parent's size.
10113 // We also want to be able to calculate the table width from its content,
10114 // whether using fixed column widths or cell content min/max width.
10115 // Probably need a boolean flag to say whether we need to stretch cells
10116 // to fit the table width, or to simply use min/max cell widths. The
10117 // trouble with this is that if cell widths are not specified, they
10118 // will be tiny; we could use arbitrary defaults but this seems unsatisfactory.
10119 // Anyway, ignoring that problem, we probably need to factor layout into a function
10120 // that can can calculate the maximum unconstrained layout in case table size is
10121 // not specified. Then LayoutToBestSize() can choose to use either parent size to
10122 // constrain Layout(), or the previously-calculated max size to constraint layout.
10127 // Finds the absolute position and row height for the given character position
10128 bool wxRichTextTable::FindPosition(wxDC
& dc
, wxRichTextDrawingContext
& context
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
10130 wxRichTextCell
* child
= GetCell(index
+1);
10133 // Find the position at the start of the child cell, since the table doesn't
10134 // have any caret position of its own.
10135 return child
->FindPosition(dc
, context
, -1, pt
, height
, forceLineStart
);
10141 // Get the cell at the given character position (in the range of the table).
10142 wxRichTextCell
* wxRichTextTable::GetCell(long pos
) const
10144 int row
= 0, col
= 0;
10145 if (GetCellRowColumnPosition(pos
, row
, col
))
10147 return GetCell(row
, col
);
10153 // Get the row/column for a given character position
10154 bool wxRichTextTable::GetCellRowColumnPosition(long pos
, int& row
, int& col
) const
10156 if (m_colCount
== 0 || m_rowCount
== 0)
10159 row
= (int) (pos
/ m_colCount
);
10160 col
= pos
- (row
* m_colCount
);
10162 wxASSERT(row
< m_rowCount
&& col
< m_colCount
);
10164 if (row
< m_rowCount
&& col
< m_colCount
)
10170 // Calculate range, taking row/cell ordering into account instead of relying
10171 // on list ordering.
10172 void wxRichTextTable::CalculateRange(long start
, long& end
)
10174 long current
= start
;
10175 long lastEnd
= current
;
10184 for (i
= 0; i
< m_rowCount
; i
++)
10186 for (j
= 0; j
< m_colCount
; j
++)
10188 wxRichTextCell
* child
= GetCell(i
, j
);
10193 child
->CalculateRange(current
, childEnd
);
10195 lastEnd
= childEnd
;
10196 current
= childEnd
+ 1;
10201 // A top-level object always has a range of size 1,
10202 // because its children don't count at this level.
10204 m_range
.SetRange(start
, start
);
10206 // An object with no children has zero length
10207 if (m_children
.GetCount() == 0)
10209 m_ownRange
.SetRange(0, lastEnd
);
10212 // Gets the range size.
10213 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
10215 return wxRichTextBox::GetRangeSize(range
, size
, descent
, dc
, context
, flags
, position
, parentSize
, partialExtents
);
10218 // Deletes content in the given range.
10219 bool wxRichTextTable::DeleteRange(const wxRichTextRange
& WXUNUSED(range
))
10221 // TODO: implement deletion of cells
10225 // Gets any text in this object for the given range.
10226 wxString
wxRichTextTable::GetTextForRange(const wxRichTextRange
& range
) const
10228 return wxRichTextBox::GetTextForRange(range
);
10231 // Copies this object.
10232 void wxRichTextTable::Copy(const wxRichTextTable
& obj
)
10234 wxRichTextBox::Copy(obj
);
10238 m_rowCount
= obj
.m_rowCount
;
10239 m_colCount
= obj
.m_colCount
;
10241 m_cells
.Add(wxRichTextObjectPtrArray(), m_rowCount
);
10244 for (i
= 0; i
< m_rowCount
; i
++)
10246 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
10247 for (j
= 0; j
< m_colCount
; j
++)
10249 wxRichTextCell
* cell
= wxDynamicCast(obj
.GetCell(i
, j
)->Clone(), wxRichTextCell
);
10252 colArray
.Add(cell
);
10257 void wxRichTextTable::ClearTable()
10265 bool wxRichTextTable::CreateTable(int rows
, int cols
)
10272 m_cells
.Add(wxRichTextObjectPtrArray(), rows
);
10275 for (i
= 0; i
< rows
; i
++)
10277 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
10278 for (j
= 0; j
< cols
; j
++)
10280 wxRichTextCell
* cell
= new wxRichTextCell
;
10282 cell
->AddParagraph(wxEmptyString
);
10284 colArray
.Add(cell
);
10291 wxRichTextCell
* wxRichTextTable::GetCell(int row
, int col
) const
10293 wxASSERT(row
< m_rowCount
);
10294 wxASSERT(col
< m_colCount
);
10296 if (row
< m_rowCount
&& col
< m_colCount
)
10298 wxRichTextObjectPtrArray
& colArray
= m_cells
[row
];
10299 wxRichTextObject
* obj
= colArray
[col
];
10300 return wxDynamicCast(obj
, wxRichTextCell
);
10306 // Returns a selection object specifying the selections between start and end character positions.
10307 // For example, a table would deduce what cells (of range length 1) are selected when dragging across the table.
10308 wxRichTextSelection
wxRichTextTable::GetSelection(long start
, long end
) const
10310 wxRichTextSelection selection
;
10311 selection
.SetContainer((wxRichTextTable
*) this);
10320 wxASSERT( start
>= 0 && end
< (m_colCount
* m_rowCount
));
10322 if (end
>= (m_colCount
* m_rowCount
))
10325 // We need to find the rectangle of cells that is described by the rectangle
10326 // with start, end as the diagonal. Make sure we don't add cells that are
10327 // not currenty visible because they are overlapped by spanning cells.
10329 --------------------------
10330 | 0 | 1 | 2 | 3 | 4 |
10331 --------------------------
10332 | 5 | 6 | 7 | 8 | 9 |
10333 --------------------------
10334 | 10 | 11 | 12 | 13 | 14 |
10335 --------------------------
10336 | 15 | 16 | 17 | 18 | 19 |
10337 --------------------------
10339 Let's say we select 6 -> 18.
10341 Left and right edge cols of rectangle are 1 and 3 inclusive. Find least/greatest to find
10342 which is left and which is right.
10344 Top and bottom edge rows are 1 and 3 inclusive. Again, find least/greatest to find top and bottom.
10346 Now go through rows from 1 to 3 and only add cells that are (a) within above column range
10352 int leftCol
= start
- m_colCount
* int(start
/m_colCount
);
10353 int rightCol
= end
- m_colCount
* int(end
/m_colCount
);
10355 int topRow
= int(start
/m_colCount
);
10356 int bottomRow
= int(end
/m_colCount
);
10358 if (leftCol
> rightCol
)
10360 int tmp
= rightCol
;
10361 rightCol
= leftCol
;
10365 if (topRow
> bottomRow
)
10367 int tmp
= bottomRow
;
10368 bottomRow
= topRow
;
10373 for (i
= topRow
; i
<= bottomRow
; i
++)
10375 for (j
= leftCol
; j
<= rightCol
; j
++)
10377 wxRichTextCell
* cell
= GetCell(i
, j
);
10378 if (cell
&& cell
->IsShown())
10379 selection
.Add(cell
->GetRange());
10386 // Sets the attributes for the cells specified by the selection.
10387 bool wxRichTextTable::SetCellStyle(const wxRichTextSelection
& selection
, const wxRichTextAttr
& style
, int flags
)
10389 if (selection
.GetContainer() != this)
10392 wxRichTextBuffer
* buffer
= GetBuffer();
10393 bool haveControl
= (buffer
&& buffer
->GetRichTextCtrl() != NULL
);
10394 bool withUndo
= haveControl
&& ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
10397 buffer
->BeginBatchUndo(_("Set Cell Style"));
10399 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
10402 wxRichTextCell
* cell
= wxDynamicCast(node
->GetData(), wxRichTextCell
);
10403 if (cell
&& selection
.WithinSelection(cell
->GetRange().GetStart()))
10404 SetStyle(cell
, style
, flags
);
10405 node
= node
->GetNext();
10408 // Do action, or delay it until end of batch.
10410 buffer
->EndBatchUndo();
10415 wxPosition
wxRichTextTable::GetFocusedCell() const
10417 wxPosition
position(-1, -1);
10418 const wxRichTextObject
* focus
= GetBuffer()->GetRichTextCtrl()->GetFocusObject();
10420 for (int row
= 0; row
< GetRowCount(); ++row
)
10422 for (int col
= 0; col
< GetColumnCount(); ++col
)
10424 if (GetCell(row
, col
) == focus
)
10426 position
.SetRow(row
);
10427 position
.SetCol(col
);
10436 bool wxRichTextTable::DeleteRows(int startRow
, int noRows
)
10438 wxASSERT((startRow
+ noRows
) <= m_rowCount
);
10439 if ((startRow
+ noRows
) > m_rowCount
)
10442 wxCHECK_MSG(noRows
!= m_rowCount
, false, "Trying to delete all the cells in a table");
10444 wxRichTextBuffer
* buffer
= GetBuffer();
10445 wxRichTextCtrl
* rtc
= buffer
->GetRichTextCtrl();
10447 wxPosition position
= GetFocusedCell();
10448 int focusCol
= position
.GetCol();
10449 int focusRow
= position
.GetRow();
10450 if (focusRow
>= startRow
&& focusRow
< (startRow
+noRows
))
10452 // Deleting a focused cell causes a segfault later when laying out, due to GetFocusedObject() returning an invalid object
10453 if ((startRow
+ noRows
) < m_rowCount
)
10455 // There are more rows after the one(s) to be deleted, so set focus in the first of them
10456 rtc
->SetFocusObject(GetCell(startRow
+ noRows
, focusCol
));
10460 // Otherwise set focus in the preceding row
10461 rtc
->SetFocusObject(GetCell(startRow
- 1, focusCol
));
10465 wxRichTextAction
* action
= NULL
;
10466 wxRichTextTable
* clone
= NULL
;
10467 if (!rtc
->SuppressingUndo())
10469 // Create a clone containing the current state of the table. It will be used to Undo the action
10470 clone
= wxStaticCast(this->Clone(), wxRichTextTable
);
10471 clone
->SetParent(GetParent());
10472 action
= new wxRichTextAction(NULL
, _("Delete row"), wxRICHTEXT_CHANGE_OBJECT
, buffer
, this, rtc
);
10473 action
->SetObject(this);
10474 action
->SetPosition(GetRange().GetStart());
10478 for (i
= startRow
; i
< (startRow
+noRows
); i
++)
10480 wxRichTextObjectPtrArray
& colArray
= m_cells
[startRow
];
10481 for (j
= 0; j
< (int) colArray
.GetCount(); j
++)
10483 wxRichTextObject
* cell
= colArray
[j
];
10484 RemoveChild(cell
, true);
10487 // Keep deleting at the same position, since we move all
10489 m_cells
.RemoveAt(startRow
);
10492 m_rowCount
= m_rowCount
- noRows
;
10494 if (!rtc
->SuppressingUndo())
10496 buffer
->SubmitAction(action
);
10497 // Finally store the original-state clone; doing so earlier would cause various failures
10498 action
->StoreObject(clone
);
10504 bool wxRichTextTable::DeleteColumns(int startCol
, int noCols
)
10506 wxASSERT((startCol
+ noCols
) <= m_colCount
);
10507 if ((startCol
+ noCols
) > m_colCount
)
10510 wxCHECK_MSG(noCols
!= m_colCount
, false, "Trying to delete all the cells in a table");
10512 wxRichTextBuffer
* buffer
= GetBuffer();
10513 wxRichTextCtrl
* rtc
= buffer
->GetRichTextCtrl();
10515 wxPosition position
= GetFocusedCell();
10516 int focusCol
= position
.GetCol();
10517 int focusRow
= position
.GetRow();
10518 if (focusCol
>= startCol
&& focusCol
< (startCol
+noCols
))
10520 // Deleting a focused cell causes a segfault later when laying out, due to GetFocusedObject() returning an invalid object
10521 if ((startCol
+ noCols
) < m_colCount
)
10523 // There are more columns after the one(s) to be deleted, so set focus in the first of them
10524 rtc
->SetFocusObject(GetCell(focusRow
, startCol
+ noCols
));
10528 // Otherwise set focus in the preceding column
10529 rtc
->SetFocusObject(GetCell(focusRow
, startCol
- 1));
10533 wxRichTextAction
* action
= NULL
;
10534 wxRichTextTable
* clone
= NULL
;
10535 if (!rtc
->SuppressingUndo())
10537 // Create a clone containing the current state of the table. It will be used to Undo the action
10538 clone
= wxStaticCast(this->Clone(), wxRichTextTable
);
10539 clone
->SetParent(GetParent());
10540 action
= new wxRichTextAction(NULL
, _("Delete column"), wxRICHTEXT_CHANGE_OBJECT
, buffer
, this, rtc
);
10541 action
->SetObject(this);
10542 action
->SetPosition(GetRange().GetStart());
10545 bool deleteRows
= (noCols
== m_colCount
);
10548 for (i
= 0; i
< m_rowCount
; i
++)
10550 wxRichTextObjectPtrArray
& colArray
= m_cells
[deleteRows
? 0 : i
];
10551 for (j
= 0; j
< noCols
; j
++)
10553 wxRichTextObject
* cell
= colArray
[startCol
];
10554 RemoveChild(cell
, true);
10555 colArray
.RemoveAt(startCol
);
10559 m_cells
.RemoveAt(0);
10564 m_colCount
= m_colCount
- noCols
;
10566 if (!rtc
->SuppressingUndo())
10568 buffer
->SubmitAction(action
);
10569 // Finally store the original-state clone; doing so earlier would cause various failures
10570 action
->StoreObject(clone
);
10576 bool wxRichTextTable::AddRows(int startRow
, int noRows
, const wxRichTextAttr
& attr
)
10578 wxASSERT(startRow
<= m_rowCount
);
10579 if (startRow
> m_rowCount
)
10582 wxRichTextBuffer
* buffer
= GetBuffer();
10583 wxRichTextAction
* action
= NULL
;
10584 wxRichTextTable
* clone
= NULL
;
10585 if (!buffer
->GetRichTextCtrl()->SuppressingUndo())
10587 // Create a clone containing the current state of the table. It will be used to Undo the action
10588 clone
= wxStaticCast(this->Clone(), wxRichTextTable
);
10589 clone
->SetParent(GetParent());
10590 action
= new wxRichTextAction(NULL
, _("Add row"), wxRICHTEXT_CHANGE_OBJECT
, buffer
, this, buffer
->GetRichTextCtrl());
10591 action
->SetObject(this);
10592 action
->SetPosition(GetRange().GetStart());
10596 for (i
= 0; i
< noRows
; i
++)
10599 if (startRow
== m_rowCount
)
10601 m_cells
.Add(wxRichTextObjectPtrArray());
10602 idx
= m_cells
.GetCount() - 1;
10606 m_cells
.Insert(wxRichTextObjectPtrArray(), startRow
+i
);
10610 wxRichTextObjectPtrArray
& colArray
= m_cells
[idx
];
10611 for (j
= 0; j
< m_colCount
; j
++)
10613 wxRichTextCell
* cell
= new wxRichTextCell
;
10614 cell
->GetAttributes() = attr
;
10617 cell
->AddParagraph(wxEmptyString
);
10618 colArray
.Add(cell
);
10622 m_rowCount
= m_rowCount
+ noRows
;
10624 if (!buffer
->GetRichTextCtrl()->SuppressingUndo())
10626 buffer
->SubmitAction(action
);
10627 // Finally store the original-state clone; doing so earlier would cause various failures
10628 action
->StoreObject(clone
);
10634 bool wxRichTextTable::AddColumns(int startCol
, int noCols
, const wxRichTextAttr
& attr
)
10636 wxASSERT(startCol
<= m_colCount
);
10637 if (startCol
> m_colCount
)
10640 wxRichTextBuffer
* buffer
= GetBuffer();
10641 wxRichTextAction
* action
= NULL
;
10642 wxRichTextTable
* clone
= NULL
;
10643 if (!buffer
->GetRichTextCtrl()->SuppressingUndo())
10645 // Create a clone containing the current state of the table. It will be used to Undo the action
10646 clone
= wxStaticCast(this->Clone(), wxRichTextTable
);
10647 clone
->SetParent(GetParent());
10648 action
= new wxRichTextAction(NULL
, _("Add column"), wxRICHTEXT_CHANGE_OBJECT
, buffer
, this, buffer
->GetRichTextCtrl());
10649 action
->SetObject(this);
10650 action
->SetPosition(GetRange().GetStart());
10654 for (i
= 0; i
< m_rowCount
; i
++)
10656 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
10657 for (j
= 0; j
< noCols
; j
++)
10659 wxRichTextCell
* cell
= new wxRichTextCell
;
10660 cell
->GetAttributes() = attr
;
10663 cell
->AddParagraph(wxEmptyString
);
10665 if (startCol
== m_colCount
)
10666 colArray
.Add(cell
);
10668 colArray
.Insert(cell
, startCol
+j
);
10672 m_colCount
= m_colCount
+ noCols
;
10674 if (!buffer
->GetRichTextCtrl()->SuppressingUndo())
10676 buffer
->SubmitAction(action
);
10677 // Finally store the original-state clone; doing so earlier would cause various failures
10678 action
->StoreObject(clone
);
10684 // Edit properties via a GUI
10685 bool wxRichTextTable::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
10687 wxRichTextObjectPropertiesDialog
boxDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Table Properties"));
10688 boxDlg
.SetAttributes(GetAttributes());
10690 if (boxDlg
.ShowModal() == wxID_OK
)
10692 boxDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
10700 * Module to initialise and clean up handlers
10703 class wxRichTextModule
: public wxModule
10705 DECLARE_DYNAMIC_CLASS(wxRichTextModule
)
10707 wxRichTextModule() {}
10710 wxRichTextBuffer::SetRenderer(new wxRichTextStdRenderer
);
10711 wxRichTextBuffer::InitStandardHandlers();
10712 wxRichTextParagraph::InitDefaultTabs();
10714 wxRichTextXMLHandler::RegisterNodeName(wxT("text"), wxT("wxRichTextPlainText"));
10715 wxRichTextXMLHandler::RegisterNodeName(wxT("symbol"), wxT("wxRichTextPlainText"));
10716 wxRichTextXMLHandler::RegisterNodeName(wxT("image"), wxT("wxRichTextImage"));
10717 wxRichTextXMLHandler::RegisterNodeName(wxT("paragraph"), wxT("wxRichTextParagraph"));
10718 wxRichTextXMLHandler::RegisterNodeName(wxT("paragraphlayout"), wxT("wxRichTextParagraphLayoutBox"));
10719 wxRichTextXMLHandler::RegisterNodeName(wxT("textbox"), wxT("wxRichTextBox"));
10720 wxRichTextXMLHandler::RegisterNodeName(wxT("cell"), wxT("wxRichTextCell"));
10721 wxRichTextXMLHandler::RegisterNodeName(wxT("table"), wxT("wxRichTextTable"));
10722 wxRichTextXMLHandler::RegisterNodeName(wxT("field"), wxT("wxRichTextField"));
10728 wxRichTextBuffer::CleanUpHandlers();
10729 wxRichTextBuffer::CleanUpDrawingHandlers();
10730 wxRichTextBuffer::CleanUpFieldTypes();
10731 wxRichTextXMLHandler::ClearNodeToClassMap();
10732 wxRichTextDecimalToRoman(-1);
10733 wxRichTextParagraph::ClearDefaultTabs();
10734 wxRichTextCtrl::ClearAvailableFontNames();
10735 wxRichTextBuffer::SetRenderer(NULL
);
10739 IMPLEMENT_DYNAMIC_CLASS(wxRichTextModule
, wxModule
)
10742 // If the richtext lib is dynamically loaded after the app has already started
10743 // (such as from wxPython) then the built-in module system will not init this
10744 // module. Provide this function to do it manually.
10745 void wxRichTextModuleInit()
10747 wxModule
* module = new wxRichTextModule
;
10748 wxModule::RegisterModule(module);
10749 wxModule::InitializeModules();
10754 * Commands for undo/redo
10758 wxRichTextCommand::wxRichTextCommand(const wxString
& name
, wxRichTextCommandId id
, wxRichTextBuffer
* buffer
,
10759 wxRichTextParagraphLayoutBox
* container
, wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
): wxCommand(true, name
)
10761 /* wxRichTextAction* action = */ new wxRichTextAction(this, name
, id
, buffer
, container
, ctrl
, ignoreFirstTime
);
10764 wxRichTextCommand::wxRichTextCommand(const wxString
& name
): wxCommand(true, name
)
10768 wxRichTextCommand::~wxRichTextCommand()
10773 void wxRichTextCommand::AddAction(wxRichTextAction
* action
)
10775 if (!m_actions
.Member(action
))
10776 m_actions
.Append(action
);
10779 bool wxRichTextCommand::Do()
10781 for (wxList::compatibility_iterator node
= m_actions
.GetFirst(); node
; node
= node
->GetNext())
10783 wxRichTextAction
* action
= (wxRichTextAction
*) node
->GetData();
10790 bool wxRichTextCommand::Undo()
10792 for (wxList::compatibility_iterator node
= m_actions
.GetLast(); node
; node
= node
->GetPrevious())
10794 wxRichTextAction
* action
= (wxRichTextAction
*) node
->GetData();
10801 void wxRichTextCommand::ClearActions()
10803 WX_CLEAR_LIST(wxList
, m_actions
);
10807 * Individual action
10811 wxRichTextAction::wxRichTextAction(wxRichTextCommand
* cmd
, const wxString
& name
, wxRichTextCommandId id
,
10812 wxRichTextBuffer
* buffer
, wxRichTextParagraphLayoutBox
* container
,
10813 wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
)
10817 m_containerAddress
.Create(buffer
, container
);
10818 m_ignoreThis
= ignoreFirstTime
;
10823 m_newParagraphs
.SetDefaultStyle(buffer
->GetDefaultStyle());
10824 m_newParagraphs
.SetBasicStyle(buffer
->GetBasicStyle());
10826 cmd
->AddAction(this);
10829 wxRichTextAction::~wxRichTextAction()
10835 // Returns the container that this action refers to, using the container address and top-level buffer.
10836 wxRichTextParagraphLayoutBox
* wxRichTextAction::GetContainer() const
10838 wxRichTextParagraphLayoutBox
* container
= wxDynamicCast(GetContainerAddress().GetObject(m_buffer
), wxRichTextParagraphLayoutBox
);
10843 void wxRichTextAction::CalculateRefreshOptimizations(wxArrayInt
& optimizationLineCharPositions
, wxArrayInt
& optimizationLineYPositions
)
10845 // Store a list of line start character and y positions so we can figure out which area
10846 // we need to refresh
10848 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10849 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10850 wxASSERT(container
!= NULL
);
10854 // NOTE: we're assuming that the buffer is laid out correctly at this point.
10855 // If we had several actions, which only invalidate and leave layout until the
10856 // paint handler is called, then this might not be true. So we may need to switch
10857 // optimisation on only when we're simply adding text and not simultaneously
10858 // deleting a selection, for example. Or, we make sure the buffer is laid out correctly
10859 // first, but of course this means we'll be doing it twice.
10860 if (!m_buffer
->IsDirty() && m_ctrl
) // can only do optimisation if the buffer is already laid out correctly
10862 wxSize clientSize
= m_ctrl
->GetUnscaledSize(m_ctrl
->GetClientSize());
10863 wxPoint firstVisiblePt
= m_ctrl
->GetUnscaledPoint(m_ctrl
->GetFirstVisiblePoint());
10864 int lastY
= firstVisiblePt
.y
+ clientSize
.y
;
10866 wxRichTextParagraph
* para
= container
->GetParagraphAtPosition(GetRange().GetStart());
10867 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(para
);
10870 wxRichTextParagraph
* child
= (wxRichTextParagraph
*) node
->GetData();
10871 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
10874 wxRichTextLine
* line
= node2
->GetData();
10875 wxPoint pt
= line
->GetAbsolutePosition();
10876 wxRichTextRange range
= line
->GetAbsoluteRange();
10880 node2
= wxRichTextLineList::compatibility_iterator();
10881 node
= wxRichTextObjectList::compatibility_iterator();
10883 else if (range
.GetStart() > GetPosition() && pt
.y
>= firstVisiblePt
.y
)
10885 optimizationLineCharPositions
.Add(range
.GetStart());
10886 optimizationLineYPositions
.Add(pt
.y
);
10890 node2
= node2
->GetNext();
10894 node
= node
->GetNext();
10900 bool wxRichTextAction::Do()
10902 m_buffer
->Modify(true);
10904 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10905 wxASSERT(container
!= NULL
);
10911 case wxRICHTEXT_INSERT
:
10913 // Store a list of line start character and y positions so we can figure out which area
10914 // we need to refresh
10915 wxArrayInt optimizationLineCharPositions
;
10916 wxArrayInt optimizationLineYPositions
;
10918 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10919 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
10922 container
->InsertFragment(GetRange().GetStart(), m_newParagraphs
);
10923 container
->UpdateRanges();
10925 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10926 // Layout() would stop prematurely at the top level.
10927 container
->InvalidateHierarchy(wxRichTextRange(wxMax(0, GetRange().GetStart()-1), GetRange().GetEnd()));
10929 long newCaretPosition
= GetPosition() + m_newParagraphs
.GetOwnRange().GetLength();
10931 // Character position to caret position
10932 newCaretPosition
--;
10934 // Don't take into account the last newline
10935 if (m_newParagraphs
.GetPartialParagraph())
10936 newCaretPosition
--;
10938 if (m_newParagraphs
.GetChildren().GetCount() > 1)
10940 wxRichTextObject
* p
= (wxRichTextObject
*) m_newParagraphs
.GetChildren().GetLast()->GetData();
10941 if (p
->GetRange().GetLength() == 1)
10942 newCaretPosition
--;
10945 newCaretPosition
= wxMin(newCaretPosition
, (container
->GetOwnRange().GetEnd()-1));
10947 UpdateAppearance(newCaretPosition
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */);
10949 wxRichTextEvent
cmdEvent(
10950 wxEVT_RICHTEXT_CONTENT_INSERTED
,
10951 m_ctrl
? m_ctrl
->GetId() : -1);
10952 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10953 cmdEvent
.SetRange(GetRange());
10954 cmdEvent
.SetPosition(GetRange().GetStart());
10955 cmdEvent
.SetContainer(container
);
10957 m_buffer
->SendEvent(cmdEvent
);
10961 case wxRICHTEXT_DELETE
:
10963 wxArrayInt optimizationLineCharPositions
;
10964 wxArrayInt optimizationLineYPositions
;
10966 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10967 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
10970 container
->DeleteRange(GetRange());
10971 container
->UpdateRanges();
10972 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10973 // Layout() would stop prematurely at the top level.
10974 container
->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
10976 long caretPos
= GetRange().GetStart()-1;
10977 if (caretPos
>= container
->GetOwnRange().GetEnd())
10980 UpdateAppearance(caretPos
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */);
10982 wxRichTextEvent
cmdEvent(
10983 wxEVT_RICHTEXT_CONTENT_DELETED
,
10984 m_ctrl
? m_ctrl
->GetId() : -1);
10985 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10986 cmdEvent
.SetRange(GetRange());
10987 cmdEvent
.SetPosition(GetRange().GetStart());
10988 cmdEvent
.SetContainer(container
);
10990 m_buffer
->SendEvent(cmdEvent
);
10994 case wxRICHTEXT_CHANGE_STYLE
:
10995 case wxRICHTEXT_CHANGE_PROPERTIES
:
10997 ApplyParagraphs(GetNewParagraphs());
10999 // Invalidate the whole buffer if there were floating objects
11000 if (wxRichTextBuffer::GetFloatingLayoutMode() && container
->GetFloatingObjectCount() > 0)
11001 m_buffer
->InvalidateHierarchy(wxRICHTEXT_ALL
);
11004 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
11005 // Layout() would stop prematurely at the top level.
11006 container
->InvalidateHierarchy(GetRange());
11009 UpdateAppearance(GetPosition());
11011 wxRichTextEvent
cmdEvent(
11012 m_cmdId
== wxRICHTEXT_CHANGE_STYLE
? wxEVT_RICHTEXT_STYLE_CHANGED
: wxEVT_RICHTEXT_PROPERTIES_CHANGED
,
11013 m_ctrl
? m_ctrl
->GetId() : -1);
11014 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
11015 cmdEvent
.SetRange(GetRange());
11016 cmdEvent
.SetPosition(GetRange().GetStart());
11017 cmdEvent
.SetContainer(container
);
11019 m_buffer
->SendEvent(cmdEvent
);
11023 case wxRICHTEXT_CHANGE_ATTRIBUTES
:
11025 wxRichTextObject
* obj
= m_objectAddress
.GetObject(m_buffer
); // container->GetChildAtPosition(GetRange().GetStart());
11028 wxRichTextAttr oldAttr
= obj
->GetAttributes();
11029 obj
->GetAttributes() = m_attributes
;
11030 m_attributes
= oldAttr
;
11033 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
11034 // Layout() would stop prematurely at the top level.
11035 // Invalidate the whole buffer if there were floating objects
11036 if (wxRichTextBuffer::GetFloatingLayoutMode() && container
->GetFloatingObjectCount() > 0)
11037 m_buffer
->InvalidateHierarchy(wxRICHTEXT_ALL
);
11039 container
->InvalidateHierarchy(GetRange());
11041 UpdateAppearance(GetPosition());
11043 wxRichTextEvent
cmdEvent(
11044 wxEVT_RICHTEXT_STYLE_CHANGED
,
11045 m_ctrl
? m_ctrl
->GetId() : -1);
11046 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
11047 cmdEvent
.SetRange(GetRange());
11048 cmdEvent
.SetPosition(GetRange().GetStart());
11049 cmdEvent
.SetContainer(container
);
11051 m_buffer
->SendEvent(cmdEvent
);
11055 case wxRICHTEXT_CHANGE_OBJECT
:
11057 wxRichTextObject
* obj
= m_objectAddress
.GetObject(m_buffer
);
11058 if (obj
&& m_object
&& m_ctrl
)
11060 // The plan is to swap the current object with the stored, previous-state, clone
11061 // We can't get 'node' from the containing buffer (as it doesn't directly store objects)
11062 // so use the parent paragraph
11063 wxRichTextParagraph
* para
= wxDynamicCast(obj
->GetParent(), wxRichTextParagraph
);
11064 wxCHECK_MSG(para
, false, "Invalid parent paragraph");
11066 // The stored object, m_object, may have a stale parent paragraph. This would cause
11067 // a crash during layout, so use obj's parent para, which should be the correct one.
11068 // (An alternative would be to return the parent too from m_objectAddress.GetObject(),
11069 // or to set obj's parent there before returning)
11070 m_object
->SetParent(para
);
11072 wxRichTextObjectList::compatibility_iterator node
= para
->GetChildren().Find(obj
);
11075 wxRichTextObject
* obj
= node
->GetData();
11076 node
->SetData(m_object
);
11081 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
11082 // Layout() would stop prematurely at the top level.
11083 // Invalidate the whole buffer if there were floating objects
11084 if (wxRichTextBuffer::GetFloatingLayoutMode() && container
->GetFloatingObjectCount() > 0)
11085 m_buffer
->InvalidateHierarchy(wxRICHTEXT_ALL
);
11087 container
->InvalidateHierarchy(GetRange());
11089 UpdateAppearance(GetPosition());
11091 // TODO: send new kind of modification event
11102 bool wxRichTextAction::Undo()
11104 m_buffer
->Modify(true);
11106 wxRichTextParagraphLayoutBox
* container
= GetContainer();
11107 wxASSERT(container
!= NULL
);
11113 case wxRICHTEXT_INSERT
:
11115 wxArrayInt optimizationLineCharPositions
;
11116 wxArrayInt optimizationLineYPositions
;
11118 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
11119 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
11122 container
->DeleteRange(GetRange());
11123 container
->UpdateRanges();
11125 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
11126 // Layout() would stop prematurely at the top level.
11127 container
->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
11129 long newCaretPosition
= GetPosition() - 1;
11131 UpdateAppearance(newCaretPosition
, true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */);
11133 wxRichTextEvent
cmdEvent(
11134 wxEVT_RICHTEXT_CONTENT_DELETED
,
11135 m_ctrl
? m_ctrl
->GetId() : -1);
11136 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
11137 cmdEvent
.SetRange(GetRange());
11138 cmdEvent
.SetPosition(GetRange().GetStart());
11139 cmdEvent
.SetContainer(container
);
11141 m_buffer
->SendEvent(cmdEvent
);
11145 case wxRICHTEXT_DELETE
:
11147 wxArrayInt optimizationLineCharPositions
;
11148 wxArrayInt optimizationLineYPositions
;
11150 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
11151 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
11154 container
->InsertFragment(GetRange().GetStart(), m_oldParagraphs
);
11155 container
->UpdateRanges();
11157 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
11158 // Layout() would stop prematurely at the top level.
11159 container
->InvalidateHierarchy(GetRange());
11161 UpdateAppearance(GetPosition(), true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */);
11163 wxRichTextEvent
cmdEvent(
11164 wxEVT_RICHTEXT_CONTENT_INSERTED
,
11165 m_ctrl
? m_ctrl
->GetId() : -1);
11166 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
11167 cmdEvent
.SetRange(GetRange());
11168 cmdEvent
.SetPosition(GetRange().GetStart());
11169 cmdEvent
.SetContainer(container
);
11171 m_buffer
->SendEvent(cmdEvent
);
11175 case wxRICHTEXT_CHANGE_STYLE
:
11176 case wxRICHTEXT_CHANGE_PROPERTIES
:
11178 ApplyParagraphs(GetOldParagraphs());
11179 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
11180 // Layout() would stop prematurely at the top level.
11181 container
->InvalidateHierarchy(GetRange());
11183 UpdateAppearance(GetPosition());
11185 wxRichTextEvent
cmdEvent(
11186 m_cmdId
== wxRICHTEXT_CHANGE_STYLE
? wxEVT_RICHTEXT_STYLE_CHANGED
: wxEVT_RICHTEXT_PROPERTIES_CHANGED
,
11187 m_ctrl
? m_ctrl
->GetId() : -1);
11188 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
11189 cmdEvent
.SetRange(GetRange());
11190 cmdEvent
.SetPosition(GetRange().GetStart());
11191 cmdEvent
.SetContainer(container
);
11193 m_buffer
->SendEvent(cmdEvent
);
11197 case wxRICHTEXT_CHANGE_ATTRIBUTES
:
11198 case wxRICHTEXT_CHANGE_OBJECT
:
11209 /// Update the control appearance
11210 void wxRichTextAction::UpdateAppearance(long caretPosition
, bool sendUpdateEvent
, wxArrayInt
* optimizationLineCharPositions
, wxArrayInt
* optimizationLineYPositions
, bool isDoCmd
)
11212 wxRichTextParagraphLayoutBox
* container
= GetContainer();
11213 wxASSERT(container
!= NULL
);
11219 m_ctrl
->SetFocusObject(container
);
11220 m_ctrl
->SetCaretPosition(caretPosition
);
11222 if (!m_ctrl
->IsFrozen())
11224 wxRect containerRect
= container
->GetRect();
11226 m_ctrl
->LayoutContent();
11228 // Refresh everything if there were floating objects or the container changed size
11229 // (we can't yet optimize in these cases, since more complex interaction with other content occurs)
11230 if ((wxRichTextBuffer::GetFloatingLayoutMode() && container
->GetFloatingObjectCount() > 0) || (container
->GetParent() && containerRect
!= container
->GetRect()))
11232 m_ctrl
->Refresh(false);
11236 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
11237 // Find refresh rectangle if we are in a position to optimise refresh
11238 if ((m_cmdId
== wxRICHTEXT_INSERT
|| m_cmdId
== wxRICHTEXT_DELETE
) && optimizationLineCharPositions
)
11242 wxSize clientSize
= m_ctrl
->GetUnscaledSize(m_ctrl
->GetClientSize());
11243 wxPoint firstVisiblePt
= m_ctrl
->GetUnscaledPoint(m_ctrl
->GetFirstVisiblePoint());
11245 // Start/end positions
11247 int lastY
= firstVisiblePt
.y
+ clientSize
.y
;
11249 bool foundEnd
= false;
11251 // position offset - how many characters were inserted
11252 int positionOffset
= GetRange().GetLength();
11254 // Determine whether this is Do or Undo, and adjust positionOffset accordingly
11255 if ((m_cmdId
== wxRICHTEXT_DELETE
&& isDoCmd
) || (m_cmdId
== wxRICHTEXT_INSERT
&& !isDoCmd
))
11256 positionOffset
= - positionOffset
;
11258 // find the first line which is being drawn at the same position as it was
11259 // before. Since we're talking about a simple insertion, we can assume
11260 // that the rest of the window does not need to be redrawn.
11261 long pos
= GetRange().GetStart();
11263 wxRichTextParagraph
* para
= container
->GetParagraphAtPosition(pos
, false /* is not caret pos */);
11264 // Since we support floating layout, we should redraw the whole para instead of just
11265 // the first line touching the invalid range.
11268 // In case something was drawn above the paragraph,
11269 // such as a line break, allow a little extra.
11270 firstY
= para
->GetPosition().y
- 4;
11273 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(para
);
11276 wxRichTextParagraph
* child
= (wxRichTextParagraph
*) node
->GetData();
11277 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
11280 wxRichTextLine
* line
= node2
->GetData();
11281 wxPoint pt
= line
->GetAbsolutePosition();
11282 wxRichTextRange range
= line
->GetAbsoluteRange();
11284 // we want to find the first line that is in the same position
11285 // as before. This will mean we're at the end of the changed text.
11287 if (pt
.y
> lastY
) // going past the end of the window, no more info
11289 node2
= wxRichTextLineList::compatibility_iterator();
11290 node
= wxRichTextObjectList::compatibility_iterator();
11292 // Detect last line in the buffer
11293 else if (!node2
->GetNext() && para
->GetRange().Contains(container
->GetOwnRange().GetEnd()))
11295 // If deleting text, make sure we refresh below as well as above
11296 if (positionOffset
>= 0)
11299 lastY
= pt
.y
+ line
->GetSize().y
;
11302 node2
= wxRichTextLineList::compatibility_iterator();
11303 node
= wxRichTextObjectList::compatibility_iterator();
11309 // search for this line being at the same position as before
11310 for (i
= 0; i
< optimizationLineCharPositions
->GetCount(); i
++)
11312 if (((*optimizationLineCharPositions
)[i
] + positionOffset
== range
.GetStart()) &&
11313 ((*optimizationLineYPositions
)[i
] == pt
.y
))
11315 // Stop, we're now the same as we were
11318 lastY
= pt
.y
+ line
->GetSize().y
;
11320 node2
= wxRichTextLineList::compatibility_iterator();
11321 node
= wxRichTextObjectList::compatibility_iterator();
11329 node2
= node2
->GetNext();
11333 node
= node
->GetNext();
11336 firstY
= wxMax(firstVisiblePt
.y
, firstY
);
11338 lastY
= firstVisiblePt
.y
+ clientSize
.y
;
11340 // Convert to device coordinates
11341 wxRect
rect(m_ctrl
->GetPhysicalPoint(m_ctrl
->GetScaledPoint(wxPoint(firstVisiblePt
.x
, firstY
))), m_ctrl
->GetScaledSize(wxSize(clientSize
.x
, lastY
- firstY
)));
11342 m_ctrl
->RefreshRect(rect
);
11346 m_ctrl
->Refresh(false);
11348 m_ctrl
->PositionCaret();
11350 // This causes styles to persist when doing programmatic
11351 // content creation except when Freeze/Thaw is used, so
11352 // disable this and check for the consequences.
11353 // m_ctrl->SetDefaultStyleToCursorStyle();
11355 if (sendUpdateEvent
)
11356 wxTextCtrl::SendTextUpdatedEvent(m_ctrl
);
11361 /// Replace the buffer paragraphs with the new ones.
11362 void wxRichTextAction::ApplyParagraphs(const wxRichTextParagraphLayoutBox
& fragment
)
11364 wxRichTextParagraphLayoutBox
* container
= GetContainer();
11365 wxASSERT(container
!= NULL
);
11369 wxRichTextObjectList::compatibility_iterator node
= fragment
.GetChildren().GetFirst();
11372 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
11373 wxASSERT (para
!= NULL
);
11375 // We'll replace the existing paragraph by finding the paragraph at this position,
11376 // delete its node data, and setting a copy as the new node data.
11377 // TODO: make more efficient by simply swapping old and new paragraph objects.
11379 wxRichTextParagraph
* existingPara
= container
->GetParagraphAtPosition(para
->GetRange().GetStart());
11382 wxRichTextObjectList::compatibility_iterator bufferParaNode
= container
->GetChildren().Find(existingPara
);
11383 if (bufferParaNode
)
11385 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(*para
);
11386 newPara
->SetParent(container
);
11388 bufferParaNode
->SetData(newPara
);
11390 delete existingPara
;
11394 node
= node
->GetNext();
11401 * This stores beginning and end positions for a range of data.
11404 WX_DEFINE_OBJARRAY(wxRichTextRangeArray
);
11406 /// Limit this range to be within 'range'
11407 bool wxRichTextRange::LimitTo(const wxRichTextRange
& range
)
11409 if (m_start
< range
.m_start
)
11410 m_start
= range
.m_start
;
11412 if (m_end
> range
.m_end
)
11413 m_end
= range
.m_end
;
11419 * wxRichTextImage implementation
11420 * This object represents an image.
11423 IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage
, wxRichTextObject
)
11425 wxRichTextImage::wxRichTextImage(const wxImage
& image
, wxRichTextObject
* parent
, wxRichTextAttr
* charStyle
):
11426 wxRichTextObject(parent
)
11429 m_imageBlock
.MakeImageBlockDefaultQuality(image
, wxBITMAP_TYPE_PNG
);
11431 SetAttributes(*charStyle
);
11434 wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock
& imageBlock
, wxRichTextObject
* parent
, wxRichTextAttr
* charStyle
):
11435 wxRichTextObject(parent
)
11438 m_imageBlock
= imageBlock
;
11440 SetAttributes(*charStyle
);
11443 wxRichTextImage::~wxRichTextImage()
11447 void wxRichTextImage::Init()
11449 m_originalImageSize
= wxSize(-1, -1);
11452 /// Create a cached image at the required size
11453 bool wxRichTextImage::LoadImageCache(wxDC
& dc
, bool resetCache
, const wxSize
& parentSize
)
11455 if (!m_imageBlock
.IsOk())
11458 // If we have an original image size, use that to compute the cached bitmap size
11459 // instead of loading the image each time. This way we can avoid loading
11460 // the image so long as the new cached bitmap size hasn't changed.
11463 if (resetCache
|| m_originalImageSize
.GetWidth() <= 0 || m_originalImageSize
.GetHeight() <= 0)
11465 m_imageCache
= wxNullBitmap
;
11467 m_imageBlock
.Load(image
);
11471 m_originalImageSize
= wxSize(image
.GetWidth(), image
.GetHeight());
11474 int width
= m_originalImageSize
.GetWidth();
11475 int height
= m_originalImageSize
.GetHeight();
11477 int parentWidth
= 0;
11478 int parentHeight
= 0;
11481 int maxHeight
= -1;
11483 wxSize sz
= parentSize
;
11484 if (sz
== wxDefaultSize
)
11486 if (GetParent() && GetParent()->GetParent())
11487 sz
= GetParent()->GetParent()->GetCachedSize();
11490 if (sz
!= wxDefaultSize
)
11492 wxRichTextBuffer
* buffer
= GetBuffer();
11495 // Find the actual space available when margin is taken into account
11496 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
11497 marginRect
= wxRect(0, 0, sz
.x
, sz
.y
);
11498 if (GetParent() && GetParent()->GetParent())
11500 buffer
->GetBoxRects(dc
, buffer
, GetParent()->GetParent()->GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
11501 sz
= contentRect
.GetSize();
11504 // Use a minimum size to stop images becoming very small
11505 parentWidth
= wxMax(100, sz
.GetWidth());
11506 parentHeight
= wxMax(100, sz
.GetHeight());
11508 if (buffer
->GetRichTextCtrl())
11509 // Start with a maximum width of the control size, even if not specified by the content,
11510 // to minimize the amount of picture overlapping the right-hand side
11511 maxWidth
= parentWidth
;
11515 if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetWidth().GetValue() > 0)
11517 if (parentWidth
> 0 && GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
11518 width
= (int) ((GetAttributes().GetTextBoxAttr().GetWidth().GetValue() * parentWidth
)/100.0);
11519 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
11520 width
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetWidth().GetValue());
11521 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
11522 width
= GetAttributes().GetTextBoxAttr().GetWidth().GetValue();
11525 // Limit to max width
11527 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() > 0)
11531 if (parentWidth
> 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
11532 mw
= (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() * parentWidth
)/100.0);
11533 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
11534 mw
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue());
11535 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
11536 mw
= GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue();
11538 // If we already have a smaller max width due to the constraints of the control size,
11539 // don't use the larger max width.
11540 if (mw
!= -1 && ((maxWidth
== -1) || (mw
< maxWidth
)))
11544 if (maxWidth
> 0 && width
> maxWidth
)
11547 // Preserve the aspect ratio
11548 if (width
!= m_originalImageSize
.GetWidth())
11549 height
= (int) (float(m_originalImageSize
.GetHeight()) * (float(width
)/float(m_originalImageSize
.GetWidth())));
11551 if (GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetHeight().GetValue() > 0)
11553 if (parentHeight
> 0 && GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
11554 height
= (int) ((GetAttributes().GetTextBoxAttr().GetHeight().GetValue() * parentHeight
)/100.0);
11555 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
11556 height
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetHeight().GetValue());
11557 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
11558 height
= GetAttributes().GetTextBoxAttr().GetHeight().GetValue();
11560 // Preserve the aspect ratio
11561 if (height
!= m_originalImageSize
.GetHeight())
11562 width
= (int) (float(m_originalImageSize
.GetWidth()) * (float(height
)/float(m_originalImageSize
.GetHeight())));
11565 // Limit to max height
11567 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() > 0)
11569 if (parentHeight
> 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
11570 maxHeight
= (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() * parentHeight
)/100.0);
11571 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
11572 maxHeight
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue());
11573 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
11574 maxHeight
= GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue();
11577 if (maxHeight
> 0 && height
> maxHeight
)
11579 height
= maxHeight
;
11581 // Preserve the aspect ratio
11582 if (height
!= m_originalImageSize
.GetHeight())
11583 width
= (int) (float(m_originalImageSize
.GetWidth()) * (float(height
)/float(m_originalImageSize
.GetHeight())));
11586 // Prevent the use of zero size
11587 width
= wxMax(1, width
);
11588 height
= wxMax(1, height
);
11590 if (m_imageCache
.IsOk() && m_imageCache
.GetWidth() == width
&& m_imageCache
.GetHeight() == height
)
11592 // Do nothing, we didn't need to change the image cache
11598 m_imageBlock
.Load(image
);
11603 if (image
.GetWidth() == width
&& image
.GetHeight() == height
)
11604 m_imageCache
= wxBitmap(image
);
11607 // If the original width and height is small, e.g. 400 or below,
11608 // scale up and then down to improve image quality. This can make
11609 // a big difference, with not much performance hit.
11610 int upscaleThreshold
= 400;
11612 if (image
.GetWidth() <= upscaleThreshold
|| image
.GetHeight() <= upscaleThreshold
)
11614 img
= image
.Scale(image
.GetWidth()*2, image
.GetHeight()*2);
11615 img
.Rescale(width
, height
, wxIMAGE_QUALITY_HIGH
);
11618 img
= image
.Scale(width
, height
, wxIMAGE_QUALITY_HIGH
);
11619 m_imageCache
= wxBitmap(img
);
11623 return m_imageCache
.IsOk();
11627 bool wxRichTextImage::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& WXUNUSED(range
), const wxRichTextSelection
& selection
, const wxRect
& rect
, int WXUNUSED(descent
), int WXUNUSED(style
))
11632 // Don't need cached size AFAIK
11633 // wxSize size = GetCachedSize();
11634 if (!LoadImageCache(dc
))
11637 wxRichTextAttr
attr(GetAttributes());
11638 context
.ApplyVirtualAttributes(attr
, this);
11640 DrawBoxAttributes(dc
, GetBuffer(), attr
, wxRect(rect
.GetPosition(), GetCachedSize()));
11642 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
11643 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
11644 marginRect
= rect
; // outer rectangle, will calculate contentRect
11645 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
11647 dc
.DrawBitmap(m_imageCache
, contentRect
.x
, contentRect
.y
, true);
11649 if (selection
.WithinSelection(GetRange().GetStart(), this))
11651 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
11652 wxCheckSetPen(dc
, *wxBLACK_PEN
);
11653 dc
.SetLogicalFunction(wxINVERT
);
11654 dc
.DrawRectangle(contentRect
);
11655 dc
.SetLogicalFunction(wxCOPY
);
11661 /// Lay the item out
11662 bool wxRichTextImage::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& WXUNUSED(parentRect
), int WXUNUSED(style
))
11664 if (!LoadImageCache(dc
))
11667 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
11668 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
11669 contentRect
= wxRect(wxPoint(0,0), imageSize
);
11671 wxRichTextAttr
attr(GetAttributes());
11672 context
.ApplyVirtualAttributes(attr
, this);
11674 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
11676 wxSize overallSize
= marginRect
.GetSize();
11678 SetCachedSize(overallSize
);
11679 SetMaxSize(overallSize
);
11680 SetMinSize(overallSize
);
11681 SetPosition(rect
.GetPosition());
11686 /// Get/set the object size for the given range. Returns false if the range
11687 /// is invalid for this object.
11688 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
11690 if (!range
.IsWithin(GetRange()))
11693 if (!((wxRichTextImage
*)this)->LoadImageCache(dc
, false, parentSize
))
11695 size
.x
= 0; size
.y
= 0;
11696 if (partialExtents
)
11697 partialExtents
->Add(0);
11701 wxRichTextAttr
attr(GetAttributes());
11702 context
.ApplyVirtualAttributes(attr
, (wxRichTextObject
*) this);
11704 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
11705 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
11706 contentRect
= wxRect(wxPoint(0,0), imageSize
);
11707 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
11709 wxSize overallSize
= marginRect
.GetSize();
11711 if (partialExtents
)
11712 partialExtents
->Add(overallSize
.x
);
11714 size
= overallSize
;
11719 // Get the 'natural' size for an object. For an image, it would be the
11721 wxTextAttrSize
wxRichTextImage::GetNaturalSize() const
11723 wxTextAttrSize size
;
11724 if (GetImageCache().IsOk())
11726 size
.SetWidth(GetImageCache().GetWidth(), wxTEXT_ATTR_UNITS_PIXELS
);
11727 size
.SetHeight(GetImageCache().GetHeight(), wxTEXT_ATTR_UNITS_PIXELS
);
11734 void wxRichTextImage::Copy(const wxRichTextImage
& obj
)
11736 wxRichTextObject::Copy(obj
);
11738 m_imageBlock
= obj
.m_imageBlock
;
11739 m_originalImageSize
= obj
.m_originalImageSize
;
11742 /// Edit properties via a GUI
11743 bool wxRichTextImage::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
11745 wxRichTextObjectPropertiesDialog
imageDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Picture Properties"));
11746 imageDlg
.SetAttributes(GetAttributes());
11748 if (imageDlg
.ShowModal() == wxID_OK
)
11750 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
11751 // indeterminate in the object.
11752 imageDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
11764 /// Compare two attribute objects
11765 bool wxTextAttrEq(const wxRichTextAttr
& attr1
, const wxRichTextAttr
& attr2
)
11767 return (attr1
== attr2
);
11771 bool wxRichTextTabsEq(const wxArrayInt
& tabs1
, const wxArrayInt
& tabs2
)
11773 if (tabs1
.GetCount() != tabs2
.GetCount())
11777 for (i
= 0; i
< tabs1
.GetCount(); i
++)
11779 if (tabs1
[i
] != tabs2
[i
])
11785 bool wxRichTextApplyStyle(wxRichTextAttr
& destStyle
, const wxRichTextAttr
& style
, wxRichTextAttr
* compareWith
)
11787 return destStyle
.Apply(style
, compareWith
);
11790 // Remove attributes
11791 bool wxRichTextRemoveStyle(wxRichTextAttr
& destStyle
, const wxRichTextAttr
& style
)
11793 return destStyle
.RemoveStyle(style
);
11796 /// Combine two bitlists, specifying the bits of interest with separate flags.
11797 bool wxRichTextCombineBitlists(int& valueA
, int valueB
, int& flagsA
, int flagsB
)
11799 return wxRichTextAttr::CombineBitlists(valueA
, valueB
, flagsA
, flagsB
);
11802 /// Compare two bitlists
11803 bool wxRichTextBitlistsEqPartial(int valueA
, int valueB
, int flags
)
11805 return wxRichTextAttr::BitlistsEqPartial(valueA
, valueB
, flags
);
11808 /// Split into paragraph and character styles
11809 bool wxRichTextSplitParaCharStyles(const wxRichTextAttr
& style
, wxRichTextAttr
& parStyle
, wxRichTextAttr
& charStyle
)
11811 return wxRichTextAttr::SplitParaCharStyles(style
, parStyle
, charStyle
);
11814 /// Convert a decimal to Roman numerals
11815 wxString
wxRichTextDecimalToRoman(long n
)
11817 static wxArrayInt decimalNumbers
;
11818 static wxArrayString romanNumbers
;
11823 decimalNumbers
.Clear();
11824 romanNumbers
.Clear();
11825 return wxEmptyString
;
11828 if (decimalNumbers
.GetCount() == 0)
11830 #define wxRichTextAddDecRom(n, r) decimalNumbers.Add(n); romanNumbers.Add(r);
11832 wxRichTextAddDecRom(1000, wxT("M"));
11833 wxRichTextAddDecRom(900, wxT("CM"));
11834 wxRichTextAddDecRom(500, wxT("D"));
11835 wxRichTextAddDecRom(400, wxT("CD"));
11836 wxRichTextAddDecRom(100, wxT("C"));
11837 wxRichTextAddDecRom(90, wxT("XC"));
11838 wxRichTextAddDecRom(50, wxT("L"));
11839 wxRichTextAddDecRom(40, wxT("XL"));
11840 wxRichTextAddDecRom(10, wxT("X"));
11841 wxRichTextAddDecRom(9, wxT("IX"));
11842 wxRichTextAddDecRom(5, wxT("V"));
11843 wxRichTextAddDecRom(4, wxT("IV"));
11844 wxRichTextAddDecRom(1, wxT("I"));
11850 while (n
> 0 && i
< 13)
11852 if (n
>= decimalNumbers
[i
])
11854 n
-= decimalNumbers
[i
];
11855 roman
+= romanNumbers
[i
];
11862 if (roman
.IsEmpty())
11868 * wxRichTextFileHandler
11869 * Base class for file handlers
11872 IMPLEMENT_CLASS(wxRichTextFileHandler
, wxObject
)
11874 #if wxUSE_FFILE && wxUSE_STREAMS
11875 bool wxRichTextFileHandler::LoadFile(wxRichTextBuffer
*buffer
, const wxString
& filename
)
11877 wxFFileInputStream
stream(filename
);
11879 return LoadFile(buffer
, stream
);
11884 bool wxRichTextFileHandler::SaveFile(wxRichTextBuffer
*buffer
, const wxString
& filename
)
11886 wxFFileOutputStream
stream(filename
);
11888 return SaveFile(buffer
, stream
);
11892 #endif // wxUSE_FFILE && wxUSE_STREAMS
11894 /// Can we handle this filename (if using files)? By default, checks the extension.
11895 bool wxRichTextFileHandler::CanHandle(const wxString
& filename
) const
11897 wxString path
, file
, ext
;
11898 wxFileName::SplitPath(filename
, & path
, & file
, & ext
);
11900 return (ext
.Lower() == GetExtension());
11904 * wxRichTextTextHandler
11905 * Plain text handler
11908 IMPLEMENT_CLASS(wxRichTextPlainTextHandler
, wxRichTextFileHandler
)
11911 bool wxRichTextPlainTextHandler::DoLoadFile(wxRichTextBuffer
*buffer
, wxInputStream
& stream
)
11913 if (!stream
.IsOk())
11919 while (!stream
.Eof())
11921 int ch
= stream
.GetC();
11925 if (ch
== 10 && lastCh
!= 13)
11928 if (ch
> 0 && ch
!= 10)
11935 buffer
->ResetAndClearCommands();
11937 buffer
->AddParagraphs(str
);
11938 buffer
->UpdateRanges();
11943 bool wxRichTextPlainTextHandler::DoSaveFile(wxRichTextBuffer
*buffer
, wxOutputStream
& stream
)
11945 if (!stream
.IsOk())
11948 wxString text
= buffer
->GetText();
11950 wxString newLine
= wxRichTextLineBreakChar
;
11951 text
.Replace(newLine
, wxT("\n"));
11953 wxCharBuffer buf
= text
.ToAscii();
11955 stream
.Write((const char*) buf
, text
.length());
11958 #endif // wxUSE_STREAMS
11961 * Stores information about an image, in binary in-memory form
11964 wxRichTextImageBlock::wxRichTextImageBlock()
11969 wxRichTextImageBlock::wxRichTextImageBlock(const wxRichTextImageBlock
& block
):wxObject()
11975 wxRichTextImageBlock::~wxRichTextImageBlock()
11980 void wxRichTextImageBlock::Init()
11984 m_imageType
= wxBITMAP_TYPE_INVALID
;
11987 void wxRichTextImageBlock::Clear()
11991 m_imageType
= wxBITMAP_TYPE_INVALID
;
11995 // Load the original image into a memory block.
11996 // If the image is not a JPEG, we must convert it into a JPEG
11997 // to conserve space.
11998 // If it's not a JPEG we can make use of 'image', already scaled, so we don't have to
11999 // load the image a 2nd time.
12001 bool wxRichTextImageBlock::MakeImageBlock(const wxString
& filename
, wxBitmapType imageType
,
12002 wxImage
& image
, bool convertToJPEG
)
12004 m_imageType
= imageType
;
12006 wxString
filenameToRead(filename
);
12007 bool removeFile
= false;
12009 if (imageType
== wxBITMAP_TYPE_INVALID
)
12010 return false; // Could not determine image type
12012 if ((imageType
!= wxBITMAP_TYPE_JPEG
) && convertToJPEG
)
12014 wxString tempFile
=
12015 wxFileName::CreateTempFileName(_("image"));
12017 wxASSERT(!tempFile
.IsEmpty());
12019 image
.SaveFile(tempFile
, wxBITMAP_TYPE_JPEG
);
12020 filenameToRead
= tempFile
;
12023 m_imageType
= wxBITMAP_TYPE_JPEG
;
12026 if (!file
.Open(filenameToRead
))
12029 m_dataSize
= (size_t) file
.Length();
12034 m_data
= ReadBlock(filenameToRead
, m_dataSize
);
12037 wxRemoveFile(filenameToRead
);
12039 return (m_data
!= NULL
);
12042 // Make an image block from the wxImage in the given
12044 bool wxRichTextImageBlock::MakeImageBlock(wxImage
& image
, wxBitmapType imageType
, int quality
)
12046 image
.SetOption(wxT("quality"), quality
);
12048 if (imageType
== wxBITMAP_TYPE_INVALID
)
12049 return false; // Could not determine image type
12051 return DoMakeImageBlock(image
, imageType
);
12054 // Uses a const wxImage for efficiency, but can't set quality (only relevant for JPEG)
12055 bool wxRichTextImageBlock::MakeImageBlockDefaultQuality(const wxImage
& image
, wxBitmapType imageType
)
12057 if (imageType
== wxBITMAP_TYPE_INVALID
)
12058 return false; // Could not determine image type
12060 return DoMakeImageBlock(image
, imageType
);
12063 // Makes the image block
12064 bool wxRichTextImageBlock::DoMakeImageBlock(const wxImage
& image
, wxBitmapType imageType
)
12066 wxMemoryOutputStream memStream
;
12067 if (!image
.SaveFile(memStream
, imageType
))
12072 unsigned char* block
= new unsigned char[memStream
.GetSize()];
12080 m_imageType
= imageType
;
12081 m_dataSize
= memStream
.GetSize();
12083 memStream
.CopyTo(m_data
, m_dataSize
);
12085 return (m_data
!= NULL
);
12089 bool wxRichTextImageBlock::Write(const wxString
& filename
)
12091 return WriteBlock(filename
, m_data
, m_dataSize
);
12094 void wxRichTextImageBlock::Copy(const wxRichTextImageBlock
& block
)
12096 m_imageType
= block
.m_imageType
;
12098 m_dataSize
= block
.m_dataSize
;
12099 if (m_dataSize
== 0)
12102 m_data
= new unsigned char[m_dataSize
];
12104 for (i
= 0; i
< m_dataSize
; i
++)
12105 m_data
[i
] = block
.m_data
[i
];
12109 void wxRichTextImageBlock::operator=(const wxRichTextImageBlock
& block
)
12114 // Load a wxImage from the block
12115 bool wxRichTextImageBlock::Load(wxImage
& image
)
12120 // Read in the image.
12122 wxMemoryInputStream
mstream(m_data
, m_dataSize
);
12123 bool success
= image
.LoadFile(mstream
, GetImageType());
12125 wxString tempFile
= wxFileName::CreateTempFileName(_("image"));
12126 wxASSERT(!tempFile
.IsEmpty());
12128 if (!WriteBlock(tempFile
, m_data
, m_dataSize
))
12132 success
= image
.LoadFile(tempFile
, GetImageType());
12133 wxRemoveFile(tempFile
);
12139 // Write data in hex to a stream
12140 bool wxRichTextImageBlock::WriteHex(wxOutputStream
& stream
)
12142 if (m_dataSize
== 0)
12145 int bufSize
= 100000;
12146 if (int(2*m_dataSize
) < bufSize
)
12147 bufSize
= 2*m_dataSize
;
12148 char* buf
= new char[bufSize
+1];
12150 int left
= m_dataSize
;
12155 if (left
*2 > bufSize
)
12157 n
= bufSize
; left
-= (bufSize
/2);
12161 n
= left
*2; left
= 0;
12165 for (i
= 0; i
< (n
/2); i
++)
12167 wxDecToHex(m_data
[j
], b
, b
+1);
12172 stream
.Write((const char*) buf
, n
);
12178 // Read data in hex from a stream
12179 bool wxRichTextImageBlock::ReadHex(wxInputStream
& stream
, int length
, wxBitmapType imageType
)
12181 int dataSize
= length
/2;
12186 // create a null terminated temporary string:
12190 m_data
= new unsigned char[dataSize
];
12192 for (i
= 0; i
< dataSize
; i
++)
12194 str
[0] = (char)stream
.GetC();
12195 str
[1] = (char)stream
.GetC();
12197 m_data
[i
] = (unsigned char)wxHexToDec(str
);
12200 m_dataSize
= dataSize
;
12201 m_imageType
= imageType
;
12206 // Allocate and read from stream as a block of memory
12207 unsigned char* wxRichTextImageBlock::ReadBlock(wxInputStream
& stream
, size_t size
)
12209 unsigned char* block
= new unsigned char[size
];
12213 stream
.Read(block
, size
);
12218 unsigned char* wxRichTextImageBlock::ReadBlock(const wxString
& filename
, size_t size
)
12220 wxFileInputStream
stream(filename
);
12221 if (!stream
.IsOk())
12224 return ReadBlock(stream
, size
);
12227 // Write memory block to stream
12228 bool wxRichTextImageBlock::WriteBlock(wxOutputStream
& stream
, unsigned char* block
, size_t size
)
12230 stream
.Write((void*) block
, size
);
12231 return stream
.IsOk();
12235 // Write memory block to file
12236 bool wxRichTextImageBlock::WriteBlock(const wxString
& filename
, unsigned char* block
, size_t size
)
12238 wxFileOutputStream
outStream(filename
);
12239 if (!outStream
.IsOk())
12242 return WriteBlock(outStream
, block
, size
);
12245 // Gets the extension for the block's type
12246 wxString
wxRichTextImageBlock::GetExtension() const
12248 wxImageHandler
* handler
= wxImage::FindHandler(GetImageType());
12250 return handler
->GetExtension();
12252 return wxEmptyString
;
12258 * The data object for a wxRichTextBuffer
12261 const wxChar
*wxRichTextBufferDataObject::ms_richTextBufferFormatId
= wxT("wxRichText");
12263 wxRichTextBufferDataObject::wxRichTextBufferDataObject(wxRichTextBuffer
* richTextBuffer
)
12265 m_richTextBuffer
= richTextBuffer
;
12267 // this string should uniquely identify our format, but is otherwise
12269 m_formatRichTextBuffer
.SetId(GetRichTextBufferFormatId());
12271 SetFormat(m_formatRichTextBuffer
);
12274 wxRichTextBufferDataObject::~wxRichTextBufferDataObject()
12276 delete m_richTextBuffer
;
12279 // after a call to this function, the richTextBuffer is owned by the caller and it
12280 // is responsible for deleting it!
12281 wxRichTextBuffer
* wxRichTextBufferDataObject::GetRichTextBuffer()
12283 wxRichTextBuffer
* richTextBuffer
= m_richTextBuffer
;
12284 m_richTextBuffer
= NULL
;
12286 return richTextBuffer
;
12289 wxDataFormat
wxRichTextBufferDataObject::GetPreferredFormat(Direction
WXUNUSED(dir
)) const
12291 return m_formatRichTextBuffer
;
12294 size_t wxRichTextBufferDataObject::GetDataSize() const
12296 if (!m_richTextBuffer
)
12302 wxStringOutputStream
stream(& bufXML
);
12303 if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
))
12305 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
12311 wxCharBuffer buffer
= bufXML
.mb_str(wxConvUTF8
);
12312 return strlen(buffer
) + 1;
12314 return bufXML
.Length()+1;
12318 bool wxRichTextBufferDataObject::GetDataHere(void *pBuf
) const
12320 if (!pBuf
|| !m_richTextBuffer
)
12326 wxStringOutputStream
stream(& bufXML
);
12327 if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
))
12329 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
12335 wxCharBuffer buffer
= bufXML
.mb_str(wxConvUTF8
);
12336 size_t len
= strlen(buffer
);
12337 memcpy((char*) pBuf
, (const char*) buffer
, len
);
12338 ((char*) pBuf
)[len
] = 0;
12340 size_t len
= bufXML
.Length();
12341 memcpy((char*) pBuf
, (const char*) bufXML
.c_str(), len
);
12342 ((char*) pBuf
)[len
] = 0;
12348 bool wxRichTextBufferDataObject::SetData(size_t WXUNUSED(len
), const void *buf
)
12350 wxDELETE(m_richTextBuffer
);
12352 wxString
bufXML((const char*) buf
, wxConvUTF8
);
12354 m_richTextBuffer
= new wxRichTextBuffer
;
12356 wxStringInputStream
stream(bufXML
);
12357 if (!m_richTextBuffer
->LoadFile(stream
, wxRICHTEXT_TYPE_XML
))
12359 wxLogError(wxT("Could not read the buffer from an XML stream.\nYou may have forgotten to add the XML file handler."));
12361 wxDELETE(m_richTextBuffer
);
12373 * wxRichTextFontTable
12374 * Manages quick access to a pool of fonts for rendering rich text
12377 WX_DECLARE_STRING_HASH_MAP_WITH_DECL(wxFont
, wxRichTextFontTableHashMap
, class WXDLLIMPEXP_RICHTEXT
);
12379 class wxRichTextFontTableData
: public wxObjectRefData
12382 wxRichTextFontTableData() {}
12384 wxFont
FindFont(const wxRichTextAttr
& fontSpec
, double fontScale
);
12386 wxRichTextFontTableHashMap m_hashMap
;
12389 wxFont
wxRichTextFontTableData::FindFont(const wxRichTextAttr
& fontSpec
, double fontScale
)
12391 wxString
facename(fontSpec
.GetFontFaceName());
12393 int fontSize
= fontSpec
.GetFontSize();
12394 if (fontScale
!= 1.0)
12395 fontSize
= (int) ((double(fontSize
) * fontScale
) + 0.5);
12398 if (fontSpec
.HasFontPixelSize() && !fontSpec
.HasFontPointSize())
12402 wxString spec
= wxString::Format(wxT("%d-%s-%d-%d-%d-%d-%s-%d"),
12403 fontSize
, units
.c_str(), fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), (int) fontSpec
.GetFontUnderlined(), (int) fontSpec
.GetFontStrikethrough(),
12404 facename
.c_str(), (int) fontSpec
.GetFontEncoding());
12406 wxRichTextFontTableHashMap::iterator entry
= m_hashMap
.find(spec
);
12407 if ( entry
== m_hashMap
.end() )
12409 if (fontSpec
.HasFontPixelSize() && !fontSpec
.HasFontPointSize())
12411 wxFont
font(wxSize(0, fontSize
), wxFONTFAMILY_DEFAULT
, fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), fontSpec
.GetFontUnderlined(), facename
);
12412 if (fontSpec
.HasFontStrikethrough() && fontSpec
.GetFontStrikethrough())
12413 font
.SetStrikethrough(true);
12414 m_hashMap
[spec
] = font
;
12419 wxFont
font(fontSize
, wxFONTFAMILY_DEFAULT
, fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), fontSpec
.GetFontUnderlined(), facename
.c_str());
12420 if (fontSpec
.HasFontStrikethrough() && fontSpec
.GetFontStrikethrough())
12421 font
.SetStrikethrough(true);
12423 m_hashMap
[spec
] = font
;
12429 return entry
->second
;
12433 IMPLEMENT_DYNAMIC_CLASS(wxRichTextFontTable
, wxObject
)
12435 wxRichTextFontTable::wxRichTextFontTable()
12437 m_refData
= new wxRichTextFontTableData
;
12441 wxRichTextFontTable::wxRichTextFontTable(const wxRichTextFontTable
& table
)
12447 wxRichTextFontTable::~wxRichTextFontTable()
12452 bool wxRichTextFontTable::operator == (const wxRichTextFontTable
& table
) const
12454 return (m_refData
== table
.m_refData
);
12457 void wxRichTextFontTable::operator= (const wxRichTextFontTable
& table
)
12460 m_fontScale
= table
.m_fontScale
;
12463 wxFont
wxRichTextFontTable::FindFont(const wxRichTextAttr
& fontSpec
)
12465 wxRichTextFontTableData
* data
= (wxRichTextFontTableData
*) m_refData
;
12467 return data
->FindFont(fontSpec
, m_fontScale
);
12472 void wxRichTextFontTable::Clear()
12474 wxRichTextFontTableData
* data
= (wxRichTextFontTableData
*) m_refData
;
12476 data
->m_hashMap
.clear();
12479 void wxRichTextFontTable::SetFontScale(double fontScale
)
12481 if (fontScale
!= m_fontScale
)
12483 m_fontScale
= fontScale
;
12488 void wxTextBoxAttr::Reset()
12491 m_floatMode
= wxTEXT_BOX_ATTR_FLOAT_NONE
;
12492 m_clearMode
= wxTEXT_BOX_ATTR_CLEAR_NONE
;
12493 m_collapseMode
= wxTEXT_BOX_ATTR_COLLAPSE_NONE
;
12494 m_verticalAlignment
= wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_NONE
;
12495 m_boxStyleName
= wxEmptyString
;
12499 m_position
.Reset();
12510 bool wxTextBoxAttr::operator== (const wxTextBoxAttr
& attr
) const
12513 m_flags
== attr
.m_flags
&&
12514 m_floatMode
== attr
.m_floatMode
&&
12515 m_clearMode
== attr
.m_clearMode
&&
12516 m_collapseMode
== attr
.m_collapseMode
&&
12517 m_verticalAlignment
== attr
.m_verticalAlignment
&&
12519 m_margins
== attr
.m_margins
&&
12520 m_padding
== attr
.m_padding
&&
12521 m_position
== attr
.m_position
&&
12523 m_size
== attr
.m_size
&&
12524 m_minSize
== attr
.m_minSize
&&
12525 m_maxSize
== attr
.m_maxSize
&&
12527 m_border
== attr
.m_border
&&
12528 m_outline
== attr
.m_outline
&&
12530 m_boxStyleName
== attr
.m_boxStyleName
12534 // Partial equality test
12535 bool wxTextBoxAttr::EqPartial(const wxTextBoxAttr
& attr
, bool weakTest
) const
12538 ((!HasFloatMode() && attr
.HasFloatMode()) ||
12539 (!HasClearMode() && attr
.HasClearMode()) ||
12540 (!HasCollapseBorders() && attr
.HasCollapseBorders()) ||
12541 (!HasVerticalAlignment() && attr
.HasVerticalAlignment()) ||
12542 (!HasBoxStyleName() && attr
.HasBoxStyleName())))
12546 if (attr
.HasFloatMode() && HasFloatMode() && (GetFloatMode() != attr
.GetFloatMode()))
12549 if (attr
.HasClearMode() && HasClearMode() && (GetClearMode() != attr
.GetClearMode()))
12552 if (attr
.HasCollapseBorders() && HasCollapseBorders() && (attr
.GetCollapseBorders() != GetCollapseBorders()))
12555 if (attr
.HasVerticalAlignment() && HasVerticalAlignment() && (attr
.GetVerticalAlignment() != GetVerticalAlignment()))
12558 if (attr
.HasBoxStyleName() && HasBoxStyleName() && (attr
.GetBoxStyleName() != GetBoxStyleName()))
12563 if (!m_position
.EqPartial(attr
.m_position
, weakTest
))
12568 if (!m_size
.EqPartial(attr
.m_size
, weakTest
))
12570 if (!m_minSize
.EqPartial(attr
.m_minSize
, weakTest
))
12572 if (!m_maxSize
.EqPartial(attr
.m_maxSize
, weakTest
))
12577 if (!m_margins
.EqPartial(attr
.m_margins
, weakTest
))
12582 if (!m_padding
.EqPartial(attr
.m_padding
, weakTest
))
12587 if (!GetBorder().EqPartial(attr
.GetBorder(), weakTest
))
12592 if (!GetOutline().EqPartial(attr
.GetOutline(), weakTest
))
12598 // Merges the given attributes. If compareWith
12599 // is non-NULL, then it will be used to mask out those attributes that are the same in style
12600 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
12601 bool wxTextBoxAttr::Apply(const wxTextBoxAttr
& attr
, const wxTextBoxAttr
* compareWith
)
12603 if (attr
.HasFloatMode())
12605 if (!(compareWith
&& compareWith
->HasFloatMode() && compareWith
->GetFloatMode() == attr
.GetFloatMode()))
12606 SetFloatMode(attr
.GetFloatMode());
12609 if (attr
.HasClearMode())
12611 if (!(compareWith
&& compareWith
->HasClearMode() && compareWith
->GetClearMode() == attr
.GetClearMode()))
12612 SetClearMode(attr
.GetClearMode());
12615 if (attr
.HasCollapseBorders())
12617 if (!(compareWith
&& compareWith
->HasCollapseBorders() && compareWith
->GetCollapseBorders() == attr
.GetCollapseBorders()))
12618 SetCollapseBorders(attr
.GetCollapseBorders());
12621 if (attr
.HasVerticalAlignment())
12623 if (!(compareWith
&& compareWith
->HasVerticalAlignment() && compareWith
->GetVerticalAlignment() == attr
.GetVerticalAlignment()))
12624 SetVerticalAlignment(attr
.GetVerticalAlignment());
12627 if (attr
.HasBoxStyleName())
12629 if (!(compareWith
&& compareWith
->HasBoxStyleName() && compareWith
->GetBoxStyleName() == attr
.GetBoxStyleName()))
12630 SetBoxStyleName(attr
.GetBoxStyleName());
12633 m_margins
.Apply(attr
.m_margins
, compareWith
? (& attr
.m_margins
) : (const wxTextAttrDimensions
*) NULL
);
12634 m_padding
.Apply(attr
.m_padding
, compareWith
? (& attr
.m_padding
) : (const wxTextAttrDimensions
*) NULL
);
12635 m_position
.Apply(attr
.m_position
, compareWith
? (& attr
.m_position
) : (const wxTextAttrDimensions
*) NULL
);
12637 m_size
.Apply(attr
.m_size
, compareWith
? (& attr
.m_size
) : (const wxTextAttrSize
*) NULL
);
12638 m_minSize
.Apply(attr
.m_minSize
, compareWith
? (& attr
.m_minSize
) : (const wxTextAttrSize
*) NULL
);
12639 m_maxSize
.Apply(attr
.m_maxSize
, compareWith
? (& attr
.m_maxSize
) : (const wxTextAttrSize
*) NULL
);
12641 m_border
.Apply(attr
.m_border
, compareWith
? (& attr
.m_border
) : (const wxTextAttrBorders
*) NULL
);
12642 m_outline
.Apply(attr
.m_outline
, compareWith
? (& attr
.m_outline
) : (const wxTextAttrBorders
*) NULL
);
12647 // Remove specified attributes from this object
12648 bool wxTextBoxAttr::RemoveStyle(const wxTextBoxAttr
& attr
)
12650 if (attr
.HasFloatMode())
12651 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT
);
12653 if (attr
.HasClearMode())
12654 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR
);
12656 if (attr
.HasCollapseBorders())
12657 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
12659 if (attr
.HasVerticalAlignment())
12660 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
12662 if (attr
.HasBoxStyleName())
12664 SetBoxStyleName(wxEmptyString
);
12665 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
12668 m_margins
.RemoveStyle(attr
.m_margins
);
12669 m_padding
.RemoveStyle(attr
.m_padding
);
12670 m_position
.RemoveStyle(attr
.m_position
);
12672 m_size
.RemoveStyle(attr
.m_size
);
12673 m_minSize
.RemoveStyle(attr
.m_minSize
);
12674 m_maxSize
.RemoveStyle(attr
.m_maxSize
);
12676 m_border
.RemoveStyle(attr
.m_border
);
12677 m_outline
.RemoveStyle(attr
.m_outline
);
12682 // Collects the attributes that are common to a range of content, building up a note of
12683 // which attributes are absent in some objects and which clash in some objects.
12684 void wxTextBoxAttr::CollectCommonAttributes(const wxTextBoxAttr
& attr
, wxTextBoxAttr
& clashingAttr
, wxTextBoxAttr
& absentAttr
)
12686 if (attr
.HasFloatMode())
12688 if (!clashingAttr
.HasFloatMode() && !absentAttr
.HasFloatMode())
12690 if (HasFloatMode())
12692 if (GetFloatMode() != attr
.GetFloatMode())
12694 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_FLOAT
);
12695 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT
);
12699 SetFloatMode(attr
.GetFloatMode());
12703 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_FLOAT
);
12705 if (attr
.HasClearMode())
12707 if (!clashingAttr
.HasClearMode() && !absentAttr
.HasClearMode())
12709 if (HasClearMode())
12711 if (GetClearMode() != attr
.GetClearMode())
12713 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_CLEAR
);
12714 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR
);
12718 SetClearMode(attr
.GetClearMode());
12722 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_CLEAR
);
12724 if (attr
.HasCollapseBorders())
12726 if (!clashingAttr
.HasCollapseBorders() && !absentAttr
.HasCollapseBorders())
12728 if (HasCollapseBorders())
12730 if (GetCollapseBorders() != attr
.GetCollapseBorders())
12732 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
12733 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
12737 SetCollapseBorders(attr
.GetCollapseBorders());
12741 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
12743 if (attr
.HasVerticalAlignment())
12745 if (!clashingAttr
.HasVerticalAlignment() && !absentAttr
.HasVerticalAlignment())
12747 if (HasVerticalAlignment())
12749 if (GetVerticalAlignment() != attr
.GetVerticalAlignment())
12751 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
12752 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
12756 SetVerticalAlignment(attr
.GetVerticalAlignment());
12760 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
12762 if (attr
.HasBoxStyleName())
12764 if (!clashingAttr
.HasBoxStyleName() && !absentAttr
.HasBoxStyleName())
12766 if (HasBoxStyleName())
12768 if (GetBoxStyleName() != attr
.GetBoxStyleName())
12770 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
12771 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
12775 SetBoxStyleName(attr
.GetBoxStyleName());
12779 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
12781 m_margins
.CollectCommonAttributes(attr
.m_margins
, clashingAttr
.m_margins
, absentAttr
.m_margins
);
12782 m_padding
.CollectCommonAttributes(attr
.m_padding
, clashingAttr
.m_padding
, absentAttr
.m_padding
);
12783 m_position
.CollectCommonAttributes(attr
.m_position
, clashingAttr
.m_position
, absentAttr
.m_position
);
12785 m_size
.CollectCommonAttributes(attr
.m_size
, clashingAttr
.m_size
, absentAttr
.m_size
);
12786 m_minSize
.CollectCommonAttributes(attr
.m_minSize
, clashingAttr
.m_minSize
, absentAttr
.m_minSize
);
12787 m_maxSize
.CollectCommonAttributes(attr
.m_maxSize
, clashingAttr
.m_maxSize
, absentAttr
.m_maxSize
);
12789 m_border
.CollectCommonAttributes(attr
.m_border
, clashingAttr
.m_border
, absentAttr
.m_border
);
12790 m_outline
.CollectCommonAttributes(attr
.m_outline
, clashingAttr
.m_outline
, absentAttr
.m_outline
);
12793 bool wxTextBoxAttr::IsDefault() const
12795 return GetFlags() == 0 && !m_border
.IsValid() && !m_outline
.IsValid() &&
12796 !m_size
.IsValid() && !m_minSize
.IsValid() && !m_maxSize
.IsValid() &&
12797 !m_position
.IsValid() && !m_padding
.IsValid() && !m_margins
.IsValid();
12802 void wxRichTextAttr::Copy(const wxRichTextAttr
& attr
)
12804 wxTextAttr::Copy(attr
);
12806 m_textBoxAttr
= attr
.m_textBoxAttr
;
12809 bool wxRichTextAttr::operator==(const wxRichTextAttr
& attr
) const
12811 if (!(wxTextAttr::operator==(attr
)))
12814 return (m_textBoxAttr
== attr
.m_textBoxAttr
);
12817 // Partial equality test
12818 bool wxRichTextAttr::EqPartial(const wxRichTextAttr
& attr
, bool weakTest
) const
12820 if (!(wxTextAttr::EqPartial(attr
, weakTest
)))
12823 return m_textBoxAttr
.EqPartial(attr
.m_textBoxAttr
, weakTest
);
12826 // Merges the given attributes. If compareWith
12827 // is non-NULL, then it will be used to mask out those attributes that are the same in style
12828 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
12829 bool wxRichTextAttr::Apply(const wxRichTextAttr
& style
, const wxRichTextAttr
* compareWith
)
12831 wxTextAttr::Apply(style
, compareWith
);
12833 return m_textBoxAttr
.Apply(style
.m_textBoxAttr
, compareWith
? (& compareWith
->m_textBoxAttr
) : (const wxTextBoxAttr
*) NULL
);
12836 // Remove specified attributes from this object
12837 bool wxRichTextAttr::RemoveStyle(const wxRichTextAttr
& attr
)
12839 wxTextAttr::RemoveStyle(*this, attr
);
12841 return m_textBoxAttr
.RemoveStyle(attr
.m_textBoxAttr
);
12844 // Collects the attributes that are common to a range of content, building up a note of
12845 // which attributes are absent in some objects and which clash in some objects.
12846 void wxRichTextAttr::CollectCommonAttributes(const wxRichTextAttr
& attr
, wxRichTextAttr
& clashingAttr
, wxRichTextAttr
& absentAttr
)
12848 wxTextAttrCollectCommonAttributes(*this, attr
, clashingAttr
, absentAttr
);
12850 m_textBoxAttr
.CollectCommonAttributes(attr
.m_textBoxAttr
, clashingAttr
.m_textBoxAttr
, absentAttr
.m_textBoxAttr
);
12853 // Partial equality test
12854 bool wxTextAttrBorder::EqPartial(const wxTextAttrBorder
& border
, bool weakTest
) const
12857 ((!HasStyle() && border
.HasStyle()) ||
12858 (!HasColour() && border
.HasColour()) ||
12859 (!HasWidth() && border
.HasWidth())))
12864 if (border
.HasStyle() && HasStyle() && (border
.GetStyle() != GetStyle()))
12867 if (border
.HasColour() && HasColour() && (border
.GetColourLong() != GetColourLong()))
12870 if (border
.HasWidth() && HasWidth() && !(border
.GetWidth() == GetWidth()))
12876 // Apply border to 'this', but not if the same as compareWith
12877 bool wxTextAttrBorder::Apply(const wxTextAttrBorder
& border
, const wxTextAttrBorder
* compareWith
)
12879 if (border
.HasStyle())
12881 if (!(compareWith
&& (border
.GetStyle() == compareWith
->GetStyle())))
12882 SetStyle(border
.GetStyle());
12884 if (border
.HasColour())
12886 if (!(compareWith
&& (border
.GetColourLong() == compareWith
->GetColourLong())))
12887 SetColour(border
.GetColourLong());
12889 if (border
.HasWidth())
12891 if (!(compareWith
&& (border
.GetWidth() == compareWith
->GetWidth())))
12892 SetWidth(border
.GetWidth());
12898 // Remove specified attributes from this object
12899 bool wxTextAttrBorder::RemoveStyle(const wxTextAttrBorder
& attr
)
12901 if (attr
.HasStyle() && HasStyle())
12902 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_STYLE
);
12903 if (attr
.HasColour() && HasColour())
12904 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_COLOUR
);
12905 if (attr
.HasWidth() && HasWidth())
12906 m_borderWidth
.Reset();
12911 // Collects the attributes that are common to a range of content, building up a note of
12912 // which attributes are absent in some objects and which clash in some objects.
12913 void wxTextAttrBorder::CollectCommonAttributes(const wxTextAttrBorder
& attr
, wxTextAttrBorder
& clashingAttr
, wxTextAttrBorder
& absentAttr
)
12915 if (attr
.HasStyle())
12917 if (!clashingAttr
.HasStyle() && !absentAttr
.HasStyle())
12921 if (GetStyle() != attr
.GetStyle())
12923 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
12924 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
12928 SetStyle(attr
.GetStyle());
12932 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
12934 if (attr
.HasColour())
12936 if (!clashingAttr
.HasColour() && !absentAttr
.HasColour())
12940 if (GetColour() != attr
.GetColour())
12942 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
12943 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
12947 SetColour(attr
.GetColourLong());
12951 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
12953 m_borderWidth
.CollectCommonAttributes(attr
.m_borderWidth
, clashingAttr
.m_borderWidth
, absentAttr
.m_borderWidth
);
12956 // Partial equality test
12957 bool wxTextAttrBorders::EqPartial(const wxTextAttrBorders
& borders
, bool weakTest
) const
12959 return m_left
.EqPartial(borders
.m_left
, weakTest
) && m_right
.EqPartial(borders
.m_right
, weakTest
) &&
12960 m_top
.EqPartial(borders
.m_top
, weakTest
) && m_bottom
.EqPartial(borders
.m_bottom
, weakTest
);
12963 // Apply border to 'this', but not if the same as compareWith
12964 bool wxTextAttrBorders::Apply(const wxTextAttrBorders
& borders
, const wxTextAttrBorders
* compareWith
)
12966 m_left
.Apply(borders
.m_left
, compareWith
? (& compareWith
->m_left
) : (const wxTextAttrBorder
*) NULL
);
12967 m_right
.Apply(borders
.m_right
, compareWith
? (& compareWith
->m_right
) : (const wxTextAttrBorder
*) NULL
);
12968 m_top
.Apply(borders
.m_top
, compareWith
? (& compareWith
->m_top
) : (const wxTextAttrBorder
*) NULL
);
12969 m_bottom
.Apply(borders
.m_bottom
, compareWith
? (& compareWith
->m_bottom
) : (const wxTextAttrBorder
*) NULL
);
12973 // Remove specified attributes from this object
12974 bool wxTextAttrBorders::RemoveStyle(const wxTextAttrBorders
& attr
)
12976 m_left
.RemoveStyle(attr
.m_left
);
12977 m_right
.RemoveStyle(attr
.m_right
);
12978 m_top
.RemoveStyle(attr
.m_top
);
12979 m_bottom
.RemoveStyle(attr
.m_bottom
);
12983 // Collects the attributes that are common to a range of content, building up a note of
12984 // which attributes are absent in some objects and which clash in some objects.
12985 void wxTextAttrBorders::CollectCommonAttributes(const wxTextAttrBorders
& attr
, wxTextAttrBorders
& clashingAttr
, wxTextAttrBorders
& absentAttr
)
12987 m_left
.CollectCommonAttributes(attr
.m_left
, clashingAttr
.m_left
, absentAttr
.m_left
);
12988 m_right
.CollectCommonAttributes(attr
.m_right
, clashingAttr
.m_right
, absentAttr
.m_right
);
12989 m_top
.CollectCommonAttributes(attr
.m_top
, clashingAttr
.m_top
, absentAttr
.m_top
);
12990 m_bottom
.CollectCommonAttributes(attr
.m_bottom
, clashingAttr
.m_bottom
, absentAttr
.m_bottom
);
12993 // Set style of all borders
12994 void wxTextAttrBorders::SetStyle(int style
)
12996 m_left
.SetStyle(style
);
12997 m_right
.SetStyle(style
);
12998 m_top
.SetStyle(style
);
12999 m_bottom
.SetStyle(style
);
13002 // Set colour of all borders
13003 void wxTextAttrBorders::SetColour(unsigned long colour
)
13005 m_left
.SetColour(colour
);
13006 m_right
.SetColour(colour
);
13007 m_top
.SetColour(colour
);
13008 m_bottom
.SetColour(colour
);
13011 void wxTextAttrBorders::SetColour(const wxColour
& colour
)
13013 m_left
.SetColour(colour
);
13014 m_right
.SetColour(colour
);
13015 m_top
.SetColour(colour
);
13016 m_bottom
.SetColour(colour
);
13019 // Set width of all borders
13020 void wxTextAttrBorders::SetWidth(const wxTextAttrDimension
& width
)
13022 m_left
.SetWidth(width
);
13023 m_right
.SetWidth(width
);
13024 m_top
.SetWidth(width
);
13025 m_bottom
.SetWidth(width
);
13028 // Partial equality test
13029 bool wxTextAttrDimension::EqPartial(const wxTextAttrDimension
& dim
, bool weakTest
) const
13031 if (!weakTest
&& !IsValid() && dim
.IsValid())
13034 if (dim
.IsValid() && IsValid() && !((*this) == dim
))
13040 bool wxTextAttrDimension::Apply(const wxTextAttrDimension
& dim
, const wxTextAttrDimension
* compareWith
)
13044 if (!(compareWith
&& dim
== (*compareWith
)))
13051 // Collects the attributes that are common to a range of content, building up a note of
13052 // which attributes are absent in some objects and which clash in some objects.
13053 void wxTextAttrDimension::CollectCommonAttributes(const wxTextAttrDimension
& attr
, wxTextAttrDimension
& clashingAttr
, wxTextAttrDimension
& absentAttr
)
13055 if (attr
.IsValid())
13057 if (!clashingAttr
.IsValid() && !absentAttr
.IsValid())
13061 if (!((*this) == attr
))
13063 clashingAttr
.SetValid(true);
13072 absentAttr
.SetValid(true);
13075 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(wxDC
& dc
, double scale
, const wxSize
& parentSize
)
13077 m_ppi
= dc
.GetPPI().x
; m_scale
= scale
; m_parentSize
= parentSize
;
13080 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(int ppi
, double scale
, const wxSize
& parentSize
)
13082 m_ppi
= ppi
; m_scale
= scale
; m_parentSize
= parentSize
;
13085 int wxTextAttrDimensionConverter::ConvertTenthsMMToPixels(int units
) const
13087 return wxRichTextObject::ConvertTenthsMMToPixels(m_ppi
, units
, m_scale
);
13090 int wxTextAttrDimensionConverter::ConvertPixelsToTenthsMM(int pixels
) const
13092 return wxRichTextObject::ConvertPixelsToTenthsMM(m_ppi
, pixels
, m_scale
);
13095 int wxTextAttrDimensionConverter::GetPixels(const wxTextAttrDimension
& dim
, int direction
) const
13097 if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
13098 return ConvertTenthsMMToPixels(dim
.GetValue());
13099 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
13100 return dim
.GetValue();
13101 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
13103 wxASSERT(m_parentSize
!= wxDefaultSize
);
13104 if (direction
== wxHORIZONTAL
)
13105 return (int) (double(m_parentSize
.x
) * double(dim
.GetValue()) / 100.0);
13107 return (int) (double(m_parentSize
.y
) * double(dim
.GetValue()) / 100.0);
13116 int wxTextAttrDimensionConverter::GetTenthsMM(const wxTextAttrDimension
& dim
) const
13118 if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
13119 return dim
.GetValue();
13120 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
13121 return ConvertPixelsToTenthsMM(dim
.GetValue());
13129 // Partial equality test
13130 bool wxTextAttrDimensions::EqPartial(const wxTextAttrDimensions
& dims
, bool weakTest
) const
13132 if (!m_left
.EqPartial(dims
.m_left
, weakTest
))
13135 if (!m_right
.EqPartial(dims
.m_right
, weakTest
))
13138 if (!m_top
.EqPartial(dims
.m_top
, weakTest
))
13141 if (!m_bottom
.EqPartial(dims
.m_bottom
, weakTest
))
13147 // Apply border to 'this', but not if the same as compareWith
13148 bool wxTextAttrDimensions::Apply(const wxTextAttrDimensions
& dims
, const wxTextAttrDimensions
* compareWith
)
13150 m_left
.Apply(dims
.m_left
, compareWith
? (& compareWith
->m_left
) : (const wxTextAttrDimension
*) NULL
);
13151 m_right
.Apply(dims
.m_right
, compareWith
? (& compareWith
->m_right
): (const wxTextAttrDimension
*) NULL
);
13152 m_top
.Apply(dims
.m_top
, compareWith
? (& compareWith
->m_top
): (const wxTextAttrDimension
*) NULL
);
13153 m_bottom
.Apply(dims
.m_bottom
, compareWith
? (& compareWith
->m_bottom
): (const wxTextAttrDimension
*) NULL
);
13158 // Remove specified attributes from this object
13159 bool wxTextAttrDimensions::RemoveStyle(const wxTextAttrDimensions
& attr
)
13161 if (attr
.m_left
.IsValid())
13163 if (attr
.m_right
.IsValid())
13165 if (attr
.m_top
.IsValid())
13167 if (attr
.m_bottom
.IsValid())
13173 // Collects the attributes that are common to a range of content, building up a note of
13174 // which attributes are absent in some objects and which clash in some objects.
13175 void wxTextAttrDimensions::CollectCommonAttributes(const wxTextAttrDimensions
& attr
, wxTextAttrDimensions
& clashingAttr
, wxTextAttrDimensions
& absentAttr
)
13177 m_left
.CollectCommonAttributes(attr
.m_left
, clashingAttr
.m_left
, absentAttr
.m_left
);
13178 m_right
.CollectCommonAttributes(attr
.m_right
, clashingAttr
.m_right
, absentAttr
.m_right
);
13179 m_top
.CollectCommonAttributes(attr
.m_top
, clashingAttr
.m_top
, absentAttr
.m_top
);
13180 m_bottom
.CollectCommonAttributes(attr
.m_bottom
, clashingAttr
.m_bottom
, absentAttr
.m_bottom
);
13183 // Partial equality test
13184 bool wxTextAttrSize::EqPartial(const wxTextAttrSize
& size
, bool weakTest
) const
13186 if (!m_width
.EqPartial(size
.m_width
, weakTest
))
13189 if (!m_height
.EqPartial(size
.m_height
, weakTest
))
13195 // Apply border to 'this', but not if the same as compareWith
13196 bool wxTextAttrSize::Apply(const wxTextAttrSize
& size
, const wxTextAttrSize
* compareWith
)
13198 m_width
.Apply(size
.m_width
, compareWith
? (& compareWith
->m_width
) : (const wxTextAttrDimension
*) NULL
);
13199 m_height
.Apply(size
.m_height
, compareWith
? (& compareWith
->m_height
): (const wxTextAttrDimension
*) NULL
);
13204 // Remove specified attributes from this object
13205 bool wxTextAttrSize::RemoveStyle(const wxTextAttrSize
& attr
)
13207 if (attr
.m_width
.IsValid())
13209 if (attr
.m_height
.IsValid())
13215 // Collects the attributes that are common to a range of content, building up a note of
13216 // which attributes are absent in some objects and which clash in some objects.
13217 void wxTextAttrSize::CollectCommonAttributes(const wxTextAttrSize
& attr
, wxTextAttrSize
& clashingAttr
, wxTextAttrSize
& absentAttr
)
13219 m_width
.CollectCommonAttributes(attr
.m_width
, clashingAttr
.m_width
, absentAttr
.m_width
);
13220 m_height
.CollectCommonAttributes(attr
.m_height
, clashingAttr
.m_height
, absentAttr
.m_height
);
13223 // Collects the attributes that are common to a range of content, building up a note of
13224 // which attributes are absent in some objects and which clash in some objects.
13225 void wxTextAttrCollectCommonAttributes(wxTextAttr
& currentStyle
, const wxTextAttr
& attr
, wxTextAttr
& clashingAttr
, wxTextAttr
& absentAttr
)
13227 absentAttr
.SetFlags(absentAttr
.GetFlags() | (~attr
.GetFlags() & wxTEXT_ATTR_ALL
));
13228 absentAttr
.SetTextEffectFlags(absentAttr
.GetTextEffectFlags() | (~attr
.GetTextEffectFlags() & 0xFFFF));
13230 long forbiddenFlags
= clashingAttr
.GetFlags()|absentAttr
.GetFlags();
13232 // If different font size units are being used, this is a clash.
13233 if (((attr
.GetFlags() & wxTEXT_ATTR_FONT_SIZE
) | (currentStyle
.GetFlags() & wxTEXT_ATTR_FONT_SIZE
)) == wxTEXT_ATTR_FONT_SIZE
)
13235 currentStyle
.SetFontSize(0);
13236 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_SIZE
);
13237 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_SIZE
);
13241 if (attr
.HasFontPointSize() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_POINT_SIZE
))
13243 if (currentStyle
.HasFontPointSize())
13245 if (currentStyle
.GetFontSize() != attr
.GetFontSize())
13247 // Clash of attr - mark as such
13248 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE
);
13249 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE
);
13253 currentStyle
.SetFontSize(attr
.GetFontSize());
13255 else if (!attr
.HasFontPointSize() && currentStyle
.HasFontPointSize())
13257 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE
);
13258 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE
);
13261 if (attr
.HasFontPixelSize() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_PIXEL_SIZE
))
13263 if (currentStyle
.HasFontPixelSize())
13265 if (currentStyle
.GetFontSize() != attr
.GetFontSize())
13267 // Clash of attr - mark as such
13268 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE
);
13269 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE
);
13273 currentStyle
.SetFontPixelSize(attr
.GetFontSize());
13275 else if (!attr
.HasFontPixelSize() && currentStyle
.HasFontPixelSize())
13277 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE
);
13278 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE
);
13282 if (attr
.HasFontItalic() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_ITALIC
))
13284 if (currentStyle
.HasFontItalic())
13286 if (currentStyle
.GetFontStyle() != attr
.GetFontStyle())
13288 // Clash of attr - mark as such
13289 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_ITALIC
);
13290 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC
);
13294 currentStyle
.SetFontStyle(attr
.GetFontStyle());
13296 else if (!attr
.HasFontItalic() && currentStyle
.HasFontItalic())
13298 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_ITALIC
);
13299 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC
);
13302 if (attr
.HasFontFamily() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_FAMILY
))
13304 if (currentStyle
.HasFontFamily())
13306 if (currentStyle
.GetFontFamily() != attr
.GetFontFamily())
13308 // Clash of attr - mark as such
13309 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FAMILY
);
13310 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY
);
13314 currentStyle
.SetFontFamily(attr
.GetFontFamily());
13316 else if (!attr
.HasFontFamily() && currentStyle
.HasFontFamily())
13318 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FAMILY
);
13319 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY
);
13322 if (attr
.HasFontWeight() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_WEIGHT
))
13324 if (currentStyle
.HasFontWeight())
13326 if (currentStyle
.GetFontWeight() != attr
.GetFontWeight())
13328 // Clash of attr - mark as such
13329 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_WEIGHT
);
13330 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT
);
13334 currentStyle
.SetFontWeight(attr
.GetFontWeight());
13336 else if (!attr
.HasFontWeight() && currentStyle
.HasFontWeight())
13338 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_WEIGHT
);
13339 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT
);
13342 if (attr
.HasFontFaceName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_FACE
))
13344 if (currentStyle
.HasFontFaceName())
13346 wxString
faceName1(currentStyle
.GetFontFaceName());
13347 wxString
faceName2(attr
.GetFontFaceName());
13349 if (faceName1
!= faceName2
)
13351 // Clash of attr - mark as such
13352 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FACE
);
13353 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FACE
);
13357 currentStyle
.SetFontFaceName(attr
.GetFontFaceName());
13359 else if (!attr
.HasFontFaceName() && currentStyle
.HasFontFaceName())
13361 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FACE
);
13362 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FACE
);
13365 if (attr
.HasFontUnderlined() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_UNDERLINE
))
13367 if (currentStyle
.HasFontUnderlined())
13369 if (currentStyle
.GetFontUnderlined() != attr
.GetFontUnderlined())
13371 // Clash of attr - mark as such
13372 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
13373 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
13377 currentStyle
.SetFontUnderlined(attr
.GetFontUnderlined());
13379 else if (!attr
.HasFontUnderlined() && currentStyle
.HasFontUnderlined())
13381 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
13382 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
13385 if (attr
.HasFontStrikethrough() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_STRIKETHROUGH
))
13387 if (currentStyle
.HasFontStrikethrough())
13389 if (currentStyle
.GetFontStrikethrough() != attr
.GetFontStrikethrough())
13391 // Clash of attr - mark as such
13392 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH
);
13393 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH
);
13397 currentStyle
.SetFontStrikethrough(attr
.GetFontStrikethrough());
13399 else if (!attr
.HasFontStrikethrough() && currentStyle
.HasFontStrikethrough())
13401 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH
);
13402 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH
);
13405 if (attr
.HasTextColour() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_TEXT_COLOUR
))
13407 if (currentStyle
.HasTextColour())
13409 if (currentStyle
.GetTextColour() != attr
.GetTextColour())
13411 // Clash of attr - mark as such
13412 clashingAttr
.AddFlag(wxTEXT_ATTR_TEXT_COLOUR
);
13413 currentStyle
.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR
);
13417 currentStyle
.SetTextColour(attr
.GetTextColour());
13419 else if (!attr
.HasTextColour() && currentStyle
.HasTextColour())
13421 clashingAttr
.AddFlag(wxTEXT_ATTR_TEXT_COLOUR
);
13422 currentStyle
.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR
);
13425 if (attr
.HasBackgroundColour() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BACKGROUND_COLOUR
))
13427 if (currentStyle
.HasBackgroundColour())
13429 if (currentStyle
.GetBackgroundColour() != attr
.GetBackgroundColour())
13431 // Clash of attr - mark as such
13432 clashingAttr
.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
13433 currentStyle
.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
13437 currentStyle
.SetBackgroundColour(attr
.GetBackgroundColour());
13439 else if (!attr
.HasBackgroundColour() && currentStyle
.HasBackgroundColour())
13441 clashingAttr
.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
13442 currentStyle
.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
13445 if (attr
.HasAlignment() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_ALIGNMENT
))
13447 if (currentStyle
.HasAlignment())
13449 if (currentStyle
.GetAlignment() != attr
.GetAlignment())
13451 // Clash of attr - mark as such
13452 clashingAttr
.AddFlag(wxTEXT_ATTR_ALIGNMENT
);
13453 currentStyle
.RemoveFlag(wxTEXT_ATTR_ALIGNMENT
);
13457 currentStyle
.SetAlignment(attr
.GetAlignment());
13459 else if (!attr
.HasAlignment() && currentStyle
.HasAlignment())
13461 clashingAttr
.AddFlag(wxTEXT_ATTR_ALIGNMENT
);
13462 currentStyle
.RemoveFlag(wxTEXT_ATTR_ALIGNMENT
);
13465 if (attr
.HasTabs() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_TABS
))
13467 if (currentStyle
.HasTabs())
13469 if (!wxRichTextTabsEq(currentStyle
.GetTabs(), attr
.GetTabs()))
13471 // Clash of attr - mark as such
13472 clashingAttr
.AddFlag(wxTEXT_ATTR_TABS
);
13473 currentStyle
.RemoveFlag(wxTEXT_ATTR_TABS
);
13477 currentStyle
.SetTabs(attr
.GetTabs());
13479 else if (!attr
.HasTabs() && currentStyle
.HasTabs())
13481 clashingAttr
.AddFlag(wxTEXT_ATTR_TABS
);
13482 currentStyle
.RemoveFlag(wxTEXT_ATTR_TABS
);
13485 if (attr
.HasLeftIndent() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LEFT_INDENT
))
13487 if (currentStyle
.HasLeftIndent())
13489 if (currentStyle
.GetLeftIndent() != attr
.GetLeftIndent() || currentStyle
.GetLeftSubIndent() != attr
.GetLeftSubIndent())
13491 // Clash of attr - mark as such
13492 clashingAttr
.AddFlag(wxTEXT_ATTR_LEFT_INDENT
);
13493 currentStyle
.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT
);
13497 currentStyle
.SetLeftIndent(attr
.GetLeftIndent(), attr
.GetLeftSubIndent());
13499 else if (!attr
.HasLeftIndent() && currentStyle
.HasLeftIndent())
13501 clashingAttr
.AddFlag(wxTEXT_ATTR_LEFT_INDENT
);
13502 currentStyle
.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT
);
13505 if (attr
.HasRightIndent() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_RIGHT_INDENT
))
13507 if (currentStyle
.HasRightIndent())
13509 if (currentStyle
.GetRightIndent() != attr
.GetRightIndent())
13511 // Clash of attr - mark as such
13512 clashingAttr
.AddFlag(wxTEXT_ATTR_RIGHT_INDENT
);
13513 currentStyle
.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT
);
13517 currentStyle
.SetRightIndent(attr
.GetRightIndent());
13519 else if (!attr
.HasRightIndent() && currentStyle
.HasRightIndent())
13521 clashingAttr
.AddFlag(wxTEXT_ATTR_RIGHT_INDENT
);
13522 currentStyle
.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT
);
13525 if (attr
.HasParagraphSpacingAfter() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARA_SPACING_AFTER
))
13527 if (currentStyle
.HasParagraphSpacingAfter())
13529 if (currentStyle
.GetParagraphSpacingAfter() != attr
.GetParagraphSpacingAfter())
13531 // Clash of attr - mark as such
13532 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
13533 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
13537 currentStyle
.SetParagraphSpacingAfter(attr
.GetParagraphSpacingAfter());
13539 else if (!attr
.HasParagraphSpacingAfter() && currentStyle
.HasParagraphSpacingAfter())
13541 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
13542 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
13545 if (attr
.HasParagraphSpacingBefore() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARA_SPACING_BEFORE
))
13547 if (currentStyle
.HasParagraphSpacingBefore())
13549 if (currentStyle
.GetParagraphSpacingBefore() != attr
.GetParagraphSpacingBefore())
13551 // Clash of attr - mark as such
13552 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
13553 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
13557 currentStyle
.SetParagraphSpacingBefore(attr
.GetParagraphSpacingBefore());
13559 else if (!attr
.HasParagraphSpacingBefore() && currentStyle
.HasParagraphSpacingBefore())
13561 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
13562 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
13565 if (attr
.HasLineSpacing() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LINE_SPACING
))
13567 if (currentStyle
.HasLineSpacing())
13569 if (currentStyle
.GetLineSpacing() != attr
.GetLineSpacing())
13571 // Clash of attr - mark as such
13572 clashingAttr
.AddFlag(wxTEXT_ATTR_LINE_SPACING
);
13573 currentStyle
.RemoveFlag(wxTEXT_ATTR_LINE_SPACING
);
13577 currentStyle
.SetLineSpacing(attr
.GetLineSpacing());
13579 else if (!attr
.HasLineSpacing() && currentStyle
.HasLineSpacing())
13581 clashingAttr
.AddFlag(wxTEXT_ATTR_LINE_SPACING
);
13582 currentStyle
.RemoveFlag(wxTEXT_ATTR_LINE_SPACING
);
13585 if (attr
.HasCharacterStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_CHARACTER_STYLE_NAME
))
13587 if (currentStyle
.HasCharacterStyleName())
13589 if (currentStyle
.GetCharacterStyleName() != attr
.GetCharacterStyleName())
13591 // Clash of attr - mark as such
13592 clashingAttr
.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
13593 currentStyle
.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
13597 currentStyle
.SetCharacterStyleName(attr
.GetCharacterStyleName());
13599 else if (!attr
.HasCharacterStyleName() && currentStyle
.HasCharacterStyleName())
13601 clashingAttr
.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
13602 currentStyle
.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
13605 if (attr
.HasParagraphStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
))
13607 if (currentStyle
.HasParagraphStyleName())
13609 if (currentStyle
.GetParagraphStyleName() != attr
.GetParagraphStyleName())
13611 // Clash of attr - mark as such
13612 clashingAttr
.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
13613 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
13617 currentStyle
.SetParagraphStyleName(attr
.GetParagraphStyleName());
13619 else if (!attr
.HasParagraphStyleName() && currentStyle
.HasParagraphStyleName())
13621 clashingAttr
.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
13622 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
13625 if (attr
.HasListStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LIST_STYLE_NAME
))
13627 if (currentStyle
.HasListStyleName())
13629 if (currentStyle
.GetListStyleName() != attr
.GetListStyleName())
13631 // Clash of attr - mark as such
13632 clashingAttr
.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
13633 currentStyle
.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
13637 currentStyle
.SetListStyleName(attr
.GetListStyleName());
13639 else if (!attr
.HasListStyleName() && currentStyle
.HasListStyleName())
13641 clashingAttr
.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
13642 currentStyle
.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
13645 if (attr
.HasBulletStyle() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_STYLE
))
13647 if (currentStyle
.HasBulletStyle())
13649 if (currentStyle
.GetBulletStyle() != attr
.GetBulletStyle())
13651 // Clash of attr - mark as such
13652 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_STYLE
);
13653 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE
);
13657 currentStyle
.SetBulletStyle(attr
.GetBulletStyle());
13659 else if (!attr
.HasBulletStyle() && currentStyle
.HasBulletStyle())
13661 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_STYLE
);
13662 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE
);
13665 if (attr
.HasBulletNumber() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_NUMBER
))
13667 if (currentStyle
.HasBulletNumber())
13669 if (currentStyle
.GetBulletNumber() != attr
.GetBulletNumber())
13671 // Clash of attr - mark as such
13672 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NUMBER
);
13673 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER
);
13677 currentStyle
.SetBulletNumber(attr
.GetBulletNumber());
13679 else if (!attr
.HasBulletNumber() && currentStyle
.HasBulletNumber())
13681 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NUMBER
);
13682 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER
);
13685 if (attr
.HasBulletText() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_TEXT
))
13687 if (currentStyle
.HasBulletText())
13689 if (currentStyle
.GetBulletText() != attr
.GetBulletText())
13691 // Clash of attr - mark as such
13692 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_TEXT
);
13693 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT
);
13698 currentStyle
.SetBulletText(attr
.GetBulletText());
13699 currentStyle
.SetBulletFont(attr
.GetBulletFont());
13702 else if (!attr
.HasBulletText() && currentStyle
.HasBulletText())
13704 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_TEXT
);
13705 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT
);
13708 if (attr
.HasBulletName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_NAME
))
13710 if (currentStyle
.HasBulletName())
13712 if (currentStyle
.GetBulletName() != attr
.GetBulletName())
13714 // Clash of attr - mark as such
13715 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NAME
);
13716 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NAME
);
13721 currentStyle
.SetBulletName(attr
.GetBulletName());
13724 else if (!attr
.HasBulletName() && currentStyle
.HasBulletName())
13726 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NAME
);
13727 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NAME
);
13730 if (attr
.HasURL() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_URL
))
13732 if (currentStyle
.HasURL())
13734 if (currentStyle
.GetURL() != attr
.GetURL())
13736 // Clash of attr - mark as such
13737 clashingAttr
.AddFlag(wxTEXT_ATTR_URL
);
13738 currentStyle
.RemoveFlag(wxTEXT_ATTR_URL
);
13743 currentStyle
.SetURL(attr
.GetURL());
13746 else if (!attr
.HasURL() && currentStyle
.HasURL())
13748 clashingAttr
.AddFlag(wxTEXT_ATTR_URL
);
13749 currentStyle
.RemoveFlag(wxTEXT_ATTR_URL
);
13752 if (attr
.HasTextEffects() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_EFFECTS
))
13754 if (currentStyle
.HasTextEffects())
13756 // We need to find the bits in the new attr that are different:
13757 // just look at those bits that are specified by the new attr.
13759 // We need to remove the bits and flags that are not common between current attr
13760 // and new attr. In so doing we need to take account of the styles absent from one or more of the
13761 // previous styles.
13763 int currentRelevantTextEffects
= currentStyle
.GetTextEffects() & attr
.GetTextEffectFlags();
13764 int newRelevantTextEffects
= attr
.GetTextEffects() & attr
.GetTextEffectFlags();
13766 if (currentRelevantTextEffects
!= newRelevantTextEffects
)
13768 // Find the text effects that were different, using XOR
13769 int differentEffects
= currentRelevantTextEffects
^ newRelevantTextEffects
;
13771 // Clash of attr - mark as such
13772 clashingAttr
.SetTextEffectFlags(clashingAttr
.GetTextEffectFlags() | differentEffects
);
13773 currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~differentEffects
);
13778 currentStyle
.SetTextEffects(attr
.GetTextEffects());
13779 currentStyle
.SetTextEffectFlags(attr
.GetTextEffectFlags());
13782 // Mask out the flags and values that cannot be common because they were absent in one or more objecrs
13783 // that we've looked at so far
13784 currentStyle
.SetTextEffects(currentStyle
.GetTextEffects() & ~absentAttr
.GetTextEffectFlags());
13785 currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~absentAttr
.GetTextEffectFlags());
13787 if (currentStyle
.GetTextEffectFlags() == 0)
13788 currentStyle
.RemoveFlag(wxTEXT_ATTR_EFFECTS
);
13790 else if (!attr
.HasTextEffects() && currentStyle
.HasTextEffects())
13792 clashingAttr
.AddFlag(wxTEXT_ATTR_EFFECTS
);
13793 currentStyle
.RemoveFlag(wxTEXT_ATTR_EFFECTS
);
13796 if (attr
.HasOutlineLevel() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_OUTLINE_LEVEL
))
13798 if (currentStyle
.HasOutlineLevel())
13800 if (currentStyle
.GetOutlineLevel() != attr
.GetOutlineLevel())
13802 // Clash of attr - mark as such
13803 clashingAttr
.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
13804 currentStyle
.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
13808 currentStyle
.SetOutlineLevel(attr
.GetOutlineLevel());
13810 else if (!attr
.HasOutlineLevel() && currentStyle
.HasOutlineLevel())
13812 clashingAttr
.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
13813 currentStyle
.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
13817 WX_DEFINE_OBJARRAY(wxRichTextVariantArray
);
13820 WX_DEFINE_OBJARRAY(wxRichTextAttrArray
);
13822 IMPLEMENT_DYNAMIC_CLASS(wxRichTextProperties
, wxObject
)
13824 bool wxRichTextProperties::operator==(const wxRichTextProperties
& props
) const
13826 if (m_properties
.GetCount() != props
.GetCount())
13830 for (i
= 0; i
< m_properties
.GetCount(); i
++)
13832 const wxVariant
& var1
= m_properties
[i
];
13833 int idx
= props
.Find(var1
.GetName());
13836 const wxVariant
& var2
= props
.m_properties
[idx
];
13837 if (!(var1
== var2
))
13844 wxArrayString
wxRichTextProperties::GetPropertyNames() const
13848 for (i
= 0; i
< m_properties
.GetCount(); i
++)
13850 arr
.Add(m_properties
[i
].GetName());
13855 int wxRichTextProperties::Find(const wxString
& name
) const
13858 for (i
= 0; i
< m_properties
.GetCount(); i
++)
13860 if (m_properties
[i
].GetName() == name
)
13866 bool wxRichTextProperties::Remove(const wxString
& name
)
13868 int idx
= Find(name
);
13871 m_properties
.RemoveAt(idx
);
13878 wxVariant
* wxRichTextProperties::FindOrCreateProperty(const wxString
& name
)
13880 int idx
= Find(name
);
13881 if (idx
== wxNOT_FOUND
)
13882 SetProperty(name
, wxString());
13884 if (idx
!= wxNOT_FOUND
)
13886 return & (*this)[idx
];
13892 const wxVariant
& wxRichTextProperties::GetProperty(const wxString
& name
) const
13894 static const wxVariant nullVariant
;
13895 int idx
= Find(name
);
13897 return m_properties
[idx
];
13899 return nullVariant
;
13902 wxString
wxRichTextProperties::GetPropertyString(const wxString
& name
) const
13904 return GetProperty(name
).GetString();
13907 long wxRichTextProperties::GetPropertyLong(const wxString
& name
) const
13909 return GetProperty(name
).GetLong();
13912 bool wxRichTextProperties::GetPropertyBool(const wxString
& name
) const
13914 return GetProperty(name
).GetBool();
13917 double wxRichTextProperties::GetPropertyDouble(const wxString
& name
) const
13919 return GetProperty(name
).GetDouble();
13922 void wxRichTextProperties::SetProperty(const wxVariant
& variant
)
13924 wxASSERT(!variant
.GetName().IsEmpty());
13926 int idx
= Find(variant
.GetName());
13929 m_properties
.Add(variant
);
13931 m_properties
[idx
] = variant
;
13934 void wxRichTextProperties::SetProperty(const wxString
& name
, const wxVariant
& variant
)
13936 int idx
= Find(name
);
13937 wxVariant
var(variant
);
13941 m_properties
.Add(var
);
13943 m_properties
[idx
] = var
;
13946 void wxRichTextProperties::SetProperty(const wxString
& name
, const wxString
& value
)
13948 SetProperty(name
, wxVariant(value
, name
));
13951 void wxRichTextProperties::SetProperty(const wxString
& name
, long value
)
13953 SetProperty(name
, wxVariant(value
, name
));
13956 void wxRichTextProperties::SetProperty(const wxString
& name
, double value
)
13958 SetProperty(name
, wxVariant(value
, name
));
13961 void wxRichTextProperties::SetProperty(const wxString
& name
, bool value
)
13963 SetProperty(name
, wxVariant(value
, name
));
13966 void wxRichTextProperties::RemoveProperties(const wxRichTextProperties
& properties
)
13969 for (i
= 0; i
< properties
.GetCount(); i
++)
13971 wxString name
= properties
.GetProperties()[i
].GetName();
13972 if (HasProperty(name
))
13977 void wxRichTextProperties::MergeProperties(const wxRichTextProperties
& properties
)
13980 for (i
= 0; i
< properties
.GetCount(); i
++)
13982 SetProperty(properties
.GetProperties()[i
]);
13986 wxRichTextObject
* wxRichTextObjectAddress::GetObject(wxRichTextParagraphLayoutBox
* topLevelContainer
) const
13988 if (m_address
.GetCount() == 0)
13989 return topLevelContainer
;
13991 wxRichTextCompositeObject
* p
= topLevelContainer
;
13993 while (p
&& i
< m_address
.GetCount())
13995 int pos
= m_address
[i
];
13996 wxASSERT(pos
>= 0 && pos
< (int) p
->GetChildren().GetCount());
13997 if (pos
< 0 || pos
>= (int) p
->GetChildren().GetCount())
14000 wxRichTextObject
* p1
= p
->GetChild(pos
);
14001 if (i
== (m_address
.GetCount()-1))
14004 p
= wxDynamicCast(p1
, wxRichTextCompositeObject
);
14010 bool wxRichTextObjectAddress::Create(wxRichTextParagraphLayoutBox
* topLevelContainer
, wxRichTextObject
* obj
)
14014 if (topLevelContainer
== obj
)
14017 wxRichTextObject
* o
= obj
;
14020 wxRichTextCompositeObject
* p
= wxDynamicCast(o
->GetParent(), wxRichTextCompositeObject
);
14024 int pos
= p
->GetChildren().IndexOf(o
);
14028 m_address
.Insert(pos
, 0);
14030 if (p
== topLevelContainer
)
14039 bool wxRichTextSelection::operator==(const wxRichTextSelection
& sel
) const
14041 if (m_container
!= sel
.m_container
)
14043 if (m_ranges
.GetCount() != sel
.m_ranges
.GetCount())
14046 for (i
= 0; i
< m_ranges
.GetCount(); i
++)
14047 if (!(m_ranges
[i
] == sel
.m_ranges
[i
]))
14052 // Get the selections appropriate to the specified object, if any; returns wxRICHTEXT_NO_SELECTION if none
14053 // or none at the level of the object's container.
14054 wxRichTextRangeArray
wxRichTextSelection::GetSelectionForObject(wxRichTextObject
* obj
) const
14058 wxRichTextParagraphLayoutBox
* container
= obj
->GetParentContainer();
14060 if (container
== m_container
)
14063 container
= obj
->GetContainer();
14066 if (container
->GetParent())
14068 // If we found that our object's container is within the range of
14069 // a selection higher up, then assume the whole original object
14070 // is also selected.
14071 wxRichTextParagraphLayoutBox
* parentContainer
= container
->GetParentContainer();
14072 if (parentContainer
== m_container
)
14074 if (WithinSelection(container
->GetRange().GetStart(), m_ranges
))
14076 wxRichTextRangeArray ranges
;
14077 ranges
.Add(obj
->GetRange());
14082 container
= parentContainer
;
14091 return wxRichTextRangeArray();
14094 // Is the given position within the selection?
14095 bool wxRichTextSelection::WithinSelection(long pos
, wxRichTextObject
* obj
) const
14101 wxRichTextRangeArray selectionRanges
= GetSelectionForObject(obj
);
14102 return WithinSelection(pos
, selectionRanges
);
14106 // Is the given position within the selection range?
14107 bool wxRichTextSelection::WithinSelection(long pos
, const wxRichTextRangeArray
& ranges
)
14110 for (i
= 0; i
< ranges
.GetCount(); i
++)
14112 const wxRichTextRange
& range
= ranges
[i
];
14113 if (pos
>= range
.GetStart() && pos
<= range
.GetEnd())
14119 // Is the given range completely within the selection range?
14120 bool wxRichTextSelection::WithinSelection(const wxRichTextRange
& range
, const wxRichTextRangeArray
& ranges
)
14123 for (i
= 0; i
< ranges
.GetCount(); i
++)
14125 const wxRichTextRange
& eachRange
= ranges
[i
];
14126 if (range
.IsWithin(eachRange
))
14132 IMPLEMENT_CLASS(wxRichTextDrawingHandler
, wxObject
)
14133 IMPLEMENT_CLASS(wxRichTextDrawingContext
, wxObject
)
14135 wxRichTextDrawingContext::wxRichTextDrawingContext(wxRichTextBuffer
* buffer
)
14139 if (m_buffer
&& m_buffer
->GetRichTextCtrl())
14140 EnableVirtualAttributes(m_buffer
->GetRichTextCtrl()->GetVirtualAttributesEnabled());
14143 bool wxRichTextDrawingContext::HasVirtualAttributes(wxRichTextObject
* obj
) const
14145 if (!GetVirtualAttributesEnabled())
14148 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
14151 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
14152 if (handler
->HasVirtualAttributes(obj
))
14155 node
= node
->GetNext();
14160 wxRichTextAttr
wxRichTextDrawingContext::GetVirtualAttributes(wxRichTextObject
* obj
) const
14162 wxRichTextAttr attr
;
14163 if (!GetVirtualAttributesEnabled())
14166 // We apply all handlers, so we can may combine several different attributes
14167 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
14170 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
14171 if (handler
->HasVirtualAttributes(obj
))
14173 bool success
= handler
->GetVirtualAttributes(attr
, obj
);
14175 wxUnusedVar(success
);
14178 node
= node
->GetNext();
14183 bool wxRichTextDrawingContext::ApplyVirtualAttributes(wxRichTextAttr
& attr
, wxRichTextObject
* obj
) const
14185 if (!GetVirtualAttributesEnabled())
14188 if (HasVirtualAttributes(obj
))
14190 wxRichTextAttr
a(GetVirtualAttributes(obj
));
14198 int wxRichTextDrawingContext::GetVirtualSubobjectAttributesCount(wxRichTextObject
* obj
) const
14200 if (!GetVirtualAttributesEnabled())
14203 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
14206 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
14207 int count
= handler
->GetVirtualSubobjectAttributesCount(obj
);
14211 node
= node
->GetNext();
14216 int wxRichTextDrawingContext::GetVirtualSubobjectAttributes(wxRichTextObject
* obj
, wxArrayInt
& positions
, wxRichTextAttrArray
& attributes
) const
14218 if (!GetVirtualAttributesEnabled())
14221 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
14224 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
14225 if (handler
->GetVirtualSubobjectAttributes(obj
, positions
, attributes
))
14226 return positions
.GetCount();
14228 node
= node
->GetNext();
14233 bool wxRichTextDrawingContext::HasVirtualText(const wxRichTextPlainText
* obj
) const
14235 if (!GetVirtualAttributesEnabled())
14238 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
14241 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
14242 if (handler
->HasVirtualText(obj
))
14245 node
= node
->GetNext();
14250 bool wxRichTextDrawingContext::GetVirtualText(const wxRichTextPlainText
* obj
, wxString
& text
) const
14252 if (!GetVirtualAttributesEnabled())
14255 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
14258 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
14259 if (handler
->GetVirtualText(obj
, text
))
14262 node
= node
->GetNext();
14267 /// Adds a handler to the end
14268 void wxRichTextBuffer::AddDrawingHandler(wxRichTextDrawingHandler
*handler
)
14270 sm_drawingHandlers
.Append(handler
);
14273 /// Inserts a handler at the front
14274 void wxRichTextBuffer::InsertDrawingHandler(wxRichTextDrawingHandler
*handler
)
14276 sm_drawingHandlers
.Insert( handler
);
14279 /// Removes a handler
14280 bool wxRichTextBuffer::RemoveDrawingHandler(const wxString
& name
)
14282 wxRichTextDrawingHandler
*handler
= FindDrawingHandler(name
);
14285 sm_drawingHandlers
.DeleteObject(handler
);
14293 wxRichTextDrawingHandler
* wxRichTextBuffer::FindDrawingHandler(const wxString
& name
)
14295 wxList::compatibility_iterator node
= sm_drawingHandlers
.GetFirst();
14298 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
14299 if (handler
->GetName().Lower() == name
.Lower()) return handler
;
14301 node
= node
->GetNext();
14306 void wxRichTextBuffer::CleanUpDrawingHandlers()
14308 wxList::compatibility_iterator node
= sm_drawingHandlers
.GetFirst();
14311 wxRichTextDrawingHandler
* handler
= (wxRichTextDrawingHandler
*)node
->GetData();
14312 wxList::compatibility_iterator next
= node
->GetNext();
14317 sm_drawingHandlers
.Clear();
14320 void wxRichTextBuffer::AddFieldType(wxRichTextFieldType
*fieldType
)
14322 sm_fieldTypes
[fieldType
->GetName()] = fieldType
;
14325 bool wxRichTextBuffer::RemoveFieldType(const wxString
& name
)
14327 wxRichTextFieldTypeHashMap::iterator it
= sm_fieldTypes
.find(name
);
14328 if (it
== sm_fieldTypes
.end())
14332 wxRichTextFieldType
* fieldType
= it
->second
;
14333 sm_fieldTypes
.erase(it
);
14339 wxRichTextFieldType
*wxRichTextBuffer::FindFieldType(const wxString
& name
)
14341 wxRichTextFieldTypeHashMap::iterator it
= sm_fieldTypes
.find(name
);
14342 if (it
== sm_fieldTypes
.end())
14348 void wxRichTextBuffer::CleanUpFieldTypes()
14350 wxRichTextFieldTypeHashMap::iterator it
;
14351 for( it
= sm_fieldTypes
.begin(); it
!= sm_fieldTypes
.end(); ++it
)
14353 wxRichTextFieldType
* fieldType
= it
->second
;
14357 sm_fieldTypes
.clear();