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
.GetTop().GetColour());
811 // If pen width is > 1, resorts to a solid rectangle.
812 if (borderBottom
== 1)
814 int penStyle
= wxSOLID
;
815 if (attr
.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
817 else if (attr
.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
818 penStyle
= wxLONG_DASH
;
819 wxPen
pen(col
, 1, penStyle
);
821 dc
.DrawLine(rect
.x
, rect
.y
+ rect
.height
, rect
.x
+ rect
.width
, rect
.y
+ rect
.height
);
824 else if (borderBottom
> 1)
830 dc
.DrawRectangle(rect
.x
, rect
.y
+ rect
.height
- borderBottom
, rect
.width
, borderBottom
);
837 // Get the various rectangles of the box model in pixels. You can either specify contentRect (inner)
838 // or marginRect (outer), and the other must be the default rectangle (no width or height).
839 // Note that the outline doesn't affect the position of the rectangle, it's drawn in whatever space
842 // | Margin | Border | Padding | CONTENT | Padding | Border | Margin |
844 bool wxRichTextObject::GetBoxRects(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& attr
, wxRect
& marginRect
, wxRect
& borderRect
, wxRect
& contentRect
, wxRect
& paddingRect
, wxRect
& outlineRect
)
846 int borderLeft
= 0, borderRight
= 0, borderTop
= 0, borderBottom
= 0;
847 int outlineLeft
= 0, outlineRight
= 0, outlineTop
= 0, outlineBottom
= 0;
848 int paddingLeft
= 0, paddingRight
= 0, paddingTop
= 0, paddingBottom
= 0;
849 int marginLeft
= 0, marginRight
= 0, marginTop
= 0, marginBottom
= 0;
851 wxTextAttrDimensionConverter
converter(dc
, buffer
? buffer
->GetScale() : 1.0);
853 if (attr
.GetTextBoxAttr().GetMargins().GetLeft().IsValid())
854 marginLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetLeft());
855 if (attr
.GetTextBoxAttr().GetMargins().GetRight().IsValid())
856 marginRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetRight());
857 if (attr
.GetTextBoxAttr().GetMargins().GetTop().IsValid())
858 marginTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetTop());
859 if (attr
.GetTextBoxAttr().GetMargins().GetBottom().IsValid())
860 marginBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetBottom());
862 if (attr
.GetTextBoxAttr().GetBorder().GetLeft().GetWidth().IsValid())
863 borderLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetLeft().GetWidth());
864 if (attr
.GetTextBoxAttr().GetBorder().GetRight().GetWidth().IsValid())
865 borderRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetRight().GetWidth());
866 if (attr
.GetTextBoxAttr().GetBorder().GetTop().GetWidth().IsValid())
867 borderTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetTop().GetWidth());
868 if (attr
.GetTextBoxAttr().GetBorder().GetBottom().GetWidth().IsValid())
869 borderBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetBottom().GetWidth());
871 if (attr
.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
872 paddingLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetLeft());
873 if (attr
.GetTextBoxAttr().GetPadding().GetRight().IsValid())
874 paddingRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetRight());
875 if (attr
.GetTextBoxAttr().GetPadding().GetTop().IsValid())
876 paddingTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetTop());
877 if (attr
.GetTextBoxAttr().GetPadding().GetBottom().IsValid())
878 paddingBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetBottom());
880 if (attr
.GetTextBoxAttr().GetOutline().GetLeft().GetWidth().IsValid())
881 outlineLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetLeft().GetWidth());
882 if (attr
.GetTextBoxAttr().GetOutline().GetRight().GetWidth().IsValid())
883 outlineRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetRight().GetWidth());
884 if (attr
.GetTextBoxAttr().GetOutline().GetTop().GetWidth().IsValid())
885 outlineTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetTop().GetWidth());
886 if (attr
.GetTextBoxAttr().GetOutline().GetBottom().GetWidth().IsValid())
887 outlineBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetBottom().GetWidth());
889 int leftTotal
= marginLeft
+ borderLeft
+ paddingLeft
;
890 int rightTotal
= marginRight
+ borderRight
+ paddingRight
;
891 int topTotal
= marginTop
+ borderTop
+ paddingTop
;
892 int bottomTotal
= marginBottom
+ borderBottom
+ paddingBottom
;
894 if (marginRect
!= wxRect())
896 contentRect
.x
= marginRect
.x
+ leftTotal
;
897 contentRect
.y
= marginRect
.y
+ topTotal
;
898 contentRect
.width
= marginRect
.width
- (leftTotal
+ rightTotal
);
899 contentRect
.height
= marginRect
.height
- (topTotal
+ bottomTotal
);
903 marginRect
.x
= contentRect
.x
- leftTotal
;
904 marginRect
.y
= contentRect
.y
- topTotal
;
905 marginRect
.width
= contentRect
.width
+ (leftTotal
+ rightTotal
);
906 marginRect
.height
= contentRect
.height
+ (topTotal
+ bottomTotal
);
909 borderRect
.x
= marginRect
.x
+ marginLeft
;
910 borderRect
.y
= marginRect
.y
+ marginTop
;
911 borderRect
.width
= marginRect
.width
- (marginLeft
+ marginRight
);
912 borderRect
.height
= marginRect
.height
- (marginTop
+ marginBottom
);
914 paddingRect
.x
= marginRect
.x
+ marginLeft
+ borderLeft
;
915 paddingRect
.y
= marginRect
.y
+ marginTop
+ borderTop
;
916 paddingRect
.width
= marginRect
.width
- (marginLeft
+ marginRight
+ borderLeft
+ borderRight
);
917 paddingRect
.height
= marginRect
.height
- (marginTop
+ marginBottom
+ borderTop
+ borderBottom
);
919 // The outline is outside the margin and doesn't influence the overall box position or content size.
920 outlineRect
.x
= marginRect
.x
- outlineLeft
;
921 outlineRect
.y
= marginRect
.y
- outlineTop
;
922 outlineRect
.width
= marginRect
.width
+ (outlineLeft
+ outlineRight
);
923 outlineRect
.height
= marginRect
.height
+ (outlineTop
+ outlineBottom
);
928 // Get the total margin for the object in pixels, taking into account margin, padding and border size
929 bool wxRichTextObject::GetTotalMargin(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& attr
, int& leftMargin
, int& rightMargin
,
930 int& topMargin
, int& bottomMargin
)
932 // Assume boxRect is the area around the content
933 wxRect contentRect
, marginRect
, borderRect
, paddingRect
, outlineRect
;
934 marginRect
= wxRect(0, 0, 1000, 1000);
936 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
938 leftMargin
= contentRect
.GetLeft() - marginRect
.GetLeft();
939 rightMargin
= marginRect
.GetRight() - contentRect
.GetRight();
940 topMargin
= contentRect
.GetTop() - marginRect
.GetTop();
941 bottomMargin
= marginRect
.GetBottom() - contentRect
.GetBottom();
946 // Returns the rectangle which the child has available to it given restrictions specified in the
947 // child attribute, e.g. 50% width of the parent, 400 pixels, x position 20% of the parent, etc.
948 // availableContainerSpace might be a parent that the cell has to compute its width relative to.
949 // E.g. a cell that's 50% of its parent.
950 wxRect
wxRichTextObject::AdjustAvailableSpace(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& WXUNUSED(parentAttr
), const wxRichTextAttr
& childAttr
, const wxRect
& availableParentSpace
, const wxRect
& availableContainerSpace
)
952 wxRect rect
= availableParentSpace
;
955 scale
= buffer
->GetScale();
957 wxTextAttrDimensionConverter
converter(dc
, scale
, availableContainerSpace
.GetSize());
959 if (childAttr
.GetTextBoxAttr().GetWidth().IsValid())
960 rect
.width
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetWidth());
962 if (childAttr
.GetTextBoxAttr().GetHeight().IsValid())
963 rect
.height
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetHeight());
965 // Can specify either left or right for the position (we're assuming we can't
966 // set the left and right edges to effectively set the size. Would we want to do that?)
967 if (childAttr
.GetTextBoxAttr().GetPosition().GetLeft().IsValid())
969 rect
.x
= rect
.x
+ converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetLeft());
971 else if (childAttr
.GetTextBoxAttr().GetPosition().GetRight().IsValid())
973 int x
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetRight());
974 if (childAttr
.GetTextBoxAttr().GetPosition().GetRight().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE
)
975 rect
.x
= availableContainerSpace
.x
+ availableContainerSpace
.width
- rect
.width
;
980 if (childAttr
.GetTextBoxAttr().GetPosition().GetTop().IsValid())
982 rect
.y
= rect
.y
+ converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetTop());
984 else if (childAttr
.GetTextBoxAttr().GetPosition().GetBottom().IsValid())
986 int y
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetBottom());
987 if (childAttr
.GetTextBoxAttr().GetPosition().GetBottom().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE
)
988 rect
.y
= availableContainerSpace
.y
+ availableContainerSpace
.height
- rect
.height
;
993 if (rect
.GetWidth() > availableParentSpace
.GetWidth())
994 rect
.SetWidth(availableParentSpace
.GetWidth());
999 // Dump to output stream for debugging
1000 void wxRichTextObject::Dump(wxTextOutputStream
& stream
)
1002 stream
<< GetClassInfo()->GetClassName() << wxT("\n");
1003 stream
<< wxString::Format(wxT("Size: %d,%d. Position: %d,%d, Range: %ld,%ld"), m_size
.x
, m_size
.y
, m_pos
.x
, m_pos
.y
, m_range
.GetStart(), m_range
.GetEnd()) << wxT("\n");
1004 stream
<< wxString::Format(wxT("Text colour: %d,%d,%d."), (int) m_attributes
.GetTextColour().Red(), (int) m_attributes
.GetTextColour().Green(), (int) m_attributes
.GetTextColour().Blue()) << wxT("\n");
1007 // Gets the containing buffer
1008 wxRichTextBuffer
* wxRichTextObject::GetBuffer() const
1010 const wxRichTextObject
* obj
= this;
1011 while (obj
&& !wxDynamicCast(obj
, wxRichTextBuffer
))
1012 obj
= obj
->GetParent();
1013 return wxDynamicCast(obj
, wxRichTextBuffer
);
1016 // Get the absolute object position, by traversing up the child/parent hierarchy
1017 wxPoint
wxRichTextObject::GetAbsolutePosition() const
1019 wxPoint pt
= GetPosition();
1021 wxRichTextObject
* p
= GetParent();
1024 pt
= pt
+ p
->GetPosition();
1031 // Hit-testing: returns a flag indicating hit test details, plus
1032 // information about position
1033 int wxRichTextObject::HitTest(wxDC
& WXUNUSED(dc
), wxRichTextDrawingContext
& WXUNUSED(context
), const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int WXUNUSED(flags
))
1036 return wxRICHTEXT_HITTEST_NONE
;
1038 wxRect rect
= GetRect();
1039 if (pt
.x
>= rect
.x
&& pt
.x
< rect
.x
+ rect
.width
&&
1040 pt
.y
>= rect
.y
&& pt
.y
< rect
.y
+ rect
.height
)
1043 *contextObj
= GetParentContainer();
1044 textPosition
= GetRange().GetStart();
1045 return wxRICHTEXT_HITTEST_ON
;
1048 return wxRICHTEXT_HITTEST_NONE
;
1051 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1052 // lays out the object again using the maximum ('best') size
1053 bool wxRichTextObject::LayoutToBestSize(wxDC
& dc
, wxRichTextDrawingContext
& context
, wxRichTextBuffer
* buffer
,
1054 const wxRichTextAttr
& parentAttr
, const wxRichTextAttr
& attr
,
1055 const wxRect
& availableParentSpace
, const wxRect
& availableContainerSpace
,
1058 wxRect availableChildRect
= AdjustAvailableSpace(dc
, buffer
, parentAttr
, attr
, availableParentSpace
, availableContainerSpace
);
1059 wxRect originalAvailableRect
= availableChildRect
;
1060 Layout(dc
, context
, availableChildRect
, availableContainerSpace
, style
);
1062 wxSize maxSize
= GetMaxSize();
1064 // Don't ignore if maxSize.x is zero, since we need to redo the paragraph's lines
1066 if (!attr
.GetTextBoxAttr().GetWidth().IsValid() && maxSize
.x
< availableChildRect
.width
)
1068 // Redo the layout with a fixed, minimum size this time.
1069 Invalidate(wxRICHTEXT_ALL
);
1070 wxRichTextAttr
newAttr(attr
);
1071 newAttr
.GetTextBoxAttr().GetWidth().SetValue(maxSize
.x
, wxTEXT_ATTR_UNITS_PIXELS
);
1072 newAttr
.GetTextBoxAttr().GetWidth().SetPosition(wxTEXT_BOX_ATTR_POSITION_ABSOLUTE
);
1074 availableChildRect
= AdjustAvailableSpace(dc
, buffer
, parentAttr
, newAttr
, availableParentSpace
, availableContainerSpace
);
1076 // If a paragraph, align the whole paragraph.
1077 // Problem with this: if we're limited by a floating object, a line may be centered
1078 // w.r.t. the smaller resulting box rather than the actual available width.
1079 // FIXME: aligning whole paragraph not compatible with floating objects
1080 if (attr
.HasAlignment() && (!wxRichTextBuffer::GetFloatingLayoutMode() || (GetContainer()->GetFloatCollector() && !GetContainer()->GetFloatCollector()->HasFloats())))
1082 // centering, right-justification
1083 if (attr
.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE
)
1085 availableChildRect
.x
= (originalAvailableRect
.GetWidth() - availableChildRect
.GetWidth())/2 + availableChildRect
.x
;
1087 else if (attr
.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT
)
1089 availableChildRect
.x
= availableChildRect
.x
+ originalAvailableRect
.GetWidth() - availableChildRect
.GetWidth();
1093 Layout(dc
, context
, availableChildRect
, availableContainerSpace
, style
);
1107 // Move the object recursively, by adding the offset from old to new
1108 void wxRichTextObject::Move(const wxPoint
& pt
)
1115 * wxRichTextCompositeObject
1116 * This is the base for drawable objects.
1119 IMPLEMENT_CLASS(wxRichTextCompositeObject
, wxRichTextObject
)
1121 wxRichTextCompositeObject::wxRichTextCompositeObject(wxRichTextObject
* parent
):
1122 wxRichTextObject(parent
)
1126 wxRichTextCompositeObject::~wxRichTextCompositeObject()
1131 /// Get the nth child
1132 wxRichTextObject
* wxRichTextCompositeObject::GetChild(size_t n
) const
1134 wxASSERT ( n
< m_children
.GetCount() );
1136 return m_children
.Item(n
)->GetData();
1139 /// Append a child, returning the position
1140 size_t wxRichTextCompositeObject::AppendChild(wxRichTextObject
* child
)
1142 m_children
.Append(child
);
1143 child
->SetParent(this);
1144 return m_children
.GetCount() - 1;
1147 /// Insert the child in front of the given object, or at the beginning
1148 bool wxRichTextCompositeObject::InsertChild(wxRichTextObject
* child
, wxRichTextObject
* inFrontOf
)
1152 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(inFrontOf
);
1153 m_children
.Insert(node
, child
);
1156 m_children
.Insert(child
);
1157 child
->SetParent(this);
1162 /// Delete the child
1163 bool wxRichTextCompositeObject::RemoveChild(wxRichTextObject
* child
, bool deleteChild
)
1165 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(child
);
1168 wxRichTextObject
* obj
= node
->GetData();
1169 m_children
.Erase(node
);
1178 /// Delete all children
1179 bool wxRichTextCompositeObject::DeleteChildren()
1181 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1184 wxRichTextObjectList::compatibility_iterator oldNode
= node
;
1186 wxRichTextObject
* child
= node
->GetData();
1187 child
->Dereference(); // Only delete if reference count is zero
1189 node
= node
->GetNext();
1190 m_children
.Erase(oldNode
);
1196 /// Get the child count
1197 size_t wxRichTextCompositeObject::GetChildCount() const
1199 return m_children
.GetCount();
1203 void wxRichTextCompositeObject::Copy(const wxRichTextCompositeObject
& obj
)
1205 wxRichTextObject::Copy(obj
);
1209 wxRichTextObjectList::compatibility_iterator node
= obj
.m_children
.GetFirst();
1212 wxRichTextObject
* child
= node
->GetData();
1213 wxRichTextObject
* newChild
= child
->Clone();
1214 newChild
->SetParent(this);
1215 m_children
.Append(newChild
);
1217 node
= node
->GetNext();
1221 /// Hit-testing: returns a flag indicating hit test details, plus
1222 /// information about position
1223 int wxRichTextCompositeObject::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
1226 return wxRICHTEXT_HITTEST_NONE
;
1228 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1231 wxRichTextObject
* child
= node
->GetData();
1233 if (child
->IsShown() && child
->IsTopLevel() && (flags
& wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS
))
1235 // Just check if we hit the overall object
1236 int ret
= child
->wxRichTextObject::HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
1237 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
1240 else if (child
->IsShown())
1242 int ret
= child
->HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
1243 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
1247 node
= node
->GetNext();
1250 return wxRICHTEXT_HITTEST_NONE
;
1253 /// Finds the absolute position and row height for the given character position
1254 bool wxRichTextCompositeObject::FindPosition(wxDC
& dc
, wxRichTextDrawingContext
& context
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
1256 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1259 wxRichTextObject
* child
= node
->GetData();
1261 // Don't recurse if the child is a top-level object,
1262 // such as a text box, because the character position will no longer
1263 // apply. By definition, a top-level object has its own range of
1264 // character positions.
1265 if (!child
->IsTopLevel() && child
->FindPosition(dc
, context
, index
, pt
, height
, forceLineStart
))
1268 node
= node
->GetNext();
1275 void wxRichTextCompositeObject::CalculateRange(long start
, long& end
)
1277 long current
= start
;
1278 long lastEnd
= current
;
1286 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1289 wxRichTextObject
* child
= node
->GetData();
1292 child
->CalculateRange(current
, childEnd
);
1295 current
= childEnd
+ 1;
1297 node
= node
->GetNext();
1302 // A top-level object always has a range of size 1,
1303 // because its children don't count at this level.
1305 m_range
.SetRange(start
, start
);
1307 // An object with no children has zero length
1308 if (m_children
.GetCount() == 0)
1310 m_ownRange
.SetRange(0, lastEnd
);
1316 // An object with no children has zero length
1317 if (m_children
.GetCount() == 0)
1320 m_range
.SetRange(start
, end
);
1324 /// Delete range from layout.
1325 bool wxRichTextCompositeObject::DeleteRange(const wxRichTextRange
& range
)
1327 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1331 wxRichTextObject
* obj
= (wxRichTextObject
*) node
->GetData();
1332 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
1334 // Delete the range in each paragraph
1336 // When a chunk has been deleted, internally the content does not
1337 // now match the ranges.
1338 // However, so long as deletion is not done on the same object twice this is OK.
1339 // If you may delete content from the same object twice, recalculate
1340 // the ranges inbetween DeleteRange calls by calling CalculateRanges, and
1341 // adjust the range you're deleting accordingly.
1343 if (!obj
->GetRange().IsOutside(range
))
1345 // No need to delete within a top-level object; just removing this object will do fine
1346 if (!obj
->IsTopLevel())
1347 obj
->DeleteRange(range
);
1349 // Delete an empty object, or paragraph within this range.
1350 if (obj
->IsEmpty() ||
1351 (range
.GetStart() <= obj
->GetRange().GetStart() && range
.GetEnd() >= obj
->GetRange().GetEnd()))
1353 // An empty paragraph has length 1, so won't be deleted unless the
1354 // whole range is deleted.
1355 RemoveChild(obj
, true);
1365 /// Get any text in this object for the given range
1366 wxString
wxRichTextCompositeObject::GetTextForRange(const wxRichTextRange
& range
) const
1369 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1372 wxRichTextObject
* child
= node
->GetData();
1373 wxRichTextRange childRange
= range
;
1374 if (!child
->GetRange().IsOutside(range
))
1376 childRange
.LimitTo(child
->GetRange());
1378 wxString childText
= child
->GetTextForRange(childRange
);
1382 node
= node
->GetNext();
1388 /// Get the child object at the given character position
1389 wxRichTextObject
* wxRichTextCompositeObject::GetChildAtPosition(long pos
) const
1391 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1394 wxRichTextObject
* child
= node
->GetData();
1395 if (child
->GetRange().GetStart() == pos
)
1397 node
= node
->GetNext();
1402 /// Recursively merge all pieces that can be merged.
1403 bool wxRichTextCompositeObject::Defragment(wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
)
1405 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1408 wxRichTextObject
* child
= node
->GetData();
1409 if (range
== wxRICHTEXT_ALL
|| !child
->GetRange().IsOutside(range
))
1411 wxRichTextCompositeObject
* composite
= wxDynamicCast(child
, wxRichTextCompositeObject
);
1413 composite
->Defragment(context
);
1415 // Optimization: if there are no virtual attributes, we won't need to
1416 // to split objects in order to paint individually attributed chunks.
1417 // So only merge in this case.
1418 if (!context
.GetVirtualAttributesEnabled())
1420 if (node
->GetNext())
1422 wxRichTextObject
* nextChild
= node
->GetNext()->GetData();
1423 if (child
->CanMerge(nextChild
, context
) && child
->Merge(nextChild
, context
))
1425 nextChild
->Dereference();
1426 m_children
.Erase(node
->GetNext());
1429 node
= node
->GetNext();
1432 node
= node
->GetNext();
1436 // If we might have virtual attributes, we first see if we have to split
1437 // objects so that they may be painted with potential virtual attributes,
1438 // since text objects can only draw or measure with a single attributes object
1440 wxRichTextObject
* childAfterSplit
= child
;
1441 if (child
->CanSplit(context
))
1443 childAfterSplit
= child
->Split(context
);
1444 node
= m_children
.Find(childAfterSplit
);
1447 if (node
->GetNext())
1449 wxRichTextObject
* nextChild
= node
->GetNext()->GetData();
1450 wxRichTextObjectList::compatibility_iterator nextNode
= node
->GetNext();
1452 // First split child and nextChild so we have smaller fragments to merge.
1453 // Then Merge only has to test per-object virtual attributes
1454 // because for an object with all the same sub-object attributes,
1455 // then any general virtual attributes should be merged with sub-objects by
1456 // the implementation.
1458 wxRichTextObject
* nextChildAfterSplit
= nextChild
;
1460 if (nextChildAfterSplit
->CanSplit(context
))
1461 nextChildAfterSplit
= nextChild
->Split(context
);
1463 bool splitNextChild
= nextChild
!= nextChildAfterSplit
;
1465 // See if we can merge this new fragment with (perhaps the first part of) the next object.
1466 // Note that we use nextChild because if we had split nextChild, the first object always
1467 // remains (and further parts are appended). However we must use childAfterSplit since
1468 // it's the last part of a possibly split child.
1470 if (childAfterSplit
->CanMerge(nextChild
, context
) && childAfterSplit
->Merge(nextChild
, context
))
1472 nextChild
->Dereference();
1473 m_children
.Erase(node
->GetNext());
1475 // Don't set node -- we'll see if we can merge again with the next
1476 // child. UNLESS we split this or the next child, in which case we know we have to
1477 // move on to the end of the next child.
1479 node
= m_children
.Find(nextChildAfterSplit
);
1484 node
= m_children
.Find(nextChildAfterSplit
); // start from the last object in the split
1486 node
= node
->GetNext();
1490 node
= node
->GetNext();
1494 node
= node
->GetNext();
1497 // Delete any remaining empty objects, but leave at least one empty object per composite object.
1498 if (GetChildCount() > 1)
1500 node
= m_children
.GetFirst();
1503 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
1504 wxRichTextObject
* child
= node
->GetData();
1505 if (range
== wxRICHTEXT_ALL
|| !child
->GetRange().IsOutside(range
))
1507 if (child
->IsEmpty())
1509 child
->Dereference();
1510 m_children
.Erase(node
);
1515 node
= node
->GetNext();
1522 /// Dump to output stream for debugging
1523 void wxRichTextCompositeObject::Dump(wxTextOutputStream
& stream
)
1525 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1528 wxRichTextObject
* child
= node
->GetData();
1529 child
->Dump(stream
);
1530 node
= node
->GetNext();
1534 /// Get/set the object size for the given range. Returns false if the range
1535 /// is invalid for this object.
1536 bool wxRichTextCompositeObject::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
1538 if (!range
.IsWithin(GetRange()))
1543 wxArrayInt childExtents
;
1550 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1553 wxRichTextObject
* child
= node
->GetData();
1554 if (!child
->GetRange().IsOutside(range
))
1556 // Floating objects have a zero size within the paragraph.
1557 if (child
->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode())
1562 if (partialExtents
->GetCount() > 0)
1563 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
1567 partialExtents
->Add(0 /* zero size */ + lastSize
);
1574 wxRichTextRange rangeToUse
= range
;
1575 rangeToUse
.LimitTo(child
->GetRange());
1576 if (child
->IsTopLevel())
1577 rangeToUse
= child
->GetOwnRange();
1579 int childDescent
= 0;
1581 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we're already cached the size,
1582 // but it's only going to be used after caching has taken place.
1583 if ((flags
& wxRICHTEXT_HEIGHT_ONLY
) && child
->GetCachedSize().y
!= 0)
1585 childDescent
= child
->GetDescent();
1586 childSize
= child
->GetCachedSize();
1588 sz
.y
= wxMax(sz
.y
, childSize
.y
);
1589 sz
.x
+= childSize
.x
;
1590 descent
= wxMax(descent
, childDescent
);
1592 else if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, context
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
), p
))
1594 sz
.y
= wxMax(sz
.y
, childSize
.y
);
1595 sz
.x
+= childSize
.x
;
1596 descent
= wxMax(descent
, childDescent
);
1598 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange() || child
->IsTopLevel()))
1600 child
->SetCachedSize(childSize
);
1601 child
->SetDescent(childDescent
);
1607 if (partialExtents
->GetCount() > 0)
1608 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
1613 for (i
= 0; i
< childExtents
.GetCount(); i
++)
1615 partialExtents
->Add(childExtents
[i
] + lastSize
);
1625 node
= node
->GetNext();
1631 // Invalidate the buffer. With no argument, invalidates whole buffer.
1632 void wxRichTextCompositeObject::Invalidate(const wxRichTextRange
& invalidRange
)
1634 wxRichTextObject::Invalidate(invalidRange
);
1636 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1639 wxRichTextObject
* child
= node
->GetData();
1640 if (invalidRange
!= wxRICHTEXT_ALL
&& invalidRange
!= wxRICHTEXT_NONE
&& child
->GetRange().IsOutside(invalidRange
))
1644 else if (child
->IsTopLevel())
1646 if (wxRichTextBuffer::GetFloatingLayoutMode() && child
->IsFloating() && GetBuffer()->GetFloatCollector() && GetBuffer()->GetFloatCollector()->HasFloat(child
))
1648 // Don't invalidate subhierarchy if we've already been laid out
1652 if (invalidRange
== wxRICHTEXT_NONE
)
1653 child
->Invalidate(wxRICHTEXT_NONE
);
1655 child
->Invalidate(wxRICHTEXT_ALL
); // All children must be invalidated if within parent range
1659 child
->Invalidate(invalidRange
);
1660 node
= node
->GetNext();
1664 // Move the object recursively, by adding the offset from old to new
1665 void wxRichTextCompositeObject::Move(const wxPoint
& pt
)
1667 wxPoint oldPos
= GetPosition();
1669 wxPoint offset
= pt
- oldPos
;
1671 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1674 wxRichTextObject
* child
= node
->GetData();
1675 wxPoint childPos
= child
->GetPosition() + offset
;
1676 child
->Move(childPos
);
1677 node
= node
->GetNext();
1683 * wxRichTextParagraphLayoutBox
1684 * This box knows how to lay out paragraphs.
1687 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraphLayoutBox
, wxRichTextCompositeObject
)
1689 wxRichTextParagraphLayoutBox::wxRichTextParagraphLayoutBox(wxRichTextObject
* parent
):
1690 wxRichTextCompositeObject(parent
)
1695 wxRichTextParagraphLayoutBox::~wxRichTextParagraphLayoutBox()
1697 if (m_floatCollector
)
1699 delete m_floatCollector
;
1700 m_floatCollector
= NULL
;
1704 /// Initialize the object.
1705 void wxRichTextParagraphLayoutBox::Init()
1709 // For now, assume is the only box and has no initial size.
1710 m_range
= wxRichTextRange(0, -1);
1711 m_ownRange
= wxRichTextRange(0, -1);
1713 m_invalidRange
= wxRICHTEXT_ALL
;
1715 m_partialParagraph
= false;
1716 m_floatCollector
= NULL
;
1719 void wxRichTextParagraphLayoutBox::Clear()
1723 if (m_floatCollector
)
1724 delete m_floatCollector
;
1725 m_floatCollector
= NULL
;
1726 m_partialParagraph
= false;
1730 void wxRichTextParagraphLayoutBox::Copy(const wxRichTextParagraphLayoutBox
& obj
)
1734 wxRichTextCompositeObject::Copy(obj
);
1736 m_partialParagraph
= obj
.m_partialParagraph
;
1737 m_defaultAttributes
= obj
.m_defaultAttributes
;
1740 // Gather information about floating objects; only gather floats for those paragraphs that
1741 // will not be formatted again due to optimization, after which floats will be gathered per-paragraph
1743 bool wxRichTextParagraphLayoutBox::UpdateFloatingObjects(const wxRect
& availableRect
, wxRichTextObject
* untilObj
)
1745 if (m_floatCollector
!= NULL
)
1746 delete m_floatCollector
;
1747 m_floatCollector
= new wxRichTextFloatCollector(availableRect
);
1748 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1749 // Only gather floats up to the point we'll start formatting paragraphs.
1750 while (untilObj
&& node
&& node
->GetData() != untilObj
)
1752 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
1753 wxASSERT (child
!= NULL
);
1755 m_floatCollector
->CollectFloat(child
);
1756 node
= node
->GetNext();
1762 // Returns the style sheet associated with the overall buffer.
1763 wxRichTextStyleSheet
* wxRichTextParagraphLayoutBox::GetStyleSheet() const
1765 return GetBuffer() ? GetBuffer()->GetStyleSheet() : (wxRichTextStyleSheet
*) NULL
;
1768 // Get the number of floating objects at this level
1769 int wxRichTextParagraphLayoutBox::GetFloatingObjectCount() const
1771 if (m_floatCollector
)
1772 return m_floatCollector
->GetFloatingObjectCount();
1777 // Get a list of floating objects
1778 bool wxRichTextParagraphLayoutBox::GetFloatingObjects(wxRichTextObjectList
& objects
) const
1780 if (m_floatCollector
)
1782 return m_floatCollector
->GetFloatingObjects(objects
);
1789 void wxRichTextParagraphLayoutBox::UpdateRanges()
1793 start
= GetRange().GetStart();
1795 CalculateRange(start
, end
);
1799 int wxRichTextParagraphLayoutBox::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
1802 return wxRICHTEXT_HITTEST_NONE
;
1804 int ret
= wxRICHTEXT_HITTEST_NONE
;
1805 if (wxRichTextBuffer::GetFloatingLayoutMode() && m_floatCollector
&& (flags
& wxRICHTEXT_HITTEST_NO_FLOATING_OBJECTS
) == 0)
1806 ret
= m_floatCollector
->HitTest(dc
, context
, pt
, textPosition
, obj
, flags
);
1808 if (ret
== wxRICHTEXT_HITTEST_NONE
)
1809 return wxRichTextCompositeObject::HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
1817 /// Draw the floating objects
1818 void wxRichTextParagraphLayoutBox::DrawFloats(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
1820 if (wxRichTextBuffer::GetFloatingLayoutMode() && m_floatCollector
)
1821 m_floatCollector
->Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
1824 void wxRichTextParagraphLayoutBox::MoveAnchoredObjectToParagraph(wxRichTextParagraph
* from
, wxRichTextParagraph
* to
, wxRichTextObject
* obj
)
1829 from
->RemoveChild(obj
);
1830 to
->AppendChild(obj
);
1834 bool wxRichTextParagraphLayoutBox::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
1839 wxRect
thisRect(GetPosition(), GetCachedSize());
1841 wxRichTextAttr
attr(GetAttributes());
1842 context
.ApplyVirtualAttributes(attr
, this);
1845 if (selection
.IsValid() && GetParentContainer() != this && selection
.WithinSelection(GetRange().GetStart(), GetParentContainer()))
1846 flags
|= wxRICHTEXT_DRAW_SELECTED
;
1848 // Don't draw guidelines if at top level
1849 int theseFlags
= flags
;
1851 theseFlags
&= ~wxRICHTEXT_DRAW_GUIDELINES
;
1852 DrawBoxAttributes(dc
, GetBuffer(), attr
, thisRect
, theseFlags
);
1854 if (wxRichTextBuffer::GetFloatingLayoutMode())
1855 DrawFloats(dc
, context
, range
, selection
, rect
, descent
, style
);
1857 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1860 wxRichTextObject
* child
= node
->GetData();
1862 if (child
&& !child
->GetRange().IsOutside(range
))
1864 wxRect
childRect(child
->GetPosition(), child
->GetCachedSize());
1865 wxRichTextRange childRange
= range
;
1866 if (child
->IsTopLevel())
1868 childRange
= child
->GetOwnRange();
1871 if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) == 0) && childRect
.GetTop() > rect
.GetBottom())
1876 else if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) == 0) && childRect
.GetBottom() < rect
.GetTop())
1881 child
->Draw(dc
, context
, childRange
, selection
, rect
, descent
, style
);
1884 node
= node
->GetNext();
1889 /// Lay the item out
1890 bool wxRichTextParagraphLayoutBox::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
)
1892 SetPosition(rect
.GetPosition());
1897 wxRect availableSpace
;
1898 bool formatRect
= (style
& wxRICHTEXT_LAYOUT_SPECIFIED_RECT
) == wxRICHTEXT_LAYOUT_SPECIFIED_RECT
;
1900 wxRichTextAttr
attr(GetAttributes());
1901 context
.ApplyVirtualAttributes(attr
, this);
1903 // If only laying out a specific area, the passed rect has a different meaning:
1904 // the visible part of the buffer. This is used in wxRichTextCtrl::OnSize,
1905 // so that during a size, only the visible part will be relaid out, or
1906 // it would take too long causing flicker. As an approximation, we assume that
1907 // everything up to the start of the visible area is laid out correctly.
1910 wxRect
rect2(0, 0, rect
.width
, rect
.height
);
1911 availableSpace
= GetAvailableContentArea(dc
, context
, rect2
);
1913 // Invalidate the part of the buffer from the first visible line
1914 // to the end. If other parts of the buffer are currently invalid,
1915 // then they too will be taken into account if they are above
1916 // the visible point.
1918 wxRichTextLine
* line
= GetLineAtYPosition(rect
.y
);
1920 startPos
= line
->GetAbsoluteRange().GetStart();
1922 Invalidate(wxRichTextRange(startPos
, GetOwnRange().GetEnd()));
1926 availableSpace
= GetAvailableContentArea(dc
, context
, rect
);
1929 // Fix the width if we're at the top level
1931 attr
.GetTextBoxAttr().GetWidth().SetValue(rect
.GetWidth(), wxTEXT_ATTR_UNITS_PIXELS
);
1933 int leftMargin
, rightMargin
, topMargin
, bottomMargin
;
1934 wxRichTextObject::GetTotalMargin(dc
, GetBuffer(), attr
, leftMargin
, rightMargin
,
1935 topMargin
, bottomMargin
);
1940 // The maximum paragraph maximum width, so we can set the overall maximum width for this object
1941 int maxMaxWidth
= 0;
1943 // The maximum paragraph minimum width, so we can set the overall minimum width for this object
1944 int maxMinWidth
= 0;
1946 // If we have vertical alignment, we must recalculate everything.
1947 bool hasVerticalAlignment
= (attr
.GetTextBoxAttr().HasVerticalAlignment() &&
1948 (attr
.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP
));
1950 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1952 bool layoutAll
= true;
1954 // Get invalid range, rounding to paragraph start/end.
1955 wxRichTextRange invalidRange
= GetInvalidRange(true);
1957 if (invalidRange
== wxRICHTEXT_NONE
&& !formatRect
)
1960 if (invalidRange
== wxRICHTEXT_ALL
|| hasVerticalAlignment
)
1962 else // If we know what range is affected, start laying out from that point on.
1963 if (invalidRange
.GetStart() >= GetOwnRange().GetStart())
1965 wxRichTextParagraph
* firstParagraph
= GetParagraphAtPosition(invalidRange
.GetStart());
1968 wxRichTextObjectList::compatibility_iterator firstNode
= m_children
.Find(firstParagraph
);
1969 wxRichTextObjectList::compatibility_iterator previousNode
;
1971 previousNode
= firstNode
->GetPrevious();
1976 wxRichTextParagraph
* previousParagraph
= wxDynamicCast(previousNode
->GetData(), wxRichTextParagraph
);
1977 availableSpace
.y
= previousParagraph
->GetPosition().y
+ previousParagraph
->GetCachedSize().y
;
1980 // Now we're going to start iterating from the first affected paragraph.
1988 // Gather information about only those floating objects that will not be formatted,
1989 // after which floats will be gathered per-paragraph during layout.
1990 if (wxRichTextBuffer::GetFloatingLayoutMode())
1991 UpdateFloatingObjects(availableSpace
, node
? node
->GetData() : (wxRichTextObject
*) NULL
);
1993 // A way to force speedy rest-of-buffer layout (the 'else' below)
1994 bool forceQuickLayout
= false;
1996 // First get the size of the paragraphs we won't be laying out
1997 wxRichTextObjectList::compatibility_iterator n
= m_children
.GetFirst();
1998 while (n
&& n
!= node
)
2000 wxRichTextParagraph
* child
= wxDynamicCast(n
->GetData(), wxRichTextParagraph
);
2003 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
2004 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
2005 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
2012 // Assume this box only contains paragraphs
2014 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2015 // Unsure if this is needed
2016 // wxCHECK_MSG( child, false, wxT("Unknown object in layout") );
2018 if (child
&& child
->IsShown())
2020 // TODO: what if the child hasn't been laid out (e.g. involved in Undo) but still has 'old' lines
2021 if ( !forceQuickLayout
&&
2023 child
->GetLines().IsEmpty() ||
2024 !child
->GetRange().IsOutside(invalidRange
)) )
2026 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
2027 // lays out the object again using the minimum size
2028 child
->LayoutToBestSize(dc
, context
, GetBuffer(),
2029 attr
, child
->GetAttributes(), availableSpace
, rect
, style
&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT
);
2031 // Layout must set the cached size
2032 availableSpace
.y
+= child
->GetCachedSize().y
;
2033 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
2034 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
2035 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
2037 // If we're just formatting the visible part of the buffer,
2038 // and we're now past the bottom of the window, and we don't have any
2039 // floating objects (since they may cause wrapping to change for the rest of the
2040 // the buffer), start quick layout.
2041 if (!hasVerticalAlignment
&& formatRect
&& child
->GetPosition().y
> rect
.GetBottom() && GetFloatingObjectCount() == 0)
2042 forceQuickLayout
= true;
2046 // We're outside the immediately affected range, so now let's just
2047 // move everything up or down. This assumes that all the children have previously
2048 // been laid out and have wrapped line lists associated with them.
2049 // TODO: check all paragraphs before the affected range.
2051 int inc
= availableSpace
.y
- child
->GetPosition().y
;
2055 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2058 if (child
->GetLines().GetCount() == 0)
2060 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
2061 // lays out the object again using the minimum size
2062 child
->LayoutToBestSize(dc
, context
, GetBuffer(),
2063 attr
, child
->GetAttributes(), availableSpace
, rect
, style
&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT
);
2065 //child->Layout(dc, availableChildRect, style);
2068 child
->Move(wxPoint(child
->GetPosition().x
, child
->GetPosition().y
+ inc
));
2070 availableSpace
.y
+= child
->GetCachedSize().y
;
2071 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
2072 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
2073 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
2076 node
= node
->GetNext();
2082 node
= node
->GetNext();
2085 node
= m_children
.GetLast();
2086 if (node
&& node
->GetData()->IsShown())
2088 wxRichTextObject
* child
= node
->GetData();
2089 maxHeight
= child
->GetPosition().y
- (GetPosition().y
+ topMargin
) + child
->GetCachedSize().y
;
2092 maxHeight
= 0; // topMargin + bottomMargin;
2094 // Check the bottom edge of any floating object
2095 if (wxRichTextBuffer::GetFloatingLayoutMode() && GetFloatCollector() && GetFloatCollector()->HasFloats())
2097 int bottom
= GetFloatCollector()->GetLastRectBottom();
2098 if (bottom
> maxHeight
)
2102 if (attr
.GetTextBoxAttr().GetSize().GetWidth().IsValid())
2104 wxRect r
= AdjustAvailableSpace(dc
, GetBuffer(), wxRichTextAttr() /* not used */, attr
, parentRect
, parentRect
);
2105 int w
= r
.GetWidth();
2107 // Convert external to content rect
2108 w
= w
- leftMargin
- rightMargin
;
2109 maxWidth
= wxMax(maxWidth
, w
);
2110 maxMaxWidth
= wxMax(maxMaxWidth
, w
);
2114 // TODO: Make sure the layout box's position reflects
2115 // the position of the children, but without
2116 // breaking layout of a box within a paragraph.
2119 // TODO: (also in para layout) should set the
2120 // object's size to an absolute one if specified,
2121 // but if not specified, calculate it from content.
2123 // We need to add back the margins etc.
2125 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2126 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxWidth
, maxHeight
));
2127 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2128 SetCachedSize(marginRect
.GetSize());
2131 // The maximum size is the greatest of all maximum widths for all paragraphs.
2133 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2134 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxMaxWidth
, maxHeight
)); // Actually max height is a lie, we can't know it
2135 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2136 SetMaxSize(marginRect
.GetSize());
2139 // The minimum size is the greatest of all minimum widths for all paragraphs.
2141 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2142 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxMinWidth
, maxHeight
)); // Actually max height is a lie, we can't know it
2143 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2144 SetMinSize(marginRect
.GetSize());
2147 if (attr
.GetTextBoxAttr().HasVerticalAlignment() &&
2148 (attr
.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP
))
2151 int leftOverSpace
= availableSpace
.height
- topMargin
- bottomMargin
- maxHeight
;
2152 if (leftOverSpace
> 0)
2154 if (attr
.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_CENTRE
)
2156 yOffset
= (leftOverSpace
/2);
2158 else if (attr
.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_BOTTOM
)
2160 yOffset
= leftOverSpace
;
2164 // Move all the children to vertically align the content
2165 // This doesn't take into account floating objects, unfortunately.
2168 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2171 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2173 child
->Move(wxPoint(child
->GetPosition().x
, child
->GetPosition().y
+ yOffset
));
2175 node
= node
->GetNext();
2180 m_invalidRange
= wxRICHTEXT_NONE
;
2185 /// Get/set the size for the given range.
2186 bool wxRichTextParagraphLayoutBox::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* WXUNUSED(partialExtents
)) const
2190 wxRichTextObjectList::compatibility_iterator startPara
= wxRichTextObjectList::compatibility_iterator();
2191 wxRichTextObjectList::compatibility_iterator endPara
= wxRichTextObjectList::compatibility_iterator();
2193 // First find the first paragraph whose starting position is within the range.
2194 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2197 // child is a paragraph
2198 wxRichTextObject
* child
= node
->GetData();
2199 const wxRichTextRange
& r
= child
->GetRange();
2201 if (r
.GetStart() <= range
.GetStart() && r
.GetEnd() >= range
.GetStart())
2207 node
= node
->GetNext();
2210 // Next find the last paragraph containing part of the range
2211 node
= m_children
.GetFirst();
2214 // child is a paragraph
2215 wxRichTextObject
* child
= node
->GetData();
2216 const wxRichTextRange
& r
= child
->GetRange();
2218 if (r
.GetStart() <= range
.GetEnd() && r
.GetEnd() >= range
.GetEnd())
2224 node
= node
->GetNext();
2227 if (!startPara
|| !endPara
)
2230 // Now we can add up the sizes
2231 for (node
= startPara
; node
; node
= node
->GetNext())
2233 // child is a paragraph
2234 wxRichTextObject
* child
= node
->GetData();
2235 const wxRichTextRange
& childRange
= child
->GetRange();
2236 wxRichTextRange rangeToFind
= range
;
2237 rangeToFind
.LimitTo(childRange
);
2239 if (child
->IsTopLevel())
2240 rangeToFind
= child
->GetOwnRange();
2244 int childDescent
= 0;
2245 child
->GetRangeSize(rangeToFind
, childSize
, childDescent
, dc
, context
, flags
, position
);
2247 descent
= wxMax(childDescent
, descent
);
2249 sz
.x
= wxMax(sz
.x
, childSize
.x
);
2250 sz
.y
+= childSize
.y
;
2252 if (node
== endPara
)
2261 /// Get the paragraph at the given position
2262 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphAtPosition(long pos
, bool caretPosition
) const
2267 // First find the first paragraph whose starting position is within the range.
2268 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2271 // child is a paragraph
2272 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2273 // wxASSERT (child != NULL);
2277 // Return first child in buffer if position is -1
2281 if (child
->GetRange().Contains(pos
))
2285 node
= node
->GetNext();
2290 /// Get the line at the given position
2291 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineAtPosition(long pos
, bool caretPosition
) const
2296 // First find the first paragraph whose starting position is within the range.
2297 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2300 wxRichTextObject
* obj
= (wxRichTextObject
*) node
->GetData();
2301 if (obj
->GetRange().Contains(pos
))
2303 // child is a paragraph
2304 wxRichTextParagraph
* child
= wxDynamicCast(obj
, wxRichTextParagraph
);
2305 // wxASSERT (child != NULL);
2309 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2312 wxRichTextLine
* line
= node2
->GetData();
2314 wxRichTextRange range
= line
->GetAbsoluteRange();
2316 if (range
.Contains(pos
) ||
2318 // If the position is end-of-paragraph, then return the last line of
2319 // of the paragraph.
2320 ((range
.GetEnd() == child
->GetRange().GetEnd()-1) && (pos
== child
->GetRange().GetEnd())))
2323 node2
= node2
->GetNext();
2328 node
= node
->GetNext();
2331 int lineCount
= GetLineCount();
2333 return GetLineForVisibleLineNumber(lineCount
-1);
2338 /// Get the line at the given y pixel position, or the last line.
2339 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineAtYPosition(int y
) const
2341 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2344 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2345 // wxASSERT (child != NULL);
2349 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2352 wxRichTextLine
* line
= node2
->GetData();
2354 wxRect
rect(line
->GetRect());
2356 if (y
<= rect
.GetBottom())
2359 node2
= node2
->GetNext();
2363 node
= node
->GetNext();
2367 int lineCount
= GetLineCount();
2369 return GetLineForVisibleLineNumber(lineCount
-1);
2374 /// Get the number of visible lines
2375 int wxRichTextParagraphLayoutBox::GetLineCount() const
2379 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2382 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2383 // wxASSERT (child != NULL);
2386 count
+= child
->GetLines().GetCount();
2388 node
= node
->GetNext();
2394 /// Get the paragraph for a given line
2395 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphForLine(wxRichTextLine
* line
) const
2397 return GetParagraphAtPosition(line
->GetAbsoluteRange().GetStart());
2400 /// Get the line size at the given position
2401 wxSize
wxRichTextParagraphLayoutBox::GetLineSizeAtPosition(long pos
, bool caretPosition
) const
2403 wxRichTextLine
* line
= GetLineAtPosition(pos
, caretPosition
);
2406 return line
->GetSize();
2409 return wxSize(0, 0);
2413 /// Convenience function to add a paragraph of text
2414 wxRichTextRange
wxRichTextParagraphLayoutBox::AddParagraph(const wxString
& text
, wxRichTextAttr
* paraStyle
)
2416 // Don't use the base style, just the default style, and the base style will
2417 // be combined at display time.
2418 // Divide into paragraph and character styles.
2420 wxRichTextAttr defaultCharStyle
;
2421 wxRichTextAttr defaultParaStyle
;
2423 // If the default style is a named paragraph style, don't apply any character formatting
2424 // to the initial text string.
2425 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2427 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2429 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2432 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2434 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2435 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2437 wxRichTextParagraph
* para
= new wxRichTextParagraph(text
, this, pStyle
, cStyle
);
2438 para
->GetAttributes().GetTextBoxAttr().Reset();
2444 return para
->GetRange();
2447 /// Adds multiple paragraphs, based on newlines.
2448 wxRichTextRange
wxRichTextParagraphLayoutBox::AddParagraphs(const wxString
& text
, wxRichTextAttr
* paraStyle
)
2450 // Don't use the base style, just the default style, and the base style will
2451 // be combined at display time.
2452 // Divide into paragraph and character styles.
2454 wxRichTextAttr defaultCharStyle
;
2455 wxRichTextAttr defaultParaStyle
;
2457 // If the default style is a named paragraph style, don't apply any character formatting
2458 // to the initial text string.
2459 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2461 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2463 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2466 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2468 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2469 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2471 wxRichTextParagraph
* firstPara
= NULL
;
2472 wxRichTextParagraph
* lastPara
= NULL
;
2474 wxRichTextRange
range(-1, -1);
2477 size_t len
= text
.length();
2479 wxRichTextParagraph
* para
= new wxRichTextParagraph(wxEmptyString
, this, pStyle
, cStyle
);
2480 para
->GetAttributes().GetTextBoxAttr().Reset();
2489 wxChar ch
= text
[i
];
2490 if (ch
== wxT('\n') || ch
== wxT('\r'))
2494 wxRichTextPlainText
* plainText
= (wxRichTextPlainText
*) para
->GetChildren().GetFirst()->GetData();
2495 plainText
->SetText(line
);
2497 para
= new wxRichTextParagraph(wxEmptyString
, this, pStyle
, cStyle
);
2498 para
->GetAttributes().GetTextBoxAttr().Reset();
2503 line
= wxEmptyString
;
2514 wxRichTextPlainText
* plainText
= (wxRichTextPlainText
*) para
->GetChildren().GetFirst()->GetData();
2515 plainText
->SetText(line
);
2520 return wxRichTextRange(firstPara
->GetRange().GetStart(), lastPara
->GetRange().GetEnd());
2523 /// Convenience function to add an image
2524 wxRichTextRange
wxRichTextParagraphLayoutBox::AddImage(const wxImage
& image
, wxRichTextAttr
* paraStyle
)
2526 // Don't use the base style, just the default style, and the base style will
2527 // be combined at display time.
2528 // Divide into paragraph and character styles.
2530 wxRichTextAttr defaultCharStyle
;
2531 wxRichTextAttr defaultParaStyle
;
2533 // If the default style is a named paragraph style, don't apply any character formatting
2534 // to the initial text string.
2535 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2537 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2539 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2542 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2544 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2545 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2547 wxRichTextParagraph
* para
= new wxRichTextParagraph(this, pStyle
);
2548 para
->GetAttributes().GetTextBoxAttr().Reset();
2550 para
->AppendChild(new wxRichTextImage(image
, this, cStyle
));
2554 return para
->GetRange();
2558 /// Insert fragment into this box at the given position. If partialParagraph is true,
2559 /// it is assumed that the last (or only) paragraph is just a piece of data with no paragraph
2562 bool wxRichTextParagraphLayoutBox::InsertFragment(long position
, wxRichTextParagraphLayoutBox
& fragment
)
2564 // First, find the first paragraph whose starting position is within the range.
2565 wxRichTextParagraph
* para
= GetParagraphAtPosition(position
);
2568 wxRichTextAttr originalAttr
= para
->GetAttributes();
2570 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(para
);
2572 // Now split at this position, returning the object to insert the new
2573 // ones in front of.
2574 wxRichTextObject
* nextObject
= para
->SplitAt(position
);
2576 // Special case: partial paragraph, just one paragraph. Might be a small amount of
2577 // text, for example, so let's optimize.
2579 if (fragment
.GetPartialParagraph() && fragment
.GetChildren().GetCount() == 1)
2581 // Add the first para to this para...
2582 wxRichTextObjectList::compatibility_iterator firstParaNode
= fragment
.GetChildren().GetFirst();
2586 // Iterate through the fragment paragraph inserting the content into this paragraph.
2587 wxRichTextParagraph
* firstPara
= wxDynamicCast(firstParaNode
->GetData(), wxRichTextParagraph
);
2588 wxASSERT (firstPara
!= NULL
);
2590 wxRichTextObjectList::compatibility_iterator objectNode
= firstPara
->GetChildren().GetFirst();
2593 wxRichTextObject
* newObj
= objectNode
->GetData()->Clone();
2598 para
->AppendChild(newObj
);
2602 // Insert before nextObject
2603 para
->InsertChild(newObj
, nextObject
);
2606 objectNode
= objectNode
->GetNext();
2613 // Procedure for inserting a fragment consisting of a number of
2616 // 1. Remove and save the content that's after the insertion point, for adding
2617 // back once we've added the fragment.
2618 // 2. Add the content from the first fragment paragraph to the current
2620 // 3. Add remaining fragment paragraphs after the current paragraph.
2621 // 4. Add back the saved content from the first paragraph. If partialParagraph
2622 // is true, add it to the last paragraph added and not a new one.
2624 // 1. Remove and save objects after split point.
2625 wxList savedObjects
;
2627 para
->MoveToList(nextObject
, savedObjects
);
2629 // 2. Add the content from the 1st fragment paragraph.
2630 wxRichTextObjectList::compatibility_iterator firstParaNode
= fragment
.GetChildren().GetFirst();
2634 wxRichTextParagraph
* firstPara
= wxDynamicCast(firstParaNode
->GetData(), wxRichTextParagraph
);
2635 wxASSERT(firstPara
!= NULL
);
2637 if (!(fragment
.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
))
2638 para
->SetAttributes(firstPara
->GetAttributes());
2640 // Save empty paragraph attributes for appending later
2641 // These are character attributes deliberately set for a new paragraph. Without this,
2642 // we couldn't pass default attributes when appending a new paragraph.
2643 wxRichTextAttr emptyParagraphAttributes
;
2645 wxRichTextObjectList::compatibility_iterator objectNode
= firstPara
->GetChildren().GetFirst();
2647 if (objectNode
&& firstPara
->GetChildren().GetCount() == 1 && objectNode
->GetData()->IsEmpty())
2648 emptyParagraphAttributes
= objectNode
->GetData()->GetAttributes();
2652 wxRichTextObject
* newObj
= objectNode
->GetData()->Clone();
2655 para
->AppendChild(newObj
);
2657 objectNode
= objectNode
->GetNext();
2660 // 3. Add remaining fragment paragraphs after the current paragraph.
2661 wxRichTextObjectList::compatibility_iterator nextParagraphNode
= node
->GetNext();
2662 wxRichTextObject
* nextParagraph
= NULL
;
2663 if (nextParagraphNode
)
2664 nextParagraph
= nextParagraphNode
->GetData();
2666 wxRichTextObjectList::compatibility_iterator i
= fragment
.GetChildren().GetFirst()->GetNext();
2667 wxRichTextParagraph
* finalPara
= para
;
2669 bool needExtraPara
= (!i
|| !fragment
.GetPartialParagraph());
2671 // If there was only one paragraph, we need to insert a new one.
2674 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2675 wxASSERT( para
!= NULL
);
2677 finalPara
= (wxRichTextParagraph
*) para
->Clone();
2680 InsertChild(finalPara
, nextParagraph
);
2682 AppendChild(finalPara
);
2687 // If there was only one paragraph, or we have full paragraphs in our fragment,
2688 // we need to insert a new one.
2691 finalPara
= new wxRichTextParagraph
;
2694 InsertChild(finalPara
, nextParagraph
);
2696 AppendChild(finalPara
);
2699 // 4. Add back the remaining content.
2703 finalPara
->MoveFromList(savedObjects
);
2705 // Ensure there's at least one object
2706 if (finalPara
->GetChildCount() == 0)
2708 wxRichTextPlainText
* text
= new wxRichTextPlainText(wxEmptyString
);
2709 text
->SetAttributes(emptyParagraphAttributes
);
2711 finalPara
->AppendChild(text
);
2715 if ((fragment
.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
) && firstPara
)
2716 finalPara
->SetAttributes(firstPara
->GetAttributes());
2717 else if (finalPara
&& finalPara
!= para
)
2718 finalPara
->SetAttributes(originalAttr
);
2726 wxRichTextObjectList::compatibility_iterator i
= fragment
.GetChildren().GetFirst();
2729 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2730 wxASSERT( para
!= NULL
);
2732 AppendChild(para
->Clone());
2741 /// Make a copy of the fragment corresponding to the given range, putting it in 'fragment'.
2742 /// If there was an incomplete paragraph at the end, partialParagraph is set to true.
2743 bool wxRichTextParagraphLayoutBox::CopyFragment(const wxRichTextRange
& range
, wxRichTextParagraphLayoutBox
& fragment
)
2745 wxRichTextObjectList::compatibility_iterator i
= GetChildren().GetFirst();
2748 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2749 wxASSERT( para
!= NULL
);
2751 if (!para
->GetRange().IsOutside(range
))
2753 fragment
.AppendChild(para
->Clone());
2758 // Now top and tail the first and last paragraphs in our new fragment (which might be the same).
2759 if (!fragment
.IsEmpty())
2761 wxRichTextParagraph
* firstPara
= wxDynamicCast(fragment
.GetChildren().GetFirst()->GetData(), wxRichTextParagraph
);
2762 wxASSERT( firstPara
!= NULL
);
2764 wxRichTextParagraph
* lastPara
= wxDynamicCast(fragment
.GetChildren().GetLast()->GetData(), wxRichTextParagraph
);
2765 wxASSERT( lastPara
!= NULL
);
2767 if (!firstPara
|| !lastPara
)
2770 bool isFragment
= (range
.GetEnd() < lastPara
->GetRange().GetEnd());
2772 long firstPos
= firstPara
->GetRange().GetStart();
2774 // Adjust for renumbering from zero
2775 wxRichTextRange
topTailRange(range
.GetStart() - firstPos
, range
.GetEnd() - firstPos
);
2778 fragment
.CalculateRange(0, end
);
2780 // Chop off the start of the paragraph
2781 if (topTailRange
.GetStart() > 0)
2783 wxRichTextRange
r(0, topTailRange
.GetStart()-1);
2784 firstPara
->DeleteRange(r
);
2786 // Make sure the numbering is correct
2787 fragment
.CalculateRange(0, end
);
2789 // Now, we've deleted some positions, so adjust the range
2791 topTailRange
.SetStart(range
.GetLength());
2792 topTailRange
.SetEnd(fragment
.GetOwnRange().GetEnd());
2796 topTailRange
.SetStart(range
.GetLength());
2797 topTailRange
.SetEnd(fragment
.GetOwnRange().GetEnd());
2800 if (topTailRange
.GetStart() < lastPara
->GetRange().GetEnd())
2802 lastPara
->DeleteRange(topTailRange
);
2804 // Make sure the numbering is correct
2806 fragment
.CalculateRange(0, end
);
2808 // We only have part of a paragraph at the end
2809 fragment
.SetPartialParagraph(true);
2813 // We have a partial paragraph (don't save last new paragraph marker)
2814 // or complete paragraph
2815 fragment
.SetPartialParagraph(isFragment
);
2822 /// Given a position, get the number of the visible line (potentially many to a paragraph),
2823 /// starting from zero at the start of the buffer.
2824 long wxRichTextParagraphLayoutBox::GetVisibleLineNumber(long pos
, bool caretPosition
, bool startOfLine
) const
2831 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2834 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2835 // wxASSERT( child != NULL );
2839 if (child
->GetRange().Contains(pos
))
2841 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2844 wxRichTextLine
* line
= node2
->GetData();
2845 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
2847 if (lineRange
.Contains(pos
) || pos
== lineRange
.GetStart())
2849 // If the caret is displayed at the end of the previous wrapped line,
2850 // we want to return the line it's _displayed_ at (not the actual line
2851 // containing the position).
2852 if (lineRange
.GetStart() == pos
&& !startOfLine
&& child
->GetRange().GetStart() != pos
)
2853 return lineCount
- 1;
2860 node2
= node2
->GetNext();
2862 // If we didn't find it in the lines, it must be
2863 // the last position of the paragraph. So return the last line.
2867 lineCount
+= child
->GetLines().GetCount();
2870 node
= node
->GetNext();
2877 /// Given a line number, get the corresponding wxRichTextLine object.
2878 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineForVisibleLineNumber(long lineNumber
) const
2882 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2885 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2886 // wxASSERT(child != NULL);
2890 if (lineNumber
< (int) (child
->GetLines().GetCount() + lineCount
))
2892 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2895 wxRichTextLine
* line
= node2
->GetData();
2897 if (lineCount
== lineNumber
)
2902 node2
= node2
->GetNext();
2906 lineCount
+= child
->GetLines().GetCount();
2909 node
= node
->GetNext();
2916 /// Delete range from layout.
2917 bool wxRichTextParagraphLayoutBox::DeleteRange(const wxRichTextRange
& range
)
2919 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2921 wxRichTextParagraph
* firstPara
= NULL
;
2924 wxRichTextParagraph
* obj
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2925 // wxASSERT (obj != NULL);
2927 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
2931 // Delete the range in each paragraph
2933 if (!obj
->GetRange().IsOutside(range
))
2935 // Deletes the content of this object within the given range
2936 obj
->DeleteRange(range
);
2938 wxRichTextRange thisRange
= obj
->GetRange();
2939 wxRichTextAttr thisAttr
= obj
->GetAttributes();
2941 // If the whole paragraph is within the range to delete,
2942 // delete the whole thing.
2943 if (range
.GetStart() <= thisRange
.GetStart() && range
.GetEnd() >= thisRange
.GetEnd())
2945 // Delete the whole object
2946 RemoveChild(obj
, true);
2949 else if (!firstPara
)
2952 // If the range includes the paragraph end, we need to join this
2953 // and the next paragraph.
2954 if (range
.GetEnd() <= thisRange
.GetEnd())
2956 // We need to move the objects from the next paragraph
2957 // to this paragraph
2959 wxRichTextParagraph
* nextParagraph
= NULL
;
2960 if ((range
.GetEnd() < thisRange
.GetEnd()) && obj
)
2961 nextParagraph
= obj
;
2964 // We're ending at the end of the paragraph, so merge the _next_ paragraph.
2966 nextParagraph
= wxDynamicCast(next
->GetData(), wxRichTextParagraph
);
2969 bool applyFinalParagraphStyle
= firstPara
&& nextParagraph
&& nextParagraph
!= firstPara
;
2971 wxRichTextAttr nextParaAttr
;
2972 if (applyFinalParagraphStyle
)
2974 // Special case when deleting the end of a paragraph - use _this_ paragraph's style,
2975 // not the next one.
2976 if (range
.GetStart() == range
.GetEnd() && range
.GetStart() == thisRange
.GetEnd())
2977 nextParaAttr
= thisAttr
;
2979 nextParaAttr
= nextParagraph
->GetAttributes();
2982 if (firstPara
&& nextParagraph
&& firstPara
!= nextParagraph
)
2984 // Move the objects to the previous para
2985 wxRichTextObjectList::compatibility_iterator node1
= nextParagraph
->GetChildren().GetFirst();
2989 wxRichTextObject
* obj1
= node1
->GetData();
2991 firstPara
->AppendChild(obj1
);
2993 wxRichTextObjectList::compatibility_iterator next1
= node1
->GetNext();
2994 nextParagraph
->GetChildren().Erase(node1
);
2999 // Delete the paragraph
3000 RemoveChild(nextParagraph
, true);
3003 // Avoid empty paragraphs
3004 if (firstPara
&& firstPara
->GetChildren().GetCount() == 0)
3006 wxRichTextPlainText
* text
= new wxRichTextPlainText(wxEmptyString
);
3007 firstPara
->AppendChild(text
);
3010 if (applyFinalParagraphStyle
)
3011 firstPara
->SetAttributes(nextParaAttr
);
3024 /// Get any text in this object for the given range
3025 wxString
wxRichTextParagraphLayoutBox::GetTextForRange(const wxRichTextRange
& range
) const
3029 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3032 wxRichTextObject
* child
= node
->GetData();
3033 if (!child
->GetRange().IsOutside(range
))
3035 wxRichTextRange childRange
= range
;
3036 childRange
.LimitTo(child
->GetRange());
3038 wxString childText
= child
->GetTextForRange(childRange
);
3042 if ((childRange
.GetEnd() == child
->GetRange().GetEnd()) && node
->GetNext())
3047 node
= node
->GetNext();
3053 /// Get all the text
3054 wxString
wxRichTextParagraphLayoutBox::GetText() const
3056 return GetTextForRange(GetOwnRange());
3059 /// Get the paragraph by number
3060 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphAtLine(long paragraphNumber
) const
3062 if ((size_t) paragraphNumber
>= GetChildCount())
3065 return (wxRichTextParagraph
*) GetChild((size_t) paragraphNumber
);
3068 /// Get the length of the paragraph
3069 int wxRichTextParagraphLayoutBox::GetParagraphLength(long paragraphNumber
) const
3071 wxRichTextParagraph
* para
= GetParagraphAtLine(paragraphNumber
);
3073 return para
->GetRange().GetLength() - 1; // don't include newline
3078 /// Get the text of the paragraph
3079 wxString
wxRichTextParagraphLayoutBox::GetParagraphText(long paragraphNumber
) const
3081 wxRichTextParagraph
* para
= GetParagraphAtLine(paragraphNumber
);
3083 return para
->GetTextForRange(para
->GetRange());
3085 return wxEmptyString
;
3088 /// Convert zero-based line column and paragraph number to a position.
3089 long wxRichTextParagraphLayoutBox::XYToPosition(long x
, long y
) const
3091 wxRichTextParagraph
* para
= GetParagraphAtLine(y
);
3094 return para
->GetRange().GetStart() + x
;
3100 /// Convert zero-based position to line column and paragraph number
3101 bool wxRichTextParagraphLayoutBox::PositionToXY(long pos
, long* x
, long* y
) const
3103 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
);
3107 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3110 wxRichTextObject
* child
= node
->GetData();
3114 node
= node
->GetNext();
3118 *x
= pos
- para
->GetRange().GetStart();
3126 /// Get the leaf object in a paragraph at this position.
3127 /// Given a line number, get the corresponding wxRichTextLine object.
3128 wxRichTextObject
* wxRichTextParagraphLayoutBox::GetLeafObjectAtPosition(long position
) const
3130 wxRichTextParagraph
* para
= GetParagraphAtPosition(position
);
3133 wxRichTextObjectList::compatibility_iterator node
= para
->GetChildren().GetFirst();
3137 wxRichTextObject
* child
= node
->GetData();
3138 if (child
->GetRange().Contains(position
))
3141 node
= node
->GetNext();
3143 if (position
== para
->GetRange().GetEnd() && para
->GetChildCount() > 0)
3144 return para
->GetChildren().GetLast()->GetData();
3149 /// Set character or paragraph text attributes: apply character styles only to immediate text nodes
3150 bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange
& range
, const wxRichTextAttr
& style
, int flags
)
3152 bool characterStyle
= false;
3153 bool paragraphStyle
= false;
3155 if (style
.IsCharacterStyle())
3156 characterStyle
= true;
3157 if (style
.IsParagraphStyle())
3158 paragraphStyle
= true;
3160 wxRichTextBuffer
* buffer
= GetBuffer();
3162 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
3163 bool applyMinimal
= ((flags
& wxRICHTEXT_SETSTYLE_OPTIMIZE
) != 0);
3164 bool parasOnly
= ((flags
& wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY
) != 0);
3165 bool charactersOnly
= ((flags
& wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY
) != 0);
3166 bool resetExistingStyle
= ((flags
& wxRICHTEXT_SETSTYLE_RESET
) != 0);
3167 bool removeStyle
= ((flags
& wxRICHTEXT_SETSTYLE_REMOVE
) != 0);
3169 // Apply paragraph style first, if any
3170 wxRichTextAttr
wholeStyle(style
);
3172 if (!removeStyle
&& wholeStyle
.HasParagraphStyleName() && buffer
->GetStyleSheet())
3174 wxRichTextParagraphStyleDefinition
* def
= buffer
->GetStyleSheet()->FindParagraphStyle(wholeStyle
.GetParagraphStyleName());
3176 wxRichTextApplyStyle(wholeStyle
, def
->GetStyleMergedWithBase(buffer
->GetStyleSheet()));
3179 // Limit the attributes to be set to the content to only character attributes.
3180 wxRichTextAttr
characterAttributes(wholeStyle
);
3181 characterAttributes
.SetFlags(characterAttributes
.GetFlags() & (wxTEXT_ATTR_CHARACTER
));
3183 if (!removeStyle
&& characterAttributes
.HasCharacterStyleName() && buffer
->GetStyleSheet())
3185 wxRichTextCharacterStyleDefinition
* def
= buffer
->GetStyleSheet()->FindCharacterStyle(characterAttributes
.GetCharacterStyleName());
3187 wxRichTextApplyStyle(characterAttributes
, def
->GetStyleMergedWithBase(buffer
->GetStyleSheet()));
3190 // If we are associated with a control, make undoable; otherwise, apply immediately
3193 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3195 wxRichTextAction
* action
= NULL
;
3197 if (haveControl
&& withUndo
)
3199 action
= new wxRichTextAction(NULL
, _("Change Style"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
3200 action
->SetRange(range
);
3201 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3204 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3207 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3208 // wxASSERT (para != NULL);
3210 if (para
&& para
->GetChildCount() > 0)
3212 // Stop searching if we're beyond the range of interest
3213 if (para
->GetRange().GetStart() > range
.GetEnd())
3216 if (!para
->GetRange().IsOutside(range
))
3218 // We'll be using a copy of the paragraph to make style changes,
3219 // not updating the buffer directly.
3220 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3222 if (haveControl
&& withUndo
)
3224 newPara
= new wxRichTextParagraph(*para
);
3225 action
->GetNewParagraphs().AppendChild(newPara
);
3227 // Also store the old ones for Undo
3228 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3233 // If we're specifying paragraphs only, then we really mean character formatting
3234 // to be included in the paragraph style
3235 if ((paragraphStyle
|| parasOnly
) && !charactersOnly
)
3239 // Removes the given style from the paragraph
3240 wxRichTextRemoveStyle(newPara
->GetAttributes(), style
);
3242 else if (resetExistingStyle
)
3243 newPara
->GetAttributes() = wholeStyle
;
3248 // Only apply attributes that will make a difference to the combined
3249 // style as seen on the display
3250 wxRichTextAttr
combinedAttr(para
->GetCombinedAttributes(true));
3251 wxRichTextApplyStyle(newPara
->GetAttributes(), wholeStyle
, & combinedAttr
);
3254 wxRichTextApplyStyle(newPara
->GetAttributes(), wholeStyle
);
3258 // When applying paragraph styles dynamically, don't change the text objects' attributes
3259 // since they will computed as needed. Only apply the character styling if it's _only_
3260 // character styling. This policy is subject to change and might be put under user control.
3262 // Hm. we might well be applying a mix of paragraph and character styles, in which
3263 // case we _do_ want to apply character styles regardless of what para styles are set.
3264 // But if we're applying a paragraph style, which has some character attributes, but
3265 // we only want the paragraphs to hold this character style, then we _don't_ want to
3266 // apply the character style. So we need to be able to choose.
3268 if (!parasOnly
&& (characterStyle
|charactersOnly
) && range
.GetStart() != newPara
->GetRange().GetEnd())
3270 wxRichTextRange
childRange(range
);
3271 childRange
.LimitTo(newPara
->GetRange());
3273 // Find the starting position and if necessary split it so
3274 // we can start applying a different style.
3275 // TODO: check that the style actually changes or is different
3276 // from style outside of range
3277 wxRichTextObject
* firstObject
wxDUMMY_INITIALIZE(NULL
);
3278 wxRichTextObject
* lastObject
wxDUMMY_INITIALIZE(NULL
);
3280 if (childRange
.GetStart() == newPara
->GetRange().GetStart())
3281 firstObject
= newPara
->GetChildren().GetFirst()->GetData();
3283 firstObject
= newPara
->SplitAt(range
.GetStart());
3285 // Increment by 1 because we're apply the style one _after_ the split point
3286 long splitPoint
= childRange
.GetEnd();
3287 if (splitPoint
!= newPara
->GetRange().GetEnd())
3291 if (splitPoint
== newPara
->GetRange().GetEnd())
3292 lastObject
= newPara
->GetChildren().GetLast()->GetData();
3294 // lastObject is set as a side-effect of splitting. It's
3295 // returned as the object before the new object.
3296 (void) newPara
->SplitAt(splitPoint
, & lastObject
);
3298 wxASSERT(firstObject
!= NULL
);
3299 wxASSERT(lastObject
!= NULL
);
3301 if (!firstObject
|| !lastObject
)
3304 wxRichTextObjectList::compatibility_iterator firstNode
= newPara
->GetChildren().Find(firstObject
);
3305 wxRichTextObjectList::compatibility_iterator lastNode
= newPara
->GetChildren().Find(lastObject
);
3307 wxASSERT(firstNode
);
3310 wxRichTextObjectList::compatibility_iterator node2
= firstNode
;
3314 wxRichTextObject
* child
= node2
->GetData();
3318 // Removes the given style from the paragraph
3319 wxRichTextRemoveStyle(child
->GetAttributes(), style
);
3321 else if (resetExistingStyle
)
3322 child
->GetAttributes() = characterAttributes
;
3327 // Only apply attributes that will make a difference to the combined
3328 // style as seen on the display
3329 wxRichTextAttr
combinedAttr(newPara
->GetCombinedAttributes(child
->GetAttributes(), true));
3330 wxRichTextApplyStyle(child
->GetAttributes(), characterAttributes
, & combinedAttr
);
3333 wxRichTextApplyStyle(child
->GetAttributes(), characterAttributes
);
3336 if (node2
== lastNode
)
3339 node2
= node2
->GetNext();
3345 node
= node
->GetNext();
3348 // Do action, or delay it until end of batch.
3349 if (haveControl
&& withUndo
)
3350 buffer
->SubmitAction(action
);
3355 // Just change the attributes for this single object.
3356 void wxRichTextParagraphLayoutBox::SetStyle(wxRichTextObject
* obj
, const wxRichTextAttr
& textAttr
, int flags
)
3358 wxRichTextBuffer
* buffer
= GetBuffer();
3359 bool withUndo
= flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
;
3360 bool resetExistingStyle
= ((flags
& wxRICHTEXT_SETSTYLE_RESET
) != 0);
3361 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3363 wxRichTextAction
*action
= NULL
;
3364 wxRichTextAttr newAttr
= obj
->GetAttributes();
3365 if (resetExistingStyle
)
3368 newAttr
.Apply(textAttr
);
3370 if (haveControl
&& withUndo
)
3372 action
= new wxRichTextAction(NULL
, _("Change Object Style"), wxRICHTEXT_CHANGE_ATTRIBUTES
, buffer
, obj
->GetContainer(), buffer
->GetRichTextCtrl());
3373 action
->SetRange(obj
->GetRange().FromInternal());
3374 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3375 action
->MakeObject(obj
);
3377 action
->GetAttributes() = newAttr
;
3380 obj
->GetAttributes() = newAttr
;
3382 if (haveControl
&& withUndo
)
3383 buffer
->SubmitAction(action
);
3386 /// Get the text attributes for this position.
3387 bool wxRichTextParagraphLayoutBox::GetStyle(long position
, wxRichTextAttr
& style
)
3389 return DoGetStyle(position
, style
, true);
3392 bool wxRichTextParagraphLayoutBox::GetUncombinedStyle(long position
, wxRichTextAttr
& style
)
3394 return DoGetStyle(position
, style
, false);
3397 /// Implementation helper for GetStyle. If combineStyles is true, combine base, paragraph and
3398 /// context attributes.
3399 bool wxRichTextParagraphLayoutBox::DoGetStyle(long position
, wxRichTextAttr
& style
, bool combineStyles
)
3401 wxRichTextObject
* obj
wxDUMMY_INITIALIZE(NULL
);
3403 if (style
.IsParagraphStyle())
3405 obj
= GetParagraphAtPosition(position
);
3410 // Start with the base style
3411 style
= GetAttributes();
3412 style
.GetTextBoxAttr().Reset();
3414 // Apply the paragraph style
3415 wxRichTextApplyStyle(style
, obj
->GetAttributes());
3418 style
= obj
->GetAttributes();
3425 obj
= GetLeafObjectAtPosition(position
);
3430 wxRichTextParagraph
* para
= wxDynamicCast(obj
->GetParent(), wxRichTextParagraph
);
3431 style
= para
? para
->GetCombinedAttributes(obj
->GetAttributes()) : obj
->GetAttributes();
3434 style
= obj
->GetAttributes();
3442 static bool wxHasStyle(long flags
, long style
)
3444 return (flags
& style
) != 0;
3447 /// Combines 'style' with 'currentStyle' for the purpose of summarising the attributes of a range of
3449 bool wxRichTextParagraphLayoutBox::CollectStyle(wxRichTextAttr
& currentStyle
, const wxRichTextAttr
& style
, wxRichTextAttr
& clashingAttr
, wxRichTextAttr
& absentAttr
)
3451 currentStyle
.CollectCommonAttributes(style
, clashingAttr
, absentAttr
);
3456 /// Get the combined style for a range - if any attribute is different within the range,
3457 /// that attribute is not present within the flags.
3458 /// *** Note that this is not recursive, and so assumes that content inside a paragraph is not itself
3460 bool wxRichTextParagraphLayoutBox::GetStyleForRange(const wxRichTextRange
& range
, wxRichTextAttr
& style
)
3462 style
= wxRichTextAttr();
3464 wxRichTextAttr clashingAttrPara
, clashingAttrChar
;
3465 wxRichTextAttr absentAttrPara
, absentAttrChar
;
3467 wxRichTextObjectList::compatibility_iterator node
= GetChildren().GetFirst();
3470 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3471 if (para
&& !(para
->GetRange().GetStart() > range
.GetEnd() || para
->GetRange().GetEnd() < range
.GetStart()))
3473 if (para
->GetChildren().GetCount() == 0)
3475 wxRichTextAttr paraStyle
= para
->GetCombinedAttributes(true /* use box attributes */);
3477 CollectStyle(style
, paraStyle
, clashingAttrPara
, absentAttrPara
);
3481 wxRichTextRange
paraRange(para
->GetRange());
3482 paraRange
.LimitTo(range
);
3484 // First collect paragraph attributes only
3485 wxRichTextAttr paraStyle
= para
->GetCombinedAttributes();
3486 paraStyle
.SetFlags(paraStyle
.GetFlags() & wxTEXT_ATTR_PARAGRAPH
);
3487 CollectStyle(style
, paraStyle
, clashingAttrPara
, absentAttrPara
);
3489 wxRichTextObjectList::compatibility_iterator childNode
= para
->GetChildren().GetFirst();
3493 wxRichTextObject
* child
= childNode
->GetData();
3494 if (!(child
->GetRange().GetStart() > range
.GetEnd() || child
->GetRange().GetEnd() < range
.GetStart()))
3496 wxRichTextAttr childStyle
= para
->GetCombinedAttributes(child
->GetAttributes(), true /* include box attributes */);
3498 // Now collect character attributes only
3499 childStyle
.SetFlags(childStyle
.GetFlags() & wxTEXT_ATTR_CHARACTER
);
3501 CollectStyle(style
, childStyle
, clashingAttrChar
, absentAttrChar
);
3504 childNode
= childNode
->GetNext();
3508 node
= node
->GetNext();
3513 /// Set default style
3514 bool wxRichTextParagraphLayoutBox::SetDefaultStyle(const wxRichTextAttr
& style
)
3516 m_defaultAttributes
= style
;
3520 /// Test if this whole range has character attributes of the specified kind. If any
3521 /// of the attributes are different within the range, the test fails. You
3522 /// can use this to implement, for example, bold button updating. style must have
3523 /// flags indicating which attributes are of interest.
3524 bool wxRichTextParagraphLayoutBox::HasCharacterAttributes(const wxRichTextRange
& range
, const wxRichTextAttr
& style
) const
3527 int matchingCount
= 0;
3529 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3532 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3533 // wxASSERT (para != NULL);
3537 // Stop searching if we're beyond the range of interest
3538 if (para
->GetRange().GetStart() > range
.GetEnd())
3539 return foundCount
== matchingCount
&& foundCount
!= 0;
3541 if (!para
->GetRange().IsOutside(range
))
3543 wxRichTextObjectList::compatibility_iterator node2
= para
->GetChildren().GetFirst();
3547 wxRichTextObject
* child
= node2
->GetData();
3548 // Allow for empty string if no buffer
3549 wxRichTextRange childRange
= child
->GetRange();
3550 if (childRange
.GetLength() == 0 && GetRange().GetLength() == 1)
3551 childRange
.SetEnd(childRange
.GetEnd()+1);
3553 if (!childRange
.IsOutside(range
) && wxDynamicCast(child
, wxRichTextPlainText
))
3556 wxRichTextAttr textAttr
= para
->GetCombinedAttributes(child
->GetAttributes());
3558 if (textAttr
.EqPartial(style
, false /* strong test - attributes must be valid in both objects */))
3562 node2
= node2
->GetNext();
3567 node
= node
->GetNext();
3570 return foundCount
== matchingCount
&& foundCount
!= 0;
3573 /// Test if this whole range has paragraph attributes of the specified kind. If any
3574 /// of the attributes are different within the range, the test fails. You
3575 /// can use this to implement, for example, centering button updating. style must have
3576 /// flags indicating which attributes are of interest.
3577 bool wxRichTextParagraphLayoutBox::HasParagraphAttributes(const wxRichTextRange
& range
, const wxRichTextAttr
& style
) const
3580 int matchingCount
= 0;
3582 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3585 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3586 // wxASSERT (para != NULL);
3590 // Stop searching if we're beyond the range of interest
3591 if (para
->GetRange().GetStart() > range
.GetEnd())
3592 return foundCount
== matchingCount
&& foundCount
!= 0;
3594 if (!para
->GetRange().IsOutside(range
))
3596 wxRichTextAttr textAttr
= GetAttributes();
3597 // Apply the paragraph style
3598 wxRichTextApplyStyle(textAttr
, para
->GetAttributes());
3601 if (textAttr
.EqPartial(style
, false /* strong test */))
3606 node
= node
->GetNext();
3608 return foundCount
== matchingCount
&& foundCount
!= 0;
3611 void wxRichTextParagraphLayoutBox::PrepareContent(wxRichTextParagraphLayoutBox
& container
)
3613 wxRichTextBuffer
* buffer
= GetBuffer();
3614 if (buffer
&& buffer
->GetRichTextCtrl())
3615 buffer
->GetRichTextCtrl()->PrepareContent(container
);
3618 /// Set character or paragraph properties
3619 bool wxRichTextParagraphLayoutBox::SetProperties(const wxRichTextRange
& range
, const wxRichTextProperties
& properties
, int flags
)
3621 wxRichTextBuffer
* buffer
= GetBuffer();
3623 bool withUndo
= ((flags
& wxRICHTEXT_SETPROPERTIES_WITH_UNDO
) != 0);
3624 bool parasOnly
= ((flags
& wxRICHTEXT_SETPROPERTIES_PARAGRAPHS_ONLY
) != 0);
3625 bool charactersOnly
= ((flags
& wxRICHTEXT_SETPROPERTIES_CHARACTERS_ONLY
) != 0);
3626 bool resetExistingProperties
= ((flags
& wxRICHTEXT_SETPROPERTIES_RESET
) != 0);
3627 bool removeProperties
= ((flags
& wxRICHTEXT_SETPROPERTIES_REMOVE
) != 0);
3629 // If we are associated with a control, make undoable; otherwise, apply immediately
3632 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3634 wxRichTextAction
* action
= NULL
;
3636 if (haveControl
&& withUndo
)
3638 action
= new wxRichTextAction(NULL
, _("Change Properties"), wxRICHTEXT_CHANGE_PROPERTIES
, buffer
, this, buffer
->GetRichTextCtrl());
3639 action
->SetRange(range
);
3640 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3643 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3646 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3647 // wxASSERT (para != NULL);
3649 if (para
&& para
->GetChildCount() > 0)
3651 // Stop searching if we're beyond the range of interest
3652 if (para
->GetRange().GetStart() > range
.GetEnd())
3655 if (!para
->GetRange().IsOutside(range
))
3657 // We'll be using a copy of the paragraph to make style changes,
3658 // not updating the buffer directly.
3659 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3661 if (haveControl
&& withUndo
)
3663 newPara
= new wxRichTextParagraph(*para
);
3664 action
->GetNewParagraphs().AppendChild(newPara
);
3666 // Also store the old ones for Undo
3667 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3674 if (removeProperties
)
3676 // Removes the given style from the paragraph
3678 newPara
->GetProperties().RemoveProperties(properties
);
3680 else if (resetExistingProperties
)
3681 newPara
->GetProperties() = properties
;
3683 newPara
->GetProperties().MergeProperties(properties
);
3686 // When applying paragraph styles dynamically, don't change the text objects' attributes
3687 // since they will computed as needed. Only apply the character styling if it's _only_
3688 // character styling. This policy is subject to change and might be put under user control.
3690 // Hm. we might well be applying a mix of paragraph and character styles, in which
3691 // case we _do_ want to apply character styles regardless of what para styles are set.
3692 // But if we're applying a paragraph style, which has some character attributes, but
3693 // we only want the paragraphs to hold this character style, then we _don't_ want to
3694 // apply the character style. So we need to be able to choose.
3696 if (!parasOnly
&& charactersOnly
&& range
.GetStart() != newPara
->GetRange().GetEnd())
3698 wxRichTextRange
childRange(range
);
3699 childRange
.LimitTo(newPara
->GetRange());
3701 // Find the starting position and if necessary split it so
3702 // we can start applying different properties.
3703 // TODO: check that the properties actually change or are different
3704 // from properties outside of range
3705 wxRichTextObject
* firstObject
wxDUMMY_INITIALIZE(NULL
);
3706 wxRichTextObject
* lastObject
wxDUMMY_INITIALIZE(NULL
);
3708 if (childRange
.GetStart() == newPara
->GetRange().GetStart())
3709 firstObject
= newPara
->GetChildren().GetFirst()->GetData();
3711 firstObject
= newPara
->SplitAt(range
.GetStart());
3713 // Increment by 1 because we're apply the style one _after_ the split point
3714 long splitPoint
= childRange
.GetEnd();
3715 if (splitPoint
!= newPara
->GetRange().GetEnd())
3719 if (splitPoint
== newPara
->GetRange().GetEnd())
3720 lastObject
= newPara
->GetChildren().GetLast()->GetData();
3722 // lastObject is set as a side-effect of splitting. It's
3723 // returned as the object before the new object.
3724 (void) newPara
->SplitAt(splitPoint
, & lastObject
);
3726 wxASSERT(firstObject
!= NULL
);
3727 wxASSERT(lastObject
!= NULL
);
3729 if (!firstObject
|| !lastObject
)
3732 wxRichTextObjectList::compatibility_iterator firstNode
= newPara
->GetChildren().Find(firstObject
);
3733 wxRichTextObjectList::compatibility_iterator lastNode
= newPara
->GetChildren().Find(lastObject
);
3735 wxASSERT(firstNode
);
3738 wxRichTextObjectList::compatibility_iterator node2
= firstNode
;
3742 wxRichTextObject
* child
= node2
->GetData();
3744 if (removeProperties
)
3746 // Removes the given properties from the paragraph
3747 child
->GetProperties().RemoveProperties(properties
);
3749 else if (resetExistingProperties
)
3750 child
->GetProperties() = properties
;
3753 child
->GetProperties().MergeProperties(properties
);
3756 if (node2
== lastNode
)
3759 node2
= node2
->GetNext();
3765 node
= node
->GetNext();
3768 // Do action, or delay it until end of batch.
3769 if (haveControl
&& withUndo
)
3770 buffer
->SubmitAction(action
);
3775 void wxRichTextParagraphLayoutBox::Reset()
3779 wxRichTextBuffer
* buffer
= GetBuffer();
3780 if (buffer
&& buffer
->GetRichTextCtrl())
3782 wxRichTextEvent
event(wxEVT_COMMAND_RICHTEXT_BUFFER_RESET
, buffer
->GetRichTextCtrl()->GetId());
3783 event
.SetEventObject(buffer
->GetRichTextCtrl());
3784 event
.SetContainer(this);
3786 buffer
->SendEvent(event
, true);
3789 AddParagraph(wxEmptyString
);
3791 PrepareContent(*this);
3793 InvalidateHierarchy(wxRICHTEXT_ALL
);
3796 /// Invalidate the buffer. With no argument, invalidates whole buffer.
3797 void wxRichTextParagraphLayoutBox::Invalidate(const wxRichTextRange
& invalidRange
)
3799 wxRichTextCompositeObject::Invalidate(invalidRange
);
3801 DoInvalidate(invalidRange
);
3804 // Do the (in)validation for this object only
3805 void wxRichTextParagraphLayoutBox::DoInvalidate(const wxRichTextRange
& invalidRange
)
3807 if (invalidRange
== wxRICHTEXT_ALL
)
3809 m_invalidRange
= wxRICHTEXT_ALL
;
3811 // Already invalidating everything
3812 else if (m_invalidRange
== wxRICHTEXT_ALL
)
3817 if ((invalidRange
.GetStart() < m_invalidRange
.GetStart()) || m_invalidRange
.GetStart() == -1)
3818 m_invalidRange
.SetStart(invalidRange
.GetStart());
3819 if (invalidRange
.GetEnd() > m_invalidRange
.GetEnd())
3820 m_invalidRange
.SetEnd(invalidRange
.GetEnd());
3824 // Do the (in)validation both up and down the hierarchy
3825 void wxRichTextParagraphLayoutBox::InvalidateHierarchy(const wxRichTextRange
& invalidRange
)
3827 Invalidate(invalidRange
);
3829 if (invalidRange
!= wxRICHTEXT_NONE
)
3831 // Now go up the hierarchy
3832 wxRichTextObject
* thisObj
= this;
3833 wxRichTextObject
* p
= GetParent();
3836 wxRichTextParagraphLayoutBox
* l
= wxDynamicCast(p
, wxRichTextParagraphLayoutBox
);
3838 l
->DoInvalidate(thisObj
->GetRange());
3846 /// Get invalid range, rounding to entire paragraphs if argument is true.
3847 wxRichTextRange
wxRichTextParagraphLayoutBox::GetInvalidRange(bool wholeParagraphs
) const
3849 if (m_invalidRange
== wxRICHTEXT_ALL
|| m_invalidRange
== wxRICHTEXT_NONE
)
3850 return m_invalidRange
;
3852 wxRichTextRange range
= m_invalidRange
;
3854 if (wholeParagraphs
)
3856 wxRichTextParagraph
* para1
= GetParagraphAtPosition(range
.GetStart());
3858 range
.SetStart(para1
->GetRange().GetStart());
3860 // FIXME: be more intelligent about this. Check if we have floating objects
3861 // before the end of the range. But it's not clear how we can in general
3862 // tell where it's safe to stop laying out.
3863 // Anyway, this code is central to efficiency when laying in floating mode.
3864 if (!wxRichTextBuffer::GetFloatingLayoutMode())
3866 wxRichTextParagraph
* para2
= GetParagraphAtPosition(range
.GetEnd());
3868 range
.SetEnd(para2
->GetRange().GetEnd());
3871 // Floating layout means that all children should be laid out,
3872 // because we can't tell how the whole buffer will be affected.
3873 range
.SetEnd(GetOwnRange().GetEnd());
3878 /// Apply the style sheet to the buffer, for example if the styles have changed.
3879 bool wxRichTextParagraphLayoutBox::ApplyStyleSheet(wxRichTextStyleSheet
* styleSheet
)
3881 wxASSERT(styleSheet
!= NULL
);
3887 wxRichTextAttr
attr(GetBasicStyle());
3888 if (GetBasicStyle().HasParagraphStyleName())
3890 wxRichTextParagraphStyleDefinition
* paraDef
= styleSheet
->FindParagraphStyle(GetBasicStyle().GetParagraphStyleName());
3893 attr
.Apply(paraDef
->GetStyleMergedWithBase(styleSheet
));
3894 SetBasicStyle(attr
);
3899 if (GetBasicStyle().HasCharacterStyleName())
3901 wxRichTextCharacterStyleDefinition
* charDef
= styleSheet
->FindCharacterStyle(GetBasicStyle().GetCharacterStyleName());
3904 attr
.Apply(charDef
->GetStyleMergedWithBase(styleSheet
));
3905 SetBasicStyle(attr
);
3910 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3913 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3914 // wxASSERT (para != NULL);
3918 // Combine paragraph and list styles. If there is a list style in the original attributes,
3919 // the current indentation overrides anything else and is used to find the item indentation.
3920 // Also, for applying paragraph styles, consider having 2 modes: (1) we merge with what we have,
3921 // thereby taking into account all user changes, (2) reset the style completely (except for indentation/list
3922 // exception as above).
3923 // Problem: when changing from one list style to another, there's a danger that the level info will get lost.
3924 // So when changing a list style interactively, could retrieve level based on current style, then
3925 // set appropriate indent and apply new style.
3929 if (para
->GetAttributes().HasOutlineLevel())
3930 outline
= para
->GetAttributes().GetOutlineLevel();
3931 if (para
->GetAttributes().HasBulletNumber())
3932 num
= para
->GetAttributes().GetBulletNumber();
3934 if (!para
->GetAttributes().GetParagraphStyleName().IsEmpty() && !para
->GetAttributes().GetListStyleName().IsEmpty())
3936 int currentIndent
= para
->GetAttributes().GetLeftIndent();
3938 wxRichTextParagraphStyleDefinition
* paraDef
= styleSheet
->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
3939 wxRichTextListStyleDefinition
* listDef
= styleSheet
->FindListStyle(para
->GetAttributes().GetListStyleName());
3940 if (paraDef
&& !listDef
)
3942 para
->GetAttributes() = paraDef
->GetStyleMergedWithBase(styleSheet
);
3945 else if (listDef
&& !paraDef
)
3947 // Set overall style defined for the list style definition
3948 para
->GetAttributes() = listDef
->GetStyleMergedWithBase(styleSheet
);
3950 // Apply the style for this level
3951 wxRichTextApplyStyle(para
->GetAttributes(), * listDef
->GetLevelAttributes(listDef
->FindLevelForIndent(currentIndent
)));
3954 else if (listDef
&& paraDef
)
3956 // Combines overall list style, style for level, and paragraph style
3957 para
->GetAttributes() = listDef
->CombineWithParagraphStyle(currentIndent
, paraDef
->GetStyleMergedWithBase(styleSheet
));
3961 else if (para
->GetAttributes().GetParagraphStyleName().IsEmpty() && !para
->GetAttributes().GetListStyleName().IsEmpty())
3963 int currentIndent
= para
->GetAttributes().GetLeftIndent();
3965 wxRichTextListStyleDefinition
* listDef
= styleSheet
->FindListStyle(para
->GetAttributes().GetListStyleName());
3967 // Overall list definition style
3968 para
->GetAttributes() = listDef
->GetStyleMergedWithBase(styleSheet
);
3970 // Style for this level
3971 wxRichTextApplyStyle(para
->GetAttributes(), * listDef
->GetLevelAttributes(listDef
->FindLevelForIndent(currentIndent
)));
3975 else if (!para
->GetAttributes().GetParagraphStyleName().IsEmpty() && para
->GetAttributes().GetListStyleName().IsEmpty())
3977 wxRichTextParagraphStyleDefinition
* def
= styleSheet
->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
3980 para
->GetAttributes() = def
->GetStyleMergedWithBase(styleSheet
);
3986 para
->GetAttributes().SetOutlineLevel(outline
);
3988 para
->GetAttributes().SetBulletNumber(num
);
3991 node
= node
->GetNext();
3993 return foundCount
!= 0;
3997 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
3999 wxRichTextBuffer
* buffer
= GetBuffer();
4000 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
4002 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
4003 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
4004 bool specifyLevel
= ((flags
& wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL
) != 0);
4005 bool renumber
= ((flags
& wxRICHTEXT_SETSTYLE_RENUMBER
) != 0);
4007 // Current number, if numbering
4010 wxASSERT (!specifyLevel
|| (specifyLevel
&& (specifiedLevel
>= 0)));
4012 // If we are associated with a control, make undoable; otherwise, apply immediately
4015 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
4017 wxRichTextAction
* action
= NULL
;
4019 if (haveControl
&& withUndo
)
4021 action
= new wxRichTextAction(NULL
, _("Change List Style"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
4022 action
->SetRange(range
);
4023 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
4026 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
4029 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
4030 // wxASSERT (para != NULL);
4032 if (para
&& para
->GetChildCount() > 0)
4034 // Stop searching if we're beyond the range of interest
4035 if (para
->GetRange().GetStart() > range
.GetEnd())
4038 if (!para
->GetRange().IsOutside(range
))
4040 // We'll be using a copy of the paragraph to make style changes,
4041 // not updating the buffer directly.
4042 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
4044 if (haveControl
&& withUndo
)
4046 newPara
= new wxRichTextParagraph(*para
);
4047 action
->GetNewParagraphs().AppendChild(newPara
);
4049 // Also store the old ones for Undo
4050 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
4057 int thisIndent
= newPara
->GetAttributes().GetLeftIndent();
4058 int thisLevel
= specifyLevel
? specifiedLevel
: def
->FindLevelForIndent(thisIndent
);
4060 // How is numbering going to work?
4061 // If we are renumbering, or numbering for the first time, we need to keep
4062 // track of the number for each level. But we might be simply applying a different
4064 // In Word, applying a style to several paragraphs, even if at different levels,
4065 // reverts the level back to the same one. So we could do the same here.
4066 // Renumbering will need to be done when we promote/demote a paragraph.
4068 // Apply the overall list style, and item style for this level
4069 wxRichTextAttr
listStyle(def
->GetCombinedStyleForLevel(thisLevel
, styleSheet
));
4070 wxRichTextApplyStyle(newPara
->GetAttributes(), listStyle
);
4072 // Now we need to do numbering
4073 // Preserve the existing list item continuation bullet style, if any
4074 if (para
->GetAttributes().HasBulletStyle() && (para
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
))
4075 newPara
->GetAttributes().SetBulletStyle(newPara
->GetAttributes().GetBulletStyle()|wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
);
4080 newPara
->GetAttributes().SetBulletNumber(n
);
4086 else if (!newPara
->GetAttributes().GetListStyleName().IsEmpty())
4088 // if def is NULL, remove list style, applying any associated paragraph style
4089 // to restore the attributes
4091 newPara
->GetAttributes().SetListStyleName(wxEmptyString
);
4092 newPara
->GetAttributes().SetLeftIndent(0, 0);
4093 newPara
->GetAttributes().SetBulletText(wxEmptyString
);
4094 newPara
->GetAttributes().SetBulletStyle(0);
4096 // Eliminate the main list-related attributes
4097 newPara
->GetAttributes().SetFlags(newPara
->GetAttributes().GetFlags() & ~wxTEXT_ATTR_LEFT_INDENT
& ~wxTEXT_ATTR_BULLET_STYLE
& ~wxTEXT_ATTR_BULLET_NUMBER
& ~wxTEXT_ATTR_BULLET_TEXT
& wxTEXT_ATTR_LIST_STYLE_NAME
);
4099 if (styleSheet
&& !newPara
->GetAttributes().GetParagraphStyleName().IsEmpty())
4101 wxRichTextParagraphStyleDefinition
* def
= styleSheet
->FindParagraphStyle(newPara
->GetAttributes().GetParagraphStyleName());
4104 newPara
->GetAttributes() = def
->GetStyleMergedWithBase(styleSheet
);
4111 node
= node
->GetNext();
4114 // Do action, or delay it until end of batch.
4115 if (haveControl
&& withUndo
)
4116 buffer
->SubmitAction(action
);
4121 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
)
4123 wxRichTextBuffer
* buffer
= GetBuffer();
4124 if (buffer
&& buffer
->GetStyleSheet())
4126 wxRichTextListStyleDefinition
* def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4128 return SetListStyle(range
, def
, flags
, startFrom
, specifiedLevel
);
4133 /// Clear list for given range
4134 bool wxRichTextParagraphLayoutBox::ClearListStyle(const wxRichTextRange
& range
, int flags
)
4136 return SetListStyle(range
, NULL
, flags
);
4139 /// Number/renumber any list elements in the given range
4140 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
4142 return DoNumberList(range
, range
, 0, def
, flags
, startFrom
, specifiedLevel
);
4145 /// Number/renumber any list elements in the given range. Also do promotion or demotion of items, if specified
4146 bool wxRichTextParagraphLayoutBox::DoNumberList(const wxRichTextRange
& range
, const wxRichTextRange
& promotionRange
, int promoteBy
,
4147 wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
4149 wxRichTextBuffer
* buffer
= GetBuffer();
4150 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
4152 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
4153 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
4155 bool specifyLevel
= ((flags
& wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL
) != 0);
4158 bool renumber
= ((flags
& wxRICHTEXT_SETSTYLE_RENUMBER
) != 0);
4160 // Max number of levels
4161 const int maxLevels
= 10;
4163 // The level we're looking at now
4164 int currentLevel
= -1;
4166 // The item number for each level
4167 int levels
[maxLevels
];
4170 // Reset all numbering
4171 for (i
= 0; i
< maxLevels
; i
++)
4173 if (startFrom
!= -1)
4174 levels
[i
] = startFrom
-1;
4175 else if (renumber
) // start again
4178 levels
[i
] = -1; // start from the number we found, if any
4182 wxASSERT(!specifyLevel
|| (specifyLevel
&& (specifiedLevel
>= 0)));
4185 // If we are associated with a control, make undoable; otherwise, apply immediately
4188 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
4190 wxRichTextAction
* action
= NULL
;
4192 if (haveControl
&& withUndo
)
4194 action
= new wxRichTextAction(NULL
, _("Renumber List"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
4195 action
->SetRange(range
);
4196 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
4199 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
4202 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
4203 // wxASSERT (para != NULL);
4205 if (para
&& para
->GetChildCount() > 0)
4207 // Stop searching if we're beyond the range of interest
4208 if (para
->GetRange().GetStart() > range
.GetEnd())
4211 if (!para
->GetRange().IsOutside(range
))
4213 // We'll be using a copy of the paragraph to make style changes,
4214 // not updating the buffer directly.
4215 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
4217 if (haveControl
&& withUndo
)
4219 newPara
= new wxRichTextParagraph(*para
);
4220 action
->GetNewParagraphs().AppendChild(newPara
);
4222 // Also store the old ones for Undo
4223 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
4228 wxRichTextListStyleDefinition
* defToUse
= def
;
4231 if (styleSheet
&& !newPara
->GetAttributes().GetListStyleName().IsEmpty())
4232 defToUse
= styleSheet
->FindListStyle(newPara
->GetAttributes().GetListStyleName());
4237 int thisIndent
= newPara
->GetAttributes().GetLeftIndent();
4238 int thisLevel
= defToUse
->FindLevelForIndent(thisIndent
);
4240 // If we've specified a level to apply to all, change the level.
4241 if (specifiedLevel
!= -1)
4242 thisLevel
= specifiedLevel
;
4244 // Do promotion if specified
4245 if ((promoteBy
!= 0) && !para
->GetRange().IsOutside(promotionRange
))
4247 thisLevel
= thisLevel
- promoteBy
;
4254 // Apply the overall list style, and item style for this level
4255 wxRichTextAttr
listStyle(defToUse
->GetCombinedStyleForLevel(thisLevel
, styleSheet
));
4256 wxRichTextApplyStyle(newPara
->GetAttributes(), listStyle
);
4258 // Preserve the existing list item continuation bullet style, if any
4259 if (para
->GetAttributes().HasBulletStyle() && (para
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
))
4260 newPara
->GetAttributes().SetBulletStyle(newPara
->GetAttributes().GetBulletStyle()|wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
);
4262 // OK, we've (re)applied the style, now let's get the numbering right.
4264 if (currentLevel
== -1)
4265 currentLevel
= thisLevel
;
4267 // Same level as before, do nothing except increment level's number afterwards
4268 if (currentLevel
== thisLevel
)
4271 // A deeper level: start renumbering all levels after current level
4272 else if (thisLevel
> currentLevel
)
4274 for (i
= currentLevel
+1; i
<= thisLevel
; i
++)
4278 currentLevel
= thisLevel
;
4280 else if (thisLevel
< currentLevel
)
4282 currentLevel
= thisLevel
;
4285 // Use the current numbering if -1 and we have a bullet number already
4286 if (levels
[currentLevel
] == -1)
4288 if (newPara
->GetAttributes().HasBulletNumber())
4289 levels
[currentLevel
] = newPara
->GetAttributes().GetBulletNumber();
4291 levels
[currentLevel
] = 1;
4295 if (!(para
->GetAttributes().HasBulletStyle() && (para
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
)))
4296 levels
[currentLevel
] ++;
4299 newPara
->GetAttributes().SetBulletNumber(levels
[currentLevel
]);
4301 // Create the bullet text if an outline list
4302 if (listStyle
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
)
4305 for (i
= 0; i
<= currentLevel
; i
++)
4307 if (!text
.IsEmpty())
4309 text
+= wxString::Format(wxT("%d"), levels
[i
]);
4311 newPara
->GetAttributes().SetBulletText(text
);
4317 node
= node
->GetNext();
4320 // Do action, or delay it until end of batch.
4321 if (haveControl
&& withUndo
)
4322 buffer
->SubmitAction(action
);
4327 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
)
4329 wxRichTextBuffer
* buffer
= GetBuffer();
4330 if (buffer
->GetStyleSheet())
4332 wxRichTextListStyleDefinition
* def
= NULL
;
4333 if (!defName
.IsEmpty())
4334 def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4335 return NumberList(range
, def
, flags
, startFrom
, specifiedLevel
);
4340 /// Promote the list items within the given range. promoteBy can be a positive or negative number, e.g. 1 or -1
4341 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy
, const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int specifiedLevel
)
4344 // One strategy is to first work out the range within which renumbering must occur. Then could pass these two ranges
4345 // to NumberList with a flag indicating promotion is required within one of the ranges.
4346 // Find first and last paragraphs in range. Then for first, calculate new indentation and look back until we find
4347 // a paragraph that either has no list style, or has one that is different or whose indentation is less.
4348 // We start renumbering from the para after that different para we found. We specify that the numbering of that
4349 // list position will start from 1.
4350 // Similarly, we look after the last para in the promote range for an indentation that is less (or no list style).
4351 // We can end the renumbering at this point.
4353 // For now, only renumber within the promotion range.
4355 return DoNumberList(range
, range
, promoteBy
, def
, flags
, 1, specifiedLevel
);
4358 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy
, const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int specifiedLevel
)
4360 wxRichTextBuffer
* buffer
= GetBuffer();
4361 if (buffer
->GetStyleSheet())
4363 wxRichTextListStyleDefinition
* def
= NULL
;
4364 if (!defName
.IsEmpty())
4365 def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4366 return PromoteList(promoteBy
, range
, def
, flags
, specifiedLevel
);
4371 /// Fills in the attributes for numbering a paragraph after previousParagraph. It also finds the
4372 /// position of the paragraph that it had to start looking from.
4373 bool wxRichTextParagraphLayoutBox::FindNextParagraphNumber(wxRichTextParagraph
* previousParagraph
, wxRichTextAttr
& attr
) const
4375 // TODO: add GetNextChild/GetPreviousChild to composite
4376 // Search for a paragraph that isn't a continuation paragraph (no bullet)
4377 while (previousParagraph
&& previousParagraph
->GetAttributes().HasBulletStyle() && previousParagraph
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
)
4379 wxRichTextObjectList::compatibility_iterator node
= ((wxRichTextCompositeObject
*) previousParagraph
->GetParent())->GetChildren().Find(previousParagraph
);
4382 node
= node
->GetPrevious();
4384 previousParagraph
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
4386 previousParagraph
= NULL
;
4389 previousParagraph
= NULL
;
4392 if (!previousParagraph
|| !previousParagraph
->GetAttributes().HasFlag(wxTEXT_ATTR_BULLET_STYLE
) || previousParagraph
->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
)
4395 wxRichTextBuffer
* buffer
= GetBuffer();
4396 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
4397 if (styleSheet
&& !previousParagraph
->GetAttributes().GetListStyleName().IsEmpty())
4399 wxRichTextListStyleDefinition
* def
= styleSheet
->FindListStyle(previousParagraph
->GetAttributes().GetListStyleName());
4402 // int thisIndent = previousParagraph->GetAttributes().GetLeftIndent();
4403 // int thisLevel = def->FindLevelForIndent(thisIndent);
4405 bool isOutline
= (previousParagraph
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
) != 0;
4407 attr
.SetFlags(previousParagraph
->GetAttributes().GetFlags() & (wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_BULLET_NUMBER
|wxTEXT_ATTR_BULLET_TEXT
|wxTEXT_ATTR_BULLET_NAME
));
4408 if (previousParagraph
->GetAttributes().HasBulletName())
4409 attr
.SetBulletName(previousParagraph
->GetAttributes().GetBulletName());
4410 attr
.SetBulletStyle(previousParagraph
->GetAttributes().GetBulletStyle());
4411 attr
.SetListStyleName(previousParagraph
->GetAttributes().GetListStyleName());
4413 int nextNumber
= previousParagraph
->GetAttributes().GetBulletNumber() + 1;
4414 attr
.SetBulletNumber(nextNumber
);
4418 wxString text
= previousParagraph
->GetAttributes().GetBulletText();
4419 if (!text
.IsEmpty())
4421 int pos
= text
.Find(wxT('.'), true);
4422 if (pos
!= wxNOT_FOUND
)
4424 text
= text
.Mid(0, text
.Length() - pos
- 1);
4427 text
= wxEmptyString
;
4428 if (!text
.IsEmpty())
4430 text
+= wxString::Format(wxT("%d"), nextNumber
);
4431 attr
.SetBulletText(text
);
4445 * wxRichTextParagraph
4446 * This object represents a single paragraph (or in a straight text editor, a line).
4449 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraph
, wxRichTextCompositeObject
)
4451 wxArrayInt
wxRichTextParagraph::sm_defaultTabs
;
4453 wxRichTextParagraph::wxRichTextParagraph(wxRichTextObject
* parent
, wxRichTextAttr
* style
):
4454 wxRichTextCompositeObject(parent
)
4457 SetAttributes(*style
);
4460 wxRichTextParagraph::wxRichTextParagraph(const wxString
& text
, wxRichTextObject
* parent
, wxRichTextAttr
* paraStyle
, wxRichTextAttr
* charStyle
):
4461 wxRichTextCompositeObject(parent
)
4464 SetAttributes(*paraStyle
);
4466 AppendChild(new wxRichTextPlainText(text
, this, charStyle
));
4469 wxRichTextParagraph::~wxRichTextParagraph()
4475 bool wxRichTextParagraph::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int WXUNUSED(descent
), int style
)
4480 // Currently we don't merge these attributes with the parent, but we
4481 // should consider whether we should (e.g. if we set a border colour
4482 // for all paragraphs). But generally box attributes are likely to be
4483 // different for different objects.
4484 wxRect paraRect
= GetRect();
4485 wxRichTextAttr attr
= GetCombinedAttributes();
4486 context
.ApplyVirtualAttributes(attr
, this);
4488 DrawBoxAttributes(dc
, GetBuffer(), attr
, paraRect
);
4490 // Draw the bullet, if any
4491 if ((attr
.GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
) == 0 && (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
) == 0)
4493 if (attr
.GetLeftSubIndent() != 0)
4495 int spaceBeforePara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingBefore());
4496 int leftIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftIndent());
4498 wxRichTextAttr
bulletAttr(attr
);
4500 // Combine with the font of the first piece of content, if one is specified
4501 if (GetChildren().GetCount() > 0)
4503 wxRichTextObject
* firstObj
= (wxRichTextObject
*) GetChildren().GetFirst()->GetData();
4504 if (!firstObj
->IsFloatable() && firstObj
->GetAttributes().HasFont())
4506 wxRichTextApplyStyle(bulletAttr
, firstObj
->GetAttributes());
4510 // Get line height from first line, if any
4511 wxRichTextLine
* line
= m_cachedLines
.GetFirst() ? (wxRichTextLine
* ) m_cachedLines
.GetFirst()->GetData() : NULL
;
4514 int lineHeight
wxDUMMY_INITIALIZE(0);
4517 lineHeight
= line
->GetSize().y
;
4518 linePos
= line
->GetPosition() + GetPosition();
4523 if (bulletAttr
.HasFont() && GetBuffer())
4524 font
= GetBuffer()->GetFontTable().FindFont(bulletAttr
);
4526 font
= (*wxNORMAL_FONT
);
4528 wxCheckSetFont(dc
, font
);
4530 lineHeight
= dc
.GetCharHeight();
4531 linePos
= GetPosition();
4532 linePos
.y
+= spaceBeforePara
;
4535 wxRect
bulletRect(GetPosition().x
+ leftIndent
, linePos
.y
, linePos
.x
- (GetPosition().x
+ leftIndent
), lineHeight
);
4537 if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
)
4539 if (wxRichTextBuffer::GetRenderer())
4540 wxRichTextBuffer::GetRenderer()->DrawBitmapBullet(this, dc
, bulletAttr
, bulletRect
);
4542 else if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_STANDARD
)
4544 if (wxRichTextBuffer::GetRenderer())
4545 wxRichTextBuffer::GetRenderer()->DrawStandardBullet(this, dc
, bulletAttr
, bulletRect
);
4549 wxString bulletText
= GetBulletText();
4551 if (!bulletText
.empty() && wxRichTextBuffer::GetRenderer())
4552 wxRichTextBuffer::GetRenderer()->DrawTextBullet(this, dc
, bulletAttr
, bulletRect
, bulletText
);
4557 // Draw the range for each line, one object at a time.
4559 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
4562 wxRichTextLine
* line
= node
->GetData();
4563 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
4565 // Lines are specified relative to the paragraph
4567 wxPoint linePosition
= line
->GetPosition() + GetPosition();
4569 // Don't draw if off the screen
4570 if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) != 0) || ((linePosition
.y
+ line
->GetSize().y
) >= rect
.y
&& linePosition
.y
<= rect
.y
+ rect
.height
))
4572 wxPoint objectPosition
= linePosition
;
4573 int maxDescent
= line
->GetDescent();
4575 // Loop through objects until we get to the one within range
4576 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
4581 wxRichTextObject
* child
= node2
->GetData();
4583 if ((!child
->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && child
->GetRange().GetLength() > 0 && !child
->GetRange().IsOutside(lineRange
) && !lineRange
.IsOutside(range
))
4585 // Draw this part of the line at the correct position
4586 wxRichTextRange
objectRange(child
->GetRange());
4587 objectRange
.LimitTo(lineRange
);
4590 if (child
->IsTopLevel())
4592 objectSize
= child
->GetCachedSize();
4593 objectRange
= child
->GetOwnRange();
4597 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING && wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4598 if (i
< (int) line
->GetObjectSizes().GetCount())
4600 objectSize
.x
= line
->GetObjectSizes()[(size_t) i
];
4606 child
->GetRangeSize(objectRange
, objectSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, objectPosition
);
4610 // Use the child object's width, but the whole line's height
4611 wxRect
childRect(objectPosition
, wxSize(objectSize
.x
, line
->GetSize().y
));
4612 child
->Draw(dc
, context
, objectRange
, selection
, childRect
, maxDescent
, style
);
4614 objectPosition
.x
+= objectSize
.x
;
4617 else if (child
->GetRange().GetStart() > lineRange
.GetEnd())
4618 // Can break out of inner loop now since we've passed this line's range
4621 node2
= node2
->GetNext();
4625 node
= node
->GetNext();
4631 // Get the range width using partial extents calculated for the whole paragraph.
4632 static int wxRichTextGetRangeWidth(const wxRichTextParagraph
& para
, const wxRichTextRange
& range
, const wxArrayInt
& partialExtents
)
4634 wxASSERT(partialExtents
.GetCount() >= (size_t) range
.GetLength());
4636 if (partialExtents
.GetCount() < (size_t) range
.GetLength())
4639 int leftMostPos
= 0;
4640 if (range
.GetStart() - para
.GetRange().GetStart() > 0)
4641 leftMostPos
= partialExtents
[range
.GetStart() - para
.GetRange().GetStart() - 1];
4643 int rightMostPos
= partialExtents
[range
.GetEnd() - para
.GetRange().GetStart()];
4645 int w
= rightMostPos
- leftMostPos
;
4650 /// Lay the item out
4651 bool wxRichTextParagraph::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
)
4653 // Deal with floating objects firstly before the normal layout
4654 wxRichTextBuffer
* buffer
= GetBuffer();
4657 wxRichTextFloatCollector
* collector
= GetContainer()->GetFloatCollector();
4659 if (wxRichTextBuffer::GetFloatingLayoutMode())
4661 wxASSERT(collector
!= NULL
);
4663 LayoutFloat(dc
, context
, rect
, parentRect
, style
, collector
);
4666 wxRichTextAttr attr
= GetCombinedAttributes();
4667 context
.ApplyVirtualAttributes(attr
, this);
4671 // Increase the size of the paragraph due to spacing
4672 int spaceBeforePara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingBefore());
4673 int spaceAfterPara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingAfter());
4674 int leftIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftIndent());
4675 int leftSubIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftSubIndent());
4676 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
4678 int lineSpacing
= 0;
4680 // Let's assume line spacing of 10 is normal, 15 is 1.5, 20 is 2, etc.
4681 if (attr
.HasLineSpacing() && attr
.GetLineSpacing() > 0 && attr
.GetFont().IsOk())
4683 wxCheckSetFont(dc
, attr
.GetFont());
4684 lineSpacing
= (int) (double(dc
.GetCharHeight()) * (double(attr
.GetLineSpacing())/10.0 - 1.0));
4687 // Start position for each line relative to the paragraph
4688 int startPositionFirstLine
= leftIndent
;
4689 int startPositionSubsequentLines
= leftIndent
+ leftSubIndent
;
4691 // If we have a bullet in this paragraph, the start position for the first line's text
4692 // is actually leftIndent + leftSubIndent.
4693 if (attr
.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE
)
4694 startPositionFirstLine
= startPositionSubsequentLines
;
4696 long lastEndPos
= GetRange().GetStart()-1;
4697 long lastCompletedEndPos
= lastEndPos
;
4699 int currentWidth
= 0;
4700 SetPosition(rect
.GetPosition());
4702 wxPoint
currentPosition(0, spaceBeforePara
); // We will calculate lines relative to paragraph
4705 int maxHeight
= currentPosition
.y
;
4710 int lineDescent
= 0;
4712 wxRichTextObjectList::compatibility_iterator node
;
4714 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4716 wxArrayInt partialExtents
;
4719 int paraDescent
= 0;
4721 // This calculates the partial text extents
4722 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, rect
.GetPosition(), & partialExtents
);
4724 node
= m_children
.GetFirst();
4727 wxRichTextObject
* child
= node
->GetData();
4729 //child->SetCachedSize(wxDefaultSize);
4730 child
->Layout(dc
, context
, rect
, style
);
4732 node
= node
->GetNext();
4738 // We may need to go back to a previous child, in which case create the new line,
4739 // find the child corresponding to the start position of the string, and
4742 wxRect availableRect
;
4744 node
= m_children
.GetFirst();
4747 wxRichTextObject
* child
= node
->GetData();
4749 // If floating, ignore. We already laid out floats.
4750 // Also ignore if empty object, except if we haven't got any
4752 if ((child
->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode())
4753 || !child
->IsShown() || (child
->GetRange().GetLength() == 0 && maxHeight
> spaceBeforePara
)
4756 node
= node
->GetNext();
4760 // If this is e.g. a composite text box, it will need to be laid out itself.
4761 // But if just a text fragment or image, for example, this will
4762 // do nothing. NB: won't we need to set the position after layout?
4763 // since for example if position is dependent on vertical line size, we
4764 // can't tell the position until the size is determined. So possibly introduce
4765 // another layout phase.
4767 // We may only be looking at part of a child, if we searched back for wrapping
4768 // and found a suitable point some way into the child. So get the size for the fragment
4771 long nextBreakPos
= GetFirstLineBreakPosition(lastEndPos
+1);
4772 long lastPosToUse
= child
->GetRange().GetEnd();
4773 bool lineBreakInThisObject
= (nextBreakPos
> -1 && nextBreakPos
<= child
->GetRange().GetEnd());
4775 if (lineBreakInThisObject
)
4776 lastPosToUse
= nextBreakPos
;
4779 int childDescent
= 0;
4781 int startOffset
= (lineCount
== 0 ? startPositionFirstLine
: startPositionSubsequentLines
);
4782 availableRect
= wxRect(rect
.x
+ startOffset
, rect
.y
+ currentPosition
.y
,
4783 rect
.width
- startOffset
- rightIndent
, rect
.height
);
4785 if (child
->IsTopLevel())
4787 wxSize oldSize
= child
->GetCachedSize();
4789 child
->Invalidate(wxRICHTEXT_ALL
);
4790 child
->SetPosition(wxPoint(0, 0));
4792 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4793 // lays out the object again using the minimum size
4794 // The position will be determined by its location in its line,
4795 // and not by the child's actual position.
4796 child
->LayoutToBestSize(dc
, context
, buffer
,
4797 attr
, child
->GetAttributes(), availableRect
, parentRect
, style
);
4799 if (oldSize
!= child
->GetCachedSize())
4801 partialExtents
.Clear();
4803 // Recalculate the partial text extents since the child object changed size
4804 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), & partialExtents
);
4808 // Problem: we need to layout composites here for which we need the available width,
4809 // but we can't get the available width without using the float collector which
4810 // needs to know the object height.
4812 if ((nextBreakPos
== -1) && (lastEndPos
== child
->GetRange().GetStart() - 1)) // i.e. we want to get the whole thing
4814 childSize
= child
->GetCachedSize();
4815 childDescent
= child
->GetDescent();
4819 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4820 // Get height only, then the width using the partial extents
4821 GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
);
4822 childSize
.x
= wxRichTextGetRangeWidth(*this, wxRichTextRange(lastEndPos
+1, lastPosToUse
), partialExtents
);
4824 GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, rect
.GetPosition());
4829 int loopIterations
= 0;
4831 // If there are nested objects that need to lay themselves out, we have to do this in a
4832 // loop because the height of the object may well depend on the available width.
4833 // And because of floating object positioning, the available width depends on the
4834 // height of the object and whether it will clash with the floating objects.
4835 // So, we see whether the available width changes due to the presence of floating images.
4836 // If it does, then we'll use the new restricted width to find the object height again.
4837 // If this causes another restriction in the available width, we'll try again, until
4838 // either we lose patience or the available width settles down.
4843 wxRect oldAvailableRect
= availableRect
;
4845 // Available width depends on the floating objects and the line height.
4846 // Note: the floating objects may be placed vertically along the two sides of
4847 // buffer, so we may have different available line widths with different
4848 // [startY, endY]. So, we can't determine how wide the available
4849 // space is until we know the exact line height.
4850 if (childDescent
== 0)
4852 lineHeight
= wxMax(lineHeight
, childSize
.y
);
4853 lineDescent
= maxDescent
;
4854 lineAscent
= maxAscent
;
4858 lineDescent
= wxMax(childDescent
, maxDescent
);
4859 lineAscent
= wxMax(childSize
.y
-childDescent
, maxAscent
);
4861 lineHeight
= wxMax(lineHeight
, (lineDescent
+ lineAscent
));
4863 if (wxRichTextBuffer::GetFloatingLayoutMode() && collector
)
4865 wxRect floatAvailableRect
= collector
->GetAvailableRect(rect
.y
+ currentPosition
.y
, rect
.y
+ currentPosition
.y
+ lineHeight
);
4867 // Adjust availableRect to the space that is available when taking floating objects into account.
4869 if (floatAvailableRect
.x
+ startOffset
> availableRect
.x
)
4871 int newX
= floatAvailableRect
.x
+ startOffset
;
4872 int newW
= availableRect
.width
- (newX
- availableRect
.x
);
4873 availableRect
.x
= newX
;
4874 availableRect
.width
= newW
;
4877 if (floatAvailableRect
.width
< availableRect
.width
)
4878 availableRect
.width
= floatAvailableRect
.width
;
4881 currentPosition
.x
= availableRect
.x
- rect
.x
;
4883 if (child
->IsTopLevel() && loopIterations
<= 20)
4885 if (availableRect
!= oldAvailableRect
)
4887 wxSize oldSize
= child
->GetCachedSize();
4889 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4890 // lays out the object again using the minimum size
4891 child
->Invalidate(wxRICHTEXT_ALL
);
4892 child
->LayoutToBestSize(dc
, context
, buffer
,
4893 attr
, child
->GetAttributes(), availableRect
, parentRect
, style
);
4894 childSize
= child
->GetCachedSize();
4895 childDescent
= child
->GetDescent();
4897 if (oldSize
!= child
->GetCachedSize())
4899 partialExtents
.Clear();
4901 // Recalculate the partial text extents since the child object changed size
4902 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), & partialExtents
);
4905 // Go around the loop finding the available rect for the given floating objects
4915 if (child
->IsTopLevel())
4917 // We can move it to the correct position at this point
4918 // TODO: probably need to add margin
4919 child
->Move(GetPosition() + wxPoint(currentWidth
+ (wxMax(leftIndent
, leftIndent
+ leftSubIndent
)), currentPosition
.y
));
4923 // 1) There was a line break BEFORE the natural break
4924 // 2) There was a line break AFTER the natural break
4925 // 3) It's the last line
4926 // 4) The child still fits (carry on) - 'else' clause
4928 if ((lineBreakInThisObject
&& (childSize
.x
+ currentWidth
<= availableRect
.width
))
4930 (childSize
.x
+ currentWidth
> availableRect
.width
)
4932 ((childSize
.x
+ currentWidth
<= availableRect
.width
) && !node
->GetNext())
4936 long wrapPosition
= 0;
4937 if ((childSize
.x
+ currentWidth
<= availableRect
.width
) && !node
->GetNext() && !lineBreakInThisObject
)
4938 wrapPosition
= child
->GetRange().GetEnd();
4941 // Find a place to wrap. This may walk back to previous children,
4942 // for example if a word spans several objects.
4943 // Note: one object must contains only one wxTextAtrr, so the line height will not
4944 // change inside one object. Thus, we can pass the remain line width to the
4945 // FindWrapPosition function.
4946 if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos
+1, child
->GetRange().GetEnd()), dc
, context
, availableRect
.width
, wrapPosition
, & partialExtents
))
4948 // If the function failed, just cut it off at the end of this child.
4949 wrapPosition
= child
->GetRange().GetEnd();
4952 // FindWrapPosition can still return a value that will put us in an endless wrapping loop
4953 if (wrapPosition
<= lastCompletedEndPos
)
4954 wrapPosition
= wxMax(lastCompletedEndPos
+1,child
->GetRange().GetEnd());
4956 // Line end position shouldn't be the same as the end, or greater.
4957 if (wrapPosition
>= GetRange().GetEnd())
4958 wrapPosition
= wxMax(0, GetRange().GetEnd()-1);
4960 // wxLogDebug(wxT("Split at %ld"), wrapPosition);
4962 // Let's find the actual size of the current line now
4964 wxRichTextRange
actualRange(lastCompletedEndPos
+1, wrapPosition
);
4968 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4969 if (!child
->IsEmpty())
4971 // Get height only, then the width using the partial extents
4972 GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
);
4973 actualSize
.x
= wxRichTextGetRangeWidth(*this, actualRange
, partialExtents
);
4977 GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
4979 currentWidth
= actualSize
.x
;
4981 // The descent for the whole line at this point, is the correct max descent
4982 maxDescent
= childDescent
;
4984 maxAscent
= actualSize
.y
-childDescent
;
4986 // lineHeight is given by the height for the whole line, since it will
4987 // take into account ascend/descend.
4988 lineHeight
= actualSize
.y
;
4990 if (lineHeight
== 0 && buffer
)
4992 wxFont
font(buffer
->GetFontTable().FindFont(attr
));
4993 wxCheckSetFont(dc
, font
);
4994 lineHeight
= dc
.GetCharHeight();
4997 if (maxDescent
== 0)
5000 dc
.GetTextExtent(wxT("X"), & w
, &h
, & maxDescent
);
5004 wxRichTextLine
* line
= AllocateLine(lineCount
);
5006 // Set relative range so we won't have to change line ranges when paragraphs are moved
5007 line
->SetRange(wxRichTextRange(actualRange
.GetStart() - GetRange().GetStart(), actualRange
.GetEnd() - GetRange().GetStart()));
5008 line
->SetPosition(currentPosition
);
5009 line
->SetSize(wxSize(currentWidth
, lineHeight
));
5010 line
->SetDescent(maxDescent
);
5012 maxHeight
= currentPosition
.y
+ lineHeight
;
5014 // Now move down a line. TODO: add margins, spacing
5015 currentPosition
.y
+= lineHeight
;
5016 currentPosition
.y
+= lineSpacing
;
5019 maxWidth
= wxMax(maxWidth
, currentWidth
+startOffset
);
5024 // TODO: account for zero-length objects
5025 // wxASSERT(wrapPosition > lastCompletedEndPos);
5027 lastEndPos
= wrapPosition
;
5028 lastCompletedEndPos
= lastEndPos
;
5032 if (wrapPosition
< GetRange().GetEnd()-1)
5034 // May need to set the node back to a previous one, due to searching back in wrapping
5035 wxRichTextObject
* childAfterWrapPosition
= FindObjectAtPosition(wrapPosition
+1);
5036 if (childAfterWrapPosition
)
5037 node
= m_children
.Find(childAfterWrapPosition
);
5039 node
= node
->GetNext();
5042 node
= node
->GetNext();
5044 // Apply paragraph styles such as alignment to the wrapped line
5045 ApplyParagraphStyle(line
, attr
, availableRect
, dc
);
5049 // We still fit, so don't add a line, and keep going
5050 currentWidth
+= childSize
.x
;
5052 if (childDescent
== 0)
5054 // An object with a zero descend value wants to take up the whole
5055 // height regardless of baseline
5056 lineHeight
= wxMax(lineHeight
, childSize
.y
);
5060 maxDescent
= wxMax(childDescent
, maxDescent
);
5061 maxAscent
= wxMax(childSize
.y
-childDescent
, maxAscent
);
5064 lineHeight
= wxMax(lineHeight
, (maxDescent
+ maxAscent
));
5066 maxWidth
= wxMax(maxWidth
, currentWidth
+startOffset
);
5067 lastEndPos
= child
->GetRange().GetEnd();
5069 node
= node
->GetNext();
5073 //wxASSERT(!(lastCompletedEndPos != -1 && lastCompletedEndPos < GetRange().GetEnd()-1));
5075 // Remove remaining unused line objects, if any
5076 ClearUnusedLines(lineCount
);
5078 // We need to add back the margins etc.
5080 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
5081 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxWidth
, currentPosition
.y
+ spaceAfterPara
));
5082 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
5083 SetCachedSize(marginRect
.GetSize());
5086 // The maximum size is the length of the paragraph stretched out into a line.
5087 // So if there were a single word, or an image, or a fixed-size text box, the object could be shrunk around
5088 // this size. TODO: take into account line breaks.
5090 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
5091 contentRect
= wxRect(wxPoint(0, 0), wxSize(paraSize
.x
+ wxMax(leftIndent
, leftIndent
+ leftSubIndent
) + rightIndent
, currentPosition
.y
+ spaceAfterPara
));
5092 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
5093 SetMaxSize(marginRect
.GetSize());
5096 // Find the greatest minimum size. Currently we only look at non-text objects,
5097 // which isn't ideal but it would be slow to find the maximum word width to
5098 // use as the minimum.
5101 node
= m_children
.GetFirst();
5104 wxRichTextObject
* child
= node
->GetData();
5106 // If floating, ignore. We already laid out floats.
5107 // Also ignore if empty object, except if we haven't got any
5109 if ((!child
->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && child
->GetRange().GetLength() != 0 && !wxDynamicCast(child
, wxRichTextPlainText
))
5111 if (child
->GetCachedSize().x
> minWidth
)
5112 minWidth
= child
->GetMinSize().x
;
5114 node
= node
->GetNext();
5117 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
5118 contentRect
= wxRect(wxPoint(0, 0), wxSize(minWidth
, currentPosition
.y
+ spaceAfterPara
));
5119 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
5120 SetMinSize(marginRect
.GetSize());
5123 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5124 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
5125 // Use the text extents to calculate the size of each fragment in each line
5126 wxRichTextLineList::compatibility_iterator lineNode
= m_cachedLines
.GetFirst();
5129 wxRichTextLine
* line
= lineNode
->GetData();
5130 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5132 // Loop through objects until we get to the one within range
5133 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
5137 wxRichTextObject
* child
= node2
->GetData();
5139 if (child
->GetRange().GetLength() > 0 && !child
->GetRange().IsOutside(lineRange
))
5141 wxRichTextRange rangeToUse
= lineRange
;
5142 rangeToUse
.LimitTo(child
->GetRange());
5144 // Find the size of the child from the text extents, and store in an array
5145 // for drawing later
5147 if (rangeToUse
.GetStart() > GetRange().GetStart())
5148 left
= partialExtents
[(rangeToUse
.GetStart()-1) - GetRange().GetStart()];
5149 int right
= partialExtents
[rangeToUse
.GetEnd() - GetRange().GetStart()];
5150 int sz
= right
- left
;
5151 line
->GetObjectSizes().Add(sz
);
5153 else if (child
->GetRange().GetStart() > lineRange
.GetEnd())
5154 // Can break out of inner loop now since we've passed this line's range
5157 node2
= node2
->GetNext();
5160 lineNode
= lineNode
->GetNext();
5168 /// Apply paragraph styles, such as centering, to wrapped lines
5169 /// TODO: take into account box attributes, possibly
5170 void wxRichTextParagraph::ApplyParagraphStyle(wxRichTextLine
* line
, const wxRichTextAttr
& attr
, const wxRect
& rect
, wxDC
& dc
)
5172 if (!attr
.HasAlignment())
5175 wxPoint pos
= line
->GetPosition();
5176 wxPoint originalPos
= pos
;
5177 wxSize size
= line
->GetSize();
5179 // centering, right-justification
5180 if (attr
.HasAlignment() && attr
.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE
)
5182 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
5183 pos
.x
= (rect
.GetWidth() - rightIndent
- size
.x
)/2 + pos
.x
;
5184 line
->SetPosition(pos
);
5186 else if (attr
.HasAlignment() && attr
.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT
)
5188 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
5189 pos
.x
= pos
.x
+ rect
.GetWidth() - size
.x
- rightIndent
;
5190 line
->SetPosition(pos
);
5193 if (pos
!= originalPos
)
5195 wxPoint inc
= pos
- originalPos
;
5197 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5201 wxRichTextObject
* child
= node
->GetData();
5202 if (child
->IsTopLevel() && !child
->GetRange().IsOutside(line
->GetAbsoluteRange()))
5203 child
->Move(child
->GetPosition() + inc
);
5205 node
= node
->GetNext();
5210 /// Insert text at the given position
5211 bool wxRichTextParagraph::InsertText(long pos
, const wxString
& text
)
5213 wxRichTextObject
* childToUse
= NULL
;
5214 wxRichTextObjectList::compatibility_iterator nodeToUse
= wxRichTextObjectList::compatibility_iterator();
5216 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5219 wxRichTextObject
* child
= node
->GetData();
5220 if (child
->GetRange().Contains(pos
) && child
->GetRange().GetLength() > 0)
5227 node
= node
->GetNext();
5232 wxRichTextPlainText
* textObject
= wxDynamicCast(childToUse
, wxRichTextPlainText
);
5235 int posInString
= pos
- textObject
->GetRange().GetStart();
5237 wxString newText
= textObject
->GetText().Mid(0, posInString
) +
5238 text
+ textObject
->GetText().Mid(posInString
);
5239 textObject
->SetText(newText
);
5241 int textLength
= text
.length();
5243 textObject
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart(),
5244 textObject
->GetRange().GetEnd() + textLength
));
5246 // Increment the end range of subsequent fragments in this paragraph.
5247 // We'll set the paragraph range itself at a higher level.
5249 wxRichTextObjectList::compatibility_iterator node
= nodeToUse
->GetNext();
5252 wxRichTextObject
* child
= node
->GetData();
5253 child
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart() + textLength
,
5254 textObject
->GetRange().GetEnd() + textLength
));
5256 node
= node
->GetNext();
5263 // TODO: if not a text object, insert at closest position, e.g. in front of it
5269 // Don't pass parent initially to suppress auto-setting of parent range.
5270 // We'll do that at a higher level.
5271 wxRichTextPlainText
* textObject
= new wxRichTextPlainText(text
, this);
5273 AppendChild(textObject
);
5280 void wxRichTextParagraph::Copy(const wxRichTextParagraph
& obj
)
5282 wxRichTextCompositeObject::Copy(obj
);
5285 /// Clear the cached lines
5286 void wxRichTextParagraph::ClearLines()
5288 WX_CLEAR_LIST(wxRichTextLineList
, m_cachedLines
);
5291 /// Get/set the object size for the given range. Returns false if the range
5292 /// is invalid for this object.
5293 bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
5295 if (!range
.IsWithin(GetRange()))
5298 if (flags
& wxRICHTEXT_UNFORMATTED
)
5300 // Just use unformatted data, assume no line breaks
5303 wxArrayInt childExtents
;
5312 int maxLineHeight
= 0;
5314 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5317 wxRichTextObject
* child
= node
->GetData();
5318 if (!child
->GetRange().IsOutside(range
))
5320 // Floating objects have a zero size within the paragraph.
5321 if (child
->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode())
5326 if (partialExtents
->GetCount() > 0)
5327 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5331 partialExtents
->Add(0 /* zero size */ + lastSize
);
5338 wxRichTextRange rangeToUse
= range
;
5339 rangeToUse
.LimitTo(child
->GetRange());
5340 int childDescent
= 0;
5342 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we've already cached the size,
5343 // but it's only going to be used after caching has taken place.
5344 if ((flags
& wxRICHTEXT_HEIGHT_ONLY
) && child
->GetCachedSize().y
!= 0)
5346 childDescent
= child
->GetDescent();
5347 childSize
= child
->GetCachedSize();
5349 if (childDescent
== 0)
5351 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5355 maxDescent
= wxMax(maxDescent
, childDescent
);
5356 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5359 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5361 sz
.y
= wxMax(sz
.y
, maxLineHeight
);
5362 sz
.x
+= childSize
.x
;
5363 descent
= maxDescent
;
5365 else if (child
->IsTopLevel())
5367 childDescent
= child
->GetDescent();
5368 childSize
= child
->GetCachedSize();
5370 if (childDescent
== 0)
5372 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5376 maxDescent
= wxMax(maxDescent
, childDescent
);
5377 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5380 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5382 sz
.y
= wxMax(sz
.y
, maxLineHeight
);
5383 sz
.x
+= childSize
.x
;
5384 descent
= maxDescent
;
5386 // FIXME: this won't change the original values.
5387 // Should we be calling GetRangeSize above instead of using cached values?
5389 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange()))
5391 child
->SetCachedSize(childSize
);
5392 child
->SetDescent(childDescent
);
5399 if (partialExtents
->GetCount() > 0)
5400 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5404 partialExtents
->Add(childSize
.x
+ lastSize
);
5407 else if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, context
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
), p
))
5409 if (childDescent
== 0)
5411 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5415 maxDescent
= wxMax(maxDescent
, childDescent
);
5416 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5419 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5421 sz
.y
= wxMax(sz
.y
, maxLineHeight
);
5422 sz
.x
+= childSize
.x
;
5423 descent
= maxDescent
;
5425 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange()))
5427 child
->SetCachedSize(childSize
);
5428 child
->SetDescent(childDescent
);
5434 if (partialExtents
->GetCount() > 0)
5435 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5440 for (i
= 0; i
< childExtents
.GetCount(); i
++)
5442 partialExtents
->Add(childExtents
[i
] + lastSize
);
5452 node
= node
->GetNext();
5458 // Use formatted data, with line breaks
5461 // We're going to loop through each line, and then for each line,
5462 // call GetRangeSize for the fragment that comprises that line.
5463 // Only we have to do that multiple times within the line, because
5464 // the line may be broken into pieces. For now ignore line break commands
5465 // (so we can assume that getting the unformatted size for a fragment
5466 // within a line is the actual size)
5468 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5471 wxRichTextLine
* line
= node
->GetData();
5472 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5473 if (!lineRange
.IsOutside(range
))
5477 int maxLineHeight
= 0;
5478 int maxLineWidth
= 0;
5480 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
5483 wxRichTextObject
* child
= node2
->GetData();
5485 if ((!child
->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && !child
->GetRange().IsOutside(lineRange
))
5487 wxRichTextRange rangeToUse
= lineRange
;
5488 rangeToUse
.LimitTo(child
->GetRange());
5489 if (child
->IsTopLevel())
5490 rangeToUse
= child
->GetOwnRange();
5493 int childDescent
= 0;
5494 if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, context
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
)))
5496 if (childDescent
== 0)
5498 // Assume that if descent is zero, this child can occupy the full line height
5499 // and does not need space for the line's maximum descent. So we influence
5500 // the overall max line height only.
5501 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5505 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5506 maxDescent
= wxMax(maxAscent
, childDescent
);
5508 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5509 maxLineWidth
+= childSize
.x
;
5513 node2
= node2
->GetNext();
5516 descent
= wxMax(descent
, maxDescent
);
5518 // Increase size by a line (TODO: paragraph spacing)
5519 sz
.y
+= maxLineHeight
;
5520 sz
.x
= wxMax(sz
.x
, maxLineWidth
);
5522 node
= node
->GetNext();
5529 /// Finds the absolute position and row height for the given character position
5530 bool wxRichTextParagraph::FindPosition(wxDC
& dc
, wxRichTextDrawingContext
& context
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
5534 wxRichTextLine
* line
= ((wxRichTextParagraphLayoutBox
*)GetParent())->GetLineAtPosition(0);
5536 *height
= line
->GetSize().y
;
5538 *height
= dc
.GetCharHeight();
5540 // -1 means 'the start of the buffer'.
5543 pt
= pt
+ line
->GetPosition();
5548 // The final position in a paragraph is taken to mean the position
5549 // at the start of the next paragraph.
5550 if (index
== GetRange().GetEnd())
5552 wxRichTextParagraphLayoutBox
* parent
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
5553 wxASSERT( parent
!= NULL
);
5555 // Find the height at the next paragraph, if any
5556 wxRichTextLine
* line
= parent
->GetLineAtPosition(index
+ 1);
5559 *height
= line
->GetSize().y
;
5560 pt
= line
->GetAbsolutePosition();
5564 *height
= dc
.GetCharHeight();
5565 int indent
= ConvertTenthsMMToPixels(dc
, m_attributes
.GetLeftIndent());
5566 pt
= wxPoint(indent
, GetCachedSize().y
);
5572 if (index
< GetRange().GetStart() || index
> GetRange().GetEnd())
5575 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5578 wxRichTextLine
* line
= node
->GetData();
5579 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5580 if (index
>= lineRange
.GetStart() && index
<= lineRange
.GetEnd())
5582 // If this is the last point in the line, and we're forcing the
5583 // returned value to be the start of the next line, do the required
5585 if (index
== lineRange
.GetEnd() && forceLineStart
)
5587 if (node
->GetNext())
5589 wxRichTextLine
* nextLine
= node
->GetNext()->GetData();
5590 *height
= nextLine
->GetSize().y
;
5591 pt
= nextLine
->GetAbsolutePosition();
5596 pt
.y
= line
->GetPosition().y
+ GetPosition().y
;
5598 wxRichTextRange
r(lineRange
.GetStart(), index
);
5602 // We find the size of the line up to this point,
5603 // then we can add this size to the line start position and
5604 // paragraph start position to find the actual position.
5606 if (GetRangeSize(r
, rangeSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, line
->GetPosition()+ GetPosition()))
5608 pt
.x
= line
->GetPosition().x
+ GetPosition().x
+ rangeSize
.x
;
5609 *height
= line
->GetSize().y
;
5616 node
= node
->GetNext();
5622 /// Hit-testing: returns a flag indicating hit test details, plus
5623 /// information about position
5624 int wxRichTextParagraph::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
5627 return wxRICHTEXT_HITTEST_NONE
;
5629 // If we're in the top-level container, then we can return
5630 // a suitable hit test code even if the point is outside the container area,
5631 // so that we can position the caret sensibly even if we don't
5632 // click on valid content. If we're not at the top-level, and the point
5633 // is not within this paragraph object, then we don't want to stop more
5634 // precise hit-testing from working prematurely, so return immediately.
5635 // NEW STRATEGY: use the parent boundary to test whether we're in the
5636 // right region, not the paragraph, since the paragraph may be positioned
5637 // some way in from where the user clicks.
5640 wxRichTextObject
* tempObj
, *tempContextObj
;
5641 if (GetParent() && GetParent()->wxRichTextObject::HitTest(dc
, context
, pt
, tmpPos
, & tempObj
, & tempContextObj
, flags
) == wxRICHTEXT_HITTEST_NONE
)
5642 return wxRICHTEXT_HITTEST_NONE
;
5645 wxRichTextObjectList::compatibility_iterator objNode
= m_children
.GetFirst();
5648 wxRichTextObject
* child
= objNode
->GetData();
5649 // Don't recurse if we have wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS,
5650 // and also, if this seems composite but actually is marked as atomic,
5652 if (child
->IsTopLevel() && ((flags
& wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS
) == 0) &&
5653 (! (((flags
& wxRICHTEXT_HITTEST_HONOUR_ATOMIC
) != 0) && child
->IsAtomic())))
5656 int hitTest
= child
->HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
);
5657 if (hitTest
!= wxRICHTEXT_HITTEST_NONE
)
5662 objNode
= objNode
->GetNext();
5665 wxPoint paraPos
= GetPosition();
5667 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5670 wxRichTextLine
* line
= node
->GetData();
5671 wxPoint linePos
= paraPos
+ line
->GetPosition();
5672 wxSize lineSize
= line
->GetSize();
5673 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5675 if (pt
.y
<= linePos
.y
+ lineSize
.y
)
5677 if (pt
.x
< linePos
.x
)
5679 textPosition
= lineRange
.GetStart();
5680 *obj
= FindObjectAtPosition(textPosition
);
5681 *contextObj
= GetContainer();
5682 return wxRICHTEXT_HITTEST_BEFORE
|wxRICHTEXT_HITTEST_OUTSIDE
;
5684 else if (pt
.x
>= (linePos
.x
+ lineSize
.x
))
5686 textPosition
= lineRange
.GetEnd();
5687 *obj
= FindObjectAtPosition(textPosition
);
5688 *contextObj
= GetContainer();
5689 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
;
5693 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5694 wxArrayInt partialExtents
;
5699 // This calculates the partial text extents
5700 GetRangeSize(lineRange
, paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, linePos
, & partialExtents
);
5702 int lastX
= linePos
.x
;
5704 for (i
= 0; i
< partialExtents
.GetCount(); i
++)
5706 int nextX
= partialExtents
[i
] + linePos
.x
;
5708 if (pt
.x
>= lastX
&& pt
.x
<= nextX
)
5710 textPosition
= i
+ lineRange
.GetStart(); // minus 1?
5712 *obj
= FindObjectAtPosition(textPosition
);
5713 *contextObj
= GetContainer();
5715 // So now we know it's between i-1 and i.
5716 // Let's see if we can be more precise about
5717 // which side of the position it's on.
5719 int midPoint
= (nextX
+ lastX
)/2;
5720 if (pt
.x
>= midPoint
)
5721 return wxRICHTEXT_HITTEST_AFTER
;
5723 return wxRICHTEXT_HITTEST_BEFORE
;
5730 int lastX
= linePos
.x
;
5731 for (i
= lineRange
.GetStart(); i
<= lineRange
.GetEnd(); i
++)
5736 wxRichTextRange
rangeToUse(lineRange
.GetStart(), i
);
5738 GetRangeSize(rangeToUse
, childSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, linePos
);
5740 int nextX
= childSize
.x
+ linePos
.x
;
5742 if (pt
.x
>= lastX
&& pt
.x
<= nextX
)
5746 *obj
= FindObjectAtPosition(textPosition
);
5747 *contextObj
= GetContainer();
5749 // So now we know it's between i-1 and i.
5750 // Let's see if we can be more precise about
5751 // which side of the position it's on.
5753 int midPoint
= (nextX
+ lastX
)/2;
5754 if (pt
.x
>= midPoint
)
5755 return wxRICHTEXT_HITTEST_AFTER
;
5757 return wxRICHTEXT_HITTEST_BEFORE
;
5768 node
= node
->GetNext();
5771 return wxRICHTEXT_HITTEST_NONE
;
5774 /// Split an object at this position if necessary, and return
5775 /// the previous object, or NULL if inserting at beginning.
5776 wxRichTextObject
* wxRichTextParagraph::SplitAt(long pos
, wxRichTextObject
** previousObject
)
5778 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5781 wxRichTextObject
* child
= node
->GetData();
5783 if (pos
== child
->GetRange().GetStart())
5787 if (node
->GetPrevious())
5788 *previousObject
= node
->GetPrevious()->GetData();
5790 *previousObject
= NULL
;
5796 if (child
->GetRange().Contains(pos
))
5798 // This should create a new object, transferring part of
5799 // the content to the old object and the rest to the new object.
5800 wxRichTextObject
* newObject
= child
->DoSplit(pos
);
5802 // If we couldn't split this object, just insert in front of it.
5805 // Maybe this is an empty string, try the next one
5810 // Insert the new object after 'child'
5811 if (node
->GetNext())
5812 m_children
.Insert(node
->GetNext(), newObject
);
5814 m_children
.Append(newObject
);
5815 newObject
->SetParent(this);
5818 *previousObject
= child
;
5824 node
= node
->GetNext();
5827 *previousObject
= NULL
;
5831 /// Move content to a list from obj on
5832 void wxRichTextParagraph::MoveToList(wxRichTextObject
* obj
, wxList
& list
)
5834 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(obj
);
5837 wxRichTextObject
* child
= node
->GetData();
5840 wxRichTextObjectList::compatibility_iterator oldNode
= node
;
5842 node
= node
->GetNext();
5844 m_children
.DeleteNode(oldNode
);
5848 /// Add content back from list
5849 void wxRichTextParagraph::MoveFromList(wxList
& list
)
5851 for (wxList::compatibility_iterator node
= list
.GetFirst(); node
; node
= node
->GetNext())
5853 AppendChild((wxRichTextObject
*) node
->GetData());
5858 void wxRichTextParagraph::CalculateRange(long start
, long& end
)
5860 wxRichTextCompositeObject::CalculateRange(start
, end
);
5862 // Add one for end of paragraph
5865 m_range
.SetRange(start
, end
);
5868 /// Find the object at the given position
5869 wxRichTextObject
* wxRichTextParagraph::FindObjectAtPosition(long position
)
5871 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5874 wxRichTextObject
* obj
= node
->GetData();
5875 if (obj
->GetRange().Contains(position
) ||
5876 obj
->GetRange().GetStart() == position
||
5877 obj
->GetRange().GetEnd() == position
)
5880 node
= node
->GetNext();
5885 /// Get the plain text searching from the start or end of the range.
5886 /// The resulting string may be shorter than the range given.
5887 bool wxRichTextParagraph::GetContiguousPlainText(wxString
& text
, const wxRichTextRange
& range
, bool fromStart
)
5889 text
= wxEmptyString
;
5893 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5896 wxRichTextObject
* obj
= node
->GetData();
5897 if (!obj
->GetRange().IsOutside(range
))
5899 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5902 text
+= textObj
->GetTextForRange(range
);
5910 node
= node
->GetNext();
5915 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetLast();
5918 wxRichTextObject
* obj
= node
->GetData();
5919 if (!obj
->GetRange().IsOutside(range
))
5921 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5924 text
= textObj
->GetTextForRange(range
) + text
;
5928 text
= wxT(" ") + text
;
5932 node
= node
->GetPrevious();
5939 /// Find a suitable wrap position.
5940 bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange
& range
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int availableSpace
, long& wrapPosition
, wxArrayInt
* partialExtents
)
5942 if (range
.GetLength() <= 0)
5945 // Find the first position where the line exceeds the available space.
5947 long breakPosition
= range
.GetEnd();
5949 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5950 if (partialExtents
&& partialExtents
->GetCount() >= (size_t) (GetRange().GetLength()-1)) // the final position in a paragraph is the newline
5954 if (range
.GetStart() > GetRange().GetStart())
5955 widthBefore
= (*partialExtents
)[range
.GetStart() - GetRange().GetStart() - 1];
5960 for (i
= (size_t) range
.GetStart(); i
<= (size_t) range
.GetEnd(); i
++)
5962 int widthFromStartOfThisRange
= (*partialExtents
)[i
- GetRange().GetStart()] - widthBefore
;
5964 if (widthFromStartOfThisRange
> availableSpace
)
5966 breakPosition
= i
-1;
5974 // Binary chop for speed
5975 long minPos
= range
.GetStart();
5976 long maxPos
= range
.GetEnd();
5979 if (minPos
== maxPos
)
5982 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
5984 if (sz
.x
> availableSpace
)
5985 breakPosition
= minPos
- 1;
5988 else if ((maxPos
- minPos
) == 1)
5991 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
5993 if (sz
.x
> availableSpace
)
5994 breakPosition
= minPos
- 1;
5997 GetRangeSize(wxRichTextRange(range
.GetStart(), maxPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
5998 if (sz
.x
> availableSpace
)
5999 breakPosition
= maxPos
-1;
6005 long nextPos
= minPos
+ ((maxPos
- minPos
) / 2);
6008 GetRangeSize(wxRichTextRange(range
.GetStart(), nextPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
6010 if (sz
.x
> availableSpace
)
6022 // Now we know the last position on the line.
6023 // Let's try to find a word break.
6026 if (GetContiguousPlainText(plainText
, wxRichTextRange(range
.GetStart(), breakPosition
), false))
6028 int newLinePos
= plainText
.Find(wxRichTextLineBreakChar
);
6029 if (newLinePos
!= wxNOT_FOUND
)
6031 breakPosition
= wxMax(0, range
.GetStart() + newLinePos
);
6035 int spacePos
= plainText
.Find(wxT(' '), true);
6036 int tabPos
= plainText
.Find(wxT('\t'), true);
6037 int pos
= wxMax(spacePos
, tabPos
);
6038 if (pos
!= wxNOT_FOUND
)
6040 int positionsFromEndOfString
= plainText
.length() - pos
- 1;
6041 breakPosition
= breakPosition
- positionsFromEndOfString
;
6046 wrapPosition
= breakPosition
;
6051 /// Get the bullet text for this paragraph.
6052 wxString
wxRichTextParagraph::GetBulletText()
6054 if (GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
||
6055 (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
))
6056 return wxEmptyString
;
6058 int number
= GetAttributes().GetBulletNumber();
6061 if ((GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ARABIC
) || (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
))
6063 text
.Printf(wxT("%d"), number
);
6065 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_UPPER
)
6067 // TODO: Unicode, and also check if number > 26
6068 text
.Printf(wxT("%c"), (wxChar
) (number
+64));
6070 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_LOWER
)
6072 // TODO: Unicode, and also check if number > 26
6073 text
.Printf(wxT("%c"), (wxChar
) (number
+96));
6075 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER
)
6077 text
= wxRichTextDecimalToRoman(number
);
6079 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER
)
6081 text
= wxRichTextDecimalToRoman(number
);
6084 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
)
6086 text
= GetAttributes().GetBulletText();
6089 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
)
6091 // The outline style relies on the text being computed statically,
6092 // since it depends on other levels points (e.g. 1.2.1.1). So normally the bullet text
6093 // should be stored in the attributes; if not, just use the number for this
6094 // level, as previously computed.
6095 if (!GetAttributes().GetBulletText().IsEmpty())
6096 text
= GetAttributes().GetBulletText();
6099 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PARENTHESES
)
6101 text
= wxT("(") + text
+ wxT(")");
6103 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_RIGHT_PARENTHESIS
)
6105 text
= text
+ wxT(")");
6108 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PERIOD
)
6116 /// Allocate or reuse a line object
6117 wxRichTextLine
* wxRichTextParagraph::AllocateLine(int pos
)
6119 if (pos
< (int) m_cachedLines
.GetCount())
6121 wxRichTextLine
* line
= m_cachedLines
.Item(pos
)->GetData();
6127 wxRichTextLine
* line
= new wxRichTextLine(this);
6128 m_cachedLines
.Append(line
);
6133 /// Clear remaining unused line objects, if any
6134 bool wxRichTextParagraph::ClearUnusedLines(int lineCount
)
6136 int cachedLineCount
= m_cachedLines
.GetCount();
6137 if ((int) cachedLineCount
> lineCount
)
6139 for (int i
= 0; i
< (int) (cachedLineCount
- lineCount
); i
++)
6141 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetLast();
6142 wxRichTextLine
* line
= node
->GetData();
6143 m_cachedLines
.Erase(node
);
6150 /// Get combined attributes of the base style, paragraph style and character style. We use this to dynamically
6151 /// retrieve the actual style.
6152 wxRichTextAttr
wxRichTextParagraph::GetCombinedAttributes(const wxRichTextAttr
& contentStyle
, bool includingBoxAttr
) const
6154 wxRichTextAttr attr
;
6155 wxRichTextParagraphLayoutBox
* buf
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
6158 attr
= buf
->GetBasicStyle();
6159 if (!includingBoxAttr
)
6161 attr
.GetTextBoxAttr().Reset();
6162 // The background colour will be painted by the container, and we don't
6163 // want to unnecessarily overwrite the background when we're drawing text
6164 // because this may erase the guideline (which appears just under the text
6165 // if there's no padding).
6166 attr
.SetFlags(attr
.GetFlags() & ~wxTEXT_ATTR_BACKGROUND_COLOUR
);
6168 wxRichTextApplyStyle(attr
, GetAttributes());
6171 attr
= GetAttributes();
6173 wxRichTextApplyStyle(attr
, contentStyle
);
6177 /// Get combined attributes of the base style and paragraph style.
6178 wxRichTextAttr
wxRichTextParagraph::GetCombinedAttributes(bool includingBoxAttr
) const
6180 wxRichTextAttr attr
;
6181 wxRichTextParagraphLayoutBox
* buf
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
6184 attr
= buf
->GetBasicStyle();
6185 if (!includingBoxAttr
)
6186 attr
.GetTextBoxAttr().Reset();
6187 wxRichTextApplyStyle(attr
, GetAttributes());
6190 attr
= GetAttributes();
6195 // Create default tabstop array
6196 void wxRichTextParagraph::InitDefaultTabs()
6198 // create a default tab list at 10 mm each.
6199 for (int i
= 0; i
< 20; ++i
)
6201 sm_defaultTabs
.Add(i
*100);
6205 // Clear default tabstop array
6206 void wxRichTextParagraph::ClearDefaultTabs()
6208 sm_defaultTabs
.Clear();
6211 void wxRichTextParagraph::LayoutFloat(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
, wxRichTextFloatCollector
* floatCollector
)
6213 wxRichTextObjectList::compatibility_iterator node
= GetChildren().GetFirst();
6216 wxRichTextObject
* anchored
= node
->GetData();
6217 if (anchored
&& anchored
->IsFloating() && !floatCollector
->HasFloat(anchored
))
6220 wxRichTextAttr
parentAttr(GetAttributes());
6221 context
.ApplyVirtualAttributes(parentAttr
, this);
6224 wxRect availableSpace
= GetParent()->GetAvailableContentArea(dc
, context
, rect
);
6226 anchored
->LayoutToBestSize(dc
, context
, GetBuffer(),
6227 parentAttr
, anchored
->GetAttributes(),
6228 parentRect
, availableSpace
,
6230 wxSize size
= anchored
->GetCachedSize();
6234 anchored
->GetRangeSize(anchored
->GetRange(), size
, descent
, dc
, context
, style
);
6238 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().IsValid())
6240 offsetY
= anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetValue();
6241 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
6243 offsetY
= ConvertTenthsMMToPixels(dc
, offsetY
);
6247 int pos
= floatCollector
->GetFitPosition(anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode(), rect
.y
+ offsetY
, size
.y
);
6249 /* Update the offset */
6250 int newOffsetY
= pos
- rect
.y
;
6251 if (newOffsetY
!= offsetY
)
6253 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
6254 newOffsetY
= ConvertPixelsToTenthsMM(dc
, newOffsetY
);
6255 anchored
->GetAttributes().GetTextBoxAttr().GetTop().SetValue(newOffsetY
);
6258 if (anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT
)
6260 else if (anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT
)
6261 x
= rect
.x
+ rect
.width
- size
.x
;
6263 //anchored->SetPosition(wxPoint(x, pos));
6264 anchored
->Move(wxPoint(x
, pos
)); // should move children
6265 anchored
->SetCachedSize(size
);
6266 floatCollector
->CollectFloat(this, anchored
);
6269 node
= node
->GetNext();
6273 // Get the first position from pos that has a line break character.
6274 long wxRichTextParagraph::GetFirstLineBreakPosition(long pos
)
6276 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
6279 wxRichTextObject
* obj
= node
->GetData();
6280 if (pos
>= obj
->GetRange().GetStart() && pos
<= obj
->GetRange().GetEnd())
6282 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
6285 long breakPos
= textObj
->GetFirstLineBreakPosition(pos
);
6290 node
= node
->GetNext();
6297 * This object represents a line in a paragraph, and stores
6298 * offsets from the start of the paragraph representing the
6299 * start and end positions of the line.
6302 wxRichTextLine::wxRichTextLine(wxRichTextParagraph
* parent
)
6308 void wxRichTextLine::Init(wxRichTextParagraph
* parent
)
6311 m_range
.SetRange(-1, -1);
6312 m_pos
= wxPoint(0, 0);
6313 m_size
= wxSize(0, 0);
6315 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6316 m_objectSizes
.Clear();
6321 void wxRichTextLine::Copy(const wxRichTextLine
& obj
)
6323 m_range
= obj
.m_range
;
6324 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6325 m_objectSizes
= obj
.m_objectSizes
;
6329 /// Get the absolute object position
6330 wxPoint
wxRichTextLine::GetAbsolutePosition() const
6332 return m_parent
->GetPosition() + m_pos
;
6335 /// Get the absolute range
6336 wxRichTextRange
wxRichTextLine::GetAbsoluteRange() const
6338 wxRichTextRange
range(m_range
.GetStart() + m_parent
->GetRange().GetStart(), 0);
6339 range
.SetEnd(range
.GetStart() + m_range
.GetLength()-1);
6344 * wxRichTextPlainText
6345 * This object represents a single piece of text.
6348 IMPLEMENT_DYNAMIC_CLASS(wxRichTextPlainText
, wxRichTextObject
)
6350 wxRichTextPlainText::wxRichTextPlainText(const wxString
& text
, wxRichTextObject
* parent
, wxRichTextAttr
* style
):
6351 wxRichTextObject(parent
)
6354 SetAttributes(*style
);
6359 #define USE_KERNING_FIX 1
6361 // If insufficient tabs are defined, this is the tab width used
6362 #define WIDTH_FOR_DEFAULT_TABS 50
6365 bool wxRichTextPlainText::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int WXUNUSED(style
))
6367 wxRichTextParagraph
* para
= wxDynamicCast(GetParent(), wxRichTextParagraph
);
6368 wxASSERT (para
!= NULL
);
6370 wxRichTextAttr
textAttr(para
? para
->GetCombinedAttributes(GetAttributes(), false /* no box attributes */) : GetAttributes());
6371 context
.ApplyVirtualAttributes(textAttr
, this);
6373 // Let's make the assumption for now that for content in a paragraph, including
6374 // text, we never have a discontinuous selection. So we only deal with a
6376 wxRichTextRange selectionRange
;
6377 if (selection
.IsValid())
6379 wxRichTextRangeArray selectionRanges
= selection
.GetSelectionForObject(this);
6380 if (selectionRanges
.GetCount() > 0)
6381 selectionRange
= selectionRanges
[0];
6383 selectionRange
= wxRICHTEXT_NO_SELECTION
;
6386 selectionRange
= wxRICHTEXT_NO_SELECTION
;
6388 int offset
= GetRange().GetStart();
6390 wxString str
= m_text
;
6391 if (context
.HasVirtualText(this))
6393 if (!context
.GetVirtualText(this, str
) || str
.Length() != m_text
.Length())
6397 // Replace line break characters with spaces
6398 wxString toRemove
= wxRichTextLineBreakChar
;
6399 str
.Replace(toRemove
, wxT(" "));
6400 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS
|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS
)))
6403 long len
= range
.GetLength();
6404 wxString stringChunk
= str
.Mid(range
.GetStart() - offset
, (size_t) len
);
6406 // Test for the optimized situations where all is selected, or none
6409 wxFont
textFont(GetBuffer()->GetFontTable().FindFont(textAttr
));
6410 wxCheckSetFont(dc
, textFont
);
6411 int charHeight
= dc
.GetCharHeight();
6414 if ( textFont
.IsOk() )
6416 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SMALL_CAPITALS
))
6418 textFont
.SetPointSize((int) (textFont
.GetPointSize()*0.75));
6419 wxCheckSetFont(dc
, textFont
);
6420 charHeight
= dc
.GetCharHeight();
6423 if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
) )
6425 if (textFont
.IsUsingSizeInPixels())
6427 double size
= static_cast<double>(textFont
.GetPixelSize().y
) / wxSCRIPT_MUL_FACTOR
;
6428 textFont
.SetPixelSize(wxSize(0, static_cast<int>(size
)));
6434 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6435 textFont
.SetPointSize(static_cast<int>(size
));
6439 wxCheckSetFont(dc
, textFont
);
6441 else if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) )
6443 if (textFont
.IsUsingSizeInPixels())
6445 double size
= static_cast<double>(textFont
.GetPixelSize().y
) / wxSCRIPT_MUL_FACTOR
;
6446 textFont
.SetPixelSize(wxSize(0, static_cast<int>(size
)));
6448 int sub_height
= static_cast<int>(static_cast<double>(charHeight
) / wxSCRIPT_MUL_FACTOR
);
6449 y
= rect
.y
+ (rect
.height
- sub_height
+ (descent
- m_descent
));
6453 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6454 textFont
.SetPointSize(static_cast<int>(size
));
6456 int sub_height
= static_cast<int>(static_cast<double>(charHeight
) / wxSCRIPT_MUL_FACTOR
);
6457 y
= rect
.y
+ (rect
.height
- sub_height
+ (descent
- m_descent
));
6459 wxCheckSetFont(dc
, textFont
);
6464 y
= rect
.y
+ (rect
.height
- charHeight
- (descent
- m_descent
));
6470 y
= rect
.y
+ (rect
.height
- charHeight
- (descent
- m_descent
));
6473 // TODO: new selection code
6475 // (a) All selected.
6476 if (selectionRange
.GetStart() <= range
.GetStart() && selectionRange
.GetEnd() >= range
.GetEnd())
6478 DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, true);
6480 // (b) None selected.
6481 else if (selectionRange
.GetEnd() < range
.GetStart() || selectionRange
.GetStart() > range
.GetEnd())
6483 // Draw all unselected
6484 DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, false);
6488 // (c) Part selected, part not
6489 // Let's draw unselected chunk, selected chunk, then unselected chunk.
6491 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6493 // 1. Initial unselected chunk, if any, up until start of selection.
6494 if (selectionRange
.GetStart() > range
.GetStart() && selectionRange
.GetStart() <= range
.GetEnd())
6496 int r1
= range
.GetStart();
6497 int s1
= selectionRange
.GetStart()-1;
6498 int fragmentLen
= s1
- r1
+ 1;
6499 if (fragmentLen
< 0)
6501 wxLogDebug(wxT("Mid(%d, %d"), (int)(r1
- offset
), (int)fragmentLen
);
6503 wxString stringFragment
= str
.Mid(r1
- offset
, fragmentLen
);
6505 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false);
6508 if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
)
6510 // Compensate for kerning difference
6511 wxString
stringFragment2(str
.Mid(r1
- offset
, fragmentLen
+1));
6512 wxString
stringFragment3(str
.Mid(r1
- offset
+ fragmentLen
, 1));
6514 wxCoord w1
, h1
, w2
, h2
, w3
, h3
;
6515 dc
.GetTextExtent(stringFragment
, & w1
, & h1
);
6516 dc
.GetTextExtent(stringFragment2
, & w2
, & h2
);
6517 dc
.GetTextExtent(stringFragment3
, & w3
, & h3
);
6519 int kerningDiff
= (w1
+ w3
) - w2
;
6520 x
= x
- kerningDiff
;
6525 // 2. Selected chunk, if any.
6526 if (selectionRange
.GetEnd() >= range
.GetStart())
6528 int s1
= wxMax(selectionRange
.GetStart(), range
.GetStart());
6529 int s2
= wxMin(selectionRange
.GetEnd(), range
.GetEnd());
6531 int fragmentLen
= s2
- s1
+ 1;
6532 if (fragmentLen
< 0)
6534 wxLogDebug(wxT("Mid(%d, %d"), (int)(s1
- offset
), (int)fragmentLen
);
6536 wxString stringFragment
= str
.Mid(s1
- offset
, fragmentLen
);
6538 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, true);
6541 if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
)
6543 // Compensate for kerning difference
6544 wxString
stringFragment2(str
.Mid(s1
- offset
, fragmentLen
+1));
6545 wxString
stringFragment3(str
.Mid(s1
- offset
+ fragmentLen
, 1));
6547 wxCoord w1
, h1
, w2
, h2
, w3
, h3
;
6548 dc
.GetTextExtent(stringFragment
, & w1
, & h1
);
6549 dc
.GetTextExtent(stringFragment2
, & w2
, & h2
);
6550 dc
.GetTextExtent(stringFragment3
, & w3
, & h3
);
6552 int kerningDiff
= (w1
+ w3
) - w2
;
6553 x
= x
- kerningDiff
;
6558 // 3. Remaining unselected chunk, if any
6559 if (selectionRange
.GetEnd() < range
.GetEnd())
6561 int s2
= wxMin(selectionRange
.GetEnd()+1, range
.GetEnd());
6562 int r2
= range
.GetEnd();
6564 int fragmentLen
= r2
- s2
+ 1;
6565 if (fragmentLen
< 0)
6567 wxLogDebug(wxT("Mid(%d, %d"), (int)(s2
- offset
), (int)fragmentLen
);
6569 wxString stringFragment
= str
.Mid(s2
- offset
, fragmentLen
);
6571 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false);
6578 bool wxRichTextPlainText::DrawTabbedString(wxDC
& dc
, const wxRichTextAttr
& attr
, const wxRect
& rect
,wxString
& str
, wxCoord
& x
, wxCoord
& y
, bool selected
)
6580 bool hasTabs
= (str
.Find(wxT('\t')) != wxNOT_FOUND
);
6582 wxArrayInt tabArray
;
6586 if (attr
.GetTabs().IsEmpty())
6587 tabArray
= wxRichTextParagraph::GetDefaultTabs();
6589 tabArray
= attr
.GetTabs();
6590 tabCount
= tabArray
.GetCount();
6592 for (int i
= 0; i
< tabCount
; ++i
)
6594 int pos
= tabArray
[i
];
6595 pos
= ConvertTenthsMMToPixels(dc
, pos
);
6602 int nextTabPos
= -1;
6608 wxColour
highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
));
6609 wxColour
highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
));
6611 wxCheckSetBrush(dc
, wxBrush(highlightColour
));
6612 wxCheckSetPen(dc
, wxPen(highlightColour
));
6613 dc
.SetTextForeground(highlightTextColour
);
6614 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6618 dc
.SetTextForeground(attr
.GetTextColour());
6620 if (attr
.HasFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
) && attr
.GetBackgroundColour().IsOk())
6622 dc
.SetBackgroundMode(wxBRUSHSTYLE_SOLID
);
6623 dc
.SetTextBackground(attr
.GetBackgroundColour());
6626 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6629 wxCoord x_orig
= GetParent()->GetPosition().x
;
6632 // the string has a tab
6633 // break up the string at the Tab
6634 wxString stringChunk
= str
.BeforeFirst(wxT('\t'));
6635 str
= str
.AfterFirst(wxT('\t'));
6636 dc
.GetTextExtent(stringChunk
, & w
, & h
);
6638 bool not_found
= true;
6639 for (int i
= 0; i
< tabCount
&& not_found
; ++i
)
6641 nextTabPos
= tabArray
.Item(i
) + x_orig
;
6643 // Find the next tab position.
6644 // Even if we're at the end of the tab array, we must still draw the chunk.
6646 if (nextTabPos
> tabPos
|| (i
== (tabCount
- 1)))
6648 if (nextTabPos
<= tabPos
)
6650 int defaultTabWidth
= ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
);
6651 nextTabPos
= tabPos
+ defaultTabWidth
;
6658 wxRect
selRect(x
, rect
.y
, w
, rect
.GetHeight());
6659 dc
.DrawRectangle(selRect
);
6661 dc
.DrawText(stringChunk
, x
, y
);
6663 if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
))
6665 wxPen oldPen
= dc
.GetPen();
6666 wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1));
6667 dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5));
6668 wxCheckSetPen(dc
, oldPen
);
6674 hasTabs
= (str
.Find(wxT('\t')) != wxNOT_FOUND
);
6679 dc
.GetTextExtent(str
, & w
, & h
);
6682 wxRect
selRect(x
, rect
.y
, w
, rect
.GetHeight());
6683 dc
.DrawRectangle(selRect
);
6685 dc
.DrawText(str
, x
, y
);
6687 if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
))
6689 wxPen oldPen
= dc
.GetPen();
6690 wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1));
6691 dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5));
6692 wxCheckSetPen(dc
, oldPen
);
6701 /// Lay the item out
6702 bool wxRichTextPlainText::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& WXUNUSED(rect
), const wxRect
& WXUNUSED(parentRect
), int WXUNUSED(style
))
6704 // Only lay out if we haven't already cached the size
6706 GetRangeSize(GetRange(), m_size
, m_descent
, dc
, context
, 0, wxPoint(0, 0));
6708 // Eventually we want to have a reasonable estimate of minimum size.
6709 m_minSize
= wxSize(0, 0);
6714 void wxRichTextPlainText::Copy(const wxRichTextPlainText
& obj
)
6716 wxRichTextObject::Copy(obj
);
6718 m_text
= obj
.m_text
;
6721 /// Get/set the object size for the given range. Returns false if the range
6722 /// is invalid for this object.
6723 bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int WXUNUSED(flags
), wxPoint position
, wxArrayInt
* partialExtents
) const
6725 if (!range
.IsWithin(GetRange()))
6728 wxRichTextParagraph
* para
= wxDynamicCast(GetParent(), wxRichTextParagraph
);
6729 wxASSERT (para
!= NULL
);
6731 int relativeX
= position
.x
- GetParent()->GetPosition().x
;
6733 wxRichTextAttr
textAttr(para
? para
->GetCombinedAttributes(GetAttributes()) : GetAttributes());
6734 context
.ApplyVirtualAttributes(textAttr
, (wxRichTextObject
*) this);
6736 // Always assume unformatted text, since at this level we have no knowledge
6737 // of line breaks - and we don't need it, since we'll calculate size within
6738 // formatted text by doing it in chunks according to the line ranges
6740 bool bScript(false);
6741 wxFont
font(GetBuffer()->GetFontTable().FindFont(textAttr
));
6744 if ( textAttr
.HasTextEffects() && ( (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
)
6745 || (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) ) )
6747 wxFont textFont
= font
;
6748 if (textFont
.IsUsingSizeInPixels())
6750 double size
= static_cast<double>(textFont
.GetPixelSize().y
) / wxSCRIPT_MUL_FACTOR
;
6751 textFont
.SetPixelSize(wxSize(0, static_cast<int>(size
)));
6755 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6756 textFont
.SetPointSize(static_cast<int>(size
));
6758 wxCheckSetFont(dc
, textFont
);
6761 else if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SMALL_CAPITALS
))
6763 wxFont textFont
= font
;
6764 textFont
.SetPointSize((int) (textFont
.GetPointSize()*0.75));
6765 wxCheckSetFont(dc
, textFont
);
6770 wxCheckSetFont(dc
, font
);
6774 bool haveDescent
= false;
6775 int startPos
= range
.GetStart() - GetRange().GetStart();
6776 long len
= range
.GetLength();
6778 wxString
str(m_text
);
6779 if (context
.HasVirtualText(this))
6781 if (!context
.GetVirtualText(this, str
) || str
.Length() != m_text
.Length())
6785 wxString toReplace
= wxRichTextLineBreakChar
;
6786 str
.Replace(toReplace
, wxT(" "));
6788 wxString stringChunk
= str
.Mid(startPos
, (size_t) len
);
6790 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS
|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS
)))
6791 stringChunk
.MakeUpper();
6795 if (stringChunk
.Find(wxT('\t')) != wxNOT_FOUND
)
6797 // the string has a tab
6798 wxArrayInt tabArray
;
6799 if (textAttr
.GetTabs().IsEmpty())
6800 tabArray
= wxRichTextParagraph::GetDefaultTabs();
6802 tabArray
= textAttr
.GetTabs();
6804 int tabCount
= tabArray
.GetCount();
6806 for (int i
= 0; i
< tabCount
; ++i
)
6808 int pos
= tabArray
[i
];
6809 pos
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, pos
);
6813 int nextTabPos
= -1;
6815 while (stringChunk
.Find(wxT('\t')) >= 0)
6817 int absoluteWidth
= 0;
6819 // the string has a tab
6820 // break up the string at the Tab
6821 wxString stringFragment
= stringChunk
.BeforeFirst(wxT('\t'));
6822 stringChunk
= stringChunk
.AfterFirst(wxT('\t'));
6827 if (partialExtents
->GetCount() > 0)
6828 oldWidth
= (*partialExtents
)[partialExtents
->GetCount()-1];
6832 // Add these partial extents
6834 dc
.GetPartialTextExtents(stringFragment
, p
);
6836 for (j
= 0; j
< p
.GetCount(); j
++)
6837 partialExtents
->Add(oldWidth
+ p
[j
]);
6839 if (partialExtents
->GetCount() > 0)
6840 absoluteWidth
= (*partialExtents
)[(*partialExtents
).GetCount()-1] + relativeX
;
6842 absoluteWidth
= relativeX
;
6846 dc
.GetTextExtent(stringFragment
, & w
, & h
);
6848 absoluteWidth
= width
+ relativeX
;
6852 bool notFound
= true;
6853 for (int i
= 0; i
< tabCount
&& notFound
; ++i
)
6855 nextTabPos
= tabArray
.Item(i
);
6857 // Find the next tab position.
6858 // Even if we're at the end of the tab array, we must still process the chunk.
6860 if (nextTabPos
> absoluteWidth
|| (i
== (tabCount
- 1)))
6862 if (nextTabPos
<= absoluteWidth
)
6864 int defaultTabWidth
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
);
6865 nextTabPos
= absoluteWidth
+ defaultTabWidth
;
6869 width
= nextTabPos
- relativeX
;
6872 partialExtents
->Add(width
);
6878 if (!stringChunk
.IsEmpty())
6883 if (partialExtents
->GetCount() > 0)
6884 oldWidth
= (*partialExtents
)[partialExtents
->GetCount()-1];
6888 // Add these partial extents
6890 dc
.GetPartialTextExtents(stringChunk
, p
);
6892 for (j
= 0; j
< p
.GetCount(); j
++)
6893 partialExtents
->Add(oldWidth
+ p
[j
]);
6897 dc
.GetTextExtent(stringChunk
, & w
, & h
, & descent
);
6905 int charHeight
= dc
.GetCharHeight();
6906 if ((*partialExtents
).GetCount() > 0)
6907 w
= (*partialExtents
)[partialExtents
->GetCount()-1];
6910 size
= wxSize(w
, charHeight
);
6914 size
= wxSize(width
, dc
.GetCharHeight());
6918 dc
.GetTextExtent(wxT("X"), & w
, & h
, & descent
);
6926 /// Do a split, returning an object containing the second part, and setting
6927 /// the first part in 'this'.
6928 wxRichTextObject
* wxRichTextPlainText::DoSplit(long pos
)
6930 long index
= pos
- GetRange().GetStart();
6932 if (index
< 0 || index
>= (int) m_text
.length())
6935 wxString firstPart
= m_text
.Mid(0, index
);
6936 wxString secondPart
= m_text
.Mid(index
);
6940 wxRichTextPlainText
* newObject
= new wxRichTextPlainText(secondPart
);
6941 newObject
->SetAttributes(GetAttributes());
6942 newObject
->SetProperties(GetProperties());
6944 newObject
->SetRange(wxRichTextRange(pos
, GetRange().GetEnd()));
6945 GetRange().SetEnd(pos
-1);
6951 void wxRichTextPlainText::CalculateRange(long start
, long& end
)
6953 end
= start
+ m_text
.length() - 1;
6954 m_range
.SetRange(start
, end
);
6958 bool wxRichTextPlainText::DeleteRange(const wxRichTextRange
& range
)
6960 wxRichTextRange r
= range
;
6962 r
.LimitTo(GetRange());
6964 if (r
.GetStart() == GetRange().GetStart() && r
.GetEnd() == GetRange().GetEnd())
6970 long startIndex
= r
.GetStart() - GetRange().GetStart();
6971 long len
= r
.GetLength();
6973 m_text
= m_text
.Mid(0, startIndex
) + m_text
.Mid(startIndex
+len
);
6977 /// Get text for the given range.
6978 wxString
wxRichTextPlainText::GetTextForRange(const wxRichTextRange
& range
) const
6980 wxRichTextRange r
= range
;
6982 r
.LimitTo(GetRange());
6984 long startIndex
= r
.GetStart() - GetRange().GetStart();
6985 long len
= r
.GetLength();
6987 return m_text
.Mid(startIndex
, len
);
6990 /// Returns true if this object can merge itself with the given one.
6991 bool wxRichTextPlainText::CanMerge(wxRichTextObject
* object
, wxRichTextDrawingContext
& context
) const
6994 if (!context
.GetVirtualAttributesEnabled())
6996 return object
->GetClassInfo() == wxCLASSINFO(wxRichTextPlainText
) &&
6997 (m_text
.empty() || (wxTextAttrEq(GetAttributes(), object
->GetAttributes()) && m_properties
== object
->GetProperties()));
7001 wxRichTextPlainText
* otherObj
= wxDynamicCast(object
, wxRichTextPlainText
);
7002 if (!otherObj
|| m_text
.empty())
7005 if (!wxTextAttrEq(GetAttributes(), object
->GetAttributes()) || !(m_properties
== object
->GetProperties()))
7008 // Check if differing virtual attributes makes it impossible to merge
7011 bool hasVirtualAttr1
= context
.HasVirtualAttributes((wxRichTextObject
*) this);
7012 bool hasVirtualAttr2
= context
.HasVirtualAttributes((wxRichTextObject
*) object
);
7013 if (!hasVirtualAttr1
&& !hasVirtualAttr2
)
7015 else if (hasVirtualAttr1
!= hasVirtualAttr2
)
7019 wxRichTextAttr virtualAttr1
= context
.GetVirtualAttributes((wxRichTextObject
*) this);
7020 wxRichTextAttr virtualAttr2
= context
.GetVirtualAttributes((wxRichTextObject
*) object
);
7021 return virtualAttr1
== virtualAttr2
;
7026 /// Returns true if this object merged itself with the given one.
7027 /// The calling code will then delete the given object.
7028 bool wxRichTextPlainText::Merge(wxRichTextObject
* object
, wxRichTextDrawingContext
& WXUNUSED(context
))
7030 wxRichTextPlainText
* textObject
= wxDynamicCast(object
, wxRichTextPlainText
);
7031 wxASSERT( textObject
!= NULL
);
7035 m_text
+= textObject
->GetText();
7036 wxRichTextApplyStyle(m_attributes
, textObject
->GetAttributes());
7043 bool wxRichTextPlainText::CanSplit(wxRichTextDrawingContext
& context
) const
7045 // If this object has any virtual attributes at all, whether for the whole object
7046 // or individual ones, we should try splitting it by calling Split.
7047 // Must be more than one character in order to be able to split.
7048 return m_text
.Length() > 1 && context
.HasVirtualAttributes((wxRichTextObject
*) this);
7051 wxRichTextObject
* wxRichTextPlainText::Split(wxRichTextDrawingContext
& context
)
7053 int count
= context
.GetVirtualSubobjectAttributesCount(this);
7054 if (count
> 0 && GetParent())
7056 wxRichTextCompositeObject
* parent
= wxDynamicCast(GetParent(), wxRichTextCompositeObject
);
7057 wxRichTextObjectList::compatibility_iterator node
= parent
->GetChildren().Find(this);
7060 const wxRichTextAttr emptyAttr
;
7061 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
7063 wxArrayInt positions
;
7064 wxRichTextAttrArray attributes
;
7065 if (context
.GetVirtualSubobjectAttributes(this, positions
, attributes
) && positions
.GetCount() > 0)
7067 wxASSERT(positions
.GetCount() == attributes
.GetCount());
7069 // We will gather up runs of text with the same virtual attributes
7071 int len
= m_text
.Length();
7074 // runStart and runEnd represent the accumulated run with a consistent attribute
7075 // that hasn't yet been appended
7078 wxRichTextAttr currentAttr
;
7079 wxString text
= m_text
;
7080 wxRichTextPlainText
* lastPlainText
= this;
7082 for (i
= 0; i
< (int) positions
.GetCount(); i
++)
7084 int pos
= positions
[i
];
7085 wxASSERT(pos
>= 0 && pos
< len
);
7086 if (pos
>= 0 && pos
< len
)
7088 const wxRichTextAttr
& attr
= attributes
[i
];
7095 // Check if there was a gap from the last known attribute and this.
7096 // In that case, we need to do something with the span of non-attributed text.
7097 else if ((pos
-1) > runEnd
)
7101 // We hadn't processed anything previously, so the previous run is from the text start
7102 // to just before this position. The current attribute remains empty.
7108 // If the previous attribute matches the gap's attribute (i.e., no attributes)
7109 // then just extend the run.
7110 if (currentAttr
.IsDefault())
7116 // We need to add an object, or reuse the existing one.
7119 lastPlainText
= this;
7120 SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7124 wxRichTextPlainText
* obj
= new wxRichTextPlainText
;
7125 lastPlainText
= obj
;
7126 obj
->SetAttributes(GetAttributes());
7127 obj
->SetProperties(GetProperties());
7128 obj
->SetParent(parent
);
7130 obj
->SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7132 parent
->GetChildren().Insert(next
, obj
);
7134 parent
->GetChildren().Append(obj
);
7137 runStart
= runEnd
+1;
7140 currentAttr
= emptyAttr
;
7145 wxASSERT(runEnd
== pos
-1);
7147 // Now we only have to deal with the previous run
7148 if (currentAttr
== attr
)
7150 // If we still have the same attributes, then we
7151 // simply increase the run size.
7158 // We need to add an object, or reuse the existing one.
7161 lastPlainText
= this;
7162 SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7166 wxRichTextPlainText
* obj
= new wxRichTextPlainText
;
7167 lastPlainText
= obj
;
7168 obj
->SetAttributes(GetAttributes());
7169 obj
->SetProperties(GetProperties());
7170 obj
->SetParent(parent
);
7172 obj
->SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7174 parent
->GetChildren().Insert(next
, obj
);
7176 parent
->GetChildren().Append(obj
);
7188 // We may still have a run to add, and possibly a no-attribute text fragment after that.
7189 // If the whole string was already a single attribute (the run covers the whole string), don't split.
7190 if ((runStart
!= -1) && !(runStart
== 0 && runEnd
== (len
-1)))
7192 // If the current attribute is empty, merge the run with the next fragment
7193 // which by definition (because it's not specified) has empty attributes.
7194 if (currentAttr
.IsDefault())
7197 if (runEnd
< (len
-1))
7199 // We need to add an object, or reuse the existing one.
7202 lastPlainText
= this;
7203 SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7207 wxRichTextPlainText
* obj
= new wxRichTextPlainText
;
7208 lastPlainText
= obj
;
7209 obj
->SetAttributes(GetAttributes());
7210 obj
->SetProperties(GetProperties());
7211 obj
->SetParent(parent
);
7213 obj
->SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7215 parent
->GetChildren().Insert(next
, obj
);
7217 parent
->GetChildren().Append(obj
);
7220 runStart
= runEnd
+1;
7224 // Now the last, non-attributed fragment at the end, if any
7225 if ((runStart
< len
) && !(runStart
== 0 && runEnd
== (len
-1)))
7227 wxASSERT(runStart
!= 0);
7229 wxRichTextPlainText
* obj
= new wxRichTextPlainText
;
7230 obj
->SetAttributes(GetAttributes());
7231 obj
->SetProperties(GetProperties());
7232 obj
->SetParent(parent
);
7234 obj
->SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7236 parent
->GetChildren().Insert(next
, obj
);
7238 parent
->GetChildren().Append(obj
);
7240 lastPlainText
= obj
;
7244 return lastPlainText
;
7251 /// Dump to output stream for debugging
7252 void wxRichTextPlainText::Dump(wxTextOutputStream
& stream
)
7254 wxRichTextObject::Dump(stream
);
7255 stream
<< m_text
<< wxT("\n");
7258 /// Get the first position from pos that has a line break character.
7259 long wxRichTextPlainText::GetFirstLineBreakPosition(long pos
)
7262 int len
= m_text
.length();
7263 int startPos
= pos
- m_range
.GetStart();
7264 for (i
= startPos
; i
< len
; i
++)
7266 wxChar ch
= m_text
[i
];
7267 if (ch
== wxRichTextLineBreakChar
)
7269 return i
+ m_range
.GetStart();
7277 * This is a kind of box, used to represent the whole buffer
7280 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBuffer
, wxRichTextParagraphLayoutBox
)
7282 wxList
wxRichTextBuffer::sm_handlers
;
7283 wxList
wxRichTextBuffer::sm_drawingHandlers
;
7284 wxRichTextFieldTypeHashMap
wxRichTextBuffer::sm_fieldTypes
;
7285 wxRichTextRenderer
* wxRichTextBuffer::sm_renderer
= NULL
;
7286 int wxRichTextBuffer::sm_bulletRightMargin
= 20;
7287 float wxRichTextBuffer::sm_bulletProportion
= (float) 0.3;
7288 bool wxRichTextBuffer::sm_floatingLayoutMode
= true;
7291 void wxRichTextBuffer::Init()
7293 m_commandProcessor
= new wxCommandProcessor
;
7294 m_styleSheet
= NULL
;
7296 m_batchedCommandDepth
= 0;
7297 m_batchedCommand
= NULL
;
7301 m_dimensionScale
= 1.0;
7307 wxRichTextBuffer::~wxRichTextBuffer()
7309 delete m_commandProcessor
;
7310 delete m_batchedCommand
;
7313 ClearEventHandlers();
7316 void wxRichTextBuffer::ResetAndClearCommands()
7320 GetCommandProcessor()->ClearCommands();
7323 Invalidate(wxRICHTEXT_ALL
);
7326 void wxRichTextBuffer::Copy(const wxRichTextBuffer
& obj
)
7328 wxRichTextParagraphLayoutBox::Copy(obj
);
7330 m_styleSheet
= obj
.m_styleSheet
;
7331 m_modified
= obj
.m_modified
;
7332 m_batchedCommandDepth
= 0;
7333 if (m_batchedCommand
)
7334 delete m_batchedCommand
;
7335 m_batchedCommand
= NULL
;
7336 m_suppressUndo
= obj
.m_suppressUndo
;
7337 m_invalidRange
= obj
.m_invalidRange
;
7338 m_dimensionScale
= obj
.m_dimensionScale
;
7339 m_fontScale
= obj
.m_fontScale
;
7342 /// Push style sheet to top of stack
7343 bool wxRichTextBuffer::PushStyleSheet(wxRichTextStyleSheet
* styleSheet
)
7346 styleSheet
->InsertSheet(m_styleSheet
);
7348 SetStyleSheet(styleSheet
);
7353 /// Pop style sheet from top of stack
7354 wxRichTextStyleSheet
* wxRichTextBuffer::PopStyleSheet()
7358 wxRichTextStyleSheet
* oldSheet
= m_styleSheet
;
7359 m_styleSheet
= oldSheet
->GetNextSheet();
7368 /// Submit command to insert paragraphs
7369 bool wxRichTextBuffer::InsertParagraphsWithUndo(long pos
, const wxRichTextParagraphLayoutBox
& paragraphs
, wxRichTextCtrl
* ctrl
, int flags
)
7371 return ctrl
->GetFocusObject()->InsertParagraphsWithUndo(this, pos
, paragraphs
, ctrl
, flags
);
7374 /// Submit command to insert paragraphs
7375 bool wxRichTextParagraphLayoutBox::InsertParagraphsWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxRichTextParagraphLayoutBox
& paragraphs
, wxRichTextCtrl
* ctrl
, int WXUNUSED(flags
))
7377 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7379 action
->GetNewParagraphs() = paragraphs
;
7381 action
->SetPosition(pos
);
7383 wxRichTextRange range
= wxRichTextRange(pos
, pos
+ paragraphs
.GetOwnRange().GetEnd() - 1);
7384 if (!paragraphs
.GetPartialParagraph())
7385 range
.SetEnd(range
.GetEnd()+1);
7387 // Set the range we'll need to delete in Undo
7388 action
->SetRange(range
);
7390 buffer
->SubmitAction(action
);
7395 /// Submit command to insert the given text
7396 bool wxRichTextBuffer::InsertTextWithUndo(long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
, int flags
)
7398 return ctrl
->GetFocusObject()->InsertTextWithUndo(this, pos
, text
, ctrl
, flags
);
7401 /// Submit command to insert the given text
7402 bool wxRichTextParagraphLayoutBox::InsertTextWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
, int flags
)
7404 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7406 wxRichTextAttr
* p
= NULL
;
7407 wxRichTextAttr paraAttr
;
7408 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7410 // Get appropriate paragraph style
7411 paraAttr
= GetStyleForNewParagraph(buffer
, pos
, false, false);
7412 if (!paraAttr
.IsDefault())
7416 action
->GetNewParagraphs().AddParagraphs(text
, p
);
7418 int length
= action
->GetNewParagraphs().GetOwnRange().GetLength();
7420 if (!text
.empty() && text
.Last() != wxT('\n'))
7422 // Don't count the newline when undoing
7424 action
->GetNewParagraphs().SetPartialParagraph(true);
7426 else if (!text
.empty() && text
.Last() == wxT('\n'))
7429 action
->SetPosition(pos
);
7431 // Set the range we'll need to delete in Undo
7432 action
->SetRange(wxRichTextRange(pos
, pos
+ length
- 1));
7434 buffer
->SubmitAction(action
);
7439 /// Submit command to insert the given text
7440 bool wxRichTextBuffer::InsertNewlineWithUndo(long pos
, wxRichTextCtrl
* ctrl
, int flags
)
7442 return ctrl
->GetFocusObject()->InsertNewlineWithUndo(this, pos
, ctrl
, flags
);
7445 /// Submit command to insert the given text
7446 bool wxRichTextParagraphLayoutBox::InsertNewlineWithUndo(wxRichTextBuffer
* buffer
, long pos
, wxRichTextCtrl
* ctrl
, int flags
)
7448 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7450 wxRichTextAttr
* p
= NULL
;
7451 wxRichTextAttr paraAttr
;
7452 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7454 paraAttr
= GetStyleForNewParagraph(buffer
, pos
, false, true /* look for next paragraph style */);
7455 if (!paraAttr
.IsDefault())
7459 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7460 // Don't include box attributes such as margins
7461 attr
.GetTextBoxAttr().Reset();
7463 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(wxEmptyString
, this, & attr
);
7464 action
->GetNewParagraphs().AppendChild(newPara
);
7465 action
->GetNewParagraphs().UpdateRanges();
7466 action
->GetNewParagraphs().SetPartialParagraph(false);
7467 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
, false);
7471 newPara
->SetAttributes(*p
);
7473 if (flags
& wxRICHTEXT_INSERT_INTERACTIVE
)
7475 if (para
&& para
->GetRange().GetEnd() == pos
)
7478 // Now see if we need to number the paragraph.
7479 if (newPara
->GetAttributes().HasBulletNumber())
7481 wxRichTextAttr numberingAttr
;
7482 if (FindNextParagraphNumber(para
, numberingAttr
))
7483 wxRichTextApplyStyle(newPara
->GetAttributes(), (const wxRichTextAttr
&) numberingAttr
);
7487 action
->SetPosition(pos
);
7489 // Use the default character style
7490 if (!buffer
->GetDefaultStyle().IsDefault() && newPara
->GetChildren().GetFirst())
7492 // Check whether the default style merely reflects the paragraph/basic style,
7493 // in which case don't apply it.
7494 wxRichTextAttr
defaultStyle(buffer
->GetDefaultStyle());
7495 defaultStyle
.GetTextBoxAttr().Reset();
7496 wxRichTextAttr toApply
;
7499 wxRichTextAttr combinedAttr
= para
->GetCombinedAttributes(true /* include box attributes */);
7500 wxRichTextAttr newAttr
;
7501 // This filters out attributes that are accounted for by the current
7502 // paragraph/basic style
7503 wxRichTextApplyStyle(toApply
, defaultStyle
, & combinedAttr
);
7506 toApply
= defaultStyle
;
7508 if (!toApply
.IsDefault())
7509 newPara
->GetChildren().GetFirst()->GetData()->SetAttributes(toApply
);
7512 // Set the range we'll need to delete in Undo
7513 action
->SetRange(wxRichTextRange(pos1
, pos1
));
7515 buffer
->SubmitAction(action
);
7520 /// Submit command to insert the given image
7521 bool wxRichTextBuffer::InsertImageWithUndo(long pos
, const wxRichTextImageBlock
& imageBlock
, wxRichTextCtrl
* ctrl
, int flags
,
7522 const wxRichTextAttr
& textAttr
)
7524 return ctrl
->GetFocusObject()->InsertImageWithUndo(this, pos
, imageBlock
, ctrl
, flags
, textAttr
);
7527 /// Submit command to insert the given image
7528 bool wxRichTextParagraphLayoutBox::InsertImageWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxRichTextImageBlock
& imageBlock
,
7529 wxRichTextCtrl
* ctrl
, int flags
,
7530 const wxRichTextAttr
& textAttr
)
7532 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7534 wxRichTextAttr
* p
= NULL
;
7535 wxRichTextAttr paraAttr
;
7536 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7538 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
7539 if (!paraAttr
.IsDefault())
7543 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7545 // Don't include box attributes such as margins
7546 attr
.GetTextBoxAttr().Reset();
7548 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7550 newPara
->SetAttributes(*p
);
7552 wxRichTextImage
* imageObject
= new wxRichTextImage(imageBlock
, newPara
);
7553 newPara
->AppendChild(imageObject
);
7554 imageObject
->SetAttributes(textAttr
);
7555 action
->GetNewParagraphs().AppendChild(newPara
);
7556 action
->GetNewParagraphs().UpdateRanges();
7558 action
->GetNewParagraphs().SetPartialParagraph(true);
7560 action
->SetPosition(pos
);
7562 // Set the range we'll need to delete in Undo
7563 action
->SetRange(wxRichTextRange(pos
, pos
));
7565 buffer
->SubmitAction(action
);
7570 // Insert an object with no change of it
7571 wxRichTextObject
* wxRichTextBuffer::InsertObjectWithUndo(long pos
, wxRichTextObject
*object
, wxRichTextCtrl
* ctrl
, int flags
)
7573 return ctrl
->GetFocusObject()->InsertObjectWithUndo(this, pos
, object
, ctrl
, flags
);
7576 // Insert an object with no change of it
7577 wxRichTextObject
* wxRichTextParagraphLayoutBox::InsertObjectWithUndo(wxRichTextBuffer
* buffer
, long pos
, wxRichTextObject
*object
, wxRichTextCtrl
* ctrl
, int flags
)
7579 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Object"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7581 wxRichTextAttr
* p
= NULL
;
7582 wxRichTextAttr paraAttr
;
7583 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7585 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
7586 if (!paraAttr
.IsDefault())
7590 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7592 // Don't include box attributes such as margins
7593 attr
.GetTextBoxAttr().Reset();
7595 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7597 newPara
->SetAttributes(*p
);
7599 newPara
->AppendChild(object
);
7600 action
->GetNewParagraphs().AppendChild(newPara
);
7601 action
->GetNewParagraphs().UpdateRanges();
7603 action
->GetNewParagraphs().SetPartialParagraph(true);
7605 action
->SetPosition(pos
);
7607 // Set the range we'll need to delete in Undo
7608 action
->SetRange(wxRichTextRange(pos
, pos
));
7610 buffer
->SubmitAction(action
);
7612 wxRichTextObject
* obj
= GetLeafObjectAtPosition(pos
);
7616 wxRichTextField
* wxRichTextParagraphLayoutBox::InsertFieldWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxString
& fieldType
,
7617 const wxRichTextProperties
& properties
,
7618 wxRichTextCtrl
* ctrl
, int flags
,
7619 const wxRichTextAttr
& textAttr
)
7621 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Field"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7623 wxRichTextAttr
* p
= NULL
;
7624 wxRichTextAttr paraAttr
;
7625 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7627 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
7628 if (!paraAttr
.IsDefault())
7632 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7634 // Don't include box attributes such as margins
7635 attr
.GetTextBoxAttr().Reset();
7637 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7639 newPara
->SetAttributes(*p
);
7641 wxRichTextField
* fieldObject
= new wxRichTextField();
7642 fieldObject
->wxRichTextObject::SetProperties(properties
);
7643 fieldObject
->SetFieldType(fieldType
);
7644 fieldObject
->SetAttributes(textAttr
);
7645 newPara
->AppendChild(fieldObject
);
7646 action
->GetNewParagraphs().AppendChild(newPara
);
7647 action
->GetNewParagraphs().UpdateRanges();
7648 action
->GetNewParagraphs().SetPartialParagraph(true);
7649 action
->SetPosition(pos
);
7651 // Set the range we'll need to delete in Undo
7652 action
->SetRange(wxRichTextRange(pos
, pos
));
7654 buffer
->SubmitAction(action
);
7656 wxRichTextField
* obj
= wxDynamicCast(GetLeafObjectAtPosition(pos
), wxRichTextField
);
7660 /// Get the style that is appropriate for a new paragraph at this position.
7661 /// If the previous paragraph has a paragraph style name, look up the next-paragraph
7663 wxRichTextAttr
wxRichTextParagraphLayoutBox::GetStyleForNewParagraph(wxRichTextBuffer
* buffer
, long pos
, bool caretPosition
, bool lookUpNewParaStyle
) const
7665 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
, caretPosition
);
7668 wxRichTextAttr attr
;
7669 bool foundAttributes
= false;
7671 // Look for a matching paragraph style
7672 if (lookUpNewParaStyle
&& !para
->GetAttributes().GetParagraphStyleName().IsEmpty() && buffer
->GetStyleSheet())
7674 wxRichTextParagraphStyleDefinition
* paraDef
= buffer
->GetStyleSheet()->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
7677 // If we're not at the end of the paragraph, then we apply THIS style, and not the designated next style.
7678 if (para
->GetRange().GetEnd() == pos
&& !paraDef
->GetNextStyle().IsEmpty())
7680 wxRichTextParagraphStyleDefinition
* nextParaDef
= buffer
->GetStyleSheet()->FindParagraphStyle(paraDef
->GetNextStyle());
7683 foundAttributes
= true;
7684 attr
= nextParaDef
->GetStyleMergedWithBase(buffer
->GetStyleSheet());
7688 // If we didn't find the 'next style', use this style instead.
7689 if (!foundAttributes
)
7691 foundAttributes
= true;
7692 attr
= paraDef
->GetStyleMergedWithBase(buffer
->GetStyleSheet());
7697 // Also apply list style if present
7698 if (lookUpNewParaStyle
&& !para
->GetAttributes().GetListStyleName().IsEmpty() && buffer
->GetStyleSheet())
7700 wxRichTextListStyleDefinition
* listDef
= buffer
->GetStyleSheet()->FindListStyle(para
->GetAttributes().GetListStyleName());
7703 int thisIndent
= para
->GetAttributes().GetLeftIndent();
7704 int thisLevel
= para
->GetAttributes().HasOutlineLevel() ? para
->GetAttributes().GetOutlineLevel() : listDef
->FindLevelForIndent(thisIndent
);
7706 // Apply the overall list style, and item style for this level
7707 wxRichTextAttr
listStyle(listDef
->GetCombinedStyleForLevel(thisLevel
, buffer
->GetStyleSheet()));
7708 wxRichTextApplyStyle(attr
, listStyle
);
7709 attr
.SetOutlineLevel(thisLevel
);
7710 if (para
->GetAttributes().HasBulletNumber())
7711 attr
.SetBulletNumber(para
->GetAttributes().GetBulletNumber());
7715 if (!foundAttributes
)
7717 attr
= para
->GetAttributes();
7718 int flags
= attr
.GetFlags();
7720 // Eliminate character styles
7721 flags
&= ( (~ wxTEXT_ATTR_FONT
) |
7722 (~ wxTEXT_ATTR_TEXT_COLOUR
) |
7723 (~ wxTEXT_ATTR_BACKGROUND_COLOUR
) );
7724 attr
.SetFlags(flags
);
7730 return wxRichTextAttr();
7733 /// Submit command to delete this range
7734 bool wxRichTextBuffer::DeleteRangeWithUndo(const wxRichTextRange
& range
, wxRichTextCtrl
* ctrl
)
7736 return ctrl
->GetFocusObject()->DeleteRangeWithUndo(range
, ctrl
, this);
7739 /// Submit command to delete this range
7740 bool wxRichTextParagraphLayoutBox::DeleteRangeWithUndo(const wxRichTextRange
& range
, wxRichTextCtrl
* ctrl
, wxRichTextBuffer
* buffer
)
7742 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Delete"), wxRICHTEXT_DELETE
, buffer
, this, ctrl
);
7744 action
->SetPosition(ctrl
->GetCaretPosition());
7746 // Set the range to delete
7747 action
->SetRange(range
);
7749 // Copy the fragment that we'll need to restore in Undo
7750 CopyFragment(range
, action
->GetOldParagraphs());
7752 // See if we're deleting a paragraph marker, in which case we need to
7753 // make a note not to copy the attributes from the 2nd paragraph to the 1st.
7754 if (range
.GetStart() == range
.GetEnd())
7756 wxRichTextParagraph
* para
= GetParagraphAtPosition(range
.GetStart());
7757 if (para
&& para
->GetRange().GetEnd() == range
.GetEnd())
7759 wxRichTextParagraph
* nextPara
= GetParagraphAtPosition(range
.GetStart()+1);
7760 if (nextPara
&& nextPara
!= para
)
7762 action
->GetOldParagraphs().GetChildren().GetFirst()->GetData()->SetAttributes(nextPara
->GetAttributes());
7763 action
->GetOldParagraphs().GetAttributes().SetFlags(action
->GetOldParagraphs().GetAttributes().GetFlags() | wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
);
7768 buffer
->SubmitAction(action
);
7773 /// Collapse undo/redo commands
7774 bool wxRichTextBuffer::BeginBatchUndo(const wxString
& cmdName
)
7776 if (m_batchedCommandDepth
== 0)
7778 wxASSERT(m_batchedCommand
== NULL
);
7779 if (m_batchedCommand
)
7781 GetCommandProcessor()->Store(m_batchedCommand
);
7783 m_batchedCommand
= new wxRichTextCommand(cmdName
);
7786 m_batchedCommandDepth
++;
7791 /// Collapse undo/redo commands
7792 bool wxRichTextBuffer::EndBatchUndo()
7794 m_batchedCommandDepth
--;
7796 wxASSERT(m_batchedCommandDepth
>= 0);
7797 wxASSERT(m_batchedCommand
!= NULL
);
7799 if (m_batchedCommandDepth
== 0)
7801 GetCommandProcessor()->Store(m_batchedCommand
);
7802 m_batchedCommand
= NULL
;
7808 /// Submit immediately, or delay according to whether collapsing is on
7809 bool wxRichTextBuffer::SubmitAction(wxRichTextAction
* action
)
7811 if (action
&& !action
->GetNewParagraphs().IsEmpty())
7812 PrepareContent(action
->GetNewParagraphs());
7814 if (BatchingUndo() && m_batchedCommand
&& !SuppressingUndo())
7816 wxRichTextCommand
* cmd
= new wxRichTextCommand(action
->GetName());
7817 cmd
->AddAction(action
);
7819 cmd
->GetActions().Clear();
7822 m_batchedCommand
->AddAction(action
);
7826 wxRichTextCommand
* cmd
= new wxRichTextCommand(action
->GetName());
7827 cmd
->AddAction(action
);
7829 // Only store it if we're not suppressing undo.
7830 return GetCommandProcessor()->Submit(cmd
, !SuppressingUndo());
7836 /// Begin suppressing undo/redo commands.
7837 bool wxRichTextBuffer::BeginSuppressUndo()
7844 /// End suppressing undo/redo commands.
7845 bool wxRichTextBuffer::EndSuppressUndo()
7852 /// Begin using a style
7853 bool wxRichTextBuffer::BeginStyle(const wxRichTextAttr
& style
)
7855 wxRichTextAttr
newStyle(GetDefaultStyle());
7856 newStyle
.GetTextBoxAttr().Reset();
7858 // Save the old default style
7859 m_attributeStack
.Append((wxObject
*) new wxRichTextAttr(newStyle
));
7861 wxRichTextApplyStyle(newStyle
, style
);
7862 newStyle
.SetFlags(style
.GetFlags()|newStyle
.GetFlags());
7864 SetDefaultStyle(newStyle
);
7870 bool wxRichTextBuffer::EndStyle()
7872 if (!m_attributeStack
.GetFirst())
7874 wxLogDebug(_("Too many EndStyle calls!"));
7878 wxList::compatibility_iterator node
= m_attributeStack
.GetLast();
7879 wxRichTextAttr
* attr
= (wxRichTextAttr
*)node
->GetData();
7880 m_attributeStack
.Erase(node
);
7882 SetDefaultStyle(*attr
);
7889 bool wxRichTextBuffer::EndAllStyles()
7891 while (m_attributeStack
.GetCount() != 0)
7896 /// Clear the style stack
7897 void wxRichTextBuffer::ClearStyleStack()
7899 for (wxList::compatibility_iterator node
= m_attributeStack
.GetFirst(); node
; node
= node
->GetNext())
7900 delete (wxRichTextAttr
*) node
->GetData();
7901 m_attributeStack
.Clear();
7904 /// Begin using bold
7905 bool wxRichTextBuffer::BeginBold()
7907 wxRichTextAttr attr
;
7908 attr
.SetFontWeight(wxFONTWEIGHT_BOLD
);
7910 return BeginStyle(attr
);
7913 /// Begin using italic
7914 bool wxRichTextBuffer::BeginItalic()
7916 wxRichTextAttr attr
;
7917 attr
.SetFontStyle(wxFONTSTYLE_ITALIC
);
7919 return BeginStyle(attr
);
7922 /// Begin using underline
7923 bool wxRichTextBuffer::BeginUnderline()
7925 wxRichTextAttr attr
;
7926 attr
.SetFontUnderlined(true);
7928 return BeginStyle(attr
);
7931 /// Begin using point size
7932 bool wxRichTextBuffer::BeginFontSize(int pointSize
)
7934 wxRichTextAttr attr
;
7935 attr
.SetFontSize(pointSize
);
7937 return BeginStyle(attr
);
7940 /// Begin using this font
7941 bool wxRichTextBuffer::BeginFont(const wxFont
& font
)
7943 wxRichTextAttr attr
;
7946 return BeginStyle(attr
);
7949 /// Begin using this colour
7950 bool wxRichTextBuffer::BeginTextColour(const wxColour
& colour
)
7952 wxRichTextAttr attr
;
7953 attr
.SetFlags(wxTEXT_ATTR_TEXT_COLOUR
);
7954 attr
.SetTextColour(colour
);
7956 return BeginStyle(attr
);
7959 /// Begin using alignment
7960 bool wxRichTextBuffer::BeginAlignment(wxTextAttrAlignment alignment
)
7962 wxRichTextAttr attr
;
7963 attr
.SetFlags(wxTEXT_ATTR_ALIGNMENT
);
7964 attr
.SetAlignment(alignment
);
7966 return BeginStyle(attr
);
7969 /// Begin left indent
7970 bool wxRichTextBuffer::BeginLeftIndent(int leftIndent
, int leftSubIndent
)
7972 wxRichTextAttr attr
;
7973 attr
.SetFlags(wxTEXT_ATTR_LEFT_INDENT
);
7974 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7976 return BeginStyle(attr
);
7979 /// Begin right indent
7980 bool wxRichTextBuffer::BeginRightIndent(int rightIndent
)
7982 wxRichTextAttr attr
;
7983 attr
.SetFlags(wxTEXT_ATTR_RIGHT_INDENT
);
7984 attr
.SetRightIndent(rightIndent
);
7986 return BeginStyle(attr
);
7989 /// Begin paragraph spacing
7990 bool wxRichTextBuffer::BeginParagraphSpacing(int before
, int after
)
7994 flags
|= wxTEXT_ATTR_PARA_SPACING_BEFORE
;
7996 flags
|= wxTEXT_ATTR_PARA_SPACING_AFTER
;
7998 wxRichTextAttr attr
;
7999 attr
.SetFlags(flags
);
8000 attr
.SetParagraphSpacingBefore(before
);
8001 attr
.SetParagraphSpacingAfter(after
);
8003 return BeginStyle(attr
);
8006 /// Begin line spacing
8007 bool wxRichTextBuffer::BeginLineSpacing(int lineSpacing
)
8009 wxRichTextAttr attr
;
8010 attr
.SetFlags(wxTEXT_ATTR_LINE_SPACING
);
8011 attr
.SetLineSpacing(lineSpacing
);
8013 return BeginStyle(attr
);
8016 /// Begin numbered bullet
8017 bool wxRichTextBuffer::BeginNumberedBullet(int bulletNumber
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
8019 wxRichTextAttr attr
;
8020 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
8021 attr
.SetBulletStyle(bulletStyle
);
8022 attr
.SetBulletNumber(bulletNumber
);
8023 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
8025 return BeginStyle(attr
);
8028 /// Begin symbol bullet
8029 bool wxRichTextBuffer::BeginSymbolBullet(const wxString
& symbol
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
8031 wxRichTextAttr attr
;
8032 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
8033 attr
.SetBulletStyle(bulletStyle
);
8034 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
8035 attr
.SetBulletText(symbol
);
8037 return BeginStyle(attr
);
8040 /// Begin standard bullet
8041 bool wxRichTextBuffer::BeginStandardBullet(const wxString
& bulletName
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
8043 wxRichTextAttr attr
;
8044 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
8045 attr
.SetBulletStyle(bulletStyle
);
8046 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
8047 attr
.SetBulletName(bulletName
);
8049 return BeginStyle(attr
);
8052 /// Begin named character style
8053 bool wxRichTextBuffer::BeginCharacterStyle(const wxString
& characterStyle
)
8055 if (GetStyleSheet())
8057 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->FindCharacterStyle(characterStyle
);
8060 wxRichTextAttr attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
8061 return BeginStyle(attr
);
8067 /// Begin named paragraph style
8068 bool wxRichTextBuffer::BeginParagraphStyle(const wxString
& paragraphStyle
)
8070 if (GetStyleSheet())
8072 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(paragraphStyle
);
8075 wxRichTextAttr attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
8076 return BeginStyle(attr
);
8082 /// Begin named list style
8083 bool wxRichTextBuffer::BeginListStyle(const wxString
& listStyle
, int level
, int number
)
8085 if (GetStyleSheet())
8087 wxRichTextListStyleDefinition
* def
= GetStyleSheet()->FindListStyle(listStyle
);
8090 wxRichTextAttr
attr(def
->GetCombinedStyleForLevel(level
));
8092 attr
.SetBulletNumber(number
);
8094 return BeginStyle(attr
);
8101 bool wxRichTextBuffer::BeginURL(const wxString
& url
, const wxString
& characterStyle
)
8103 wxRichTextAttr attr
;
8105 if (!characterStyle
.IsEmpty() && GetStyleSheet())
8107 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->FindCharacterStyle(characterStyle
);
8110 attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
8115 return BeginStyle(attr
);
8118 /// Adds a handler to the end
8119 void wxRichTextBuffer::AddHandler(wxRichTextFileHandler
*handler
)
8121 sm_handlers
.Append(handler
);
8124 /// Inserts a handler at the front
8125 void wxRichTextBuffer::InsertHandler(wxRichTextFileHandler
*handler
)
8127 sm_handlers
.Insert( handler
);
8130 /// Removes a handler
8131 bool wxRichTextBuffer::RemoveHandler(const wxString
& name
)
8133 wxRichTextFileHandler
*handler
= FindHandler(name
);
8136 sm_handlers
.DeleteObject(handler
);
8144 /// Finds a handler by filename or, if supplied, type
8145 wxRichTextFileHandler
*wxRichTextBuffer::FindHandlerFilenameOrType(const wxString
& filename
,
8146 wxRichTextFileType imageType
)
8148 if (imageType
!= wxRICHTEXT_TYPE_ANY
)
8149 return FindHandler(imageType
);
8150 else if (!filename
.IsEmpty())
8152 wxString path
, file
, ext
;
8153 wxFileName::SplitPath(filename
, & path
, & file
, & ext
);
8154 return FindHandler(ext
, imageType
);
8161 /// Finds a handler by name
8162 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(const wxString
& name
)
8164 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
8167 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
8168 if (handler
->GetName().Lower() == name
.Lower()) return handler
;
8170 node
= node
->GetNext();
8175 /// Finds a handler by extension and type
8176 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(const wxString
& extension
, wxRichTextFileType type
)
8178 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
8181 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
8182 if ( handler
->GetExtension().Lower() == extension
.Lower() &&
8183 (type
== wxRICHTEXT_TYPE_ANY
|| handler
->GetType() == type
) )
8185 node
= node
->GetNext();
8190 /// Finds a handler by type
8191 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(wxRichTextFileType type
)
8193 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
8196 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
8197 if (handler
->GetType() == type
) return handler
;
8198 node
= node
->GetNext();
8203 void wxRichTextBuffer::InitStandardHandlers()
8205 if (!FindHandler(wxRICHTEXT_TYPE_TEXT
))
8206 AddHandler(new wxRichTextPlainTextHandler
);
8209 void wxRichTextBuffer::CleanUpHandlers()
8211 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
8214 wxRichTextFileHandler
* handler
= (wxRichTextFileHandler
*)node
->GetData();
8215 wxList::compatibility_iterator next
= node
->GetNext();
8220 sm_handlers
.Clear();
8223 wxString
wxRichTextBuffer::GetExtWildcard(bool combine
, bool save
, wxArrayInt
* types
)
8230 wxList::compatibility_iterator node
= GetHandlers().GetFirst();
8234 wxRichTextFileHandler
* handler
= (wxRichTextFileHandler
*) node
->GetData();
8235 if (handler
->IsVisible() && ((save
&& handler
->CanSave()) || (!save
&& handler
->CanLoad())))
8240 wildcard
+= wxT(";");
8241 wildcard
+= wxT("*.") + handler
->GetExtension();
8246 wildcard
+= wxT("|");
8247 wildcard
+= handler
->GetName();
8248 wildcard
+= wxT(" ");
8249 wildcard
+= _("files");
8250 wildcard
+= wxT(" (*.");
8251 wildcard
+= handler
->GetExtension();
8252 wildcard
+= wxT(")|*.");
8253 wildcard
+= handler
->GetExtension();
8255 types
->Add(handler
->GetType());
8260 node
= node
->GetNext();
8264 wildcard
= wxT("(") + wildcard
+ wxT(")|") + wildcard
;
8269 bool wxRichTextBuffer::LoadFile(const wxString
& filename
, wxRichTextFileType type
)
8271 wxRichTextFileHandler
* handler
= FindHandlerFilenameOrType(filename
, type
);
8274 SetDefaultStyle(wxRichTextAttr());
8275 handler
->SetFlags(GetHandlerFlags());
8276 bool success
= handler
->LoadFile(this, filename
);
8277 Invalidate(wxRICHTEXT_ALL
);
8285 bool wxRichTextBuffer::SaveFile(const wxString
& filename
, wxRichTextFileType type
)
8287 wxRichTextFileHandler
* handler
= FindHandlerFilenameOrType(filename
, type
);
8290 handler
->SetFlags(GetHandlerFlags());
8291 return handler
->SaveFile(this, filename
);
8297 /// Load from a stream
8298 bool wxRichTextBuffer::LoadFile(wxInputStream
& stream
, wxRichTextFileType type
)
8300 wxRichTextFileHandler
* handler
= FindHandler(type
);
8303 SetDefaultStyle(wxRichTextAttr());
8304 handler
->SetFlags(GetHandlerFlags());
8305 bool success
= handler
->LoadFile(this, stream
);
8306 Invalidate(wxRICHTEXT_ALL
);
8313 /// Save to a stream
8314 bool wxRichTextBuffer::SaveFile(wxOutputStream
& stream
, wxRichTextFileType type
)
8316 wxRichTextFileHandler
* handler
= FindHandler(type
);
8319 handler
->SetFlags(GetHandlerFlags());
8320 return handler
->SaveFile(this, stream
);
8326 /// Copy the range to the clipboard
8327 bool wxRichTextBuffer::CopyToClipboard(const wxRichTextRange
& range
)
8329 bool success
= false;
8330 wxRichTextParagraphLayoutBox
* container
= this;
8331 if (GetRichTextCtrl())
8332 container
= GetRichTextCtrl()->GetFocusObject();
8334 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
8336 if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open())
8338 wxTheClipboard
->Clear();
8340 // Add composite object
8342 wxDataObjectComposite
* compositeObject
= new wxDataObjectComposite();
8345 wxString text
= container
->GetTextForRange(range
);
8348 text
= wxTextFile::Translate(text
, wxTextFileType_Dos
);
8351 compositeObject
->Add(new wxTextDataObject(text
), false /* not preferred */);
8354 // Add rich text buffer data object. This needs the XML handler to be present.
8356 if (FindHandler(wxRICHTEXT_TYPE_XML
))
8358 wxRichTextBuffer
* richTextBuf
= new wxRichTextBuffer
;
8359 container
->CopyFragment(range
, *richTextBuf
);
8361 compositeObject
->Add(new wxRichTextBufferDataObject(richTextBuf
), true /* preferred */);
8364 if (wxTheClipboard
->SetData(compositeObject
))
8367 wxTheClipboard
->Close();
8376 /// Paste the clipboard content to the buffer
8377 bool wxRichTextBuffer::PasteFromClipboard(long position
)
8379 bool success
= false;
8380 wxRichTextParagraphLayoutBox
* container
= this;
8381 if (GetRichTextCtrl())
8382 container
= GetRichTextCtrl()->GetFocusObject();
8384 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
8385 if (CanPasteFromClipboard())
8387 if (wxTheClipboard
->Open())
8389 if (wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())))
8391 wxRichTextBufferDataObject data
;
8392 wxTheClipboard
->GetData(data
);
8393 wxRichTextBuffer
* richTextBuffer
= data
.GetRichTextBuffer();
8396 container
->InsertParagraphsWithUndo(this, position
+1, *richTextBuffer
, GetRichTextCtrl(), 0);
8397 if (GetRichTextCtrl())
8398 GetRichTextCtrl()->ShowPosition(position
+ richTextBuffer
->GetOwnRange().GetEnd());
8399 delete richTextBuffer
;
8402 else if (wxTheClipboard
->IsSupported(wxDF_TEXT
)
8404 || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
)
8408 wxTextDataObject data
;
8409 wxTheClipboard
->GetData(data
);
8410 wxString
text(data
.GetText());
8413 text2
.Alloc(text
.Length()+1);
8415 for (i
= 0; i
< text
.Length(); i
++)
8417 wxChar ch
= text
[i
];
8418 if (ch
!= wxT('\r'))
8422 wxString text2
= text
;
8424 container
->InsertTextWithUndo(this, position
+1, text2
, GetRichTextCtrl(), wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
);
8426 if (GetRichTextCtrl())
8427 GetRichTextCtrl()->ShowPosition(position
+ text2
.Length());
8431 else if (wxTheClipboard
->IsSupported(wxDF_BITMAP
))
8433 wxBitmapDataObject data
;
8434 wxTheClipboard
->GetData(data
);
8435 wxBitmap
bitmap(data
.GetBitmap());
8436 wxImage
image(bitmap
.ConvertToImage());
8438 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, this, container
, GetRichTextCtrl(), false);
8440 action
->GetNewParagraphs().AddImage(image
);
8442 if (action
->GetNewParagraphs().GetChildCount() == 1)
8443 action
->GetNewParagraphs().SetPartialParagraph(true);
8445 action
->SetPosition(position
+1);
8447 // Set the range we'll need to delete in Undo
8448 action
->SetRange(wxRichTextRange(position
+1, position
+1));
8450 SubmitAction(action
);
8454 wxTheClipboard
->Close();
8458 wxUnusedVar(position
);
8463 /// Can we paste from the clipboard?
8464 bool wxRichTextBuffer::CanPasteFromClipboard() const
8466 bool canPaste
= false;
8467 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
8468 if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open())
8470 if (wxTheClipboard
->IsSupported(wxDF_TEXT
)
8472 || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
)
8474 || wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())) ||
8475 wxTheClipboard
->IsSupported(wxDF_BITMAP
))
8479 wxTheClipboard
->Close();
8485 /// Dumps contents of buffer for debugging purposes
8486 void wxRichTextBuffer::Dump()
8490 wxStringOutputStream
stream(& text
);
8491 wxTextOutputStream
textStream(stream
);
8498 /// Add an event handler
8499 bool wxRichTextBuffer::AddEventHandler(wxEvtHandler
* handler
)
8501 m_eventHandlers
.Append(handler
);
8505 /// Remove an event handler
8506 bool wxRichTextBuffer::RemoveEventHandler(wxEvtHandler
* handler
, bool deleteHandler
)
8508 wxList::compatibility_iterator node
= m_eventHandlers
.Find(handler
);
8511 m_eventHandlers
.Erase(node
);
8521 /// Clear event handlers
8522 void wxRichTextBuffer::ClearEventHandlers()
8524 m_eventHandlers
.Clear();
8527 /// Send event to event handlers. If sendToAll is true, will send to all event handlers,
8528 /// otherwise will stop at the first successful one.
8529 bool wxRichTextBuffer::SendEvent(wxEvent
& event
, bool sendToAll
)
8531 bool success
= false;
8532 for (wxList::compatibility_iterator node
= m_eventHandlers
.GetFirst(); node
; node
= node
->GetNext())
8534 wxEvtHandler
* handler
= (wxEvtHandler
*) node
->GetData();
8535 if (handler
->ProcessEvent(event
))
8545 /// Set style sheet and notify of the change
8546 bool wxRichTextBuffer::SetStyleSheetAndNotify(wxRichTextStyleSheet
* sheet
)
8548 wxRichTextStyleSheet
* oldSheet
= GetStyleSheet();
8550 wxWindowID winid
= wxID_ANY
;
8551 if (GetRichTextCtrl())
8552 winid
= GetRichTextCtrl()->GetId();
8554 wxRichTextEvent
event(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACING
, winid
);
8555 event
.SetEventObject(GetRichTextCtrl());
8556 event
.SetContainer(GetRichTextCtrl()->GetFocusObject());
8557 event
.SetOldStyleSheet(oldSheet
);
8558 event
.SetNewStyleSheet(sheet
);
8561 if (SendEvent(event
) && !event
.IsAllowed())
8563 if (sheet
!= oldSheet
)
8569 if (oldSheet
&& oldSheet
!= sheet
)
8572 SetStyleSheet(sheet
);
8574 event
.SetEventType(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACED
);
8575 event
.SetOldStyleSheet(NULL
);
8578 return SendEvent(event
);
8581 /// Set renderer, deleting old one
8582 void wxRichTextBuffer::SetRenderer(wxRichTextRenderer
* renderer
)
8586 sm_renderer
= renderer
;
8589 /// Hit-testing: returns a flag indicating hit test details, plus
8590 /// information about position
8591 int wxRichTextBuffer::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
8593 int ret
= wxRichTextParagraphLayoutBox::HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
8594 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
8600 textPosition
= m_ownRange
.GetEnd()-1;
8603 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
;
8607 void wxRichTextBuffer::SetFontScale(double fontScale
)
8609 m_fontScale
= fontScale
;
8610 m_fontTable
.SetFontScale(fontScale
);
8613 void wxRichTextBuffer::SetDimensionScale(double dimScale
)
8615 m_dimensionScale
= dimScale
;
8618 bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxRichTextAttr
& bulletAttr
, const wxRect
& rect
)
8620 if (bulletAttr
.GetTextColour().IsOk())
8622 wxCheckSetPen(dc
, wxPen(bulletAttr
.GetTextColour()));
8623 wxCheckSetBrush(dc
, wxBrush(bulletAttr
.GetTextColour()));
8627 wxCheckSetPen(dc
, *wxBLACK_PEN
);
8628 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
8632 if (bulletAttr
.HasFont())
8634 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(bulletAttr
);
8637 font
= (*wxNORMAL_FONT
);
8639 wxCheckSetFont(dc
, font
);
8641 int charHeight
= dc
.GetCharHeight();
8643 int bulletWidth
= (int) (((float) charHeight
) * wxRichTextBuffer::GetBulletProportion());
8644 int bulletHeight
= bulletWidth
;
8648 // Calculate the top position of the character (as opposed to the whole line height)
8649 int y
= rect
.y
+ (rect
.height
- charHeight
);
8651 // Calculate where the bullet should be positioned
8652 y
= y
+ (charHeight
+1)/2 - (bulletHeight
+1)/2;
8654 // The margin between a bullet and text.
8655 int margin
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer::GetBulletRightMargin());
8657 if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
)
8658 x
= rect
.x
+ rect
.width
- bulletWidth
- margin
;
8659 else if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
)
8660 x
= x
+ (rect
.width
)/2 - bulletWidth
/2;
8662 if (bulletAttr
.GetBulletName() == wxT("standard/square"))
8664 dc
.DrawRectangle(x
, y
, bulletWidth
, bulletHeight
);
8666 else if (bulletAttr
.GetBulletName() == wxT("standard/diamond"))
8669 pts
[0].x
= x
; pts
[0].y
= y
+ bulletHeight
/2;
8670 pts
[1].x
= x
+ bulletWidth
/2; pts
[1].y
= y
;
8671 pts
[2].x
= x
+ bulletWidth
; pts
[2].y
= y
+ bulletHeight
/2;
8672 pts
[3].x
= x
+ bulletWidth
/2; pts
[3].y
= y
+ bulletHeight
;
8674 dc
.DrawPolygon(4, pts
);
8676 else if (bulletAttr
.GetBulletName() == wxT("standard/triangle"))
8679 pts
[0].x
= x
; pts
[0].y
= y
;
8680 pts
[1].x
= x
+ bulletWidth
; pts
[1].y
= y
+ bulletHeight
/2;
8681 pts
[2].x
= x
; pts
[2].y
= y
+ bulletHeight
;
8683 dc
.DrawPolygon(3, pts
);
8685 else if (bulletAttr
.GetBulletName() == wxT("standard/circle-outline"))
8687 wxCheckSetBrush(dc
, *wxTRANSPARENT_BRUSH
);
8688 dc
.DrawEllipse(x
, y
, bulletWidth
, bulletHeight
);
8690 else // "standard/circle", and catch-all
8692 dc
.DrawEllipse(x
, y
, bulletWidth
, bulletHeight
);
8698 bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxRichTextAttr
& attr
, const wxRect
& rect
, const wxString
& text
)
8703 if ((attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
) && !attr
.GetBulletFont().IsEmpty() && attr
.HasFont())
8705 wxRichTextAttr fontAttr
;
8706 if (attr
.HasFontPixelSize())
8707 fontAttr
.SetFontPixelSize(attr
.GetFontSize());
8709 fontAttr
.SetFontPointSize(attr
.GetFontSize());
8710 fontAttr
.SetFontStyle(attr
.GetFontStyle());
8711 fontAttr
.SetFontWeight(attr
.GetFontWeight());
8712 fontAttr
.SetFontUnderlined(attr
.GetFontUnderlined());
8713 fontAttr
.SetFontFaceName(attr
.GetBulletFont());
8714 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(fontAttr
);
8716 else if (attr
.HasFont())
8717 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(attr
);
8719 font
= (*wxNORMAL_FONT
);
8721 wxCheckSetFont(dc
, font
);
8723 if (attr
.GetTextColour().IsOk())
8724 dc
.SetTextForeground(attr
.GetTextColour());
8726 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
8728 int charHeight
= dc
.GetCharHeight();
8730 dc
.GetTextExtent(text
, & tw
, & th
);
8734 // Calculate the top position of the character (as opposed to the whole line height)
8735 int y
= rect
.y
+ (rect
.height
- charHeight
);
8737 // The margin between a bullet and text.
8738 int margin
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer::GetBulletRightMargin());
8740 if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
)
8741 x
= (rect
.x
+ rect
.width
) - tw
- margin
;
8742 else if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
)
8743 x
= x
+ (rect
.width
)/2 - tw
/2;
8745 dc
.DrawText(text
, x
, y
);
8753 bool wxRichTextStdRenderer::DrawBitmapBullet(wxRichTextParagraph
* WXUNUSED(paragraph
), wxDC
& WXUNUSED(dc
), const wxRichTextAttr
& WXUNUSED(attr
), const wxRect
& WXUNUSED(rect
))
8755 // Currently unimplemented. The intention is to store bitmaps by name in a media store associated
8756 // with the buffer. The store will allow retrieval from memory, disk or other means.
8760 /// Enumerate the standard bullet names currently supported
8761 bool wxRichTextStdRenderer::EnumerateStandardBulletNames(wxArrayString
& bulletNames
)
8763 bulletNames
.Add(wxTRANSLATE("standard/circle"));
8764 bulletNames
.Add(wxTRANSLATE("standard/circle-outline"));
8765 bulletNames
.Add(wxTRANSLATE("standard/square"));
8766 bulletNames
.Add(wxTRANSLATE("standard/diamond"));
8767 bulletNames
.Add(wxTRANSLATE("standard/triangle"));
8776 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBox
, wxRichTextParagraphLayoutBox
)
8778 wxRichTextBox::wxRichTextBox(wxRichTextObject
* parent
):
8779 wxRichTextParagraphLayoutBox(parent
)
8784 bool wxRichTextBox::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8789 // TODO: if the active object in the control, draw an indication.
8790 // We need to add the concept of active object, and not just focus object,
8791 // so we can apply commands (properties, delete, ...) to objects such as text boxes and images.
8792 // Ultimately we would like to be able to interactively resize an active object
8793 // using drag handles.
8794 return wxRichTextParagraphLayoutBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8798 void wxRichTextBox::Copy(const wxRichTextBox
& obj
)
8800 wxRichTextParagraphLayoutBox::Copy(obj
);
8803 // Edit properties via a GUI
8804 bool wxRichTextBox::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8806 wxRichTextObjectPropertiesDialog
boxDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Box Properties"));
8807 boxDlg
.SetAttributes(GetAttributes());
8809 if (boxDlg
.ShowModal() == wxID_OK
)
8811 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
8812 // indeterminate in the object.
8813 boxDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
8824 IMPLEMENT_DYNAMIC_CLASS(wxRichTextField
, wxRichTextParagraphLayoutBox
)
8826 wxRichTextField::wxRichTextField(const wxString
& fieldType
, wxRichTextObject
* parent
):
8827 wxRichTextParagraphLayoutBox(parent
)
8829 SetFieldType(fieldType
);
8833 bool wxRichTextField::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8838 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8839 if (fieldType
&& fieldType
->Draw(this, dc
, context
, range
, selection
, rect
, descent
, style
))
8842 // Fallback; but don't draw guidelines.
8843 style
&= ~wxRICHTEXT_DRAW_GUIDELINES
;
8844 return wxRichTextParagraphLayoutBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8847 bool wxRichTextField::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
)
8849 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8850 if (fieldType
&& fieldType
->Layout(this, dc
, context
, rect
, parentRect
, style
))
8854 return wxRichTextParagraphLayoutBox::Layout(dc
, context
, rect
, parentRect
, style
);
8857 bool wxRichTextField::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
8859 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8861 return fieldType
->GetRangeSize((wxRichTextField
*) this, range
, size
, descent
, dc
, context
, flags
, position
, partialExtents
);
8863 return wxRichTextParagraphLayoutBox::GetRangeSize(range
, size
, descent
, dc
, context
, flags
, position
, partialExtents
);
8867 void wxRichTextField::CalculateRange(long start
, long& end
)
8870 wxRichTextParagraphLayoutBox::CalculateRange(start
, end
);
8872 wxRichTextObject::CalculateRange(start
, end
);
8876 void wxRichTextField::Copy(const wxRichTextField
& obj
)
8878 wxRichTextParagraphLayoutBox::Copy(obj
);
8880 UpdateField(GetBuffer());
8883 // Edit properties via a GUI
8884 bool wxRichTextField::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8886 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8888 return fieldType
->EditProperties(this, parent
, buffer
);
8893 bool wxRichTextField::CanEditProperties() const
8895 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8897 return fieldType
->CanEditProperties((wxRichTextField
*) this);
8902 wxString
wxRichTextField::GetPropertiesMenuLabel() const
8904 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8906 return fieldType
->GetPropertiesMenuLabel((wxRichTextField
*) this);
8908 return wxEmptyString
;
8911 bool wxRichTextField::UpdateField(wxRichTextBuffer
* buffer
)
8913 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8915 return fieldType
->UpdateField(buffer
, (wxRichTextField
*) this);
8920 bool wxRichTextField::IsTopLevel() const
8922 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8924 return fieldType
->IsTopLevel((wxRichTextField
*) this);
8929 IMPLEMENT_CLASS(wxRichTextFieldType
, wxObject
)
8931 IMPLEMENT_CLASS(wxRichTextFieldTypeStandard
, wxRichTextFieldType
)
8933 wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString
& name
, const wxString
& label
, int displayStyle
)
8939 SetDisplayStyle(displayStyle
);
8942 wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString
& name
, const wxBitmap
& bitmap
, int displayStyle
)
8948 SetDisplayStyle(displayStyle
);
8951 void wxRichTextFieldTypeStandard::Init()
8953 m_displayStyle
= wxRICHTEXT_FIELD_STYLE_RECTANGLE
;
8954 m_font
= wxFont(6, wxFONTFAMILY_SWISS
, wxFONTSTYLE_NORMAL
, wxFONTWEIGHT_NORMAL
);
8955 m_textColour
= *wxWHITE
;
8956 m_borderColour
= *wxBLACK
;
8957 m_backgroundColour
= *wxBLACK
;
8958 m_verticalPadding
= 1;
8959 m_horizontalPadding
= 3;
8960 m_horizontalMargin
= 2;
8961 m_verticalMargin
= 0;
8964 void wxRichTextFieldTypeStandard::Copy(const wxRichTextFieldTypeStandard
& field
)
8966 wxRichTextFieldType::Copy(field
);
8968 m_label
= field
.m_label
;
8969 m_displayStyle
= field
.m_displayStyle
;
8970 m_font
= field
.m_font
;
8971 m_textColour
= field
.m_textColour
;
8972 m_borderColour
= field
.m_borderColour
;
8973 m_backgroundColour
= field
.m_backgroundColour
;
8974 m_verticalPadding
= field
.m_verticalPadding
;
8975 m_horizontalPadding
= field
.m_horizontalPadding
;
8976 m_horizontalMargin
= field
.m_horizontalMargin
;
8977 m_bitmap
= field
.m_bitmap
;
8980 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
))
8982 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_COMPOSITE
)
8983 return false; // USe default composite drawing
8984 else // if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_RECTANGLE || m_displayStyle == wxRICHTEXT_FIELD_STYLE_NOBORDER)
8988 wxPen
borderPen(m_borderColour
, 1, wxSOLID
);
8989 wxBrush
backgroundBrush(m_backgroundColour
);
8990 wxColour
textColour(m_textColour
);
8992 if (selection
.WithinSelection(obj
->GetRange().GetStart(), obj
))
8994 wxColour
highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
));
8995 wxColour
highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
));
8997 borderPen
= wxPen(highlightTextColour
, 1, wxSOLID
);
8998 backgroundBrush
= wxBrush(highlightColour
);
9000 wxCheckSetBrush(dc
, backgroundBrush
);
9001 wxCheckSetPen(dc
, wxPen(highlightColour
, 1, wxSOLID
));
9002 dc
.DrawRectangle(rect
);
9005 if (m_displayStyle
!= wxRICHTEXT_FIELD_STYLE_NO_BORDER
)
9008 // objectRect is the area where the content is drawn, after margins around it have been taken into account
9009 wxRect objectRect
= wxRect(wxPoint(rect
.x
+ m_horizontalMargin
, rect
.y
+ wxMax(0, rect
.height
- descent
- obj
->GetCachedSize().y
)),
9010 wxSize(obj
->GetCachedSize().x
- 2*m_horizontalMargin
- borderSize
, obj
->GetCachedSize().y
));
9012 // clientArea is where the text is actually written
9013 wxRect clientArea
= objectRect
;
9015 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_RECTANGLE
)
9017 dc
.SetPen(borderPen
);
9018 dc
.SetBrush(backgroundBrush
);
9019 dc
.DrawRoundedRectangle(objectRect
, 4.0);
9021 else if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_START_TAG
)
9023 int arrowLength
= objectRect
.height
/2;
9024 clientArea
.width
-= (arrowLength
- m_horizontalPadding
);
9027 pts
[0].x
= objectRect
.x
; pts
[0].y
= objectRect
.y
;
9028 pts
[1].x
= objectRect
.x
+ objectRect
.width
- arrowLength
; pts
[1].y
= objectRect
.y
;
9029 pts
[2].x
= objectRect
.x
+ objectRect
.width
; pts
[2].y
= objectRect
.y
+ (objectRect
.height
/2);
9030 pts
[3].x
= objectRect
.x
+ objectRect
.width
- arrowLength
; pts
[3].y
= objectRect
.y
+ objectRect
.height
;
9031 pts
[4].x
= objectRect
.x
; pts
[4].y
= objectRect
.y
+ objectRect
.height
;
9032 dc
.SetPen(borderPen
);
9033 dc
.SetBrush(backgroundBrush
);
9034 dc
.DrawPolygon(5, pts
);
9036 else if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_END_TAG
)
9038 int arrowLength
= objectRect
.height
/2;
9039 clientArea
.width
-= (arrowLength
- m_horizontalPadding
);
9040 clientArea
.x
+= (arrowLength
- m_horizontalPadding
);
9043 pts
[0].x
= objectRect
.x
+ objectRect
.width
; pts
[0].y
= objectRect
.y
;
9044 pts
[1].x
= objectRect
.x
+ arrowLength
; pts
[1].y
= objectRect
.y
;
9045 pts
[2].x
= objectRect
.x
; pts
[2].y
= objectRect
.y
+ (objectRect
.height
/2);
9046 pts
[3].x
= objectRect
.x
+ arrowLength
; pts
[3].y
= objectRect
.y
+ objectRect
.height
;
9047 pts
[4].x
= objectRect
.x
+ objectRect
.width
; pts
[4].y
= objectRect
.y
+ objectRect
.height
;
9048 dc
.SetPen(borderPen
);
9049 dc
.SetBrush(backgroundBrush
);
9050 dc
.DrawPolygon(5, pts
);
9053 if (m_bitmap
.IsOk())
9055 int x
= clientArea
.x
+ (clientArea
.width
- m_bitmap
.GetWidth())/2;
9056 int y
= clientArea
.y
+ m_verticalPadding
;
9057 dc
.DrawBitmap(m_bitmap
, x
, y
, true);
9059 if (selection
.WithinSelection(obj
->GetRange().GetStart(), obj
))
9061 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
9062 wxCheckSetPen(dc
, *wxBLACK_PEN
);
9063 dc
.SetLogicalFunction(wxINVERT
);
9064 dc
.DrawRectangle(wxRect(x
, y
, m_bitmap
.GetWidth(), m_bitmap
.GetHeight()));
9065 dc
.SetLogicalFunction(wxCOPY
);
9070 wxString
label(m_label
);
9071 if (label
.IsEmpty())
9073 int w
, h
, maxDescent
;
9075 dc
.GetTextExtent(m_label
, & w
, &h
, & maxDescent
);
9076 dc
.SetTextForeground(textColour
);
9078 int x
= clientArea
.x
+ (clientArea
.width
- w
)/2;
9079 int y
= clientArea
.y
+ (clientArea
.height
- (h
- maxDescent
))/2;
9080 dc
.DrawText(m_label
, x
, y
);
9087 bool wxRichTextFieldTypeStandard::Layout(wxRichTextField
* obj
, wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& WXUNUSED(rect
), const wxRect
& WXUNUSED(parentRect
), int style
)
9089 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_COMPOSITE
)
9090 return false; // USe default composite layout
9092 wxSize size
= GetSize(obj
, dc
, context
, style
);
9093 obj
->SetCachedSize(size
);
9094 obj
->SetMinSize(size
);
9095 obj
->SetMaxSize(size
);
9099 bool wxRichTextFieldTypeStandard::GetRangeSize(wxRichTextField
* obj
, const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
9101 if (IsTopLevel(obj
))
9102 return obj
->wxRichTextParagraphLayoutBox::GetRangeSize(range
, size
, descent
, dc
, context
, flags
, position
);
9105 wxSize sz
= GetSize(obj
, dc
, context
, 0);
9109 if (partialExtents
->GetCount() > 0)
9110 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
9113 partialExtents
->Add(lastSize
+ sz
.x
);
9120 wxSize
wxRichTextFieldTypeStandard::GetSize(wxRichTextField
* WXUNUSED(obj
), wxDC
& dc
, wxRichTextDrawingContext
& WXUNUSED(context
), int WXUNUSED(style
)) const
9123 int w
= 0, h
= 0, maxDescent
= 0;
9126 if (m_bitmap
.IsOk())
9128 w
= m_bitmap
.GetWidth();
9129 h
= m_bitmap
.GetHeight();
9131 sz
= wxSize(w
+ m_horizontalMargin
*2, h
+ m_verticalMargin
*2);
9135 wxString
label(m_label
);
9136 if (label
.IsEmpty())
9139 dc
.GetTextExtent(label
, & w
, &h
, & maxDescent
);
9141 sz
= wxSize(w
+ m_horizontalPadding
*2 + m_horizontalMargin
*2, h
+ m_verticalPadding
*2 + m_verticalMargin
*2);
9144 if (m_displayStyle
!= wxRICHTEXT_FIELD_STYLE_NO_BORDER
)
9146 sz
.x
+= borderSize
*2;
9147 sz
.y
+= borderSize
*2;
9150 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_START_TAG
|| m_displayStyle
== wxRICHTEXT_FIELD_STYLE_END_TAG
)
9152 // Add space for the arrow
9153 sz
.x
+= (sz
.y
/2 - m_horizontalPadding
);
9159 IMPLEMENT_DYNAMIC_CLASS(wxRichTextCell
, wxRichTextBox
)
9161 wxRichTextCell::wxRichTextCell(wxRichTextObject
* parent
):
9162 wxRichTextBox(parent
)
9167 bool wxRichTextCell::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
9169 return wxRichTextBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
9173 void wxRichTextCell::Copy(const wxRichTextCell
& obj
)
9175 wxRichTextBox::Copy(obj
);
9178 // Edit properties via a GUI
9179 bool wxRichTextCell::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
9181 // We need to gather common attributes for all selected cells.
9183 wxRichTextTable
* table
= wxDynamicCast(GetParent(), wxRichTextTable
);
9184 bool multipleCells
= false;
9185 wxRichTextAttr attr
;
9187 if (table
&& buffer
&& buffer
->GetRichTextCtrl() && buffer
->GetRichTextCtrl()->GetSelection().IsValid() &&
9188 buffer
->GetRichTextCtrl()->GetSelection().GetContainer() == GetParent())
9190 wxRichTextAttr clashingAttr
, absentAttr
;
9191 const wxRichTextSelection
& sel
= buffer
->GetRichTextCtrl()->GetSelection();
9193 int selectedCellCount
= 0;
9194 for (i
= 0; i
< sel
.GetCount(); i
++)
9196 const wxRichTextRange
& range
= sel
[i
];
9197 wxRichTextCell
* cell
= table
->GetCell(range
.GetStart());
9200 wxRichTextAttr cellStyle
= cell
->GetAttributes();
9202 CollectStyle(attr
, cellStyle
, clashingAttr
, absentAttr
);
9204 selectedCellCount
++;
9207 multipleCells
= selectedCellCount
> 1;
9211 attr
= GetAttributes();
9216 caption
= _("Multiple Cell Properties");
9218 caption
= _("Cell Properties");
9220 wxRichTextObjectPropertiesDialog
cellDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, caption
);
9221 cellDlg
.SetAttributes(attr
);
9223 wxRichTextSizePage
* sizePage
= wxDynamicCast(cellDlg
.FindPage(wxCLASSINFO(wxRichTextSizePage
)), wxRichTextSizePage
);
9226 // We don't want position and floating controls for a cell.
9227 sizePage
->ShowPositionControls(false);
9228 sizePage
->ShowFloatingControls(false);
9231 if (cellDlg
.ShowModal() == wxID_OK
)
9235 const wxRichTextSelection
& sel
= buffer
->GetRichTextCtrl()->GetSelection();
9236 // Apply the style; we interpret indeterminate attributes as 'don't touch this attribute'
9237 // since it may represent clashing attributes across multiple objects.
9238 table
->SetCellStyle(sel
, attr
);
9241 // For a single object, indeterminate attributes set by the user should be reflected in the
9242 // actual object style, so pass the wxRICHTEXT_SETSTYLE_RESET flag to assign
9243 // the style directly instead of applying (which ignores indeterminate attributes,
9244 // leaving them as they were).
9245 cellDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
9252 WX_DEFINE_OBJARRAY(wxRichTextObjectPtrArrayArray
)
9254 IMPLEMENT_DYNAMIC_CLASS(wxRichTextTable
, wxRichTextBox
)
9256 wxRichTextTable::wxRichTextTable(wxRichTextObject
* parent
): wxRichTextBox(parent
)
9262 // Draws the object.
9263 bool wxRichTextTable::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
9265 return wxRichTextBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
9268 WX_DECLARE_OBJARRAY(wxRect
, wxRichTextRectArray
);
9269 WX_DEFINE_OBJARRAY(wxRichTextRectArray
);
9271 // Lays the object out. rect is the space available for layout. Often it will
9272 // be the specified overall space for this object, if trying to constrain
9273 // layout to a particular size, or it could be the total space available in the
9274 // parent. rect is the overall size, so we must subtract margins and padding.
9275 // to get the actual available space.
9276 bool wxRichTextTable::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& WXUNUSED(parentRect
), int style
)
9278 SetPosition(rect
.GetPosition());
9280 // TODO: the meaty bit. Calculate sizes of all cells and rows. Try to use
9281 // minimum size if within alloted size, then divide up remaining size
9282 // between rows/cols.
9285 wxRichTextBuffer
* buffer
= GetBuffer();
9286 if (buffer
) scale
= buffer
->GetScale();
9288 wxRect availableSpace
= GetAvailableContentArea(dc
, context
, rect
);
9289 wxTextAttrDimensionConverter
converter(dc
, scale
, availableSpace
.GetSize());
9291 wxRichTextAttr
attr(GetAttributes());
9292 context
.ApplyVirtualAttributes(attr
, this);
9294 // If we have no fixed table size, and assuming we're not pushed for
9295 // space, then we don't have to try to stretch the table to fit the contents.
9296 bool stretchToFitTableWidth
= false;
9298 int tableWidth
= rect
.width
;
9299 if (attr
.GetTextBoxAttr().GetWidth().IsValid())
9301 tableWidth
= converter
.GetPixels(attr
.GetTextBoxAttr().GetWidth());
9303 // Fixed table width, so we do want to stretch columns out if necessary.
9304 stretchToFitTableWidth
= true;
9306 // Shouldn't be able to exceed the size passed to this function
9307 tableWidth
= wxMin(rect
.width
, tableWidth
);
9310 // Get internal padding
9311 int paddingLeft
= 0, paddingTop
= 0;
9312 if (attr
.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
9313 paddingLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetLeft());
9314 if (attr
.GetTextBoxAttr().GetPadding().GetTop().IsValid())
9315 paddingTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetTop());
9317 // Assume that left and top padding are also used for inter-cell padding.
9318 int paddingX
= paddingLeft
;
9319 int paddingY
= paddingTop
;
9321 int totalLeftMargin
= 0, totalRightMargin
= 0, totalTopMargin
= 0, totalBottomMargin
= 0;
9322 GetTotalMargin(dc
, buffer
, attr
, totalLeftMargin
, totalRightMargin
, totalTopMargin
, totalBottomMargin
);
9324 // Internal table width - the area for content
9325 int internalTableWidth
= tableWidth
- totalLeftMargin
- totalRightMargin
;
9327 int rowCount
= m_cells
.GetCount();
9328 if (m_colCount
== 0 || rowCount
== 0)
9330 wxRect
overallRect(rect
.x
, rect
.y
, totalLeftMargin
+ totalRightMargin
, totalTopMargin
+ totalBottomMargin
);
9331 SetCachedSize(overallRect
.GetSize());
9333 // Zero content size
9334 SetMinSize(overallRect
.GetSize());
9335 SetMaxSize(GetMinSize());
9339 // The final calculated widths
9340 wxArrayInt colWidths
;
9341 colWidths
.Add(0, m_colCount
);
9343 wxArrayInt absoluteColWidths
;
9344 absoluteColWidths
.Add(0, m_colCount
);
9346 wxArrayInt percentageColWidths
;
9347 percentageColWidths
.Add(0, m_colCount
);
9348 // wxArrayInt percentageColWidthsSpanning(m_colCount);
9349 // These are only relevant when the first column contains spanning information.
9350 // wxArrayInt columnSpans(m_colCount); // Each contains 1 for non-spanning cell, > 1 for spanning cell.
9351 wxArrayInt maxColWidths
;
9352 maxColWidths
.Add(0, m_colCount
);
9353 wxArrayInt minColWidths
;
9354 minColWidths
.Add(0, m_colCount
);
9356 wxSize
tableSize(tableWidth
, 0);
9360 for (i
= 0; i
< m_colCount
; i
++)
9362 absoluteColWidths
[i
] = 0;
9363 // absoluteColWidthsSpanning[i] = 0;
9364 percentageColWidths
[i
] = -1;
9365 // percentageColWidthsSpanning[i] = -1;
9367 maxColWidths
[i
] = 0;
9368 minColWidths
[i
] = 0;
9369 // columnSpans[i] = 1;
9372 // (0) Determine which cells are visible according to spans
9374 // __________________
9379 // |------------------|
9380 // |__________________| 4
9382 // To calculate cell visibility:
9383 // First find all spanning cells. Build an array of span records with start x, y and end x, y.
9384 // Then for each cell, test whether we're within one of those cells, and unless we're at the start of
9385 // that cell, hide the cell.
9387 // We can also use this array to match the size of spanning cells to the grid. Or just do
9388 // this when we iterate through all cells.
9390 // 0.1: add spanning cells to an array
9391 wxRichTextRectArray rectArray
;
9392 for (j
= 0; j
< m_rowCount
; j
++)
9394 for (i
= 0; i
< m_colCount
; i
++)
9396 wxRichTextBox
* cell
= GetCell(j
, i
);
9397 int colSpan
= 1, rowSpan
= 1;
9398 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9399 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9400 if (cell
->GetProperties().HasProperty(wxT("rowspan")))
9401 rowSpan
= cell
->GetProperties().GetPropertyLong(wxT("rowspan"));
9402 if (colSpan
> 1 || rowSpan
> 1)
9404 rectArray
.Add(wxRect(i
, j
, colSpan
, rowSpan
));
9408 // 0.2: find which cells are subsumed by a spanning cell
9409 for (j
= 0; j
< m_rowCount
; j
++)
9411 for (i
= 0; i
< m_colCount
; i
++)
9413 wxRichTextBox
* cell
= GetCell(j
, i
);
9414 if (rectArray
.GetCount() == 0)
9420 int colSpan
= 1, rowSpan
= 1;
9421 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9422 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9423 if (cell
->GetProperties().HasProperty(wxT("rowspan")))
9424 rowSpan
= cell
->GetProperties().GetPropertyLong(wxT("rowspan"));
9425 if (colSpan
> 1 || rowSpan
> 1)
9427 // Assume all spanning cells are shown
9433 for (k
= 0; k
< (int) rectArray
.GetCount(); k
++)
9435 if (rectArray
[k
].Contains(wxPoint(i
, j
)))
9447 // TODO: find the first spanned cell in each row that spans the most columns and doesn't
9448 // overlap with a spanned cell starting at a previous column position.
9449 // This means we need to keep an array of rects so we can check. However
9450 // it does also mean that some spans simply may not be taken into account
9451 // where there are different spans happening on different rows. In these cases,
9452 // they will simply be as wide as their constituent columns.
9454 // (1) Do an initial layout for all cells to get minimum and maximum size, and get
9455 // the absolute or percentage width of each column.
9457 for (j
= 0; j
< m_rowCount
; j
++)
9459 // First get the overall margins so we can calculate percentage widths based on
9460 // the available content space for all cells on the row
9462 int overallRowContentMargin
= 0;
9463 int visibleCellCount
= 0;
9465 for (i
= 0; i
< m_colCount
; i
++)
9467 wxRichTextBox
* cell
= GetCell(j
, i
);
9468 if (cell
->IsShown())
9470 int cellTotalLeftMargin
= 0, cellTotalRightMargin
= 0, cellTotalTopMargin
= 0, cellTotalBottomMargin
= 0;
9471 GetTotalMargin(dc
, buffer
, cell
->GetAttributes(), cellTotalLeftMargin
, cellTotalRightMargin
, cellTotalTopMargin
, cellTotalBottomMargin
);
9473 overallRowContentMargin
+= (cellTotalLeftMargin
+ cellTotalRightMargin
);
9474 visibleCellCount
++;
9478 // Add in inter-cell padding
9479 overallRowContentMargin
+= ((visibleCellCount
-1) * paddingX
);
9481 int rowContentWidth
= internalTableWidth
- overallRowContentMargin
;
9482 wxSize
rowTableSize(rowContentWidth
, 0);
9483 wxTextAttrDimensionConverter
converter(dc
, scale
, rowTableSize
);
9485 for (i
= 0; i
< m_colCount
; i
++)
9487 wxRichTextBox
* cell
= GetCell(j
, i
);
9488 if (cell
->IsShown())
9491 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9492 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9494 // Lay out cell to find min/max widths
9495 cell
->Invalidate(wxRICHTEXT_ALL
);
9496 cell
->Layout(dc
, context
, availableSpace
, availableSpace
, style
);
9500 int absoluteCellWidth
= -1;
9501 int percentageCellWidth
= -1;
9503 // I think we need to calculate percentages from the internal table size,
9504 // minus the padding between cells which we'll need to calculate from the
9505 // (number of VISIBLE cells - 1)*paddingX. Then percentages that add up to 100%
9506 // will add up to 100%. In CSS, the width specifies the cell's content rect width,
9507 // so if we want to conform to that we'll need to add in the overall cell margins.
9508 // However, this will make it difficult to specify percentages that add up to
9509 // 100% and still fit within the table width.
9510 // Let's say two cells have 50% width. They have 10 pixels of overall margin each.
9511 // The table content rect is 500 pixels and the inter-cell padding is 20 pixels.
9512 // If we're using internal content size for the width, we would calculate the
9513 // the overall cell width for n cells as:
9514 // (500 - 20*(n-1) - overallCellMargin1 - overallCellMargin2 - ...) * percentage / 100
9515 // + thisOverallCellMargin
9516 // = 500 - 20 - 10 - 10) * 0.5 + 10 = 240 pixels overall cell width.
9517 // Adding this back, we get 240 + 240 + 20 = 500 pixels.
9519 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9521 int w
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetWidth());
9522 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
9524 percentageCellWidth
= w
;
9528 absoluteCellWidth
= w
;
9530 // Override absolute width with minimum width if necessary
9531 if (cell
->GetMinSize().x
> 0 && absoluteCellWidth
!=1 && cell
->GetMinSize().x
> absoluteCellWidth
)
9532 absoluteCellWidth
= cell
->GetMinSize().x
;
9535 if (absoluteCellWidth
!= -1)
9537 if (absoluteCellWidth
> absoluteColWidths
[i
])
9538 absoluteColWidths
[i
] = absoluteCellWidth
;
9541 if (percentageCellWidth
!= -1)
9543 if (percentageCellWidth
> percentageColWidths
[i
])
9544 percentageColWidths
[i
] = percentageCellWidth
;
9547 if (colSpan
== 1 && cell
->GetMinSize().x
&& cell
->GetMinSize().x
> minColWidths
[i
])
9548 minColWidths
[i
] = cell
->GetMinSize().x
;
9549 if (colSpan
== 1 && cell
->GetMaxSize().x
&& cell
->GetMaxSize().x
> maxColWidths
[i
])
9550 maxColWidths
[i
] = cell
->GetMaxSize().x
;
9556 // (2) Allocate initial column widths from minimum widths, absolute values and proportions
9557 // TODO: simply merge this into (1).
9558 for (i
= 0; i
< m_colCount
; i
++)
9560 if (absoluteColWidths
[i
] > 0)
9562 colWidths
[i
] = absoluteColWidths
[i
];
9564 else if (percentageColWidths
[i
] > 0)
9566 colWidths
[i
] = percentageColWidths
[i
];
9568 // This is rubbish - we calculated the absolute widths from percentages, so
9569 // we can't do it again here.
9570 //colWidths[i] = (int) (double(percentageColWidths[i]) * double(tableWidth) / 100.0 + 0.5);
9574 // (3) Process absolute or proportional widths of spanning columns,
9575 // now that we know what our fixed column widths are going to be.
9576 // Spanned cells will try to adjust columns so the span will fit.
9577 // Even existing fixed column widths can be expanded if necessary.
9578 // Actually, currently fixed columns widths aren't adjusted; instead,
9579 // the algorithm favours earlier rows and adjusts unspecified column widths
9580 // the first time only. After that, we can't know whether the column has been
9581 // specified explicitly or not. (We could make a note if necessary.)
9582 for (j
= 0; j
< m_rowCount
; j
++)
9584 // First get the overall margins so we can calculate percentage widths based on
9585 // the available content space for all cells on the row
9587 int overallRowContentMargin
= 0;
9588 int visibleCellCount
= 0;
9590 for (i
= 0; i
< m_colCount
; i
++)
9592 wxRichTextBox
* cell
= GetCell(j
, i
);
9593 if (cell
->IsShown())
9595 int cellTotalLeftMargin
= 0, cellTotalRightMargin
= 0, cellTotalTopMargin
= 0, cellTotalBottomMargin
= 0;
9596 GetTotalMargin(dc
, buffer
, cell
->GetAttributes(), cellTotalLeftMargin
, cellTotalRightMargin
, cellTotalTopMargin
, cellTotalBottomMargin
);
9598 overallRowContentMargin
+= (cellTotalLeftMargin
+ cellTotalRightMargin
);
9599 visibleCellCount
++;
9603 // Add in inter-cell padding
9604 overallRowContentMargin
+= ((visibleCellCount
-1) * paddingX
);
9606 int rowContentWidth
= internalTableWidth
- overallRowContentMargin
;
9607 wxSize
rowTableSize(rowContentWidth
, 0);
9608 wxTextAttrDimensionConverter
converter(dc
, scale
, rowTableSize
);
9610 for (i
= 0; i
< m_colCount
; i
++)
9612 wxRichTextBox
* cell
= GetCell(j
, i
);
9613 if (cell
->IsShown())
9616 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9617 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9621 int spans
= wxMin(colSpan
, m_colCount
- i
);
9625 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9627 cellWidth
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetWidth());
9628 // Override absolute width with minimum width if necessary
9629 if (cell
->GetMinSize().x
> 0 && cellWidth
!=1 && cell
->GetMinSize().x
> cellWidth
)
9630 cellWidth
= cell
->GetMinSize().x
;
9634 // Do we want to do this? It's the only chance we get to
9635 // use the cell's min/max sizes, so we need to work out
9636 // how we're going to balance the unspecified spanning cell
9637 // width with the possibility more-constrained constituent cell widths.
9638 // Say there's a tiny bitmap giving it a max width of 10 pixels. We
9639 // don't want to constraint all the spanned columns to fit into this cell.
9640 // OK, let's say that if any of the constituent columns don't fit,
9641 // then we simply stop constraining the columns; instead, we'll just fit the spanning
9642 // cells to the columns later.
9643 cellWidth
= cell
->GetMinSize().x
;
9644 if (cell
->GetMaxSize().x
> cellWidth
)
9645 cellWidth
= cell
->GetMaxSize().x
;
9648 // Subtract the padding between cells
9649 int spanningWidth
= cellWidth
;
9650 spanningWidth
-= paddingX
* (spans
-1);
9652 if (spanningWidth
> 0)
9654 // Now share the spanning width between columns within that span
9655 // TODO: take into account min widths of columns within the span
9656 int spanningWidthLeft
= spanningWidth
;
9657 int stretchColCount
= 0;
9658 for (k
= i
; k
< (i
+spans
); k
++)
9660 if (colWidths
[k
] > 0) // absolute or proportional width has been specified
9661 spanningWidthLeft
-= colWidths
[k
];
9665 // Now divide what's left between the remaining columns
9667 if (stretchColCount
> 0)
9668 colShare
= spanningWidthLeft
/ stretchColCount
;
9669 int colShareRemainder
= spanningWidthLeft
- (colShare
* stretchColCount
);
9671 // If fixed-width columns are currently too big, then we'll later
9672 // stretch the spanned cell to fit.
9674 if (spanningWidthLeft
> 0)
9676 for (k
= i
; k
< (i
+spans
); k
++)
9678 if (colWidths
[k
] <= 0) // absolute or proportional width has not been specified
9680 int newWidth
= colShare
;
9681 if (k
== (i
+spans
-1))
9682 newWidth
+= colShareRemainder
; // ensure all pixels are filled
9683 colWidths
[k
] = newWidth
;
9694 // (4) Next, share any remaining space out between columns that have not yet been calculated.
9695 // TODO: take into account min widths of columns within the span
9696 int tableWidthMinusPadding
= internalTableWidth
- (m_colCount
-1)*paddingX
;
9697 int widthLeft
= tableWidthMinusPadding
;
9698 int stretchColCount
= 0;
9699 for (i
= 0; i
< m_colCount
; i
++)
9701 // TODO: we need to take into account min widths.
9702 // Subtract min width from width left, then
9703 // add the colShare to the min width
9704 if (colWidths
[i
] > 0) // absolute or proportional width has been specified
9705 widthLeft
-= colWidths
[i
];
9708 if (minColWidths
[i
] > 0)
9709 widthLeft
-= minColWidths
[i
];
9715 // Now divide what's left between the remaining columns
9717 if (stretchColCount
> 0)
9718 colShare
= widthLeft
/ stretchColCount
;
9719 int colShareRemainder
= widthLeft
- (colShare
* stretchColCount
);
9721 // Check we don't have enough space, in which case shrink all columns, overriding
9722 // any absolute/proportional widths
9723 // TODO: actually we would like to divide up the shrinkage according to size.
9724 // How do we calculate the proportions that will achieve this?
9725 // Could first choose an arbitrary value for stretching cells, and then calculate
9726 // factors to multiply each width by.
9727 // TODO: want to record this fact and pass to an iteration that tries e.g. min widths
9728 if (widthLeft
< 0 || (stretchToFitTableWidth
&& (stretchColCount
== 0)))
9730 colShare
= tableWidthMinusPadding
/ m_colCount
;
9731 colShareRemainder
= tableWidthMinusPadding
- (colShare
* m_colCount
);
9732 for (i
= 0; i
< m_colCount
; i
++)
9735 minColWidths
[i
] = 0;
9739 // We have to adjust the columns if either we need to shrink the
9740 // table to fit the parent/table width, or we explicitly set the
9741 // table width and need to stretch out the table.
9742 if (widthLeft
< 0 || stretchToFitTableWidth
)
9744 for (i
= 0; i
< m_colCount
; i
++)
9746 if (colWidths
[i
] <= 0) // absolute or proportional width has not been specified
9748 if (minColWidths
[i
] > 0)
9749 colWidths
[i
] = minColWidths
[i
] + colShare
;
9751 colWidths
[i
] = colShare
;
9752 if (i
== (m_colCount
-1))
9753 colWidths
[i
] += colShareRemainder
; // ensure all pixels are filled
9758 // TODO: if spanned cells have no specified or max width, make them the
9759 // as big as the columns they span. Do this for all spanned cells in all
9760 // rows, of course. Size any spanned cells left over at the end - even if they
9761 // have width > 0, make sure they're limited to the appropriate column edge.
9765 Sort out confusion between content width
9766 and overall width later. For now, assume we specify overall width.
9768 So, now we've laid out the table to fit into the given space
9769 and have used specified widths and minimum widths.
9771 Now we need to consider how we will try to take maximum width into account.
9775 // (??) TODO: take max width into account
9777 // (6) Lay out all cells again with the current values
9780 int y
= availableSpace
.y
;
9781 for (j
= 0; j
< m_rowCount
; j
++)
9783 int x
= availableSpace
.x
; // TODO: take into account centering etc.
9784 int maxCellHeight
= 0;
9785 int maxSpecifiedCellHeight
= 0;
9787 wxArrayInt actualWidths
;
9788 actualWidths
.Add(0, m_colCount
);
9790 wxTextAttrDimensionConverter
converter(dc
, scale
);
9791 for (i
= 0; i
< m_colCount
; i
++)
9793 wxRichTextCell
* cell
= GetCell(j
, i
);
9794 if (cell
->IsShown())
9796 // Get max specified cell height
9797 // Don't handle percentages for height
9798 if (cell
->GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && cell
->GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() != wxTEXT_ATTR_UNITS_PERCENTAGE
)
9800 int h
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetHeight());
9801 if (h
> maxSpecifiedCellHeight
)
9802 maxSpecifiedCellHeight
= h
;
9805 if (colWidths
[i
] > 0) // absolute or proportional width has been specified
9808 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9809 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9811 wxRect availableCellSpace
;
9813 // TODO: take into acount spans
9816 // Calculate the size of this spanning cell from its constituent columns
9818 int spans
= wxMin(colSpan
, m_colCount
- i
);
9819 for (k
= i
; k
< spans
; k
++)
9825 availableCellSpace
= wxRect(x
, y
, xx
, -1);
9828 availableCellSpace
= wxRect(x
, y
, colWidths
[i
], -1);
9830 // Store actual width so we can force cell to be the appropriate width on the final loop
9831 actualWidths
[i
] = availableCellSpace
.GetWidth();
9834 cell
->Invalidate(wxRICHTEXT_ALL
);
9835 cell
->Layout(dc
, context
, availableCellSpace
, availableSpace
, style
);
9837 // TODO: use GetCachedSize().x to compute 'natural' size
9839 x
+= (availableCellSpace
.GetWidth() + paddingX
);
9840 if (cell
->GetCachedSize().y
> maxCellHeight
)
9841 maxCellHeight
= cell
->GetCachedSize().y
;
9846 maxCellHeight
= wxMax(maxCellHeight
, maxSpecifiedCellHeight
);
9848 for (i
= 0; i
< m_colCount
; i
++)
9850 wxRichTextCell
* cell
= GetCell(j
, i
);
9851 if (cell
->IsShown())
9853 wxRect availableCellSpace
= wxRect(cell
->GetPosition(), wxSize(actualWidths
[i
], maxCellHeight
));
9854 // Lay out cell with new height
9855 cell
->Invalidate(wxRICHTEXT_ALL
);
9856 cell
->Layout(dc
, context
, availableCellSpace
, availableSpace
, style
);
9858 // Make sure the cell size really is the appropriate size,
9859 // not the calculated box size
9860 cell
->SetCachedSize(wxSize(actualWidths
[i
], maxCellHeight
));
9862 maxRight
= wxMax(maxRight
, cell
->GetPosition().x
+ cell
->GetCachedSize().x
);
9867 if (j
< (m_rowCount
-1))
9871 // We need to add back the margins etc.
9873 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
9874 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxRight
- availableSpace
.x
, y
- availableSpace
.y
));
9875 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
9876 SetCachedSize(marginRect
.GetSize());
9879 // TODO: calculate max size
9881 SetMaxSize(GetCachedSize());
9884 // TODO: calculate min size
9886 SetMinSize(GetCachedSize());
9889 // TODO: currently we use either a fixed table width or the parent's size.
9890 // We also want to be able to calculate the table width from its content,
9891 // whether using fixed column widths or cell content min/max width.
9892 // Probably need a boolean flag to say whether we need to stretch cells
9893 // to fit the table width, or to simply use min/max cell widths. The
9894 // trouble with this is that if cell widths are not specified, they
9895 // will be tiny; we could use arbitrary defaults but this seems unsatisfactory.
9896 // Anyway, ignoring that problem, we probably need to factor layout into a function
9897 // that can can calculate the maximum unconstrained layout in case table size is
9898 // not specified. Then LayoutToBestSize() can choose to use either parent size to
9899 // constrain Layout(), or the previously-calculated max size to constraint layout.
9904 // Finds the absolute position and row height for the given character position
9905 bool wxRichTextTable::FindPosition(wxDC
& dc
, wxRichTextDrawingContext
& context
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
9907 wxRichTextCell
* child
= GetCell(index
+1);
9910 // Find the position at the start of the child cell, since the table doesn't
9911 // have any caret position of its own.
9912 return child
->FindPosition(dc
, context
, -1, pt
, height
, forceLineStart
);
9918 // Get the cell at the given character position (in the range of the table).
9919 wxRichTextCell
* wxRichTextTable::GetCell(long pos
) const
9921 int row
= 0, col
= 0;
9922 if (GetCellRowColumnPosition(pos
, row
, col
))
9924 return GetCell(row
, col
);
9930 // Get the row/column for a given character position
9931 bool wxRichTextTable::GetCellRowColumnPosition(long pos
, int& row
, int& col
) const
9933 if (m_colCount
== 0 || m_rowCount
== 0)
9936 row
= (int) (pos
/ m_colCount
);
9937 col
= pos
- (row
* m_colCount
);
9939 wxASSERT(row
< m_rowCount
&& col
< m_colCount
);
9941 if (row
< m_rowCount
&& col
< m_colCount
)
9947 // Calculate range, taking row/cell ordering into account instead of relying
9948 // on list ordering.
9949 void wxRichTextTable::CalculateRange(long start
, long& end
)
9951 long current
= start
;
9952 long lastEnd
= current
;
9961 for (i
= 0; i
< m_rowCount
; i
++)
9963 for (j
= 0; j
< m_colCount
; j
++)
9965 wxRichTextCell
* child
= GetCell(i
, j
);
9970 child
->CalculateRange(current
, childEnd
);
9973 current
= childEnd
+ 1;
9978 // A top-level object always has a range of size 1,
9979 // because its children don't count at this level.
9981 m_range
.SetRange(start
, start
);
9983 // An object with no children has zero length
9984 if (m_children
.GetCount() == 0)
9986 m_ownRange
.SetRange(0, lastEnd
);
9989 // Gets the range size.
9990 bool wxRichTextTable::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
9992 return wxRichTextBox::GetRangeSize(range
, size
, descent
, dc
, context
, flags
, position
, partialExtents
);
9995 // Deletes content in the given range.
9996 bool wxRichTextTable::DeleteRange(const wxRichTextRange
& WXUNUSED(range
))
9998 // TODO: implement deletion of cells
10002 // Gets any text in this object for the given range.
10003 wxString
wxRichTextTable::GetTextForRange(const wxRichTextRange
& range
) const
10005 return wxRichTextBox::GetTextForRange(range
);
10008 // Copies this object.
10009 void wxRichTextTable::Copy(const wxRichTextTable
& obj
)
10011 wxRichTextBox::Copy(obj
);
10015 m_rowCount
= obj
.m_rowCount
;
10016 m_colCount
= obj
.m_colCount
;
10018 m_cells
.Add(wxRichTextObjectPtrArray(), m_rowCount
);
10021 for (i
= 0; i
< m_rowCount
; i
++)
10023 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
10024 for (j
= 0; j
< m_colCount
; j
++)
10026 wxRichTextCell
* cell
= wxDynamicCast(obj
.GetCell(i
, j
)->Clone(), wxRichTextCell
);
10029 colArray
.Add(cell
);
10034 void wxRichTextTable::ClearTable()
10040 bool wxRichTextTable::CreateTable(int rows
, int cols
)
10047 m_cells
.Add(wxRichTextObjectPtrArray(), rows
);
10050 for (i
= 0; i
< rows
; i
++)
10052 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
10053 for (j
= 0; j
< cols
; j
++)
10055 wxRichTextCell
* cell
= new wxRichTextCell
;
10057 cell
->AddParagraph(wxEmptyString
);
10059 colArray
.Add(cell
);
10066 wxRichTextCell
* wxRichTextTable::GetCell(int row
, int col
) const
10068 wxASSERT(row
< m_rowCount
);
10069 wxASSERT(col
< m_colCount
);
10071 if (row
< m_rowCount
&& col
< m_colCount
)
10073 wxRichTextObjectPtrArray
& colArray
= m_cells
[row
];
10074 wxRichTextObject
* obj
= colArray
[col
];
10075 return wxDynamicCast(obj
, wxRichTextCell
);
10081 // Returns a selection object specifying the selections between start and end character positions.
10082 // For example, a table would deduce what cells (of range length 1) are selected when dragging across the table.
10083 wxRichTextSelection
wxRichTextTable::GetSelection(long start
, long end
) const
10085 wxRichTextSelection selection
;
10086 selection
.SetContainer((wxRichTextTable
*) this);
10095 wxASSERT( start
>= 0 && end
< (m_colCount
* m_rowCount
));
10097 if (end
>= (m_colCount
* m_rowCount
))
10100 // We need to find the rectangle of cells that is described by the rectangle
10101 // with start, end as the diagonal. Make sure we don't add cells that are
10102 // not currenty visible because they are overlapped by spanning cells.
10104 --------------------------
10105 | 0 | 1 | 2 | 3 | 4 |
10106 --------------------------
10107 | 5 | 6 | 7 | 8 | 9 |
10108 --------------------------
10109 | 10 | 11 | 12 | 13 | 14 |
10110 --------------------------
10111 | 15 | 16 | 17 | 18 | 19 |
10112 --------------------------
10114 Let's say we select 6 -> 18.
10116 Left and right edge cols of rectangle are 1 and 3 inclusive. Find least/greatest to find
10117 which is left and which is right.
10119 Top and bottom edge rows are 1 and 3 inclusive. Again, find least/greatest to find top and bottom.
10121 Now go through rows from 1 to 3 and only add cells that are (a) within above column range
10127 int leftCol
= start
- m_colCount
* int(start
/m_colCount
);
10128 int rightCol
= end
- m_colCount
* int(end
/m_colCount
);
10130 int topRow
= int(start
/m_colCount
);
10131 int bottomRow
= int(end
/m_colCount
);
10133 if (leftCol
> rightCol
)
10135 int tmp
= rightCol
;
10136 rightCol
= leftCol
;
10140 if (topRow
> bottomRow
)
10142 int tmp
= bottomRow
;
10143 bottomRow
= topRow
;
10148 for (i
= topRow
; i
<= bottomRow
; i
++)
10150 for (j
= leftCol
; j
<= rightCol
; j
++)
10152 wxRichTextCell
* cell
= GetCell(i
, j
);
10153 if (cell
&& cell
->IsShown())
10154 selection
.Add(cell
->GetRange());
10161 // Sets the attributes for the cells specified by the selection.
10162 bool wxRichTextTable::SetCellStyle(const wxRichTextSelection
& selection
, const wxRichTextAttr
& style
, int flags
)
10164 if (selection
.GetContainer() != this)
10167 wxRichTextBuffer
* buffer
= GetBuffer();
10168 bool haveControl
= (buffer
&& buffer
->GetRichTextCtrl() != NULL
);
10169 bool withUndo
= haveControl
&& ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
10172 buffer
->BeginBatchUndo(_("Set Cell Style"));
10174 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
10177 wxRichTextCell
* cell
= wxDynamicCast(node
->GetData(), wxRichTextCell
);
10178 if (cell
&& selection
.WithinSelection(cell
->GetRange().GetStart()))
10179 SetStyle(cell
, style
, flags
);
10180 node
= node
->GetNext();
10183 // Do action, or delay it until end of batch.
10185 buffer
->EndBatchUndo();
10190 bool wxRichTextTable::DeleteRows(int startRow
, int noRows
)
10192 wxASSERT((startRow
+ noRows
) < m_rowCount
);
10193 if ((startRow
+ noRows
) >= m_rowCount
)
10197 for (i
= startRow
; i
< (startRow
+noRows
); i
++)
10199 wxRichTextObjectPtrArray
& colArray
= m_cells
[startRow
];
10200 for (j
= 0; j
< (int) colArray
.GetCount(); j
++)
10202 wxRichTextObject
* cell
= colArray
[j
];
10203 RemoveChild(cell
, true);
10206 // Keep deleting at the same position, since we move all
10208 m_cells
.RemoveAt(startRow
);
10211 m_rowCount
= m_rowCount
- noRows
;
10216 bool wxRichTextTable::DeleteColumns(int startCol
, int noCols
)
10218 wxASSERT((startCol
+ noCols
) < m_colCount
);
10219 if ((startCol
+ noCols
) >= m_colCount
)
10222 bool deleteRows
= (noCols
== m_colCount
);
10225 for (i
= 0; i
< m_rowCount
; i
++)
10227 wxRichTextObjectPtrArray
& colArray
= m_cells
[deleteRows
? 0 : i
];
10228 for (j
= startCol
; j
< (startCol
+noCols
); j
++)
10230 wxRichTextObject
* cell
= colArray
[j
];
10231 RemoveChild(cell
, true);
10235 m_cells
.RemoveAt(0);
10240 m_colCount
= m_colCount
- noCols
;
10245 bool wxRichTextTable::AddRows(int startRow
, int noRows
, const wxRichTextAttr
& attr
)
10247 wxASSERT(startRow
<= m_rowCount
);
10248 if (startRow
> m_rowCount
)
10252 for (i
= 0; i
< noRows
; i
++)
10255 if (startRow
== m_rowCount
)
10257 m_cells
.Add(wxRichTextObjectPtrArray());
10258 idx
= m_cells
.GetCount() - 1;
10262 m_cells
.Insert(wxRichTextObjectPtrArray(), startRow
+i
);
10266 wxRichTextObjectPtrArray
& colArray
= m_cells
[idx
];
10267 for (j
= 0; j
< m_colCount
; j
++)
10269 wxRichTextCell
* cell
= new wxRichTextCell
;
10270 cell
->GetAttributes() = attr
;
10273 colArray
.Add(cell
);
10277 m_rowCount
= m_rowCount
+ noRows
;
10281 bool wxRichTextTable::AddColumns(int startCol
, int noCols
, const wxRichTextAttr
& attr
)
10283 wxASSERT(startCol
<= m_colCount
);
10284 if (startCol
> m_colCount
)
10288 for (i
= 0; i
< m_rowCount
; i
++)
10290 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
10291 for (j
= 0; j
< noCols
; j
++)
10293 wxRichTextCell
* cell
= new wxRichTextCell
;
10294 cell
->GetAttributes() = attr
;
10298 if (startCol
== m_colCount
)
10299 colArray
.Add(cell
);
10301 colArray
.Insert(cell
, startCol
+j
);
10305 m_colCount
= m_colCount
+ noCols
;
10310 // Edit properties via a GUI
10311 bool wxRichTextTable::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
10313 wxRichTextObjectPropertiesDialog
boxDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Table Properties"));
10314 boxDlg
.SetAttributes(GetAttributes());
10316 if (boxDlg
.ShowModal() == wxID_OK
)
10318 boxDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
10326 * Module to initialise and clean up handlers
10329 class wxRichTextModule
: public wxModule
10331 DECLARE_DYNAMIC_CLASS(wxRichTextModule
)
10333 wxRichTextModule() {}
10336 wxRichTextBuffer::SetRenderer(new wxRichTextStdRenderer
);
10337 wxRichTextBuffer::InitStandardHandlers();
10338 wxRichTextParagraph::InitDefaultTabs();
10340 wxRichTextXMLHandler::RegisterNodeName(wxT("text"), wxT("wxRichTextPlainText"));
10341 wxRichTextXMLHandler::RegisterNodeName(wxT("symbol"), wxT("wxRichTextPlainText"));
10342 wxRichTextXMLHandler::RegisterNodeName(wxT("image"), wxT("wxRichTextImage"));
10343 wxRichTextXMLHandler::RegisterNodeName(wxT("paragraph"), wxT("wxRichTextParagraph"));
10344 wxRichTextXMLHandler::RegisterNodeName(wxT("paragraphlayout"), wxT("wxRichTextParagraphLayoutBox"));
10345 wxRichTextXMLHandler::RegisterNodeName(wxT("textbox"), wxT("wxRichTextBox"));
10346 wxRichTextXMLHandler::RegisterNodeName(wxT("cell"), wxT("wxRichTextCell"));
10347 wxRichTextXMLHandler::RegisterNodeName(wxT("table"), wxT("wxRichTextTable"));
10348 wxRichTextXMLHandler::RegisterNodeName(wxT("field"), wxT("wxRichTextField"));
10354 wxRichTextBuffer::CleanUpHandlers();
10355 wxRichTextBuffer::CleanUpDrawingHandlers();
10356 wxRichTextBuffer::CleanUpFieldTypes();
10357 wxRichTextXMLHandler::ClearNodeToClassMap();
10358 wxRichTextDecimalToRoman(-1);
10359 wxRichTextParagraph::ClearDefaultTabs();
10360 wxRichTextCtrl::ClearAvailableFontNames();
10361 wxRichTextBuffer::SetRenderer(NULL
);
10365 IMPLEMENT_DYNAMIC_CLASS(wxRichTextModule
, wxModule
)
10368 // If the richtext lib is dynamically loaded after the app has already started
10369 // (such as from wxPython) then the built-in module system will not init this
10370 // module. Provide this function to do it manually.
10371 void wxRichTextModuleInit()
10373 wxModule
* module = new wxRichTextModule
;
10375 wxModule::RegisterModule(module);
10380 * Commands for undo/redo
10384 wxRichTextCommand::wxRichTextCommand(const wxString
& name
, wxRichTextCommandId id
, wxRichTextBuffer
* buffer
,
10385 wxRichTextParagraphLayoutBox
* container
, wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
): wxCommand(true, name
)
10387 /* wxRichTextAction* action = */ new wxRichTextAction(this, name
, id
, buffer
, container
, ctrl
, ignoreFirstTime
);
10390 wxRichTextCommand::wxRichTextCommand(const wxString
& name
): wxCommand(true, name
)
10394 wxRichTextCommand::~wxRichTextCommand()
10399 void wxRichTextCommand::AddAction(wxRichTextAction
* action
)
10401 if (!m_actions
.Member(action
))
10402 m_actions
.Append(action
);
10405 bool wxRichTextCommand::Do()
10407 for (wxList::compatibility_iterator node
= m_actions
.GetFirst(); node
; node
= node
->GetNext())
10409 wxRichTextAction
* action
= (wxRichTextAction
*) node
->GetData();
10416 bool wxRichTextCommand::Undo()
10418 for (wxList::compatibility_iterator node
= m_actions
.GetLast(); node
; node
= node
->GetPrevious())
10420 wxRichTextAction
* action
= (wxRichTextAction
*) node
->GetData();
10427 void wxRichTextCommand::ClearActions()
10429 WX_CLEAR_LIST(wxList
, m_actions
);
10433 * Individual action
10437 wxRichTextAction::wxRichTextAction(wxRichTextCommand
* cmd
, const wxString
& name
, wxRichTextCommandId id
,
10438 wxRichTextBuffer
* buffer
, wxRichTextParagraphLayoutBox
* container
,
10439 wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
)
10443 m_containerAddress
.Create(buffer
, container
);
10444 m_ignoreThis
= ignoreFirstTime
;
10449 m_newParagraphs
.SetDefaultStyle(buffer
->GetDefaultStyle());
10450 m_newParagraphs
.SetBasicStyle(buffer
->GetBasicStyle());
10452 cmd
->AddAction(this);
10455 wxRichTextAction::~wxRichTextAction()
10461 // Returns the container that this action refers to, using the container address and top-level buffer.
10462 wxRichTextParagraphLayoutBox
* wxRichTextAction::GetContainer() const
10464 wxRichTextParagraphLayoutBox
* container
= wxDynamicCast(GetContainerAddress().GetObject(m_buffer
), wxRichTextParagraphLayoutBox
);
10469 void wxRichTextAction::CalculateRefreshOptimizations(wxArrayInt
& optimizationLineCharPositions
, wxArrayInt
& optimizationLineYPositions
)
10471 // Store a list of line start character and y positions so we can figure out which area
10472 // we need to refresh
10474 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10475 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10476 wxASSERT(container
!= NULL
);
10480 // NOTE: we're assuming that the buffer is laid out correctly at this point.
10481 // If we had several actions, which only invalidate and leave layout until the
10482 // paint handler is called, then this might not be true. So we may need to switch
10483 // optimisation on only when we're simply adding text and not simultaneously
10484 // deleting a selection, for example. Or, we make sure the buffer is laid out correctly
10485 // first, but of course this means we'll be doing it twice.
10486 if (!m_buffer
->IsDirty() && m_ctrl
) // can only do optimisation if the buffer is already laid out correctly
10488 wxSize clientSize
= m_ctrl
->GetUnscaledSize(m_ctrl
->GetClientSize());
10489 wxPoint firstVisiblePt
= m_ctrl
->GetUnscaledPoint(m_ctrl
->GetFirstVisiblePoint());
10490 int lastY
= firstVisiblePt
.y
+ clientSize
.y
;
10492 wxRichTextParagraph
* para
= container
->GetParagraphAtPosition(GetRange().GetStart());
10493 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(para
);
10496 wxRichTextParagraph
* child
= (wxRichTextParagraph
*) node
->GetData();
10497 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
10500 wxRichTextLine
* line
= node2
->GetData();
10501 wxPoint pt
= line
->GetAbsolutePosition();
10502 wxRichTextRange range
= line
->GetAbsoluteRange();
10506 node2
= wxRichTextLineList::compatibility_iterator();
10507 node
= wxRichTextObjectList::compatibility_iterator();
10509 else if (range
.GetStart() > GetPosition() && pt
.y
>= firstVisiblePt
.y
)
10511 optimizationLineCharPositions
.Add(range
.GetStart());
10512 optimizationLineYPositions
.Add(pt
.y
);
10516 node2
= node2
->GetNext();
10520 node
= node
->GetNext();
10526 bool wxRichTextAction::Do()
10528 m_buffer
->Modify(true);
10530 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10531 wxASSERT(container
!= NULL
);
10537 case wxRICHTEXT_INSERT
:
10539 // Store a list of line start character and y positions so we can figure out which area
10540 // we need to refresh
10541 wxArrayInt optimizationLineCharPositions
;
10542 wxArrayInt optimizationLineYPositions
;
10544 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10545 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
10548 container
->InsertFragment(GetRange().GetStart(), m_newParagraphs
);
10549 container
->UpdateRanges();
10551 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10552 // Layout() would stop prematurely at the top level.
10553 container
->InvalidateHierarchy(wxRichTextRange(wxMax(0, GetRange().GetStart()-1), GetRange().GetEnd()));
10555 long newCaretPosition
= GetPosition() + m_newParagraphs
.GetOwnRange().GetLength();
10557 // Character position to caret position
10558 newCaretPosition
--;
10560 // Don't take into account the last newline
10561 if (m_newParagraphs
.GetPartialParagraph())
10562 newCaretPosition
--;
10564 if (m_newParagraphs
.GetChildren().GetCount() > 1)
10566 wxRichTextObject
* p
= (wxRichTextObject
*) m_newParagraphs
.GetChildren().GetLast()->GetData();
10567 if (p
->GetRange().GetLength() == 1)
10568 newCaretPosition
--;
10571 newCaretPosition
= wxMin(newCaretPosition
, (container
->GetOwnRange().GetEnd()-1));
10573 UpdateAppearance(newCaretPosition
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */);
10575 wxRichTextEvent
cmdEvent(
10576 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED
,
10577 m_ctrl
? m_ctrl
->GetId() : -1);
10578 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10579 cmdEvent
.SetRange(GetRange());
10580 cmdEvent
.SetPosition(GetRange().GetStart());
10581 cmdEvent
.SetContainer(container
);
10583 m_buffer
->SendEvent(cmdEvent
);
10587 case wxRICHTEXT_DELETE
:
10589 wxArrayInt optimizationLineCharPositions
;
10590 wxArrayInt optimizationLineYPositions
;
10592 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10593 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
10596 container
->DeleteRange(GetRange());
10597 container
->UpdateRanges();
10598 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10599 // Layout() would stop prematurely at the top level.
10600 container
->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
10602 long caretPos
= GetRange().GetStart()-1;
10603 if (caretPos
>= container
->GetOwnRange().GetEnd())
10606 UpdateAppearance(caretPos
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */);
10608 wxRichTextEvent
cmdEvent(
10609 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED
,
10610 m_ctrl
? m_ctrl
->GetId() : -1);
10611 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10612 cmdEvent
.SetRange(GetRange());
10613 cmdEvent
.SetPosition(GetRange().GetStart());
10614 cmdEvent
.SetContainer(container
);
10616 m_buffer
->SendEvent(cmdEvent
);
10620 case wxRICHTEXT_CHANGE_STYLE
:
10621 case wxRICHTEXT_CHANGE_PROPERTIES
:
10623 ApplyParagraphs(GetNewParagraphs());
10625 // Invalidate the whole buffer if there were floating objects
10626 if (wxRichTextBuffer::GetFloatingLayoutMode() && container
->GetFloatingObjectCount() > 0)
10627 m_buffer
->InvalidateHierarchy(wxRICHTEXT_ALL
);
10630 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10631 // Layout() would stop prematurely at the top level.
10632 container
->InvalidateHierarchy(GetRange());
10635 UpdateAppearance(GetPosition());
10637 wxRichTextEvent
cmdEvent(
10638 m_cmdId
== wxRICHTEXT_CHANGE_STYLE
? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
: wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED
,
10639 m_ctrl
? m_ctrl
->GetId() : -1);
10640 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10641 cmdEvent
.SetRange(GetRange());
10642 cmdEvent
.SetPosition(GetRange().GetStart());
10643 cmdEvent
.SetContainer(container
);
10645 m_buffer
->SendEvent(cmdEvent
);
10649 case wxRICHTEXT_CHANGE_ATTRIBUTES
:
10651 wxRichTextObject
* obj
= m_objectAddress
.GetObject(m_buffer
); // container->GetChildAtPosition(GetRange().GetStart());
10654 wxRichTextAttr oldAttr
= obj
->GetAttributes();
10655 obj
->GetAttributes() = m_attributes
;
10656 m_attributes
= oldAttr
;
10659 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10660 // Layout() would stop prematurely at the top level.
10661 // Invalidate the whole buffer if there were floating objects
10662 if (wxRichTextBuffer::GetFloatingLayoutMode() && container
->GetFloatingObjectCount() > 0)
10663 m_buffer
->InvalidateHierarchy(wxRICHTEXT_ALL
);
10665 container
->InvalidateHierarchy(GetRange());
10667 UpdateAppearance(GetPosition());
10669 wxRichTextEvent
cmdEvent(
10670 wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
,
10671 m_ctrl
? m_ctrl
->GetId() : -1);
10672 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10673 cmdEvent
.SetRange(GetRange());
10674 cmdEvent
.SetPosition(GetRange().GetStart());
10675 cmdEvent
.SetContainer(container
);
10677 m_buffer
->SendEvent(cmdEvent
);
10681 case wxRICHTEXT_CHANGE_OBJECT
:
10683 wxRichTextObject
* obj
= m_objectAddress
.GetObject(m_buffer
);
10684 // wxRichTextObject* obj = container->GetChildAtPosition(GetRange().GetStart());
10685 if (obj
&& m_object
)
10687 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(obj
);
10690 wxRichTextObject
* obj
= node
->GetData();
10691 node
->SetData(m_object
);
10696 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10697 // Layout() would stop prematurely at the top level.
10698 // Invalidate the whole buffer if there were floating objects
10699 if (wxRichTextBuffer::GetFloatingLayoutMode() && container
->GetFloatingObjectCount() > 0)
10700 m_buffer
->InvalidateHierarchy(wxRICHTEXT_ALL
);
10702 container
->InvalidateHierarchy(GetRange());
10704 UpdateAppearance(GetPosition());
10706 // TODO: send new kind of modification event
10717 bool wxRichTextAction::Undo()
10719 m_buffer
->Modify(true);
10721 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10722 wxASSERT(container
!= NULL
);
10728 case wxRICHTEXT_INSERT
:
10730 wxArrayInt optimizationLineCharPositions
;
10731 wxArrayInt optimizationLineYPositions
;
10733 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10734 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
10737 container
->DeleteRange(GetRange());
10738 container
->UpdateRanges();
10740 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10741 // Layout() would stop prematurely at the top level.
10742 container
->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
10744 long newCaretPosition
= GetPosition() - 1;
10746 UpdateAppearance(newCaretPosition
, true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */);
10748 wxRichTextEvent
cmdEvent(
10749 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED
,
10750 m_ctrl
? m_ctrl
->GetId() : -1);
10751 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10752 cmdEvent
.SetRange(GetRange());
10753 cmdEvent
.SetPosition(GetRange().GetStart());
10754 cmdEvent
.SetContainer(container
);
10756 m_buffer
->SendEvent(cmdEvent
);
10760 case wxRICHTEXT_DELETE
:
10762 wxArrayInt optimizationLineCharPositions
;
10763 wxArrayInt optimizationLineYPositions
;
10765 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10766 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
10769 container
->InsertFragment(GetRange().GetStart(), m_oldParagraphs
);
10770 container
->UpdateRanges();
10772 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10773 // Layout() would stop prematurely at the top level.
10774 container
->InvalidateHierarchy(GetRange());
10776 UpdateAppearance(GetPosition(), true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */);
10778 wxRichTextEvent
cmdEvent(
10779 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED
,
10780 m_ctrl
? m_ctrl
->GetId() : -1);
10781 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10782 cmdEvent
.SetRange(GetRange());
10783 cmdEvent
.SetPosition(GetRange().GetStart());
10784 cmdEvent
.SetContainer(container
);
10786 m_buffer
->SendEvent(cmdEvent
);
10790 case wxRICHTEXT_CHANGE_STYLE
:
10791 case wxRICHTEXT_CHANGE_PROPERTIES
:
10793 ApplyParagraphs(GetOldParagraphs());
10794 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10795 // Layout() would stop prematurely at the top level.
10796 container
->InvalidateHierarchy(GetRange());
10798 UpdateAppearance(GetPosition());
10800 wxRichTextEvent
cmdEvent(
10801 m_cmdId
== wxRICHTEXT_CHANGE_STYLE
? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
: wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED
,
10802 m_ctrl
? m_ctrl
->GetId() : -1);
10803 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10804 cmdEvent
.SetRange(GetRange());
10805 cmdEvent
.SetPosition(GetRange().GetStart());
10806 cmdEvent
.SetContainer(container
);
10808 m_buffer
->SendEvent(cmdEvent
);
10812 case wxRICHTEXT_CHANGE_ATTRIBUTES
:
10813 case wxRICHTEXT_CHANGE_OBJECT
:
10824 /// Update the control appearance
10825 void wxRichTextAction::UpdateAppearance(long caretPosition
, bool sendUpdateEvent
, wxArrayInt
* optimizationLineCharPositions
, wxArrayInt
* optimizationLineYPositions
, bool isDoCmd
)
10827 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10828 wxASSERT(container
!= NULL
);
10834 m_ctrl
->SetFocusObject(container
);
10835 m_ctrl
->SetCaretPosition(caretPosition
);
10837 if (!m_ctrl
->IsFrozen())
10839 wxRect containerRect
= container
->GetRect();
10841 m_ctrl
->LayoutContent();
10843 // Refresh everything if there were floating objects or the container changed size
10844 // (we can't yet optimize in these cases, since more complex interaction with other content occurs)
10845 if ((wxRichTextBuffer::GetFloatingLayoutMode() && container
->GetFloatingObjectCount() > 0) || (container
->GetParent() && containerRect
!= container
->GetRect()))
10847 m_ctrl
->Refresh(false);
10851 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10852 // Find refresh rectangle if we are in a position to optimise refresh
10853 if ((m_cmdId
== wxRICHTEXT_INSERT
|| m_cmdId
== wxRICHTEXT_DELETE
) && optimizationLineCharPositions
)
10857 wxSize clientSize
= m_ctrl
->GetUnscaledSize(m_ctrl
->GetClientSize());
10858 wxPoint firstVisiblePt
= m_ctrl
->GetUnscaledPoint(m_ctrl
->GetFirstVisiblePoint());
10860 // Start/end positions
10862 int lastY
= firstVisiblePt
.y
+ clientSize
.y
;
10864 bool foundEnd
= false;
10866 // position offset - how many characters were inserted
10867 int positionOffset
= GetRange().GetLength();
10869 // Determine whether this is Do or Undo, and adjust positionOffset accordingly
10870 if ((m_cmdId
== wxRICHTEXT_DELETE
&& isDoCmd
) || (m_cmdId
== wxRICHTEXT_INSERT
&& !isDoCmd
))
10871 positionOffset
= - positionOffset
;
10873 // find the first line which is being drawn at the same position as it was
10874 // before. Since we're talking about a simple insertion, we can assume
10875 // that the rest of the window does not need to be redrawn.
10877 wxRichTextParagraph
* para
= container
->GetParagraphAtPosition(GetPosition());
10878 // Since we support floating layout, we should redraw the whole para instead of just
10879 // the first line touching the invalid range.
10882 firstY
= para
->GetPosition().y
;
10885 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(para
);
10888 wxRichTextParagraph
* child
= (wxRichTextParagraph
*) node
->GetData();
10889 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
10892 wxRichTextLine
* line
= node2
->GetData();
10893 wxPoint pt
= line
->GetAbsolutePosition();
10894 wxRichTextRange range
= line
->GetAbsoluteRange();
10896 // we want to find the first line that is in the same position
10897 // as before. This will mean we're at the end of the changed text.
10899 if (pt
.y
> lastY
) // going past the end of the window, no more info
10901 node2
= wxRichTextLineList::compatibility_iterator();
10902 node
= wxRichTextObjectList::compatibility_iterator();
10904 // Detect last line in the buffer
10905 else if (!node2
->GetNext() && para
->GetRange().Contains(container
->GetOwnRange().GetEnd()))
10907 // If deleting text, make sure we refresh below as well as above
10908 if (positionOffset
>= 0)
10911 lastY
= pt
.y
+ line
->GetSize().y
;
10914 node2
= wxRichTextLineList::compatibility_iterator();
10915 node
= wxRichTextObjectList::compatibility_iterator();
10921 // search for this line being at the same position as before
10922 for (i
= 0; i
< optimizationLineCharPositions
->GetCount(); i
++)
10924 if (((*optimizationLineCharPositions
)[i
] + positionOffset
== range
.GetStart()) &&
10925 ((*optimizationLineYPositions
)[i
] == pt
.y
))
10927 // Stop, we're now the same as we were
10932 node2
= wxRichTextLineList::compatibility_iterator();
10933 node
= wxRichTextObjectList::compatibility_iterator();
10941 node2
= node2
->GetNext();
10945 node
= node
->GetNext();
10948 firstY
= wxMax(firstVisiblePt
.y
, firstY
);
10950 lastY
= firstVisiblePt
.y
+ clientSize
.y
;
10952 // Convert to device coordinates
10953 wxRect
rect(m_ctrl
->GetPhysicalPoint(m_ctrl
->GetScaledPoint(wxPoint(firstVisiblePt
.x
, firstY
))), m_ctrl
->GetScaledSize(wxSize(clientSize
.x
, lastY
- firstY
)));
10954 m_ctrl
->RefreshRect(rect
);
10958 m_ctrl
->Refresh(false);
10960 m_ctrl
->PositionCaret();
10962 // This causes styles to persist when doing programmatic
10963 // content creation except when Freeze/Thaw is used, so
10964 // disable this and check for the consequences.
10965 // m_ctrl->SetDefaultStyleToCursorStyle();
10967 if (sendUpdateEvent
)
10968 wxTextCtrl::SendTextUpdatedEvent(m_ctrl
);
10973 /// Replace the buffer paragraphs with the new ones.
10974 void wxRichTextAction::ApplyParagraphs(const wxRichTextParagraphLayoutBox
& fragment
)
10976 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10977 wxASSERT(container
!= NULL
);
10981 wxRichTextObjectList::compatibility_iterator node
= fragment
.GetChildren().GetFirst();
10984 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
10985 wxASSERT (para
!= NULL
);
10987 // We'll replace the existing paragraph by finding the paragraph at this position,
10988 // delete its node data, and setting a copy as the new node data.
10989 // TODO: make more efficient by simply swapping old and new paragraph objects.
10991 wxRichTextParagraph
* existingPara
= container
->GetParagraphAtPosition(para
->GetRange().GetStart());
10994 wxRichTextObjectList::compatibility_iterator bufferParaNode
= container
->GetChildren().Find(existingPara
);
10995 if (bufferParaNode
)
10997 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(*para
);
10998 newPara
->SetParent(container
);
11000 bufferParaNode
->SetData(newPara
);
11002 delete existingPara
;
11006 node
= node
->GetNext();
11013 * This stores beginning and end positions for a range of data.
11016 WX_DEFINE_OBJARRAY(wxRichTextRangeArray
);
11018 /// Limit this range to be within 'range'
11019 bool wxRichTextRange::LimitTo(const wxRichTextRange
& range
)
11021 if (m_start
< range
.m_start
)
11022 m_start
= range
.m_start
;
11024 if (m_end
> range
.m_end
)
11025 m_end
= range
.m_end
;
11031 * wxRichTextImage implementation
11032 * This object represents an image.
11035 IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage
, wxRichTextObject
)
11037 wxRichTextImage::wxRichTextImage(const wxImage
& image
, wxRichTextObject
* parent
, wxRichTextAttr
* charStyle
):
11038 wxRichTextObject(parent
)
11041 m_imageBlock
.MakeImageBlockDefaultQuality(image
, wxBITMAP_TYPE_PNG
);
11043 SetAttributes(*charStyle
);
11046 wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock
& imageBlock
, wxRichTextObject
* parent
, wxRichTextAttr
* charStyle
):
11047 wxRichTextObject(parent
)
11050 m_imageBlock
= imageBlock
;
11052 SetAttributes(*charStyle
);
11055 void wxRichTextImage::Init()
11057 m_originalImageSize
= wxSize(-1, -1);
11060 /// Create a cached image at the required size
11061 bool wxRichTextImage::LoadImageCache(wxDC
& dc
, bool resetCache
)
11063 if (!m_imageBlock
.IsOk())
11066 // If we have an original image size, use that to compute the cached bitmap size
11067 // instead of loading the image each time. This way we can avoid loading
11068 // the image so long as the new cached bitmap size hasn't changed.
11071 if (resetCache
|| m_originalImageSize
.GetWidth() <= 0 || m_originalImageSize
.GetHeight() <= 0)
11073 m_imageCache
= wxNullBitmap
;
11075 m_imageBlock
.Load(image
);
11079 m_originalImageSize
= wxSize(image
.GetWidth(), image
.GetHeight());
11082 int width
= m_originalImageSize
.GetWidth();
11083 int height
= m_originalImageSize
.GetHeight();
11085 int parentWidth
= 0;
11086 int parentHeight
= 0;
11089 int maxHeight
= -1;
11091 wxRichTextBuffer
* buffer
= GetBuffer();
11095 if (buffer
->GetRichTextCtrl())
11097 // Subtract borders
11098 sz
= buffer
->GetRichTextCtrl()->GetClientSize();
11100 // Use a minimum size to stop images becoming very small
11101 sz
.x
= wxMax(sz
.x
, 100);
11102 sz
.y
= wxMax(sz
.y
, 100);
11104 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
11105 marginRect
= wxRect(0, 0, sz
.x
, sz
.y
);
11106 buffer
->GetBoxRects(dc
, buffer
, buffer
->GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
11108 sz
= contentRect
.GetSize();
11110 // Start with a maximum width of the control size, even if not specified by the content,
11111 // to minimize the amount of picture overlapping the right-hand side
11115 sz
= buffer
->GetCachedSize();
11116 parentWidth
= sz
.GetWidth();
11117 parentHeight
= sz
.GetHeight();
11120 if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetWidth().GetValue() > 0)
11122 if (parentWidth
> 0 && GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
11123 width
= (int) ((GetAttributes().GetTextBoxAttr().GetWidth().GetValue() * parentWidth
)/100.0);
11124 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
11125 width
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetWidth().GetValue());
11126 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
11127 width
= GetAttributes().GetTextBoxAttr().GetWidth().GetValue();
11130 // Limit to max width
11132 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() > 0)
11136 if (parentWidth
> 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
11137 mw
= (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() * parentWidth
)/100.0);
11138 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
11139 mw
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue());
11140 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
11141 mw
= GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue();
11143 // If we already have a smaller max width due to the constraints of the control size,
11144 // don't use the larger max width.
11145 if (mw
!= -1 && ((maxWidth
== -1) || (mw
< maxWidth
)))
11149 if (maxWidth
> 0 && width
> maxWidth
)
11152 // Preserve the aspect ratio
11153 if (width
!= m_originalImageSize
.GetWidth())
11154 height
= (int) (float(m_originalImageSize
.GetHeight()) * (float(width
)/float(m_originalImageSize
.GetWidth())));
11156 if (GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetHeight().GetValue() > 0)
11158 if (parentHeight
> 0 && GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
11159 height
= (int) ((GetAttributes().GetTextBoxAttr().GetHeight().GetValue() * parentHeight
)/100.0);
11160 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
11161 height
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetHeight().GetValue());
11162 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
11163 height
= GetAttributes().GetTextBoxAttr().GetHeight().GetValue();
11165 // Preserve the aspect ratio
11166 if (height
!= m_originalImageSize
.GetHeight())
11167 width
= (int) (float(m_originalImageSize
.GetWidth()) * (float(height
)/float(m_originalImageSize
.GetHeight())));
11170 // Limit to max height
11172 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() > 0)
11174 if (parentHeight
> 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
11175 maxHeight
= (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() * parentHeight
)/100.0);
11176 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
11177 maxHeight
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue());
11178 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
11179 maxHeight
= GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue();
11182 if (maxHeight
> 0 && height
> maxHeight
)
11184 height
= maxHeight
;
11186 // Preserve the aspect ratio
11187 if (height
!= m_originalImageSize
.GetHeight())
11188 width
= (int) (float(m_originalImageSize
.GetWidth()) * (float(height
)/float(m_originalImageSize
.GetHeight())));
11191 // Prevent the use of zero size
11192 width
= wxMax(1, width
);
11193 height
= wxMax(1, height
);
11195 if (m_imageCache
.IsOk() && m_imageCache
.GetWidth() == width
&& m_imageCache
.GetHeight() == height
)
11197 // Do nothing, we didn't need to change the image cache
11203 m_imageBlock
.Load(image
);
11208 if (image
.GetWidth() == width
&& image
.GetHeight() == height
)
11209 m_imageCache
= wxBitmap(image
);
11212 // If the original width and height is small, e.g. 400 or below,
11213 // scale up and then down to improve image quality. This can make
11214 // a big difference, with not much performance hit.
11215 int upscaleThreshold
= 400;
11217 if (image
.GetWidth() <= upscaleThreshold
|| image
.GetHeight() <= upscaleThreshold
)
11219 img
= image
.Scale(image
.GetWidth()*2, image
.GetHeight()*2);
11220 img
.Rescale(width
, height
, wxIMAGE_QUALITY_HIGH
);
11223 img
= image
.Scale(width
, height
, wxIMAGE_QUALITY_HIGH
);
11224 m_imageCache
= wxBitmap(img
);
11228 return m_imageCache
.IsOk();
11232 bool wxRichTextImage::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& WXUNUSED(range
), const wxRichTextSelection
& selection
, const wxRect
& rect
, int WXUNUSED(descent
), int WXUNUSED(style
))
11237 // Don't need cached size AFAIK
11238 // wxSize size = GetCachedSize();
11239 if (!LoadImageCache(dc
))
11242 wxRichTextAttr
attr(GetAttributes());
11243 context
.ApplyVirtualAttributes(attr
, this);
11245 DrawBoxAttributes(dc
, GetBuffer(), attr
, wxRect(rect
.GetPosition(), GetCachedSize()));
11247 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
11248 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
11249 marginRect
= rect
; // outer rectangle, will calculate contentRect
11250 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
11252 dc
.DrawBitmap(m_imageCache
, contentRect
.x
, contentRect
.y
, true);
11254 if (selection
.WithinSelection(GetRange().GetStart(), this))
11256 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
11257 wxCheckSetPen(dc
, *wxBLACK_PEN
);
11258 dc
.SetLogicalFunction(wxINVERT
);
11259 dc
.DrawRectangle(contentRect
);
11260 dc
.SetLogicalFunction(wxCOPY
);
11266 /// Lay the item out
11267 bool wxRichTextImage::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& WXUNUSED(parentRect
), int WXUNUSED(style
))
11269 if (!LoadImageCache(dc
))
11272 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
11273 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
11274 contentRect
= wxRect(wxPoint(0,0), imageSize
);
11276 wxRichTextAttr
attr(GetAttributes());
11277 context
.ApplyVirtualAttributes(attr
, this);
11279 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
11281 wxSize overallSize
= marginRect
.GetSize();
11283 SetCachedSize(overallSize
);
11284 SetMaxSize(overallSize
);
11285 SetMinSize(overallSize
);
11286 SetPosition(rect
.GetPosition());
11291 /// Get/set the object size for the given range. Returns false if the range
11292 /// is invalid for this object.
11293 bool wxRichTextImage::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& WXUNUSED(descent
), wxDC
& dc
, wxRichTextDrawingContext
& context
, int WXUNUSED(flags
), wxPoint
WXUNUSED(position
), wxArrayInt
* partialExtents
) const
11295 if (!range
.IsWithin(GetRange()))
11298 if (!((wxRichTextImage
*)this)->LoadImageCache(dc
))
11300 size
.x
= 0; size
.y
= 0;
11301 if (partialExtents
)
11302 partialExtents
->Add(0);
11306 wxRichTextAttr
attr(GetAttributes());
11307 context
.ApplyVirtualAttributes(attr
, (wxRichTextObject
*) this);
11309 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
11310 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
11311 contentRect
= wxRect(wxPoint(0,0), imageSize
);
11312 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
11314 wxSize overallSize
= marginRect
.GetSize();
11316 if (partialExtents
)
11317 partialExtents
->Add(overallSize
.x
);
11319 size
= overallSize
;
11324 // Get the 'natural' size for an object. For an image, it would be the
11326 wxTextAttrSize
wxRichTextImage::GetNaturalSize() const
11328 wxTextAttrSize size
;
11329 if (GetImageCache().IsOk())
11331 size
.SetWidth(GetImageCache().GetWidth(), wxTEXT_ATTR_UNITS_PIXELS
);
11332 size
.SetHeight(GetImageCache().GetHeight(), wxTEXT_ATTR_UNITS_PIXELS
);
11339 void wxRichTextImage::Copy(const wxRichTextImage
& obj
)
11341 wxRichTextObject::Copy(obj
);
11343 m_imageBlock
= obj
.m_imageBlock
;
11344 m_originalImageSize
= obj
.m_originalImageSize
;
11347 /// Edit properties via a GUI
11348 bool wxRichTextImage::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
11350 wxRichTextObjectPropertiesDialog
imageDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Picture Properties"));
11351 imageDlg
.SetAttributes(GetAttributes());
11353 if (imageDlg
.ShowModal() == wxID_OK
)
11355 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
11356 // indeterminate in the object.
11357 imageDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
11369 /// Compare two attribute objects
11370 bool wxTextAttrEq(const wxRichTextAttr
& attr1
, const wxRichTextAttr
& attr2
)
11372 return (attr1
== attr2
);
11376 bool wxRichTextTabsEq(const wxArrayInt
& tabs1
, const wxArrayInt
& tabs2
)
11378 if (tabs1
.GetCount() != tabs2
.GetCount())
11382 for (i
= 0; i
< tabs1
.GetCount(); i
++)
11384 if (tabs1
[i
] != tabs2
[i
])
11390 bool wxRichTextApplyStyle(wxRichTextAttr
& destStyle
, const wxRichTextAttr
& style
, wxRichTextAttr
* compareWith
)
11392 return destStyle
.Apply(style
, compareWith
);
11395 // Remove attributes
11396 bool wxRichTextRemoveStyle(wxRichTextAttr
& destStyle
, const wxRichTextAttr
& style
)
11398 return destStyle
.RemoveStyle(style
);
11401 /// Combine two bitlists, specifying the bits of interest with separate flags.
11402 bool wxRichTextCombineBitlists(int& valueA
, int valueB
, int& flagsA
, int flagsB
)
11404 return wxRichTextAttr::CombineBitlists(valueA
, valueB
, flagsA
, flagsB
);
11407 /// Compare two bitlists
11408 bool wxRichTextBitlistsEqPartial(int valueA
, int valueB
, int flags
)
11410 return wxRichTextAttr::BitlistsEqPartial(valueA
, valueB
, flags
);
11413 /// Split into paragraph and character styles
11414 bool wxRichTextSplitParaCharStyles(const wxRichTextAttr
& style
, wxRichTextAttr
& parStyle
, wxRichTextAttr
& charStyle
)
11416 return wxRichTextAttr::SplitParaCharStyles(style
, parStyle
, charStyle
);
11419 /// Convert a decimal to Roman numerals
11420 wxString
wxRichTextDecimalToRoman(long n
)
11422 static wxArrayInt decimalNumbers
;
11423 static wxArrayString romanNumbers
;
11428 decimalNumbers
.Clear();
11429 romanNumbers
.Clear();
11430 return wxEmptyString
;
11433 if (decimalNumbers
.GetCount() == 0)
11435 #define wxRichTextAddDecRom(n, r) decimalNumbers.Add(n); romanNumbers.Add(r);
11437 wxRichTextAddDecRom(1000, wxT("M"));
11438 wxRichTextAddDecRom(900, wxT("CM"));
11439 wxRichTextAddDecRom(500, wxT("D"));
11440 wxRichTextAddDecRom(400, wxT("CD"));
11441 wxRichTextAddDecRom(100, wxT("C"));
11442 wxRichTextAddDecRom(90, wxT("XC"));
11443 wxRichTextAddDecRom(50, wxT("L"));
11444 wxRichTextAddDecRom(40, wxT("XL"));
11445 wxRichTextAddDecRom(10, wxT("X"));
11446 wxRichTextAddDecRom(9, wxT("IX"));
11447 wxRichTextAddDecRom(5, wxT("V"));
11448 wxRichTextAddDecRom(4, wxT("IV"));
11449 wxRichTextAddDecRom(1, wxT("I"));
11455 while (n
> 0 && i
< 13)
11457 if (n
>= decimalNumbers
[i
])
11459 n
-= decimalNumbers
[i
];
11460 roman
+= romanNumbers
[i
];
11467 if (roman
.IsEmpty())
11473 * wxRichTextFileHandler
11474 * Base class for file handlers
11477 IMPLEMENT_CLASS(wxRichTextFileHandler
, wxObject
)
11479 #if wxUSE_FFILE && wxUSE_STREAMS
11480 bool wxRichTextFileHandler::LoadFile(wxRichTextBuffer
*buffer
, const wxString
& filename
)
11482 wxFFileInputStream
stream(filename
);
11484 return LoadFile(buffer
, stream
);
11489 bool wxRichTextFileHandler::SaveFile(wxRichTextBuffer
*buffer
, const wxString
& filename
)
11491 wxFFileOutputStream
stream(filename
);
11493 return SaveFile(buffer
, stream
);
11497 #endif // wxUSE_FFILE && wxUSE_STREAMS
11499 /// Can we handle this filename (if using files)? By default, checks the extension.
11500 bool wxRichTextFileHandler::CanHandle(const wxString
& filename
) const
11502 wxString path
, file
, ext
;
11503 wxFileName::SplitPath(filename
, & path
, & file
, & ext
);
11505 return (ext
.Lower() == GetExtension());
11509 * wxRichTextTextHandler
11510 * Plain text handler
11513 IMPLEMENT_CLASS(wxRichTextPlainTextHandler
, wxRichTextFileHandler
)
11516 bool wxRichTextPlainTextHandler::DoLoadFile(wxRichTextBuffer
*buffer
, wxInputStream
& stream
)
11518 if (!stream
.IsOk())
11524 while (!stream
.Eof())
11526 int ch
= stream
.GetC();
11530 if (ch
== 10 && lastCh
!= 13)
11533 if (ch
> 0 && ch
!= 10)
11540 buffer
->ResetAndClearCommands();
11542 buffer
->AddParagraphs(str
);
11543 buffer
->UpdateRanges();
11548 bool wxRichTextPlainTextHandler::DoSaveFile(wxRichTextBuffer
*buffer
, wxOutputStream
& stream
)
11550 if (!stream
.IsOk())
11553 wxString text
= buffer
->GetText();
11555 wxString newLine
= wxRichTextLineBreakChar
;
11556 text
.Replace(newLine
, wxT("\n"));
11558 wxCharBuffer buf
= text
.ToAscii();
11560 stream
.Write((const char*) buf
, text
.length());
11563 #endif // wxUSE_STREAMS
11566 * Stores information about an image, in binary in-memory form
11569 wxRichTextImageBlock::wxRichTextImageBlock()
11574 wxRichTextImageBlock::wxRichTextImageBlock(const wxRichTextImageBlock
& block
):wxObject()
11580 wxRichTextImageBlock::~wxRichTextImageBlock()
11585 void wxRichTextImageBlock::Init()
11589 m_imageType
= wxBITMAP_TYPE_INVALID
;
11592 void wxRichTextImageBlock::Clear()
11596 m_imageType
= wxBITMAP_TYPE_INVALID
;
11600 // Load the original image into a memory block.
11601 // If the image is not a JPEG, we must convert it into a JPEG
11602 // to conserve space.
11603 // If it's not a JPEG we can make use of 'image', already scaled, so we don't have to
11604 // load the image a 2nd time.
11606 bool wxRichTextImageBlock::MakeImageBlock(const wxString
& filename
, wxBitmapType imageType
,
11607 wxImage
& image
, bool convertToJPEG
)
11609 m_imageType
= imageType
;
11611 wxString
filenameToRead(filename
);
11612 bool removeFile
= false;
11614 if (imageType
== wxBITMAP_TYPE_INVALID
)
11615 return false; // Could not determine image type
11617 if ((imageType
!= wxBITMAP_TYPE_JPEG
) && convertToJPEG
)
11619 wxString tempFile
=
11620 wxFileName::CreateTempFileName(_("image"));
11622 wxASSERT(!tempFile
.IsEmpty());
11624 image
.SaveFile(tempFile
, wxBITMAP_TYPE_JPEG
);
11625 filenameToRead
= tempFile
;
11628 m_imageType
= wxBITMAP_TYPE_JPEG
;
11631 if (!file
.Open(filenameToRead
))
11634 m_dataSize
= (size_t) file
.Length();
11639 m_data
= ReadBlock(filenameToRead
, m_dataSize
);
11642 wxRemoveFile(filenameToRead
);
11644 return (m_data
!= NULL
);
11647 // Make an image block from the wxImage in the given
11649 bool wxRichTextImageBlock::MakeImageBlock(wxImage
& image
, wxBitmapType imageType
, int quality
)
11651 image
.SetOption(wxT("quality"), quality
);
11653 if (imageType
== wxBITMAP_TYPE_INVALID
)
11654 return false; // Could not determine image type
11656 return DoMakeImageBlock(image
, imageType
);
11659 // Uses a const wxImage for efficiency, but can't set quality (only relevant for JPEG)
11660 bool wxRichTextImageBlock::MakeImageBlockDefaultQuality(const wxImage
& image
, wxBitmapType imageType
)
11662 if (imageType
== wxBITMAP_TYPE_INVALID
)
11663 return false; // Could not determine image type
11665 return DoMakeImageBlock(image
, imageType
);
11668 // Makes the image block
11669 bool wxRichTextImageBlock::DoMakeImageBlock(const wxImage
& image
, wxBitmapType imageType
)
11671 wxMemoryOutputStream memStream
;
11672 if (!image
.SaveFile(memStream
, imageType
))
11677 unsigned char* block
= new unsigned char[memStream
.GetSize()];
11685 m_imageType
= imageType
;
11686 m_dataSize
= memStream
.GetSize();
11688 memStream
.CopyTo(m_data
, m_dataSize
);
11690 return (m_data
!= NULL
);
11694 bool wxRichTextImageBlock::Write(const wxString
& filename
)
11696 return WriteBlock(filename
, m_data
, m_dataSize
);
11699 void wxRichTextImageBlock::Copy(const wxRichTextImageBlock
& block
)
11701 m_imageType
= block
.m_imageType
;
11703 m_dataSize
= block
.m_dataSize
;
11704 if (m_dataSize
== 0)
11707 m_data
= new unsigned char[m_dataSize
];
11709 for (i
= 0; i
< m_dataSize
; i
++)
11710 m_data
[i
] = block
.m_data
[i
];
11714 void wxRichTextImageBlock::operator=(const wxRichTextImageBlock
& block
)
11719 // Load a wxImage from the block
11720 bool wxRichTextImageBlock::Load(wxImage
& image
)
11725 // Read in the image.
11727 wxMemoryInputStream
mstream(m_data
, m_dataSize
);
11728 bool success
= image
.LoadFile(mstream
, GetImageType());
11730 wxString tempFile
= wxFileName::CreateTempFileName(_("image"));
11731 wxASSERT(!tempFile
.IsEmpty());
11733 if (!WriteBlock(tempFile
, m_data
, m_dataSize
))
11737 success
= image
.LoadFile(tempFile
, GetImageType());
11738 wxRemoveFile(tempFile
);
11744 // Write data in hex to a stream
11745 bool wxRichTextImageBlock::WriteHex(wxOutputStream
& stream
)
11747 if (m_dataSize
== 0)
11750 int bufSize
= 100000;
11751 if (int(2*m_dataSize
) < bufSize
)
11752 bufSize
= 2*m_dataSize
;
11753 char* buf
= new char[bufSize
+1];
11755 int left
= m_dataSize
;
11760 if (left
*2 > bufSize
)
11762 n
= bufSize
; left
-= (bufSize
/2);
11766 n
= left
*2; left
= 0;
11770 for (i
= 0; i
< (n
/2); i
++)
11772 wxDecToHex(m_data
[j
], b
, b
+1);
11777 stream
.Write((const char*) buf
, n
);
11783 // Read data in hex from a stream
11784 bool wxRichTextImageBlock::ReadHex(wxInputStream
& stream
, int length
, wxBitmapType imageType
)
11786 int dataSize
= length
/2;
11791 // create a null terminated temporary string:
11795 m_data
= new unsigned char[dataSize
];
11797 for (i
= 0; i
< dataSize
; i
++)
11799 str
[0] = (char)stream
.GetC();
11800 str
[1] = (char)stream
.GetC();
11802 m_data
[i
] = (unsigned char)wxHexToDec(str
);
11805 m_dataSize
= dataSize
;
11806 m_imageType
= imageType
;
11811 // Allocate and read from stream as a block of memory
11812 unsigned char* wxRichTextImageBlock::ReadBlock(wxInputStream
& stream
, size_t size
)
11814 unsigned char* block
= new unsigned char[size
];
11818 stream
.Read(block
, size
);
11823 unsigned char* wxRichTextImageBlock::ReadBlock(const wxString
& filename
, size_t size
)
11825 wxFileInputStream
stream(filename
);
11826 if (!stream
.IsOk())
11829 return ReadBlock(stream
, size
);
11832 // Write memory block to stream
11833 bool wxRichTextImageBlock::WriteBlock(wxOutputStream
& stream
, unsigned char* block
, size_t size
)
11835 stream
.Write((void*) block
, size
);
11836 return stream
.IsOk();
11840 // Write memory block to file
11841 bool wxRichTextImageBlock::WriteBlock(const wxString
& filename
, unsigned char* block
, size_t size
)
11843 wxFileOutputStream
outStream(filename
);
11844 if (!outStream
.IsOk())
11847 return WriteBlock(outStream
, block
, size
);
11850 // Gets the extension for the block's type
11851 wxString
wxRichTextImageBlock::GetExtension() const
11853 wxImageHandler
* handler
= wxImage::FindHandler(GetImageType());
11855 return handler
->GetExtension();
11857 return wxEmptyString
;
11863 * The data object for a wxRichTextBuffer
11866 const wxChar
*wxRichTextBufferDataObject::ms_richTextBufferFormatId
= wxT("wxShape");
11868 wxRichTextBufferDataObject::wxRichTextBufferDataObject(wxRichTextBuffer
* richTextBuffer
)
11870 m_richTextBuffer
= richTextBuffer
;
11872 // this string should uniquely identify our format, but is otherwise
11874 m_formatRichTextBuffer
.SetId(GetRichTextBufferFormatId());
11876 SetFormat(m_formatRichTextBuffer
);
11879 wxRichTextBufferDataObject::~wxRichTextBufferDataObject()
11881 delete m_richTextBuffer
;
11884 // after a call to this function, the richTextBuffer is owned by the caller and it
11885 // is responsible for deleting it!
11886 wxRichTextBuffer
* wxRichTextBufferDataObject::GetRichTextBuffer()
11888 wxRichTextBuffer
* richTextBuffer
= m_richTextBuffer
;
11889 m_richTextBuffer
= NULL
;
11891 return richTextBuffer
;
11894 wxDataFormat
wxRichTextBufferDataObject::GetPreferredFormat(Direction
WXUNUSED(dir
)) const
11896 return m_formatRichTextBuffer
;
11899 size_t wxRichTextBufferDataObject::GetDataSize() const
11901 if (!m_richTextBuffer
)
11907 wxStringOutputStream
stream(& bufXML
);
11908 if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
))
11910 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
11916 wxCharBuffer buffer
= bufXML
.mb_str(wxConvUTF8
);
11917 return strlen(buffer
) + 1;
11919 return bufXML
.Length()+1;
11923 bool wxRichTextBufferDataObject::GetDataHere(void *pBuf
) const
11925 if (!pBuf
|| !m_richTextBuffer
)
11931 wxStringOutputStream
stream(& bufXML
);
11932 if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
))
11934 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
11940 wxCharBuffer buffer
= bufXML
.mb_str(wxConvUTF8
);
11941 size_t len
= strlen(buffer
);
11942 memcpy((char*) pBuf
, (const char*) buffer
, len
);
11943 ((char*) pBuf
)[len
] = 0;
11945 size_t len
= bufXML
.Length();
11946 memcpy((char*) pBuf
, (const char*) bufXML
.c_str(), len
);
11947 ((char*) pBuf
)[len
] = 0;
11953 bool wxRichTextBufferDataObject::SetData(size_t WXUNUSED(len
), const void *buf
)
11955 wxDELETE(m_richTextBuffer
);
11957 wxString
bufXML((const char*) buf
, wxConvUTF8
);
11959 m_richTextBuffer
= new wxRichTextBuffer
;
11961 wxStringInputStream
stream(bufXML
);
11962 if (!m_richTextBuffer
->LoadFile(stream
, wxRICHTEXT_TYPE_XML
))
11964 wxLogError(wxT("Could not read the buffer from an XML stream.\nYou may have forgotten to add the XML file handler."));
11966 wxDELETE(m_richTextBuffer
);
11978 * wxRichTextFontTable
11979 * Manages quick access to a pool of fonts for rendering rich text
11982 WX_DECLARE_STRING_HASH_MAP_WITH_DECL(wxFont
, wxRichTextFontTableHashMap
, class WXDLLIMPEXP_RICHTEXT
);
11984 class wxRichTextFontTableData
: public wxObjectRefData
11987 wxRichTextFontTableData() {}
11989 wxFont
FindFont(const wxRichTextAttr
& fontSpec
, double fontScale
);
11991 wxRichTextFontTableHashMap m_hashMap
;
11994 wxFont
wxRichTextFontTableData::FindFont(const wxRichTextAttr
& fontSpec
, double fontScale
)
11996 wxString
facename(fontSpec
.GetFontFaceName());
11998 int fontSize
= fontSpec
.GetFontSize();
11999 if (fontScale
!= 1.0)
12000 fontSize
= (int) ((double(fontSize
) * fontScale
) + 0.5);
12003 if (fontSpec
.HasFontPixelSize() && !fontSpec
.HasFontPointSize())
12007 wxString spec
= wxString::Format(wxT("%d-%s-%d-%d-%d-%d-%s-%d"),
12008 fontSize
, units
.c_str(), fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), (int) fontSpec
.GetFontUnderlined(), (int) fontSpec
.GetFontStrikethrough(),
12009 facename
.c_str(), (int) fontSpec
.GetFontEncoding());
12011 wxRichTextFontTableHashMap::iterator entry
= m_hashMap
.find(spec
);
12012 if ( entry
== m_hashMap
.end() )
12014 if (fontSpec
.HasFontPixelSize() && !fontSpec
.HasFontPointSize())
12016 wxFont
font(wxSize(0, fontSize
), wxFONTFAMILY_DEFAULT
, fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), fontSpec
.GetFontUnderlined(), facename
);
12017 if (fontSpec
.HasFontStrikethrough() && fontSpec
.GetFontStrikethrough())
12018 font
.SetStrikethrough(true);
12019 m_hashMap
[spec
] = font
;
12024 wxFont
font(fontSize
, wxFONTFAMILY_DEFAULT
, fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), fontSpec
.GetFontUnderlined(), facename
.c_str());
12025 if (fontSpec
.HasFontStrikethrough() && fontSpec
.GetFontStrikethrough())
12026 font
.SetStrikethrough(true);
12028 m_hashMap
[spec
] = font
;
12034 return entry
->second
;
12038 IMPLEMENT_DYNAMIC_CLASS(wxRichTextFontTable
, wxObject
)
12040 wxRichTextFontTable::wxRichTextFontTable()
12042 m_refData
= new wxRichTextFontTableData
;
12046 wxRichTextFontTable::wxRichTextFontTable(const wxRichTextFontTable
& table
)
12052 wxRichTextFontTable::~wxRichTextFontTable()
12057 bool wxRichTextFontTable::operator == (const wxRichTextFontTable
& table
) const
12059 return (m_refData
== table
.m_refData
);
12062 void wxRichTextFontTable::operator= (const wxRichTextFontTable
& table
)
12065 m_fontScale
= table
.m_fontScale
;
12068 wxFont
wxRichTextFontTable::FindFont(const wxRichTextAttr
& fontSpec
)
12070 wxRichTextFontTableData
* data
= (wxRichTextFontTableData
*) m_refData
;
12072 return data
->FindFont(fontSpec
, m_fontScale
);
12077 void wxRichTextFontTable::Clear()
12079 wxRichTextFontTableData
* data
= (wxRichTextFontTableData
*) m_refData
;
12081 data
->m_hashMap
.clear();
12084 void wxRichTextFontTable::SetFontScale(double fontScale
)
12086 if (fontScale
!= m_fontScale
)
12088 m_fontScale
= fontScale
;
12093 void wxTextBoxAttr::Reset()
12096 m_floatMode
= wxTEXT_BOX_ATTR_FLOAT_NONE
;
12097 m_clearMode
= wxTEXT_BOX_ATTR_CLEAR_NONE
;
12098 m_collapseMode
= wxTEXT_BOX_ATTR_COLLAPSE_NONE
;
12099 m_verticalAlignment
= wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_NONE
;
12100 m_boxStyleName
= wxEmptyString
;
12104 m_position
.Reset();
12115 bool wxTextBoxAttr::operator== (const wxTextBoxAttr
& attr
) const
12118 m_flags
== attr
.m_flags
&&
12119 m_floatMode
== attr
.m_floatMode
&&
12120 m_clearMode
== attr
.m_clearMode
&&
12121 m_collapseMode
== attr
.m_collapseMode
&&
12122 m_verticalAlignment
== attr
.m_verticalAlignment
&&
12124 m_margins
== attr
.m_margins
&&
12125 m_padding
== attr
.m_padding
&&
12126 m_position
== attr
.m_position
&&
12128 m_size
== attr
.m_size
&&
12129 m_minSize
== attr
.m_minSize
&&
12130 m_maxSize
== attr
.m_maxSize
&&
12132 m_border
== attr
.m_border
&&
12133 m_outline
== attr
.m_outline
&&
12135 m_boxStyleName
== attr
.m_boxStyleName
12139 // Partial equality test
12140 bool wxTextBoxAttr::EqPartial(const wxTextBoxAttr
& attr
, bool weakTest
) const
12143 ((!HasFloatMode() && attr
.HasFloatMode()) ||
12144 (!HasClearMode() && attr
.HasClearMode()) ||
12145 (!HasCollapseBorders() && attr
.HasCollapseBorders()) ||
12146 (!HasVerticalAlignment() && attr
.HasVerticalAlignment()) ||
12147 (!HasBoxStyleName() && attr
.HasBoxStyleName())))
12151 if (attr
.HasFloatMode() && HasFloatMode() && (GetFloatMode() != attr
.GetFloatMode()))
12154 if (attr
.HasClearMode() && HasClearMode() && (GetClearMode() != attr
.GetClearMode()))
12157 if (attr
.HasCollapseBorders() && HasCollapseBorders() && (attr
.GetCollapseBorders() != GetCollapseBorders()))
12160 if (attr
.HasVerticalAlignment() && HasVerticalAlignment() && (attr
.GetVerticalAlignment() != GetVerticalAlignment()))
12163 if (attr
.HasBoxStyleName() && HasBoxStyleName() && (attr
.GetBoxStyleName() != GetBoxStyleName()))
12168 if (!m_position
.EqPartial(attr
.m_position
, weakTest
))
12173 if (!m_size
.EqPartial(attr
.m_size
, weakTest
))
12175 if (!m_minSize
.EqPartial(attr
.m_minSize
, weakTest
))
12177 if (!m_maxSize
.EqPartial(attr
.m_maxSize
, weakTest
))
12182 if (!m_margins
.EqPartial(attr
.m_margins
, weakTest
))
12187 if (!m_padding
.EqPartial(attr
.m_padding
, weakTest
))
12192 if (!GetBorder().EqPartial(attr
.GetBorder(), weakTest
))
12197 if (!GetOutline().EqPartial(attr
.GetOutline(), weakTest
))
12203 // Merges the given attributes. If compareWith
12204 // is non-NULL, then it will be used to mask out those attributes that are the same in style
12205 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
12206 bool wxTextBoxAttr::Apply(const wxTextBoxAttr
& attr
, const wxTextBoxAttr
* compareWith
)
12208 if (attr
.HasFloatMode())
12210 if (!(compareWith
&& compareWith
->HasFloatMode() && compareWith
->GetFloatMode() == attr
.GetFloatMode()))
12211 SetFloatMode(attr
.GetFloatMode());
12214 if (attr
.HasClearMode())
12216 if (!(compareWith
&& compareWith
->HasClearMode() && compareWith
->GetClearMode() == attr
.GetClearMode()))
12217 SetClearMode(attr
.GetClearMode());
12220 if (attr
.HasCollapseBorders())
12222 if (!(compareWith
&& compareWith
->HasCollapseBorders() && compareWith
->GetCollapseBorders() == attr
.GetCollapseBorders()))
12223 SetCollapseBorders(attr
.GetCollapseBorders());
12226 if (attr
.HasVerticalAlignment())
12228 if (!(compareWith
&& compareWith
->HasVerticalAlignment() && compareWith
->GetVerticalAlignment() == attr
.GetVerticalAlignment()))
12229 SetVerticalAlignment(attr
.GetVerticalAlignment());
12232 if (attr
.HasBoxStyleName())
12234 if (!(compareWith
&& compareWith
->HasBoxStyleName() && compareWith
->GetBoxStyleName() == attr
.GetBoxStyleName()))
12235 SetBoxStyleName(attr
.GetBoxStyleName());
12238 m_margins
.Apply(attr
.m_margins
, compareWith
? (& attr
.m_margins
) : (const wxTextAttrDimensions
*) NULL
);
12239 m_padding
.Apply(attr
.m_padding
, compareWith
? (& attr
.m_padding
) : (const wxTextAttrDimensions
*) NULL
);
12240 m_position
.Apply(attr
.m_position
, compareWith
? (& attr
.m_position
) : (const wxTextAttrDimensions
*) NULL
);
12242 m_size
.Apply(attr
.m_size
, compareWith
? (& attr
.m_size
) : (const wxTextAttrSize
*) NULL
);
12243 m_minSize
.Apply(attr
.m_minSize
, compareWith
? (& attr
.m_minSize
) : (const wxTextAttrSize
*) NULL
);
12244 m_maxSize
.Apply(attr
.m_maxSize
, compareWith
? (& attr
.m_maxSize
) : (const wxTextAttrSize
*) NULL
);
12246 m_border
.Apply(attr
.m_border
, compareWith
? (& attr
.m_border
) : (const wxTextAttrBorders
*) NULL
);
12247 m_outline
.Apply(attr
.m_outline
, compareWith
? (& attr
.m_outline
) : (const wxTextAttrBorders
*) NULL
);
12252 // Remove specified attributes from this object
12253 bool wxTextBoxAttr::RemoveStyle(const wxTextBoxAttr
& attr
)
12255 if (attr
.HasFloatMode())
12256 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT
);
12258 if (attr
.HasClearMode())
12259 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR
);
12261 if (attr
.HasCollapseBorders())
12262 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
12264 if (attr
.HasVerticalAlignment())
12265 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
12267 if (attr
.HasBoxStyleName())
12269 SetBoxStyleName(wxEmptyString
);
12270 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
12273 m_margins
.RemoveStyle(attr
.m_margins
);
12274 m_padding
.RemoveStyle(attr
.m_padding
);
12275 m_position
.RemoveStyle(attr
.m_position
);
12277 m_size
.RemoveStyle(attr
.m_size
);
12278 m_minSize
.RemoveStyle(attr
.m_minSize
);
12279 m_maxSize
.RemoveStyle(attr
.m_maxSize
);
12281 m_border
.RemoveStyle(attr
.m_border
);
12282 m_outline
.RemoveStyle(attr
.m_outline
);
12287 // Collects the attributes that are common to a range of content, building up a note of
12288 // which attributes are absent in some objects and which clash in some objects.
12289 void wxTextBoxAttr::CollectCommonAttributes(const wxTextBoxAttr
& attr
, wxTextBoxAttr
& clashingAttr
, wxTextBoxAttr
& absentAttr
)
12291 if (attr
.HasFloatMode())
12293 if (!clashingAttr
.HasFloatMode() && !absentAttr
.HasFloatMode())
12295 if (HasFloatMode())
12297 if (GetFloatMode() != attr
.GetFloatMode())
12299 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_FLOAT
);
12300 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT
);
12304 SetFloatMode(attr
.GetFloatMode());
12308 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_FLOAT
);
12310 if (attr
.HasClearMode())
12312 if (!clashingAttr
.HasClearMode() && !absentAttr
.HasClearMode())
12314 if (HasClearMode())
12316 if (GetClearMode() != attr
.GetClearMode())
12318 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_CLEAR
);
12319 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR
);
12323 SetClearMode(attr
.GetClearMode());
12327 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_CLEAR
);
12329 if (attr
.HasCollapseBorders())
12331 if (!clashingAttr
.HasCollapseBorders() && !absentAttr
.HasCollapseBorders())
12333 if (HasCollapseBorders())
12335 if (GetCollapseBorders() != attr
.GetCollapseBorders())
12337 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
12338 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
12342 SetCollapseBorders(attr
.GetCollapseBorders());
12346 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
12348 if (attr
.HasVerticalAlignment())
12350 if (!clashingAttr
.HasVerticalAlignment() && !absentAttr
.HasVerticalAlignment())
12352 if (HasVerticalAlignment())
12354 if (GetVerticalAlignment() != attr
.GetVerticalAlignment())
12356 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
12357 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
12361 SetVerticalAlignment(attr
.GetVerticalAlignment());
12365 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
12367 if (attr
.HasBoxStyleName())
12369 if (!clashingAttr
.HasBoxStyleName() && !absentAttr
.HasBoxStyleName())
12371 if (HasBoxStyleName())
12373 if (GetBoxStyleName() != attr
.GetBoxStyleName())
12375 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
12376 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
12380 SetBoxStyleName(attr
.GetBoxStyleName());
12384 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
12386 m_margins
.CollectCommonAttributes(attr
.m_margins
, clashingAttr
.m_margins
, absentAttr
.m_margins
);
12387 m_padding
.CollectCommonAttributes(attr
.m_padding
, clashingAttr
.m_padding
, absentAttr
.m_padding
);
12388 m_position
.CollectCommonAttributes(attr
.m_position
, clashingAttr
.m_position
, absentAttr
.m_position
);
12390 m_size
.CollectCommonAttributes(attr
.m_size
, clashingAttr
.m_size
, absentAttr
.m_size
);
12391 m_minSize
.CollectCommonAttributes(attr
.m_minSize
, clashingAttr
.m_minSize
, absentAttr
.m_minSize
);
12392 m_maxSize
.CollectCommonAttributes(attr
.m_maxSize
, clashingAttr
.m_maxSize
, absentAttr
.m_maxSize
);
12394 m_border
.CollectCommonAttributes(attr
.m_border
, clashingAttr
.m_border
, absentAttr
.m_border
);
12395 m_outline
.CollectCommonAttributes(attr
.m_outline
, clashingAttr
.m_outline
, absentAttr
.m_outline
);
12398 bool wxTextBoxAttr::IsDefault() const
12400 return GetFlags() == 0 && !m_border
.IsValid() && !m_outline
.IsValid() &&
12401 !m_size
.IsValid() && !m_minSize
.IsValid() && !m_maxSize
.IsValid() &&
12402 !m_position
.IsValid() && !m_padding
.IsValid() && !m_margins
.IsValid();
12407 void wxRichTextAttr::Copy(const wxRichTextAttr
& attr
)
12409 wxTextAttr::Copy(attr
);
12411 m_textBoxAttr
= attr
.m_textBoxAttr
;
12414 bool wxRichTextAttr::operator==(const wxRichTextAttr
& attr
) const
12416 if (!(wxTextAttr::operator==(attr
)))
12419 return (m_textBoxAttr
== attr
.m_textBoxAttr
);
12422 // Partial equality test
12423 bool wxRichTextAttr::EqPartial(const wxRichTextAttr
& attr
, bool weakTest
) const
12425 if (!(wxTextAttr::EqPartial(attr
, weakTest
)))
12428 return m_textBoxAttr
.EqPartial(attr
.m_textBoxAttr
, weakTest
);
12431 // Merges the given attributes. If compareWith
12432 // is non-NULL, then it will be used to mask out those attributes that are the same in style
12433 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
12434 bool wxRichTextAttr::Apply(const wxRichTextAttr
& style
, const wxRichTextAttr
* compareWith
)
12436 wxTextAttr::Apply(style
, compareWith
);
12438 return m_textBoxAttr
.Apply(style
.m_textBoxAttr
, compareWith
? (& compareWith
->m_textBoxAttr
) : (const wxTextBoxAttr
*) NULL
);
12441 // Remove specified attributes from this object
12442 bool wxRichTextAttr::RemoveStyle(const wxRichTextAttr
& attr
)
12444 wxTextAttr::RemoveStyle(*this, attr
);
12446 return m_textBoxAttr
.RemoveStyle(attr
.m_textBoxAttr
);
12449 // Collects the attributes that are common to a range of content, building up a note of
12450 // which attributes are absent in some objects and which clash in some objects.
12451 void wxRichTextAttr::CollectCommonAttributes(const wxRichTextAttr
& attr
, wxRichTextAttr
& clashingAttr
, wxRichTextAttr
& absentAttr
)
12453 wxTextAttrCollectCommonAttributes(*this, attr
, clashingAttr
, absentAttr
);
12455 m_textBoxAttr
.CollectCommonAttributes(attr
.m_textBoxAttr
, clashingAttr
.m_textBoxAttr
, absentAttr
.m_textBoxAttr
);
12458 // Partial equality test
12459 bool wxTextAttrBorder::EqPartial(const wxTextAttrBorder
& border
, bool weakTest
) const
12462 ((!HasStyle() && border
.HasStyle()) ||
12463 (!HasColour() && border
.HasColour()) ||
12464 (!HasWidth() && border
.HasWidth())))
12469 if (border
.HasStyle() && HasStyle() && (border
.GetStyle() != GetStyle()))
12472 if (border
.HasColour() && HasColour() && (border
.GetColourLong() != GetColourLong()))
12475 if (border
.HasWidth() && HasWidth() && !(border
.GetWidth() == GetWidth()))
12481 // Apply border to 'this', but not if the same as compareWith
12482 bool wxTextAttrBorder::Apply(const wxTextAttrBorder
& border
, const wxTextAttrBorder
* compareWith
)
12484 if (border
.HasStyle())
12486 if (!(compareWith
&& (border
.GetStyle() == compareWith
->GetStyle())))
12487 SetStyle(border
.GetStyle());
12489 if (border
.HasColour())
12491 if (!(compareWith
&& (border
.GetColourLong() == compareWith
->GetColourLong())))
12492 SetColour(border
.GetColourLong());
12494 if (border
.HasWidth())
12496 if (!(compareWith
&& (border
.GetWidth() == compareWith
->GetWidth())))
12497 SetWidth(border
.GetWidth());
12503 // Remove specified attributes from this object
12504 bool wxTextAttrBorder::RemoveStyle(const wxTextAttrBorder
& attr
)
12506 if (attr
.HasStyle() && HasStyle())
12507 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_STYLE
);
12508 if (attr
.HasColour() && HasColour())
12509 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_COLOUR
);
12510 if (attr
.HasWidth() && HasWidth())
12511 m_borderWidth
.Reset();
12516 // Collects the attributes that are common to a range of content, building up a note of
12517 // which attributes are absent in some objects and which clash in some objects.
12518 void wxTextAttrBorder::CollectCommonAttributes(const wxTextAttrBorder
& attr
, wxTextAttrBorder
& clashingAttr
, wxTextAttrBorder
& absentAttr
)
12520 if (attr
.HasStyle())
12522 if (!clashingAttr
.HasStyle() && !absentAttr
.HasStyle())
12526 if (GetStyle() != attr
.GetStyle())
12528 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
12529 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
12533 SetStyle(attr
.GetStyle());
12537 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
12539 if (attr
.HasColour())
12541 if (!clashingAttr
.HasColour() && !absentAttr
.HasColour())
12545 if (GetColour() != attr
.GetColour())
12547 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
12548 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
12552 SetColour(attr
.GetColourLong());
12556 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
12558 m_borderWidth
.CollectCommonAttributes(attr
.m_borderWidth
, clashingAttr
.m_borderWidth
, absentAttr
.m_borderWidth
);
12561 // Partial equality test
12562 bool wxTextAttrBorders::EqPartial(const wxTextAttrBorders
& borders
, bool weakTest
) const
12564 return m_left
.EqPartial(borders
.m_left
, weakTest
) && m_right
.EqPartial(borders
.m_right
, weakTest
) &&
12565 m_top
.EqPartial(borders
.m_top
, weakTest
) && m_bottom
.EqPartial(borders
.m_bottom
, weakTest
);
12568 // Apply border to 'this', but not if the same as compareWith
12569 bool wxTextAttrBorders::Apply(const wxTextAttrBorders
& borders
, const wxTextAttrBorders
* compareWith
)
12571 m_left
.Apply(borders
.m_left
, compareWith
? (& compareWith
->m_left
) : (const wxTextAttrBorder
*) NULL
);
12572 m_right
.Apply(borders
.m_right
, compareWith
? (& compareWith
->m_right
) : (const wxTextAttrBorder
*) NULL
);
12573 m_top
.Apply(borders
.m_top
, compareWith
? (& compareWith
->m_top
) : (const wxTextAttrBorder
*) NULL
);
12574 m_bottom
.Apply(borders
.m_bottom
, compareWith
? (& compareWith
->m_bottom
) : (const wxTextAttrBorder
*) NULL
);
12578 // Remove specified attributes from this object
12579 bool wxTextAttrBorders::RemoveStyle(const wxTextAttrBorders
& attr
)
12581 m_left
.RemoveStyle(attr
.m_left
);
12582 m_right
.RemoveStyle(attr
.m_right
);
12583 m_top
.RemoveStyle(attr
.m_top
);
12584 m_bottom
.RemoveStyle(attr
.m_bottom
);
12588 // Collects the attributes that are common to a range of content, building up a note of
12589 // which attributes are absent in some objects and which clash in some objects.
12590 void wxTextAttrBorders::CollectCommonAttributes(const wxTextAttrBorders
& attr
, wxTextAttrBorders
& clashingAttr
, wxTextAttrBorders
& absentAttr
)
12592 m_left
.CollectCommonAttributes(attr
.m_left
, clashingAttr
.m_left
, absentAttr
.m_left
);
12593 m_right
.CollectCommonAttributes(attr
.m_right
, clashingAttr
.m_right
, absentAttr
.m_right
);
12594 m_top
.CollectCommonAttributes(attr
.m_top
, clashingAttr
.m_top
, absentAttr
.m_top
);
12595 m_bottom
.CollectCommonAttributes(attr
.m_bottom
, clashingAttr
.m_bottom
, absentAttr
.m_bottom
);
12598 // Set style of all borders
12599 void wxTextAttrBorders::SetStyle(int style
)
12601 m_left
.SetStyle(style
);
12602 m_right
.SetStyle(style
);
12603 m_top
.SetStyle(style
);
12604 m_bottom
.SetStyle(style
);
12607 // Set colour of all borders
12608 void wxTextAttrBorders::SetColour(unsigned long colour
)
12610 m_left
.SetColour(colour
);
12611 m_right
.SetColour(colour
);
12612 m_top
.SetColour(colour
);
12613 m_bottom
.SetColour(colour
);
12616 void wxTextAttrBorders::SetColour(const wxColour
& colour
)
12618 m_left
.SetColour(colour
);
12619 m_right
.SetColour(colour
);
12620 m_top
.SetColour(colour
);
12621 m_bottom
.SetColour(colour
);
12624 // Set width of all borders
12625 void wxTextAttrBorders::SetWidth(const wxTextAttrDimension
& width
)
12627 m_left
.SetWidth(width
);
12628 m_right
.SetWidth(width
);
12629 m_top
.SetWidth(width
);
12630 m_bottom
.SetWidth(width
);
12633 // Partial equality test
12634 bool wxTextAttrDimension::EqPartial(const wxTextAttrDimension
& dim
, bool weakTest
) const
12636 if (!weakTest
&& !IsValid() && dim
.IsValid())
12639 if (dim
.IsValid() && IsValid() && !((*this) == dim
))
12645 bool wxTextAttrDimension::Apply(const wxTextAttrDimension
& dim
, const wxTextAttrDimension
* compareWith
)
12649 if (!(compareWith
&& dim
== (*compareWith
)))
12656 // Collects the attributes that are common to a range of content, building up a note of
12657 // which attributes are absent in some objects and which clash in some objects.
12658 void wxTextAttrDimension::CollectCommonAttributes(const wxTextAttrDimension
& attr
, wxTextAttrDimension
& clashingAttr
, wxTextAttrDimension
& absentAttr
)
12660 if (attr
.IsValid())
12662 if (!clashingAttr
.IsValid() && !absentAttr
.IsValid())
12666 if (!((*this) == attr
))
12668 clashingAttr
.SetValid(true);
12677 absentAttr
.SetValid(true);
12680 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(wxDC
& dc
, double scale
, const wxSize
& parentSize
)
12682 m_ppi
= dc
.GetPPI().x
; m_scale
= scale
; m_parentSize
= parentSize
;
12685 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(int ppi
, double scale
, const wxSize
& parentSize
)
12687 m_ppi
= ppi
; m_scale
= scale
; m_parentSize
= parentSize
;
12690 int wxTextAttrDimensionConverter::ConvertTenthsMMToPixels(int units
) const
12692 return wxRichTextObject::ConvertTenthsMMToPixels(m_ppi
, units
, m_scale
);
12695 int wxTextAttrDimensionConverter::ConvertPixelsToTenthsMM(int pixels
) const
12697 return wxRichTextObject::ConvertPixelsToTenthsMM(m_ppi
, pixels
, m_scale
);
12700 int wxTextAttrDimensionConverter::GetPixels(const wxTextAttrDimension
& dim
, int direction
) const
12702 if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
12703 return ConvertTenthsMMToPixels(dim
.GetValue());
12704 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
12705 return dim
.GetValue();
12706 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
12708 wxASSERT(m_parentSize
!= wxDefaultSize
);
12709 if (direction
== wxHORIZONTAL
)
12710 return (int) (double(m_parentSize
.x
) * double(dim
.GetValue()) / 100.0);
12712 return (int) (double(m_parentSize
.y
) * double(dim
.GetValue()) / 100.0);
12721 int wxTextAttrDimensionConverter::GetTenthsMM(const wxTextAttrDimension
& dim
) const
12723 if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
12724 return dim
.GetValue();
12725 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
12726 return ConvertPixelsToTenthsMM(dim
.GetValue());
12734 // Partial equality test
12735 bool wxTextAttrDimensions::EqPartial(const wxTextAttrDimensions
& dims
, bool weakTest
) const
12737 if (!m_left
.EqPartial(dims
.m_left
, weakTest
))
12740 if (!m_right
.EqPartial(dims
.m_right
, weakTest
))
12743 if (!m_top
.EqPartial(dims
.m_top
, weakTest
))
12746 if (!m_bottom
.EqPartial(dims
.m_bottom
, weakTest
))
12752 // Apply border to 'this', but not if the same as compareWith
12753 bool wxTextAttrDimensions::Apply(const wxTextAttrDimensions
& dims
, const wxTextAttrDimensions
* compareWith
)
12755 m_left
.Apply(dims
.m_left
, compareWith
? (& compareWith
->m_left
) : (const wxTextAttrDimension
*) NULL
);
12756 m_right
.Apply(dims
.m_right
, compareWith
? (& compareWith
->m_right
): (const wxTextAttrDimension
*) NULL
);
12757 m_top
.Apply(dims
.m_top
, compareWith
? (& compareWith
->m_top
): (const wxTextAttrDimension
*) NULL
);
12758 m_bottom
.Apply(dims
.m_bottom
, compareWith
? (& compareWith
->m_bottom
): (const wxTextAttrDimension
*) NULL
);
12763 // Remove specified attributes from this object
12764 bool wxTextAttrDimensions::RemoveStyle(const wxTextAttrDimensions
& attr
)
12766 if (attr
.m_left
.IsValid())
12768 if (attr
.m_right
.IsValid())
12770 if (attr
.m_top
.IsValid())
12772 if (attr
.m_bottom
.IsValid())
12778 // Collects the attributes that are common to a range of content, building up a note of
12779 // which attributes are absent in some objects and which clash in some objects.
12780 void wxTextAttrDimensions::CollectCommonAttributes(const wxTextAttrDimensions
& attr
, wxTextAttrDimensions
& clashingAttr
, wxTextAttrDimensions
& absentAttr
)
12782 m_left
.CollectCommonAttributes(attr
.m_left
, clashingAttr
.m_left
, absentAttr
.m_left
);
12783 m_right
.CollectCommonAttributes(attr
.m_right
, clashingAttr
.m_right
, absentAttr
.m_right
);
12784 m_top
.CollectCommonAttributes(attr
.m_top
, clashingAttr
.m_top
, absentAttr
.m_top
);
12785 m_bottom
.CollectCommonAttributes(attr
.m_bottom
, clashingAttr
.m_bottom
, absentAttr
.m_bottom
);
12788 // Partial equality test
12789 bool wxTextAttrSize::EqPartial(const wxTextAttrSize
& size
, bool weakTest
) const
12791 if (!m_width
.EqPartial(size
.m_width
, weakTest
))
12794 if (!m_height
.EqPartial(size
.m_height
, weakTest
))
12800 // Apply border to 'this', but not if the same as compareWith
12801 bool wxTextAttrSize::Apply(const wxTextAttrSize
& size
, const wxTextAttrSize
* compareWith
)
12803 m_width
.Apply(size
.m_width
, compareWith
? (& compareWith
->m_width
) : (const wxTextAttrDimension
*) NULL
);
12804 m_height
.Apply(size
.m_height
, compareWith
? (& compareWith
->m_height
): (const wxTextAttrDimension
*) NULL
);
12809 // Remove specified attributes from this object
12810 bool wxTextAttrSize::RemoveStyle(const wxTextAttrSize
& attr
)
12812 if (attr
.m_width
.IsValid())
12814 if (attr
.m_height
.IsValid())
12820 // Collects the attributes that are common to a range of content, building up a note of
12821 // which attributes are absent in some objects and which clash in some objects.
12822 void wxTextAttrSize::CollectCommonAttributes(const wxTextAttrSize
& attr
, wxTextAttrSize
& clashingAttr
, wxTextAttrSize
& absentAttr
)
12824 m_width
.CollectCommonAttributes(attr
.m_width
, clashingAttr
.m_width
, absentAttr
.m_width
);
12825 m_height
.CollectCommonAttributes(attr
.m_height
, clashingAttr
.m_height
, absentAttr
.m_height
);
12828 // Collects the attributes that are common to a range of content, building up a note of
12829 // which attributes are absent in some objects and which clash in some objects.
12830 void wxTextAttrCollectCommonAttributes(wxTextAttr
& currentStyle
, const wxTextAttr
& attr
, wxTextAttr
& clashingAttr
, wxTextAttr
& absentAttr
)
12832 absentAttr
.SetFlags(absentAttr
.GetFlags() | (~attr
.GetFlags() & wxTEXT_ATTR_ALL
));
12833 absentAttr
.SetTextEffectFlags(absentAttr
.GetTextEffectFlags() | (~attr
.GetTextEffectFlags() & 0xFFFF));
12835 long forbiddenFlags
= clashingAttr
.GetFlags()|absentAttr
.GetFlags();
12837 // If different font size units are being used, this is a clash.
12838 if (((attr
.GetFlags() & wxTEXT_ATTR_FONT_SIZE
) | (currentStyle
.GetFlags() & wxTEXT_ATTR_FONT_SIZE
)) == wxTEXT_ATTR_FONT_SIZE
)
12840 currentStyle
.SetFontSize(0);
12841 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_SIZE
);
12842 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_SIZE
);
12846 if (attr
.HasFontPointSize() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_POINT_SIZE
))
12848 if (currentStyle
.HasFontPointSize())
12850 if (currentStyle
.GetFontSize() != attr
.GetFontSize())
12852 // Clash of attr - mark as such
12853 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE
);
12854 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE
);
12858 currentStyle
.SetFontSize(attr
.GetFontSize());
12860 else if (!attr
.HasFontPointSize() && currentStyle
.HasFontPointSize())
12862 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE
);
12863 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE
);
12866 if (attr
.HasFontPixelSize() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_PIXEL_SIZE
))
12868 if (currentStyle
.HasFontPixelSize())
12870 if (currentStyle
.GetFontSize() != attr
.GetFontSize())
12872 // Clash of attr - mark as such
12873 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE
);
12874 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE
);
12878 currentStyle
.SetFontPixelSize(attr
.GetFontSize());
12880 else if (!attr
.HasFontPixelSize() && currentStyle
.HasFontPixelSize())
12882 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE
);
12883 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE
);
12887 if (attr
.HasFontItalic() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_ITALIC
))
12889 if (currentStyle
.HasFontItalic())
12891 if (currentStyle
.GetFontStyle() != attr
.GetFontStyle())
12893 // Clash of attr - mark as such
12894 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_ITALIC
);
12895 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC
);
12899 currentStyle
.SetFontStyle(attr
.GetFontStyle());
12901 else if (!attr
.HasFontItalic() && currentStyle
.HasFontItalic())
12903 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_ITALIC
);
12904 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC
);
12907 if (attr
.HasFontFamily() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_FAMILY
))
12909 if (currentStyle
.HasFontFamily())
12911 if (currentStyle
.GetFontFamily() != attr
.GetFontFamily())
12913 // Clash of attr - mark as such
12914 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FAMILY
);
12915 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY
);
12919 currentStyle
.SetFontFamily(attr
.GetFontFamily());
12921 else if (!attr
.HasFontFamily() && currentStyle
.HasFontFamily())
12923 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FAMILY
);
12924 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY
);
12927 if (attr
.HasFontWeight() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_WEIGHT
))
12929 if (currentStyle
.HasFontWeight())
12931 if (currentStyle
.GetFontWeight() != attr
.GetFontWeight())
12933 // Clash of attr - mark as such
12934 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_WEIGHT
);
12935 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT
);
12939 currentStyle
.SetFontWeight(attr
.GetFontWeight());
12941 else if (!attr
.HasFontWeight() && currentStyle
.HasFontWeight())
12943 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_WEIGHT
);
12944 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT
);
12947 if (attr
.HasFontFaceName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_FACE
))
12949 if (currentStyle
.HasFontFaceName())
12951 wxString
faceName1(currentStyle
.GetFontFaceName());
12952 wxString
faceName2(attr
.GetFontFaceName());
12954 if (faceName1
!= faceName2
)
12956 // Clash of attr - mark as such
12957 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FACE
);
12958 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FACE
);
12962 currentStyle
.SetFontFaceName(attr
.GetFontFaceName());
12964 else if (!attr
.HasFontFaceName() && currentStyle
.HasFontFaceName())
12966 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FACE
);
12967 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FACE
);
12970 if (attr
.HasFontUnderlined() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_UNDERLINE
))
12972 if (currentStyle
.HasFontUnderlined())
12974 if (currentStyle
.GetFontUnderlined() != attr
.GetFontUnderlined())
12976 // Clash of attr - mark as such
12977 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
12978 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
12982 currentStyle
.SetFontUnderlined(attr
.GetFontUnderlined());
12984 else if (!attr
.HasFontUnderlined() && currentStyle
.HasFontUnderlined())
12986 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
12987 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
12990 if (attr
.HasFontStrikethrough() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_STRIKETHROUGH
))
12992 if (currentStyle
.HasFontStrikethrough())
12994 if (currentStyle
.GetFontStrikethrough() != attr
.GetFontStrikethrough())
12996 // Clash of attr - mark as such
12997 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH
);
12998 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH
);
13002 currentStyle
.SetFontStrikethrough(attr
.GetFontStrikethrough());
13004 else if (!attr
.HasFontStrikethrough() && currentStyle
.HasFontStrikethrough())
13006 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH
);
13007 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH
);
13010 if (attr
.HasTextColour() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_TEXT_COLOUR
))
13012 if (currentStyle
.HasTextColour())
13014 if (currentStyle
.GetTextColour() != attr
.GetTextColour())
13016 // Clash of attr - mark as such
13017 clashingAttr
.AddFlag(wxTEXT_ATTR_TEXT_COLOUR
);
13018 currentStyle
.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR
);
13022 currentStyle
.SetTextColour(attr
.GetTextColour());
13024 else if (!attr
.HasTextColour() && currentStyle
.HasTextColour())
13026 clashingAttr
.AddFlag(wxTEXT_ATTR_TEXT_COLOUR
);
13027 currentStyle
.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR
);
13030 if (attr
.HasBackgroundColour() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BACKGROUND_COLOUR
))
13032 if (currentStyle
.HasBackgroundColour())
13034 if (currentStyle
.GetBackgroundColour() != attr
.GetBackgroundColour())
13036 // Clash of attr - mark as such
13037 clashingAttr
.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
13038 currentStyle
.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
13042 currentStyle
.SetBackgroundColour(attr
.GetBackgroundColour());
13044 else if (!attr
.HasBackgroundColour() && currentStyle
.HasBackgroundColour())
13046 clashingAttr
.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
13047 currentStyle
.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
13050 if (attr
.HasAlignment() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_ALIGNMENT
))
13052 if (currentStyle
.HasAlignment())
13054 if (currentStyle
.GetAlignment() != attr
.GetAlignment())
13056 // Clash of attr - mark as such
13057 clashingAttr
.AddFlag(wxTEXT_ATTR_ALIGNMENT
);
13058 currentStyle
.RemoveFlag(wxTEXT_ATTR_ALIGNMENT
);
13062 currentStyle
.SetAlignment(attr
.GetAlignment());
13064 else if (!attr
.HasAlignment() && currentStyle
.HasAlignment())
13066 clashingAttr
.AddFlag(wxTEXT_ATTR_ALIGNMENT
);
13067 currentStyle
.RemoveFlag(wxTEXT_ATTR_ALIGNMENT
);
13070 if (attr
.HasTabs() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_TABS
))
13072 if (currentStyle
.HasTabs())
13074 if (!wxRichTextTabsEq(currentStyle
.GetTabs(), attr
.GetTabs()))
13076 // Clash of attr - mark as such
13077 clashingAttr
.AddFlag(wxTEXT_ATTR_TABS
);
13078 currentStyle
.RemoveFlag(wxTEXT_ATTR_TABS
);
13082 currentStyle
.SetTabs(attr
.GetTabs());
13084 else if (!attr
.HasTabs() && currentStyle
.HasTabs())
13086 clashingAttr
.AddFlag(wxTEXT_ATTR_TABS
);
13087 currentStyle
.RemoveFlag(wxTEXT_ATTR_TABS
);
13090 if (attr
.HasLeftIndent() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LEFT_INDENT
))
13092 if (currentStyle
.HasLeftIndent())
13094 if (currentStyle
.GetLeftIndent() != attr
.GetLeftIndent() || currentStyle
.GetLeftSubIndent() != attr
.GetLeftSubIndent())
13096 // Clash of attr - mark as such
13097 clashingAttr
.AddFlag(wxTEXT_ATTR_LEFT_INDENT
);
13098 currentStyle
.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT
);
13102 currentStyle
.SetLeftIndent(attr
.GetLeftIndent(), attr
.GetLeftSubIndent());
13104 else if (!attr
.HasLeftIndent() && currentStyle
.HasLeftIndent())
13106 clashingAttr
.AddFlag(wxTEXT_ATTR_LEFT_INDENT
);
13107 currentStyle
.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT
);
13110 if (attr
.HasRightIndent() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_RIGHT_INDENT
))
13112 if (currentStyle
.HasRightIndent())
13114 if (currentStyle
.GetRightIndent() != attr
.GetRightIndent())
13116 // Clash of attr - mark as such
13117 clashingAttr
.AddFlag(wxTEXT_ATTR_RIGHT_INDENT
);
13118 currentStyle
.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT
);
13122 currentStyle
.SetRightIndent(attr
.GetRightIndent());
13124 else if (!attr
.HasRightIndent() && currentStyle
.HasRightIndent())
13126 clashingAttr
.AddFlag(wxTEXT_ATTR_RIGHT_INDENT
);
13127 currentStyle
.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT
);
13130 if (attr
.HasParagraphSpacingAfter() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARA_SPACING_AFTER
))
13132 if (currentStyle
.HasParagraphSpacingAfter())
13134 if (currentStyle
.GetParagraphSpacingAfter() != attr
.GetParagraphSpacingAfter())
13136 // Clash of attr - mark as such
13137 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
13138 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
13142 currentStyle
.SetParagraphSpacingAfter(attr
.GetParagraphSpacingAfter());
13144 else if (!attr
.HasParagraphSpacingAfter() && currentStyle
.HasParagraphSpacingAfter())
13146 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
13147 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
13150 if (attr
.HasParagraphSpacingBefore() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARA_SPACING_BEFORE
))
13152 if (currentStyle
.HasParagraphSpacingBefore())
13154 if (currentStyle
.GetParagraphSpacingBefore() != attr
.GetParagraphSpacingBefore())
13156 // Clash of attr - mark as such
13157 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
13158 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
13162 currentStyle
.SetParagraphSpacingBefore(attr
.GetParagraphSpacingBefore());
13164 else if (!attr
.HasParagraphSpacingBefore() && currentStyle
.HasParagraphSpacingBefore())
13166 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
13167 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
13170 if (attr
.HasLineSpacing() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LINE_SPACING
))
13172 if (currentStyle
.HasLineSpacing())
13174 if (currentStyle
.GetLineSpacing() != attr
.GetLineSpacing())
13176 // Clash of attr - mark as such
13177 clashingAttr
.AddFlag(wxTEXT_ATTR_LINE_SPACING
);
13178 currentStyle
.RemoveFlag(wxTEXT_ATTR_LINE_SPACING
);
13182 currentStyle
.SetLineSpacing(attr
.GetLineSpacing());
13184 else if (!attr
.HasLineSpacing() && currentStyle
.HasLineSpacing())
13186 clashingAttr
.AddFlag(wxTEXT_ATTR_LINE_SPACING
);
13187 currentStyle
.RemoveFlag(wxTEXT_ATTR_LINE_SPACING
);
13190 if (attr
.HasCharacterStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_CHARACTER_STYLE_NAME
))
13192 if (currentStyle
.HasCharacterStyleName())
13194 if (currentStyle
.GetCharacterStyleName() != attr
.GetCharacterStyleName())
13196 // Clash of attr - mark as such
13197 clashingAttr
.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
13198 currentStyle
.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
13202 currentStyle
.SetCharacterStyleName(attr
.GetCharacterStyleName());
13204 else if (!attr
.HasCharacterStyleName() && currentStyle
.HasCharacterStyleName())
13206 clashingAttr
.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
13207 currentStyle
.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
13210 if (attr
.HasParagraphStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
))
13212 if (currentStyle
.HasParagraphStyleName())
13214 if (currentStyle
.GetParagraphStyleName() != attr
.GetParagraphStyleName())
13216 // Clash of attr - mark as such
13217 clashingAttr
.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
13218 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
13222 currentStyle
.SetParagraphStyleName(attr
.GetParagraphStyleName());
13224 else if (!attr
.HasParagraphStyleName() && currentStyle
.HasParagraphStyleName())
13226 clashingAttr
.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
13227 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
13230 if (attr
.HasListStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LIST_STYLE_NAME
))
13232 if (currentStyle
.HasListStyleName())
13234 if (currentStyle
.GetListStyleName() != attr
.GetListStyleName())
13236 // Clash of attr - mark as such
13237 clashingAttr
.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
13238 currentStyle
.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
13242 currentStyle
.SetListStyleName(attr
.GetListStyleName());
13244 else if (!attr
.HasListStyleName() && currentStyle
.HasListStyleName())
13246 clashingAttr
.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
13247 currentStyle
.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
13250 if (attr
.HasBulletStyle() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_STYLE
))
13252 if (currentStyle
.HasBulletStyle())
13254 if (currentStyle
.GetBulletStyle() != attr
.GetBulletStyle())
13256 // Clash of attr - mark as such
13257 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_STYLE
);
13258 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE
);
13262 currentStyle
.SetBulletStyle(attr
.GetBulletStyle());
13264 else if (!attr
.HasBulletStyle() && currentStyle
.HasBulletStyle())
13266 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_STYLE
);
13267 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE
);
13270 if (attr
.HasBulletNumber() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_NUMBER
))
13272 if (currentStyle
.HasBulletNumber())
13274 if (currentStyle
.GetBulletNumber() != attr
.GetBulletNumber())
13276 // Clash of attr - mark as such
13277 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NUMBER
);
13278 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER
);
13282 currentStyle
.SetBulletNumber(attr
.GetBulletNumber());
13284 else if (!attr
.HasBulletNumber() && currentStyle
.HasBulletNumber())
13286 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NUMBER
);
13287 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER
);
13290 if (attr
.HasBulletText() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_TEXT
))
13292 if (currentStyle
.HasBulletText())
13294 if (currentStyle
.GetBulletText() != attr
.GetBulletText())
13296 // Clash of attr - mark as such
13297 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_TEXT
);
13298 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT
);
13303 currentStyle
.SetBulletText(attr
.GetBulletText());
13304 currentStyle
.SetBulletFont(attr
.GetBulletFont());
13307 else if (!attr
.HasBulletText() && currentStyle
.HasBulletText())
13309 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_TEXT
);
13310 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT
);
13313 if (attr
.HasBulletName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_NAME
))
13315 if (currentStyle
.HasBulletName())
13317 if (currentStyle
.GetBulletName() != attr
.GetBulletName())
13319 // Clash of attr - mark as such
13320 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NAME
);
13321 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NAME
);
13326 currentStyle
.SetBulletName(attr
.GetBulletName());
13329 else if (!attr
.HasBulletName() && currentStyle
.HasBulletName())
13331 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NAME
);
13332 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NAME
);
13335 if (attr
.HasURL() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_URL
))
13337 if (currentStyle
.HasURL())
13339 if (currentStyle
.GetURL() != attr
.GetURL())
13341 // Clash of attr - mark as such
13342 clashingAttr
.AddFlag(wxTEXT_ATTR_URL
);
13343 currentStyle
.RemoveFlag(wxTEXT_ATTR_URL
);
13348 currentStyle
.SetURL(attr
.GetURL());
13351 else if (!attr
.HasURL() && currentStyle
.HasURL())
13353 clashingAttr
.AddFlag(wxTEXT_ATTR_URL
);
13354 currentStyle
.RemoveFlag(wxTEXT_ATTR_URL
);
13357 if (attr
.HasTextEffects() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_EFFECTS
))
13359 if (currentStyle
.HasTextEffects())
13361 // We need to find the bits in the new attr that are different:
13362 // just look at those bits that are specified by the new attr.
13364 // We need to remove the bits and flags that are not common between current attr
13365 // and new attr. In so doing we need to take account of the styles absent from one or more of the
13366 // previous styles.
13368 int currentRelevantTextEffects
= currentStyle
.GetTextEffects() & attr
.GetTextEffectFlags();
13369 int newRelevantTextEffects
= attr
.GetTextEffects() & attr
.GetTextEffectFlags();
13371 if (currentRelevantTextEffects
!= newRelevantTextEffects
)
13373 // Find the text effects that were different, using XOR
13374 int differentEffects
= currentRelevantTextEffects
^ newRelevantTextEffects
;
13376 // Clash of attr - mark as such
13377 clashingAttr
.SetTextEffectFlags(clashingAttr
.GetTextEffectFlags() | differentEffects
);
13378 currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~differentEffects
);
13383 currentStyle
.SetTextEffects(attr
.GetTextEffects());
13384 currentStyle
.SetTextEffectFlags(attr
.GetTextEffectFlags());
13387 // Mask out the flags and values that cannot be common because they were absent in one or more objecrs
13388 // that we've looked at so far
13389 currentStyle
.SetTextEffects(currentStyle
.GetTextEffects() & ~absentAttr
.GetTextEffectFlags());
13390 currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~absentAttr
.GetTextEffectFlags());
13392 if (currentStyle
.GetTextEffectFlags() == 0)
13393 currentStyle
.RemoveFlag(wxTEXT_ATTR_EFFECTS
);
13395 else if (!attr
.HasTextEffects() && currentStyle
.HasTextEffects())
13397 clashingAttr
.AddFlag(wxTEXT_ATTR_EFFECTS
);
13398 currentStyle
.RemoveFlag(wxTEXT_ATTR_EFFECTS
);
13401 if (attr
.HasOutlineLevel() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_OUTLINE_LEVEL
))
13403 if (currentStyle
.HasOutlineLevel())
13405 if (currentStyle
.GetOutlineLevel() != attr
.GetOutlineLevel())
13407 // Clash of attr - mark as such
13408 clashingAttr
.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
13409 currentStyle
.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
13413 currentStyle
.SetOutlineLevel(attr
.GetOutlineLevel());
13415 else if (!attr
.HasOutlineLevel() && currentStyle
.HasOutlineLevel())
13417 clashingAttr
.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
13418 currentStyle
.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
13422 WX_DEFINE_OBJARRAY(wxRichTextVariantArray
);
13425 WX_DEFINE_OBJARRAY(wxRichTextAttrArray
);
13427 IMPLEMENT_DYNAMIC_CLASS(wxRichTextProperties
, wxObject
)
13429 bool wxRichTextProperties::operator==(const wxRichTextProperties
& props
) const
13431 if (m_properties
.GetCount() != props
.GetCount())
13435 for (i
= 0; i
< m_properties
.GetCount(); i
++)
13437 const wxVariant
& var1
= m_properties
[i
];
13438 int idx
= props
.Find(var1
.GetName());
13441 const wxVariant
& var2
= props
.m_properties
[idx
];
13442 if (!(var1
== var2
))
13449 wxArrayString
wxRichTextProperties::GetPropertyNames() const
13453 for (i
= 0; i
< m_properties
.GetCount(); i
++)
13455 arr
.Add(m_properties
[i
].GetName());
13460 int wxRichTextProperties::Find(const wxString
& name
) const
13463 for (i
= 0; i
< m_properties
.GetCount(); i
++)
13465 if (m_properties
[i
].GetName() == name
)
13471 bool wxRichTextProperties::Remove(const wxString
& name
)
13473 int idx
= Find(name
);
13476 m_properties
.RemoveAt(idx
);
13483 wxVariant
* wxRichTextProperties::FindOrCreateProperty(const wxString
& name
)
13485 int idx
= Find(name
);
13486 if (idx
== wxNOT_FOUND
)
13487 SetProperty(name
, wxString());
13489 if (idx
!= wxNOT_FOUND
)
13491 return & (*this)[idx
];
13497 const wxVariant
& wxRichTextProperties::GetProperty(const wxString
& name
) const
13499 static const wxVariant nullVariant
;
13500 int idx
= Find(name
);
13502 return m_properties
[idx
];
13504 return nullVariant
;
13507 wxString
wxRichTextProperties::GetPropertyString(const wxString
& name
) const
13509 return GetProperty(name
).GetString();
13512 long wxRichTextProperties::GetPropertyLong(const wxString
& name
) const
13514 return GetProperty(name
).GetLong();
13517 bool wxRichTextProperties::GetPropertyBool(const wxString
& name
) const
13519 return GetProperty(name
).GetBool();
13522 double wxRichTextProperties::GetPropertyDouble(const wxString
& name
) const
13524 return GetProperty(name
).GetDouble();
13527 void wxRichTextProperties::SetProperty(const wxVariant
& variant
)
13529 wxASSERT(!variant
.GetName().IsEmpty());
13531 int idx
= Find(variant
.GetName());
13534 m_properties
.Add(variant
);
13536 m_properties
[idx
] = variant
;
13539 void wxRichTextProperties::SetProperty(const wxString
& name
, const wxVariant
& variant
)
13541 int idx
= Find(name
);
13542 wxVariant
var(variant
);
13546 m_properties
.Add(var
);
13548 m_properties
[idx
] = var
;
13551 void wxRichTextProperties::SetProperty(const wxString
& name
, const wxString
& value
)
13553 SetProperty(name
, wxVariant(value
, name
));
13556 void wxRichTextProperties::SetProperty(const wxString
& name
, long value
)
13558 SetProperty(name
, wxVariant(value
, name
));
13561 void wxRichTextProperties::SetProperty(const wxString
& name
, double value
)
13563 SetProperty(name
, wxVariant(value
, name
));
13566 void wxRichTextProperties::SetProperty(const wxString
& name
, bool value
)
13568 SetProperty(name
, wxVariant(value
, name
));
13571 void wxRichTextProperties::RemoveProperties(const wxRichTextProperties
& properties
)
13574 for (i
= 0; i
< properties
.GetCount(); i
++)
13576 wxString name
= properties
.GetProperties()[i
].GetName();
13577 if (HasProperty(name
))
13582 void wxRichTextProperties::MergeProperties(const wxRichTextProperties
& properties
)
13585 for (i
= 0; i
< properties
.GetCount(); i
++)
13587 SetProperty(properties
.GetProperties()[i
]);
13591 wxRichTextObject
* wxRichTextObjectAddress::GetObject(wxRichTextParagraphLayoutBox
* topLevelContainer
) const
13593 if (m_address
.GetCount() == 0)
13594 return topLevelContainer
;
13596 wxRichTextCompositeObject
* p
= topLevelContainer
;
13598 while (p
&& i
< m_address
.GetCount())
13600 int pos
= m_address
[i
];
13601 wxASSERT(pos
>= 0 && pos
< (int) p
->GetChildren().GetCount());
13602 if (pos
< 0 || pos
>= (int) p
->GetChildren().GetCount())
13605 wxRichTextObject
* p1
= p
->GetChild(pos
);
13606 if (i
== (m_address
.GetCount()-1))
13609 p
= wxDynamicCast(p1
, wxRichTextCompositeObject
);
13615 bool wxRichTextObjectAddress::Create(wxRichTextParagraphLayoutBox
* topLevelContainer
, wxRichTextObject
* obj
)
13619 if (topLevelContainer
== obj
)
13622 wxRichTextObject
* o
= obj
;
13625 wxRichTextCompositeObject
* p
= wxDynamicCast(o
->GetParent(), wxRichTextCompositeObject
);
13629 int pos
= p
->GetChildren().IndexOf(o
);
13633 m_address
.Insert(pos
, 0);
13635 if (p
== topLevelContainer
)
13644 bool wxRichTextSelection::operator==(const wxRichTextSelection
& sel
) const
13646 if (m_container
!= sel
.m_container
)
13648 if (m_ranges
.GetCount() != sel
.m_ranges
.GetCount())
13651 for (i
= 0; i
< m_ranges
.GetCount(); i
++)
13652 if (!(m_ranges
[i
] == sel
.m_ranges
[i
]))
13657 // Get the selections appropriate to the specified object, if any; returns wxRICHTEXT_NO_SELECTION if none
13658 // or none at the level of the object's container.
13659 wxRichTextRangeArray
wxRichTextSelection::GetSelectionForObject(wxRichTextObject
* obj
) const
13663 wxRichTextParagraphLayoutBox
* container
= obj
->GetParentContainer();
13665 if (container
== m_container
)
13668 container
= obj
->GetContainer();
13671 if (container
->GetParent())
13673 // If we found that our object's container is within the range of
13674 // a selection higher up, then assume the whole original object
13675 // is also selected.
13676 wxRichTextParagraphLayoutBox
* parentContainer
= container
->GetParentContainer();
13677 if (parentContainer
== m_container
)
13679 if (WithinSelection(container
->GetRange().GetStart(), m_ranges
))
13681 wxRichTextRangeArray ranges
;
13682 ranges
.Add(obj
->GetRange());
13687 container
= parentContainer
;
13696 return wxRichTextRangeArray();
13699 // Is the given position within the selection?
13700 bool wxRichTextSelection::WithinSelection(long pos
, wxRichTextObject
* obj
) const
13706 wxRichTextRangeArray selectionRanges
= GetSelectionForObject(obj
);
13707 return WithinSelection(pos
, selectionRanges
);
13711 // Is the given position within the selection range?
13712 bool wxRichTextSelection::WithinSelection(long pos
, const wxRichTextRangeArray
& ranges
)
13715 for (i
= 0; i
< ranges
.GetCount(); i
++)
13717 const wxRichTextRange
& range
= ranges
[i
];
13718 if (pos
>= range
.GetStart() && pos
<= range
.GetEnd())
13724 // Is the given range completely within the selection range?
13725 bool wxRichTextSelection::WithinSelection(const wxRichTextRange
& range
, const wxRichTextRangeArray
& ranges
)
13728 for (i
= 0; i
< ranges
.GetCount(); i
++)
13730 const wxRichTextRange
& eachRange
= ranges
[i
];
13731 if (range
.IsWithin(eachRange
))
13737 IMPLEMENT_CLASS(wxRichTextDrawingHandler
, wxObject
)
13738 IMPLEMENT_CLASS(wxRichTextDrawingContext
, wxObject
)
13740 wxRichTextDrawingContext::wxRichTextDrawingContext(wxRichTextBuffer
* buffer
)
13744 if (m_buffer
&& m_buffer
->GetRichTextCtrl())
13745 EnableVirtualAttributes(m_buffer
->GetRichTextCtrl()->GetVirtualAttributesEnabled());
13748 bool wxRichTextDrawingContext::HasVirtualAttributes(wxRichTextObject
* obj
) const
13750 if (!GetVirtualAttributesEnabled())
13753 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
13756 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13757 if (handler
->HasVirtualAttributes(obj
))
13760 node
= node
->GetNext();
13765 wxRichTextAttr
wxRichTextDrawingContext::GetVirtualAttributes(wxRichTextObject
* obj
) const
13767 wxRichTextAttr attr
;
13768 if (!GetVirtualAttributesEnabled())
13771 // We apply all handlers, so we can may combine several different attributes
13772 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
13775 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13776 if (handler
->HasVirtualAttributes(obj
))
13778 bool success
= handler
->GetVirtualAttributes(attr
, obj
);
13780 wxUnusedVar(success
);
13783 node
= node
->GetNext();
13788 bool wxRichTextDrawingContext::ApplyVirtualAttributes(wxRichTextAttr
& attr
, wxRichTextObject
* obj
) const
13790 if (!GetVirtualAttributesEnabled())
13793 if (HasVirtualAttributes(obj
))
13795 wxRichTextAttr
a(GetVirtualAttributes(obj
));
13803 int wxRichTextDrawingContext::GetVirtualSubobjectAttributesCount(wxRichTextObject
* obj
) const
13805 if (!GetVirtualAttributesEnabled())
13808 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
13811 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13812 int count
= handler
->GetVirtualSubobjectAttributesCount(obj
);
13816 node
= node
->GetNext();
13821 int wxRichTextDrawingContext::GetVirtualSubobjectAttributes(wxRichTextObject
* obj
, wxArrayInt
& positions
, wxRichTextAttrArray
& attributes
) const
13823 if (!GetVirtualAttributesEnabled())
13826 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
13829 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13830 if (handler
->GetVirtualSubobjectAttributes(obj
, positions
, attributes
))
13831 return positions
.GetCount();
13833 node
= node
->GetNext();
13838 bool wxRichTextDrawingContext::HasVirtualText(const wxRichTextPlainText
* obj
) const
13840 if (!GetVirtualAttributesEnabled())
13843 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
13846 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13847 if (handler
->HasVirtualText(obj
))
13850 node
= node
->GetNext();
13855 bool wxRichTextDrawingContext::GetVirtualText(const wxRichTextPlainText
* obj
, wxString
& text
) const
13857 if (!GetVirtualAttributesEnabled())
13860 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
13863 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13864 if (handler
->GetVirtualText(obj
, text
))
13867 node
= node
->GetNext();
13872 /// Adds a handler to the end
13873 void wxRichTextBuffer::AddDrawingHandler(wxRichTextDrawingHandler
*handler
)
13875 sm_drawingHandlers
.Append(handler
);
13878 /// Inserts a handler at the front
13879 void wxRichTextBuffer::InsertDrawingHandler(wxRichTextDrawingHandler
*handler
)
13881 sm_drawingHandlers
.Insert( handler
);
13884 /// Removes a handler
13885 bool wxRichTextBuffer::RemoveDrawingHandler(const wxString
& name
)
13887 wxRichTextDrawingHandler
*handler
= FindDrawingHandler(name
);
13890 sm_drawingHandlers
.DeleteObject(handler
);
13898 wxRichTextDrawingHandler
* wxRichTextBuffer::FindDrawingHandler(const wxString
& name
)
13900 wxList::compatibility_iterator node
= sm_drawingHandlers
.GetFirst();
13903 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13904 if (handler
->GetName().Lower() == name
.Lower()) return handler
;
13906 node
= node
->GetNext();
13911 void wxRichTextBuffer::CleanUpDrawingHandlers()
13913 wxList::compatibility_iterator node
= sm_drawingHandlers
.GetFirst();
13916 wxRichTextDrawingHandler
* handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13917 wxList::compatibility_iterator next
= node
->GetNext();
13922 sm_drawingHandlers
.Clear();
13925 void wxRichTextBuffer::AddFieldType(wxRichTextFieldType
*fieldType
)
13927 sm_fieldTypes
[fieldType
->GetName()] = fieldType
;
13930 bool wxRichTextBuffer::RemoveFieldType(const wxString
& name
)
13932 wxRichTextFieldTypeHashMap::iterator it
= sm_fieldTypes
.find(name
);
13933 if (it
== sm_fieldTypes
.end())
13937 wxRichTextFieldType
* fieldType
= it
->second
;
13938 sm_fieldTypes
.erase(it
);
13944 wxRichTextFieldType
*wxRichTextBuffer::FindFieldType(const wxString
& name
)
13946 wxRichTextFieldTypeHashMap::iterator it
= sm_fieldTypes
.find(name
);
13947 if (it
== sm_fieldTypes
.end())
13953 void wxRichTextBuffer::CleanUpFieldTypes()
13955 wxRichTextFieldTypeHashMap::iterator it
;
13956 for( it
= sm_fieldTypes
.begin(); it
!= sm_fieldTypes
.end(); ++it
)
13958 wxRichTextFieldType
* fieldType
= it
->second
;
13962 sm_fieldTypes
.clear();