1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/richtext/richtextbuffer.cpp
3 // Purpose: Buffer for wxRichTextCtrl
4 // Author: Julian Smart
7 // Copyright: (c) Julian Smart
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
11 // For compilers that support precompilation, includes "wx.h".
12 #include "wx/wxprec.h"
20 #include "wx/richtext/richtextbuffer.h"
26 #include "wx/dataobj.h"
27 #include "wx/module.h"
30 #include "wx/settings.h"
31 #include "wx/filename.h"
32 #include "wx/clipbrd.h"
33 #include "wx/wfstream.h"
34 #include "wx/mstream.h"
35 #include "wx/sstream.h"
36 #include "wx/textfile.h"
37 #include "wx/hashmap.h"
38 #include "wx/dynarray.h"
40 #include "wx/richtext/richtextctrl.h"
41 #include "wx/richtext/richtextstyles.h"
42 #include "wx/richtext/richtextimagedlg.h"
43 #include "wx/richtext/richtextsizepage.h"
44 #include "wx/richtext/richtextxml.h"
46 #include "wx/listimpl.cpp"
47 #include "wx/arrimpl.cpp"
49 WX_DEFINE_LIST(wxRichTextObjectList
)
50 WX_DEFINE_LIST(wxRichTextLineList
)
52 // Switch off if the platform doesn't like it for some reason
53 #define wxRICHTEXT_USE_OPTIMIZED_DRAWING 1
55 // Use GetPartialTextExtents for platforms that support it natively
56 #define wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS 1
58 const wxChar wxRichTextLineBreakChar
= (wxChar
) 29;
60 // Helper classes for floating layout
61 struct wxRichTextFloatRectMap
63 wxRichTextFloatRectMap(int sY
, int eY
, int w
, wxRichTextObject
* obj
)
73 wxRichTextObject
* anchor
;
76 WX_DEFINE_SORTED_ARRAY(wxRichTextFloatRectMap
*, wxRichTextFloatRectMapArray
);
78 int wxRichTextFloatRectMapCmp(wxRichTextFloatRectMap
* r1
, wxRichTextFloatRectMap
* r2
)
80 return r1
->startY
- r2
->startY
;
83 class wxRichTextFloatCollector
86 wxRichTextFloatCollector(const wxRect
& availableRect
);
87 ~wxRichTextFloatCollector();
89 // Collect the floating objects info in the given paragraph
90 void CollectFloat(wxRichTextParagraph
* para
);
91 void CollectFloat(wxRichTextParagraph
* para
, wxRichTextObject
* floating
);
93 // Return the last paragraph we collected
94 wxRichTextParagraph
* LastParagraph();
96 // Given the start y position and the height of the line,
97 // find out how wide the line can be
98 wxRect
GetAvailableRect(int startY
, int endY
);
100 // Given a floating box, find its fit position
101 int GetFitPosition(int direction
, int start
, int height
) const;
102 int GetFitPosition(const wxRichTextFloatRectMapArray
& array
, int start
, int height
) const;
104 // Find the last y position
105 int GetLastRectBottom();
107 // Draw the floats inside a rect
108 void Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
);
110 // HitTest the floats
111 int HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, int flags
);
113 // Get floating object count
114 int GetFloatingObjectCount() const { return m_left
.GetCount() + m_right
.GetCount(); }
116 // Get floating objects
117 bool GetFloatingObjects(wxRichTextObjectList
& objects
) const;
120 bool DeleteFloat(wxRichTextObject
* obj
);
122 // Do we have this float already?
123 bool HasFloat(wxRichTextObject
* obj
);
125 bool HasFloats() const { return m_left
.GetCount() >0 || m_right
.GetCount() > 0; }
127 static int SearchAdjacentRect(const wxRichTextFloatRectMapArray
& array
, int point
);
129 static int GetWidthFromFloatRect(const wxRichTextFloatRectMapArray
& array
, int index
, int startY
, int endY
);
131 static void FreeFloatRectMapArray(wxRichTextFloatRectMapArray
& array
);
133 static void DrawFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
);
135 static int HitTestFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, int flags
);
138 wxRichTextFloatRectMapArray m_left
;
139 wxRichTextFloatRectMapArray m_right
;
141 wxRect m_availableRect
;
142 wxRichTextParagraph
* m_para
;
146 bool wxRichTextFloatCollector::DeleteFloat(wxRichTextObject
* obj
)
149 for (i
= 0; i
< m_left
.GetCount(); i
++)
151 if (m_left
[i
]->anchor
== obj
)
157 for (i
= 0; i
< m_right
.GetCount(); i
++)
159 if (m_right
[i
]->anchor
== obj
)
168 // Do we have this float already?
169 bool wxRichTextFloatCollector::HasFloat(wxRichTextObject
* obj
)
172 for (i
= 0; i
< m_left
.GetCount(); i
++)
174 if (m_left
[i
]->anchor
== obj
)
179 for (i
= 0; i
< m_right
.GetCount(); i
++)
181 if (m_right
[i
]->anchor
== obj
)
189 // Get floating objects
190 bool wxRichTextFloatCollector::GetFloatingObjects(wxRichTextObjectList
& objects
) const
193 for (i
= 0; i
< m_left
.GetCount(); i
++)
194 objects
.Append(m_left
[i
]->anchor
);
195 for (i
= 0; i
< m_right
.GetCount(); i
++)
196 objects
.Append(m_right
[i
]->anchor
);
202 * Binary search helper function
203 * The argument point is the Y coordinate, and this fuction
204 * always return the floating rect that contain this coordinate
205 * or under this coordinate.
207 int wxRichTextFloatCollector::SearchAdjacentRect(const wxRichTextFloatRectMapArray
& array
, int point
)
209 int end
= array
.GetCount() - 1;
222 int mid
= (start
+ end
) / 2;
223 if (array
[mid
]->startY
<= point
&& array
[mid
]->endY
>= point
)
225 else if (array
[mid
]->startY
> point
)
230 else if (array
[mid
]->endY
< point
)
240 int wxRichTextFloatCollector::GetWidthFromFloatRect(const wxRichTextFloatRectMapArray
& array
, int index
, int startY
, int endY
)
243 int len
= array
.GetCount();
245 wxASSERT(index
>= 0 && index
< len
);
247 if (array
[index
]->startY
< startY
&& array
[index
]->endY
> startY
)
248 ret
= ret
< array
[index
]->width
? array
[index
]->width
: ret
;
249 while (index
< len
&& array
[index
]->startY
<= endY
)
251 ret
= ret
< array
[index
]->width
? array
[index
]->width
: ret
;
258 wxRichTextFloatCollector::wxRichTextFloatCollector(const wxRect
& rect
) : m_left(wxRichTextFloatRectMapCmp
), m_right(wxRichTextFloatRectMapCmp
)
260 m_availableRect
= rect
;
264 void wxRichTextFloatCollector::FreeFloatRectMapArray(wxRichTextFloatRectMapArray
& array
)
266 int len
= array
.GetCount();
267 for (int i
= 0; i
< len
; i
++)
271 wxRichTextFloatCollector::~wxRichTextFloatCollector()
273 FreeFloatRectMapArray(m_left
);
274 FreeFloatRectMapArray(m_right
);
277 int wxRichTextFloatCollector::GetFitPosition(const wxRichTextFloatRectMapArray
& array
, int start
, int height
) const
279 if (array
.GetCount() == 0)
282 int i
= SearchAdjacentRect(array
, start
);
284 while (i
< (int) array
.GetCount())
286 if (array
[i
]->startY
- last
>= height
)
288 last
= array
[i
]->endY
;
295 int wxRichTextFloatCollector::GetFitPosition(int direction
, int start
, int height
) const
297 if (direction
== wxTEXT_BOX_ATTR_FLOAT_LEFT
)
298 return GetFitPosition(m_left
, start
, height
);
299 else if (direction
== wxTEXT_BOX_ATTR_FLOAT_RIGHT
)
300 return GetFitPosition(m_right
, start
, height
);
303 wxASSERT("Never should be here");
308 // Adds a floating image to the float collector.
309 // The actual positioning is done by wxRichTextParagraph::LayoutFloat.
310 void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph
* para
, wxRichTextObject
* floating
)
312 int direction
= floating
->GetFloatDirection();
314 wxPoint pos
= floating
->GetPosition();
315 wxSize size
= floating
->GetCachedSize();
316 wxRichTextFloatRectMap
*map
= new wxRichTextFloatRectMap(pos
.y
, pos
.y
+ size
.y
, size
.x
, floating
);
319 case wxTEXT_BOX_ATTR_FLOAT_NONE
:
322 case wxTEXT_BOX_ATTR_FLOAT_LEFT
:
323 // Just a not-enough simple assertion
324 wxASSERT (m_left
.Index(map
) == wxNOT_FOUND
);
327 case wxTEXT_BOX_ATTR_FLOAT_RIGHT
:
328 wxASSERT (m_right
.Index(map
) == wxNOT_FOUND
);
333 wxASSERT("Unrecognised float attribute.");
339 void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph
* para
)
341 wxRichTextObjectList::compatibility_iterator node
= para
->GetChildren().GetFirst();
344 wxRichTextObject
* floating
= node
->GetData();
346 if (floating
->IsFloating())
348 CollectFloat(para
, floating
);
351 node
= node
->GetNext();
357 wxRichTextParagraph
* wxRichTextFloatCollector::LastParagraph()
362 wxRect
wxRichTextFloatCollector::GetAvailableRect(int startY
, int endY
)
364 int widthLeft
= 0, widthRight
= 0;
365 if (m_left
.GetCount() != 0)
367 int i
= SearchAdjacentRect(m_left
, startY
);
368 if (i
< (int) m_left
.GetCount())
369 widthLeft
= GetWidthFromFloatRect(m_left
, i
, startY
, endY
);
371 if (m_right
.GetCount() != 0)
373 int j
= SearchAdjacentRect(m_right
, startY
);
374 if (j
< (int) m_right
.GetCount())
375 widthRight
= GetWidthFromFloatRect(m_right
, j
, startY
, endY
);
378 // TODO: actually we want to use the actual image positions to find the
379 // available remaining space, since the image might not be right up against
380 // the left or right edge of the container.
381 return wxRect(widthLeft
+ m_availableRect
.x
, 0, m_availableRect
.width
- widthLeft
- widthRight
, 0);
384 int wxRichTextFloatCollector::GetLastRectBottom()
387 int len
= m_left
.GetCount();
389 ret
= ret
> m_left
[len
-1]->endY
? ret
: m_left
[len
-1]->endY
;
391 len
= m_right
.GetCount();
393 ret
= ret
> m_right
[len
-1]->endY
? ret
: m_right
[len
-1]->endY
;
399 void wxRichTextFloatCollector::DrawFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& WXUNUSED(range
), const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
402 int end
= rect
.y
+ rect
.height
;
404 i
= SearchAdjacentRect(array
, start
);
405 if (i
< 0 || i
>= (int) array
.GetCount())
407 j
= SearchAdjacentRect(array
, end
);
408 if (j
< 0 || j
>= (int) array
.GetCount())
409 j
= array
.GetCount() - 1;
412 wxRichTextObject
* obj
= array
[i
]->anchor
;
413 wxRichTextRange r
= obj
->GetRange();
414 obj
->Draw(dc
, context
, r
, selection
, wxRect(obj
->GetPosition(), obj
->GetCachedSize()), descent
, style
);
419 void wxRichTextFloatCollector::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
421 if (m_left
.GetCount() > 0)
422 DrawFloat(m_left
, dc
, context
, range
, selection
, rect
, descent
, style
);
423 if (m_right
.GetCount() > 0)
424 DrawFloat(m_right
, dc
, context
, range
, selection
, rect
, descent
, style
);
427 int wxRichTextFloatCollector::HitTestFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& WXUNUSED(dc
), wxRichTextDrawingContext
& WXUNUSED(context
), const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, int WXUNUSED(flags
))
430 if (array
.GetCount() == 0)
431 return wxRICHTEXT_HITTEST_NONE
;
432 i
= SearchAdjacentRect(array
, pt
.y
);
433 if (i
< 0 || i
>= (int) array
.GetCount())
434 return wxRICHTEXT_HITTEST_NONE
;
435 if (!array
[i
]->anchor
->IsShown())
436 return wxRICHTEXT_HITTEST_NONE
;
438 wxPoint point
= array
[i
]->anchor
->GetPosition();
439 wxSize size
= array
[i
]->anchor
->GetCachedSize();
440 if (point
.x
<= pt
.x
&& point
.x
+ size
.x
>= pt
.x
441 && point
.y
<= pt
.y
&& point
.y
+ size
.y
>= pt
.y
)
443 textPosition
= array
[i
]->anchor
->GetRange().GetStart();
444 * obj
= array
[i
]->anchor
;
445 if (pt
.x
> (pt
.x
+ pt
.x
+ size
.x
) / 2)
446 return wxRICHTEXT_HITTEST_BEFORE
;
448 return wxRICHTEXT_HITTEST_AFTER
;
451 return wxRICHTEXT_HITTEST_NONE
;
454 int wxRichTextFloatCollector::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, int flags
)
456 int ret
= HitTestFloat(m_left
, dc
, context
, pt
, textPosition
, obj
, flags
);
457 if (ret
== wxRICHTEXT_HITTEST_NONE
)
459 ret
= HitTestFloat(m_right
, dc
, context
, pt
, textPosition
, obj
, flags
);
464 // Helpers for efficiency
465 inline void wxCheckSetFont(wxDC
& dc
, const wxFont
& font
)
470 inline void wxCheckSetPen(wxDC
& dc
, const wxPen
& pen
)
472 const wxPen
& pen1
= dc
.GetPen();
473 if (pen1
.IsOk() && pen
.IsOk())
475 if (pen1
.GetWidth() == pen
.GetWidth() &&
476 pen1
.GetStyle() == pen
.GetStyle() &&
477 pen1
.GetColour() == pen
.GetColour())
483 inline void wxCheckSetBrush(wxDC
& dc
, const wxBrush
& brush
)
485 const wxBrush
& brush1
= dc
.GetBrush();
486 if (brush1
.IsOk() && brush
.IsOk())
488 if (brush1
.GetStyle() == brush
.GetStyle() &&
489 brush1
.GetColour() == brush
.GetColour())
497 * This is the base for drawable objects.
500 IMPLEMENT_CLASS(wxRichTextObject
, wxObject
)
502 wxRichTextObject::wxRichTextObject(wxRichTextObject
* parent
)
510 wxRichTextObject::~wxRichTextObject()
514 void wxRichTextObject::Dereference()
522 void wxRichTextObject::Copy(const wxRichTextObject
& obj
)
525 m_maxSize
= obj
.m_maxSize
;
526 m_minSize
= obj
.m_minSize
;
528 m_range
= obj
.m_range
;
529 m_ownRange
= obj
.m_ownRange
;
530 m_attributes
= obj
.m_attributes
;
531 m_properties
= obj
.m_properties
;
532 m_descent
= obj
.m_descent
;
536 // Get/set the top-level container of this object.
537 wxRichTextParagraphLayoutBox
* wxRichTextObject::GetContainer() const
539 const wxRichTextObject
* p
= this;
544 return wxDynamicCast(p
, wxRichTextParagraphLayoutBox
);
551 void wxRichTextObject::SetMargins(int margin
)
553 SetMargins(margin
, margin
, margin
, margin
);
556 void wxRichTextObject::SetMargins(int leftMargin
, int rightMargin
, int topMargin
, int bottomMargin
)
558 GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().SetValue(leftMargin
, wxTEXT_ATTR_UNITS_PIXELS
);
559 GetAttributes().GetTextBoxAttr().GetMargins().GetRight().SetValue(rightMargin
, wxTEXT_ATTR_UNITS_PIXELS
);
560 GetAttributes().GetTextBoxAttr().GetMargins().GetTop().SetValue(topMargin
, wxTEXT_ATTR_UNITS_PIXELS
);
561 GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().SetValue(bottomMargin
, wxTEXT_ATTR_UNITS_PIXELS
);
564 int wxRichTextObject::GetLeftMargin() const
566 return GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().GetValue();
569 int wxRichTextObject::GetRightMargin() const
571 return GetAttributes().GetTextBoxAttr().GetMargins().GetRight().GetValue();
574 int wxRichTextObject::GetTopMargin() const
576 return GetAttributes().GetTextBoxAttr().GetMargins().GetTop().GetValue();
579 int wxRichTextObject::GetBottomMargin() const
581 return GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().GetValue();
584 // Calculate the available content space in the given rectangle, given the
585 // margins, border and padding specified in the object's attributes.
586 wxRect
wxRichTextObject::GetAvailableContentArea(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& outerRect
) const
588 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
589 marginRect
= outerRect
;
590 wxRichTextAttr
attr(GetAttributes());
591 context
.ApplyVirtualAttributes(attr
, (wxRichTextObject
*) this);
592 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
596 // Invalidate the buffer. With no argument, invalidates whole buffer.
597 void wxRichTextObject::Invalidate(const wxRichTextRange
& invalidRange
)
599 if (invalidRange
!= wxRICHTEXT_NONE
)
601 // If this is a floating object, size may not be recalculated
602 // after floats have been collected in an early stage of Layout.
603 // So avoid resetting the cache for floating objects during layout.
604 if (!IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode())
605 SetCachedSize(wxDefaultSize
);
606 SetMaxSize(wxDefaultSize
);
607 SetMinSize(wxDefaultSize
);
611 // Convert units in tenths of a millimetre to device units
612 int wxRichTextObject::ConvertTenthsMMToPixels(wxDC
& dc
, int units
) const
617 scale
= GetBuffer()->GetScale() / GetBuffer()->GetDimensionScale();
618 int p
= ConvertTenthsMMToPixels(dc
.GetPPI().x
, units
, scale
);
623 // Convert units in tenths of a millimetre to device units
624 int wxRichTextObject::ConvertTenthsMMToPixels(int ppi
, int units
, double scale
)
626 // There are ppi pixels in 254.1 "1/10 mm"
628 double pixels
= ((double) units
* (double)ppi
) / 254.1;
632 // If the result is very small, make it at least one pixel in size.
633 if (pixels
== 0 && units
> 0)
639 // Convert units in pixels to tenths of a millimetre
640 int wxRichTextObject::ConvertPixelsToTenthsMM(wxDC
& dc
, int pixels
) const
645 scale
= GetBuffer()->GetScale();
647 return ConvertPixelsToTenthsMM(dc
.GetPPI().x
, p
, scale
);
650 int wxRichTextObject::ConvertPixelsToTenthsMM(int ppi
, int pixels
, double scale
)
652 // There are ppi pixels in 254.1 "1/10 mm"
654 double p
= double(pixels
);
659 int units
= int( p
* 254.1 / (double) ppi
);
663 // Draw the borders and background for the given rectangle and attributes.
664 // Width and height are taken to be the outer margin size, not the content.
665 bool wxRichTextObject::DrawBoxAttributes(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& attr
, const wxRect
& boxRect
, int flags
, wxRichTextObject
* obj
)
667 // Assume boxRect is the area around the content
668 wxRect marginRect
= boxRect
;
669 wxRect contentRect
, borderRect
, paddingRect
, outlineRect
;
671 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
673 // Margin is transparent. Draw background from margin.
674 if (attr
.HasBackgroundColour() || (flags
& wxRICHTEXT_DRAW_SELECTED
))
677 if (flags
& wxRICHTEXT_DRAW_SELECTED
)
679 // TODO: get selection colour from control?
680 colour
= wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
);
683 colour
= attr
.GetBackgroundColour();
686 wxBrush
brush(colour
);
690 dc
.DrawRectangle(borderRect
);
693 if (flags
& wxRICHTEXT_DRAW_GUIDELINES
)
695 wxRichTextAttr editBorderAttr
;
696 // TODO: make guideline colour configurable
697 editBorderAttr
.GetTextBoxAttr().GetBorder().SetColour(*wxLIGHT_GREY
);
698 editBorderAttr
.GetTextBoxAttr().GetBorder().SetWidth(1, wxTEXT_ATTR_UNITS_PIXELS
);
699 editBorderAttr
.GetTextBoxAttr().GetBorder().SetStyle(wxTEXT_BOX_ATTR_BORDER_SOLID
);
703 wxRichTextCell
* cell
= wxDynamicCast(obj
, wxRichTextCell
);
706 // This ensures that thin lines drawn by adjacent cells (left and above)
707 // don't get overwritten by the guidelines.
708 editBorderAttr
.GetTextBoxAttr().GetBorder().GetLeft().Reset();
709 editBorderAttr
.GetTextBoxAttr().GetBorder().GetTop().Reset();
713 DrawBorder(dc
, buffer
, editBorderAttr
.GetTextBoxAttr().GetBorder(), borderRect
, flags
);
716 if (attr
.GetTextBoxAttr().GetBorder().IsValid())
717 DrawBorder(dc
, buffer
, attr
.GetTextBoxAttr().GetBorder(), borderRect
);
719 if (attr
.GetTextBoxAttr().GetOutline().IsValid())
720 DrawBorder(dc
, buffer
, attr
.GetTextBoxAttr().GetOutline(), outlineRect
);
726 bool wxRichTextObject::DrawBorder(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxTextAttrBorders
& attr
, const wxRect
& rect
, int WXUNUSED(flags
))
728 int borderLeft
= 0, borderRight
= 0, borderTop
= 0, borderBottom
= 0;
729 wxTextAttrDimensionConverter
converter(dc
, buffer
? buffer
->GetScale() : 1.0);
731 if (attr
.GetLeft().IsValid() && attr
.GetLeft().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
733 borderLeft
= converter
.GetPixels(attr
.GetLeft().GetWidth());
734 wxColour
col(attr
.GetLeft().GetColour());
736 // If pen width is > 1, resorts to a solid rectangle.
739 int penStyle
= wxSOLID
;
740 if (attr
.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
742 else if (attr
.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
743 penStyle
= wxLONG_DASH
;
744 wxPen
pen(col
, 1, penStyle
);
746 dc
.DrawLine(rect
.x
, rect
.y
, rect
.x
, rect
.y
+ rect
.height
);
749 else if (borderLeft
> 1)
755 dc
.DrawRectangle(rect
.x
, rect
.y
, borderLeft
, rect
.height
);
759 if (attr
.GetRight().IsValid() && attr
.GetRight().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
761 borderRight
= converter
.GetPixels(attr
.GetRight().GetWidth());
763 wxColour
col(attr
.GetRight().GetColour());
765 // If pen width is > 1, resorts to a solid rectangle.
766 if (borderRight
== 1)
768 int penStyle
= wxSOLID
;
769 if (attr
.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
771 else if (attr
.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
772 penStyle
= wxLONG_DASH
;
773 wxPen
pen(col
, 1, penStyle
);
775 dc
.DrawLine(rect
.x
+ rect
.width
, rect
.y
, rect
.x
+ rect
.width
, rect
.y
+ rect
.height
+ 1);
778 else if (borderRight
> 1)
784 dc
.DrawRectangle(rect
.x
+ rect
.width
- borderRight
, rect
.y
, borderRight
, rect
.height
+ 1);
788 if (attr
.GetTop().IsValid() && attr
.GetTop().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
790 borderTop
= converter
.GetPixels(attr
.GetTop().GetWidth());
792 wxColour
col(attr
.GetTop().GetColour());
794 // If pen width is > 1, resorts to a solid rectangle.
797 int penStyle
= wxSOLID
;
798 if (attr
.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
800 else if (attr
.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
801 penStyle
= wxLONG_DASH
;
802 wxPen
pen(col
, 1, penStyle
);
804 dc
.DrawLine(rect
.x
, rect
.y
, rect
.x
+ rect
.width
, rect
.y
);
807 else if (borderTop
> 1)
813 dc
.DrawRectangle(rect
.x
, rect
.y
, rect
.width
, borderTop
);
817 if (attr
.GetBottom().IsValid() && attr
.GetBottom().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
819 borderBottom
= converter
.GetPixels(attr
.GetBottom().GetWidth());
820 wxColour
col(attr
.GetBottom().GetColour());
822 // If pen width is > 1, resorts to a solid rectangle.
823 if (borderBottom
== 1)
825 int penStyle
= wxSOLID
;
826 if (attr
.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
828 else if (attr
.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
829 penStyle
= wxLONG_DASH
;
830 wxPen
pen(col
, 1, penStyle
);
832 dc
.DrawLine(rect
.x
, rect
.y
+ rect
.height
, rect
.x
+ rect
.width
, rect
.y
+ rect
.height
);
835 else if (borderBottom
> 1)
841 dc
.DrawRectangle(rect
.x
, rect
.y
+ rect
.height
- borderBottom
+ 1, rect
.width
, borderBottom
);
848 // Get the various rectangles of the box model in pixels. You can either specify contentRect (inner)
849 // or marginRect (outer), and the other must be the default rectangle (no width or height).
850 // Note that the outline doesn't affect the position of the rectangle, it's drawn in whatever space
853 // | Margin | Border | Padding | CONTENT | Padding | Border | Margin |
855 bool wxRichTextObject::GetBoxRects(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& attr
, wxRect
& marginRect
, wxRect
& borderRect
, wxRect
& contentRect
, wxRect
& paddingRect
, wxRect
& outlineRect
)
857 int borderLeft
= 0, borderRight
= 0, borderTop
= 0, borderBottom
= 0;
858 int outlineLeft
= 0, outlineRight
= 0, outlineTop
= 0, outlineBottom
= 0;
859 int paddingLeft
= 0, paddingRight
= 0, paddingTop
= 0, paddingBottom
= 0;
860 int marginLeft
= 0, marginRight
= 0, marginTop
= 0, marginBottom
= 0;
862 wxTextAttrDimensionConverter
converter(dc
, buffer
? buffer
->GetScale() : 1.0);
864 if (attr
.GetTextBoxAttr().GetMargins().GetLeft().IsValid())
865 marginLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetLeft());
866 if (attr
.GetTextBoxAttr().GetMargins().GetRight().IsValid())
867 marginRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetRight());
868 if (attr
.GetTextBoxAttr().GetMargins().GetTop().IsValid())
869 marginTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetTop());
870 if (attr
.GetTextBoxAttr().GetMargins().GetBottom().IsValid())
871 marginBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetBottom());
873 if (attr
.GetTextBoxAttr().GetBorder().GetLeft().GetWidth().IsValid())
874 borderLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetLeft().GetWidth());
875 if (attr
.GetTextBoxAttr().GetBorder().GetRight().GetWidth().IsValid())
876 borderRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetRight().GetWidth());
877 if (attr
.GetTextBoxAttr().GetBorder().GetTop().GetWidth().IsValid())
878 borderTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetTop().GetWidth());
879 if (attr
.GetTextBoxAttr().GetBorder().GetBottom().GetWidth().IsValid())
880 borderBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetBottom().GetWidth());
882 if (attr
.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
883 paddingLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetLeft());
884 if (attr
.GetTextBoxAttr().GetPadding().GetRight().IsValid())
885 paddingRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetRight());
886 if (attr
.GetTextBoxAttr().GetPadding().GetTop().IsValid())
887 paddingTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetTop());
888 if (attr
.GetTextBoxAttr().GetPadding().GetBottom().IsValid())
889 paddingBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetBottom());
891 if (attr
.GetTextBoxAttr().GetOutline().GetLeft().GetWidth().IsValid())
892 outlineLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetLeft().GetWidth());
893 if (attr
.GetTextBoxAttr().GetOutline().GetRight().GetWidth().IsValid())
894 outlineRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetRight().GetWidth());
895 if (attr
.GetTextBoxAttr().GetOutline().GetTop().GetWidth().IsValid())
896 outlineTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetTop().GetWidth());
897 if (attr
.GetTextBoxAttr().GetOutline().GetBottom().GetWidth().IsValid())
898 outlineBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetBottom().GetWidth());
900 int leftTotal
= marginLeft
+ borderLeft
+ paddingLeft
;
901 int rightTotal
= marginRight
+ borderRight
+ paddingRight
;
902 int topTotal
= marginTop
+ borderTop
+ paddingTop
;
903 int bottomTotal
= marginBottom
+ borderBottom
+ paddingBottom
;
905 if (marginRect
!= wxRect())
907 contentRect
.x
= marginRect
.x
+ leftTotal
;
908 contentRect
.y
= marginRect
.y
+ topTotal
;
909 contentRect
.width
= marginRect
.width
- (leftTotal
+ rightTotal
);
910 contentRect
.height
= marginRect
.height
- (topTotal
+ bottomTotal
);
914 marginRect
.x
= contentRect
.x
- leftTotal
;
915 marginRect
.y
= contentRect
.y
- topTotal
;
916 marginRect
.width
= contentRect
.width
+ (leftTotal
+ rightTotal
);
917 marginRect
.height
= contentRect
.height
+ (topTotal
+ bottomTotal
);
920 borderRect
.x
= marginRect
.x
+ marginLeft
;
921 borderRect
.y
= marginRect
.y
+ marginTop
;
922 borderRect
.width
= marginRect
.width
- (marginLeft
+ marginRight
);
923 borderRect
.height
= marginRect
.height
- (marginTop
+ marginBottom
);
925 paddingRect
.x
= marginRect
.x
+ marginLeft
+ borderLeft
;
926 paddingRect
.y
= marginRect
.y
+ marginTop
+ borderTop
;
927 paddingRect
.width
= marginRect
.width
- (marginLeft
+ marginRight
+ borderLeft
+ borderRight
);
928 paddingRect
.height
= marginRect
.height
- (marginTop
+ marginBottom
+ borderTop
+ borderBottom
);
930 // The outline is outside the margin and doesn't influence the overall box position or content size.
931 outlineRect
.x
= marginRect
.x
- outlineLeft
;
932 outlineRect
.y
= marginRect
.y
- outlineTop
;
933 outlineRect
.width
= marginRect
.width
+ (outlineLeft
+ outlineRight
);
934 outlineRect
.height
= marginRect
.height
+ (outlineTop
+ outlineBottom
);
939 // Get the total margin for the object in pixels, taking into account margin, padding and border size
940 bool wxRichTextObject::GetTotalMargin(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& attr
, int& leftMargin
, int& rightMargin
,
941 int& topMargin
, int& bottomMargin
)
943 // Assume boxRect is the area around the content
944 wxRect contentRect
, marginRect
, borderRect
, paddingRect
, outlineRect
;
945 marginRect
= wxRect(0, 0, 1000, 1000);
947 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
949 leftMargin
= contentRect
.GetLeft() - marginRect
.GetLeft();
950 rightMargin
= marginRect
.GetRight() - contentRect
.GetRight();
951 topMargin
= contentRect
.GetTop() - marginRect
.GetTop();
952 bottomMargin
= marginRect
.GetBottom() - contentRect
.GetBottom();
957 // Returns the rectangle which the child has available to it given restrictions specified in the
958 // child attribute, e.g. 50% width of the parent, 400 pixels, x position 20% of the parent, etc.
959 // availableContainerSpace might be a parent that the cell has to compute its width relative to.
960 // E.g. a cell that's 50% of its parent.
961 wxRect
wxRichTextObject::AdjustAvailableSpace(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& WXUNUSED(parentAttr
), const wxRichTextAttr
& childAttr
, const wxRect
& availableParentSpace
, const wxRect
& availableContainerSpace
)
963 wxRect rect
= availableParentSpace
;
966 scale
= buffer
->GetScale();
968 wxTextAttrDimensionConverter
converter(dc
, scale
, availableContainerSpace
.GetSize());
970 if (childAttr
.GetTextBoxAttr().GetWidth().IsValid())
971 rect
.width
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetWidth());
973 if (childAttr
.GetTextBoxAttr().GetHeight().IsValid())
974 rect
.height
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetHeight());
976 // Can specify either left or right for the position (we're assuming we can't
977 // set the left and right edges to effectively set the size. Would we want to do that?)
978 if (childAttr
.GetTextBoxAttr().GetPosition().GetLeft().IsValid())
980 rect
.x
= rect
.x
+ converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetLeft());
982 else if (childAttr
.GetTextBoxAttr().GetPosition().GetRight().IsValid())
984 int x
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetRight());
985 if (childAttr
.GetTextBoxAttr().GetPosition().GetRight().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE
)
986 rect
.x
= availableContainerSpace
.x
+ availableContainerSpace
.width
- rect
.width
;
991 if (childAttr
.GetTextBoxAttr().GetPosition().GetTop().IsValid())
993 rect
.y
= rect
.y
+ converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetTop());
995 else if (childAttr
.GetTextBoxAttr().GetPosition().GetBottom().IsValid())
997 int y
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetBottom());
998 if (childAttr
.GetTextBoxAttr().GetPosition().GetBottom().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE
)
999 rect
.y
= availableContainerSpace
.y
+ availableContainerSpace
.height
- rect
.height
;
1004 if (rect
.GetWidth() > availableParentSpace
.GetWidth())
1005 rect
.SetWidth(availableParentSpace
.GetWidth());
1010 // Dump to output stream for debugging
1011 void wxRichTextObject::Dump(wxTextOutputStream
& stream
)
1013 stream
<< GetClassInfo()->GetClassName() << wxT("\n");
1014 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");
1015 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");
1018 // Gets the containing buffer
1019 wxRichTextBuffer
* wxRichTextObject::GetBuffer() const
1021 const wxRichTextObject
* obj
= this;
1022 while (obj
&& !wxDynamicCast(obj
, wxRichTextBuffer
))
1023 obj
= obj
->GetParent();
1024 return wxDynamicCast(obj
, wxRichTextBuffer
);
1027 // Get the absolute object position, by traversing up the child/parent hierarchy
1028 wxPoint
wxRichTextObject::GetAbsolutePosition() const
1030 wxPoint pt
= GetPosition();
1032 wxRichTextObject
* p
= GetParent();
1035 pt
= pt
+ p
->GetPosition();
1042 // Hit-testing: returns a flag indicating hit test details, plus
1043 // information about position
1044 int wxRichTextObject::HitTest(wxDC
& WXUNUSED(dc
), wxRichTextDrawingContext
& WXUNUSED(context
), const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int WXUNUSED(flags
))
1047 return wxRICHTEXT_HITTEST_NONE
;
1049 wxRect rect
= GetRect();
1050 if (pt
.x
>= rect
.x
&& pt
.x
< rect
.x
+ rect
.width
&&
1051 pt
.y
>= rect
.y
&& pt
.y
< rect
.y
+ rect
.height
)
1054 *contextObj
= GetParentContainer();
1055 textPosition
= GetRange().GetStart();
1056 return wxRICHTEXT_HITTEST_ON
;
1059 return wxRICHTEXT_HITTEST_NONE
;
1062 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1063 // lays out the object again using the maximum ('best') size
1064 bool wxRichTextObject::LayoutToBestSize(wxDC
& dc
, wxRichTextDrawingContext
& context
, wxRichTextBuffer
* buffer
,
1065 const wxRichTextAttr
& parentAttr
, const wxRichTextAttr
& attr
,
1066 const wxRect
& availableParentSpace
, const wxRect
& availableContainerSpace
,
1069 wxRect availableChildRect
= AdjustAvailableSpace(dc
, buffer
, parentAttr
, attr
, availableParentSpace
, availableContainerSpace
);
1070 wxRect originalAvailableRect
= availableChildRect
;
1071 Layout(dc
, context
, availableChildRect
, availableContainerSpace
, style
);
1073 wxSize maxSize
= GetMaxSize();
1075 // Don't ignore if maxSize.x is zero, since we need to redo the paragraph's lines
1077 if (!attr
.GetTextBoxAttr().GetWidth().IsValid() && maxSize
.x
< availableChildRect
.width
)
1079 // Redo the layout with a fixed, minimum size this time.
1080 Invalidate(wxRICHTEXT_ALL
);
1081 wxRichTextAttr
newAttr(attr
);
1082 newAttr
.GetTextBoxAttr().GetWidth().SetValue(maxSize
.x
, wxTEXT_ATTR_UNITS_PIXELS
);
1083 newAttr
.GetTextBoxAttr().GetWidth().SetPosition(wxTEXT_BOX_ATTR_POSITION_ABSOLUTE
);
1085 availableChildRect
= AdjustAvailableSpace(dc
, buffer
, parentAttr
, newAttr
, availableParentSpace
, availableContainerSpace
);
1087 // If a paragraph, align the whole paragraph.
1088 // Problem with this: if we're limited by a floating object, a line may be centered
1089 // w.r.t. the smaller resulting box rather than the actual available width.
1090 // FIXME: aligning whole paragraph not compatible with floating objects
1091 if (attr
.HasAlignment() && (!wxRichTextBuffer::GetFloatingLayoutMode() || (GetContainer()->GetFloatCollector() && !GetContainer()->GetFloatCollector()->HasFloats())))
1093 // centering, right-justification
1094 if (attr
.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE
)
1096 availableChildRect
.x
= (originalAvailableRect
.GetWidth() - availableChildRect
.GetWidth())/2 + availableChildRect
.x
;
1098 else if (attr
.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT
)
1100 availableChildRect
.x
= availableChildRect
.x
+ originalAvailableRect
.GetWidth() - availableChildRect
.GetWidth();
1104 Layout(dc
, context
, availableChildRect
, availableContainerSpace
, style
);
1118 // Move the object recursively, by adding the offset from old to new
1119 void wxRichTextObject::Move(const wxPoint
& pt
)
1126 * wxRichTextCompositeObject
1127 * This is the base for drawable objects.
1130 IMPLEMENT_CLASS(wxRichTextCompositeObject
, wxRichTextObject
)
1132 wxRichTextCompositeObject::wxRichTextCompositeObject(wxRichTextObject
* parent
):
1133 wxRichTextObject(parent
)
1137 wxRichTextCompositeObject::~wxRichTextCompositeObject()
1142 /// Get the nth child
1143 wxRichTextObject
* wxRichTextCompositeObject::GetChild(size_t n
) const
1145 wxASSERT ( n
< m_children
.GetCount() );
1147 return m_children
.Item(n
)->GetData();
1150 /// Append a child, returning the position
1151 size_t wxRichTextCompositeObject::AppendChild(wxRichTextObject
* child
)
1153 m_children
.Append(child
);
1154 child
->SetParent(this);
1155 return m_children
.GetCount() - 1;
1158 /// Insert the child in front of the given object, or at the beginning
1159 bool wxRichTextCompositeObject::InsertChild(wxRichTextObject
* child
, wxRichTextObject
* inFrontOf
)
1163 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(inFrontOf
);
1164 m_children
.Insert(node
, child
);
1167 m_children
.Insert(child
);
1168 child
->SetParent(this);
1173 /// Delete the child
1174 bool wxRichTextCompositeObject::RemoveChild(wxRichTextObject
* child
, bool deleteChild
)
1176 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(child
);
1179 wxRichTextObject
* obj
= node
->GetData();
1180 m_children
.Erase(node
);
1189 /// Delete all children
1190 bool wxRichTextCompositeObject::DeleteChildren()
1192 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1195 wxRichTextObjectList::compatibility_iterator oldNode
= node
;
1197 wxRichTextObject
* child
= node
->GetData();
1198 child
->Dereference(); // Only delete if reference count is zero
1200 node
= node
->GetNext();
1201 m_children
.Erase(oldNode
);
1207 /// Get the child count
1208 size_t wxRichTextCompositeObject::GetChildCount() const
1210 return m_children
.GetCount();
1214 void wxRichTextCompositeObject::Copy(const wxRichTextCompositeObject
& obj
)
1216 wxRichTextObject::Copy(obj
);
1220 wxRichTextObjectList::compatibility_iterator node
= obj
.m_children
.GetFirst();
1223 wxRichTextObject
* child
= node
->GetData();
1224 wxRichTextObject
* newChild
= child
->Clone();
1225 newChild
->SetParent(this);
1226 m_children
.Append(newChild
);
1228 node
= node
->GetNext();
1232 /// Hit-testing: returns a flag indicating hit test details, plus
1233 /// information about position
1234 int wxRichTextCompositeObject::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
1237 return wxRICHTEXT_HITTEST_NONE
;
1239 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1242 wxRichTextObject
* child
= node
->GetData();
1244 if (child
->IsShown() && child
->IsTopLevel() && (flags
& wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS
))
1246 // Just check if we hit the overall object
1247 int ret
= child
->wxRichTextObject::HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
1248 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
1251 else if (child
->IsShown())
1253 int ret
= child
->HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
1254 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
1258 node
= node
->GetNext();
1261 return wxRICHTEXT_HITTEST_NONE
;
1264 /// Finds the absolute position and row height for the given character position
1265 bool wxRichTextCompositeObject::FindPosition(wxDC
& dc
, wxRichTextDrawingContext
& context
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
1267 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1270 wxRichTextObject
* child
= node
->GetData();
1272 // Don't recurse if the child is a top-level object,
1273 // such as a text box, because the character position will no longer
1274 // apply. By definition, a top-level object has its own range of
1275 // character positions.
1276 if (!child
->IsTopLevel() && child
->FindPosition(dc
, context
, index
, pt
, height
, forceLineStart
))
1279 node
= node
->GetNext();
1286 void wxRichTextCompositeObject::CalculateRange(long start
, long& end
)
1288 long current
= start
;
1289 long lastEnd
= current
;
1297 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1300 wxRichTextObject
* child
= node
->GetData();
1303 child
->CalculateRange(current
, childEnd
);
1306 current
= childEnd
+ 1;
1308 node
= node
->GetNext();
1313 // A top-level object always has a range of size 1,
1314 // because its children don't count at this level.
1316 m_range
.SetRange(start
, start
);
1318 // An object with no children has zero length
1319 if (m_children
.GetCount() == 0)
1321 m_ownRange
.SetRange(0, lastEnd
);
1327 // An object with no children has zero length
1328 if (m_children
.GetCount() == 0)
1331 m_range
.SetRange(start
, end
);
1335 /// Delete range from layout.
1336 bool wxRichTextCompositeObject::DeleteRange(const wxRichTextRange
& range
)
1338 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1342 wxRichTextObject
* obj
= (wxRichTextObject
*) node
->GetData();
1343 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
1345 // Delete the range in each paragraph
1347 // When a chunk has been deleted, internally the content does not
1348 // now match the ranges.
1349 // However, so long as deletion is not done on the same object twice this is OK.
1350 // If you may delete content from the same object twice, recalculate
1351 // the ranges inbetween DeleteRange calls by calling CalculateRanges, and
1352 // adjust the range you're deleting accordingly.
1354 if (!obj
->GetRange().IsOutside(range
))
1356 // No need to delete within a top-level object; just removing this object will do fine
1357 if (!obj
->IsTopLevel())
1358 obj
->DeleteRange(range
);
1360 // Delete an empty object, or paragraph within this range.
1361 if (obj
->IsEmpty() ||
1362 (range
.GetStart() <= obj
->GetRange().GetStart() && range
.GetEnd() >= obj
->GetRange().GetEnd()))
1364 // An empty paragraph has length 1, so won't be deleted unless the
1365 // whole range is deleted.
1366 RemoveChild(obj
, true);
1376 /// Get any text in this object for the given range
1377 wxString
wxRichTextCompositeObject::GetTextForRange(const wxRichTextRange
& range
) const
1380 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1383 wxRichTextObject
* child
= node
->GetData();
1384 wxRichTextRange childRange
= range
;
1385 if (!child
->GetRange().IsOutside(range
))
1387 childRange
.LimitTo(child
->GetRange());
1389 wxString childText
= child
->GetTextForRange(childRange
);
1393 node
= node
->GetNext();
1399 /// Get the child object at the given character position
1400 wxRichTextObject
* wxRichTextCompositeObject::GetChildAtPosition(long pos
) const
1402 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1405 wxRichTextObject
* child
= node
->GetData();
1406 if (child
->GetRange().GetStart() == pos
)
1408 node
= node
->GetNext();
1413 /// Recursively merge all pieces that can be merged.
1414 bool wxRichTextCompositeObject::Defragment(wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
)
1416 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1419 wxRichTextObject
* child
= node
->GetData();
1420 if (range
== wxRICHTEXT_ALL
|| !child
->GetRange().IsOutside(range
))
1422 wxRichTextCompositeObject
* composite
= wxDynamicCast(child
, wxRichTextCompositeObject
);
1424 composite
->Defragment(context
);
1426 // Optimization: if there are no virtual attributes, we won't need to
1427 // to split objects in order to paint individually attributed chunks.
1428 // So only merge in this case.
1429 if (!context
.GetVirtualAttributesEnabled())
1431 if (node
->GetNext())
1433 wxRichTextObject
* nextChild
= node
->GetNext()->GetData();
1434 if (child
->CanMerge(nextChild
, context
) && child
->Merge(nextChild
, context
))
1436 nextChild
->Dereference();
1437 m_children
.Erase(node
->GetNext());
1440 node
= node
->GetNext();
1443 node
= node
->GetNext();
1447 // If we might have virtual attributes, we first see if we have to split
1448 // objects so that they may be painted with potential virtual attributes,
1449 // since text objects can only draw or measure with a single attributes object
1451 wxRichTextObject
* childAfterSplit
= child
;
1452 if (child
->CanSplit(context
))
1454 childAfterSplit
= child
->Split(context
);
1455 node
= m_children
.Find(childAfterSplit
);
1458 if (node
->GetNext())
1460 wxRichTextObject
* nextChild
= node
->GetNext()->GetData();
1462 // First split child and nextChild so we have smaller fragments to merge.
1463 // Then Merge only has to test per-object virtual attributes
1464 // because for an object with all the same sub-object attributes,
1465 // then any general virtual attributes should be merged with sub-objects by
1466 // the implementation.
1468 wxRichTextObject
* nextChildAfterSplit
= nextChild
;
1470 if (nextChildAfterSplit
->CanSplit(context
))
1471 nextChildAfterSplit
= nextChild
->Split(context
);
1473 bool splitNextChild
= nextChild
!= nextChildAfterSplit
;
1475 // See if we can merge this new fragment with (perhaps the first part of) the next object.
1476 // Note that we use nextChild because if we had split nextChild, the first object always
1477 // remains (and further parts are appended). However we must use childAfterSplit since
1478 // it's the last part of a possibly split child.
1480 if (childAfterSplit
->CanMerge(nextChild
, context
) && childAfterSplit
->Merge(nextChild
, context
))
1482 nextChild
->Dereference();
1483 m_children
.Erase(node
->GetNext());
1485 // Don't set node -- we'll see if we can merge again with the next
1486 // child. UNLESS we split this or the next child, in which case we know we have to
1487 // move on to the end of the next child.
1489 node
= m_children
.Find(nextChildAfterSplit
);
1494 node
= m_children
.Find(nextChildAfterSplit
); // start from the last object in the split
1496 node
= node
->GetNext();
1500 node
= node
->GetNext();
1504 node
= node
->GetNext();
1507 // Delete any remaining empty objects, but leave at least one empty object per composite object.
1508 if (GetChildCount() > 1)
1510 node
= m_children
.GetFirst();
1513 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
1514 wxRichTextObject
* child
= node
->GetData();
1515 if (range
== wxRICHTEXT_ALL
|| !child
->GetRange().IsOutside(range
))
1517 if (child
->IsEmpty())
1519 child
->Dereference();
1520 m_children
.Erase(node
);
1525 node
= node
->GetNext();
1532 /// Dump to output stream for debugging
1533 void wxRichTextCompositeObject::Dump(wxTextOutputStream
& stream
)
1535 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1538 wxRichTextObject
* child
= node
->GetData();
1539 child
->Dump(stream
);
1540 node
= node
->GetNext();
1544 /// Get/set the object size for the given range. Returns false if the range
1545 /// is invalid for this object.
1546 bool wxRichTextCompositeObject::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, const wxPoint
& position
, const wxSize
& parentSize
, wxArrayInt
* partialExtents
) const
1548 if (!range
.IsWithin(GetRange()))
1553 wxArrayInt childExtents
;
1560 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1563 wxRichTextObject
* child
= node
->GetData();
1564 if (!child
->GetRange().IsOutside(range
))
1566 // Floating objects have a zero size within the paragraph.
1567 if (child
->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode())
1572 if (partialExtents
->GetCount() > 0)
1573 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
1577 partialExtents
->Add(0 /* zero size */ + lastSize
);
1584 wxRichTextRange rangeToUse
= range
;
1585 rangeToUse
.LimitTo(child
->GetRange());
1586 if (child
->IsTopLevel())
1587 rangeToUse
= child
->GetOwnRange();
1589 int childDescent
= 0;
1591 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we're already cached the size,
1592 // but it's only going to be used after caching has taken place.
1593 if ((flags
& wxRICHTEXT_HEIGHT_ONLY
) && child
->GetCachedSize().y
!= 0)
1595 childDescent
= child
->GetDescent();
1596 childSize
= child
->GetCachedSize();
1598 sz
.y
= wxMax(sz
.y
, childSize
.y
);
1599 sz
.x
+= childSize
.x
;
1600 descent
= wxMax(descent
, childDescent
);
1602 else if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, context
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
), parentSize
, p
))
1604 sz
.y
= wxMax(sz
.y
, childSize
.y
);
1605 sz
.x
+= childSize
.x
;
1606 descent
= wxMax(descent
, childDescent
);
1608 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange() || child
->IsTopLevel()))
1610 child
->SetCachedSize(childSize
);
1611 child
->SetDescent(childDescent
);
1617 if (partialExtents
->GetCount() > 0)
1618 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
1623 for (i
= 0; i
< childExtents
.GetCount(); i
++)
1625 partialExtents
->Add(childExtents
[i
] + lastSize
);
1635 node
= node
->GetNext();
1641 // Invalidate the buffer. With no argument, invalidates whole buffer.
1642 void wxRichTextCompositeObject::Invalidate(const wxRichTextRange
& invalidRange
)
1644 wxRichTextObject::Invalidate(invalidRange
);
1646 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1649 wxRichTextObject
* child
= node
->GetData();
1650 if (invalidRange
!= wxRICHTEXT_ALL
&& invalidRange
!= wxRICHTEXT_NONE
&& child
->GetRange().IsOutside(invalidRange
))
1654 else if (child
->IsTopLevel())
1656 if (wxRichTextBuffer::GetFloatingLayoutMode() && child
->IsFloating() && GetBuffer()->GetFloatCollector() && GetBuffer()->GetFloatCollector()->HasFloat(child
))
1658 // Don't invalidate subhierarchy if we've already been laid out
1662 if (invalidRange
== wxRICHTEXT_NONE
)
1663 child
->Invalidate(wxRICHTEXT_NONE
);
1665 child
->Invalidate(wxRICHTEXT_ALL
); // All children must be invalidated if within parent range
1669 child
->Invalidate(invalidRange
);
1670 node
= node
->GetNext();
1674 // Move the object recursively, by adding the offset from old to new
1675 void wxRichTextCompositeObject::Move(const wxPoint
& pt
)
1677 wxPoint oldPos
= GetPosition();
1679 wxPoint offset
= pt
- oldPos
;
1681 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1684 wxRichTextObject
* child
= node
->GetData();
1685 wxPoint childPos
= child
->GetPosition() + offset
;
1686 child
->Move(childPos
);
1687 node
= node
->GetNext();
1693 * wxRichTextParagraphLayoutBox
1694 * This box knows how to lay out paragraphs.
1697 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraphLayoutBox
, wxRichTextCompositeObject
)
1699 wxRichTextParagraphLayoutBox::wxRichTextParagraphLayoutBox(wxRichTextObject
* parent
):
1700 wxRichTextCompositeObject(parent
)
1705 wxRichTextParagraphLayoutBox::~wxRichTextParagraphLayoutBox()
1707 if (m_floatCollector
)
1709 delete m_floatCollector
;
1710 m_floatCollector
= NULL
;
1714 /// Initialize the object.
1715 void wxRichTextParagraphLayoutBox::Init()
1719 // For now, assume is the only box and has no initial size.
1720 m_range
= wxRichTextRange(0, -1);
1721 m_ownRange
= wxRichTextRange(0, -1);
1723 m_invalidRange
= wxRICHTEXT_ALL
;
1725 m_partialParagraph
= false;
1726 m_floatCollector
= NULL
;
1729 void wxRichTextParagraphLayoutBox::Clear()
1733 if (m_floatCollector
)
1734 delete m_floatCollector
;
1735 m_floatCollector
= NULL
;
1736 m_partialParagraph
= false;
1740 void wxRichTextParagraphLayoutBox::Copy(const wxRichTextParagraphLayoutBox
& obj
)
1744 wxRichTextCompositeObject::Copy(obj
);
1746 m_partialParagraph
= obj
.m_partialParagraph
;
1747 m_defaultAttributes
= obj
.m_defaultAttributes
;
1750 // Gather information about floating objects; only gather floats for those paragraphs that
1751 // will not be formatted again due to optimization, after which floats will be gathered per-paragraph
1753 bool wxRichTextParagraphLayoutBox::UpdateFloatingObjects(const wxRect
& availableRect
, wxRichTextObject
* untilObj
)
1755 if (m_floatCollector
!= NULL
)
1756 delete m_floatCollector
;
1757 m_floatCollector
= new wxRichTextFloatCollector(availableRect
);
1758 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1759 // Only gather floats up to the point we'll start formatting paragraphs.
1760 while (untilObj
&& node
&& node
->GetData() != untilObj
)
1762 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
1763 wxASSERT (child
!= NULL
);
1765 m_floatCollector
->CollectFloat(child
);
1766 node
= node
->GetNext();
1772 // Returns the style sheet associated with the overall buffer.
1773 wxRichTextStyleSheet
* wxRichTextParagraphLayoutBox::GetStyleSheet() const
1775 return GetBuffer() ? GetBuffer()->GetStyleSheet() : (wxRichTextStyleSheet
*) NULL
;
1778 // Get the number of floating objects at this level
1779 int wxRichTextParagraphLayoutBox::GetFloatingObjectCount() const
1781 if (m_floatCollector
)
1782 return m_floatCollector
->GetFloatingObjectCount();
1787 // Get a list of floating objects
1788 bool wxRichTextParagraphLayoutBox::GetFloatingObjects(wxRichTextObjectList
& objects
) const
1790 if (m_floatCollector
)
1792 return m_floatCollector
->GetFloatingObjects(objects
);
1799 void wxRichTextParagraphLayoutBox::UpdateRanges()
1803 start
= GetRange().GetStart();
1805 CalculateRange(start
, end
);
1809 int wxRichTextParagraphLayoutBox::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
1812 return wxRICHTEXT_HITTEST_NONE
;
1814 int ret
= wxRICHTEXT_HITTEST_NONE
;
1815 if (wxRichTextBuffer::GetFloatingLayoutMode() && m_floatCollector
&& (flags
& wxRICHTEXT_HITTEST_NO_FLOATING_OBJECTS
) == 0)
1816 ret
= m_floatCollector
->HitTest(dc
, context
, pt
, textPosition
, obj
, flags
);
1818 if (ret
== wxRICHTEXT_HITTEST_NONE
)
1819 return wxRichTextCompositeObject::HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
1827 /// Draw the floating objects
1828 void wxRichTextParagraphLayoutBox::DrawFloats(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
1830 if (wxRichTextBuffer::GetFloatingLayoutMode() && m_floatCollector
)
1831 m_floatCollector
->Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
1834 void wxRichTextParagraphLayoutBox::MoveAnchoredObjectToParagraph(wxRichTextParagraph
* from
, wxRichTextParagraph
* to
, wxRichTextObject
* obj
)
1839 from
->RemoveChild(obj
);
1840 to
->AppendChild(obj
);
1844 bool wxRichTextParagraphLayoutBox::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
1849 wxRect
thisRect(GetPosition(), GetCachedSize());
1851 wxRichTextAttr
attr(GetAttributes());
1852 context
.ApplyVirtualAttributes(attr
, this);
1855 if (selection
.IsValid() && GetParentContainer() != this && selection
.GetContainer() == this && selection
.WithinSelection(GetRange().GetStart(), GetParentContainer()))
1856 flags
|= wxRICHTEXT_DRAW_SELECTED
;
1858 // Don't draw guidelines if at top level
1859 int theseFlags
= flags
;
1861 theseFlags
&= ~wxRICHTEXT_DRAW_GUIDELINES
;
1862 DrawBoxAttributes(dc
, GetBuffer(), attr
, thisRect
, theseFlags
, this);
1864 if (wxRichTextBuffer::GetFloatingLayoutMode())
1865 DrawFloats(dc
, context
, range
, selection
, rect
, descent
, style
);
1867 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1870 wxRichTextObject
* child
= node
->GetData();
1872 if (child
&& !child
->GetRange().IsOutside(range
))
1874 wxRect
childRect(child
->GetPosition(), child
->GetCachedSize());
1875 wxRichTextRange childRange
= range
;
1876 if (child
->IsTopLevel())
1878 childRange
= child
->GetOwnRange();
1881 if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) == 0) && childRect
.GetTop() > rect
.GetBottom())
1886 else if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) == 0) && childRect
.GetBottom() < rect
.GetTop())
1891 child
->Draw(dc
, context
, childRange
, selection
, rect
, descent
, style
);
1894 node
= node
->GetNext();
1899 /// Lay the item out
1900 bool wxRichTextParagraphLayoutBox::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
)
1902 SetPosition(rect
.GetPosition());
1907 wxRect availableSpace
;
1908 bool formatRect
= (style
& wxRICHTEXT_LAYOUT_SPECIFIED_RECT
) == wxRICHTEXT_LAYOUT_SPECIFIED_RECT
;
1910 wxRichTextAttr
attr(GetAttributes());
1911 context
.ApplyVirtualAttributes(attr
, this);
1913 // If only laying out a specific area, the passed rect has a different meaning:
1914 // the visible part of the buffer. This is used in wxRichTextCtrl::OnSize,
1915 // so that during a size, only the visible part will be relaid out, or
1916 // it would take too long causing flicker. As an approximation, we assume that
1917 // everything up to the start of the visible area is laid out correctly.
1920 wxRect
rect2(0, 0, rect
.width
, rect
.height
);
1921 availableSpace
= GetAvailableContentArea(dc
, context
, rect2
);
1923 // Invalidate the part of the buffer from the first visible line
1924 // to the end. If other parts of the buffer are currently invalid,
1925 // then they too will be taken into account if they are above
1926 // the visible point.
1928 wxRichTextLine
* line
= GetLineAtYPosition(rect
.y
);
1930 startPos
= line
->GetAbsoluteRange().GetStart();
1932 Invalidate(wxRichTextRange(startPos
, GetOwnRange().GetEnd()));
1936 availableSpace
= GetAvailableContentArea(dc
, context
, rect
);
1939 // Fix the width if we're at the top level
1941 attr
.GetTextBoxAttr().GetWidth().SetValue(rect
.GetWidth(), wxTEXT_ATTR_UNITS_PIXELS
);
1943 int leftMargin
, rightMargin
, topMargin
, bottomMargin
;
1944 wxRichTextObject::GetTotalMargin(dc
, GetBuffer(), attr
, leftMargin
, rightMargin
,
1945 topMargin
, bottomMargin
);
1950 // The maximum paragraph maximum width, so we can set the overall maximum width for this object
1951 int maxMaxWidth
= 0;
1953 // The maximum paragraph minimum width, so we can set the overall minimum width for this object
1954 int maxMinWidth
= 0;
1956 // If we have vertical alignment, we must recalculate everything.
1957 bool hasVerticalAlignment
= (attr
.GetTextBoxAttr().HasVerticalAlignment() &&
1958 (attr
.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP
));
1960 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1962 bool layoutAll
= true;
1964 // Get invalid range, rounding to paragraph start/end.
1965 wxRichTextRange invalidRange
= GetInvalidRange(true);
1967 if (invalidRange
== wxRICHTEXT_NONE
&& !formatRect
)
1970 if (invalidRange
== wxRICHTEXT_ALL
|| hasVerticalAlignment
)
1972 else // If we know what range is affected, start laying out from that point on.
1973 if (invalidRange
.GetStart() >= GetOwnRange().GetStart())
1975 wxRichTextParagraph
* firstParagraph
= GetParagraphAtPosition(invalidRange
.GetStart());
1978 wxRichTextObjectList::compatibility_iterator firstNode
= m_children
.Find(firstParagraph
);
1979 wxRichTextObjectList::compatibility_iterator previousNode
;
1981 previousNode
= firstNode
->GetPrevious();
1986 wxRichTextParagraph
* previousParagraph
= wxDynamicCast(previousNode
->GetData(), wxRichTextParagraph
);
1987 availableSpace
.y
= previousParagraph
->GetPosition().y
+ previousParagraph
->GetCachedSize().y
;
1990 // Now we're going to start iterating from the first affected paragraph.
1998 // Gather information about only those floating objects that will not be formatted,
1999 // after which floats will be gathered per-paragraph during layout.
2000 if (wxRichTextBuffer::GetFloatingLayoutMode())
2001 UpdateFloatingObjects(availableSpace
, node
? node
->GetData() : (wxRichTextObject
*) NULL
);
2003 // A way to force speedy rest-of-buffer layout (the 'else' below)
2004 bool forceQuickLayout
= false;
2006 // First get the size of the paragraphs we won't be laying out
2007 wxRichTextObjectList::compatibility_iterator n
= m_children
.GetFirst();
2008 while (n
&& n
!= node
)
2010 wxRichTextParagraph
* child
= wxDynamicCast(n
->GetData(), wxRichTextParagraph
);
2013 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
2014 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
2015 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
2022 // Assume this box only contains paragraphs
2024 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2025 // Unsure if this is needed
2026 // wxCHECK_MSG( child, false, wxT("Unknown object in layout") );
2028 if (child
&& child
->IsShown())
2030 // TODO: what if the child hasn't been laid out (e.g. involved in Undo) but still has 'old' lines
2031 if ( !forceQuickLayout
&&
2033 child
->GetLines().IsEmpty() ||
2034 !child
->GetRange().IsOutside(invalidRange
)) )
2036 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
2037 // lays out the object again using the minimum size
2038 child
->LayoutToBestSize(dc
, context
, GetBuffer(),
2039 attr
, child
->GetAttributes(), availableSpace
, rect
, style
&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT
);
2041 // Layout must set the cached size
2042 availableSpace
.y
+= child
->GetCachedSize().y
;
2043 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
2044 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
2045 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
2047 // If we're just formatting the visible part of the buffer,
2048 // and we're now past the bottom of the window, and we don't have any
2049 // floating objects (since they may cause wrapping to change for the rest of the
2050 // the buffer), start quick layout.
2051 if (!hasVerticalAlignment
&& formatRect
&& child
->GetPosition().y
> rect
.GetBottom() && GetFloatingObjectCount() == 0)
2052 forceQuickLayout
= true;
2056 // We're outside the immediately affected range, so now let's just
2057 // move everything up or down. This assumes that all the children have previously
2058 // been laid out and have wrapped line lists associated with them.
2059 // TODO: check all paragraphs before the affected range.
2061 int inc
= availableSpace
.y
- child
->GetPosition().y
;
2065 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2068 if (child
->GetLines().GetCount() == 0)
2070 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
2071 // lays out the object again using the minimum size
2072 child
->LayoutToBestSize(dc
, context
, GetBuffer(),
2073 attr
, child
->GetAttributes(), availableSpace
, rect
, style
&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT
);
2075 //child->Layout(dc, availableChildRect, style);
2078 child
->Move(wxPoint(child
->GetPosition().x
, child
->GetPosition().y
+ inc
));
2080 availableSpace
.y
+= child
->GetCachedSize().y
;
2081 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
2082 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
2083 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
2086 node
= node
->GetNext();
2092 node
= node
->GetNext();
2095 node
= m_children
.GetLast();
2096 if (node
&& node
->GetData()->IsShown())
2098 wxRichTextObject
* child
= node
->GetData();
2099 maxHeight
= child
->GetPosition().y
- (GetPosition().y
+ topMargin
) + child
->GetCachedSize().y
;
2102 maxHeight
= 0; // topMargin + bottomMargin;
2104 // Check the bottom edge of any floating object
2105 if (wxRichTextBuffer::GetFloatingLayoutMode() && GetFloatCollector() && GetFloatCollector()->HasFloats())
2107 int bottom
= GetFloatCollector()->GetLastRectBottom();
2108 if (bottom
> maxHeight
)
2112 if (attr
.GetTextBoxAttr().GetSize().GetWidth().IsValid())
2114 wxRect r
= AdjustAvailableSpace(dc
, GetBuffer(), wxRichTextAttr() /* not used */, attr
, parentRect
, parentRect
);
2115 int w
= r
.GetWidth();
2117 // Convert external to content rect
2118 w
= w
- leftMargin
- rightMargin
;
2119 maxWidth
= wxMax(maxWidth
, w
);
2120 maxMaxWidth
= wxMax(maxMaxWidth
, w
);
2124 // TODO: Make sure the layout box's position reflects
2125 // the position of the children, but without
2126 // breaking layout of a box within a paragraph.
2129 // TODO: (also in para layout) should set the
2130 // object's size to an absolute one if specified,
2131 // but if not specified, calculate it from content.
2133 // We need to add back the margins etc.
2135 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2136 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxWidth
, maxHeight
));
2137 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2138 SetCachedSize(marginRect
.GetSize());
2141 // The maximum size is the greatest of all maximum widths for all paragraphs.
2143 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2144 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxMaxWidth
, maxHeight
)); // Actually max height is a lie, we can't know it
2145 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2146 SetMaxSize(marginRect
.GetSize());
2149 // The minimum size is the greatest of all minimum widths for all paragraphs.
2151 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2152 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxMinWidth
, maxHeight
)); // Actually max height is a lie, we can't know it
2153 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2154 SetMinSize(marginRect
.GetSize());
2157 if (attr
.GetTextBoxAttr().HasVerticalAlignment() &&
2158 (attr
.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP
))
2161 int leftOverSpace
= availableSpace
.height
- topMargin
- bottomMargin
- maxHeight
;
2162 if (leftOverSpace
> 0)
2164 if (attr
.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_CENTRE
)
2166 yOffset
= (leftOverSpace
/2);
2168 else if (attr
.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_BOTTOM
)
2170 yOffset
= leftOverSpace
;
2174 // Move all the children to vertically align the content
2175 // This doesn't take into account floating objects, unfortunately.
2178 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2181 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2183 child
->Move(wxPoint(child
->GetPosition().x
, child
->GetPosition().y
+ yOffset
));
2185 node
= node
->GetNext();
2190 m_invalidRange
= wxRICHTEXT_NONE
;
2195 /// Get/set the size for the given range.
2196 bool wxRichTextParagraphLayoutBox::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, const wxPoint
& position
, const wxSize
& parentSize
, wxArrayInt
* WXUNUSED(partialExtents
)) const
2200 wxRichTextObjectList::compatibility_iterator startPara
= wxRichTextObjectList::compatibility_iterator();
2201 wxRichTextObjectList::compatibility_iterator endPara
= wxRichTextObjectList::compatibility_iterator();
2203 // First find the first paragraph whose starting position is within the range.
2204 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2207 // child is a paragraph
2208 wxRichTextObject
* child
= node
->GetData();
2209 const wxRichTextRange
& r
= child
->GetRange();
2211 if (r
.GetStart() <= range
.GetStart() && r
.GetEnd() >= range
.GetStart())
2217 node
= node
->GetNext();
2220 // Next find the last paragraph containing part of the range
2221 node
= m_children
.GetFirst();
2224 // child is a paragraph
2225 wxRichTextObject
* child
= node
->GetData();
2226 const wxRichTextRange
& r
= child
->GetRange();
2228 if (r
.GetStart() <= range
.GetEnd() && r
.GetEnd() >= range
.GetEnd())
2234 node
= node
->GetNext();
2237 if (!startPara
|| !endPara
)
2240 // Now we can add up the sizes
2241 for (node
= startPara
; node
; node
= node
->GetNext())
2243 // child is a paragraph
2244 wxRichTextObject
* child
= node
->GetData();
2245 const wxRichTextRange
& childRange
= child
->GetRange();
2246 wxRichTextRange rangeToFind
= range
;
2247 rangeToFind
.LimitTo(childRange
);
2249 if (child
->IsTopLevel())
2250 rangeToFind
= child
->GetOwnRange();
2254 int childDescent
= 0;
2255 child
->GetRangeSize(rangeToFind
, childSize
, childDescent
, dc
, context
, flags
, position
, parentSize
);
2257 descent
= wxMax(childDescent
, descent
);
2259 sz
.x
= wxMax(sz
.x
, childSize
.x
);
2260 sz
.y
+= childSize
.y
;
2262 if (node
== endPara
)
2271 /// Get the paragraph at the given position
2272 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphAtPosition(long pos
, bool caretPosition
) const
2277 // First find the first paragraph whose starting position is within the range.
2278 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2281 // child is a paragraph
2282 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2283 // wxASSERT (child != NULL);
2287 // Return first child in buffer if position is -1
2291 if (child
->GetRange().Contains(pos
))
2295 node
= node
->GetNext();
2300 /// Get the line at the given position
2301 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineAtPosition(long pos
, bool caretPosition
) const
2306 // First find the first paragraph whose starting position is within the range.
2307 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2310 wxRichTextObject
* obj
= (wxRichTextObject
*) node
->GetData();
2311 if (obj
->GetRange().Contains(pos
))
2313 // child is a paragraph
2314 wxRichTextParagraph
* child
= wxDynamicCast(obj
, wxRichTextParagraph
);
2315 // wxASSERT (child != NULL);
2319 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2322 wxRichTextLine
* line
= node2
->GetData();
2324 wxRichTextRange range
= line
->GetAbsoluteRange();
2326 if (range
.Contains(pos
) ||
2328 // If the position is end-of-paragraph, then return the last line of
2329 // of the paragraph.
2330 ((range
.GetEnd() == child
->GetRange().GetEnd()-1) && (pos
== child
->GetRange().GetEnd())))
2333 node2
= node2
->GetNext();
2338 node
= node
->GetNext();
2341 int lineCount
= GetLineCount();
2343 return GetLineForVisibleLineNumber(lineCount
-1);
2348 /// Get the line at the given y pixel position, or the last line.
2349 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineAtYPosition(int y
) const
2351 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2354 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2355 // wxASSERT (child != NULL);
2359 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2362 wxRichTextLine
* line
= node2
->GetData();
2364 wxRect
rect(line
->GetRect());
2366 if (y
<= rect
.GetBottom())
2369 node2
= node2
->GetNext();
2373 node
= node
->GetNext();
2377 int lineCount
= GetLineCount();
2379 return GetLineForVisibleLineNumber(lineCount
-1);
2384 /// Get the number of visible lines
2385 int wxRichTextParagraphLayoutBox::GetLineCount() const
2389 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2392 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2393 // wxASSERT (child != NULL);
2396 count
+= child
->GetLines().GetCount();
2398 node
= node
->GetNext();
2404 /// Get the paragraph for a given line
2405 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphForLine(wxRichTextLine
* line
) const
2407 return GetParagraphAtPosition(line
->GetAbsoluteRange().GetStart());
2410 /// Get the line size at the given position
2411 wxSize
wxRichTextParagraphLayoutBox::GetLineSizeAtPosition(long pos
, bool caretPosition
) const
2413 wxRichTextLine
* line
= GetLineAtPosition(pos
, caretPosition
);
2416 return line
->GetSize();
2419 return wxSize(0, 0);
2423 /// Convenience function to add a paragraph of text
2424 wxRichTextRange
wxRichTextParagraphLayoutBox::AddParagraph(const wxString
& text
, wxRichTextAttr
* paraStyle
)
2426 // Don't use the base style, just the default style, and the base style will
2427 // be combined at display time.
2428 // Divide into paragraph and character styles.
2430 wxRichTextAttr defaultCharStyle
;
2431 wxRichTextAttr defaultParaStyle
;
2433 // If the default style is a named paragraph style, don't apply any character formatting
2434 // to the initial text string.
2435 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2437 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2439 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2442 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2444 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2445 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2447 wxRichTextParagraph
* para
= new wxRichTextParagraph(text
, this, pStyle
, cStyle
);
2448 para
->GetAttributes().GetTextBoxAttr().Reset();
2454 return para
->GetRange();
2457 /// Adds multiple paragraphs, based on newlines.
2458 wxRichTextRange
wxRichTextParagraphLayoutBox::AddParagraphs(const wxString
& text
, wxRichTextAttr
* paraStyle
)
2460 // Don't use the base style, just the default style, and the base style will
2461 // be combined at display time.
2462 // Divide into paragraph and character styles.
2464 wxRichTextAttr defaultCharStyle
;
2465 wxRichTextAttr defaultParaStyle
;
2467 // If the default style is a named paragraph style, don't apply any character formatting
2468 // to the initial text string.
2469 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2471 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2473 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2476 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2478 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2479 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2481 wxRichTextParagraph
* firstPara
= NULL
;
2482 wxRichTextParagraph
* lastPara
= NULL
;
2484 wxRichTextRange
range(-1, -1);
2487 size_t len
= text
.length();
2489 wxRichTextParagraph
* para
= new wxRichTextParagraph(wxEmptyString
, this, pStyle
, cStyle
);
2490 para
->GetAttributes().GetTextBoxAttr().Reset();
2499 wxChar ch
= text
[i
];
2500 if (ch
== wxT('\n') || ch
== wxT('\r'))
2504 wxRichTextPlainText
* plainText
= (wxRichTextPlainText
*) para
->GetChildren().GetFirst()->GetData();
2505 plainText
->SetText(line
);
2507 para
= new wxRichTextParagraph(wxEmptyString
, this, pStyle
, cStyle
);
2508 para
->GetAttributes().GetTextBoxAttr().Reset();
2513 line
= wxEmptyString
;
2524 wxRichTextPlainText
* plainText
= (wxRichTextPlainText
*) para
->GetChildren().GetFirst()->GetData();
2525 plainText
->SetText(line
);
2530 return wxRichTextRange(firstPara
->GetRange().GetStart(), lastPara
->GetRange().GetEnd());
2533 /// Convenience function to add an image
2534 wxRichTextRange
wxRichTextParagraphLayoutBox::AddImage(const wxImage
& image
, wxRichTextAttr
* paraStyle
)
2536 // Don't use the base style, just the default style, and the base style will
2537 // be combined at display time.
2538 // Divide into paragraph and character styles.
2540 wxRichTextAttr defaultCharStyle
;
2541 wxRichTextAttr defaultParaStyle
;
2543 // If the default style is a named paragraph style, don't apply any character formatting
2544 // to the initial text string.
2545 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2547 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2549 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2552 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2554 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2555 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2557 wxRichTextParagraph
* para
= new wxRichTextParagraph(this, pStyle
);
2558 para
->GetAttributes().GetTextBoxAttr().Reset();
2560 para
->AppendChild(new wxRichTextImage(image
, this, cStyle
));
2564 return para
->GetRange();
2568 /// Insert fragment into this box at the given position. If partialParagraph is true,
2569 /// it is assumed that the last (or only) paragraph is just a piece of data with no paragraph
2572 bool wxRichTextParagraphLayoutBox::InsertFragment(long position
, wxRichTextParagraphLayoutBox
& fragment
)
2574 // First, find the first paragraph whose starting position is within the range.
2575 wxRichTextParagraph
* para
= GetParagraphAtPosition(position
);
2578 wxRichTextAttr originalAttr
= para
->GetAttributes();
2580 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(para
);
2582 // Now split at this position, returning the object to insert the new
2583 // ones in front of.
2584 wxRichTextObject
* nextObject
= para
->SplitAt(position
);
2586 // Special case: partial paragraph, just one paragraph. Might be a small amount of
2587 // text, for example, so let's optimize.
2589 if (fragment
.GetPartialParagraph() && fragment
.GetChildren().GetCount() == 1)
2591 // Add the first para to this para...
2592 wxRichTextObjectList::compatibility_iterator firstParaNode
= fragment
.GetChildren().GetFirst();
2596 // Iterate through the fragment paragraph inserting the content into this paragraph.
2597 wxRichTextParagraph
* firstPara
= wxDynamicCast(firstParaNode
->GetData(), wxRichTextParagraph
);
2598 wxASSERT (firstPara
!= NULL
);
2600 wxRichTextObjectList::compatibility_iterator objectNode
= firstPara
->GetChildren().GetFirst();
2603 wxRichTextObject
* newObj
= objectNode
->GetData()->Clone();
2608 para
->AppendChild(newObj
);
2612 // Insert before nextObject
2613 para
->InsertChild(newObj
, nextObject
);
2616 objectNode
= objectNode
->GetNext();
2623 // Procedure for inserting a fragment consisting of a number of
2626 // 1. Remove and save the content that's after the insertion point, for adding
2627 // back once we've added the fragment.
2628 // 2. Add the content from the first fragment paragraph to the current
2630 // 3. Add remaining fragment paragraphs after the current paragraph.
2631 // 4. Add back the saved content from the first paragraph. If partialParagraph
2632 // is true, add it to the last paragraph added and not a new one.
2634 // 1. Remove and save objects after split point.
2635 wxList savedObjects
;
2637 para
->MoveToList(nextObject
, savedObjects
);
2639 // 2. Add the content from the 1st fragment paragraph.
2640 wxRichTextObjectList::compatibility_iterator firstParaNode
= fragment
.GetChildren().GetFirst();
2644 wxRichTextParagraph
* firstPara
= wxDynamicCast(firstParaNode
->GetData(), wxRichTextParagraph
);
2645 wxASSERT(firstPara
!= NULL
);
2647 if (!(fragment
.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
))
2648 para
->SetAttributes(firstPara
->GetAttributes());
2650 // Save empty paragraph attributes for appending later
2651 // These are character attributes deliberately set for a new paragraph. Without this,
2652 // we couldn't pass default attributes when appending a new paragraph.
2653 wxRichTextAttr emptyParagraphAttributes
;
2655 wxRichTextObjectList::compatibility_iterator objectNode
= firstPara
->GetChildren().GetFirst();
2657 if (objectNode
&& firstPara
->GetChildren().GetCount() == 1 && objectNode
->GetData()->IsEmpty())
2658 emptyParagraphAttributes
= objectNode
->GetData()->GetAttributes();
2662 wxRichTextObject
* newObj
= objectNode
->GetData()->Clone();
2665 para
->AppendChild(newObj
);
2667 objectNode
= objectNode
->GetNext();
2670 // 3. Add remaining fragment paragraphs after the current paragraph.
2671 wxRichTextObjectList::compatibility_iterator nextParagraphNode
= node
->GetNext();
2672 wxRichTextObject
* nextParagraph
= NULL
;
2673 if (nextParagraphNode
)
2674 nextParagraph
= nextParagraphNode
->GetData();
2676 wxRichTextObjectList::compatibility_iterator i
= fragment
.GetChildren().GetFirst()->GetNext();
2677 wxRichTextParagraph
* finalPara
= para
;
2679 bool needExtraPara
= (!i
|| !fragment
.GetPartialParagraph());
2681 // If there was only one paragraph, we need to insert a new one.
2684 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2685 wxASSERT( para
!= NULL
);
2687 finalPara
= (wxRichTextParagraph
*) para
->Clone();
2690 InsertChild(finalPara
, nextParagraph
);
2692 AppendChild(finalPara
);
2697 // If there was only one paragraph, or we have full paragraphs in our fragment,
2698 // we need to insert a new one.
2701 finalPara
= new wxRichTextParagraph
;
2704 InsertChild(finalPara
, nextParagraph
);
2706 AppendChild(finalPara
);
2709 // 4. Add back the remaining content.
2713 finalPara
->MoveFromList(savedObjects
);
2715 // Ensure there's at least one object
2716 if (finalPara
->GetChildCount() == 0)
2718 wxRichTextPlainText
* text
= new wxRichTextPlainText(wxEmptyString
);
2719 text
->SetAttributes(emptyParagraphAttributes
);
2721 finalPara
->AppendChild(text
);
2725 if ((fragment
.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
) && firstPara
)
2726 finalPara
->SetAttributes(firstPara
->GetAttributes());
2727 else if (finalPara
&& finalPara
!= para
)
2728 finalPara
->SetAttributes(originalAttr
);
2736 wxRichTextObjectList::compatibility_iterator i
= fragment
.GetChildren().GetFirst();
2739 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2740 wxASSERT( para
!= NULL
);
2742 AppendChild(para
->Clone());
2751 /// Make a copy of the fragment corresponding to the given range, putting it in 'fragment'.
2752 /// If there was an incomplete paragraph at the end, partialParagraph is set to true.
2753 bool wxRichTextParagraphLayoutBox::CopyFragment(const wxRichTextRange
& range
, wxRichTextParagraphLayoutBox
& fragment
)
2755 wxRichTextObjectList::compatibility_iterator i
= GetChildren().GetFirst();
2758 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2759 wxASSERT( para
!= NULL
);
2761 if (!para
->GetRange().IsOutside(range
))
2763 fragment
.AppendChild(para
->Clone());
2768 // Now top and tail the first and last paragraphs in our new fragment (which might be the same).
2769 if (!fragment
.IsEmpty())
2771 wxRichTextParagraph
* firstPara
= wxDynamicCast(fragment
.GetChildren().GetFirst()->GetData(), wxRichTextParagraph
);
2772 wxASSERT( firstPara
!= NULL
);
2774 wxRichTextParagraph
* lastPara
= wxDynamicCast(fragment
.GetChildren().GetLast()->GetData(), wxRichTextParagraph
);
2775 wxASSERT( lastPara
!= NULL
);
2777 if (!firstPara
|| !lastPara
)
2780 bool isFragment
= (range
.GetEnd() < lastPara
->GetRange().GetEnd());
2782 long firstPos
= firstPara
->GetRange().GetStart();
2784 // Adjust for renumbering from zero
2785 wxRichTextRange
topTailRange(range
.GetStart() - firstPos
, range
.GetEnd() - firstPos
);
2788 fragment
.CalculateRange(0, end
);
2790 // Chop off the start of the paragraph
2791 if (topTailRange
.GetStart() > 0)
2793 wxRichTextRange
r(0, topTailRange
.GetStart()-1);
2794 firstPara
->DeleteRange(r
);
2796 // Make sure the numbering is correct
2797 fragment
.CalculateRange(0, end
);
2799 // Now, we've deleted some positions, so adjust the range
2801 topTailRange
.SetStart(range
.GetLength());
2802 topTailRange
.SetEnd(fragment
.GetOwnRange().GetEnd());
2806 topTailRange
.SetStart(range
.GetLength());
2807 topTailRange
.SetEnd(fragment
.GetOwnRange().GetEnd());
2810 if (topTailRange
.GetStart() < lastPara
->GetRange().GetEnd())
2812 lastPara
->DeleteRange(topTailRange
);
2814 // Make sure the numbering is correct
2816 fragment
.CalculateRange(0, end
);
2818 // We only have part of a paragraph at the end
2819 fragment
.SetPartialParagraph(true);
2823 // We have a partial paragraph (don't save last new paragraph marker)
2824 // or complete paragraph
2825 fragment
.SetPartialParagraph(isFragment
);
2832 /// Given a position, get the number of the visible line (potentially many to a paragraph),
2833 /// starting from zero at the start of the buffer.
2834 long wxRichTextParagraphLayoutBox::GetVisibleLineNumber(long pos
, bool caretPosition
, bool startOfLine
) const
2841 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2844 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2845 // wxASSERT( child != NULL );
2849 if (child
->GetRange().Contains(pos
))
2851 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2854 wxRichTextLine
* line
= node2
->GetData();
2855 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
2857 if (lineRange
.Contains(pos
) || pos
== lineRange
.GetStart())
2859 // If the caret is displayed at the end of the previous wrapped line,
2860 // we want to return the line it's _displayed_ at (not the actual line
2861 // containing the position).
2862 if (lineRange
.GetStart() == pos
&& !startOfLine
&& child
->GetRange().GetStart() != pos
)
2863 return lineCount
- 1;
2870 node2
= node2
->GetNext();
2872 // If we didn't find it in the lines, it must be
2873 // the last position of the paragraph. So return the last line.
2877 lineCount
+= child
->GetLines().GetCount();
2880 node
= node
->GetNext();
2887 /// Given a line number, get the corresponding wxRichTextLine object.
2888 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineForVisibleLineNumber(long lineNumber
) const
2892 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2895 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2896 // wxASSERT(child != NULL);
2900 if (lineNumber
< (int) (child
->GetLines().GetCount() + lineCount
))
2902 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2905 wxRichTextLine
* line
= node2
->GetData();
2907 if (lineCount
== lineNumber
)
2912 node2
= node2
->GetNext();
2916 lineCount
+= child
->GetLines().GetCount();
2919 node
= node
->GetNext();
2926 /// Delete range from layout.
2927 bool wxRichTextParagraphLayoutBox::DeleteRange(const wxRichTextRange
& range
)
2929 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2931 wxRichTextParagraph
* firstPara
= NULL
;
2934 wxRichTextParagraph
* obj
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2935 // wxASSERT (obj != NULL);
2937 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
2941 // Delete the range in each paragraph
2943 if (!obj
->GetRange().IsOutside(range
))
2945 // Deletes the content of this object within the given range
2946 obj
->DeleteRange(range
);
2948 wxRichTextRange thisRange
= obj
->GetRange();
2949 wxRichTextAttr thisAttr
= obj
->GetAttributes();
2951 // If the whole paragraph is within the range to delete,
2952 // delete the whole thing.
2953 if (range
.GetStart() <= thisRange
.GetStart() && range
.GetEnd() >= thisRange
.GetEnd())
2955 // Delete the whole object
2956 RemoveChild(obj
, true);
2959 else if (!firstPara
)
2962 // If the range includes the paragraph end, we need to join this
2963 // and the next paragraph.
2964 if (range
.GetEnd() <= thisRange
.GetEnd())
2966 // We need to move the objects from the next paragraph
2967 // to this paragraph
2969 wxRichTextParagraph
* nextParagraph
= NULL
;
2970 if ((range
.GetEnd() < thisRange
.GetEnd()) && obj
)
2971 nextParagraph
= obj
;
2974 // We're ending at the end of the paragraph, so merge the _next_ paragraph.
2976 nextParagraph
= wxDynamicCast(next
->GetData(), wxRichTextParagraph
);
2979 bool applyFinalParagraphStyle
= firstPara
&& nextParagraph
&& nextParagraph
!= firstPara
;
2981 wxRichTextAttr nextParaAttr
;
2982 if (applyFinalParagraphStyle
)
2984 // Special case when deleting the end of a paragraph - use _this_ paragraph's style,
2985 // not the next one.
2986 if (range
.GetStart() == range
.GetEnd() && range
.GetStart() == thisRange
.GetEnd())
2987 nextParaAttr
= thisAttr
;
2989 nextParaAttr
= nextParagraph
->GetAttributes();
2992 if (firstPara
&& nextParagraph
&& firstPara
!= nextParagraph
)
2994 // Move the objects to the previous para
2995 wxRichTextObjectList::compatibility_iterator node1
= nextParagraph
->GetChildren().GetFirst();
2999 wxRichTextObject
* obj1
= node1
->GetData();
3001 firstPara
->AppendChild(obj1
);
3003 wxRichTextObjectList::compatibility_iterator next1
= node1
->GetNext();
3004 nextParagraph
->GetChildren().Erase(node1
);
3009 // Delete the paragraph
3010 RemoveChild(nextParagraph
, true);
3013 // Avoid empty paragraphs
3014 if (firstPara
&& firstPara
->GetChildren().GetCount() == 0)
3016 wxRichTextPlainText
* text
= new wxRichTextPlainText(wxEmptyString
);
3017 firstPara
->AppendChild(text
);
3020 if (applyFinalParagraphStyle
)
3021 firstPara
->SetAttributes(nextParaAttr
);
3034 /// Get any text in this object for the given range
3035 wxString
wxRichTextParagraphLayoutBox::GetTextForRange(const wxRichTextRange
& range
) const
3039 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3042 wxRichTextObject
* child
= node
->GetData();
3043 if (!child
->GetRange().IsOutside(range
))
3045 wxRichTextRange childRange
= range
;
3046 childRange
.LimitTo(child
->GetRange());
3048 wxString childText
= child
->GetTextForRange(childRange
);
3052 if ((childRange
.GetEnd() == child
->GetRange().GetEnd()) && node
->GetNext())
3057 node
= node
->GetNext();
3063 /// Get all the text
3064 wxString
wxRichTextParagraphLayoutBox::GetText() const
3066 return GetTextForRange(GetOwnRange());
3069 /// Get the paragraph by number
3070 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphAtLine(long paragraphNumber
) const
3072 if ((size_t) paragraphNumber
>= GetChildCount())
3075 return (wxRichTextParagraph
*) GetChild((size_t) paragraphNumber
);
3078 /// Get the length of the paragraph
3079 int wxRichTextParagraphLayoutBox::GetParagraphLength(long paragraphNumber
) const
3081 wxRichTextParagraph
* para
= GetParagraphAtLine(paragraphNumber
);
3083 return para
->GetRange().GetLength() - 1; // don't include newline
3088 /// Get the text of the paragraph
3089 wxString
wxRichTextParagraphLayoutBox::GetParagraphText(long paragraphNumber
) const
3091 wxRichTextParagraph
* para
= GetParagraphAtLine(paragraphNumber
);
3093 return para
->GetTextForRange(para
->GetRange());
3095 return wxEmptyString
;
3098 /// Convert zero-based line column and paragraph number to a position.
3099 long wxRichTextParagraphLayoutBox::XYToPosition(long x
, long y
) const
3101 wxRichTextParagraph
* para
= GetParagraphAtLine(y
);
3104 return para
->GetRange().GetStart() + x
;
3110 /// Convert zero-based position to line column and paragraph number
3111 bool wxRichTextParagraphLayoutBox::PositionToXY(long pos
, long* x
, long* y
) const
3113 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
);
3117 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3120 wxRichTextObject
* child
= node
->GetData();
3124 node
= node
->GetNext();
3128 *x
= pos
- para
->GetRange().GetStart();
3136 /// Get the leaf object in a paragraph at this position.
3137 /// Given a line number, get the corresponding wxRichTextLine object.
3138 wxRichTextObject
* wxRichTextParagraphLayoutBox::GetLeafObjectAtPosition(long position
) const
3140 wxRichTextParagraph
* para
= GetParagraphAtPosition(position
);
3143 wxRichTextObjectList::compatibility_iterator node
= para
->GetChildren().GetFirst();
3147 wxRichTextObject
* child
= node
->GetData();
3148 if (child
->GetRange().Contains(position
))
3151 node
= node
->GetNext();
3153 if (position
== para
->GetRange().GetEnd() && para
->GetChildCount() > 0)
3154 return para
->GetChildren().GetLast()->GetData();
3159 /// Set character or paragraph text attributes: apply character styles only to immediate text nodes
3160 bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange
& range
, const wxRichTextAttr
& style
, int flags
)
3162 bool characterStyle
= false;
3163 bool paragraphStyle
= false;
3165 if (style
.IsCharacterStyle())
3166 characterStyle
= true;
3167 if (style
.IsParagraphStyle())
3168 paragraphStyle
= true;
3170 wxRichTextBuffer
* buffer
= GetBuffer();
3172 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
3173 bool applyMinimal
= ((flags
& wxRICHTEXT_SETSTYLE_OPTIMIZE
) != 0);
3174 bool parasOnly
= ((flags
& wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY
) != 0);
3175 bool charactersOnly
= ((flags
& wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY
) != 0);
3176 bool resetExistingStyle
= ((flags
& wxRICHTEXT_SETSTYLE_RESET
) != 0);
3177 bool removeStyle
= ((flags
& wxRICHTEXT_SETSTYLE_REMOVE
) != 0);
3179 // Apply paragraph style first, if any
3180 wxRichTextAttr
wholeStyle(style
);
3182 if (!removeStyle
&& wholeStyle
.HasParagraphStyleName() && buffer
->GetStyleSheet())
3184 wxRichTextParagraphStyleDefinition
* def
= buffer
->GetStyleSheet()->FindParagraphStyle(wholeStyle
.GetParagraphStyleName());
3186 wxRichTextApplyStyle(wholeStyle
, def
->GetStyleMergedWithBase(buffer
->GetStyleSheet()));
3189 // Limit the attributes to be set to the content to only character attributes.
3190 wxRichTextAttr
characterAttributes(wholeStyle
);
3191 characterAttributes
.SetFlags(characterAttributes
.GetFlags() & (wxTEXT_ATTR_CHARACTER
));
3193 if (!removeStyle
&& characterAttributes
.HasCharacterStyleName() && buffer
->GetStyleSheet())
3195 wxRichTextCharacterStyleDefinition
* def
= buffer
->GetStyleSheet()->FindCharacterStyle(characterAttributes
.GetCharacterStyleName());
3197 wxRichTextApplyStyle(characterAttributes
, def
->GetStyleMergedWithBase(buffer
->GetStyleSheet()));
3200 // If we are associated with a control, make undoable; otherwise, apply immediately
3203 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3205 wxRichTextAction
* action
= NULL
;
3207 if (haveControl
&& withUndo
)
3209 action
= new wxRichTextAction(NULL
, _("Change Style"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
3210 action
->SetRange(range
);
3211 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3214 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3217 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3218 // wxASSERT (para != NULL);
3220 if (para
&& para
->GetChildCount() > 0)
3222 // Stop searching if we're beyond the range of interest
3223 if (para
->GetRange().GetStart() > range
.GetEnd())
3226 if (!para
->GetRange().IsOutside(range
))
3228 // We'll be using a copy of the paragraph to make style changes,
3229 // not updating the buffer directly.
3230 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3232 if (haveControl
&& withUndo
)
3234 newPara
= new wxRichTextParagraph(*para
);
3235 action
->GetNewParagraphs().AppendChild(newPara
);
3237 // Also store the old ones for Undo
3238 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3243 // If we're specifying paragraphs only, then we really mean character formatting
3244 // to be included in the paragraph style
3245 if ((paragraphStyle
|| parasOnly
) && !charactersOnly
)
3249 // Removes the given style from the paragraph
3250 wxRichTextRemoveStyle(newPara
->GetAttributes(), style
);
3252 else if (resetExistingStyle
)
3253 newPara
->GetAttributes() = wholeStyle
;
3258 // Only apply attributes that will make a difference to the combined
3259 // style as seen on the display
3260 wxRichTextAttr
combinedAttr(para
->GetCombinedAttributes(true));
3261 wxRichTextApplyStyle(newPara
->GetAttributes(), wholeStyle
, & combinedAttr
);
3264 wxRichTextApplyStyle(newPara
->GetAttributes(), wholeStyle
);
3268 // When applying paragraph styles dynamically, don't change the text objects' attributes
3269 // since they will computed as needed. Only apply the character styling if it's _only_
3270 // character styling. This policy is subject to change and might be put under user control.
3272 // Hm. we might well be applying a mix of paragraph and character styles, in which
3273 // case we _do_ want to apply character styles regardless of what para styles are set.
3274 // But if we're applying a paragraph style, which has some character attributes, but
3275 // we only want the paragraphs to hold this character style, then we _don't_ want to
3276 // apply the character style. So we need to be able to choose.
3278 if (!parasOnly
&& (characterStyle
|charactersOnly
) && range
.GetStart() != newPara
->GetRange().GetEnd())
3280 wxRichTextRange
childRange(range
);
3281 childRange
.LimitTo(newPara
->GetRange());
3283 // Find the starting position and if necessary split it so
3284 // we can start applying a different style.
3285 // TODO: check that the style actually changes or is different
3286 // from style outside of range
3287 wxRichTextObject
* firstObject
wxDUMMY_INITIALIZE(NULL
);
3288 wxRichTextObject
* lastObject
wxDUMMY_INITIALIZE(NULL
);
3290 if (childRange
.GetStart() == newPara
->GetRange().GetStart())
3291 firstObject
= newPara
->GetChildren().GetFirst()->GetData();
3293 firstObject
= newPara
->SplitAt(range
.GetStart());
3295 // Increment by 1 because we're apply the style one _after_ the split point
3296 long splitPoint
= childRange
.GetEnd();
3297 if (splitPoint
!= newPara
->GetRange().GetEnd())
3301 if (splitPoint
== newPara
->GetRange().GetEnd())
3302 lastObject
= newPara
->GetChildren().GetLast()->GetData();
3304 // lastObject is set as a side-effect of splitting. It's
3305 // returned as the object before the new object.
3306 (void) newPara
->SplitAt(splitPoint
, & lastObject
);
3308 wxASSERT(firstObject
!= NULL
);
3309 wxASSERT(lastObject
!= NULL
);
3311 if (!firstObject
|| !lastObject
)
3314 wxRichTextObjectList::compatibility_iterator firstNode
= newPara
->GetChildren().Find(firstObject
);
3315 wxRichTextObjectList::compatibility_iterator lastNode
= newPara
->GetChildren().Find(lastObject
);
3317 wxASSERT(firstNode
);
3320 wxRichTextObjectList::compatibility_iterator node2
= firstNode
;
3324 wxRichTextObject
* child
= node2
->GetData();
3328 // Removes the given style from the paragraph
3329 wxRichTextRemoveStyle(child
->GetAttributes(), style
);
3331 else if (resetExistingStyle
)
3333 // Preserve the URL as it's not really a formatting style but a property of the object
3335 if (child
->GetAttributes().HasURL() && !characterAttributes
.HasURL())
3336 url
= child
->GetAttributes().GetURL();
3338 child
->GetAttributes() = characterAttributes
;
3341 child
->GetAttributes().SetURL(url
);
3347 // Only apply attributes that will make a difference to the combined
3348 // style as seen on the display
3349 wxRichTextAttr
combinedAttr(newPara
->GetCombinedAttributes(child
->GetAttributes(), true));
3350 wxRichTextApplyStyle(child
->GetAttributes(), characterAttributes
, & combinedAttr
);
3353 wxRichTextApplyStyle(child
->GetAttributes(), characterAttributes
);
3356 if (node2
== lastNode
)
3359 node2
= node2
->GetNext();
3365 node
= node
->GetNext();
3368 // Do action, or delay it until end of batch.
3369 if (haveControl
&& withUndo
)
3370 buffer
->SubmitAction(action
);
3375 // Just change the attributes for this single object.
3376 void wxRichTextParagraphLayoutBox::SetStyle(wxRichTextObject
* obj
, const wxRichTextAttr
& textAttr
, int flags
)
3378 wxRichTextBuffer
* buffer
= GetBuffer();
3379 bool withUndo
= flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
;
3380 bool resetExistingStyle
= ((flags
& wxRICHTEXT_SETSTYLE_RESET
) != 0);
3381 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3383 wxRichTextAction
*action
= NULL
;
3384 wxRichTextAttr newAttr
= obj
->GetAttributes();
3385 if (resetExistingStyle
)
3388 newAttr
.Apply(textAttr
);
3390 if (haveControl
&& withUndo
)
3392 action
= new wxRichTextAction(NULL
, _("Change Object Style"), wxRICHTEXT_CHANGE_ATTRIBUTES
, buffer
, obj
->GetContainer(), buffer
->GetRichTextCtrl());
3393 action
->SetRange(obj
->GetRange().FromInternal());
3394 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3395 action
->MakeObject(obj
);
3397 action
->GetAttributes() = newAttr
;
3400 obj
->GetAttributes() = newAttr
;
3402 if (haveControl
&& withUndo
)
3403 buffer
->SubmitAction(action
);
3406 /// Get the text attributes for this position.
3407 bool wxRichTextParagraphLayoutBox::GetStyle(long position
, wxRichTextAttr
& style
)
3409 return DoGetStyle(position
, style
, true);
3412 bool wxRichTextParagraphLayoutBox::GetUncombinedStyle(long position
, wxRichTextAttr
& style
)
3414 return DoGetStyle(position
, style
, false);
3417 /// Implementation helper for GetStyle. If combineStyles is true, combine base, paragraph and
3418 /// context attributes.
3419 bool wxRichTextParagraphLayoutBox::DoGetStyle(long position
, wxRichTextAttr
& style
, bool combineStyles
)
3421 wxRichTextObject
* obj
wxDUMMY_INITIALIZE(NULL
);
3423 if (style
.IsParagraphStyle())
3425 obj
= GetParagraphAtPosition(position
);
3430 // Start with the base style
3431 style
= GetAttributes();
3432 style
.GetTextBoxAttr().Reset();
3434 // Apply the paragraph style
3435 wxRichTextApplyStyle(style
, obj
->GetAttributes());
3438 style
= obj
->GetAttributes();
3445 obj
= GetLeafObjectAtPosition(position
);
3450 wxRichTextParagraph
* para
= wxDynamicCast(obj
->GetParent(), wxRichTextParagraph
);
3451 style
= para
? para
->GetCombinedAttributes(obj
->GetAttributes()) : obj
->GetAttributes();
3454 style
= obj
->GetAttributes();
3462 static bool wxHasStyle(long flags
, long style
)
3464 return (flags
& style
) != 0;
3467 /// Combines 'style' with 'currentStyle' for the purpose of summarising the attributes of a range of
3469 bool wxRichTextParagraphLayoutBox::CollectStyle(wxRichTextAttr
& currentStyle
, const wxRichTextAttr
& style
, wxRichTextAttr
& clashingAttr
, wxRichTextAttr
& absentAttr
)
3471 currentStyle
.CollectCommonAttributes(style
, clashingAttr
, absentAttr
);
3476 /// Get the combined style for a range - if any attribute is different within the range,
3477 /// that attribute is not present within the flags.
3478 /// *** Note that this is not recursive, and so assumes that content inside a paragraph is not itself
3480 bool wxRichTextParagraphLayoutBox::GetStyleForRange(const wxRichTextRange
& range
, wxRichTextAttr
& style
)
3482 style
= wxRichTextAttr();
3484 wxRichTextAttr clashingAttrPara
, clashingAttrChar
;
3485 wxRichTextAttr absentAttrPara
, absentAttrChar
;
3487 wxRichTextObjectList::compatibility_iterator node
= GetChildren().GetFirst();
3490 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3491 if (para
&& !(para
->GetRange().GetStart() > range
.GetEnd() || para
->GetRange().GetEnd() < range
.GetStart()))
3493 if (para
->GetChildren().GetCount() == 0)
3495 wxRichTextAttr paraStyle
= para
->GetCombinedAttributes(true /* use box attributes */);
3497 CollectStyle(style
, paraStyle
, clashingAttrPara
, absentAttrPara
);
3501 wxRichTextRange
paraRange(para
->GetRange());
3502 paraRange
.LimitTo(range
);
3504 // First collect paragraph attributes only
3505 wxRichTextAttr paraStyle
= para
->GetCombinedAttributes();
3506 paraStyle
.SetFlags(paraStyle
.GetFlags() & wxTEXT_ATTR_PARAGRAPH
);
3507 CollectStyle(style
, paraStyle
, clashingAttrPara
, absentAttrPara
);
3509 wxRichTextObjectList::compatibility_iterator childNode
= para
->GetChildren().GetFirst();
3513 wxRichTextObject
* child
= childNode
->GetData();
3514 if (!(child
->GetRange().GetStart() > range
.GetEnd() || child
->GetRange().GetEnd() < range
.GetStart()))
3516 wxRichTextAttr childStyle
= para
->GetCombinedAttributes(child
->GetAttributes(), true /* include box attributes */);
3518 // Now collect character attributes only
3519 childStyle
.SetFlags(childStyle
.GetFlags() & wxTEXT_ATTR_CHARACTER
);
3521 CollectStyle(style
, childStyle
, clashingAttrChar
, absentAttrChar
);
3524 childNode
= childNode
->GetNext();
3528 node
= node
->GetNext();
3533 /// Set default style
3534 bool wxRichTextParagraphLayoutBox::SetDefaultStyle(const wxRichTextAttr
& style
)
3536 m_defaultAttributes
= style
;
3540 /// Test if this whole range has character attributes of the specified kind. If any
3541 /// of the attributes are different within the range, the test fails. You
3542 /// can use this to implement, for example, bold button updating. style must have
3543 /// flags indicating which attributes are of interest.
3544 bool wxRichTextParagraphLayoutBox::HasCharacterAttributes(const wxRichTextRange
& range
, const wxRichTextAttr
& style
) const
3547 int matchingCount
= 0;
3549 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3552 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3553 // wxASSERT (para != NULL);
3557 // Stop searching if we're beyond the range of interest
3558 if (para
->GetRange().GetStart() > range
.GetEnd())
3559 return foundCount
== matchingCount
&& foundCount
!= 0;
3561 if (!para
->GetRange().IsOutside(range
))
3563 wxRichTextObjectList::compatibility_iterator node2
= para
->GetChildren().GetFirst();
3567 wxRichTextObject
* child
= node2
->GetData();
3568 // Allow for empty string if no buffer
3569 wxRichTextRange childRange
= child
->GetRange();
3570 if (childRange
.GetLength() == 0 && GetRange().GetLength() == 1)
3571 childRange
.SetEnd(childRange
.GetEnd()+1);
3573 if (!childRange
.IsOutside(range
) && wxDynamicCast(child
, wxRichTextPlainText
))
3576 wxRichTextAttr textAttr
= para
->GetCombinedAttributes(child
->GetAttributes());
3578 if (textAttr
.EqPartial(style
, false /* strong test - attributes must be valid in both objects */))
3582 node2
= node2
->GetNext();
3587 node
= node
->GetNext();
3590 return foundCount
== matchingCount
&& foundCount
!= 0;
3593 /// Test if this whole range has paragraph attributes of the specified kind. If any
3594 /// of the attributes are different within the range, the test fails. You
3595 /// can use this to implement, for example, centering button updating. style must have
3596 /// flags indicating which attributes are of interest.
3597 bool wxRichTextParagraphLayoutBox::HasParagraphAttributes(const wxRichTextRange
& range
, const wxRichTextAttr
& style
) const
3600 int matchingCount
= 0;
3602 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3605 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3606 // wxASSERT (para != NULL);
3610 // Stop searching if we're beyond the range of interest
3611 if (para
->GetRange().GetStart() > range
.GetEnd())
3612 return foundCount
== matchingCount
&& foundCount
!= 0;
3614 if (!para
->GetRange().IsOutside(range
))
3616 wxRichTextAttr textAttr
= GetAttributes();
3617 // Apply the paragraph style
3618 wxRichTextApplyStyle(textAttr
, para
->GetAttributes());
3621 if (textAttr
.EqPartial(style
, false /* strong test */))
3626 node
= node
->GetNext();
3628 return foundCount
== matchingCount
&& foundCount
!= 0;
3631 void wxRichTextParagraphLayoutBox::PrepareContent(wxRichTextParagraphLayoutBox
& container
)
3633 wxRichTextBuffer
* buffer
= GetBuffer();
3634 if (buffer
&& buffer
->GetRichTextCtrl())
3635 buffer
->GetRichTextCtrl()->PrepareContent(container
);
3638 /// Set character or paragraph properties
3639 bool wxRichTextParagraphLayoutBox::SetProperties(const wxRichTextRange
& range
, const wxRichTextProperties
& properties
, int flags
)
3641 wxRichTextBuffer
* buffer
= GetBuffer();
3643 bool withUndo
= ((flags
& wxRICHTEXT_SETPROPERTIES_WITH_UNDO
) != 0);
3644 bool parasOnly
= ((flags
& wxRICHTEXT_SETPROPERTIES_PARAGRAPHS_ONLY
) != 0);
3645 bool charactersOnly
= ((flags
& wxRICHTEXT_SETPROPERTIES_CHARACTERS_ONLY
) != 0);
3646 bool resetExistingProperties
= ((flags
& wxRICHTEXT_SETPROPERTIES_RESET
) != 0);
3647 bool removeProperties
= ((flags
& wxRICHTEXT_SETPROPERTIES_REMOVE
) != 0);
3649 // If we are associated with a control, make undoable; otherwise, apply immediately
3652 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3654 wxRichTextAction
* action
= NULL
;
3656 if (haveControl
&& withUndo
)
3658 action
= new wxRichTextAction(NULL
, _("Change Properties"), wxRICHTEXT_CHANGE_PROPERTIES
, buffer
, this, buffer
->GetRichTextCtrl());
3659 action
->SetRange(range
);
3660 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3663 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3666 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3667 // wxASSERT (para != NULL);
3669 if (para
&& para
->GetChildCount() > 0)
3671 // Stop searching if we're beyond the range of interest
3672 if (para
->GetRange().GetStart() > range
.GetEnd())
3675 if (!para
->GetRange().IsOutside(range
))
3677 // We'll be using a copy of the paragraph to make style changes,
3678 // not updating the buffer directly.
3679 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3681 if (haveControl
&& withUndo
)
3683 newPara
= new wxRichTextParagraph(*para
);
3684 action
->GetNewParagraphs().AppendChild(newPara
);
3686 // Also store the old ones for Undo
3687 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3694 if (removeProperties
)
3696 // Removes the given style from the paragraph
3698 newPara
->GetProperties().RemoveProperties(properties
);
3700 else if (resetExistingProperties
)
3701 newPara
->GetProperties() = properties
;
3703 newPara
->GetProperties().MergeProperties(properties
);
3706 // When applying paragraph styles dynamically, don't change the text objects' attributes
3707 // since they will computed as needed. Only apply the character styling if it's _only_
3708 // character styling. This policy is subject to change and might be put under user control.
3710 // Hm. we might well be applying a mix of paragraph and character styles, in which
3711 // case we _do_ want to apply character styles regardless of what para styles are set.
3712 // But if we're applying a paragraph style, which has some character attributes, but
3713 // we only want the paragraphs to hold this character style, then we _don't_ want to
3714 // apply the character style. So we need to be able to choose.
3716 if (!parasOnly
&& charactersOnly
&& range
.GetStart() != newPara
->GetRange().GetEnd())
3718 wxRichTextRange
childRange(range
);
3719 childRange
.LimitTo(newPara
->GetRange());
3721 // Find the starting position and if necessary split it so
3722 // we can start applying different properties.
3723 // TODO: check that the properties actually change or are different
3724 // from properties outside of range
3725 wxRichTextObject
* firstObject
wxDUMMY_INITIALIZE(NULL
);
3726 wxRichTextObject
* lastObject
wxDUMMY_INITIALIZE(NULL
);
3728 if (childRange
.GetStart() == newPara
->GetRange().GetStart())
3729 firstObject
= newPara
->GetChildren().GetFirst()->GetData();
3731 firstObject
= newPara
->SplitAt(range
.GetStart());
3733 // Increment by 1 because we're apply the style one _after_ the split point
3734 long splitPoint
= childRange
.GetEnd();
3735 if (splitPoint
!= newPara
->GetRange().GetEnd())
3739 if (splitPoint
== newPara
->GetRange().GetEnd())
3740 lastObject
= newPara
->GetChildren().GetLast()->GetData();
3742 // lastObject is set as a side-effect of splitting. It's
3743 // returned as the object before the new object.
3744 (void) newPara
->SplitAt(splitPoint
, & lastObject
);
3746 wxASSERT(firstObject
!= NULL
);
3747 wxASSERT(lastObject
!= NULL
);
3749 if (!firstObject
|| !lastObject
)
3752 wxRichTextObjectList::compatibility_iterator firstNode
= newPara
->GetChildren().Find(firstObject
);
3753 wxRichTextObjectList::compatibility_iterator lastNode
= newPara
->GetChildren().Find(lastObject
);
3755 wxASSERT(firstNode
);
3758 wxRichTextObjectList::compatibility_iterator node2
= firstNode
;
3762 wxRichTextObject
* child
= node2
->GetData();
3764 if (removeProperties
)
3766 // Removes the given properties from the paragraph
3767 child
->GetProperties().RemoveProperties(properties
);
3769 else if (resetExistingProperties
)
3770 child
->GetProperties() = properties
;
3773 child
->GetProperties().MergeProperties(properties
);
3776 if (node2
== lastNode
)
3779 node2
= node2
->GetNext();
3785 node
= node
->GetNext();
3788 // Do action, or delay it until end of batch.
3789 if (haveControl
&& withUndo
)
3790 buffer
->SubmitAction(action
);
3795 void wxRichTextParagraphLayoutBox::Reset()
3799 wxRichTextBuffer
* buffer
= GetBuffer();
3800 if (buffer
&& buffer
->GetRichTextCtrl())
3802 wxRichTextEvent
event(wxEVT_RICHTEXT_BUFFER_RESET
, buffer
->GetRichTextCtrl()->GetId());
3803 event
.SetEventObject(buffer
->GetRichTextCtrl());
3804 event
.SetContainer(this);
3806 buffer
->SendEvent(event
, true);
3809 AddParagraph(wxEmptyString
);
3811 PrepareContent(*this);
3813 InvalidateHierarchy(wxRICHTEXT_ALL
);
3816 /// Invalidate the buffer. With no argument, invalidates whole buffer.
3817 void wxRichTextParagraphLayoutBox::Invalidate(const wxRichTextRange
& invalidRange
)
3819 wxRichTextCompositeObject::Invalidate(invalidRange
);
3821 DoInvalidate(invalidRange
);
3824 // Do the (in)validation for this object only
3825 void wxRichTextParagraphLayoutBox::DoInvalidate(const wxRichTextRange
& invalidRange
)
3827 if (invalidRange
== wxRICHTEXT_ALL
)
3829 m_invalidRange
= wxRICHTEXT_ALL
;
3831 // Already invalidating everything
3832 else if (m_invalidRange
== wxRICHTEXT_ALL
)
3837 if ((invalidRange
.GetStart() < m_invalidRange
.GetStart()) || m_invalidRange
.GetStart() == -1)
3838 m_invalidRange
.SetStart(invalidRange
.GetStart());
3839 if (invalidRange
.GetEnd() > m_invalidRange
.GetEnd())
3840 m_invalidRange
.SetEnd(invalidRange
.GetEnd());
3844 // Do the (in)validation both up and down the hierarchy
3845 void wxRichTextParagraphLayoutBox::InvalidateHierarchy(const wxRichTextRange
& invalidRange
)
3847 Invalidate(invalidRange
);
3849 if (invalidRange
!= wxRICHTEXT_NONE
)
3851 // Now go up the hierarchy
3852 wxRichTextObject
* thisObj
= this;
3853 wxRichTextObject
* p
= GetParent();
3856 wxRichTextParagraphLayoutBox
* l
= wxDynamicCast(p
, wxRichTextParagraphLayoutBox
);
3858 l
->DoInvalidate(thisObj
->GetRange());
3866 /// Get invalid range, rounding to entire paragraphs if argument is true.
3867 wxRichTextRange
wxRichTextParagraphLayoutBox::GetInvalidRange(bool wholeParagraphs
) const
3869 if (m_invalidRange
== wxRICHTEXT_ALL
|| m_invalidRange
== wxRICHTEXT_NONE
)
3870 return m_invalidRange
;
3872 wxRichTextRange range
= m_invalidRange
;
3874 if (wholeParagraphs
)
3876 wxRichTextParagraph
* para1
= GetParagraphAtPosition(range
.GetStart());
3878 range
.SetStart(para1
->GetRange().GetStart());
3880 // FIXME: be more intelligent about this. Check if we have floating objects
3881 // before the end of the range. But it's not clear how we can in general
3882 // tell where it's safe to stop laying out.
3883 // Anyway, this code is central to efficiency when laying in floating mode.
3884 if (!wxRichTextBuffer::GetFloatingLayoutMode())
3886 wxRichTextParagraph
* para2
= GetParagraphAtPosition(range
.GetEnd());
3888 range
.SetEnd(para2
->GetRange().GetEnd());
3891 // Floating layout means that all children should be laid out,
3892 // because we can't tell how the whole buffer will be affected.
3893 range
.SetEnd(GetOwnRange().GetEnd());
3898 /// Apply the style sheet to the buffer, for example if the styles have changed.
3899 bool wxRichTextParagraphLayoutBox::ApplyStyleSheet(wxRichTextStyleSheet
* styleSheet
)
3901 wxASSERT(styleSheet
!= NULL
);
3907 wxRichTextAttr
attr(GetBasicStyle());
3908 if (GetBasicStyle().HasParagraphStyleName())
3910 wxRichTextParagraphStyleDefinition
* paraDef
= styleSheet
->FindParagraphStyle(GetBasicStyle().GetParagraphStyleName());
3913 attr
.Apply(paraDef
->GetStyleMergedWithBase(styleSheet
));
3914 SetBasicStyle(attr
);
3919 if (GetBasicStyle().HasCharacterStyleName())
3921 wxRichTextCharacterStyleDefinition
* charDef
= styleSheet
->FindCharacterStyle(GetBasicStyle().GetCharacterStyleName());
3924 attr
.Apply(charDef
->GetStyleMergedWithBase(styleSheet
));
3925 SetBasicStyle(attr
);
3930 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3933 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3934 // wxASSERT (para != NULL);
3938 // Combine paragraph and list styles. If there is a list style in the original attributes,
3939 // the current indentation overrides anything else and is used to find the item indentation.
3940 // Also, for applying paragraph styles, consider having 2 modes: (1) we merge with what we have,
3941 // thereby taking into account all user changes, (2) reset the style completely (except for indentation/list
3942 // exception as above).
3943 // Problem: when changing from one list style to another, there's a danger that the level info will get lost.
3944 // So when changing a list style interactively, could retrieve level based on current style, then
3945 // set appropriate indent and apply new style.
3949 if (para
->GetAttributes().HasOutlineLevel())
3950 outline
= para
->GetAttributes().GetOutlineLevel();
3951 if (para
->GetAttributes().HasBulletNumber())
3952 num
= para
->GetAttributes().GetBulletNumber();
3954 if (!para
->GetAttributes().GetParagraphStyleName().IsEmpty() && !para
->GetAttributes().GetListStyleName().IsEmpty())
3956 int currentIndent
= para
->GetAttributes().GetLeftIndent();
3958 wxRichTextParagraphStyleDefinition
* paraDef
= styleSheet
->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
3959 wxRichTextListStyleDefinition
* listDef
= styleSheet
->FindListStyle(para
->GetAttributes().GetListStyleName());
3960 if (paraDef
&& !listDef
)
3962 para
->GetAttributes() = paraDef
->GetStyleMergedWithBase(styleSheet
);
3965 else if (listDef
&& !paraDef
)
3967 // Set overall style defined for the list style definition
3968 para
->GetAttributes() = listDef
->GetStyleMergedWithBase(styleSheet
);
3970 // Apply the style for this level
3971 wxRichTextApplyStyle(para
->GetAttributes(), * listDef
->GetLevelAttributes(listDef
->FindLevelForIndent(currentIndent
)));
3974 else if (listDef
&& paraDef
)
3976 // Combines overall list style, style for level, and paragraph style
3977 para
->GetAttributes() = listDef
->CombineWithParagraphStyle(currentIndent
, paraDef
->GetStyleMergedWithBase(styleSheet
));
3981 else if (para
->GetAttributes().GetParagraphStyleName().IsEmpty() && !para
->GetAttributes().GetListStyleName().IsEmpty())
3983 int currentIndent
= para
->GetAttributes().GetLeftIndent();
3985 wxRichTextListStyleDefinition
* listDef
= styleSheet
->FindListStyle(para
->GetAttributes().GetListStyleName());
3987 // Overall list definition style
3988 para
->GetAttributes() = listDef
->GetStyleMergedWithBase(styleSheet
);
3990 // Style for this level
3991 wxRichTextApplyStyle(para
->GetAttributes(), * listDef
->GetLevelAttributes(listDef
->FindLevelForIndent(currentIndent
)));
3995 else if (!para
->GetAttributes().GetParagraphStyleName().IsEmpty() && para
->GetAttributes().GetListStyleName().IsEmpty())
3997 wxRichTextParagraphStyleDefinition
* def
= styleSheet
->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
4000 para
->GetAttributes() = def
->GetStyleMergedWithBase(styleSheet
);
4006 para
->GetAttributes().SetOutlineLevel(outline
);
4008 para
->GetAttributes().SetBulletNumber(num
);
4011 node
= node
->GetNext();
4013 return foundCount
!= 0;
4017 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
4019 wxRichTextBuffer
* buffer
= GetBuffer();
4020 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
4022 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
4023 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
4024 bool specifyLevel
= ((flags
& wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL
) != 0);
4025 bool renumber
= ((flags
& wxRICHTEXT_SETSTYLE_RENUMBER
) != 0);
4027 // Current number, if numbering
4030 wxASSERT (!specifyLevel
|| (specifyLevel
&& (specifiedLevel
>= 0)));
4032 // If we are associated with a control, make undoable; otherwise, apply immediately
4035 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
4037 wxRichTextAction
* action
= NULL
;
4039 if (haveControl
&& withUndo
)
4041 action
= new wxRichTextAction(NULL
, _("Change List Style"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
4042 action
->SetRange(range
);
4043 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
4046 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
4049 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
4050 // wxASSERT (para != NULL);
4052 if (para
&& para
->GetChildCount() > 0)
4054 // Stop searching if we're beyond the range of interest
4055 if (para
->GetRange().GetStart() > range
.GetEnd())
4058 if (!para
->GetRange().IsOutside(range
))
4060 // We'll be using a copy of the paragraph to make style changes,
4061 // not updating the buffer directly.
4062 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
4064 if (haveControl
&& withUndo
)
4066 newPara
= new wxRichTextParagraph(*para
);
4067 action
->GetNewParagraphs().AppendChild(newPara
);
4069 // Also store the old ones for Undo
4070 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
4077 int thisIndent
= newPara
->GetAttributes().GetLeftIndent();
4078 int thisLevel
= specifyLevel
? specifiedLevel
: def
->FindLevelForIndent(thisIndent
);
4080 // How is numbering going to work?
4081 // If we are renumbering, or numbering for the first time, we need to keep
4082 // track of the number for each level. But we might be simply applying a different
4084 // In Word, applying a style to several paragraphs, even if at different levels,
4085 // reverts the level back to the same one. So we could do the same here.
4086 // Renumbering will need to be done when we promote/demote a paragraph.
4088 // Apply the overall list style, and item style for this level
4089 wxRichTextAttr
listStyle(def
->GetCombinedStyleForLevel(thisLevel
, styleSheet
));
4090 wxRichTextApplyStyle(newPara
->GetAttributes(), listStyle
);
4092 // Now we need to do numbering
4093 // Preserve the existing list item continuation bullet style, if any
4094 if (para
->GetAttributes().HasBulletStyle() && (para
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
))
4095 newPara
->GetAttributes().SetBulletStyle(newPara
->GetAttributes().GetBulletStyle()|wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
);
4100 newPara
->GetAttributes().SetBulletNumber(n
);
4106 else if (!newPara
->GetAttributes().GetListStyleName().IsEmpty())
4108 // if def is NULL, remove list style, applying any associated paragraph style
4109 // to restore the attributes
4111 newPara
->GetAttributes().SetListStyleName(wxEmptyString
);
4112 newPara
->GetAttributes().SetLeftIndent(0, 0);
4113 newPara
->GetAttributes().SetBulletText(wxEmptyString
);
4114 newPara
->GetAttributes().SetBulletStyle(0);
4116 // Eliminate the main list-related attributes
4117 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
);
4119 if (styleSheet
&& !newPara
->GetAttributes().GetParagraphStyleName().IsEmpty())
4121 wxRichTextParagraphStyleDefinition
* def
= styleSheet
->FindParagraphStyle(newPara
->GetAttributes().GetParagraphStyleName());
4124 newPara
->GetAttributes() = def
->GetStyleMergedWithBase(styleSheet
);
4131 node
= node
->GetNext();
4134 // Do action, or delay it until end of batch.
4135 if (haveControl
&& withUndo
)
4136 buffer
->SubmitAction(action
);
4141 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
)
4143 wxRichTextBuffer
* buffer
= GetBuffer();
4144 if (buffer
&& buffer
->GetStyleSheet())
4146 wxRichTextListStyleDefinition
* def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4148 return SetListStyle(range
, def
, flags
, startFrom
, specifiedLevel
);
4153 /// Clear list for given range
4154 bool wxRichTextParagraphLayoutBox::ClearListStyle(const wxRichTextRange
& range
, int flags
)
4156 return SetListStyle(range
, NULL
, flags
);
4159 /// Number/renumber any list elements in the given range
4160 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
4162 return DoNumberList(range
, range
, 0, def
, flags
, startFrom
, specifiedLevel
);
4165 /// Number/renumber any list elements in the given range. Also do promotion or demotion of items, if specified
4166 bool wxRichTextParagraphLayoutBox::DoNumberList(const wxRichTextRange
& range
, const wxRichTextRange
& promotionRange
, int promoteBy
,
4167 wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
4169 wxRichTextBuffer
* buffer
= GetBuffer();
4170 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
4172 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
4173 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
4175 bool specifyLevel
= ((flags
& wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL
) != 0);
4178 bool renumber
= ((flags
& wxRICHTEXT_SETSTYLE_RENUMBER
) != 0);
4180 // Max number of levels
4181 const int maxLevels
= 10;
4183 // The level we're looking at now
4184 int currentLevel
= -1;
4186 // The item number for each level
4187 int levels
[maxLevels
];
4190 // Reset all numbering
4191 for (i
= 0; i
< maxLevels
; i
++)
4193 if (startFrom
!= -1)
4194 levels
[i
] = startFrom
-1;
4195 else if (renumber
) // start again
4198 levels
[i
] = -1; // start from the number we found, if any
4202 wxASSERT(!specifyLevel
|| (specifyLevel
&& (specifiedLevel
>= 0)));
4205 // If we are associated with a control, make undoable; otherwise, apply immediately
4208 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
4210 wxRichTextAction
* action
= NULL
;
4212 if (haveControl
&& withUndo
)
4214 action
= new wxRichTextAction(NULL
, _("Renumber List"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
4215 action
->SetRange(range
);
4216 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
4219 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
4222 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
4223 // wxASSERT (para != NULL);
4225 if (para
&& para
->GetChildCount() > 0)
4227 // Stop searching if we're beyond the range of interest
4228 if (para
->GetRange().GetStart() > range
.GetEnd())
4231 if (!para
->GetRange().IsOutside(range
))
4233 // We'll be using a copy of the paragraph to make style changes,
4234 // not updating the buffer directly.
4235 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
4237 if (haveControl
&& withUndo
)
4239 newPara
= new wxRichTextParagraph(*para
);
4240 action
->GetNewParagraphs().AppendChild(newPara
);
4242 // Also store the old ones for Undo
4243 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
4248 wxRichTextListStyleDefinition
* defToUse
= def
;
4251 if (styleSheet
&& !newPara
->GetAttributes().GetListStyleName().IsEmpty())
4252 defToUse
= styleSheet
->FindListStyle(newPara
->GetAttributes().GetListStyleName());
4257 int thisIndent
= newPara
->GetAttributes().GetLeftIndent();
4258 int thisLevel
= defToUse
->FindLevelForIndent(thisIndent
);
4260 // If we've specified a level to apply to all, change the level.
4261 if (specifiedLevel
!= -1)
4262 thisLevel
= specifiedLevel
;
4264 // Do promotion if specified
4265 if ((promoteBy
!= 0) && !para
->GetRange().IsOutside(promotionRange
))
4267 thisLevel
= thisLevel
- promoteBy
;
4274 // Apply the overall list style, and item style for this level
4275 wxRichTextAttr
listStyle(defToUse
->GetCombinedStyleForLevel(thisLevel
, styleSheet
));
4276 wxRichTextApplyStyle(newPara
->GetAttributes(), listStyle
);
4278 // Preserve the existing list item continuation bullet style, if any
4279 if (para
->GetAttributes().HasBulletStyle() && (para
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
))
4280 newPara
->GetAttributes().SetBulletStyle(newPara
->GetAttributes().GetBulletStyle()|wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
);
4282 // OK, we've (re)applied the style, now let's get the numbering right.
4284 if (currentLevel
== -1)
4285 currentLevel
= thisLevel
;
4287 // Same level as before, do nothing except increment level's number afterwards
4288 if (currentLevel
== thisLevel
)
4291 // A deeper level: start renumbering all levels after current level
4292 else if (thisLevel
> currentLevel
)
4294 for (i
= currentLevel
+1; i
<= thisLevel
; i
++)
4298 currentLevel
= thisLevel
;
4300 else if (thisLevel
< currentLevel
)
4302 currentLevel
= thisLevel
;
4305 // Use the current numbering if -1 and we have a bullet number already
4306 if (levels
[currentLevel
] == -1)
4308 if (newPara
->GetAttributes().HasBulletNumber())
4309 levels
[currentLevel
] = newPara
->GetAttributes().GetBulletNumber();
4311 levels
[currentLevel
] = 1;
4315 if (!(para
->GetAttributes().HasBulletStyle() && (para
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
)))
4316 levels
[currentLevel
] ++;
4319 newPara
->GetAttributes().SetBulletNumber(levels
[currentLevel
]);
4321 // Create the bullet text if an outline list
4322 if (listStyle
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
)
4325 for (i
= 0; i
<= currentLevel
; i
++)
4327 if (!text
.IsEmpty())
4329 text
+= wxString::Format(wxT("%d"), levels
[i
]);
4331 newPara
->GetAttributes().SetBulletText(text
);
4337 node
= node
->GetNext();
4340 // Do action, or delay it until end of batch.
4341 if (haveControl
&& withUndo
)
4342 buffer
->SubmitAction(action
);
4347 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
)
4349 wxRichTextBuffer
* buffer
= GetBuffer();
4350 if (buffer
->GetStyleSheet())
4352 wxRichTextListStyleDefinition
* def
= NULL
;
4353 if (!defName
.IsEmpty())
4354 def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4355 return NumberList(range
, def
, flags
, startFrom
, specifiedLevel
);
4360 /// Promote the list items within the given range. promoteBy can be a positive or negative number, e.g. 1 or -1
4361 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy
, const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int specifiedLevel
)
4364 // One strategy is to first work out the range within which renumbering must occur. Then could pass these two ranges
4365 // to NumberList with a flag indicating promotion is required within one of the ranges.
4366 // Find first and last paragraphs in range. Then for first, calculate new indentation and look back until we find
4367 // a paragraph that either has no list style, or has one that is different or whose indentation is less.
4368 // We start renumbering from the para after that different para we found. We specify that the numbering of that
4369 // list position will start from 1.
4370 // Similarly, we look after the last para in the promote range for an indentation that is less (or no list style).
4371 // We can end the renumbering at this point.
4373 // For now, only renumber within the promotion range.
4375 return DoNumberList(range
, range
, promoteBy
, def
, flags
, 1, specifiedLevel
);
4378 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy
, const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int specifiedLevel
)
4380 wxRichTextBuffer
* buffer
= GetBuffer();
4381 if (buffer
->GetStyleSheet())
4383 wxRichTextListStyleDefinition
* def
= NULL
;
4384 if (!defName
.IsEmpty())
4385 def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4386 return PromoteList(promoteBy
, range
, def
, flags
, specifiedLevel
);
4391 /// Fills in the attributes for numbering a paragraph after previousParagraph. It also finds the
4392 /// position of the paragraph that it had to start looking from.
4393 bool wxRichTextParagraphLayoutBox::FindNextParagraphNumber(wxRichTextParagraph
* previousParagraph
, wxRichTextAttr
& attr
) const
4395 // TODO: add GetNextChild/GetPreviousChild to composite
4396 // Search for a paragraph that isn't a continuation paragraph (no bullet)
4397 while (previousParagraph
&& previousParagraph
->GetAttributes().HasBulletStyle() && previousParagraph
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
)
4399 wxRichTextObjectList::compatibility_iterator node
= ((wxRichTextCompositeObject
*) previousParagraph
->GetParent())->GetChildren().Find(previousParagraph
);
4402 node
= node
->GetPrevious();
4404 previousParagraph
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
4406 previousParagraph
= NULL
;
4409 previousParagraph
= NULL
;
4412 if (!previousParagraph
|| !previousParagraph
->GetAttributes().HasFlag(wxTEXT_ATTR_BULLET_STYLE
) || previousParagraph
->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
)
4415 wxRichTextBuffer
* buffer
= GetBuffer();
4416 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
4417 if (styleSheet
&& !previousParagraph
->GetAttributes().GetListStyleName().IsEmpty())
4419 wxRichTextListStyleDefinition
* def
= styleSheet
->FindListStyle(previousParagraph
->GetAttributes().GetListStyleName());
4422 // int thisIndent = previousParagraph->GetAttributes().GetLeftIndent();
4423 // int thisLevel = def->FindLevelForIndent(thisIndent);
4425 bool isOutline
= (previousParagraph
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
) != 0;
4427 attr
.SetFlags(previousParagraph
->GetAttributes().GetFlags() & (wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_BULLET_NUMBER
|wxTEXT_ATTR_BULLET_TEXT
|wxTEXT_ATTR_BULLET_NAME
));
4428 if (previousParagraph
->GetAttributes().HasBulletName())
4429 attr
.SetBulletName(previousParagraph
->GetAttributes().GetBulletName());
4430 attr
.SetBulletStyle(previousParagraph
->GetAttributes().GetBulletStyle());
4431 attr
.SetListStyleName(previousParagraph
->GetAttributes().GetListStyleName());
4433 int nextNumber
= previousParagraph
->GetAttributes().GetBulletNumber() + 1;
4434 attr
.SetBulletNumber(nextNumber
);
4438 wxString text
= previousParagraph
->GetAttributes().GetBulletText();
4439 if (!text
.IsEmpty())
4441 int pos
= text
.Find(wxT('.'), true);
4442 if (pos
!= wxNOT_FOUND
)
4444 text
= text
.Mid(0, text
.Length() - pos
- 1);
4447 text
= wxEmptyString
;
4448 if (!text
.IsEmpty())
4450 text
+= wxString::Format(wxT("%d"), nextNumber
);
4451 attr
.SetBulletText(text
);
4465 * wxRichTextParagraph
4466 * This object represents a single paragraph (or in a straight text editor, a line).
4469 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraph
, wxRichTextCompositeObject
)
4471 wxArrayInt
wxRichTextParagraph::sm_defaultTabs
;
4473 wxRichTextParagraph::wxRichTextParagraph(wxRichTextObject
* parent
, wxRichTextAttr
* style
):
4474 wxRichTextCompositeObject(parent
)
4477 SetAttributes(*style
);
4480 wxRichTextParagraph::wxRichTextParagraph(const wxString
& text
, wxRichTextObject
* parent
, wxRichTextAttr
* paraStyle
, wxRichTextAttr
* charStyle
):
4481 wxRichTextCompositeObject(parent
)
4484 SetAttributes(*paraStyle
);
4486 AppendChild(new wxRichTextPlainText(text
, this, charStyle
));
4489 wxRichTextParagraph::~wxRichTextParagraph()
4495 bool wxRichTextParagraph::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int WXUNUSED(descent
), int style
)
4500 // Currently we don't merge these attributes with the parent, but we
4501 // should consider whether we should (e.g. if we set a border colour
4502 // for all paragraphs). But generally box attributes are likely to be
4503 // different for different objects.
4504 wxRect paraRect
= GetRect();
4505 wxRichTextAttr attr
= GetCombinedAttributes();
4506 context
.ApplyVirtualAttributes(attr
, this);
4508 DrawBoxAttributes(dc
, GetBuffer(), attr
, paraRect
);
4510 // Draw the bullet, if any
4511 if ((attr
.GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
) == 0 && (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
) == 0)
4513 if (attr
.GetLeftSubIndent() != 0)
4515 int spaceBeforePara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingBefore());
4516 int leftIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftIndent());
4518 wxRichTextAttr
bulletAttr(attr
);
4520 // Combine with the font of the first piece of content, if one is specified
4521 if (GetChildren().GetCount() > 0)
4523 wxRichTextObject
* firstObj
= (wxRichTextObject
*) GetChildren().GetFirst()->GetData();
4524 if (!firstObj
->IsFloatable() && firstObj
->GetAttributes().HasFont())
4526 wxRichTextApplyStyle(bulletAttr
, firstObj
->GetAttributes());
4530 // Get line height from first line, if any
4531 wxRichTextLine
* line
= m_cachedLines
.GetFirst() ? (wxRichTextLine
* ) m_cachedLines
.GetFirst()->GetData() : NULL
;
4534 int lineHeight
wxDUMMY_INITIALIZE(0);
4537 lineHeight
= line
->GetSize().y
;
4538 linePos
= line
->GetPosition() + GetPosition();
4543 if (bulletAttr
.HasFont() && GetBuffer())
4544 font
= GetBuffer()->GetFontTable().FindFont(bulletAttr
);
4546 font
= (*wxNORMAL_FONT
);
4548 wxCheckSetFont(dc
, font
);
4550 lineHeight
= dc
.GetCharHeight();
4551 linePos
= GetPosition();
4552 linePos
.y
+= spaceBeforePara
;
4555 wxRect
bulletRect(GetPosition().x
+ leftIndent
, linePos
.y
, linePos
.x
- (GetPosition().x
+ leftIndent
), lineHeight
);
4557 if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
)
4559 if (wxRichTextBuffer::GetRenderer())
4560 wxRichTextBuffer::GetRenderer()->DrawBitmapBullet(this, dc
, bulletAttr
, bulletRect
);
4562 else if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_STANDARD
)
4564 if (wxRichTextBuffer::GetRenderer())
4565 wxRichTextBuffer::GetRenderer()->DrawStandardBullet(this, dc
, bulletAttr
, bulletRect
);
4569 wxString bulletText
= GetBulletText();
4571 if (!bulletText
.empty() && wxRichTextBuffer::GetRenderer())
4572 wxRichTextBuffer::GetRenderer()->DrawTextBullet(this, dc
, bulletAttr
, bulletRect
, bulletText
);
4577 // Draw the range for each line, one object at a time.
4579 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
4582 wxRichTextLine
* line
= node
->GetData();
4583 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
4585 // Lines are specified relative to the paragraph
4587 wxPoint linePosition
= line
->GetPosition() + GetPosition();
4589 // Don't draw if off the screen
4590 if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) != 0) || ((linePosition
.y
+ line
->GetSize().y
) >= rect
.y
&& linePosition
.y
<= rect
.y
+ rect
.height
))
4592 wxPoint objectPosition
= linePosition
;
4593 int maxDescent
= line
->GetDescent();
4595 // Loop through objects until we get to the one within range
4596 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
4601 wxRichTextObject
* child
= node2
->GetData();
4603 if ((!child
->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && child
->GetRange().GetLength() > 0 && !child
->GetRange().IsOutside(lineRange
) && !lineRange
.IsOutside(range
))
4605 // Draw this part of the line at the correct position
4606 wxRichTextRange
objectRange(child
->GetRange());
4607 objectRange
.LimitTo(lineRange
);
4610 if (child
->IsTopLevel())
4612 objectSize
= child
->GetCachedSize();
4613 objectRange
= child
->GetOwnRange();
4617 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING && wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4618 if (i
< (int) line
->GetObjectSizes().GetCount())
4620 objectSize
.x
= line
->GetObjectSizes()[(size_t) i
];
4626 child
->GetRangeSize(objectRange
, objectSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, objectPosition
);
4630 // Use the child object's width, but the whole line's height
4631 wxRect
childRect(objectPosition
, wxSize(objectSize
.x
, line
->GetSize().y
));
4632 child
->Draw(dc
, context
, objectRange
, selection
, childRect
, maxDescent
, style
);
4634 objectPosition
.x
+= objectSize
.x
;
4637 else if (child
->GetRange().GetStart() > lineRange
.GetEnd())
4638 // Can break out of inner loop now since we've passed this line's range
4641 node2
= node2
->GetNext();
4645 node
= node
->GetNext();
4651 // Get the range width using partial extents calculated for the whole paragraph.
4652 static int wxRichTextGetRangeWidth(const wxRichTextParagraph
& para
, const wxRichTextRange
& range
, const wxArrayInt
& partialExtents
)
4654 wxASSERT(partialExtents
.GetCount() >= (size_t) range
.GetLength());
4656 if (partialExtents
.GetCount() < (size_t) range
.GetLength())
4659 int leftMostPos
= 0;
4660 if (range
.GetStart() - para
.GetRange().GetStart() > 0)
4661 leftMostPos
= partialExtents
[range
.GetStart() - para
.GetRange().GetStart() - 1];
4663 int rightMostPos
= partialExtents
[range
.GetEnd() - para
.GetRange().GetStart()];
4665 int w
= rightMostPos
- leftMostPos
;
4670 /// Lay the item out
4671 bool wxRichTextParagraph::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
)
4673 // Deal with floating objects firstly before the normal layout
4674 wxRichTextBuffer
* buffer
= GetBuffer();
4677 wxRichTextFloatCollector
* collector
= GetContainer()->GetFloatCollector();
4679 if (wxRichTextBuffer::GetFloatingLayoutMode())
4681 wxASSERT(collector
!= NULL
);
4683 LayoutFloat(dc
, context
, rect
, parentRect
, style
, collector
);
4686 wxRichTextAttr attr
= GetCombinedAttributes();
4687 context
.ApplyVirtualAttributes(attr
, this);
4691 // Increase the size of the paragraph due to spacing
4692 int spaceBeforePara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingBefore());
4693 int spaceAfterPara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingAfter());
4694 int leftIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftIndent());
4695 int leftSubIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftSubIndent());
4696 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
4698 int lineSpacing
= 0;
4700 // Let's assume line spacing of 10 is normal, 15 is 1.5, 20 is 2, etc.
4701 if (attr
.HasLineSpacing() && attr
.GetLineSpacing() > 0 && attr
.HasFont())
4703 wxFont
font(buffer
->GetFontTable().FindFont(attr
));
4706 wxCheckSetFont(dc
, font
);
4707 lineSpacing
= (int) (double(dc
.GetCharHeight()) * (double(attr
.GetLineSpacing())/10.0 - 1.0));
4711 // Start position for each line relative to the paragraph
4712 int startPositionFirstLine
= leftIndent
;
4713 int startPositionSubsequentLines
= leftIndent
+ leftSubIndent
;
4715 // If we have a bullet in this paragraph, the start position for the first line's text
4716 // is actually leftIndent + leftSubIndent.
4717 if (attr
.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE
)
4718 startPositionFirstLine
= startPositionSubsequentLines
;
4720 long lastEndPos
= GetRange().GetStart()-1;
4721 long lastCompletedEndPos
= lastEndPos
;
4723 int currentWidth
= 0;
4724 SetPosition(rect
.GetPosition());
4726 wxPoint
currentPosition(0, spaceBeforePara
); // We will calculate lines relative to paragraph
4729 int maxHeight
= currentPosition
.y
;
4734 int lineDescent
= 0;
4736 wxRichTextObjectList::compatibility_iterator node
;
4738 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4740 wxArrayInt partialExtents
;
4743 int paraDescent
= 0;
4745 // This calculates the partial text extents
4746 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, rect
.GetPosition(), parentRect
.GetSize(), & partialExtents
);
4748 node
= m_children
.GetFirst();
4751 wxRichTextObject
* child
= node
->GetData();
4753 //child->SetCachedSize(wxDefaultSize);
4754 child
->Layout(dc
, context
, rect
, style
);
4756 node
= node
->GetNext();
4762 // We may need to go back to a previous child, in which case create the new line,
4763 // find the child corresponding to the start position of the string, and
4766 wxRect availableRect
;
4768 node
= m_children
.GetFirst();
4771 wxRichTextObject
* child
= node
->GetData();
4773 // If floating, ignore. We already laid out floats.
4774 // Also ignore if empty object, except if we haven't got any
4776 if ((child
->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode())
4777 || !child
->IsShown() || (child
->GetRange().GetLength() == 0 && maxHeight
> spaceBeforePara
)
4780 node
= node
->GetNext();
4784 // If this is e.g. a composite text box, it will need to be laid out itself.
4785 // But if just a text fragment or image, for example, this will
4786 // do nothing. NB: won't we need to set the position after layout?
4787 // since for example if position is dependent on vertical line size, we
4788 // can't tell the position until the size is determined. So possibly introduce
4789 // another layout phase.
4791 // We may only be looking at part of a child, if we searched back for wrapping
4792 // and found a suitable point some way into the child. So get the size for the fragment
4795 long nextBreakPos
= GetFirstLineBreakPosition(lastEndPos
+1);
4796 long lastPosToUse
= child
->GetRange().GetEnd();
4797 bool lineBreakInThisObject
= (nextBreakPos
> -1 && nextBreakPos
<= child
->GetRange().GetEnd());
4799 if (lineBreakInThisObject
)
4800 lastPosToUse
= nextBreakPos
;
4803 int childDescent
= 0;
4805 int startOffset
= (lineCount
== 0 ? startPositionFirstLine
: startPositionSubsequentLines
);
4806 availableRect
= wxRect(rect
.x
+ startOffset
, rect
.y
+ currentPosition
.y
,
4807 rect
.width
- startOffset
- rightIndent
, rect
.height
);
4809 if (child
->IsTopLevel())
4811 wxSize oldSize
= child
->GetCachedSize();
4813 child
->Invalidate(wxRICHTEXT_ALL
);
4814 child
->SetPosition(wxPoint(0, 0));
4816 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4817 // lays out the object again using the minimum size
4818 // The position will be determined by its location in its line,
4819 // and not by the child's actual position.
4820 child
->LayoutToBestSize(dc
, context
, buffer
,
4821 attr
, child
->GetAttributes(), availableRect
, parentRect
, style
);
4823 if (oldSize
!= child
->GetCachedSize())
4825 partialExtents
.Clear();
4827 // Recalculate the partial text extents since the child object changed size
4828 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), parentRect
.GetSize(), & partialExtents
);
4832 // Problem: we need to layout composites here for which we need the available width,
4833 // but we can't get the available width without using the float collector which
4834 // needs to know the object height.
4836 if ((nextBreakPos
== -1) && (lastEndPos
== child
->GetRange().GetStart() - 1)) // i.e. we want to get the whole thing
4838 childSize
= child
->GetCachedSize();
4839 childDescent
= child
->GetDescent();
4843 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4844 // Get height only, then the width using the partial extents
4845 GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
, wxPoint(0,0), parentRect
.GetSize());
4846 childSize
.x
= wxRichTextGetRangeWidth(*this, wxRichTextRange(lastEndPos
+1, lastPosToUse
), partialExtents
);
4848 GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, rect
.GetPosition(), parentRect
.GetSize());
4853 int loopIterations
= 0;
4855 // If there are nested objects that need to lay themselves out, we have to do this in a
4856 // loop because the height of the object may well depend on the available width.
4857 // And because of floating object positioning, the available width depends on the
4858 // height of the object and whether it will clash with the floating objects.
4859 // So, we see whether the available width changes due to the presence of floating images.
4860 // If it does, then we'll use the new restricted width to find the object height again.
4861 // If this causes another restriction in the available width, we'll try again, until
4862 // either we lose patience or the available width settles down.
4867 wxRect oldAvailableRect
= availableRect
;
4869 // Available width depends on the floating objects and the line height.
4870 // Note: the floating objects may be placed vertically along the two sides of
4871 // buffer, so we may have different available line widths with different
4872 // [startY, endY]. So, we can't determine how wide the available
4873 // space is until we know the exact line height.
4874 if (childDescent
== 0)
4876 lineHeight
= wxMax(lineHeight
, childSize
.y
);
4877 lineDescent
= maxDescent
;
4878 lineAscent
= maxAscent
;
4882 lineDescent
= wxMax(childDescent
, maxDescent
);
4883 lineAscent
= wxMax(childSize
.y
-childDescent
, maxAscent
);
4885 lineHeight
= wxMax(lineHeight
, (lineDescent
+ lineAscent
));
4887 if (wxRichTextBuffer::GetFloatingLayoutMode() && collector
)
4889 wxRect floatAvailableRect
= collector
->GetAvailableRect(rect
.y
+ currentPosition
.y
, rect
.y
+ currentPosition
.y
+ lineHeight
);
4891 // Adjust availableRect to the space that is available when taking floating objects into account.
4893 if (floatAvailableRect
.x
+ startOffset
> availableRect
.x
)
4895 int newX
= floatAvailableRect
.x
+ startOffset
;
4896 int newW
= availableRect
.width
- (newX
- availableRect
.x
);
4897 availableRect
.x
= newX
;
4898 availableRect
.width
= newW
;
4901 if (floatAvailableRect
.width
< availableRect
.width
)
4902 availableRect
.width
= floatAvailableRect
.width
;
4905 currentPosition
.x
= availableRect
.x
- rect
.x
;
4907 if (child
->IsTopLevel() && loopIterations
<= 20)
4909 if (availableRect
!= oldAvailableRect
)
4911 wxSize oldSize
= child
->GetCachedSize();
4913 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4914 // lays out the object again using the minimum size
4915 child
->Invalidate(wxRICHTEXT_ALL
);
4916 child
->LayoutToBestSize(dc
, context
, buffer
,
4917 attr
, child
->GetAttributes(), availableRect
, parentRect
.GetSize(), style
);
4918 childSize
= child
->GetCachedSize();
4919 childDescent
= child
->GetDescent();
4921 if (oldSize
!= child
->GetCachedSize())
4923 partialExtents
.Clear();
4925 // Recalculate the partial text extents since the child object changed size
4926 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), parentRect
.GetSize(), & partialExtents
);
4929 // Go around the loop finding the available rect for the given floating objects
4939 if (child
->IsTopLevel())
4941 // We can move it to the correct position at this point
4942 // TODO: probably need to add margin
4943 child
->Move(GetPosition() + wxPoint(currentWidth
+ (wxMax(leftIndent
, leftIndent
+ leftSubIndent
)), currentPosition
.y
));
4947 // 1) There was a line break BEFORE the natural break
4948 // 2) There was a line break AFTER the natural break
4949 // 3) It's the last line
4950 // 4) The child still fits (carry on) - 'else' clause
4952 if ((lineBreakInThisObject
&& (childSize
.x
+ currentWidth
<= availableRect
.width
))
4954 (childSize
.x
+ currentWidth
> availableRect
.width
)
4957 ((childSize
.x
+ currentWidth
<= availableRect
.width
) && !node
->GetNext())
4961 long wrapPosition
= 0;
4962 if ((childSize
.x
+ currentWidth
<= availableRect
.width
) && !node
->GetNext() && !lineBreakInThisObject
)
4963 wrapPosition
= child
->GetRange().GetEnd();
4966 // Find a place to wrap. This may walk back to previous children,
4967 // for example if a word spans several objects.
4968 // Note: one object must contains only one wxTextAtrr, so the line height will not
4969 // change inside one object. Thus, we can pass the remain line width to the
4970 // FindWrapPosition function.
4971 if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos
+1, child
->GetRange().GetEnd()), dc
, context
, availableRect
.width
, wrapPosition
, & partialExtents
))
4973 // If the function failed, just cut it off at the end of this child.
4974 wrapPosition
= child
->GetRange().GetEnd();
4977 // FindWrapPosition can still return a value that will put us in an endless wrapping loop
4978 if (wrapPosition
<= lastCompletedEndPos
)
4979 wrapPosition
= wxMax(lastCompletedEndPos
+1,child
->GetRange().GetEnd());
4981 // Line end position shouldn't be the same as the end, or greater.
4982 if (wrapPosition
>= GetRange().GetEnd())
4983 wrapPosition
= wxMax(0, GetRange().GetEnd()-1);
4985 // wxLogDebug(wxT("Split at %ld"), wrapPosition);
4987 // Let's find the actual size of the current line now
4989 wxRichTextRange
actualRange(lastCompletedEndPos
+1, wrapPosition
);
4993 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4994 if (!child
->IsEmpty())
4996 // Get height only, then the width using the partial extents
4997 GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
, wxPoint(0,0), parentRect
.GetSize());
4998 actualSize
.x
= wxRichTextGetRangeWidth(*this, actualRange
, partialExtents
);
5002 GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, wxPoint(0,0), parentRect
.GetSize());
5004 currentWidth
= actualSize
.x
;
5006 // The descent for the whole line at this point, is the correct max descent
5007 maxDescent
= childDescent
;
5009 maxAscent
= actualSize
.y
-childDescent
;
5011 // lineHeight is given by the height for the whole line, since it will
5012 // take into account ascend/descend.
5013 lineHeight
= actualSize
.y
;
5015 if (lineHeight
== 0 && buffer
)
5017 wxFont
font(buffer
->GetFontTable().FindFont(attr
));
5018 wxCheckSetFont(dc
, font
);
5019 lineHeight
= dc
.GetCharHeight();
5022 if (maxDescent
== 0)
5025 dc
.GetTextExtent(wxT("X"), & w
, &h
, & maxDescent
);
5029 wxRichTextLine
* line
= AllocateLine(lineCount
);
5031 // Set relative range so we won't have to change line ranges when paragraphs are moved
5032 line
->SetRange(wxRichTextRange(actualRange
.GetStart() - GetRange().GetStart(), actualRange
.GetEnd() - GetRange().GetStart()));
5033 line
->SetPosition(currentPosition
);
5034 line
->SetSize(wxSize(currentWidth
, lineHeight
));
5035 line
->SetDescent(maxDescent
);
5037 maxHeight
= currentPosition
.y
+ lineHeight
;
5039 // Now move down a line. TODO: add margins, spacing
5040 currentPosition
.y
+= lineHeight
;
5041 currentPosition
.y
+= lineSpacing
;
5044 maxWidth
= wxMax(maxWidth
, currentWidth
+startOffset
);
5049 // TODO: account for zero-length objects
5050 // wxASSERT(wrapPosition > lastCompletedEndPos);
5052 lastEndPos
= wrapPosition
;
5053 lastCompletedEndPos
= lastEndPos
;
5057 if (wrapPosition
< GetRange().GetEnd()-1)
5059 // May need to set the node back to a previous one, due to searching back in wrapping
5060 wxRichTextObject
* childAfterWrapPosition
= FindObjectAtPosition(wrapPosition
+1);
5061 if (childAfterWrapPosition
)
5062 node
= m_children
.Find(childAfterWrapPosition
);
5064 node
= node
->GetNext();
5067 node
= node
->GetNext();
5069 // Apply paragraph styles such as alignment to the wrapped line
5070 ApplyParagraphStyle(line
, attr
, availableRect
, dc
);
5074 // We still fit, so don't add a line, and keep going
5075 currentWidth
+= childSize
.x
;
5077 if (childDescent
== 0)
5079 // An object with a zero descend value wants to take up the whole
5080 // height regardless of baseline
5081 lineHeight
= wxMax(lineHeight
, childSize
.y
);
5085 maxDescent
= wxMax(childDescent
, maxDescent
);
5086 maxAscent
= wxMax(childSize
.y
-childDescent
, maxAscent
);
5089 lineHeight
= wxMax(lineHeight
, (maxDescent
+ maxAscent
));
5091 maxWidth
= wxMax(maxWidth
, currentWidth
+startOffset
);
5092 lastEndPos
= child
->GetRange().GetEnd();
5094 node
= node
->GetNext();
5098 //wxASSERT(!(lastCompletedEndPos != -1 && lastCompletedEndPos < GetRange().GetEnd()-1));
5100 // Add the last line - it's the current pos -> last para pos
5101 // Substract -1 because the last position is always the end-paragraph position.
5102 if (lastCompletedEndPos
<= GetRange().GetEnd()-1)
5104 currentPosition
.x
= (lineCount
== 0 ? startPositionFirstLine
: startPositionSubsequentLines
);
5106 wxRichTextLine
* line
= AllocateLine(lineCount
);
5108 wxRichTextRange
actualRange(lastCompletedEndPos
+1, GetRange().GetEnd()-1);
5110 // Set relative range so we won't have to change line ranges when paragraphs are moved
5111 line
->SetRange(wxRichTextRange(actualRange
.GetStart() - GetRange().GetStart(), actualRange
.GetEnd() - GetRange().GetStart()));
5113 line
->SetPosition(currentPosition
);
5115 if (lineHeight
== 0 && buffer
)
5117 wxFont
font(buffer
->GetFontTable().FindFont(attr
));
5118 wxCheckSetFont(dc
, font
);
5119 lineHeight
= dc
.GetCharHeight();
5122 if (maxDescent
== 0)
5125 dc
.GetTextExtent(wxT("X"), & w
, &h
, & maxDescent
);
5128 line
->SetSize(wxSize(currentWidth
, lineHeight
));
5129 line
->SetDescent(maxDescent
);
5130 currentPosition
.y
+= lineHeight
;
5131 currentPosition
.y
+= lineSpacing
;
5134 // Apply paragraph styles such as alignment to the wrapped line
5135 ApplyParagraphStyle(line
, attr
, availableRect
, dc
);
5138 // Remove remaining unused line objects, if any
5139 ClearUnusedLines(lineCount
);
5141 // We need to add back the margins etc.
5143 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
5144 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxWidth
, currentPosition
.y
+ spaceAfterPara
));
5145 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
5146 SetCachedSize(marginRect
.GetSize());
5149 // The maximum size is the length of the paragraph stretched out into a line.
5150 // So if there were a single word, or an image, or a fixed-size text box, the object could be shrunk around
5151 // this size. TODO: take into account line breaks.
5153 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
5154 contentRect
= wxRect(wxPoint(0, 0), wxSize(paraSize
.x
+ wxMax(leftIndent
, leftIndent
+ leftSubIndent
) + rightIndent
, currentPosition
.y
+ spaceAfterPara
));
5155 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
5156 SetMaxSize(marginRect
.GetSize());
5159 // Find the greatest minimum size. Currently we only look at non-text objects,
5160 // which isn't ideal but it would be slow to find the maximum word width to
5161 // use as the minimum.
5164 node
= m_children
.GetFirst();
5167 wxRichTextObject
* child
= node
->GetData();
5169 // If floating, ignore. We already laid out floats.
5170 // Also ignore if empty object, except if we haven't got any
5172 if ((!child
->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && child
->GetRange().GetLength() != 0 && !wxDynamicCast(child
, wxRichTextPlainText
))
5174 if (child
->GetCachedSize().x
> minWidth
)
5175 minWidth
= child
->GetMinSize().x
;
5177 node
= node
->GetNext();
5180 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
5181 contentRect
= wxRect(wxPoint(0, 0), wxSize(minWidth
, currentPosition
.y
+ spaceAfterPara
));
5182 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
5183 SetMinSize(marginRect
.GetSize());
5186 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5187 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
5188 // Use the text extents to calculate the size of each fragment in each line
5189 wxRichTextLineList::compatibility_iterator lineNode
= m_cachedLines
.GetFirst();
5192 wxRichTextLine
* line
= lineNode
->GetData();
5193 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5195 // Loop through objects until we get to the one within range
5196 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
5200 wxRichTextObject
* child
= node2
->GetData();
5202 if (child
->GetRange().GetLength() > 0 && !child
->GetRange().IsOutside(lineRange
))
5204 wxRichTextRange rangeToUse
= lineRange
;
5205 rangeToUse
.LimitTo(child
->GetRange());
5207 // Find the size of the child from the text extents, and store in an array
5208 // for drawing later
5210 if (rangeToUse
.GetStart() > GetRange().GetStart())
5211 left
= partialExtents
[(rangeToUse
.GetStart()-1) - GetRange().GetStart()];
5212 int right
= partialExtents
[rangeToUse
.GetEnd() - GetRange().GetStart()];
5213 int sz
= right
- left
;
5214 line
->GetObjectSizes().Add(sz
);
5216 else if (child
->GetRange().GetStart() > lineRange
.GetEnd())
5217 // Can break out of inner loop now since we've passed this line's range
5220 node2
= node2
->GetNext();
5223 lineNode
= lineNode
->GetNext();
5231 /// Apply paragraph styles, such as centering, to wrapped lines
5232 /// TODO: take into account box attributes, possibly
5233 void wxRichTextParagraph::ApplyParagraphStyle(wxRichTextLine
* line
, const wxRichTextAttr
& attr
, const wxRect
& rect
, wxDC
& dc
)
5235 if (!attr
.HasAlignment())
5238 wxPoint pos
= line
->GetPosition();
5239 wxPoint originalPos
= pos
;
5240 wxSize size
= line
->GetSize();
5242 // centering, right-justification
5243 if (attr
.HasAlignment() && attr
.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE
)
5245 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
5246 pos
.x
= (rect
.GetWidth() - rightIndent
- size
.x
)/2 + pos
.x
;
5247 line
->SetPosition(pos
);
5249 else if (attr
.HasAlignment() && attr
.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT
)
5251 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
5252 pos
.x
= pos
.x
+ rect
.GetWidth() - size
.x
- rightIndent
;
5253 line
->SetPosition(pos
);
5256 if (pos
!= originalPos
)
5258 wxPoint inc
= pos
- originalPos
;
5260 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5264 wxRichTextObject
* child
= node
->GetData();
5265 if (child
->IsTopLevel() && !child
->GetRange().IsOutside(line
->GetAbsoluteRange()))
5266 child
->Move(child
->GetPosition() + inc
);
5268 node
= node
->GetNext();
5273 /// Insert text at the given position
5274 bool wxRichTextParagraph::InsertText(long pos
, const wxString
& text
)
5276 wxRichTextObject
* childToUse
= NULL
;
5277 wxRichTextObjectList::compatibility_iterator nodeToUse
= wxRichTextObjectList::compatibility_iterator();
5279 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5282 wxRichTextObject
* child
= node
->GetData();
5283 if (child
->GetRange().Contains(pos
) && child
->GetRange().GetLength() > 0)
5290 node
= node
->GetNext();
5295 wxRichTextPlainText
* textObject
= wxDynamicCast(childToUse
, wxRichTextPlainText
);
5298 int posInString
= pos
- textObject
->GetRange().GetStart();
5300 wxString newText
= textObject
->GetText().Mid(0, posInString
) +
5301 text
+ textObject
->GetText().Mid(posInString
);
5302 textObject
->SetText(newText
);
5304 int textLength
= text
.length();
5306 textObject
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart(),
5307 textObject
->GetRange().GetEnd() + textLength
));
5309 // Increment the end range of subsequent fragments in this paragraph.
5310 // We'll set the paragraph range itself at a higher level.
5312 wxRichTextObjectList::compatibility_iterator node
= nodeToUse
->GetNext();
5315 wxRichTextObject
* child
= node
->GetData();
5316 child
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart() + textLength
,
5317 textObject
->GetRange().GetEnd() + textLength
));
5319 node
= node
->GetNext();
5326 // TODO: if not a text object, insert at closest position, e.g. in front of it
5332 // Don't pass parent initially to suppress auto-setting of parent range.
5333 // We'll do that at a higher level.
5334 wxRichTextPlainText
* textObject
= new wxRichTextPlainText(text
, this);
5336 AppendChild(textObject
);
5343 void wxRichTextParagraph::Copy(const wxRichTextParagraph
& obj
)
5345 wxRichTextCompositeObject::Copy(obj
);
5348 /// Clear the cached lines
5349 void wxRichTextParagraph::ClearLines()
5351 WX_CLEAR_LIST(wxRichTextLineList
, m_cachedLines
);
5354 /// Get/set the object size for the given range. Returns false if the range
5355 /// is invalid for this object.
5356 bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, const wxPoint
& position
, const wxSize
& parentSize
, wxArrayInt
* partialExtents
) const
5358 if (!range
.IsWithin(GetRange()))
5361 if (flags
& wxRICHTEXT_UNFORMATTED
)
5363 // Just use unformatted data, assume no line breaks
5366 wxArrayInt childExtents
;
5375 int maxLineHeight
= 0;
5377 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5380 wxRichTextObject
* child
= node
->GetData();
5381 if (!child
->GetRange().IsOutside(range
))
5383 // Floating objects have a zero size within the paragraph.
5384 if (child
->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode())
5389 if (partialExtents
->GetCount() > 0)
5390 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5394 partialExtents
->Add(0 /* zero size */ + lastSize
);
5401 wxRichTextRange rangeToUse
= range
;
5402 rangeToUse
.LimitTo(child
->GetRange());
5403 int childDescent
= 0;
5405 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we've already cached the size,
5406 // but it's only going to be used after caching has taken place.
5407 if ((flags
& wxRICHTEXT_HEIGHT_ONLY
) && child
->GetCachedSize().y
!= 0)
5409 childDescent
= child
->GetDescent();
5410 childSize
= child
->GetCachedSize();
5412 if (childDescent
== 0)
5414 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5418 maxDescent
= wxMax(maxDescent
, childDescent
);
5419 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5422 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5424 sz
.y
= wxMax(sz
.y
, maxLineHeight
);
5425 sz
.x
+= childSize
.x
;
5426 descent
= maxDescent
;
5428 else if (child
->IsTopLevel())
5430 childDescent
= child
->GetDescent();
5431 childSize
= child
->GetCachedSize();
5433 if (childDescent
== 0)
5435 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5439 maxDescent
= wxMax(maxDescent
, childDescent
);
5440 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5443 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5445 sz
.y
= wxMax(sz
.y
, maxLineHeight
);
5446 sz
.x
+= childSize
.x
;
5447 descent
= maxDescent
;
5449 // FIXME: this won't change the original values.
5450 // Should we be calling GetRangeSize above instead of using cached values?
5452 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange()))
5454 child
->SetCachedSize(childSize
);
5455 child
->SetDescent(childDescent
);
5462 if (partialExtents
->GetCount() > 0)
5463 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5467 partialExtents
->Add(childSize
.x
+ lastSize
);
5470 else if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, context
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
), parentSize
, p
))
5472 if (childDescent
== 0)
5474 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5478 maxDescent
= wxMax(maxDescent
, childDescent
);
5479 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5482 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5484 sz
.y
= wxMax(sz
.y
, maxLineHeight
);
5485 sz
.x
+= childSize
.x
;
5486 descent
= maxDescent
;
5488 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange()))
5490 child
->SetCachedSize(childSize
);
5491 child
->SetDescent(childDescent
);
5497 if (partialExtents
->GetCount() > 0)
5498 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5503 for (i
= 0; i
< childExtents
.GetCount(); i
++)
5505 partialExtents
->Add(childExtents
[i
] + lastSize
);
5515 node
= node
->GetNext();
5521 // Use formatted data, with line breaks
5524 // We're going to loop through each line, and then for each line,
5525 // call GetRangeSize for the fragment that comprises that line.
5526 // Only we have to do that multiple times within the line, because
5527 // the line may be broken into pieces. For now ignore line break commands
5528 // (so we can assume that getting the unformatted size for a fragment
5529 // within a line is the actual size)
5531 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5534 wxRichTextLine
* line
= node
->GetData();
5535 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5536 if (!lineRange
.IsOutside(range
))
5540 int maxLineHeight
= 0;
5541 int maxLineWidth
= 0;
5543 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
5546 wxRichTextObject
* child
= node2
->GetData();
5548 if ((!child
->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && !child
->GetRange().IsOutside(lineRange
))
5550 wxRichTextRange rangeToUse
= lineRange
;
5551 rangeToUse
.LimitTo(child
->GetRange());
5552 if (child
->IsTopLevel())
5553 rangeToUse
= child
->GetOwnRange();
5556 int childDescent
= 0;
5557 if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, context
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
), parentSize
))
5559 if (childDescent
== 0)
5561 // Assume that if descent is zero, this child can occupy the full line height
5562 // and does not need space for the line's maximum descent. So we influence
5563 // the overall max line height only.
5564 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5568 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5569 maxDescent
= wxMax(maxAscent
, childDescent
);
5571 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5572 maxLineWidth
+= childSize
.x
;
5576 node2
= node2
->GetNext();
5579 descent
= wxMax(descent
, maxDescent
);
5581 // Increase size by a line (TODO: paragraph spacing)
5582 sz
.y
+= maxLineHeight
;
5583 sz
.x
= wxMax(sz
.x
, maxLineWidth
);
5585 node
= node
->GetNext();
5592 /// Finds the absolute position and row height for the given character position
5593 bool wxRichTextParagraph::FindPosition(wxDC
& dc
, wxRichTextDrawingContext
& context
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
5597 wxRichTextLine
* line
= ((wxRichTextParagraphLayoutBox
*)GetParent())->GetLineAtPosition(0);
5599 *height
= line
->GetSize().y
;
5601 *height
= dc
.GetCharHeight();
5603 // -1 means 'the start of the buffer'.
5606 pt
= pt
+ line
->GetPosition();
5611 // The final position in a paragraph is taken to mean the position
5612 // at the start of the next paragraph.
5613 if (index
== GetRange().GetEnd())
5615 wxRichTextParagraphLayoutBox
* parent
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
5616 wxASSERT( parent
!= NULL
);
5618 // Find the height at the next paragraph, if any
5619 wxRichTextLine
* line
= parent
->GetLineAtPosition(index
+ 1);
5622 *height
= line
->GetSize().y
;
5623 pt
= line
->GetAbsolutePosition();
5627 *height
= dc
.GetCharHeight();
5628 int indent
= ConvertTenthsMMToPixels(dc
, m_attributes
.GetLeftIndent());
5629 pt
= wxPoint(indent
, GetCachedSize().y
);
5635 if (index
< GetRange().GetStart() || index
> GetRange().GetEnd())
5638 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5641 wxRichTextLine
* line
= node
->GetData();
5642 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5643 if (index
>= lineRange
.GetStart() && index
<= lineRange
.GetEnd())
5645 // If this is the last point in the line, and we're forcing the
5646 // returned value to be the start of the next line, do the required
5648 if (index
== lineRange
.GetEnd() && forceLineStart
)
5650 if (node
->GetNext())
5652 wxRichTextLine
* nextLine
= node
->GetNext()->GetData();
5653 *height
= nextLine
->GetSize().y
;
5654 pt
= nextLine
->GetAbsolutePosition();
5659 pt
.y
= line
->GetPosition().y
+ GetPosition().y
;
5661 wxRichTextRange
r(lineRange
.GetStart(), index
);
5665 // We find the size of the line up to this point,
5666 // then we can add this size to the line start position and
5667 // paragraph start position to find the actual position.
5669 if (GetRangeSize(r
, rangeSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, line
->GetPosition()+ GetPosition()))
5671 pt
.x
= line
->GetPosition().x
+ GetPosition().x
+ rangeSize
.x
;
5672 *height
= line
->GetSize().y
;
5679 node
= node
->GetNext();
5685 /// Hit-testing: returns a flag indicating hit test details, plus
5686 /// information about position
5687 int wxRichTextParagraph::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
5690 return wxRICHTEXT_HITTEST_NONE
;
5692 // If we're in the top-level container, then we can return
5693 // a suitable hit test code even if the point is outside the container area,
5694 // so that we can position the caret sensibly even if we don't
5695 // click on valid content. If we're not at the top-level, and the point
5696 // is not within this paragraph object, then we don't want to stop more
5697 // precise hit-testing from working prematurely, so return immediately.
5698 // NEW STRATEGY: use the parent boundary to test whether we're in the
5699 // right region, not the paragraph, since the paragraph may be positioned
5700 // some way in from where the user clicks.
5703 wxRichTextObject
* tempObj
, *tempContextObj
;
5704 if (GetParent() && GetParent()->wxRichTextObject::HitTest(dc
, context
, pt
, tmpPos
, & tempObj
, & tempContextObj
, flags
) == wxRICHTEXT_HITTEST_NONE
)
5705 return wxRICHTEXT_HITTEST_NONE
;
5708 wxRichTextObjectList::compatibility_iterator objNode
= m_children
.GetFirst();
5711 wxRichTextObject
* child
= objNode
->GetData();
5712 // Don't recurse if we have wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS,
5713 // and also, if this seems composite but actually is marked as atomic,
5715 if (child
->IsTopLevel() && ((flags
& wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS
) == 0) &&
5716 (! (((flags
& wxRICHTEXT_HITTEST_HONOUR_ATOMIC
) != 0) && child
->IsAtomic())))
5719 int hitTest
= child
->HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
);
5720 if (hitTest
!= wxRICHTEXT_HITTEST_NONE
)
5725 objNode
= objNode
->GetNext();
5728 wxPoint paraPos
= GetPosition();
5730 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5733 wxRichTextLine
* line
= node
->GetData();
5734 wxPoint linePos
= paraPos
+ line
->GetPosition();
5735 wxSize lineSize
= line
->GetSize();
5736 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5738 if (pt
.y
<= linePos
.y
+ lineSize
.y
)
5740 if (pt
.x
< linePos
.x
)
5742 textPosition
= lineRange
.GetStart();
5743 *obj
= FindObjectAtPosition(textPosition
);
5744 *contextObj
= GetContainer();
5745 return wxRICHTEXT_HITTEST_BEFORE
|wxRICHTEXT_HITTEST_OUTSIDE
;
5747 else if (pt
.x
>= (linePos
.x
+ lineSize
.x
))
5749 textPosition
= lineRange
.GetEnd();
5750 *obj
= FindObjectAtPosition(textPosition
);
5751 *contextObj
= GetContainer();
5752 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
;
5756 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5757 wxArrayInt partialExtents
;
5762 // This calculates the partial text extents
5763 GetRangeSize(lineRange
, paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, linePos
, wxDefaultSize
, & partialExtents
);
5765 int lastX
= linePos
.x
;
5767 for (i
= 0; i
< partialExtents
.GetCount(); i
++)
5769 int nextX
= partialExtents
[i
] + linePos
.x
;
5771 if (pt
.x
>= lastX
&& pt
.x
<= nextX
)
5773 textPosition
= i
+ lineRange
.GetStart(); // minus 1?
5775 *obj
= FindObjectAtPosition(textPosition
);
5776 *contextObj
= GetContainer();
5778 // So now we know it's between i-1 and i.
5779 // Let's see if we can be more precise about
5780 // which side of the position it's on.
5782 int midPoint
= (nextX
+ lastX
)/2;
5783 if (pt
.x
>= midPoint
)
5784 return wxRICHTEXT_HITTEST_AFTER
;
5786 return wxRICHTEXT_HITTEST_BEFORE
;
5793 int lastX
= linePos
.x
;
5794 for (i
= lineRange
.GetStart(); i
<= lineRange
.GetEnd(); i
++)
5799 wxRichTextRange
rangeToUse(lineRange
.GetStart(), i
);
5801 GetRangeSize(rangeToUse
, childSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, linePos
);
5803 int nextX
= childSize
.x
+ linePos
.x
;
5805 if (pt
.x
>= lastX
&& pt
.x
<= nextX
)
5809 *obj
= FindObjectAtPosition(textPosition
);
5810 *contextObj
= GetContainer();
5812 // So now we know it's between i-1 and i.
5813 // Let's see if we can be more precise about
5814 // which side of the position it's on.
5816 int midPoint
= (nextX
+ lastX
)/2;
5817 if (pt
.x
>= midPoint
)
5818 return wxRICHTEXT_HITTEST_AFTER
;
5820 return wxRICHTEXT_HITTEST_BEFORE
;
5831 node
= node
->GetNext();
5834 return wxRICHTEXT_HITTEST_NONE
;
5837 /// Split an object at this position if necessary, and return
5838 /// the previous object, or NULL if inserting at beginning.
5839 wxRichTextObject
* wxRichTextParagraph::SplitAt(long pos
, wxRichTextObject
** previousObject
)
5841 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5844 wxRichTextObject
* child
= node
->GetData();
5846 if (pos
== child
->GetRange().GetStart())
5850 if (node
->GetPrevious())
5851 *previousObject
= node
->GetPrevious()->GetData();
5853 *previousObject
= NULL
;
5859 if (child
->GetRange().Contains(pos
))
5861 // This should create a new object, transferring part of
5862 // the content to the old object and the rest to the new object.
5863 wxRichTextObject
* newObject
= child
->DoSplit(pos
);
5865 // If we couldn't split this object, just insert in front of it.
5868 // Maybe this is an empty string, try the next one
5873 // Insert the new object after 'child'
5874 if (node
->GetNext())
5875 m_children
.Insert(node
->GetNext(), newObject
);
5877 m_children
.Append(newObject
);
5878 newObject
->SetParent(this);
5881 *previousObject
= child
;
5887 node
= node
->GetNext();
5890 *previousObject
= NULL
;
5894 /// Move content to a list from obj on
5895 void wxRichTextParagraph::MoveToList(wxRichTextObject
* obj
, wxList
& list
)
5897 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(obj
);
5900 wxRichTextObject
* child
= node
->GetData();
5903 wxRichTextObjectList::compatibility_iterator oldNode
= node
;
5905 node
= node
->GetNext();
5907 m_children
.DeleteNode(oldNode
);
5911 /// Add content back from list
5912 void wxRichTextParagraph::MoveFromList(wxList
& list
)
5914 for (wxList::compatibility_iterator node
= list
.GetFirst(); node
; node
= node
->GetNext())
5916 AppendChild((wxRichTextObject
*) node
->GetData());
5921 void wxRichTextParagraph::CalculateRange(long start
, long& end
)
5923 wxRichTextCompositeObject::CalculateRange(start
, end
);
5925 // Add one for end of paragraph
5928 m_range
.SetRange(start
, end
);
5931 /// Find the object at the given position
5932 wxRichTextObject
* wxRichTextParagraph::FindObjectAtPosition(long position
)
5934 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5937 wxRichTextObject
* obj
= node
->GetData();
5938 if (obj
->GetRange().Contains(position
) ||
5939 obj
->GetRange().GetStart() == position
||
5940 obj
->GetRange().GetEnd() == position
)
5943 node
= node
->GetNext();
5948 /// Get the plain text searching from the start or end of the range.
5949 /// The resulting string may be shorter than the range given.
5950 bool wxRichTextParagraph::GetContiguousPlainText(wxString
& text
, const wxRichTextRange
& range
, bool fromStart
)
5952 text
= wxEmptyString
;
5956 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5959 wxRichTextObject
* obj
= node
->GetData();
5960 if (!obj
->GetRange().IsOutside(range
))
5962 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5965 text
+= textObj
->GetTextForRange(range
);
5973 node
= node
->GetNext();
5978 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetLast();
5981 wxRichTextObject
* obj
= node
->GetData();
5982 if (!obj
->GetRange().IsOutside(range
))
5984 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5987 text
= textObj
->GetTextForRange(range
) + text
;
5991 text
= wxT(" ") + text
;
5995 node
= node
->GetPrevious();
6002 /// Find a suitable wrap position.
6003 bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange
& range
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int availableSpace
, long& wrapPosition
, wxArrayInt
* partialExtents
)
6005 if (range
.GetLength() <= 0)
6008 // Find the first position where the line exceeds the available space.
6010 long breakPosition
= range
.GetEnd();
6012 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
6013 if (partialExtents
&& partialExtents
->GetCount() >= (size_t) (GetRange().GetLength()-1)) // the final position in a paragraph is the newline
6017 if (range
.GetStart() > GetRange().GetStart())
6018 widthBefore
= (*partialExtents
)[range
.GetStart() - GetRange().GetStart() - 1];
6023 for (i
= (size_t) range
.GetStart(); i
<= (size_t) range
.GetEnd(); i
++)
6025 int widthFromStartOfThisRange
= (*partialExtents
)[i
- GetRange().GetStart()] - widthBefore
;
6027 if (widthFromStartOfThisRange
> availableSpace
)
6029 breakPosition
= i
-1;
6037 // Binary chop for speed
6038 long minPos
= range
.GetStart();
6039 long maxPos
= range
.GetEnd();
6042 if (minPos
== maxPos
)
6045 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
6047 if (sz
.x
> availableSpace
)
6048 breakPosition
= minPos
- 1;
6051 else if ((maxPos
- minPos
) == 1)
6054 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
6056 if (sz
.x
> availableSpace
)
6057 breakPosition
= minPos
- 1;
6060 GetRangeSize(wxRichTextRange(range
.GetStart(), maxPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
6061 if (sz
.x
> availableSpace
)
6062 breakPosition
= maxPos
-1;
6068 long nextPos
= minPos
+ ((maxPos
- minPos
) / 2);
6071 GetRangeSize(wxRichTextRange(range
.GetStart(), nextPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
6073 if (sz
.x
> availableSpace
)
6085 // Now we know the last position on the line.
6086 // Let's try to find a word break.
6089 if (GetContiguousPlainText(plainText
, wxRichTextRange(range
.GetStart(), breakPosition
), false))
6091 int newLinePos
= plainText
.Find(wxRichTextLineBreakChar
);
6092 if (newLinePos
!= wxNOT_FOUND
)
6094 breakPosition
= wxMax(0, range
.GetStart() + newLinePos
);
6098 int spacePos
= plainText
.Find(wxT(' '), true);
6099 int tabPos
= plainText
.Find(wxT('\t'), true);
6100 int pos
= wxMax(spacePos
, tabPos
);
6101 if (pos
!= wxNOT_FOUND
)
6103 int positionsFromEndOfString
= plainText
.length() - pos
- 1;
6104 breakPosition
= breakPosition
- positionsFromEndOfString
;
6109 wrapPosition
= breakPosition
;
6114 /// Get the bullet text for this paragraph.
6115 wxString
wxRichTextParagraph::GetBulletText()
6117 if (GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
||
6118 (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
))
6119 return wxEmptyString
;
6121 int number
= GetAttributes().GetBulletNumber();
6124 if ((GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ARABIC
) || (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
))
6126 text
.Printf(wxT("%d"), number
);
6128 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_UPPER
)
6130 // TODO: Unicode, and also check if number > 26
6131 text
.Printf(wxT("%c"), (wxChar
) (number
+64));
6133 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_LOWER
)
6135 // TODO: Unicode, and also check if number > 26
6136 text
.Printf(wxT("%c"), (wxChar
) (number
+96));
6138 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER
)
6140 text
= wxRichTextDecimalToRoman(number
);
6142 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER
)
6144 text
= wxRichTextDecimalToRoman(number
);
6147 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
)
6149 text
= GetAttributes().GetBulletText();
6152 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
)
6154 // The outline style relies on the text being computed statically,
6155 // since it depends on other levels points (e.g. 1.2.1.1). So normally the bullet text
6156 // should be stored in the attributes; if not, just use the number for this
6157 // level, as previously computed.
6158 if (!GetAttributes().GetBulletText().IsEmpty())
6159 text
= GetAttributes().GetBulletText();
6162 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PARENTHESES
)
6164 text
= wxT("(") + text
+ wxT(")");
6166 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_RIGHT_PARENTHESIS
)
6168 text
= text
+ wxT(")");
6171 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PERIOD
)
6179 /// Allocate or reuse a line object
6180 wxRichTextLine
* wxRichTextParagraph::AllocateLine(int pos
)
6182 if (pos
< (int) m_cachedLines
.GetCount())
6184 wxRichTextLine
* line
= m_cachedLines
.Item(pos
)->GetData();
6190 wxRichTextLine
* line
= new wxRichTextLine(this);
6191 m_cachedLines
.Append(line
);
6196 /// Clear remaining unused line objects, if any
6197 bool wxRichTextParagraph::ClearUnusedLines(int lineCount
)
6199 int cachedLineCount
= m_cachedLines
.GetCount();
6200 if ((int) cachedLineCount
> lineCount
)
6202 for (int i
= 0; i
< (int) (cachedLineCount
- lineCount
); i
++)
6204 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetLast();
6205 wxRichTextLine
* line
= node
->GetData();
6206 m_cachedLines
.Erase(node
);
6213 /// Get combined attributes of the base style, paragraph style and character style. We use this to dynamically
6214 /// retrieve the actual style.
6215 wxRichTextAttr
wxRichTextParagraph::GetCombinedAttributes(const wxRichTextAttr
& contentStyle
, bool includingBoxAttr
) const
6217 wxRichTextAttr attr
;
6218 wxRichTextParagraphLayoutBox
* buf
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
6221 attr
= buf
->GetBasicStyle();
6222 if (!includingBoxAttr
)
6224 attr
.GetTextBoxAttr().Reset();
6225 // The background colour will be painted by the container, and we don't
6226 // want to unnecessarily overwrite the background when we're drawing text
6227 // because this may erase the guideline (which appears just under the text
6228 // if there's no padding).
6229 attr
.SetFlags(attr
.GetFlags() & ~wxTEXT_ATTR_BACKGROUND_COLOUR
);
6231 wxRichTextApplyStyle(attr
, GetAttributes());
6234 attr
= GetAttributes();
6236 wxRichTextApplyStyle(attr
, contentStyle
);
6240 /// Get combined attributes of the base style and paragraph style.
6241 wxRichTextAttr
wxRichTextParagraph::GetCombinedAttributes(bool includingBoxAttr
) const
6243 wxRichTextAttr attr
;
6244 wxRichTextParagraphLayoutBox
* buf
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
6247 attr
= buf
->GetBasicStyle();
6248 if (!includingBoxAttr
)
6249 attr
.GetTextBoxAttr().Reset();
6250 wxRichTextApplyStyle(attr
, GetAttributes());
6253 attr
= GetAttributes();
6258 // Create default tabstop array
6259 void wxRichTextParagraph::InitDefaultTabs()
6261 // create a default tab list at 10 mm each.
6262 for (int i
= 0; i
< 20; ++i
)
6264 sm_defaultTabs
.Add(i
*100);
6268 // Clear default tabstop array
6269 void wxRichTextParagraph::ClearDefaultTabs()
6271 sm_defaultTabs
.Clear();
6274 void wxRichTextParagraph::LayoutFloat(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
, wxRichTextFloatCollector
* floatCollector
)
6276 wxRichTextObjectList::compatibility_iterator node
= GetChildren().GetFirst();
6279 wxRichTextObject
* anchored
= node
->GetData();
6280 if (anchored
&& anchored
->IsFloating() && !floatCollector
->HasFloat(anchored
))
6283 wxRichTextAttr
parentAttr(GetAttributes());
6284 context
.ApplyVirtualAttributes(parentAttr
, this);
6287 wxRect availableSpace
= GetParent()->GetAvailableContentArea(dc
, context
, rect
);
6289 anchored
->LayoutToBestSize(dc
, context
, GetBuffer(),
6290 parentAttr
, anchored
->GetAttributes(),
6291 parentRect
, availableSpace
,
6293 wxSize size
= anchored
->GetCachedSize();
6297 anchored
->GetRangeSize(anchored
->GetRange(), size
, descent
, dc
, context
, style
);
6301 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().IsValid())
6303 offsetY
= anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetValue();
6304 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
6306 offsetY
= ConvertTenthsMMToPixels(dc
, offsetY
);
6310 int pos
= floatCollector
->GetFitPosition(anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode(), rect
.y
+ offsetY
, size
.y
);
6312 /* Update the offset */
6313 int newOffsetY
= pos
- rect
.y
;
6314 if (newOffsetY
!= offsetY
)
6316 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
6317 newOffsetY
= ConvertPixelsToTenthsMM(dc
, newOffsetY
);
6318 anchored
->GetAttributes().GetTextBoxAttr().GetTop().SetValue(newOffsetY
);
6321 if (anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT
)
6323 else if (anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT
)
6324 x
= rect
.x
+ rect
.width
- size
.x
;
6326 //anchored->SetPosition(wxPoint(x, pos));
6327 anchored
->Move(wxPoint(x
, pos
)); // should move children
6328 anchored
->SetCachedSize(size
);
6329 floatCollector
->CollectFloat(this, anchored
);
6332 node
= node
->GetNext();
6336 // Get the first position from pos that has a line break character.
6337 long wxRichTextParagraph::GetFirstLineBreakPosition(long pos
)
6339 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
6342 wxRichTextObject
* obj
= node
->GetData();
6343 if (pos
>= obj
->GetRange().GetStart() && pos
<= obj
->GetRange().GetEnd())
6345 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
6348 long breakPos
= textObj
->GetFirstLineBreakPosition(pos
);
6353 node
= node
->GetNext();
6360 * This object represents a line in a paragraph, and stores
6361 * offsets from the start of the paragraph representing the
6362 * start and end positions of the line.
6365 wxRichTextLine::wxRichTextLine(wxRichTextParagraph
* parent
)
6371 void wxRichTextLine::Init(wxRichTextParagraph
* parent
)
6374 m_range
.SetRange(-1, -1);
6375 m_pos
= wxPoint(0, 0);
6376 m_size
= wxSize(0, 0);
6378 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6379 m_objectSizes
.Clear();
6384 void wxRichTextLine::Copy(const wxRichTextLine
& obj
)
6386 m_range
= obj
.m_range
;
6387 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6388 m_objectSizes
= obj
.m_objectSizes
;
6392 /// Get the absolute object position
6393 wxPoint
wxRichTextLine::GetAbsolutePosition() const
6395 return m_parent
->GetPosition() + m_pos
;
6398 /// Get the absolute range
6399 wxRichTextRange
wxRichTextLine::GetAbsoluteRange() const
6401 wxRichTextRange
range(m_range
.GetStart() + m_parent
->GetRange().GetStart(), 0);
6402 range
.SetEnd(range
.GetStart() + m_range
.GetLength()-1);
6407 * wxRichTextPlainText
6408 * This object represents a single piece of text.
6411 IMPLEMENT_DYNAMIC_CLASS(wxRichTextPlainText
, wxRichTextObject
)
6413 wxRichTextPlainText::wxRichTextPlainText(const wxString
& text
, wxRichTextObject
* parent
, wxRichTextAttr
* style
):
6414 wxRichTextObject(parent
)
6417 SetAttributes(*style
);
6422 #define USE_KERNING_FIX 1
6424 // If insufficient tabs are defined, this is the tab width used
6425 #define WIDTH_FOR_DEFAULT_TABS 50
6428 bool wxRichTextPlainText::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int WXUNUSED(style
))
6430 wxRichTextParagraph
* para
= wxDynamicCast(GetParent(), wxRichTextParagraph
);
6431 wxASSERT (para
!= NULL
);
6433 wxRichTextAttr
textAttr(para
? para
->GetCombinedAttributes(GetAttributes(), false /* no box attributes */) : GetAttributes());
6434 context
.ApplyVirtualAttributes(textAttr
, this);
6436 // Let's make the assumption for now that for content in a paragraph, including
6437 // text, we never have a discontinuous selection. So we only deal with a
6439 wxRichTextRange selectionRange
;
6440 if (selection
.IsValid())
6442 wxRichTextRangeArray selectionRanges
= selection
.GetSelectionForObject(this);
6443 if (selectionRanges
.GetCount() > 0)
6444 selectionRange
= selectionRanges
[0];
6446 selectionRange
= wxRICHTEXT_NO_SELECTION
;
6449 selectionRange
= wxRICHTEXT_NO_SELECTION
;
6451 int offset
= GetRange().GetStart();
6453 wxString str
= m_text
;
6454 if (context
.HasVirtualText(this))
6456 if (!context
.GetVirtualText(this, str
) || str
.Length() != m_text
.Length())
6460 // Replace line break characters with spaces
6461 wxString toRemove
= wxRichTextLineBreakChar
;
6462 str
.Replace(toRemove
, wxT(" "));
6463 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS
|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS
)))
6466 long len
= range
.GetLength();
6467 wxString stringChunk
= str
.Mid(range
.GetStart() - offset
, (size_t) len
);
6469 // Test for the optimized situations where all is selected, or none
6472 wxFont
textFont(GetBuffer()->GetFontTable().FindFont(textAttr
));
6473 wxCheckSetFont(dc
, textFont
);
6474 int charHeight
= dc
.GetCharHeight();
6477 if ( textFont
.IsOk() )
6479 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SMALL_CAPITALS
))
6481 textFont
.SetPointSize((int) (textFont
.GetPointSize()*0.75));
6482 wxCheckSetFont(dc
, textFont
);
6483 charHeight
= dc
.GetCharHeight();
6486 if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
) )
6488 if (textFont
.IsUsingSizeInPixels())
6490 double size
= static_cast<double>(textFont
.GetPixelSize().y
) / wxSCRIPT_MUL_FACTOR
;
6491 textFont
.SetPixelSize(wxSize(0, static_cast<int>(size
)));
6497 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6498 textFont
.SetPointSize(static_cast<int>(size
));
6502 wxCheckSetFont(dc
, textFont
);
6504 else if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) )
6506 if (textFont
.IsUsingSizeInPixels())
6508 double size
= static_cast<double>(textFont
.GetPixelSize().y
) / wxSCRIPT_MUL_FACTOR
;
6509 textFont
.SetPixelSize(wxSize(0, static_cast<int>(size
)));
6511 int sub_height
= static_cast<int>(static_cast<double>(charHeight
) / wxSCRIPT_MUL_FACTOR
);
6512 y
= rect
.y
+ (rect
.height
- sub_height
+ (descent
- m_descent
));
6516 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6517 textFont
.SetPointSize(static_cast<int>(size
));
6519 int sub_height
= static_cast<int>(static_cast<double>(charHeight
) / wxSCRIPT_MUL_FACTOR
);
6520 y
= rect
.y
+ (rect
.height
- sub_height
+ (descent
- m_descent
));
6522 wxCheckSetFont(dc
, textFont
);
6527 y
= rect
.y
+ (rect
.height
- charHeight
- (descent
- m_descent
));
6533 y
= rect
.y
+ (rect
.height
- charHeight
- (descent
- m_descent
));
6536 // TODO: new selection code
6538 // (a) All selected.
6539 if (selectionRange
.GetStart() <= range
.GetStart() && selectionRange
.GetEnd() >= range
.GetEnd())
6541 DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, true);
6543 // (b) None selected.
6544 else if (selectionRange
.GetEnd() < range
.GetStart() || selectionRange
.GetStart() > range
.GetEnd())
6546 // Draw all unselected
6547 DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, false);
6551 // (c) Part selected, part not
6552 // Let's draw unselected chunk, selected chunk, then unselected chunk.
6554 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6556 // 1. Initial unselected chunk, if any, up until start of selection.
6557 if (selectionRange
.GetStart() > range
.GetStart() && selectionRange
.GetStart() <= range
.GetEnd())
6559 int r1
= range
.GetStart();
6560 int s1
= selectionRange
.GetStart()-1;
6561 int fragmentLen
= s1
- r1
+ 1;
6562 if (fragmentLen
< 0)
6564 wxLogDebug(wxT("Mid(%d, %d"), (int)(r1
- offset
), (int)fragmentLen
);
6566 wxString stringFragment
= str
.Mid(r1
- offset
, fragmentLen
);
6568 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false);
6571 if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
)
6573 // Compensate for kerning difference
6574 wxString
stringFragment2(str
.Mid(r1
- offset
, fragmentLen
+1));
6575 wxString
stringFragment3(str
.Mid(r1
- offset
+ fragmentLen
, 1));
6577 wxCoord w1
, h1
, w2
, h2
, w3
, h3
;
6578 dc
.GetTextExtent(stringFragment
, & w1
, & h1
);
6579 dc
.GetTextExtent(stringFragment2
, & w2
, & h2
);
6580 dc
.GetTextExtent(stringFragment3
, & w3
, & h3
);
6582 int kerningDiff
= (w1
+ w3
) - w2
;
6583 x
= x
- kerningDiff
;
6588 // 2. Selected chunk, if any.
6589 if (selectionRange
.GetEnd() >= range
.GetStart())
6591 int s1
= wxMax(selectionRange
.GetStart(), range
.GetStart());
6592 int s2
= wxMin(selectionRange
.GetEnd(), range
.GetEnd());
6594 int fragmentLen
= s2
- s1
+ 1;
6595 if (fragmentLen
< 0)
6597 wxLogDebug(wxT("Mid(%d, %d"), (int)(s1
- offset
), (int)fragmentLen
);
6599 wxString stringFragment
= str
.Mid(s1
- offset
, fragmentLen
);
6601 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, true);
6604 if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
)
6606 // Compensate for kerning difference
6607 wxString
stringFragment2(str
.Mid(s1
- offset
, fragmentLen
+1));
6608 wxString
stringFragment3(str
.Mid(s1
- offset
+ fragmentLen
, 1));
6610 wxCoord w1
, h1
, w2
, h2
, w3
, h3
;
6611 dc
.GetTextExtent(stringFragment
, & w1
, & h1
);
6612 dc
.GetTextExtent(stringFragment2
, & w2
, & h2
);
6613 dc
.GetTextExtent(stringFragment3
, & w3
, & h3
);
6615 int kerningDiff
= (w1
+ w3
) - w2
;
6616 x
= x
- kerningDiff
;
6621 // 3. Remaining unselected chunk, if any
6622 if (selectionRange
.GetEnd() < range
.GetEnd())
6624 int s2
= wxMin(selectionRange
.GetEnd()+1, range
.GetEnd());
6625 int r2
= range
.GetEnd();
6627 int fragmentLen
= r2
- s2
+ 1;
6628 if (fragmentLen
< 0)
6630 wxLogDebug(wxT("Mid(%d, %d"), (int)(s2
- offset
), (int)fragmentLen
);
6632 wxString stringFragment
= str
.Mid(s2
- offset
, fragmentLen
);
6634 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false);
6641 bool wxRichTextPlainText::DrawTabbedString(wxDC
& dc
, const wxRichTextAttr
& attr
, const wxRect
& rect
,wxString
& str
, wxCoord
& x
, wxCoord
& y
, bool selected
)
6643 bool hasTabs
= (str
.Find(wxT('\t')) != wxNOT_FOUND
);
6645 wxArrayInt tabArray
;
6649 if (attr
.GetTabs().IsEmpty())
6650 tabArray
= wxRichTextParagraph::GetDefaultTabs();
6652 tabArray
= attr
.GetTabs();
6653 tabCount
= tabArray
.GetCount();
6655 for (int i
= 0; i
< tabCount
; ++i
)
6657 int pos
= tabArray
[i
];
6658 pos
= ConvertTenthsMMToPixels(dc
, pos
);
6665 int nextTabPos
= -1;
6671 wxColour
highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
));
6672 wxColour
highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
));
6674 wxCheckSetBrush(dc
, wxBrush(highlightColour
));
6675 wxCheckSetPen(dc
, wxPen(highlightColour
));
6676 dc
.SetTextForeground(highlightTextColour
);
6677 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6681 dc
.SetTextForeground(attr
.GetTextColour());
6683 if (attr
.HasFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
) && attr
.GetBackgroundColour().IsOk())
6685 dc
.SetBackgroundMode(wxBRUSHSTYLE_SOLID
);
6686 dc
.SetTextBackground(attr
.GetBackgroundColour());
6689 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6692 wxCoord x_orig
= GetParent()->GetPosition().x
;
6695 // the string has a tab
6696 // break up the string at the Tab
6697 wxString stringChunk
= str
.BeforeFirst(wxT('\t'));
6698 str
= str
.AfterFirst(wxT('\t'));
6699 dc
.GetTextExtent(stringChunk
, & w
, & h
);
6701 bool not_found
= true;
6702 for (int i
= 0; i
< tabCount
&& not_found
; ++i
)
6704 nextTabPos
= tabArray
.Item(i
) + x_orig
;
6706 // Find the next tab position.
6707 // Even if we're at the end of the tab array, we must still draw the chunk.
6709 if (nextTabPos
> tabPos
|| (i
== (tabCount
- 1)))
6711 if (nextTabPos
<= tabPos
)
6713 int defaultTabWidth
= ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
);
6714 nextTabPos
= tabPos
+ defaultTabWidth
;
6721 wxRect
selRect(x
, rect
.y
, w
, rect
.GetHeight());
6722 dc
.DrawRectangle(selRect
);
6724 dc
.DrawText(stringChunk
, x
, y
);
6726 if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
))
6728 wxPen oldPen
= dc
.GetPen();
6729 wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1));
6730 dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5));
6731 wxCheckSetPen(dc
, oldPen
);
6737 hasTabs
= (str
.Find(wxT('\t')) != wxNOT_FOUND
);
6742 dc
.GetTextExtent(str
, & w
, & h
);
6745 wxRect
selRect(x
, rect
.y
, w
, rect
.GetHeight());
6746 dc
.DrawRectangle(selRect
);
6748 dc
.DrawText(str
, x
, y
);
6750 if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
))
6752 wxPen oldPen
= dc
.GetPen();
6753 wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1));
6754 dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5));
6755 wxCheckSetPen(dc
, oldPen
);
6764 /// Lay the item out
6765 bool wxRichTextPlainText::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& WXUNUSED(rect
), const wxRect
& WXUNUSED(parentRect
), int WXUNUSED(style
))
6767 // Only lay out if we haven't already cached the size
6769 GetRangeSize(GetRange(), m_size
, m_descent
, dc
, context
, 0, wxPoint(0, 0));
6771 // Eventually we want to have a reasonable estimate of minimum size.
6772 m_minSize
= wxSize(0, 0);
6777 void wxRichTextPlainText::Copy(const wxRichTextPlainText
& obj
)
6779 wxRichTextObject::Copy(obj
);
6781 m_text
= obj
.m_text
;
6784 /// Get/set the object size for the given range. Returns false if the range
6785 /// is invalid for this object.
6786 bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int WXUNUSED(flags
), const wxPoint
& position
, const wxSize
& WXUNUSED(parentSize
), wxArrayInt
* partialExtents
) const
6788 if (!range
.IsWithin(GetRange()))
6791 wxRichTextParagraph
* para
= wxDynamicCast(GetParent(), wxRichTextParagraph
);
6792 wxASSERT (para
!= NULL
);
6794 int relativeX
= position
.x
- GetParent()->GetPosition().x
;
6796 wxRichTextAttr
textAttr(para
? para
->GetCombinedAttributes(GetAttributes()) : GetAttributes());
6797 context
.ApplyVirtualAttributes(textAttr
, (wxRichTextObject
*) this);
6799 // Always assume unformatted text, since at this level we have no knowledge
6800 // of line breaks - and we don't need it, since we'll calculate size within
6801 // formatted text by doing it in chunks according to the line ranges
6803 bool bScript(false);
6804 wxFont
font(GetBuffer()->GetFontTable().FindFont(textAttr
));
6807 if ( textAttr
.HasTextEffects() && ( (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
)
6808 || (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) ) )
6810 wxFont textFont
= font
;
6811 if (textFont
.IsUsingSizeInPixels())
6813 double size
= static_cast<double>(textFont
.GetPixelSize().y
) / wxSCRIPT_MUL_FACTOR
;
6814 textFont
.SetPixelSize(wxSize(0, static_cast<int>(size
)));
6818 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6819 textFont
.SetPointSize(static_cast<int>(size
));
6821 wxCheckSetFont(dc
, textFont
);
6824 else if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SMALL_CAPITALS
))
6826 wxFont textFont
= font
;
6827 textFont
.SetPointSize((int) (textFont
.GetPointSize()*0.75));
6828 wxCheckSetFont(dc
, textFont
);
6833 wxCheckSetFont(dc
, font
);
6837 bool haveDescent
= false;
6838 int startPos
= range
.GetStart() - GetRange().GetStart();
6839 long len
= range
.GetLength();
6841 wxString
str(m_text
);
6842 if (context
.HasVirtualText(this))
6844 if (!context
.GetVirtualText(this, str
) || str
.Length() != m_text
.Length())
6848 wxString toReplace
= wxRichTextLineBreakChar
;
6849 str
.Replace(toReplace
, wxT(" "));
6851 wxString stringChunk
= str
.Mid(startPos
, (size_t) len
);
6853 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS
|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS
)))
6854 stringChunk
.MakeUpper();
6858 if (stringChunk
.Find(wxT('\t')) != wxNOT_FOUND
)
6860 // the string has a tab
6861 wxArrayInt tabArray
;
6862 if (textAttr
.GetTabs().IsEmpty())
6863 tabArray
= wxRichTextParagraph::GetDefaultTabs();
6865 tabArray
= textAttr
.GetTabs();
6867 int tabCount
= tabArray
.GetCount();
6869 for (int i
= 0; i
< tabCount
; ++i
)
6871 int pos
= tabArray
[i
];
6872 pos
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, pos
);
6876 int nextTabPos
= -1;
6878 while (stringChunk
.Find(wxT('\t')) >= 0)
6880 int absoluteWidth
= 0;
6882 // the string has a tab
6883 // break up the string at the Tab
6884 wxString stringFragment
= stringChunk
.BeforeFirst(wxT('\t'));
6885 stringChunk
= stringChunk
.AfterFirst(wxT('\t'));
6890 if (partialExtents
->GetCount() > 0)
6891 oldWidth
= (*partialExtents
)[partialExtents
->GetCount()-1];
6895 // Add these partial extents
6897 dc
.GetPartialTextExtents(stringFragment
, p
);
6899 for (j
= 0; j
< p
.GetCount(); j
++)
6900 partialExtents
->Add(oldWidth
+ p
[j
]);
6902 if (partialExtents
->GetCount() > 0)
6903 absoluteWidth
= (*partialExtents
)[(*partialExtents
).GetCount()-1] + relativeX
;
6905 absoluteWidth
= relativeX
;
6909 dc
.GetTextExtent(stringFragment
, & w
, & h
);
6911 absoluteWidth
= width
+ relativeX
;
6915 bool notFound
= true;
6916 for (int i
= 0; i
< tabCount
&& notFound
; ++i
)
6918 nextTabPos
= tabArray
.Item(i
);
6920 // Find the next tab position.
6921 // Even if we're at the end of the tab array, we must still process the chunk.
6923 if (nextTabPos
> absoluteWidth
|| (i
== (tabCount
- 1)))
6925 if (nextTabPos
<= absoluteWidth
)
6927 int defaultTabWidth
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
);
6928 nextTabPos
= absoluteWidth
+ defaultTabWidth
;
6932 width
= nextTabPos
- relativeX
;
6935 partialExtents
->Add(width
);
6941 if (!stringChunk
.IsEmpty())
6946 if (partialExtents
->GetCount() > 0)
6947 oldWidth
= (*partialExtents
)[partialExtents
->GetCount()-1];
6951 // Add these partial extents
6953 dc
.GetPartialTextExtents(stringChunk
, p
);
6955 for (j
= 0; j
< p
.GetCount(); j
++)
6956 partialExtents
->Add(oldWidth
+ p
[j
]);
6960 dc
.GetTextExtent(stringChunk
, & w
, & h
, & descent
);
6968 int charHeight
= dc
.GetCharHeight();
6969 if ((*partialExtents
).GetCount() > 0)
6970 w
= (*partialExtents
)[partialExtents
->GetCount()-1];
6973 size
= wxSize(w
, charHeight
);
6977 size
= wxSize(width
, dc
.GetCharHeight());
6981 dc
.GetTextExtent(wxT("X"), & w
, & h
, & descent
);
6989 /// Do a split, returning an object containing the second part, and setting
6990 /// the first part in 'this'.
6991 wxRichTextObject
* wxRichTextPlainText::DoSplit(long pos
)
6993 long index
= pos
- GetRange().GetStart();
6995 if (index
< 0 || index
>= (int) m_text
.length())
6998 wxString firstPart
= m_text
.Mid(0, index
);
6999 wxString secondPart
= m_text
.Mid(index
);
7003 wxRichTextPlainText
* newObject
= new wxRichTextPlainText(secondPart
);
7004 newObject
->SetAttributes(GetAttributes());
7005 newObject
->SetProperties(GetProperties());
7007 newObject
->SetRange(wxRichTextRange(pos
, GetRange().GetEnd()));
7008 GetRange().SetEnd(pos
-1);
7014 void wxRichTextPlainText::CalculateRange(long start
, long& end
)
7016 end
= start
+ m_text
.length() - 1;
7017 m_range
.SetRange(start
, end
);
7021 bool wxRichTextPlainText::DeleteRange(const wxRichTextRange
& range
)
7023 wxRichTextRange r
= range
;
7025 r
.LimitTo(GetRange());
7027 if (r
.GetStart() == GetRange().GetStart() && r
.GetEnd() == GetRange().GetEnd())
7033 long startIndex
= r
.GetStart() - GetRange().GetStart();
7034 long len
= r
.GetLength();
7036 m_text
= m_text
.Mid(0, startIndex
) + m_text
.Mid(startIndex
+len
);
7040 /// Get text for the given range.
7041 wxString
wxRichTextPlainText::GetTextForRange(const wxRichTextRange
& range
) const
7043 wxRichTextRange r
= range
;
7045 r
.LimitTo(GetRange());
7047 long startIndex
= r
.GetStart() - GetRange().GetStart();
7048 long len
= r
.GetLength();
7050 return m_text
.Mid(startIndex
, len
);
7053 /// Returns true if this object can merge itself with the given one.
7054 bool wxRichTextPlainText::CanMerge(wxRichTextObject
* object
, wxRichTextDrawingContext
& context
) const
7057 if (!context
.GetVirtualAttributesEnabled())
7059 return object
->GetClassInfo() == wxCLASSINFO(wxRichTextPlainText
) &&
7060 (m_text
.empty() || (wxTextAttrEq(GetAttributes(), object
->GetAttributes()) && m_properties
== object
->GetProperties()));
7064 wxRichTextPlainText
* otherObj
= wxDynamicCast(object
, wxRichTextPlainText
);
7065 if (!otherObj
|| m_text
.empty())
7068 if (!wxTextAttrEq(GetAttributes(), object
->GetAttributes()) || !(m_properties
== object
->GetProperties()))
7071 // Check if differing virtual attributes makes it impossible to merge
7074 bool hasVirtualAttr1
= context
.HasVirtualAttributes((wxRichTextObject
*) this);
7075 bool hasVirtualAttr2
= context
.HasVirtualAttributes((wxRichTextObject
*) object
);
7076 if (!hasVirtualAttr1
&& !hasVirtualAttr2
)
7078 else if (hasVirtualAttr1
!= hasVirtualAttr2
)
7082 wxRichTextAttr virtualAttr1
= context
.GetVirtualAttributes((wxRichTextObject
*) this);
7083 wxRichTextAttr virtualAttr2
= context
.GetVirtualAttributes((wxRichTextObject
*) object
);
7084 return virtualAttr1
== virtualAttr2
;
7089 /// Returns true if this object merged itself with the given one.
7090 /// The calling code will then delete the given object.
7091 bool wxRichTextPlainText::Merge(wxRichTextObject
* object
, wxRichTextDrawingContext
& WXUNUSED(context
))
7093 wxRichTextPlainText
* textObject
= wxDynamicCast(object
, wxRichTextPlainText
);
7094 wxASSERT( textObject
!= NULL
);
7098 m_text
+= textObject
->GetText();
7099 wxRichTextApplyStyle(m_attributes
, textObject
->GetAttributes());
7106 bool wxRichTextPlainText::CanSplit(wxRichTextDrawingContext
& context
) const
7108 // If this object has any virtual attributes at all, whether for the whole object
7109 // or individual ones, we should try splitting it by calling Split.
7110 // Must be more than one character in order to be able to split.
7111 return m_text
.Length() > 1 && context
.HasVirtualAttributes((wxRichTextObject
*) this);
7114 wxRichTextObject
* wxRichTextPlainText::Split(wxRichTextDrawingContext
& context
)
7116 int count
= context
.GetVirtualSubobjectAttributesCount(this);
7117 if (count
> 0 && GetParent())
7119 wxRichTextCompositeObject
* parent
= wxDynamicCast(GetParent(), wxRichTextCompositeObject
);
7120 wxRichTextObjectList::compatibility_iterator node
= parent
->GetChildren().Find(this);
7123 const wxRichTextAttr emptyAttr
;
7124 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
7126 wxArrayInt positions
;
7127 wxRichTextAttrArray attributes
;
7128 if (context
.GetVirtualSubobjectAttributes(this, positions
, attributes
) && positions
.GetCount() > 0)
7130 wxASSERT(positions
.GetCount() == attributes
.GetCount());
7132 // We will gather up runs of text with the same virtual attributes
7134 int len
= m_text
.Length();
7137 // runStart and runEnd represent the accumulated run with a consistent attribute
7138 // that hasn't yet been appended
7141 wxRichTextAttr currentAttr
;
7142 wxString text
= m_text
;
7143 wxRichTextPlainText
* lastPlainText
= this;
7145 for (i
= 0; i
< (int) positions
.GetCount(); i
++)
7147 int pos
= positions
[i
];
7148 wxASSERT(pos
>= 0 && pos
< len
);
7149 if (pos
>= 0 && pos
< len
)
7151 const wxRichTextAttr
& attr
= attributes
[i
];
7158 // Check if there was a gap from the last known attribute and this.
7159 // In that case, we need to do something with the span of non-attributed text.
7160 else if ((pos
-1) > runEnd
)
7164 // We hadn't processed anything previously, so the previous run is from the text start
7165 // to just before this position. The current attribute remains empty.
7171 // If the previous attribute matches the gap's attribute (i.e., no attributes)
7172 // then just extend the run.
7173 if (currentAttr
.IsDefault())
7179 // We need to add an object, or reuse the existing one.
7182 lastPlainText
= this;
7183 SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7187 wxRichTextPlainText
* obj
= new wxRichTextPlainText
;
7188 lastPlainText
= obj
;
7189 obj
->SetAttributes(GetAttributes());
7190 obj
->SetProperties(GetProperties());
7191 obj
->SetParent(parent
);
7193 obj
->SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7195 parent
->GetChildren().Insert(next
, obj
);
7197 parent
->GetChildren().Append(obj
);
7200 runStart
= runEnd
+1;
7203 currentAttr
= emptyAttr
;
7208 wxASSERT(runEnd
== pos
-1);
7210 // Now we only have to deal with the previous run
7211 if (currentAttr
== attr
)
7213 // If we still have the same attributes, then we
7214 // simply increase the run size.
7221 // We need to add an object, or reuse the existing one.
7224 lastPlainText
= this;
7225 SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7229 wxRichTextPlainText
* obj
= new wxRichTextPlainText
;
7230 lastPlainText
= obj
;
7231 obj
->SetAttributes(GetAttributes());
7232 obj
->SetProperties(GetProperties());
7233 obj
->SetParent(parent
);
7235 obj
->SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7237 parent
->GetChildren().Insert(next
, obj
);
7239 parent
->GetChildren().Append(obj
);
7251 // We may still have a run to add, and possibly a no-attribute text fragment after that.
7252 // If the whole string was already a single attribute (the run covers the whole string), don't split.
7253 if ((runStart
!= -1) && !(runStart
== 0 && runEnd
== (len
-1)))
7255 // If the current attribute is empty, merge the run with the next fragment
7256 // which by definition (because it's not specified) has empty attributes.
7257 if (currentAttr
.IsDefault())
7260 if (runEnd
< (len
-1))
7262 // We need to add an object, or reuse the existing one.
7265 lastPlainText
= this;
7266 SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7270 wxRichTextPlainText
* obj
= new wxRichTextPlainText
;
7271 lastPlainText
= obj
;
7272 obj
->SetAttributes(GetAttributes());
7273 obj
->SetProperties(GetProperties());
7274 obj
->SetParent(parent
);
7276 obj
->SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7278 parent
->GetChildren().Insert(next
, obj
);
7280 parent
->GetChildren().Append(obj
);
7283 runStart
= runEnd
+1;
7287 // Now the last, non-attributed fragment at the end, if any
7288 if ((runStart
< len
) && !(runStart
== 0 && runEnd
== (len
-1)))
7290 wxASSERT(runStart
!= 0);
7292 wxRichTextPlainText
* obj
= new wxRichTextPlainText
;
7293 obj
->SetAttributes(GetAttributes());
7294 obj
->SetProperties(GetProperties());
7295 obj
->SetParent(parent
);
7297 obj
->SetText(text
.Mid(runStart
, runEnd
- runStart
+ 1));
7299 parent
->GetChildren().Insert(next
, obj
);
7301 parent
->GetChildren().Append(obj
);
7303 lastPlainText
= obj
;
7307 return lastPlainText
;
7314 /// Dump to output stream for debugging
7315 void wxRichTextPlainText::Dump(wxTextOutputStream
& stream
)
7317 wxRichTextObject::Dump(stream
);
7318 stream
<< m_text
<< wxT("\n");
7321 /// Get the first position from pos that has a line break character.
7322 long wxRichTextPlainText::GetFirstLineBreakPosition(long pos
)
7325 int len
= m_text
.length();
7326 int startPos
= pos
- m_range
.GetStart();
7327 for (i
= startPos
; i
< len
; i
++)
7329 wxChar ch
= m_text
[i
];
7330 if (ch
== wxRichTextLineBreakChar
)
7332 return i
+ m_range
.GetStart();
7340 * This is a kind of box, used to represent the whole buffer
7343 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBuffer
, wxRichTextParagraphLayoutBox
)
7345 wxList
wxRichTextBuffer::sm_handlers
;
7346 wxList
wxRichTextBuffer::sm_drawingHandlers
;
7347 wxRichTextFieldTypeHashMap
wxRichTextBuffer::sm_fieldTypes
;
7348 wxRichTextRenderer
* wxRichTextBuffer::sm_renderer
= NULL
;
7349 int wxRichTextBuffer::sm_bulletRightMargin
= 20;
7350 float wxRichTextBuffer::sm_bulletProportion
= (float) 0.3;
7351 bool wxRichTextBuffer::sm_floatingLayoutMode
= true;
7354 void wxRichTextBuffer::Init()
7356 m_commandProcessor
= new wxCommandProcessor
;
7357 m_styleSheet
= NULL
;
7359 m_batchedCommandDepth
= 0;
7360 m_batchedCommand
= NULL
;
7364 m_dimensionScale
= 1.0;
7370 wxRichTextBuffer::~wxRichTextBuffer()
7372 delete m_commandProcessor
;
7373 delete m_batchedCommand
;
7376 ClearEventHandlers();
7379 void wxRichTextBuffer::ResetAndClearCommands()
7383 GetCommandProcessor()->ClearCommands();
7386 Invalidate(wxRICHTEXT_ALL
);
7389 void wxRichTextBuffer::Copy(const wxRichTextBuffer
& obj
)
7391 wxRichTextParagraphLayoutBox::Copy(obj
);
7393 m_styleSheet
= obj
.m_styleSheet
;
7394 m_modified
= obj
.m_modified
;
7395 m_batchedCommandDepth
= 0;
7396 if (m_batchedCommand
)
7397 delete m_batchedCommand
;
7398 m_batchedCommand
= NULL
;
7399 m_suppressUndo
= obj
.m_suppressUndo
;
7400 m_invalidRange
= obj
.m_invalidRange
;
7401 m_dimensionScale
= obj
.m_dimensionScale
;
7402 m_fontScale
= obj
.m_fontScale
;
7405 /// Push style sheet to top of stack
7406 bool wxRichTextBuffer::PushStyleSheet(wxRichTextStyleSheet
* styleSheet
)
7409 styleSheet
->InsertSheet(m_styleSheet
);
7411 SetStyleSheet(styleSheet
);
7416 /// Pop style sheet from top of stack
7417 wxRichTextStyleSheet
* wxRichTextBuffer::PopStyleSheet()
7421 wxRichTextStyleSheet
* oldSheet
= m_styleSheet
;
7422 m_styleSheet
= oldSheet
->GetNextSheet();
7431 /// Submit command to insert paragraphs
7432 bool wxRichTextBuffer::InsertParagraphsWithUndo(long pos
, const wxRichTextParagraphLayoutBox
& paragraphs
, wxRichTextCtrl
* ctrl
, int flags
)
7434 return ctrl
->GetFocusObject()->InsertParagraphsWithUndo(this, pos
, paragraphs
, ctrl
, flags
);
7437 /// Submit command to insert paragraphs
7438 bool wxRichTextParagraphLayoutBox::InsertParagraphsWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxRichTextParagraphLayoutBox
& paragraphs
, wxRichTextCtrl
* ctrl
, int WXUNUSED(flags
))
7440 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7442 action
->GetNewParagraphs() = paragraphs
;
7444 action
->SetPosition(pos
);
7446 wxRichTextRange range
= wxRichTextRange(pos
, pos
+ paragraphs
.GetOwnRange().GetEnd() - 1);
7447 if (!paragraphs
.GetPartialParagraph())
7448 range
.SetEnd(range
.GetEnd()+1);
7450 // Set the range we'll need to delete in Undo
7451 action
->SetRange(range
);
7453 buffer
->SubmitAction(action
);
7458 /// Submit command to insert the given text
7459 bool wxRichTextBuffer::InsertTextWithUndo(long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
, int flags
)
7462 return ctrl
->GetFocusObject()->InsertTextWithUndo(this, pos
, text
, ctrl
, flags
);
7464 return wxRichTextParagraphLayoutBox::InsertTextWithUndo(this, pos
, text
, ctrl
, flags
);
7467 /// Submit command to insert the given text
7468 bool wxRichTextParagraphLayoutBox::InsertTextWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
, int flags
)
7470 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7472 wxRichTextAttr
* p
= NULL
;
7473 wxRichTextAttr paraAttr
;
7474 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7476 // Get appropriate paragraph style
7477 paraAttr
= GetStyleForNewParagraph(buffer
, pos
, false, false);
7478 if (!paraAttr
.IsDefault())
7482 action
->GetNewParagraphs().AddParagraphs(text
, p
);
7484 int length
= action
->GetNewParagraphs().GetOwnRange().GetLength();
7486 if (!text
.empty() && text
.Last() != wxT('\n'))
7488 // Don't count the newline when undoing
7490 action
->GetNewParagraphs().SetPartialParagraph(true);
7492 else if (!text
.empty() && text
.Last() == wxT('\n'))
7495 action
->SetPosition(pos
);
7497 // Set the range we'll need to delete in Undo
7498 action
->SetRange(wxRichTextRange(pos
, pos
+ length
- 1));
7500 buffer
->SubmitAction(action
);
7505 /// Submit command to insert the given text
7506 bool wxRichTextBuffer::InsertNewlineWithUndo(long pos
, wxRichTextCtrl
* ctrl
, int flags
)
7508 return ctrl
->GetFocusObject()->InsertNewlineWithUndo(this, pos
, ctrl
, flags
);
7511 /// Submit command to insert the given text
7512 bool wxRichTextParagraphLayoutBox::InsertNewlineWithUndo(wxRichTextBuffer
* buffer
, long pos
, wxRichTextCtrl
* ctrl
, int flags
)
7514 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7516 wxRichTextAttr
* p
= NULL
;
7517 wxRichTextAttr paraAttr
;
7518 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7520 paraAttr
= GetStyleForNewParagraph(buffer
, pos
, false, true /* look for next paragraph style */);
7521 if (!paraAttr
.IsDefault())
7525 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7526 // Don't include box attributes such as margins
7527 attr
.GetTextBoxAttr().Reset();
7529 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(wxEmptyString
, this, & attr
);
7530 action
->GetNewParagraphs().AppendChild(newPara
);
7531 action
->GetNewParagraphs().UpdateRanges();
7532 action
->GetNewParagraphs().SetPartialParagraph(false);
7533 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
, false);
7537 newPara
->SetAttributes(*p
);
7539 if (flags
& wxRICHTEXT_INSERT_INTERACTIVE
)
7541 if (para
&& para
->GetRange().GetEnd() == pos
)
7544 // Now see if we need to number the paragraph.
7545 if (newPara
->GetAttributes().HasBulletNumber())
7547 wxRichTextAttr numberingAttr
;
7548 if (FindNextParagraphNumber(para
, numberingAttr
))
7549 wxRichTextApplyStyle(newPara
->GetAttributes(), (const wxRichTextAttr
&) numberingAttr
);
7553 action
->SetPosition(pos
);
7555 // Use the default character style
7556 if (!buffer
->GetDefaultStyle().IsDefault() && newPara
->GetChildren().GetFirst())
7558 // Check whether the default style merely reflects the paragraph/basic style,
7559 // in which case don't apply it.
7560 wxRichTextAttr
defaultStyle(buffer
->GetDefaultStyle());
7561 defaultStyle
.GetTextBoxAttr().Reset();
7562 wxRichTextAttr toApply
;
7565 wxRichTextAttr combinedAttr
= para
->GetCombinedAttributes(true /* include box attributes */);
7566 wxRichTextAttr newAttr
;
7567 // This filters out attributes that are accounted for by the current
7568 // paragraph/basic style
7569 wxRichTextApplyStyle(toApply
, defaultStyle
, & combinedAttr
);
7572 toApply
= defaultStyle
;
7574 if (!toApply
.IsDefault())
7575 newPara
->GetChildren().GetFirst()->GetData()->SetAttributes(toApply
);
7578 // Set the range we'll need to delete in Undo
7579 action
->SetRange(wxRichTextRange(pos1
, pos1
));
7581 buffer
->SubmitAction(action
);
7586 /// Submit command to insert the given image
7587 bool wxRichTextBuffer::InsertImageWithUndo(long pos
, const wxRichTextImageBlock
& imageBlock
, wxRichTextCtrl
* ctrl
, int flags
,
7588 const wxRichTextAttr
& textAttr
)
7590 return ctrl
->GetFocusObject()->InsertImageWithUndo(this, pos
, imageBlock
, ctrl
, flags
, textAttr
);
7593 /// Submit command to insert the given image
7594 bool wxRichTextParagraphLayoutBox::InsertImageWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxRichTextImageBlock
& imageBlock
,
7595 wxRichTextCtrl
* ctrl
, int flags
,
7596 const wxRichTextAttr
& textAttr
)
7598 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7600 wxRichTextAttr
* p
= NULL
;
7601 wxRichTextAttr paraAttr
;
7602 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7604 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
7605 if (!paraAttr
.IsDefault())
7609 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7611 // Don't include box attributes such as margins
7612 attr
.GetTextBoxAttr().Reset();
7614 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7616 newPara
->SetAttributes(*p
);
7618 wxRichTextImage
* imageObject
= new wxRichTextImage(imageBlock
, newPara
);
7619 newPara
->AppendChild(imageObject
);
7620 imageObject
->SetAttributes(textAttr
);
7621 action
->GetNewParagraphs().AppendChild(newPara
);
7622 action
->GetNewParagraphs().UpdateRanges();
7624 action
->GetNewParagraphs().SetPartialParagraph(true);
7626 action
->SetPosition(pos
);
7628 // Set the range we'll need to delete in Undo
7629 action
->SetRange(wxRichTextRange(pos
, pos
));
7631 buffer
->SubmitAction(action
);
7636 // Insert an object with no change of it
7637 wxRichTextObject
* wxRichTextBuffer::InsertObjectWithUndo(long pos
, wxRichTextObject
*object
, wxRichTextCtrl
* ctrl
, int flags
)
7639 return ctrl
->GetFocusObject()->InsertObjectWithUndo(this, pos
, object
, ctrl
, flags
);
7642 // Insert an object with no change of it
7643 wxRichTextObject
* wxRichTextParagraphLayoutBox::InsertObjectWithUndo(wxRichTextBuffer
* buffer
, long pos
, wxRichTextObject
*object
, wxRichTextCtrl
* ctrl
, int flags
)
7645 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Object"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7647 wxRichTextAttr
* p
= NULL
;
7648 wxRichTextAttr paraAttr
;
7649 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7651 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
7652 if (!paraAttr
.IsDefault())
7656 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7658 // Don't include box attributes such as margins
7659 attr
.GetTextBoxAttr().Reset();
7661 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7663 newPara
->SetAttributes(*p
);
7665 newPara
->AppendChild(object
);
7666 action
->GetNewParagraphs().AppendChild(newPara
);
7667 action
->GetNewParagraphs().UpdateRanges();
7669 action
->GetNewParagraphs().SetPartialParagraph(true);
7671 action
->SetPosition(pos
);
7673 // Set the range we'll need to delete in Undo
7674 action
->SetRange(wxRichTextRange(pos
, pos
));
7676 buffer
->SubmitAction(action
);
7678 wxRichTextObject
* obj
= GetLeafObjectAtPosition(pos
);
7682 wxRichTextField
* wxRichTextParagraphLayoutBox::InsertFieldWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxString
& fieldType
,
7683 const wxRichTextProperties
& properties
,
7684 wxRichTextCtrl
* ctrl
, int flags
,
7685 const wxRichTextAttr
& textAttr
)
7687 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Field"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7689 wxRichTextAttr
* p
= NULL
;
7690 wxRichTextAttr paraAttr
;
7691 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7693 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
7694 if (!paraAttr
.IsDefault())
7698 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7700 // Don't include box attributes such as margins
7701 attr
.GetTextBoxAttr().Reset();
7703 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7705 newPara
->SetAttributes(*p
);
7707 wxRichTextField
* fieldObject
= new wxRichTextField();
7708 fieldObject
->wxRichTextObject::SetProperties(properties
);
7709 fieldObject
->SetFieldType(fieldType
);
7710 fieldObject
->SetAttributes(textAttr
);
7711 newPara
->AppendChild(fieldObject
);
7712 action
->GetNewParagraphs().AppendChild(newPara
);
7713 action
->GetNewParagraphs().UpdateRanges();
7714 action
->GetNewParagraphs().SetPartialParagraph(true);
7715 action
->SetPosition(pos
);
7717 // Set the range we'll need to delete in Undo
7718 action
->SetRange(wxRichTextRange(pos
, pos
));
7720 buffer
->SubmitAction(action
);
7722 wxRichTextField
* obj
= wxDynamicCast(GetLeafObjectAtPosition(pos
), wxRichTextField
);
7726 bool wxRichTextParagraphLayoutBox::SetObjectPropertiesWithUndo(wxRichTextObject
& obj
, const wxRichTextProperties
& properties
)
7728 wxRichTextBuffer
* buffer
= GetBuffer();
7729 wxCHECK_MSG(buffer
, false, wxT("Invalid buffer"));
7730 wxRichTextCtrl
* rtc
= buffer
->GetRichTextCtrl();
7731 wxCHECK_MSG(rtc
, false, wxT("Invalid rtc"));
7733 wxRichTextAction
* action
= NULL
;
7734 wxRichTextObject
* clone
= NULL
;
7737 if (rtc
->SuppressingUndo())
7738 obj
.SetProperties(properties
);
7741 clone
= obj
.Clone();
7742 clone
->SetProperties(obj
.GetProperties());
7743 action
= new wxRichTextAction(NULL
, _("Change Properties"), wxRICHTEXT_CHANGE_OBJECT
, buffer
, obj
.GetParentContainer(), rtc
);
7744 action
->SetOldAndNewObjects(& obj
, clone
);
7745 action
->SetPosition(obj
.GetRange().GetStart());
7746 action
->SetRange(obj
.GetRange());
7747 buffer
->SubmitAction(action
);
7750 if (!rtc
->SuppressingUndo())
7752 // Create a clone containing the current state of the object. It will be used to Undo the action
7753 clone
= obj
.Clone();
7754 clone
->SetParent(obj
.GetParent());
7755 action
= new wxRichTextAction(NULL
, _("Change Properties"), wxRICHTEXT_CHANGE_OBJECT
, buffer
, rtc
->GetFocusObject(), rtc
);
7756 action
->SetObject(&obj
);
7757 action
->SetPosition(GetRange().GetStart());
7760 obj
.SetProperties(properties
);
7762 if (!rtc
->SuppressingUndo())
7764 buffer
->SubmitAction(action
);
7765 // Finally store the original-state clone; doing so earlier would cause various failures
7766 action
->StoreObject(clone
);
7773 /// Get the style that is appropriate for a new paragraph at this position.
7774 /// If the previous paragraph has a paragraph style name, look up the next-paragraph
7776 wxRichTextAttr
wxRichTextParagraphLayoutBox::GetStyleForNewParagraph(wxRichTextBuffer
* buffer
, long pos
, bool caretPosition
, bool lookUpNewParaStyle
) const
7778 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
, caretPosition
);
7781 wxRichTextAttr attr
;
7782 bool foundAttributes
= false;
7784 // Look for a matching paragraph style
7785 if (lookUpNewParaStyle
&& !para
->GetAttributes().GetParagraphStyleName().IsEmpty() && buffer
->GetStyleSheet())
7787 wxRichTextParagraphStyleDefinition
* paraDef
= buffer
->GetStyleSheet()->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
7790 // If we're not at the end of the paragraph, then we apply THIS style, and not the designated next style.
7791 if (para
->GetRange().GetEnd() == pos
&& !paraDef
->GetNextStyle().IsEmpty())
7793 wxRichTextParagraphStyleDefinition
* nextParaDef
= buffer
->GetStyleSheet()->FindParagraphStyle(paraDef
->GetNextStyle());
7796 foundAttributes
= true;
7797 attr
= nextParaDef
->GetStyleMergedWithBase(buffer
->GetStyleSheet());
7801 // If we didn't find the 'next style', use this style instead.
7802 if (!foundAttributes
)
7804 foundAttributes
= true;
7805 attr
= paraDef
->GetStyleMergedWithBase(buffer
->GetStyleSheet());
7810 // Also apply list style if present
7811 if (lookUpNewParaStyle
&& !para
->GetAttributes().GetListStyleName().IsEmpty() && buffer
->GetStyleSheet())
7813 wxRichTextListStyleDefinition
* listDef
= buffer
->GetStyleSheet()->FindListStyle(para
->GetAttributes().GetListStyleName());
7816 int thisIndent
= para
->GetAttributes().GetLeftIndent();
7817 int thisLevel
= para
->GetAttributes().HasOutlineLevel() ? para
->GetAttributes().GetOutlineLevel() : listDef
->FindLevelForIndent(thisIndent
);
7819 // Apply the overall list style, and item style for this level
7820 wxRichTextAttr
listStyle(listDef
->GetCombinedStyleForLevel(thisLevel
, buffer
->GetStyleSheet()));
7821 wxRichTextApplyStyle(attr
, listStyle
);
7822 attr
.SetOutlineLevel(thisLevel
);
7823 if (para
->GetAttributes().HasBulletNumber())
7824 attr
.SetBulletNumber(para
->GetAttributes().GetBulletNumber());
7828 if (!foundAttributes
)
7830 attr
= para
->GetAttributes();
7831 int flags
= attr
.GetFlags();
7833 // Eliminate character styles
7834 flags
&= ( (~ wxTEXT_ATTR_FONT
) |
7835 (~ wxTEXT_ATTR_TEXT_COLOUR
) |
7836 (~ wxTEXT_ATTR_BACKGROUND_COLOUR
) );
7837 attr
.SetFlags(flags
);
7843 return wxRichTextAttr();
7846 /// Submit command to delete this range
7847 bool wxRichTextBuffer::DeleteRangeWithUndo(const wxRichTextRange
& range
, wxRichTextCtrl
* ctrl
)
7849 return ctrl
->GetFocusObject()->DeleteRangeWithUndo(range
, ctrl
, this);
7852 /// Submit command to delete this range
7853 bool wxRichTextParagraphLayoutBox::DeleteRangeWithUndo(const wxRichTextRange
& range
, wxRichTextCtrl
* ctrl
, wxRichTextBuffer
* buffer
)
7855 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Delete"), wxRICHTEXT_DELETE
, buffer
, this, ctrl
);
7857 action
->SetPosition(ctrl
->GetCaretPosition());
7859 // Set the range to delete
7860 action
->SetRange(range
);
7862 // Copy the fragment that we'll need to restore in Undo
7863 CopyFragment(range
, action
->GetOldParagraphs());
7865 // See if we're deleting a paragraph marker, in which case we need to
7866 // make a note not to copy the attributes from the 2nd paragraph to the 1st.
7867 if (range
.GetStart() == range
.GetEnd())
7869 wxRichTextParagraph
* para
= GetParagraphAtPosition(range
.GetStart());
7870 if (para
&& para
->GetRange().GetEnd() == range
.GetEnd())
7872 wxRichTextParagraph
* nextPara
= GetParagraphAtPosition(range
.GetStart()+1);
7873 if (nextPara
&& nextPara
!= para
)
7875 action
->GetOldParagraphs().GetChildren().GetFirst()->GetData()->SetAttributes(nextPara
->GetAttributes());
7876 action
->GetOldParagraphs().GetAttributes().SetFlags(action
->GetOldParagraphs().GetAttributes().GetFlags() | wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
);
7881 buffer
->SubmitAction(action
);
7886 /// Collapse undo/redo commands
7887 bool wxRichTextBuffer::BeginBatchUndo(const wxString
& cmdName
)
7889 if (m_batchedCommandDepth
== 0)
7891 wxASSERT(m_batchedCommand
== NULL
);
7892 if (m_batchedCommand
)
7894 GetCommandProcessor()->Store(m_batchedCommand
);
7896 m_batchedCommand
= new wxRichTextCommand(cmdName
);
7899 m_batchedCommandDepth
++;
7904 /// Collapse undo/redo commands
7905 bool wxRichTextBuffer::EndBatchUndo()
7907 m_batchedCommandDepth
--;
7909 wxASSERT(m_batchedCommandDepth
>= 0);
7910 wxASSERT(m_batchedCommand
!= NULL
);
7912 if (m_batchedCommandDepth
== 0)
7914 GetCommandProcessor()->Store(m_batchedCommand
);
7915 m_batchedCommand
= NULL
;
7921 /// Submit immediately, or delay according to whether collapsing is on
7922 bool wxRichTextBuffer::SubmitAction(wxRichTextAction
* action
)
7924 if (action
&& !action
->GetNewParagraphs().IsEmpty())
7925 PrepareContent(action
->GetNewParagraphs());
7927 if (BatchingUndo() && m_batchedCommand
&& !SuppressingUndo())
7929 wxRichTextCommand
* cmd
= new wxRichTextCommand(action
->GetName());
7930 cmd
->AddAction(action
);
7932 cmd
->GetActions().Clear();
7935 m_batchedCommand
->AddAction(action
);
7939 wxRichTextCommand
* cmd
= new wxRichTextCommand(action
->GetName());
7940 cmd
->AddAction(action
);
7942 // Only store it if we're not suppressing undo.
7943 return GetCommandProcessor()->Submit(cmd
, !SuppressingUndo());
7949 /// Begin suppressing undo/redo commands.
7950 bool wxRichTextBuffer::BeginSuppressUndo()
7957 /// End suppressing undo/redo commands.
7958 bool wxRichTextBuffer::EndSuppressUndo()
7965 /// Begin using a style
7966 bool wxRichTextBuffer::BeginStyle(const wxRichTextAttr
& style
)
7968 wxRichTextAttr
newStyle(GetDefaultStyle());
7969 newStyle
.GetTextBoxAttr().Reset();
7971 // Save the old default style
7972 m_attributeStack
.Append((wxObject
*) new wxRichTextAttr(newStyle
));
7974 wxRichTextApplyStyle(newStyle
, style
);
7975 newStyle
.SetFlags(style
.GetFlags()|newStyle
.GetFlags());
7977 SetDefaultStyle(newStyle
);
7983 bool wxRichTextBuffer::EndStyle()
7985 if (!m_attributeStack
.GetFirst())
7987 wxLogDebug(_("Too many EndStyle calls!"));
7991 wxList::compatibility_iterator node
= m_attributeStack
.GetLast();
7992 wxRichTextAttr
* attr
= (wxRichTextAttr
*)node
->GetData();
7993 m_attributeStack
.Erase(node
);
7995 SetDefaultStyle(*attr
);
8002 bool wxRichTextBuffer::EndAllStyles()
8004 while (m_attributeStack
.GetCount() != 0)
8009 /// Clear the style stack
8010 void wxRichTextBuffer::ClearStyleStack()
8012 for (wxList::compatibility_iterator node
= m_attributeStack
.GetFirst(); node
; node
= node
->GetNext())
8013 delete (wxRichTextAttr
*) node
->GetData();
8014 m_attributeStack
.Clear();
8017 /// Begin using bold
8018 bool wxRichTextBuffer::BeginBold()
8020 wxRichTextAttr attr
;
8021 attr
.SetFontWeight(wxFONTWEIGHT_BOLD
);
8023 return BeginStyle(attr
);
8026 /// Begin using italic
8027 bool wxRichTextBuffer::BeginItalic()
8029 wxRichTextAttr attr
;
8030 attr
.SetFontStyle(wxFONTSTYLE_ITALIC
);
8032 return BeginStyle(attr
);
8035 /// Begin using underline
8036 bool wxRichTextBuffer::BeginUnderline()
8038 wxRichTextAttr attr
;
8039 attr
.SetFontUnderlined(true);
8041 return BeginStyle(attr
);
8044 /// Begin using point size
8045 bool wxRichTextBuffer::BeginFontSize(int pointSize
)
8047 wxRichTextAttr attr
;
8048 attr
.SetFontSize(pointSize
);
8050 return BeginStyle(attr
);
8053 /// Begin using this font
8054 bool wxRichTextBuffer::BeginFont(const wxFont
& font
)
8056 wxRichTextAttr attr
;
8059 return BeginStyle(attr
);
8062 /// Begin using this colour
8063 bool wxRichTextBuffer::BeginTextColour(const wxColour
& colour
)
8065 wxRichTextAttr attr
;
8066 attr
.SetFlags(wxTEXT_ATTR_TEXT_COLOUR
);
8067 attr
.SetTextColour(colour
);
8069 return BeginStyle(attr
);
8072 /// Begin using alignment
8073 bool wxRichTextBuffer::BeginAlignment(wxTextAttrAlignment alignment
)
8075 wxRichTextAttr attr
;
8076 attr
.SetFlags(wxTEXT_ATTR_ALIGNMENT
);
8077 attr
.SetAlignment(alignment
);
8079 return BeginStyle(attr
);
8082 /// Begin left indent
8083 bool wxRichTextBuffer::BeginLeftIndent(int leftIndent
, int leftSubIndent
)
8085 wxRichTextAttr attr
;
8086 attr
.SetFlags(wxTEXT_ATTR_LEFT_INDENT
);
8087 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
8089 return BeginStyle(attr
);
8092 /// Begin right indent
8093 bool wxRichTextBuffer::BeginRightIndent(int rightIndent
)
8095 wxRichTextAttr attr
;
8096 attr
.SetFlags(wxTEXT_ATTR_RIGHT_INDENT
);
8097 attr
.SetRightIndent(rightIndent
);
8099 return BeginStyle(attr
);
8102 /// Begin paragraph spacing
8103 bool wxRichTextBuffer::BeginParagraphSpacing(int before
, int after
)
8107 flags
|= wxTEXT_ATTR_PARA_SPACING_BEFORE
;
8109 flags
|= wxTEXT_ATTR_PARA_SPACING_AFTER
;
8111 wxRichTextAttr attr
;
8112 attr
.SetFlags(flags
);
8113 attr
.SetParagraphSpacingBefore(before
);
8114 attr
.SetParagraphSpacingAfter(after
);
8116 return BeginStyle(attr
);
8119 /// Begin line spacing
8120 bool wxRichTextBuffer::BeginLineSpacing(int lineSpacing
)
8122 wxRichTextAttr attr
;
8123 attr
.SetFlags(wxTEXT_ATTR_LINE_SPACING
);
8124 attr
.SetLineSpacing(lineSpacing
);
8126 return BeginStyle(attr
);
8129 /// Begin numbered bullet
8130 bool wxRichTextBuffer::BeginNumberedBullet(int bulletNumber
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
8132 wxRichTextAttr attr
;
8133 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
8134 attr
.SetBulletStyle(bulletStyle
);
8135 attr
.SetBulletNumber(bulletNumber
);
8136 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
8138 return BeginStyle(attr
);
8141 /// Begin symbol bullet
8142 bool wxRichTextBuffer::BeginSymbolBullet(const wxString
& symbol
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
8144 wxRichTextAttr attr
;
8145 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
8146 attr
.SetBulletStyle(bulletStyle
);
8147 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
8148 attr
.SetBulletText(symbol
);
8150 return BeginStyle(attr
);
8153 /// Begin standard bullet
8154 bool wxRichTextBuffer::BeginStandardBullet(const wxString
& bulletName
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
8156 wxRichTextAttr attr
;
8157 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
8158 attr
.SetBulletStyle(bulletStyle
);
8159 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
8160 attr
.SetBulletName(bulletName
);
8162 return BeginStyle(attr
);
8165 /// Begin named character style
8166 bool wxRichTextBuffer::BeginCharacterStyle(const wxString
& characterStyle
)
8168 if (GetStyleSheet())
8170 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->FindCharacterStyle(characterStyle
);
8173 wxRichTextAttr attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
8174 return BeginStyle(attr
);
8180 /// Begin named paragraph style
8181 bool wxRichTextBuffer::BeginParagraphStyle(const wxString
& paragraphStyle
)
8183 if (GetStyleSheet())
8185 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(paragraphStyle
);
8188 wxRichTextAttr attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
8189 return BeginStyle(attr
);
8195 /// Begin named list style
8196 bool wxRichTextBuffer::BeginListStyle(const wxString
& listStyle
, int level
, int number
)
8198 if (GetStyleSheet())
8200 wxRichTextListStyleDefinition
* def
= GetStyleSheet()->FindListStyle(listStyle
);
8203 wxRichTextAttr
attr(def
->GetCombinedStyleForLevel(level
));
8205 attr
.SetBulletNumber(number
);
8207 return BeginStyle(attr
);
8214 bool wxRichTextBuffer::BeginURL(const wxString
& url
, const wxString
& characterStyle
)
8216 wxRichTextAttr attr
;
8218 if (!characterStyle
.IsEmpty() && GetStyleSheet())
8220 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->FindCharacterStyle(characterStyle
);
8223 attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
8228 return BeginStyle(attr
);
8231 /// Adds a handler to the end
8232 void wxRichTextBuffer::AddHandler(wxRichTextFileHandler
*handler
)
8234 sm_handlers
.Append(handler
);
8237 /// Inserts a handler at the front
8238 void wxRichTextBuffer::InsertHandler(wxRichTextFileHandler
*handler
)
8240 sm_handlers
.Insert( handler
);
8243 /// Removes a handler
8244 bool wxRichTextBuffer::RemoveHandler(const wxString
& name
)
8246 wxRichTextFileHandler
*handler
= FindHandler(name
);
8249 sm_handlers
.DeleteObject(handler
);
8257 /// Finds a handler by filename or, if supplied, type
8258 wxRichTextFileHandler
*wxRichTextBuffer::FindHandlerFilenameOrType(const wxString
& filename
,
8259 wxRichTextFileType imageType
)
8261 if (imageType
!= wxRICHTEXT_TYPE_ANY
)
8262 return FindHandler(imageType
);
8263 else if (!filename
.IsEmpty())
8265 wxString path
, file
, ext
;
8266 wxFileName::SplitPath(filename
, & path
, & file
, & ext
);
8267 return FindHandler(ext
, imageType
);
8274 /// Finds a handler by name
8275 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(const wxString
& name
)
8277 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
8280 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
8281 if (handler
->GetName().Lower() == name
.Lower()) return handler
;
8283 node
= node
->GetNext();
8288 /// Finds a handler by extension and type
8289 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(const wxString
& extension
, wxRichTextFileType type
)
8291 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
8294 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
8295 if ( handler
->GetExtension().Lower() == extension
.Lower() &&
8296 (type
== wxRICHTEXT_TYPE_ANY
|| handler
->GetType() == type
) )
8298 node
= node
->GetNext();
8303 /// Finds a handler by type
8304 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(wxRichTextFileType type
)
8306 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
8309 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
8310 if (handler
->GetType() == type
) return handler
;
8311 node
= node
->GetNext();
8316 void wxRichTextBuffer::InitStandardHandlers()
8318 if (!FindHandler(wxRICHTEXT_TYPE_TEXT
))
8319 AddHandler(new wxRichTextPlainTextHandler
);
8322 void wxRichTextBuffer::CleanUpHandlers()
8324 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
8327 wxRichTextFileHandler
* handler
= (wxRichTextFileHandler
*)node
->GetData();
8328 wxList::compatibility_iterator next
= node
->GetNext();
8333 sm_handlers
.Clear();
8336 wxString
wxRichTextBuffer::GetExtWildcard(bool combine
, bool save
, wxArrayInt
* types
)
8343 wxList::compatibility_iterator node
= GetHandlers().GetFirst();
8347 wxRichTextFileHandler
* handler
= (wxRichTextFileHandler
*) node
->GetData();
8348 if (handler
->IsVisible() && ((save
&& handler
->CanSave()) || (!save
&& handler
->CanLoad())))
8353 wildcard
+= wxT(";");
8354 wildcard
+= wxT("*.") + handler
->GetExtension();
8359 wildcard
+= wxT("|");
8360 wildcard
+= handler
->GetName();
8361 wildcard
+= wxT(" ");
8362 wildcard
+= _("files");
8363 wildcard
+= wxT(" (*.");
8364 wildcard
+= handler
->GetExtension();
8365 wildcard
+= wxT(")|*.");
8366 wildcard
+= handler
->GetExtension();
8368 types
->Add(handler
->GetType());
8373 node
= node
->GetNext();
8377 wildcard
= wxT("(") + wildcard
+ wxT(")|") + wildcard
;
8381 #if wxUSE_FFILE && wxUSE_STREAMS
8383 bool wxRichTextBuffer::LoadFile(const wxString
& filename
, wxRichTextFileType type
)
8385 wxRichTextFileHandler
* handler
= FindHandlerFilenameOrType(filename
, type
);
8388 SetDefaultStyle(wxRichTextAttr());
8389 handler
->SetFlags(GetHandlerFlags());
8390 bool success
= handler
->LoadFile(this, filename
);
8391 Invalidate(wxRICHTEXT_ALL
);
8399 bool wxRichTextBuffer::SaveFile(const wxString
& filename
, wxRichTextFileType type
)
8401 wxRichTextFileHandler
* handler
= FindHandlerFilenameOrType(filename
, type
);
8404 handler
->SetFlags(GetHandlerFlags());
8405 return handler
->SaveFile(this, filename
);
8410 #endif // wxUSE_FFILE && wxUSE_STREAMS
8413 /// Load from a stream
8414 bool wxRichTextBuffer::LoadFile(wxInputStream
& stream
, wxRichTextFileType type
)
8416 wxRichTextFileHandler
* handler
= FindHandler(type
);
8419 SetDefaultStyle(wxRichTextAttr());
8420 handler
->SetFlags(GetHandlerFlags());
8421 bool success
= handler
->LoadFile(this, stream
);
8422 Invalidate(wxRICHTEXT_ALL
);
8429 /// Save to a stream
8430 bool wxRichTextBuffer::SaveFile(wxOutputStream
& stream
, wxRichTextFileType type
)
8432 wxRichTextFileHandler
* handler
= FindHandler(type
);
8435 handler
->SetFlags(GetHandlerFlags());
8436 return handler
->SaveFile(this, stream
);
8441 #endif // wxUSE_STREAMS
8443 /// Copy the range to the clipboard
8444 bool wxRichTextBuffer::CopyToClipboard(const wxRichTextRange
& range
)
8446 bool success
= false;
8447 wxRichTextParagraphLayoutBox
* container
= this;
8448 if (GetRichTextCtrl())
8449 container
= GetRichTextCtrl()->GetFocusObject();
8451 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
8453 if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open())
8455 wxTheClipboard
->Clear();
8457 // Add composite object
8459 wxDataObjectComposite
* compositeObject
= new wxDataObjectComposite();
8462 wxString text
= container
->GetTextForRange(range
);
8465 text
= wxTextFile::Translate(text
, wxTextFileType_Dos
);
8468 compositeObject
->Add(new wxTextDataObject(text
), false /* not preferred */);
8471 // Add rich text buffer data object. This needs the XML handler to be present.
8473 if (FindHandler(wxRICHTEXT_TYPE_XML
))
8475 wxRichTextBuffer
* richTextBuf
= new wxRichTextBuffer
;
8476 container
->CopyFragment(range
, *richTextBuf
);
8478 compositeObject
->Add(new wxRichTextBufferDataObject(richTextBuf
), true /* preferred */);
8481 if (wxTheClipboard
->SetData(compositeObject
))
8484 wxTheClipboard
->Close();
8493 /// Paste the clipboard content to the buffer
8494 bool wxRichTextBuffer::PasteFromClipboard(long position
)
8496 bool success
= false;
8497 wxRichTextParagraphLayoutBox
* container
= this;
8498 if (GetRichTextCtrl())
8499 container
= GetRichTextCtrl()->GetFocusObject();
8501 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
8502 if (CanPasteFromClipboard())
8504 if (wxTheClipboard
->Open())
8506 if (wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())))
8508 wxRichTextBufferDataObject data
;
8509 wxTheClipboard
->GetData(data
);
8510 wxRichTextBuffer
* richTextBuffer
= data
.GetRichTextBuffer();
8513 container
->InsertParagraphsWithUndo(this, position
+1, *richTextBuffer
, GetRichTextCtrl(), 0);
8514 if (GetRichTextCtrl())
8515 GetRichTextCtrl()->ShowPosition(position
+ richTextBuffer
->GetOwnRange().GetEnd());
8516 delete richTextBuffer
;
8519 else if (wxTheClipboard
->IsSupported(wxDF_TEXT
)
8521 || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
)
8525 wxTextDataObject data
;
8526 wxTheClipboard
->GetData(data
);
8527 wxString
text(data
.GetText());
8530 text2
.Alloc(text
.Length()+1);
8532 for (i
= 0; i
< text
.Length(); i
++)
8534 wxChar ch
= text
[i
];
8535 if (ch
!= wxT('\r'))
8539 wxString text2
= text
;
8541 container
->InsertTextWithUndo(this, position
+1, text2
, GetRichTextCtrl(), wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
);
8543 if (GetRichTextCtrl())
8544 GetRichTextCtrl()->ShowPosition(position
+ text2
.Length());
8548 else if (wxTheClipboard
->IsSupported(wxDF_BITMAP
))
8550 wxBitmapDataObject data
;
8551 wxTheClipboard
->GetData(data
);
8552 wxBitmap
bitmap(data
.GetBitmap());
8553 wxImage
image(bitmap
.ConvertToImage());
8555 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, this, container
, GetRichTextCtrl(), false);
8557 action
->GetNewParagraphs().AddImage(image
);
8559 if (action
->GetNewParagraphs().GetChildCount() == 1)
8560 action
->GetNewParagraphs().SetPartialParagraph(true);
8562 action
->SetPosition(position
+1);
8564 // Set the range we'll need to delete in Undo
8565 action
->SetRange(wxRichTextRange(position
+1, position
+1));
8567 SubmitAction(action
);
8571 wxTheClipboard
->Close();
8575 wxUnusedVar(position
);
8580 /// Can we paste from the clipboard?
8581 bool wxRichTextBuffer::CanPasteFromClipboard() const
8583 bool canPaste
= false;
8584 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
8585 if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open())
8587 if (wxTheClipboard
->IsSupported(wxDF_TEXT
)
8589 || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
)
8591 || wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())) ||
8592 wxTheClipboard
->IsSupported(wxDF_BITMAP
))
8596 wxTheClipboard
->Close();
8602 /// Dumps contents of buffer for debugging purposes
8603 void wxRichTextBuffer::Dump()
8607 wxStringOutputStream
stream(& text
);
8608 wxTextOutputStream
textStream(stream
);
8615 /// Add an event handler
8616 bool wxRichTextBuffer::AddEventHandler(wxEvtHandler
* handler
)
8618 m_eventHandlers
.Append(handler
);
8622 /// Remove an event handler
8623 bool wxRichTextBuffer::RemoveEventHandler(wxEvtHandler
* handler
, bool deleteHandler
)
8625 wxList::compatibility_iterator node
= m_eventHandlers
.Find(handler
);
8628 m_eventHandlers
.Erase(node
);
8638 /// Clear event handlers
8639 void wxRichTextBuffer::ClearEventHandlers()
8641 m_eventHandlers
.Clear();
8644 /// Send event to event handlers. If sendToAll is true, will send to all event handlers,
8645 /// otherwise will stop at the first successful one.
8646 bool wxRichTextBuffer::SendEvent(wxEvent
& event
, bool sendToAll
)
8648 bool success
= false;
8649 for (wxList::compatibility_iterator node
= m_eventHandlers
.GetFirst(); node
; node
= node
->GetNext())
8651 wxEvtHandler
* handler
= (wxEvtHandler
*) node
->GetData();
8652 if (handler
->ProcessEvent(event
))
8662 /// Set style sheet and notify of the change
8663 bool wxRichTextBuffer::SetStyleSheetAndNotify(wxRichTextStyleSheet
* sheet
)
8665 wxRichTextStyleSheet
* oldSheet
= GetStyleSheet();
8667 wxWindowID winid
= wxID_ANY
;
8668 if (GetRichTextCtrl())
8669 winid
= GetRichTextCtrl()->GetId();
8671 wxRichTextEvent
event(wxEVT_RICHTEXT_STYLESHEET_REPLACING
, winid
);
8672 event
.SetEventObject(GetRichTextCtrl());
8673 event
.SetContainer(GetRichTextCtrl()->GetFocusObject());
8674 event
.SetOldStyleSheet(oldSheet
);
8675 event
.SetNewStyleSheet(sheet
);
8678 if (SendEvent(event
) && !event
.IsAllowed())
8680 if (sheet
!= oldSheet
)
8686 if (oldSheet
&& oldSheet
!= sheet
)
8689 SetStyleSheet(sheet
);
8691 event
.SetEventType(wxEVT_RICHTEXT_STYLESHEET_REPLACED
);
8692 event
.SetOldStyleSheet(NULL
);
8695 return SendEvent(event
);
8698 /// Set renderer, deleting old one
8699 void wxRichTextBuffer::SetRenderer(wxRichTextRenderer
* renderer
)
8703 sm_renderer
= renderer
;
8706 /// Hit-testing: returns a flag indicating hit test details, plus
8707 /// information about position
8708 int wxRichTextBuffer::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
8710 int ret
= wxRichTextParagraphLayoutBox::HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
8711 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
8717 textPosition
= m_ownRange
.GetEnd()-1;
8720 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
;
8724 void wxRichTextBuffer::SetFontScale(double fontScale
)
8726 m_fontScale
= fontScale
;
8727 m_fontTable
.SetFontScale(fontScale
);
8730 void wxRichTextBuffer::SetDimensionScale(double dimScale
)
8732 m_dimensionScale
= dimScale
;
8735 bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxRichTextAttr
& bulletAttr
, const wxRect
& rect
)
8737 if (bulletAttr
.GetTextColour().IsOk())
8739 wxCheckSetPen(dc
, wxPen(bulletAttr
.GetTextColour()));
8740 wxCheckSetBrush(dc
, wxBrush(bulletAttr
.GetTextColour()));
8744 wxCheckSetPen(dc
, *wxBLACK_PEN
);
8745 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
8749 if (bulletAttr
.HasFont())
8751 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(bulletAttr
);
8754 font
= (*wxNORMAL_FONT
);
8756 wxCheckSetFont(dc
, font
);
8758 int charHeight
= dc
.GetCharHeight();
8760 int bulletWidth
= (int) (((float) charHeight
) * wxRichTextBuffer::GetBulletProportion());
8761 int bulletHeight
= bulletWidth
;
8765 // Calculate the top position of the character (as opposed to the whole line height)
8766 int y
= rect
.y
+ (rect
.height
- charHeight
);
8768 // Calculate where the bullet should be positioned
8769 y
= y
+ (charHeight
+1)/2 - (bulletHeight
+1)/2;
8771 // The margin between a bullet and text.
8772 int margin
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer::GetBulletRightMargin());
8774 if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
)
8775 x
= rect
.x
+ rect
.width
- bulletWidth
- margin
;
8776 else if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
)
8777 x
= x
+ (rect
.width
)/2 - bulletWidth
/2;
8779 if (bulletAttr
.GetBulletName() == wxT("standard/square"))
8781 dc
.DrawRectangle(x
, y
, bulletWidth
, bulletHeight
);
8783 else if (bulletAttr
.GetBulletName() == wxT("standard/diamond"))
8786 pts
[0].x
= x
; pts
[0].y
= y
+ bulletHeight
/2;
8787 pts
[1].x
= x
+ bulletWidth
/2; pts
[1].y
= y
;
8788 pts
[2].x
= x
+ bulletWidth
; pts
[2].y
= y
+ bulletHeight
/2;
8789 pts
[3].x
= x
+ bulletWidth
/2; pts
[3].y
= y
+ bulletHeight
;
8791 dc
.DrawPolygon(4, pts
);
8793 else if (bulletAttr
.GetBulletName() == wxT("standard/triangle"))
8796 pts
[0].x
= x
; pts
[0].y
= y
;
8797 pts
[1].x
= x
+ bulletWidth
; pts
[1].y
= y
+ bulletHeight
/2;
8798 pts
[2].x
= x
; pts
[2].y
= y
+ bulletHeight
;
8800 dc
.DrawPolygon(3, pts
);
8802 else if (bulletAttr
.GetBulletName() == wxT("standard/circle-outline"))
8804 wxCheckSetBrush(dc
, *wxTRANSPARENT_BRUSH
);
8805 dc
.DrawEllipse(x
, y
, bulletWidth
, bulletHeight
);
8807 else // "standard/circle", and catch-all
8809 dc
.DrawEllipse(x
, y
, bulletWidth
, bulletHeight
);
8815 bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxRichTextAttr
& attr
, const wxRect
& rect
, const wxString
& text
)
8820 if ((attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
) && !attr
.GetBulletFont().IsEmpty() && attr
.HasFont())
8822 wxRichTextAttr fontAttr
;
8823 if (attr
.HasFontPixelSize())
8824 fontAttr
.SetFontPixelSize(attr
.GetFontSize());
8826 fontAttr
.SetFontPointSize(attr
.GetFontSize());
8827 fontAttr
.SetFontStyle(attr
.GetFontStyle());
8828 fontAttr
.SetFontWeight(attr
.GetFontWeight());
8829 fontAttr
.SetFontUnderlined(attr
.GetFontUnderlined());
8830 fontAttr
.SetFontFaceName(attr
.GetBulletFont());
8831 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(fontAttr
);
8833 else if (attr
.HasFont())
8834 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(attr
);
8836 font
= (*wxNORMAL_FONT
);
8838 wxCheckSetFont(dc
, font
);
8840 if (attr
.GetTextColour().IsOk())
8841 dc
.SetTextForeground(attr
.GetTextColour());
8843 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
8845 int charHeight
= dc
.GetCharHeight();
8847 dc
.GetTextExtent(text
, & tw
, & th
);
8851 // Calculate the top position of the character (as opposed to the whole line height)
8852 int y
= rect
.y
+ (rect
.height
- charHeight
);
8854 // The margin between a bullet and text.
8855 int margin
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer::GetBulletRightMargin());
8857 if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
)
8858 x
= (rect
.x
+ rect
.width
) - tw
- margin
;
8859 else if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
)
8860 x
= x
+ (rect
.width
)/2 - tw
/2;
8862 dc
.DrawText(text
, x
, y
);
8870 bool wxRichTextStdRenderer::DrawBitmapBullet(wxRichTextParagraph
* WXUNUSED(paragraph
), wxDC
& WXUNUSED(dc
), const wxRichTextAttr
& WXUNUSED(attr
), const wxRect
& WXUNUSED(rect
))
8872 // Currently unimplemented. The intention is to store bitmaps by name in a media store associated
8873 // with the buffer. The store will allow retrieval from memory, disk or other means.
8877 /// Enumerate the standard bullet names currently supported
8878 bool wxRichTextStdRenderer::EnumerateStandardBulletNames(wxArrayString
& bulletNames
)
8880 bulletNames
.Add(wxTRANSLATE("standard/circle"));
8881 bulletNames
.Add(wxTRANSLATE("standard/circle-outline"));
8882 bulletNames
.Add(wxTRANSLATE("standard/square"));
8883 bulletNames
.Add(wxTRANSLATE("standard/diamond"));
8884 bulletNames
.Add(wxTRANSLATE("standard/triangle"));
8893 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBox
, wxRichTextParagraphLayoutBox
)
8895 wxRichTextBox::wxRichTextBox(wxRichTextObject
* parent
):
8896 wxRichTextParagraphLayoutBox(parent
)
8901 bool wxRichTextBox::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8906 // TODO: if the active object in the control, draw an indication.
8907 // We need to add the concept of active object, and not just focus object,
8908 // so we can apply commands (properties, delete, ...) to objects such as text boxes and images.
8909 // Ultimately we would like to be able to interactively resize an active object
8910 // using drag handles.
8911 return wxRichTextParagraphLayoutBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8915 void wxRichTextBox::Copy(const wxRichTextBox
& obj
)
8917 wxRichTextParagraphLayoutBox::Copy(obj
);
8920 // Edit properties via a GUI
8921 bool wxRichTextBox::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8923 wxRichTextObjectPropertiesDialog
boxDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Box Properties"));
8924 boxDlg
.SetAttributes(GetAttributes());
8926 if (boxDlg
.ShowModal() == wxID_OK
)
8928 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
8929 // indeterminate in the object.
8930 boxDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
8941 IMPLEMENT_DYNAMIC_CLASS(wxRichTextField
, wxRichTextParagraphLayoutBox
)
8943 wxRichTextField::wxRichTextField(const wxString
& fieldType
, wxRichTextObject
* parent
):
8944 wxRichTextParagraphLayoutBox(parent
)
8946 SetFieldType(fieldType
);
8950 bool wxRichTextField::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8955 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8956 if (fieldType
&& fieldType
->Draw(this, dc
, context
, range
, selection
, rect
, descent
, style
))
8959 // Fallback; but don't draw guidelines.
8960 style
&= ~wxRICHTEXT_DRAW_GUIDELINES
;
8961 return wxRichTextParagraphLayoutBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8964 bool wxRichTextField::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
)
8966 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8967 if (fieldType
&& fieldType
->Layout(this, dc
, context
, rect
, parentRect
, style
))
8971 return wxRichTextParagraphLayoutBox::Layout(dc
, context
, rect
, parentRect
, style
);
8974 bool wxRichTextField::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, const wxPoint
& position
, const wxSize
& parentSize
, wxArrayInt
* partialExtents
) const
8976 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8978 return fieldType
->GetRangeSize((wxRichTextField
*) this, range
, size
, descent
, dc
, context
, flags
, position
, parentSize
, partialExtents
);
8980 return wxRichTextParagraphLayoutBox::GetRangeSize(range
, size
, descent
, dc
, context
, flags
, position
, parentSize
, partialExtents
);
8984 void wxRichTextField::CalculateRange(long start
, long& end
)
8987 wxRichTextParagraphLayoutBox::CalculateRange(start
, end
);
8989 wxRichTextObject::CalculateRange(start
, end
);
8993 void wxRichTextField::Copy(const wxRichTextField
& obj
)
8995 wxRichTextParagraphLayoutBox::Copy(obj
);
8997 UpdateField(GetBuffer());
9000 // Edit properties via a GUI
9001 bool wxRichTextField::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
9003 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
9005 return fieldType
->EditProperties(this, parent
, buffer
);
9010 bool wxRichTextField::CanEditProperties() const
9012 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
9014 return fieldType
->CanEditProperties((wxRichTextField
*) this);
9019 wxString
wxRichTextField::GetPropertiesMenuLabel() const
9021 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
9023 return fieldType
->GetPropertiesMenuLabel((wxRichTextField
*) this);
9025 return wxEmptyString
;
9028 bool wxRichTextField::UpdateField(wxRichTextBuffer
* buffer
)
9030 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
9032 return fieldType
->UpdateField(buffer
, (wxRichTextField
*) this);
9037 bool wxRichTextField::IsTopLevel() const
9039 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
9041 return fieldType
->IsTopLevel((wxRichTextField
*) this);
9046 IMPLEMENT_CLASS(wxRichTextFieldType
, wxObject
)
9048 IMPLEMENT_CLASS(wxRichTextFieldTypeStandard
, wxRichTextFieldType
)
9050 wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString
& name
, const wxString
& label
, int displayStyle
)
9056 SetDisplayStyle(displayStyle
);
9059 wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString
& name
, const wxBitmap
& bitmap
, int displayStyle
)
9065 SetDisplayStyle(displayStyle
);
9068 void wxRichTextFieldTypeStandard::Init()
9070 m_displayStyle
= wxRICHTEXT_FIELD_STYLE_RECTANGLE
;
9071 m_font
= wxFont(6, wxFONTFAMILY_SWISS
, wxFONTSTYLE_NORMAL
, wxFONTWEIGHT_NORMAL
);
9072 m_textColour
= *wxWHITE
;
9073 m_borderColour
= *wxBLACK
;
9074 m_backgroundColour
= *wxBLACK
;
9075 m_verticalPadding
= 1;
9076 m_horizontalPadding
= 3;
9077 m_horizontalMargin
= 2;
9078 m_verticalMargin
= 0;
9081 void wxRichTextFieldTypeStandard::Copy(const wxRichTextFieldTypeStandard
& field
)
9083 wxRichTextFieldType::Copy(field
);
9085 m_label
= field
.m_label
;
9086 m_displayStyle
= field
.m_displayStyle
;
9087 m_font
= field
.m_font
;
9088 m_textColour
= field
.m_textColour
;
9089 m_borderColour
= field
.m_borderColour
;
9090 m_backgroundColour
= field
.m_backgroundColour
;
9091 m_verticalPadding
= field
.m_verticalPadding
;
9092 m_horizontalPadding
= field
.m_horizontalPadding
;
9093 m_horizontalMargin
= field
.m_horizontalMargin
;
9094 m_bitmap
= field
.m_bitmap
;
9097 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
))
9099 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_COMPOSITE
)
9100 return false; // USe default composite drawing
9101 else // if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_RECTANGLE || m_displayStyle == wxRICHTEXT_FIELD_STYLE_NOBORDER)
9105 wxPen
borderPen(m_borderColour
, 1, wxSOLID
);
9106 wxBrush
backgroundBrush(m_backgroundColour
);
9107 wxColour
textColour(m_textColour
);
9109 if (selection
.WithinSelection(obj
->GetRange().GetStart(), obj
))
9111 wxColour
highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
));
9112 wxColour
highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
));
9114 borderPen
= wxPen(highlightTextColour
, 1, wxSOLID
);
9115 backgroundBrush
= wxBrush(highlightColour
);
9117 wxCheckSetBrush(dc
, backgroundBrush
);
9118 wxCheckSetPen(dc
, wxPen(highlightColour
, 1, wxSOLID
));
9119 dc
.DrawRectangle(rect
);
9122 if (m_displayStyle
!= wxRICHTEXT_FIELD_STYLE_NO_BORDER
)
9125 // objectRect is the area where the content is drawn, after margins around it have been taken into account
9126 wxRect objectRect
= wxRect(wxPoint(rect
.x
+ m_horizontalMargin
, rect
.y
+ wxMax(0, rect
.height
- descent
- obj
->GetCachedSize().y
)),
9127 wxSize(obj
->GetCachedSize().x
- 2*m_horizontalMargin
- borderSize
, obj
->GetCachedSize().y
));
9129 // clientArea is where the text is actually written
9130 wxRect clientArea
= objectRect
;
9132 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_RECTANGLE
)
9134 dc
.SetPen(borderPen
);
9135 dc
.SetBrush(backgroundBrush
);
9136 dc
.DrawRoundedRectangle(objectRect
, 4.0);
9138 else if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_START_TAG
)
9140 int arrowLength
= objectRect
.height
/2;
9141 clientArea
.width
-= (arrowLength
- m_horizontalPadding
);
9144 pts
[0].x
= objectRect
.x
; pts
[0].y
= objectRect
.y
;
9145 pts
[1].x
= objectRect
.x
+ objectRect
.width
- arrowLength
; pts
[1].y
= objectRect
.y
;
9146 pts
[2].x
= objectRect
.x
+ objectRect
.width
; pts
[2].y
= objectRect
.y
+ (objectRect
.height
/2);
9147 pts
[3].x
= objectRect
.x
+ objectRect
.width
- arrowLength
; pts
[3].y
= objectRect
.y
+ objectRect
.height
;
9148 pts
[4].x
= objectRect
.x
; pts
[4].y
= objectRect
.y
+ objectRect
.height
;
9149 dc
.SetPen(borderPen
);
9150 dc
.SetBrush(backgroundBrush
);
9151 dc
.DrawPolygon(5, pts
);
9153 else if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_END_TAG
)
9155 int arrowLength
= objectRect
.height
/2;
9156 clientArea
.width
-= (arrowLength
- m_horizontalPadding
);
9157 clientArea
.x
+= (arrowLength
- m_horizontalPadding
);
9160 pts
[0].x
= objectRect
.x
+ objectRect
.width
; pts
[0].y
= objectRect
.y
;
9161 pts
[1].x
= objectRect
.x
+ arrowLength
; pts
[1].y
= objectRect
.y
;
9162 pts
[2].x
= objectRect
.x
; pts
[2].y
= objectRect
.y
+ (objectRect
.height
/2);
9163 pts
[3].x
= objectRect
.x
+ arrowLength
; pts
[3].y
= objectRect
.y
+ objectRect
.height
;
9164 pts
[4].x
= objectRect
.x
+ objectRect
.width
; pts
[4].y
= objectRect
.y
+ objectRect
.height
;
9165 dc
.SetPen(borderPen
);
9166 dc
.SetBrush(backgroundBrush
);
9167 dc
.DrawPolygon(5, pts
);
9170 if (m_bitmap
.IsOk())
9172 int x
= clientArea
.x
+ (clientArea
.width
- m_bitmap
.GetWidth())/2;
9173 int y
= clientArea
.y
+ m_verticalPadding
;
9174 dc
.DrawBitmap(m_bitmap
, x
, y
, true);
9176 if (selection
.WithinSelection(obj
->GetRange().GetStart(), obj
))
9178 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
9179 wxCheckSetPen(dc
, *wxBLACK_PEN
);
9180 dc
.SetLogicalFunction(wxINVERT
);
9181 dc
.DrawRectangle(wxRect(x
, y
, m_bitmap
.GetWidth(), m_bitmap
.GetHeight()));
9182 dc
.SetLogicalFunction(wxCOPY
);
9187 wxString
label(m_label
);
9188 if (label
.IsEmpty())
9190 int w
, h
, maxDescent
;
9192 dc
.GetTextExtent(m_label
, & w
, &h
, & maxDescent
);
9193 dc
.SetTextForeground(textColour
);
9195 int x
= clientArea
.x
+ (clientArea
.width
- w
)/2;
9196 int y
= clientArea
.y
+ (clientArea
.height
- (h
- maxDescent
))/2;
9197 dc
.DrawText(m_label
, x
, y
);
9204 bool wxRichTextFieldTypeStandard::Layout(wxRichTextField
* obj
, wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& WXUNUSED(rect
), const wxRect
& WXUNUSED(parentRect
), int style
)
9206 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_COMPOSITE
)
9207 return false; // USe default composite layout
9209 wxSize size
= GetSize(obj
, dc
, context
, style
);
9210 obj
->SetCachedSize(size
);
9211 obj
->SetMinSize(size
);
9212 obj
->SetMaxSize(size
);
9216 bool wxRichTextFieldTypeStandard::GetRangeSize(wxRichTextField
* obj
, const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, const wxPoint
& position
, const wxSize
& parentSize
, wxArrayInt
* partialExtents
) const
9218 if (IsTopLevel(obj
))
9219 return obj
->wxRichTextParagraphLayoutBox::GetRangeSize(range
, size
, descent
, dc
, context
, flags
, position
, parentSize
);
9222 wxSize sz
= GetSize(obj
, dc
, context
, 0);
9226 if (partialExtents
->GetCount() > 0)
9227 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
9230 partialExtents
->Add(lastSize
+ sz
.x
);
9237 wxSize
wxRichTextFieldTypeStandard::GetSize(wxRichTextField
* WXUNUSED(obj
), wxDC
& dc
, wxRichTextDrawingContext
& WXUNUSED(context
), int WXUNUSED(style
)) const
9240 int w
= 0, h
= 0, maxDescent
= 0;
9243 if (m_bitmap
.IsOk())
9245 w
= m_bitmap
.GetWidth();
9246 h
= m_bitmap
.GetHeight();
9248 sz
= wxSize(w
+ m_horizontalMargin
*2, h
+ m_verticalMargin
*2);
9252 wxString
label(m_label
);
9253 if (label
.IsEmpty())
9256 dc
.GetTextExtent(label
, & w
, &h
, & maxDescent
);
9258 sz
= wxSize(w
+ m_horizontalPadding
*2 + m_horizontalMargin
*2, h
+ m_verticalPadding
*2 + m_verticalMargin
*2);
9261 if (m_displayStyle
!= wxRICHTEXT_FIELD_STYLE_NO_BORDER
)
9263 sz
.x
+= borderSize
*2;
9264 sz
.y
+= borderSize
*2;
9267 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_START_TAG
|| m_displayStyle
== wxRICHTEXT_FIELD_STYLE_END_TAG
)
9269 // Add space for the arrow
9270 sz
.x
+= (sz
.y
/2 - m_horizontalPadding
);
9276 IMPLEMENT_DYNAMIC_CLASS(wxRichTextCell
, wxRichTextBox
)
9278 wxRichTextCell::wxRichTextCell(wxRichTextObject
* parent
):
9279 wxRichTextBox(parent
)
9284 bool wxRichTextCell::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
9286 return wxRichTextBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
9289 int wxRichTextCell::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
9291 int ret
= wxRichTextParagraphLayoutBox::HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
9292 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
9298 textPosition
= m_ownRange
.GetEnd()-1;
9301 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
;
9306 void wxRichTextCell::Copy(const wxRichTextCell
& obj
)
9308 wxRichTextBox::Copy(obj
);
9311 // Edit properties via a GUI
9312 bool wxRichTextCell::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
9314 // We need to gather common attributes for all selected cells.
9316 wxRichTextTable
* table
= wxDynamicCast(GetParent(), wxRichTextTable
);
9317 bool multipleCells
= false;
9318 wxRichTextAttr attr
;
9320 if (table
&& buffer
&& buffer
->GetRichTextCtrl() && buffer
->GetRichTextCtrl()->GetSelection().IsValid() &&
9321 buffer
->GetRichTextCtrl()->GetSelection().GetContainer() == GetParent())
9323 wxRichTextAttr clashingAttr
, absentAttr
;
9324 const wxRichTextSelection
& sel
= buffer
->GetRichTextCtrl()->GetSelection();
9326 int selectedCellCount
= 0;
9327 for (i
= 0; i
< sel
.GetCount(); i
++)
9329 const wxRichTextRange
& range
= sel
[i
];
9330 wxRichTextCell
* cell
= table
->GetCell(range
.GetStart());
9333 wxRichTextAttr cellStyle
= cell
->GetAttributes();
9335 CollectStyle(attr
, cellStyle
, clashingAttr
, absentAttr
);
9337 selectedCellCount
++;
9340 multipleCells
= selectedCellCount
> 1;
9344 attr
= GetAttributes();
9349 caption
= _("Multiple Cell Properties");
9351 caption
= _("Cell Properties");
9353 // We don't want position and floating controls for a cell.
9354 wxRichTextSizePage::ShowPositionControls(false);
9355 wxRichTextSizePage::ShowFloatingControls(false);
9356 wxRichTextSizePage::ShowAlignmentControls(true);
9358 wxRichTextObjectPropertiesDialog
cellDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, caption
);
9359 cellDlg
.SetAttributes(attr
);
9361 bool ok
= (cellDlg
.ShowModal() == wxID_OK
);
9363 wxRichTextSizePage::ShowPositionControls(true);
9364 wxRichTextSizePage::ShowFloatingControls(true);
9370 const wxRichTextSelection
& sel
= buffer
->GetRichTextCtrl()->GetSelection();
9371 // Apply the style; we interpret indeterminate attributes as 'don't touch this attribute'
9372 // since it may represent clashing attributes across multiple objects.
9373 table
->SetCellStyle(sel
, attr
);
9376 // For a single object, indeterminate attributes set by the user should be reflected in the
9377 // actual object style, so pass the wxRICHTEXT_SETSTYLE_RESET flag to assign
9378 // the style directly instead of applying (which ignores indeterminate attributes,
9379 // leaving them as they were).
9380 cellDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
9387 // The next 2 methods return span values. Note that the default is 1, not 0
9388 int wxRichTextCell::GetColspan() const
9391 if (GetProperties().HasProperty(wxT("colspan")))
9393 span
= GetProperties().GetPropertyLong(wxT("colspan"));
9399 int wxRichTextCell::GetRowspan() const
9402 if (GetProperties().HasProperty(wxT("rowspan")))
9404 span
= GetProperties().GetPropertyLong(wxT("rowspan"));
9410 WX_DEFINE_OBJARRAY(wxRichTextObjectPtrArrayArray
)
9412 IMPLEMENT_DYNAMIC_CLASS(wxRichTextTable
, wxRichTextBox
)
9414 wxRichTextTable::wxRichTextTable(wxRichTextObject
* parent
): wxRichTextBox(parent
)
9420 // Draws the object.
9421 bool wxRichTextTable::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
9423 wxRichTextBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
9425 int colCount
= GetColumnCount();
9426 int rowCount
= GetRowCount();
9428 for (col
= 0; col
< colCount
; col
++)
9430 for (row
= 0; row
< rowCount
; row
++)
9432 if (row
== 0 || row
== (rowCount
-1) || col
== 0 || col
== (colCount
-1))
9434 wxRichTextCell
* cell
= GetCell(row
, col
);
9435 if (cell
&& cell
->IsShown() && !cell
->GetRange().IsOutside(range
))
9437 wxRect
childRect(cell
->GetPosition(), cell
->GetCachedSize());
9438 wxRichTextAttr
attr(cell
->GetAttributes());
9440 attr
.GetTextBoxAttr().GetBorder().GetTop().Reset();
9441 if (row
!= (rowCount
-1))
9442 attr
.GetTextBoxAttr().GetBorder().GetBottom().Reset();
9444 attr
.GetTextBoxAttr().GetBorder().GetLeft().Reset();
9445 if (col
!= (colCount
-1))
9446 attr
.GetTextBoxAttr().GetBorder().GetRight().Reset();
9448 if (attr
.GetTextBoxAttr().GetBorder().IsValid())
9450 wxRect
boxRect(cell
->GetPosition(), cell
->GetCachedSize());
9451 wxRect marginRect
= boxRect
;
9452 wxRect contentRect
, borderRect
, paddingRect
, outlineRect
;
9454 cell
->GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
9455 cell
->DrawBorder(dc
, GetBuffer(), attr
.GetTextBoxAttr().GetBorder(), borderRect
);
9465 // Helper function for Layout() that clears the space needed by a cell with rowspan > 1
9466 int GetRowspanDisplacement(const wxRichTextTable
* table
, int row
, int col
, int paddingX
, const wxArrayInt
& colWidths
)
9468 // If one or more cells above-left of this one has rowspan > 1, the affected cells below it
9469 // will have been hidden and have width 0. As a result they are ignored by the layout algorithm,
9470 // and all cells to their right are effectively shifted left. As a result there's no hole for
9471 // the spanning cell to fill.
9472 // So search back along the current row for hidden cells. However there's also the annoying issue of a
9473 // rowspanning cell that also has colspam. So we can't rely on the rowspanning cell being directly above
9474 // the first hidden one we come to. We also can't rely on a cell being hidden only by one type of span;
9475 // there's nothing to stop a cell being hidden by colspan, and then again hidden from above by rowspan.
9476 // The answer is to look above each hidden cell in turn, which I think covers all bases.
9478 for (int prevcol
= 0; prevcol
< col
; ++prevcol
)
9480 if (!table
->GetCell(row
, prevcol
)->IsShown())
9482 // We've found a hidden cell. If it's hidden because of colspan to its left, it's
9483 // already been taken into account; but not if there's a rowspanning cell above
9484 for (int prevrow
= row
-1; prevrow
>= 0; --prevrow
)
9486 wxRichTextCell
* cell
= table
->GetCell(prevrow
, prevcol
);
9487 if (cell
&& cell
->IsShown())
9489 int rowSpan
= cell
->GetRowspan();
9490 if (rowSpan
> 1 && rowSpan
> (row
-prevrow
))
9492 // There is a rowspanning cell above above the hidden one, so we need
9493 // to right-shift the index cell by this column's width. Furthermore,
9494 // if the cell also colspans, we need to shift by all affected columns
9495 for (int colSpan
= 0; colSpan
< cell
->GetColspan(); ++colSpan
)
9496 deltaX
+= (colWidths
[prevcol
+colSpan
] + paddingX
);
9506 // Helper function for Layout() that expands any cell with rowspan > 1
9507 void ExpandCellsWithRowspan(const wxRichTextTable
* table
, int paddingY
, int& bottomY
, wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& availableSpace
, int style
)
9509 // This is called when the table's cell layout is otherwise complete.
9510 // For any cell with rowspan > 1, expand downwards into the row(s) below.
9512 // Start by finding the current 'y' of the top of each row, plus the bottom of the available area for cells.
9513 // Deduce this from the top of a visible cell in the row below. (If none are visible, the row will be invisible anyway and can be ignored.)
9514 const int rowCount
= table
->GetRowCount();
9515 const int colCount
= table
->GetColumnCount();
9517 rowTops
.Add(0, rowCount
+1);
9519 for (row
= 0; row
< rowCount
; ++row
)
9521 for (int column
= 0; column
< colCount
; ++column
)
9523 wxRichTextCell
* cell
= table
->GetCell(row
, column
);
9524 if (cell
&& cell
->IsShown())
9526 rowTops
[row
] = cell
->GetPosition().y
;
9531 rowTops
[rowCount
] = bottomY
+ paddingY
; // The table bottom, which was passed to us
9533 bool needsRelay
= false;
9535 for (row
= 0; row
< rowCount
-1; ++row
) // -1 as the bottom row can't rowspan
9537 for (int col
= 0; col
< colCount
; ++col
)
9539 wxRichTextCell
* cell
= table
->GetCell(row
, col
);
9540 if (cell
&& cell
->IsShown())
9542 int span
= cell
->GetRowspan();
9545 span
= wxMin(span
, rowCount
-row
); // Don't try to span below the table!
9549 int availableHeight
= rowTops
[row
+span
] - rowTops
[row
] - paddingY
;
9550 wxSize newSize
= wxSize(cell
->GetCachedSize().GetWidth(), availableHeight
);
9551 wxRect availableCellSpace
= wxRect(cell
->GetPosition(), newSize
);
9552 cell
->Invalidate(wxRICHTEXT_ALL
);
9553 cell
->Layout(dc
, context
, availableCellSpace
, availableSpace
, style
);
9554 // Ensure there's room in the span to display its contents, else it'll overwrite lower rows
9555 int overhang
= cell
->GetCachedSize().GetHeight() - availableHeight
;
9556 cell
->SetCachedSize(newSize
);
9560 // There are 3 things to get right:
9561 // 1) The easiest is the rows below the span: they need to be downshifted by the overhang, and so does the table bottom itself
9562 // 2) The rows within the span, including the one holding this cell, need to be deepened by their share of the overhang
9563 // e.g. if rowspan == 3, each row should increase in depth by 1/3rd of the overhang.
9564 // 3) The cell with the rowspan shouldn't be touched in 2); its height will be set to the whole span later.
9565 int deltaY
= overhang
/ span
;
9566 int spare
= overhang
% span
;
9568 // Each row in the span needs to by deepened by its share of the overhang (give the first row any spare).
9569 // This is achieved by increasing the value stored in the following row's rowTops
9570 for (int spannedRows
= 0; spannedRows
< span
; ++spannedRows
)
9572 rowTops
[row
+spannedRows
+1] += ((deltaY
* (spannedRows
+1)) + (spannedRows
== 0 ? spare
:0));
9575 // Any rows below the span need shifting down
9576 for (int rowsBelow
= row
+ span
+1; rowsBelow
<= rowCount
; ++rowsBelow
)
9578 rowTops
[rowsBelow
] += overhang
;
9591 // There were overflowing rowspanning cells, so layout yet again to make the increased row depths show
9592 for (row
= 0; row
< rowCount
; ++row
)
9594 for (int col
= 0; col
< colCount
; ++col
)
9596 wxRichTextCell
* cell
= table
->GetCell(row
, col
);
9597 if (cell
&& cell
->IsShown())
9599 wxPoint
position(cell
->GetPosition().x
, rowTops
[row
]);
9601 // GetRowspan() will usually return 1, but may be greater
9602 wxSize
size(cell
->GetCachedSize().GetWidth(), rowTops
[row
+ cell
->GetRowspan()] - rowTops
[row
] - paddingY
);
9604 wxRect availableCellSpace
= wxRect(position
, size
);
9605 cell
->Invalidate(wxRICHTEXT_ALL
);
9606 cell
->Layout(dc
, context
, availableCellSpace
, availableSpace
, style
);
9607 cell
->SetCachedSize(size
);
9611 bottomY
= rowTops
[rowCount
] - paddingY
;
9615 // Lays the object out. rect is the space available for layout. Often it will
9616 // be the specified overall space for this object, if trying to constrain
9617 // layout to a particular size, or it could be the total space available in the
9618 // parent. rect is the overall size, so we must subtract margins and padding.
9619 // to get the actual available space.
9620 bool wxRichTextTable::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& WXUNUSED(parentRect
), int style
)
9622 SetPosition(rect
.GetPosition());
9624 // The meaty bit. Calculate sizes of all cells and rows. Try to use
9625 // minimum size if within alloted size, then divide up remaining size
9626 // between rows/cols.
9629 wxRichTextBuffer
* buffer
= GetBuffer();
9630 if (buffer
) scale
= buffer
->GetScale();
9632 wxRect availableSpace
= GetAvailableContentArea(dc
, context
, rect
);
9633 wxTextAttrDimensionConverter
converter(dc
, scale
, availableSpace
.GetSize());
9635 wxRichTextAttr
attr(GetAttributes());
9636 context
.ApplyVirtualAttributes(attr
, this);
9638 bool tableHasPercentWidth
= (attr
.GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
);
9639 // If we have no fixed table size, and assuming we're not pushed for
9640 // space, then we don't have to try to stretch the table to fit the contents.
9641 bool stretchToFitTableWidth
= tableHasPercentWidth
;
9643 int tableWidth
= rect
.width
;
9644 if (attr
.GetTextBoxAttr().GetWidth().IsValid() && !tableHasPercentWidth
)
9646 tableWidth
= converter
.GetPixels(attr
.GetTextBoxAttr().GetWidth());
9648 // Fixed table width, so we do want to stretch columns out if necessary.
9649 stretchToFitTableWidth
= true;
9651 // Shouldn't be able to exceed the size passed to this function
9652 tableWidth
= wxMin(rect
.width
, tableWidth
);
9655 // Get internal padding
9656 int paddingLeft
= 0, paddingTop
= 0;
9657 if (attr
.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
9658 paddingLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetLeft());
9659 if (attr
.GetTextBoxAttr().GetPadding().GetTop().IsValid())
9660 paddingTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetTop());
9662 // Assume that left and top padding are also used for inter-cell padding.
9663 int paddingX
= paddingLeft
;
9664 int paddingY
= paddingTop
;
9666 int totalLeftMargin
= 0, totalRightMargin
= 0, totalTopMargin
= 0, totalBottomMargin
= 0;
9667 GetTotalMargin(dc
, buffer
, attr
, totalLeftMargin
, totalRightMargin
, totalTopMargin
, totalBottomMargin
);
9669 // Internal table width - the area for content
9670 int internalTableWidth
= tableWidth
- totalLeftMargin
- totalRightMargin
;
9672 int rowCount
= m_cells
.GetCount();
9673 if (m_colCount
== 0 || rowCount
== 0)
9675 wxRect
overallRect(rect
.x
, rect
.y
, totalLeftMargin
+ totalRightMargin
, totalTopMargin
+ totalBottomMargin
);
9676 SetCachedSize(overallRect
.GetSize());
9678 // Zero content size
9679 SetMinSize(overallRect
.GetSize());
9680 SetMaxSize(GetMinSize());
9684 // The final calculated widths
9685 wxArrayInt colWidths
;
9686 colWidths
.Add(0, m_colCount
);
9688 wxArrayInt absoluteColWidths
;
9689 absoluteColWidths
.Add(0, m_colCount
);
9691 wxArrayInt percentageColWidths
;
9692 percentageColWidths
.Add(0, m_colCount
);
9693 // wxArrayInt percentageColWidthsSpanning(m_colCount);
9694 // These are only relevant when the first column contains spanning information.
9695 // wxArrayInt columnSpans(m_colCount); // Each contains 1 for non-spanning cell, > 1 for spanning cell.
9696 wxArrayInt maxColWidths
;
9697 maxColWidths
.Add(0, m_colCount
);
9698 wxArrayInt minColWidths
;
9699 minColWidths
.Add(0, m_colCount
);
9701 wxSize
tableSize(tableWidth
, 0);
9705 for (i
= 0; i
< m_colCount
; i
++)
9707 absoluteColWidths
[i
] = 0;
9708 // absoluteColWidthsSpanning[i] = 0;
9709 percentageColWidths
[i
] = -1;
9710 // percentageColWidthsSpanning[i] = -1;
9712 maxColWidths
[i
] = 0;
9713 minColWidths
[i
] = 0;
9714 // columnSpans[i] = 1;
9717 // (0) Determine which cells are visible according to spans
9719 // __________________
9724 // |------------------|
9725 // |__________________| 4
9727 // To calculate cell visibility:
9728 // First find all spanning cells. Build an array of span records with start x, y and end x, y.
9729 // Then for each cell, test whether we're within one of those cells, and unless we're at the start of
9730 // that cell, hide the cell.
9732 // We can also use this array to match the size of spanning cells to the grid. Or just do
9733 // this when we iterate through all cells.
9735 // 0.1: add spanning cells to an array
9736 wxRichTextRectArray rectArray
;
9737 for (j
= 0; j
< m_rowCount
; j
++)
9739 for (i
= 0; i
< m_colCount
; i
++)
9741 wxRichTextCell
* cell
= GetCell(j
, i
);
9742 int colSpan
= cell
->GetColspan();
9743 int rowSpan
= cell
->GetRowspan();
9744 if (colSpan
> 1 || rowSpan
> 1)
9746 rectArray
.Add(wxRect(i
, j
, colSpan
, rowSpan
));
9750 // 0.2: find which cells are subsumed by a spanning cell
9751 for (j
= 0; j
< m_rowCount
; j
++)
9753 for (i
= 0; i
< m_colCount
; i
++)
9755 wxRichTextCell
* cell
= GetCell(j
, i
);
9756 if (rectArray
.GetCount() == 0)
9762 int colSpan
= cell
->GetColspan();
9763 int rowSpan
= cell
->GetRowspan();
9765 if (colSpan
> 1 || rowSpan
> 1)
9767 // Assume all spanning cells are shown
9773 for (k
= 0; k
< (int) rectArray
.GetCount(); k
++)
9775 if (rectArray
[k
].Contains(wxPoint(i
, j
)))
9787 // Find the first spanned cell in each row that spans the most columns and doesn't
9788 // overlap with a spanned cell starting at a previous column position.
9789 // This means we need to keep an array of rects so we can check. However
9790 // it does also mean that some spans simply may not be taken into account
9791 // where there are different spans happening on different rows. In these cases,
9792 // they will simply be as wide as their constituent columns.
9794 // (1) Do an initial layout for all cells to get minimum and maximum size, and get
9795 // the absolute or percentage width of each column.
9797 for (j
= 0; j
< m_rowCount
; j
++)
9799 // First get the overall margins so we can calculate percentage widths based on
9800 // the available content space for all cells on the row
9802 int overallRowContentMargin
= 0;
9803 int visibleCellCount
= 0;
9805 for (i
= 0; i
< m_colCount
; i
++)
9807 wxRichTextBox
* cell
= GetCell(j
, i
);
9808 if (cell
->IsShown())
9810 int cellTotalLeftMargin
= 0, cellTotalRightMargin
= 0, cellTotalTopMargin
= 0, cellTotalBottomMargin
= 0;
9811 GetTotalMargin(dc
, buffer
, cell
->GetAttributes(), cellTotalLeftMargin
, cellTotalRightMargin
, cellTotalTopMargin
, cellTotalBottomMargin
);
9813 overallRowContentMargin
+= (cellTotalLeftMargin
+ cellTotalRightMargin
);
9814 visibleCellCount
++;
9818 // Add in inter-cell padding
9819 overallRowContentMargin
+= ((visibleCellCount
-1) * paddingX
);
9821 int rowContentWidth
= internalTableWidth
- overallRowContentMargin
;
9822 wxSize
rowTableSize(rowContentWidth
, 0);
9823 wxTextAttrDimensionConverter
converter(dc
, scale
, rowTableSize
);
9825 for (i
= 0; i
< m_colCount
; i
++)
9827 wxRichTextCell
* cell
= GetCell(j
, i
);
9828 if (cell
->IsShown())
9830 int colSpan
= cell
->GetColspan();
9832 // Lay out cell to find min/max widths
9833 cell
->Invalidate(wxRICHTEXT_ALL
);
9834 cell
->Layout(dc
, context
, availableSpace
, availableSpace
, style
);
9838 int absoluteCellWidth
= -1;
9839 int percentageCellWidth
= -1;
9841 // I think we need to calculate percentages from the internal table size,
9842 // minus the padding between cells which we'll need to calculate from the
9843 // (number of VISIBLE cells - 1)*paddingX. Then percentages that add up to 100%
9844 // will add up to 100%. In CSS, the width specifies the cell's content rect width,
9845 // so if we want to conform to that we'll need to add in the overall cell margins.
9846 // However, this will make it difficult to specify percentages that add up to
9847 // 100% and still fit within the table width.
9848 // Let's say two cells have 50% width. They have 10 pixels of overall margin each.
9849 // The table content rect is 500 pixels and the inter-cell padding is 20 pixels.
9850 // If we're using internal content size for the width, we would calculate the
9851 // the overall cell width for n cells as:
9852 // (500 - 20*(n-1) - overallCellMargin1 - overallCellMargin2 - ...) * percentage / 100
9853 // + thisOverallCellMargin
9854 // = 500 - 20 - 10 - 10) * 0.5 + 10 = 240 pixels overall cell width.
9855 // Adding this back, we get 240 + 240 + 20 = 500 pixels.
9857 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9859 int w
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetWidth());
9860 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
9862 percentageCellWidth
= w
;
9866 absoluteCellWidth
= w
;
9868 // Override absolute width with minimum width if necessary
9869 if (cell
->GetMinSize().x
> 0 && absoluteCellWidth
!=1 && cell
->GetMinSize().x
> absoluteCellWidth
)
9870 absoluteCellWidth
= cell
->GetMinSize().x
;
9873 if (absoluteCellWidth
!= -1)
9875 if (absoluteCellWidth
> absoluteColWidths
[i
])
9876 absoluteColWidths
[i
] = absoluteCellWidth
;
9879 if (percentageCellWidth
!= -1)
9881 if (percentageCellWidth
> percentageColWidths
[i
])
9882 percentageColWidths
[i
] = percentageCellWidth
;
9885 if (colSpan
== 1 && cell
->GetMinSize().x
&& cell
->GetMinSize().x
> minColWidths
[i
])
9886 minColWidths
[i
] = cell
->GetMinSize().x
;
9887 if (colSpan
== 1 && cell
->GetMaxSize().x
&& cell
->GetMaxSize().x
> maxColWidths
[i
])
9888 maxColWidths
[i
] = cell
->GetMaxSize().x
;
9894 // (2) Allocate initial column widths from minimum widths, absolute values and proportions
9895 // TODO: simply merge this into (1).
9896 for (i
= 0; i
< m_colCount
; i
++)
9898 if (absoluteColWidths
[i
] > 0)
9900 colWidths
[i
] = absoluteColWidths
[i
];
9902 else if (percentageColWidths
[i
] > 0)
9904 colWidths
[i
] = percentageColWidths
[i
];
9906 // This is rubbish - we calculated the absolute widths from percentages, so
9907 // we can't do it again here.
9908 //colWidths[i] = (int) (double(percentageColWidths[i]) * double(tableWidth) / 100.0 + 0.5);
9912 // (3) Process absolute or proportional widths of spanning columns,
9913 // now that we know what our fixed column widths are going to be.
9914 // Spanned cells will try to adjust columns so the span will fit.
9915 // Even existing fixed column widths can be expanded if necessary.
9916 // Actually, currently fixed columns widths aren't adjusted; instead,
9917 // the algorithm favours earlier rows and adjusts unspecified column widths
9918 // the first time only. After that, we can't know whether the column has been
9919 // specified explicitly or not. (We could make a note if necessary.)
9920 for (j
= 0; j
< m_rowCount
; j
++)
9922 // First get the overall margins so we can calculate percentage widths based on
9923 // the available content space for all cells on the row
9925 int overallRowContentMargin
= 0;
9926 int visibleCellCount
= 0;
9928 for (i
= 0; i
< m_colCount
; i
++)
9930 wxRichTextBox
* cell
= GetCell(j
, i
);
9931 if (cell
->IsShown())
9933 int cellTotalLeftMargin
= 0, cellTotalRightMargin
= 0, cellTotalTopMargin
= 0, cellTotalBottomMargin
= 0;
9934 GetTotalMargin(dc
, buffer
, cell
->GetAttributes(), cellTotalLeftMargin
, cellTotalRightMargin
, cellTotalTopMargin
, cellTotalBottomMargin
);
9936 overallRowContentMargin
+= (cellTotalLeftMargin
+ cellTotalRightMargin
);
9937 visibleCellCount
++;
9941 // Add in inter-cell padding
9942 overallRowContentMargin
+= ((visibleCellCount
-1) * paddingX
);
9944 int rowContentWidth
= internalTableWidth
- overallRowContentMargin
;
9945 wxSize
rowTableSize(rowContentWidth
, 0);
9946 wxTextAttrDimensionConverter
converter(dc
, scale
, rowTableSize
);
9948 for (i
= 0; i
< m_colCount
; i
++)
9950 wxRichTextCell
* cell
= GetCell(j
, i
);
9951 if (cell
->IsShown())
9953 int colSpan
= cell
->GetColspan();
9956 int spans
= wxMin(colSpan
, m_colCount
- i
);
9960 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9962 cellWidth
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetWidth());
9963 // Override absolute width with minimum width if necessary
9964 if (cell
->GetMinSize().x
> 0 && cellWidth
!=1 && cell
->GetMinSize().x
> cellWidth
)
9965 cellWidth
= cell
->GetMinSize().x
;
9969 // Do we want to do this? It's the only chance we get to
9970 // use the cell's min/max sizes, so we need to work out
9971 // how we're going to balance the unspecified spanning cell
9972 // width with the possibility more-constrained constituent cell widths.
9973 // Say there's a tiny bitmap giving it a max width of 10 pixels. We
9974 // don't want to constraint all the spanned columns to fit into this cell.
9975 // OK, let's say that if any of the constituent columns don't fit,
9976 // then we simply stop constraining the columns; instead, we'll just fit the spanning
9977 // cells to the columns later.
9978 cellWidth
= cell
->GetMinSize().x
;
9979 if (cell
->GetMaxSize().x
> cellWidth
)
9980 cellWidth
= cell
->GetMaxSize().x
;
9983 // Subtract the padding between cells
9984 int spanningWidth
= cellWidth
;
9985 spanningWidth
-= paddingX
* (spans
-1);
9987 if (spanningWidth
> 0)
9989 // Now share the spanning width between columns within that span
9990 // TODO: take into account min widths of columns within the span
9991 int spanningWidthLeft
= spanningWidth
;
9992 int stretchColCount
= 0;
9993 for (k
= i
; k
< (i
+spans
); k
++)
9995 if (colWidths
[k
] > 0) // absolute or proportional width has been specified
9996 spanningWidthLeft
-= colWidths
[k
];
10000 // Now divide what's left between the remaining columns
10002 if (stretchColCount
> 0)
10003 colShare
= spanningWidthLeft
/ stretchColCount
;
10004 int colShareRemainder
= spanningWidthLeft
- (colShare
* stretchColCount
);
10006 // If fixed-width columns are currently too big, then we'll later
10007 // stretch the spanned cell to fit.
10009 if (spanningWidthLeft
> 0)
10011 for (k
= i
; k
< (i
+spans
); k
++)
10013 if (colWidths
[k
] <= 0) // absolute or proportional width has not been specified
10015 int newWidth
= colShare
;
10016 if (k
== (i
+spans
-1))
10017 newWidth
+= colShareRemainder
; // ensure all pixels are filled
10018 colWidths
[k
] = newWidth
;
10029 // (4) Next, share any remaining space out between columns that have not yet been calculated.
10030 // TODO: take into account min widths of columns within the span
10031 int tableWidthMinusPadding
= internalTableWidth
- (m_colCount
-1)*paddingX
;
10032 int widthLeft
= tableWidthMinusPadding
;
10033 int stretchColCount
= 0;
10034 for (i
= 0; i
< m_colCount
; i
++)
10036 // Subtract min width from width left, then
10037 // add the colShare to the min width
10038 if (colWidths
[i
] > 0) // absolute or proportional width has been specified
10039 widthLeft
-= colWidths
[i
];
10042 if (minColWidths
[i
] > 0)
10043 widthLeft
-= minColWidths
[i
];
10045 stretchColCount
++;
10049 // Now divide what's left between the remaining columns
10051 if (stretchColCount
> 0)
10052 colShare
= widthLeft
/ stretchColCount
;
10053 int colShareRemainder
= widthLeft
- (colShare
* stretchColCount
);
10055 // Check we don't have enough space, in which case shrink all columns, overriding
10056 // any absolute/proportional widths
10057 // TODO: actually we would like to divide up the shrinkage according to size.
10058 // How do we calculate the proportions that will achieve this?
10059 // Could first choose an arbitrary value for stretching cells, and then calculate
10060 // factors to multiply each width by.
10061 // TODO: want to record this fact and pass to an iteration that tries e.g. min widths
10062 if (widthLeft
< 0 || (stretchToFitTableWidth
&& (stretchColCount
== 0)))
10064 colShare
= tableWidthMinusPadding
/ m_colCount
;
10065 colShareRemainder
= tableWidthMinusPadding
- (colShare
* m_colCount
);
10066 for (i
= 0; i
< m_colCount
; i
++)
10069 minColWidths
[i
] = 0;
10073 // We have to adjust the columns if either we need to shrink the
10074 // table to fit the parent/table width, or we explicitly set the
10075 // table width and need to stretch out the table.
10076 if (widthLeft
< 0 || stretchToFitTableWidth
)
10078 for (i
= 0; i
< m_colCount
; i
++)
10080 if (colWidths
[i
] <= 0) // absolute or proportional width has not been specified
10082 if (minColWidths
[i
] > 0)
10083 colWidths
[i
] = minColWidths
[i
] + colShare
;
10085 colWidths
[i
] = colShare
;
10086 if (i
== (m_colCount
-1))
10087 colWidths
[i
] += colShareRemainder
; // ensure all pixels are filled
10092 // TODO: if spanned cells have no specified or max width, make them the
10093 // as big as the columns they span. Do this for all spanned cells in all
10094 // rows, of course. Size any spanned cells left over at the end - even if they
10095 // have width > 0, make sure they're limited to the appropriate column edge.
10099 Sort out confusion between content width
10100 and overall width later. For now, assume we specify overall width.
10102 So, now we've laid out the table to fit into the given space
10103 and have used specified widths and minimum widths.
10105 Now we need to consider how we will try to take maximum width into account.
10109 // (??) TODO: take max width into account
10111 // (6) Lay out all cells again with the current values
10114 int y
= availableSpace
.y
;
10115 for (j
= 0; j
< m_rowCount
; j
++)
10117 int x
= availableSpace
.x
; // TODO: take into account centering etc.
10118 int maxCellHeight
= 0;
10119 int maxSpecifiedCellHeight
= 0;
10121 wxArrayInt actualWidths
;
10122 actualWidths
.Add(0, m_colCount
);
10124 wxTextAttrDimensionConverter
converter(dc
, scale
);
10125 for (i
= 0; i
< m_colCount
; i
++)
10127 wxRichTextCell
* cell
= GetCell(j
, i
);
10128 if (cell
->IsShown())
10130 // Get max specified cell height
10131 // Don't handle percentages for height
10132 if (cell
->GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && cell
->GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() != wxTEXT_ATTR_UNITS_PERCENTAGE
)
10134 int h
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetHeight());
10135 if (h
> maxSpecifiedCellHeight
)
10136 maxSpecifiedCellHeight
= h
;
10139 if (colWidths
[i
] > 0) // absolute or proportional width has been specified
10141 int colSpan
= cell
->GetColspan();
10142 wxRect availableCellSpace
;
10144 // Take into account spans
10147 // Calculate the size of this spanning cell from its constituent columns
10149 int spans
= wxMin(colSpan
, m_colCount
- i
);
10150 for (k
= i
; k
< (i
+spans
); k
++)
10154 xx
+= colWidths
[k
];
10156 availableCellSpace
= wxRect(x
, y
, xx
, -1);
10159 availableCellSpace
= wxRect(x
, y
, colWidths
[i
], -1);
10161 // Store actual width so we can force cell to be the appropriate width on the final loop
10162 actualWidths
[i
] = availableCellSpace
.GetWidth();
10164 // We now need to shift right by the width of any rowspanning cells above-left of us
10165 int deltaX
= GetRowspanDisplacement(this, j
, i
, paddingX
, colWidths
);
10166 availableCellSpace
.SetX(availableCellSpace
.GetX() + deltaX
);
10169 cell
->Invalidate(wxRICHTEXT_ALL
);
10170 cell
->Layout(dc
, context
, availableCellSpace
, availableSpace
, style
);
10172 // TODO: use GetCachedSize().x to compute 'natural' size
10174 x
+= (availableCellSpace
.GetWidth() + paddingX
);
10175 if ((cell
->GetCachedSize().y
> maxCellHeight
) && (cell
->GetRowspan() < 2))
10176 maxCellHeight
= cell
->GetCachedSize().y
;
10181 maxCellHeight
= wxMax(maxCellHeight
, maxSpecifiedCellHeight
);
10183 for (i
= 0; i
< m_colCount
; i
++)
10185 wxRichTextCell
* cell
= GetCell(j
, i
);
10186 if (cell
->IsShown())
10188 wxRect availableCellSpace
= wxRect(cell
->GetPosition(), wxSize(actualWidths
[i
], maxCellHeight
));
10189 // Lay out cell with new height
10190 cell
->Invalidate(wxRICHTEXT_ALL
);
10191 cell
->Layout(dc
, context
, availableCellSpace
, availableSpace
, style
);
10193 // Make sure the cell size really is the appropriate size,
10194 // not the calculated box size
10195 cell
->SetCachedSize(wxSize(actualWidths
[i
], maxCellHeight
));
10197 maxRight
= wxMax(maxRight
, cell
->GetPosition().x
+ cell
->GetCachedSize().x
);
10201 y
+= maxCellHeight
;
10202 if (j
< (m_rowCount
-1))
10206 // Finally we need to expand any cell with rowspan > 1. We couldn't earlier; lower rows' heights weren't known
10207 ExpandCellsWithRowspan(this, paddingY
, y
, dc
, context
, availableSpace
, style
);
10209 // We need to add back the margins etc.
10211 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
10212 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxRight
- availableSpace
.x
, y
- availableSpace
.y
));
10213 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
10214 SetCachedSize(marginRect
.GetSize());
10217 // TODO: calculate max size
10219 SetMaxSize(GetCachedSize());
10222 // TODO: calculate min size
10224 SetMinSize(GetCachedSize());
10227 // TODO: currently we use either a fixed table width or the parent's size.
10228 // We also want to be able to calculate the table width from its content,
10229 // whether using fixed column widths or cell content min/max width.
10230 // Probably need a boolean flag to say whether we need to stretch cells
10231 // to fit the table width, or to simply use min/max cell widths. The
10232 // trouble with this is that if cell widths are not specified, they
10233 // will be tiny; we could use arbitrary defaults but this seems unsatisfactory.
10234 // Anyway, ignoring that problem, we probably need to factor layout into a function
10235 // that can can calculate the maximum unconstrained layout in case table size is
10236 // not specified. Then LayoutToBestSize() can choose to use either parent size to
10237 // constrain Layout(), or the previously-calculated max size to constraint layout.
10242 // Finds the absolute position and row height for the given character position
10243 bool wxRichTextTable::FindPosition(wxDC
& dc
, wxRichTextDrawingContext
& context
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
10245 wxRichTextCell
* child
= GetCell(index
+1);
10248 // Find the position at the start of the child cell, since the table doesn't
10249 // have any caret position of its own.
10250 return child
->FindPosition(dc
, context
, -1, pt
, height
, forceLineStart
);
10256 // Get the cell at the given character position (in the range of the table).
10257 wxRichTextCell
* wxRichTextTable::GetCell(long pos
) const
10259 int row
= 0, col
= 0;
10260 if (GetCellRowColumnPosition(pos
, row
, col
))
10262 return GetCell(row
, col
);
10268 // Get the row/column for a given character position
10269 bool wxRichTextTable::GetCellRowColumnPosition(long pos
, int& row
, int& col
) const
10271 if (m_colCount
== 0 || m_rowCount
== 0)
10274 row
= (int) (pos
/ m_colCount
);
10275 col
= pos
- (row
* m_colCount
);
10277 wxASSERT(row
< m_rowCount
&& col
< m_colCount
);
10279 if (row
< m_rowCount
&& col
< m_colCount
)
10285 // Calculate range, taking row/cell ordering into account instead of relying
10286 // on list ordering.
10287 void wxRichTextTable::CalculateRange(long start
, long& end
)
10289 long current
= start
;
10290 long lastEnd
= current
;
10299 for (i
= 0; i
< m_rowCount
; i
++)
10301 for (j
= 0; j
< m_colCount
; j
++)
10303 wxRichTextCell
* child
= GetCell(i
, j
);
10308 child
->CalculateRange(current
, childEnd
);
10310 lastEnd
= childEnd
;
10311 current
= childEnd
+ 1;
10316 // A top-level object always has a range of size 1,
10317 // because its children don't count at this level.
10319 m_range
.SetRange(start
, start
);
10321 // An object with no children has zero length
10322 if (m_children
.GetCount() == 0)
10324 m_ownRange
.SetRange(0, lastEnd
);
10327 // Gets the range size.
10328 bool wxRichTextTable::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, const wxPoint
& position
, const wxSize
& parentSize
, wxArrayInt
* partialExtents
) const
10330 return wxRichTextBox::GetRangeSize(range
, size
, descent
, dc
, context
, flags
, position
, parentSize
, partialExtents
);
10333 // Deletes content in the given range.
10334 bool wxRichTextTable::DeleteRange(const wxRichTextRange
& WXUNUSED(range
))
10336 // TODO: implement deletion of cells
10340 // Gets any text in this object for the given range.
10341 wxString
wxRichTextTable::GetTextForRange(const wxRichTextRange
& range
) const
10343 return wxRichTextBox::GetTextForRange(range
);
10346 // Copies this object.
10347 void wxRichTextTable::Copy(const wxRichTextTable
& obj
)
10349 wxRichTextBox::Copy(obj
);
10353 m_rowCount
= obj
.m_rowCount
;
10354 m_colCount
= obj
.m_colCount
;
10356 m_cells
.Add(wxRichTextObjectPtrArray(), m_rowCount
);
10359 for (i
= 0; i
< m_rowCount
; i
++)
10361 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
10362 for (j
= 0; j
< m_colCount
; j
++)
10364 wxRichTextCell
* cell
= wxDynamicCast(obj
.GetCell(i
, j
)->Clone(), wxRichTextCell
);
10367 colArray
.Add(cell
);
10372 void wxRichTextTable::ClearTable()
10380 bool wxRichTextTable::CreateTable(int rows
, int cols
)
10384 wxRichTextAttr cellattr
;
10385 cellattr
.SetTextColour(GetBasicStyle().GetTextColour());
10390 m_cells
.Add(wxRichTextObjectPtrArray(), rows
);
10393 for (i
= 0; i
< rows
; i
++)
10395 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
10396 for (j
= 0; j
< cols
; j
++)
10398 wxRichTextCell
* cell
= new wxRichTextCell
;
10399 cell
->GetAttributes() = cellattr
;
10402 cell
->AddParagraph(wxEmptyString
);
10404 colArray
.Add(cell
);
10411 wxRichTextCell
* wxRichTextTable::GetCell(int row
, int col
) const
10413 wxASSERT(row
< m_rowCount
);
10414 wxASSERT(col
< m_colCount
);
10416 if (row
< m_rowCount
&& col
< m_colCount
)
10418 wxRichTextObjectPtrArray
& colArray
= m_cells
[row
];
10419 wxRichTextObject
* obj
= colArray
[col
];
10420 return wxDynamicCast(obj
, wxRichTextCell
);
10426 // Returns a selection object specifying the selections between start and end character positions.
10427 // For example, a table would deduce what cells (of range length 1) are selected when dragging across the table.
10428 wxRichTextSelection
wxRichTextTable::GetSelection(long start
, long end
) const
10430 wxRichTextSelection selection
;
10431 selection
.SetContainer((wxRichTextTable
*) this);
10440 wxASSERT( start
>= 0 && end
< (m_colCount
* m_rowCount
));
10442 if (end
>= (m_colCount
* m_rowCount
))
10445 // We need to find the rectangle of cells that is described by the rectangle
10446 // with start, end as the diagonal. Make sure we don't add cells that are
10447 // not currenty visible because they are overlapped by spanning cells.
10449 --------------------------
10450 | 0 | 1 | 2 | 3 | 4 |
10451 --------------------------
10452 | 5 | 6 | 7 | 8 | 9 |
10453 --------------------------
10454 | 10 | 11 | 12 | 13 | 14 |
10455 --------------------------
10456 | 15 | 16 | 17 | 18 | 19 |
10457 --------------------------
10459 Let's say we select 6 -> 18.
10461 Left and right edge cols of rectangle are 1 and 3 inclusive. Find least/greatest to find
10462 which is left and which is right.
10464 Top and bottom edge rows are 1 and 3 inclusive. Again, find least/greatest to find top and bottom.
10466 Now go through rows from 1 to 3 and only add cells that are (a) within above column range
10472 int leftCol
= start
- m_colCount
* int(start
/m_colCount
);
10473 int rightCol
= end
- m_colCount
* int(end
/m_colCount
);
10475 int topRow
= int(start
/m_colCount
);
10476 int bottomRow
= int(end
/m_colCount
);
10478 if (leftCol
> rightCol
)
10480 int tmp
= rightCol
;
10481 rightCol
= leftCol
;
10485 if (topRow
> bottomRow
)
10487 int tmp
= bottomRow
;
10488 bottomRow
= topRow
;
10493 for (i
= topRow
; i
<= bottomRow
; i
++)
10495 for (j
= leftCol
; j
<= rightCol
; j
++)
10497 wxRichTextCell
* cell
= GetCell(i
, j
);
10498 if (cell
&& cell
->IsShown())
10499 selection
.Add(cell
->GetRange());
10506 // Sets the attributes for the cells specified by the selection.
10507 bool wxRichTextTable::SetCellStyle(const wxRichTextSelection
& selection
, const wxRichTextAttr
& style
, int flags
)
10509 if (selection
.GetContainer() != this)
10512 wxRichTextBuffer
* buffer
= GetBuffer();
10513 bool haveControl
= (buffer
&& buffer
->GetRichTextCtrl() != NULL
);
10514 bool withUndo
= haveControl
&& ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
10517 buffer
->BeginBatchUndo(_("Set Cell Style"));
10519 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
10522 wxRichTextCell
* cell
= wxDynamicCast(node
->GetData(), wxRichTextCell
);
10523 if (cell
&& selection
.WithinSelection(cell
->GetRange().GetStart()))
10524 SetStyle(cell
, style
, flags
);
10525 node
= node
->GetNext();
10528 // Do action, or delay it until end of batch.
10530 buffer
->EndBatchUndo();
10535 wxPosition
wxRichTextTable::GetFocusedCell() const
10537 wxPosition
position(-1, -1);
10538 const wxRichTextObject
* focus
= GetBuffer()->GetRichTextCtrl()->GetFocusObject();
10540 for (int row
= 0; row
< GetRowCount(); ++row
)
10542 for (int col
= 0; col
< GetColumnCount(); ++col
)
10544 if (GetCell(row
, col
) == focus
)
10546 position
.SetRow(row
);
10547 position
.SetCol(col
);
10556 int wxRichTextTable::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
10558 for (int row
= 0; row
< GetRowCount(); ++row
)
10560 for (int col
= 0; col
< GetColumnCount(); ++col
)
10562 wxRichTextCell
* cell
= GetCell(row
, col
);
10563 if (cell
->wxRichTextObject::HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
) != wxRICHTEXT_HITTEST_NONE
)
10565 return cell
->HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
10570 return wxRICHTEXT_HITTEST_NONE
;
10573 bool wxRichTextTable::DeleteRows(int startRow
, int noRows
)
10575 wxASSERT((startRow
+ noRows
) <= m_rowCount
);
10576 if ((startRow
+ noRows
) > m_rowCount
)
10579 wxCHECK_MSG(noRows
!= m_rowCount
, false, "Trying to delete all the cells in a table");
10581 wxRichTextBuffer
* buffer
= GetBuffer();
10582 wxRichTextCtrl
* rtc
= buffer
->GetRichTextCtrl();
10584 wxRichTextAction
* action
= NULL
;
10585 wxRichTextTable
* clone
= NULL
;
10586 if (!rtc
->SuppressingUndo())
10588 // Create a clone containing the current state of the table. It will be used to Undo the action
10589 clone
= wxStaticCast(this->Clone(), wxRichTextTable
);
10590 clone
->SetParent(GetParent());
10591 action
= new wxRichTextAction(NULL
, _("Delete Row"), wxRICHTEXT_CHANGE_OBJECT
, buffer
, this, rtc
);
10592 action
->SetObject(this);
10593 action
->SetPosition(GetRange().GetStart());
10597 for (i
= startRow
; i
< (startRow
+noRows
); i
++)
10599 wxRichTextObjectPtrArray
& colArray
= m_cells
[startRow
];
10600 for (j
= 0; j
< (int) colArray
.GetCount(); j
++)
10602 wxRichTextObject
* cell
= colArray
[j
];
10603 RemoveChild(cell
, true);
10606 // Keep deleting at the same position, since we move all
10608 m_cells
.RemoveAt(startRow
);
10611 m_rowCount
= m_rowCount
- noRows
;
10613 if (!rtc
->SuppressingUndo())
10615 buffer
->SubmitAction(action
);
10616 // Finally store the original-state clone; doing so earlier would cause various failures
10617 action
->StoreObject(clone
);
10623 bool wxRichTextTable::DeleteColumns(int startCol
, int noCols
)
10625 wxASSERT((startCol
+ noCols
) <= m_colCount
);
10626 if ((startCol
+ noCols
) > m_colCount
)
10629 wxCHECK_MSG(noCols
!= m_colCount
, false, "Trying to delete all the cells in a table");
10631 wxRichTextBuffer
* buffer
= GetBuffer();
10632 wxRichTextCtrl
* rtc
= buffer
->GetRichTextCtrl();
10634 wxRichTextAction
* action
= NULL
;
10635 wxRichTextTable
* clone
= NULL
;
10636 if (!rtc
->SuppressingUndo())
10638 // Create a clone containing the current state of the table. It will be used to Undo the action
10639 clone
= wxStaticCast(this->Clone(), wxRichTextTable
);
10640 clone
->SetParent(GetParent());
10641 action
= new wxRichTextAction(NULL
, _("Delete Column"), wxRICHTEXT_CHANGE_OBJECT
, buffer
, this, rtc
);
10642 action
->SetObject(this);
10643 action
->SetPosition(GetRange().GetStart());
10646 bool deleteRows
= (noCols
== m_colCount
);
10649 for (i
= 0; i
< m_rowCount
; i
++)
10651 wxRichTextObjectPtrArray
& colArray
= m_cells
[deleteRows
? 0 : i
];
10652 for (j
= 0; j
< noCols
; j
++)
10654 wxRichTextObject
* cell
= colArray
[startCol
];
10655 RemoveChild(cell
, true);
10656 colArray
.RemoveAt(startCol
);
10660 m_cells
.RemoveAt(0);
10665 m_colCount
= m_colCount
- noCols
;
10667 if (!rtc
->SuppressingUndo())
10669 buffer
->SubmitAction(action
);
10670 // Finally store the original-state clone; doing so earlier would cause various failures
10671 action
->StoreObject(clone
);
10677 bool wxRichTextTable::AddRows(int startRow
, int noRows
, const wxRichTextAttr
& attr
)
10679 wxASSERT(startRow
<= m_rowCount
);
10680 if (startRow
> m_rowCount
)
10683 wxRichTextBuffer
* buffer
= GetBuffer();
10684 wxRichTextAction
* action
= NULL
;
10685 wxRichTextTable
* clone
= NULL
;
10687 if (!buffer
->GetRichTextCtrl()->SuppressingUndo())
10689 // Create a clone containing the current state of the table. It will be used to Undo the action
10690 clone
= wxStaticCast(this->Clone(), wxRichTextTable
);
10691 clone
->SetParent(GetParent());
10692 action
= new wxRichTextAction(NULL
, _("Add Row"), wxRICHTEXT_CHANGE_OBJECT
, buffer
, this, buffer
->GetRichTextCtrl());
10693 action
->SetObject(this);
10694 action
->SetPosition(GetRange().GetStart());
10697 wxRichTextAttr cellattr
= attr
;
10698 if (!cellattr
.GetTextColour().IsOk())
10699 cellattr
.SetTextColour(buffer
->GetBasicStyle().GetTextColour());
10702 for (i
= 0; i
< noRows
; i
++)
10705 if (startRow
== m_rowCount
)
10707 m_cells
.Add(wxRichTextObjectPtrArray());
10708 idx
= m_cells
.GetCount() - 1;
10712 m_cells
.Insert(wxRichTextObjectPtrArray(), startRow
+i
);
10716 wxRichTextObjectPtrArray
& colArray
= m_cells
[idx
];
10717 for (j
= 0; j
< m_colCount
; j
++)
10719 wxRichTextCell
* cell
= new wxRichTextCell
;
10720 cell
->GetAttributes() = cellattr
;
10723 cell
->AddParagraph(wxEmptyString
);
10724 colArray
.Add(cell
);
10728 m_rowCount
= m_rowCount
+ noRows
;
10730 if (!buffer
->GetRichTextCtrl()->SuppressingUndo())
10732 buffer
->SubmitAction(action
);
10733 // Finally store the original-state clone; doing so earlier would cause various failures
10734 action
->StoreObject(clone
);
10740 bool wxRichTextTable::AddColumns(int startCol
, int noCols
, const wxRichTextAttr
& attr
)
10742 wxASSERT(startCol
<= m_colCount
);
10743 if (startCol
> m_colCount
)
10746 wxRichTextBuffer
* buffer
= GetBuffer();
10747 wxRichTextAction
* action
= NULL
;
10748 wxRichTextTable
* clone
= NULL
;
10750 if (!buffer
->GetRichTextCtrl()->SuppressingUndo())
10752 // Create a clone containing the current state of the table. It will be used to Undo the action
10753 clone
= wxStaticCast(this->Clone(), wxRichTextTable
);
10754 clone
->SetParent(GetParent());
10755 action
= new wxRichTextAction(NULL
, _("Add Column"), wxRICHTEXT_CHANGE_OBJECT
, buffer
, this, buffer
->GetRichTextCtrl());
10756 action
->SetObject(this);
10757 action
->SetPosition(GetRange().GetStart());
10760 wxRichTextAttr cellattr
= attr
;
10761 if (!cellattr
.GetTextColour().IsOk())
10762 cellattr
.SetTextColour(buffer
->GetBasicStyle().GetTextColour());
10765 for (i
= 0; i
< m_rowCount
; i
++)
10767 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
10768 for (j
= 0; j
< noCols
; j
++)
10770 wxRichTextCell
* cell
= new wxRichTextCell
;
10771 cell
->GetAttributes() = cellattr
;
10774 cell
->AddParagraph(wxEmptyString
);
10776 if (startCol
== m_colCount
)
10777 colArray
.Add(cell
);
10779 colArray
.Insert(cell
, startCol
+j
);
10783 m_colCount
= m_colCount
+ noCols
;
10785 if (!buffer
->GetRichTextCtrl()->SuppressingUndo())
10787 buffer
->SubmitAction(action
);
10788 // Finally store the original-state clone; doing so earlier would cause various failures
10789 action
->StoreObject(clone
);
10795 // Edit properties via a GUI
10796 bool wxRichTextTable::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
10798 wxRichTextObjectPropertiesDialog
boxDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Table Properties"));
10799 boxDlg
.SetAttributes(GetAttributes());
10801 if (boxDlg
.ShowModal() == wxID_OK
)
10803 boxDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
10810 bool wxRichTextTableBlock::ComputeBlockForSelection(wxRichTextTable
* table
, wxRichTextCtrl
* ctrl
, bool requireCellSelection
)
10816 ColEnd() = table
->GetColumnCount()-1;
10818 RowEnd() = table
->GetRowCount()-1;
10820 wxRichTextSelection selection
= ctrl
->GetSelection();
10821 if (selection
.IsValid() && selection
.GetContainer() == table
)
10823 // Start with an invalid block and increase.
10824 wxRichTextTableBlock
selBlock(-1, -1, -1, -1);
10825 wxRichTextRangeArray ranges
= selection
.GetRanges();
10827 for (row
= 0; row
< table
->GetRowCount(); row
++)
10829 for (col
= 0; col
< table
->GetColumnCount(); col
++)
10831 if (selection
.WithinSelection(table
->GetCell(row
, col
)->GetRange().GetStart()))
10833 if (selBlock
.ColStart() == -1)
10834 selBlock
.ColStart() = col
;
10835 if (selBlock
.ColEnd() == -1)
10836 selBlock
.ColEnd() = col
;
10837 if (col
< selBlock
.ColStart())
10838 selBlock
.ColStart() = col
;
10839 if (col
> selBlock
.ColEnd())
10840 selBlock
.ColEnd() = col
;
10842 if (selBlock
.RowStart() == -1)
10843 selBlock
.RowStart() = row
;
10844 if (selBlock
.RowEnd() == -1)
10845 selBlock
.RowEnd() = row
;
10846 if (row
< selBlock
.RowStart())
10847 selBlock
.RowStart() = row
;
10848 if (row
> selBlock
.RowEnd())
10849 selBlock
.RowEnd() = row
;
10854 if (selBlock
.RowStart() != -1 && selBlock
.RowEnd() != -1 && selBlock
.ColStart() != -1 && selBlock
.ColEnd() != -1)
10855 (*this) = selBlock
;
10859 // See if a whole cell's contents is selected, in which case we can treat the cell as selected.
10860 // wxRTC lacks the ability to select a single cell.
10861 wxRichTextCell
* cell
= wxDynamicCast(ctrl
->GetFocusObject(), wxRichTextCell
);
10862 if (cell
&& (!requireCellSelection
|| (ctrl
->HasSelection() && ctrl
->GetSelectionRange() == cell
->GetOwnRange())))
10865 if (table
->GetCellRowColumnPosition(cell
->GetRange().GetStart(), row
, col
))
10878 // Does this block represent the whole table?
10879 bool wxRichTextTableBlock::IsWholeTable(wxRichTextTable
* table
) const
10881 return (ColStart() == 0 && RowStart() == 0 && ColEnd() == (table
->GetColumnCount()-1) && RowEnd() == (table
->GetRowCount()-1));
10884 // Returns the cell focused in the table, if any
10885 wxRichTextCell
* wxRichTextTableBlock::GetFocusedCell(wxRichTextCtrl
* ctrl
)
10890 wxRichTextCell
* cell
= wxDynamicCast(ctrl
->GetFocusObject(), wxRichTextCell
);
10895 * Module to initialise and clean up handlers
10898 class wxRichTextModule
: public wxModule
10900 DECLARE_DYNAMIC_CLASS(wxRichTextModule
)
10902 wxRichTextModule() {}
10905 wxRichTextBuffer::SetRenderer(new wxRichTextStdRenderer
);
10906 wxRichTextBuffer::InitStandardHandlers();
10907 wxRichTextParagraph::InitDefaultTabs();
10909 wxRichTextXMLHandler::RegisterNodeName(wxT("text"), wxT("wxRichTextPlainText"));
10910 wxRichTextXMLHandler::RegisterNodeName(wxT("symbol"), wxT("wxRichTextPlainText"));
10911 wxRichTextXMLHandler::RegisterNodeName(wxT("image"), wxT("wxRichTextImage"));
10912 wxRichTextXMLHandler::RegisterNodeName(wxT("paragraph"), wxT("wxRichTextParagraph"));
10913 wxRichTextXMLHandler::RegisterNodeName(wxT("paragraphlayout"), wxT("wxRichTextParagraphLayoutBox"));
10914 wxRichTextXMLHandler::RegisterNodeName(wxT("textbox"), wxT("wxRichTextBox"));
10915 wxRichTextXMLHandler::RegisterNodeName(wxT("cell"), wxT("wxRichTextCell"));
10916 wxRichTextXMLHandler::RegisterNodeName(wxT("table"), wxT("wxRichTextTable"));
10917 wxRichTextXMLHandler::RegisterNodeName(wxT("field"), wxT("wxRichTextField"));
10923 wxRichTextBuffer::CleanUpHandlers();
10924 wxRichTextBuffer::CleanUpDrawingHandlers();
10925 wxRichTextBuffer::CleanUpFieldTypes();
10926 wxRichTextXMLHandler::ClearNodeToClassMap();
10927 wxRichTextDecimalToRoman(-1);
10928 wxRichTextParagraph::ClearDefaultTabs();
10929 wxRichTextCtrl::ClearAvailableFontNames();
10930 wxRichTextBuffer::SetRenderer(NULL
);
10934 IMPLEMENT_DYNAMIC_CLASS(wxRichTextModule
, wxModule
)
10937 // If the richtext lib is dynamically loaded after the app has already started
10938 // (such as from wxPython) then the built-in module system will not init this
10939 // module. Provide this function to do it manually.
10940 void wxRichTextModuleInit()
10942 wxModule
* module = new wxRichTextModule
;
10943 wxModule::RegisterModule(module);
10944 wxModule::InitializeModules();
10949 * Commands for undo/redo
10953 wxRichTextCommand::wxRichTextCommand(const wxString
& name
, wxRichTextCommandId id
, wxRichTextBuffer
* buffer
,
10954 wxRichTextParagraphLayoutBox
* container
, wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
): wxCommand(true, name
)
10956 /* wxRichTextAction* action = */ new wxRichTextAction(this, name
, id
, buffer
, container
, ctrl
, ignoreFirstTime
);
10959 wxRichTextCommand::wxRichTextCommand(const wxString
& name
): wxCommand(true, name
)
10963 wxRichTextCommand::~wxRichTextCommand()
10968 void wxRichTextCommand::AddAction(wxRichTextAction
* action
)
10970 if (!m_actions
.Member(action
))
10971 m_actions
.Append(action
);
10974 bool wxRichTextCommand::Do()
10976 for (wxList::compatibility_iterator node
= m_actions
.GetFirst(); node
; node
= node
->GetNext())
10978 wxRichTextAction
* action
= (wxRichTextAction
*) node
->GetData();
10985 bool wxRichTextCommand::Undo()
10987 for (wxList::compatibility_iterator node
= m_actions
.GetLast(); node
; node
= node
->GetPrevious())
10989 wxRichTextAction
* action
= (wxRichTextAction
*) node
->GetData();
10996 void wxRichTextCommand::ClearActions()
10998 WX_CLEAR_LIST(wxList
, m_actions
);
11002 * Individual action
11006 wxRichTextAction::wxRichTextAction(wxRichTextCommand
* cmd
, const wxString
& name
, wxRichTextCommandId id
,
11007 wxRichTextBuffer
* buffer
, wxRichTextParagraphLayoutBox
* container
,
11008 wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
)
11012 m_containerAddress
.Create(buffer
, container
);
11013 m_ignoreThis
= ignoreFirstTime
;
11018 m_newParagraphs
.SetDefaultStyle(buffer
->GetDefaultStyle());
11019 m_newParagraphs
.SetBasicStyle(buffer
->GetBasicStyle());
11021 cmd
->AddAction(this);
11024 wxRichTextAction::~wxRichTextAction()
11030 // Returns the container that this action refers to, using the container address and top-level buffer.
11031 wxRichTextParagraphLayoutBox
* wxRichTextAction::GetContainer() const
11033 wxRichTextParagraphLayoutBox
* container
= wxDynamicCast(GetContainerAddress().GetObject(m_buffer
), wxRichTextParagraphLayoutBox
);
11038 void wxRichTextAction::CalculateRefreshOptimizations(wxArrayInt
& optimizationLineCharPositions
, wxArrayInt
& optimizationLineYPositions
)
11040 // Store a list of line start character and y positions so we can figure out which area
11041 // we need to refresh
11043 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
11044 wxRichTextParagraphLayoutBox
* container
= GetContainer();
11045 wxASSERT(container
!= NULL
);
11049 // NOTE: we're assuming that the buffer is laid out correctly at this point.
11050 // If we had several actions, which only invalidate and leave layout until the
11051 // paint handler is called, then this might not be true. So we may need to switch
11052 // optimisation on only when we're simply adding text and not simultaneously
11053 // deleting a selection, for example. Or, we make sure the buffer is laid out correctly
11054 // first, but of course this means we'll be doing it twice.
11055 if (!m_buffer
->IsDirty() && m_ctrl
) // can only do optimisation if the buffer is already laid out correctly
11057 wxSize clientSize
= m_ctrl
->GetUnscaledSize(m_ctrl
->GetClientSize());
11058 wxPoint firstVisiblePt
= m_ctrl
->GetUnscaledPoint(m_ctrl
->GetFirstVisiblePoint());
11059 int lastY
= firstVisiblePt
.y
+ clientSize
.y
;
11061 wxRichTextParagraph
* para
= container
->GetParagraphAtPosition(GetRange().GetStart());
11062 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(para
);
11065 wxRichTextParagraph
* child
= (wxRichTextParagraph
*) node
->GetData();
11066 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
11069 wxRichTextLine
* line
= node2
->GetData();
11070 wxPoint pt
= line
->GetAbsolutePosition();
11071 wxRichTextRange range
= line
->GetAbsoluteRange();
11075 node2
= wxRichTextLineList::compatibility_iterator();
11076 node
= wxRichTextObjectList::compatibility_iterator();
11078 else if (range
.GetStart() > GetPosition() && pt
.y
>= firstVisiblePt
.y
)
11080 optimizationLineCharPositions
.Add(range
.GetStart());
11081 optimizationLineYPositions
.Add(pt
.y
);
11085 node2
= node2
->GetNext();
11089 node
= node
->GetNext();
11095 bool wxRichTextAction::Do()
11097 m_buffer
->Modify(true);
11099 wxRichTextParagraphLayoutBox
* container
= GetContainer();
11100 wxASSERT(container
!= NULL
);
11106 case wxRICHTEXT_INSERT
:
11108 // Store a list of line start character and y positions so we can figure out which area
11109 // we need to refresh
11110 wxArrayInt optimizationLineCharPositions
;
11111 wxArrayInt optimizationLineYPositions
;
11113 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
11114 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
11117 container
->InsertFragment(GetRange().GetStart(), m_newParagraphs
);
11118 container
->UpdateRanges();
11120 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
11121 // Layout() would stop prematurely at the top level.
11122 container
->InvalidateHierarchy(wxRichTextRange(wxMax(0, GetRange().GetStart()-1), GetRange().GetEnd()));
11124 long newCaretPosition
= GetPosition() + m_newParagraphs
.GetOwnRange().GetLength();
11126 // Character position to caret position
11127 newCaretPosition
--;
11129 // Don't take into account the last newline
11130 if (m_newParagraphs
.GetPartialParagraph())
11131 newCaretPosition
--;
11133 if (m_newParagraphs
.GetChildren().GetCount() > 1)
11135 wxRichTextObject
* p
= (wxRichTextObject
*) m_newParagraphs
.GetChildren().GetLast()->GetData();
11136 if (p
->GetRange().GetLength() == 1)
11137 newCaretPosition
--;
11140 newCaretPosition
= wxMin(newCaretPosition
, (container
->GetOwnRange().GetEnd()-1));
11142 UpdateAppearance(newCaretPosition
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */);
11144 wxRichTextEvent
cmdEvent(
11145 wxEVT_RICHTEXT_CONTENT_INSERTED
,
11146 m_ctrl
? m_ctrl
->GetId() : -1);
11147 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
11148 cmdEvent
.SetRange(GetRange());
11149 cmdEvent
.SetPosition(GetRange().GetStart());
11150 cmdEvent
.SetContainer(container
);
11152 m_buffer
->SendEvent(cmdEvent
);
11156 case wxRICHTEXT_DELETE
:
11158 wxArrayInt optimizationLineCharPositions
;
11159 wxArrayInt optimizationLineYPositions
;
11161 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
11162 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
11165 container
->DeleteRange(GetRange());
11166 container
->UpdateRanges();
11167 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
11168 // Layout() would stop prematurely at the top level.
11169 container
->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
11171 long caretPos
= GetRange().GetStart()-1;
11172 if (caretPos
>= container
->GetOwnRange().GetEnd())
11175 UpdateAppearance(caretPos
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */);
11177 wxRichTextEvent
cmdEvent(
11178 wxEVT_RICHTEXT_CONTENT_DELETED
,
11179 m_ctrl
? m_ctrl
->GetId() : -1);
11180 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
11181 cmdEvent
.SetRange(GetRange());
11182 cmdEvent
.SetPosition(GetRange().GetStart());
11183 cmdEvent
.SetContainer(container
);
11185 m_buffer
->SendEvent(cmdEvent
);
11189 case wxRICHTEXT_CHANGE_STYLE
:
11190 case wxRICHTEXT_CHANGE_PROPERTIES
:
11192 ApplyParagraphs(GetNewParagraphs());
11194 // Invalidate the whole buffer if there were floating objects
11195 if (wxRichTextBuffer::GetFloatingLayoutMode() && container
->GetFloatingObjectCount() > 0)
11196 m_buffer
->InvalidateHierarchy(wxRICHTEXT_ALL
);
11199 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
11200 // Layout() would stop prematurely at the top level.
11201 container
->InvalidateHierarchy(GetRange());
11204 UpdateAppearance(GetPosition());
11206 wxRichTextEvent
cmdEvent(
11207 m_cmdId
== wxRICHTEXT_CHANGE_STYLE
? wxEVT_RICHTEXT_STYLE_CHANGED
: wxEVT_RICHTEXT_PROPERTIES_CHANGED
,
11208 m_ctrl
? m_ctrl
->GetId() : -1);
11209 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
11210 cmdEvent
.SetRange(GetRange());
11211 cmdEvent
.SetPosition(GetRange().GetStart());
11212 cmdEvent
.SetContainer(container
);
11214 m_buffer
->SendEvent(cmdEvent
);
11218 case wxRICHTEXT_CHANGE_ATTRIBUTES
:
11220 wxRichTextObject
* obj
= m_objectAddress
.GetObject(m_buffer
); // container->GetChildAtPosition(GetRange().GetStart());
11223 wxRichTextAttr oldAttr
= obj
->GetAttributes();
11224 obj
->GetAttributes() = m_attributes
;
11225 m_attributes
= oldAttr
;
11228 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
11229 // Layout() would stop prematurely at the top level.
11230 // Invalidate the whole buffer if there were floating objects
11231 if (wxRichTextBuffer::GetFloatingLayoutMode() && container
->GetFloatingObjectCount() > 0)
11232 m_buffer
->InvalidateHierarchy(wxRICHTEXT_ALL
);
11234 container
->InvalidateHierarchy(GetRange());
11236 UpdateAppearance(GetPosition());
11238 wxRichTextEvent
cmdEvent(
11239 wxEVT_RICHTEXT_STYLE_CHANGED
,
11240 m_ctrl
? m_ctrl
->GetId() : -1);
11241 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
11242 cmdEvent
.SetRange(GetRange());
11243 cmdEvent
.SetPosition(GetRange().GetStart());
11244 cmdEvent
.SetContainer(container
);
11246 m_buffer
->SendEvent(cmdEvent
);
11250 case wxRICHTEXT_CHANGE_OBJECT
:
11252 wxRichTextObject
* obj
= m_objectAddress
.GetObject(m_buffer
);
11253 if (obj
&& m_object
&& m_ctrl
)
11255 // The plan is to swap the current object with the stored, previous-state, clone
11256 // We can't get 'node' from the containing buffer (as it doesn't directly store objects)
11257 // so use the parent paragraph
11258 wxRichTextParagraph
* para
= wxDynamicCast(obj
->GetParent(), wxRichTextParagraph
);
11259 wxCHECK_MSG(para
, false, "Invalid parent paragraph");
11261 // The stored object, m_object, may have a stale parent paragraph. This would cause
11262 // a crash during layout, so use obj's parent para, which should be the correct one.
11263 // (An alternative would be to return the parent too from m_objectAddress.GetObject(),
11264 // or to set obj's parent there before returning)
11265 m_object
->SetParent(para
);
11267 wxRichTextObjectList::compatibility_iterator node
= para
->GetChildren().Find(obj
);
11270 wxRichTextObject
* obj
= node
->GetData();
11271 node
->SetData(m_object
);
11276 // We can't rely on the current focus-object remaining valid, if it's e.g. a table's cell.
11277 // And we can't cope with this in the calling code: a user may later click in the cell
11278 // before deciding to Undo() or Redo(). So play safe and set focus to the buffer.
11280 m_ctrl
->SetFocusObject(m_buffer
, false);
11282 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
11283 // Layout() would stop prematurely at the top level.
11284 // Invalidate the whole buffer if there were floating objects
11285 if (wxRichTextBuffer::GetFloatingLayoutMode() && container
->GetFloatingObjectCount() > 0)
11286 m_buffer
->InvalidateHierarchy(wxRICHTEXT_ALL
);
11288 container
->InvalidateHierarchy(GetRange());
11290 UpdateAppearance(GetPosition(), true);
11292 // TODO: send new kind of modification event
11303 bool wxRichTextAction::Undo()
11305 m_buffer
->Modify(true);
11307 wxRichTextParagraphLayoutBox
* container
= GetContainer();
11308 wxASSERT(container
!= NULL
);
11314 case wxRICHTEXT_INSERT
:
11316 wxArrayInt optimizationLineCharPositions
;
11317 wxArrayInt optimizationLineYPositions
;
11319 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
11320 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
11323 container
->DeleteRange(GetRange());
11324 container
->UpdateRanges();
11326 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
11327 // Layout() would stop prematurely at the top level.
11328 container
->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
11330 long newCaretPosition
= GetPosition() - 1;
11332 UpdateAppearance(newCaretPosition
, true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */);
11334 wxRichTextEvent
cmdEvent(
11335 wxEVT_RICHTEXT_CONTENT_DELETED
,
11336 m_ctrl
? m_ctrl
->GetId() : -1);
11337 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
11338 cmdEvent
.SetRange(GetRange());
11339 cmdEvent
.SetPosition(GetRange().GetStart());
11340 cmdEvent
.SetContainer(container
);
11342 m_buffer
->SendEvent(cmdEvent
);
11346 case wxRICHTEXT_DELETE
:
11348 wxArrayInt optimizationLineCharPositions
;
11349 wxArrayInt optimizationLineYPositions
;
11351 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
11352 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
11355 container
->InsertFragment(GetRange().GetStart(), m_oldParagraphs
);
11356 container
->UpdateRanges();
11358 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
11359 // Layout() would stop prematurely at the top level.
11360 container
->InvalidateHierarchy(GetRange());
11362 UpdateAppearance(GetPosition(), true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */);
11364 wxRichTextEvent
cmdEvent(
11365 wxEVT_RICHTEXT_CONTENT_INSERTED
,
11366 m_ctrl
? m_ctrl
->GetId() : -1);
11367 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
11368 cmdEvent
.SetRange(GetRange());
11369 cmdEvent
.SetPosition(GetRange().GetStart());
11370 cmdEvent
.SetContainer(container
);
11372 m_buffer
->SendEvent(cmdEvent
);
11376 case wxRICHTEXT_CHANGE_STYLE
:
11377 case wxRICHTEXT_CHANGE_PROPERTIES
:
11379 ApplyParagraphs(GetOldParagraphs());
11380 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
11381 // Layout() would stop prematurely at the top level.
11382 container
->InvalidateHierarchy(GetRange());
11384 UpdateAppearance(GetPosition());
11386 wxRichTextEvent
cmdEvent(
11387 m_cmdId
== wxRICHTEXT_CHANGE_STYLE
? wxEVT_RICHTEXT_STYLE_CHANGED
: wxEVT_RICHTEXT_PROPERTIES_CHANGED
,
11388 m_ctrl
? m_ctrl
->GetId() : -1);
11389 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
11390 cmdEvent
.SetRange(GetRange());
11391 cmdEvent
.SetPosition(GetRange().GetStart());
11392 cmdEvent
.SetContainer(container
);
11394 m_buffer
->SendEvent(cmdEvent
);
11398 case wxRICHTEXT_CHANGE_ATTRIBUTES
:
11399 case wxRICHTEXT_CHANGE_OBJECT
:
11410 /// Update the control appearance
11411 void wxRichTextAction::UpdateAppearance(long caretPosition
, bool sendUpdateEvent
, wxArrayInt
* optimizationLineCharPositions
, wxArrayInt
* optimizationLineYPositions
, bool isDoCmd
)
11413 wxRichTextParagraphLayoutBox
* container
= GetContainer();
11414 wxASSERT(container
!= NULL
);
11420 m_ctrl
->SetFocusObject(container
);
11421 m_ctrl
->SetCaretPosition(caretPosition
);
11423 if (!m_ctrl
->IsFrozen())
11425 wxRect containerRect
= container
->GetRect();
11427 m_ctrl
->LayoutContent();
11429 // Refresh everything if there were floating objects or the container changed size
11430 // (we can't yet optimize in these cases, since more complex interaction with other content occurs)
11431 if ((wxRichTextBuffer::GetFloatingLayoutMode() && container
->GetFloatingObjectCount() > 0) || (container
->GetParent() && containerRect
!= container
->GetRect()))
11433 m_ctrl
->Refresh(false);
11437 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
11438 // Find refresh rectangle if we are in a position to optimise refresh
11439 if ((m_cmdId
== wxRICHTEXT_INSERT
|| m_cmdId
== wxRICHTEXT_DELETE
) && optimizationLineCharPositions
)
11443 wxSize clientSize
= m_ctrl
->GetUnscaledSize(m_ctrl
->GetClientSize());
11444 wxPoint firstVisiblePt
= m_ctrl
->GetUnscaledPoint(m_ctrl
->GetFirstVisiblePoint());
11446 // Start/end positions
11448 int lastY
= firstVisiblePt
.y
+ clientSize
.y
;
11450 bool foundEnd
= false;
11452 // position offset - how many characters were inserted
11453 int positionOffset
= GetRange().GetLength();
11455 // Determine whether this is Do or Undo, and adjust positionOffset accordingly
11456 if ((m_cmdId
== wxRICHTEXT_DELETE
&& isDoCmd
) || (m_cmdId
== wxRICHTEXT_INSERT
&& !isDoCmd
))
11457 positionOffset
= - positionOffset
;
11459 // find the first line which is being drawn at the same position as it was
11460 // before. Since we're talking about a simple insertion, we can assume
11461 // that the rest of the window does not need to be redrawn.
11462 long pos
= GetRange().GetStart();
11464 wxRichTextParagraph
* para
= container
->GetParagraphAtPosition(pos
, false /* is not caret pos */);
11465 // Since we support floating layout, we should redraw the whole para instead of just
11466 // the first line touching the invalid range.
11469 // In case something was drawn above the paragraph,
11470 // such as a line break, allow a little extra.
11471 firstY
= para
->GetPosition().y
- 4;
11474 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(para
);
11477 wxRichTextParagraph
* child
= (wxRichTextParagraph
*) node
->GetData();
11478 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
11481 wxRichTextLine
* line
= node2
->GetData();
11482 wxPoint pt
= line
->GetAbsolutePosition();
11483 wxRichTextRange range
= line
->GetAbsoluteRange();
11485 // we want to find the first line that is in the same position
11486 // as before. This will mean we're at the end of the changed text.
11488 if (pt
.y
> lastY
) // going past the end of the window, no more info
11490 node2
= wxRichTextLineList::compatibility_iterator();
11491 node
= wxRichTextObjectList::compatibility_iterator();
11493 // Detect last line in the buffer
11494 else if (!node2
->GetNext() && para
->GetRange().Contains(container
->GetOwnRange().GetEnd()))
11496 // If deleting text, make sure we refresh below as well as above
11497 if (positionOffset
>= 0)
11500 lastY
= pt
.y
+ line
->GetSize().y
;
11503 node2
= wxRichTextLineList::compatibility_iterator();
11504 node
= wxRichTextObjectList::compatibility_iterator();
11510 // search for this line being at the same position as before
11511 for (i
= 0; i
< optimizationLineCharPositions
->GetCount(); i
++)
11513 if (((*optimizationLineCharPositions
)[i
] + positionOffset
== range
.GetStart()) &&
11514 ((*optimizationLineYPositions
)[i
] == pt
.y
))
11516 // Stop, we're now the same as we were
11519 lastY
= pt
.y
+ line
->GetSize().y
;
11521 node2
= wxRichTextLineList::compatibility_iterator();
11522 node
= wxRichTextObjectList::compatibility_iterator();
11530 node2
= node2
->GetNext();
11534 node
= node
->GetNext();
11537 firstY
= wxMax(firstVisiblePt
.y
, firstY
);
11539 lastY
= firstVisiblePt
.y
+ clientSize
.y
;
11541 // Convert to device coordinates
11542 wxRect
rect(m_ctrl
->GetPhysicalPoint(m_ctrl
->GetScaledPoint(wxPoint(firstVisiblePt
.x
, firstY
))), m_ctrl
->GetScaledSize(wxSize(clientSize
.x
, lastY
- firstY
)));
11543 m_ctrl
->RefreshRect(rect
);
11547 m_ctrl
->Refresh(false);
11549 m_ctrl
->PositionCaret();
11551 // This causes styles to persist when doing programmatic
11552 // content creation except when Freeze/Thaw is used, so
11553 // disable this and check for the consequences.
11554 // m_ctrl->SetDefaultStyleToCursorStyle();
11556 if (sendUpdateEvent
)
11557 wxTextCtrl::SendTextUpdatedEvent(m_ctrl
);
11562 /// Replace the buffer paragraphs with the new ones.
11563 void wxRichTextAction::ApplyParagraphs(const wxRichTextParagraphLayoutBox
& fragment
)
11565 wxRichTextParagraphLayoutBox
* container
= GetContainer();
11566 wxASSERT(container
!= NULL
);
11570 wxRichTextObjectList::compatibility_iterator node
= fragment
.GetChildren().GetFirst();
11573 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
11574 wxASSERT (para
!= NULL
);
11576 // We'll replace the existing paragraph by finding the paragraph at this position,
11577 // delete its node data, and setting a copy as the new node data.
11578 // TODO: make more efficient by simply swapping old and new paragraph objects.
11580 wxRichTextParagraph
* existingPara
= container
->GetParagraphAtPosition(para
->GetRange().GetStart());
11583 wxRichTextObjectList::compatibility_iterator bufferParaNode
= container
->GetChildren().Find(existingPara
);
11584 if (bufferParaNode
)
11586 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(*para
);
11587 newPara
->SetParent(container
);
11589 bufferParaNode
->SetData(newPara
);
11591 delete existingPara
;
11595 node
= node
->GetNext();
11602 * This stores beginning and end positions for a range of data.
11605 WX_DEFINE_OBJARRAY(wxRichTextRangeArray
);
11607 /// Limit this range to be within 'range'
11608 bool wxRichTextRange::LimitTo(const wxRichTextRange
& range
)
11610 if (m_start
< range
.m_start
)
11611 m_start
= range
.m_start
;
11613 if (m_end
> range
.m_end
)
11614 m_end
= range
.m_end
;
11620 * wxRichTextImage implementation
11621 * This object represents an image.
11624 IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage
, wxRichTextObject
)
11626 wxRichTextImage::wxRichTextImage(const wxImage
& image
, wxRichTextObject
* parent
, wxRichTextAttr
* charStyle
):
11627 wxRichTextObject(parent
)
11630 m_imageBlock
.MakeImageBlockDefaultQuality(image
, wxBITMAP_TYPE_PNG
);
11632 SetAttributes(*charStyle
);
11635 wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock
& imageBlock
, wxRichTextObject
* parent
, wxRichTextAttr
* charStyle
):
11636 wxRichTextObject(parent
)
11639 m_imageBlock
= imageBlock
;
11641 SetAttributes(*charStyle
);
11644 wxRichTextImage::~wxRichTextImage()
11648 void wxRichTextImage::Init()
11650 m_originalImageSize
= wxSize(-1, -1);
11653 /// Create a cached image at the required size
11654 bool wxRichTextImage::LoadImageCache(wxDC
& dc
, bool resetCache
, const wxSize
& parentSize
)
11656 if (!m_imageBlock
.IsOk())
11659 // If we have an original image size, use that to compute the cached bitmap size
11660 // instead of loading the image each time. This way we can avoid loading
11661 // the image so long as the new cached bitmap size hasn't changed.
11664 if (resetCache
|| m_originalImageSize
.GetWidth() <= 0 || m_originalImageSize
.GetHeight() <= 0)
11666 m_imageCache
= wxNullBitmap
;
11668 m_imageBlock
.Load(image
);
11672 m_originalImageSize
= wxSize(image
.GetWidth(), image
.GetHeight());
11675 int width
= m_originalImageSize
.GetWidth();
11676 int height
= m_originalImageSize
.GetHeight();
11678 int parentWidth
= 0;
11679 int parentHeight
= 0;
11682 int maxHeight
= -1;
11684 wxSize sz
= parentSize
;
11685 if (sz
== wxDefaultSize
)
11687 if (GetParent() && GetParent()->GetParent())
11688 sz
= GetParent()->GetParent()->GetCachedSize();
11691 if (sz
!= wxDefaultSize
)
11693 wxRichTextBuffer
* buffer
= GetBuffer();
11696 // Find the actual space available when margin is taken into account
11697 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
11698 marginRect
= wxRect(0, 0, sz
.x
, sz
.y
);
11699 if (GetParent() && GetParent()->GetParent())
11701 buffer
->GetBoxRects(dc
, buffer
, GetParent()->GetParent()->GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
11702 sz
= contentRect
.GetSize();
11705 // Use a minimum size to stop images becoming very small
11706 parentWidth
= wxMax(100, sz
.GetWidth());
11707 parentHeight
= wxMax(100, sz
.GetHeight());
11709 if (buffer
->GetRichTextCtrl())
11710 // Start with a maximum width of the control size, even if not specified by the content,
11711 // to minimize the amount of picture overlapping the right-hand side
11712 maxWidth
= parentWidth
;
11716 if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetWidth().GetValue() > 0)
11718 if (parentWidth
> 0 && GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
11719 width
= (int) ((GetAttributes().GetTextBoxAttr().GetWidth().GetValue() * parentWidth
)/100.0);
11720 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
11721 width
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetWidth().GetValue());
11722 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
11723 width
= GetAttributes().GetTextBoxAttr().GetWidth().GetValue();
11726 // Limit to max width
11728 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() > 0)
11732 if (parentWidth
> 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
11733 mw
= (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() * parentWidth
)/100.0);
11734 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
11735 mw
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue());
11736 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
11737 mw
= GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue();
11739 // If we already have a smaller max width due to the constraints of the control size,
11740 // don't use the larger max width.
11741 if (mw
!= -1 && ((maxWidth
== -1) || (mw
< maxWidth
)))
11745 if (maxWidth
> 0 && width
> maxWidth
)
11748 // Preserve the aspect ratio
11749 if (width
!= m_originalImageSize
.GetWidth())
11750 height
= (int) (float(m_originalImageSize
.GetHeight()) * (float(width
)/float(m_originalImageSize
.GetWidth())));
11752 if (GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetHeight().GetValue() > 0)
11754 if (parentHeight
> 0 && GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
11755 height
= (int) ((GetAttributes().GetTextBoxAttr().GetHeight().GetValue() * parentHeight
)/100.0);
11756 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
11757 height
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetHeight().GetValue());
11758 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
11759 height
= GetAttributes().GetTextBoxAttr().GetHeight().GetValue();
11761 // Preserve the aspect ratio
11762 if (height
!= m_originalImageSize
.GetHeight())
11763 width
= (int) (float(m_originalImageSize
.GetWidth()) * (float(height
)/float(m_originalImageSize
.GetHeight())));
11766 // Limit to max height
11768 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() > 0)
11770 if (parentHeight
> 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
11771 maxHeight
= (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() * parentHeight
)/100.0);
11772 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
11773 maxHeight
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue());
11774 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
11775 maxHeight
= GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue();
11778 if (maxHeight
> 0 && height
> maxHeight
)
11780 height
= maxHeight
;
11782 // Preserve the aspect ratio
11783 if (height
!= m_originalImageSize
.GetHeight())
11784 width
= (int) (float(m_originalImageSize
.GetWidth()) * (float(height
)/float(m_originalImageSize
.GetHeight())));
11787 // Prevent the use of zero size
11788 width
= wxMax(1, width
);
11789 height
= wxMax(1, height
);
11791 if (m_imageCache
.IsOk() && m_imageCache
.GetWidth() == width
&& m_imageCache
.GetHeight() == height
)
11793 // Do nothing, we didn't need to change the image cache
11799 m_imageBlock
.Load(image
);
11804 if (image
.GetWidth() == width
&& image
.GetHeight() == height
)
11805 m_imageCache
= wxBitmap(image
);
11808 // If the original width and height is small, e.g. 400 or below,
11809 // scale up and then down to improve image quality. This can make
11810 // a big difference, with not much performance hit.
11811 int upscaleThreshold
= 400;
11813 if (image
.GetWidth() <= upscaleThreshold
|| image
.GetHeight() <= upscaleThreshold
)
11815 img
= image
.Scale(image
.GetWidth()*2, image
.GetHeight()*2);
11816 img
.Rescale(width
, height
, wxIMAGE_QUALITY_HIGH
);
11819 img
= image
.Scale(width
, height
, wxIMAGE_QUALITY_HIGH
);
11820 m_imageCache
= wxBitmap(img
);
11824 return m_imageCache
.IsOk();
11828 bool wxRichTextImage::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& WXUNUSED(range
), const wxRichTextSelection
& selection
, const wxRect
& rect
, int WXUNUSED(descent
), int WXUNUSED(style
))
11833 // Don't need cached size AFAIK
11834 // wxSize size = GetCachedSize();
11835 if (!LoadImageCache(dc
))
11838 wxRichTextAttr
attr(GetAttributes());
11839 context
.ApplyVirtualAttributes(attr
, this);
11841 DrawBoxAttributes(dc
, GetBuffer(), attr
, wxRect(rect
.GetPosition(), GetCachedSize()));
11843 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
11844 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
11845 marginRect
= rect
; // outer rectangle, will calculate contentRect
11846 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
11848 dc
.DrawBitmap(m_imageCache
, contentRect
.x
, contentRect
.y
, true);
11850 if (selection
.WithinSelection(GetRange().GetStart(), this))
11852 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
11853 wxCheckSetPen(dc
, *wxBLACK_PEN
);
11854 dc
.SetLogicalFunction(wxINVERT
);
11855 dc
.DrawRectangle(contentRect
);
11856 dc
.SetLogicalFunction(wxCOPY
);
11862 /// Lay the item out
11863 bool wxRichTextImage::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& WXUNUSED(parentRect
), int WXUNUSED(style
))
11865 if (!LoadImageCache(dc
))
11868 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
11869 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
11870 contentRect
= wxRect(wxPoint(0,0), imageSize
);
11872 wxRichTextAttr
attr(GetAttributes());
11873 context
.ApplyVirtualAttributes(attr
, this);
11875 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
11877 wxSize overallSize
= marginRect
.GetSize();
11879 SetCachedSize(overallSize
);
11880 SetMaxSize(overallSize
);
11881 SetMinSize(overallSize
);
11882 SetPosition(rect
.GetPosition());
11887 /// Get/set the object size for the given range. Returns false if the range
11888 /// is invalid for this object.
11889 bool wxRichTextImage::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& WXUNUSED(descent
), wxDC
& dc
, wxRichTextDrawingContext
& context
, int WXUNUSED(flags
), const wxPoint
& WXUNUSED(position
), const wxSize
& parentSize
, wxArrayInt
* partialExtents
) const
11891 if (!range
.IsWithin(GetRange()))
11894 if (!((wxRichTextImage
*)this)->LoadImageCache(dc
, false, parentSize
))
11896 size
.x
= 0; size
.y
= 0;
11897 if (partialExtents
)
11898 partialExtents
->Add(0);
11902 wxRichTextAttr
attr(GetAttributes());
11903 context
.ApplyVirtualAttributes(attr
, (wxRichTextObject
*) this);
11905 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
11906 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
11907 contentRect
= wxRect(wxPoint(0,0), imageSize
);
11908 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
11910 wxSize overallSize
= marginRect
.GetSize();
11912 if (partialExtents
)
11913 partialExtents
->Add(overallSize
.x
);
11915 size
= overallSize
;
11920 // Get the 'natural' size for an object. For an image, it would be the
11922 wxTextAttrSize
wxRichTextImage::GetNaturalSize() const
11924 wxTextAttrSize size
;
11925 if (GetImageCache().IsOk())
11927 size
.SetWidth(GetImageCache().GetWidth(), wxTEXT_ATTR_UNITS_PIXELS
);
11928 size
.SetHeight(GetImageCache().GetHeight(), wxTEXT_ATTR_UNITS_PIXELS
);
11935 void wxRichTextImage::Copy(const wxRichTextImage
& obj
)
11937 wxRichTextObject::Copy(obj
);
11939 m_imageBlock
= obj
.m_imageBlock
;
11940 m_originalImageSize
= obj
.m_originalImageSize
;
11943 /// Edit properties via a GUI
11944 bool wxRichTextImage::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
11946 wxRichTextObjectPropertiesDialog
imageDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Picture Properties"));
11947 imageDlg
.SetAttributes(GetAttributes());
11949 if (imageDlg
.ShowModal() == wxID_OK
)
11951 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
11952 // indeterminate in the object.
11953 imageDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
11965 /// Compare two attribute objects
11966 bool wxTextAttrEq(const wxRichTextAttr
& attr1
, const wxRichTextAttr
& attr2
)
11968 return (attr1
== attr2
);
11972 bool wxRichTextTabsEq(const wxArrayInt
& tabs1
, const wxArrayInt
& tabs2
)
11974 if (tabs1
.GetCount() != tabs2
.GetCount())
11978 for (i
= 0; i
< tabs1
.GetCount(); i
++)
11980 if (tabs1
[i
] != tabs2
[i
])
11986 bool wxRichTextApplyStyle(wxRichTextAttr
& destStyle
, const wxRichTextAttr
& style
, wxRichTextAttr
* compareWith
)
11988 return destStyle
.Apply(style
, compareWith
);
11991 // Remove attributes
11992 bool wxRichTextRemoveStyle(wxRichTextAttr
& destStyle
, const wxRichTextAttr
& style
)
11994 return destStyle
.RemoveStyle(style
);
11997 /// Combine two bitlists, specifying the bits of interest with separate flags.
11998 bool wxRichTextCombineBitlists(int& valueA
, int valueB
, int& flagsA
, int flagsB
)
12000 return wxRichTextAttr::CombineBitlists(valueA
, valueB
, flagsA
, flagsB
);
12003 /// Compare two bitlists
12004 bool wxRichTextBitlistsEqPartial(int valueA
, int valueB
, int flags
)
12006 return wxRichTextAttr::BitlistsEqPartial(valueA
, valueB
, flags
);
12009 /// Split into paragraph and character styles
12010 bool wxRichTextSplitParaCharStyles(const wxRichTextAttr
& style
, wxRichTextAttr
& parStyle
, wxRichTextAttr
& charStyle
)
12012 return wxRichTextAttr::SplitParaCharStyles(style
, parStyle
, charStyle
);
12015 /// Convert a decimal to Roman numerals
12016 wxString
wxRichTextDecimalToRoman(long n
)
12018 static wxArrayInt decimalNumbers
;
12019 static wxArrayString romanNumbers
;
12024 decimalNumbers
.Clear();
12025 romanNumbers
.Clear();
12026 return wxEmptyString
;
12029 if (decimalNumbers
.GetCount() == 0)
12031 #define wxRichTextAddDecRom(n, r) decimalNumbers.Add(n); romanNumbers.Add(r);
12033 wxRichTextAddDecRom(1000, wxT("M"));
12034 wxRichTextAddDecRom(900, wxT("CM"));
12035 wxRichTextAddDecRom(500, wxT("D"));
12036 wxRichTextAddDecRom(400, wxT("CD"));
12037 wxRichTextAddDecRom(100, wxT("C"));
12038 wxRichTextAddDecRom(90, wxT("XC"));
12039 wxRichTextAddDecRom(50, wxT("L"));
12040 wxRichTextAddDecRom(40, wxT("XL"));
12041 wxRichTextAddDecRom(10, wxT("X"));
12042 wxRichTextAddDecRom(9, wxT("IX"));
12043 wxRichTextAddDecRom(5, wxT("V"));
12044 wxRichTextAddDecRom(4, wxT("IV"));
12045 wxRichTextAddDecRom(1, wxT("I"));
12051 while (n
> 0 && i
< 13)
12053 if (n
>= decimalNumbers
[i
])
12055 n
-= decimalNumbers
[i
];
12056 roman
+= romanNumbers
[i
];
12063 if (roman
.IsEmpty())
12069 * wxRichTextFileHandler
12070 * Base class for file handlers
12073 IMPLEMENT_CLASS(wxRichTextFileHandler
, wxObject
)
12075 #if wxUSE_FFILE && wxUSE_STREAMS
12076 bool wxRichTextFileHandler::LoadFile(wxRichTextBuffer
*buffer
, const wxString
& filename
)
12078 wxFFileInputStream
stream(filename
);
12080 return LoadFile(buffer
, stream
);
12085 bool wxRichTextFileHandler::SaveFile(wxRichTextBuffer
*buffer
, const wxString
& filename
)
12087 wxFFileOutputStream
stream(filename
);
12089 return SaveFile(buffer
, stream
);
12093 #endif // wxUSE_FFILE && wxUSE_STREAMS
12095 /// Can we handle this filename (if using files)? By default, checks the extension.
12096 bool wxRichTextFileHandler::CanHandle(const wxString
& filename
) const
12098 wxString path
, file
, ext
;
12099 wxFileName::SplitPath(filename
, & path
, & file
, & ext
);
12101 return (ext
.Lower() == GetExtension());
12105 * wxRichTextTextHandler
12106 * Plain text handler
12109 IMPLEMENT_CLASS(wxRichTextPlainTextHandler
, wxRichTextFileHandler
)
12112 bool wxRichTextPlainTextHandler::DoLoadFile(wxRichTextBuffer
*buffer
, wxInputStream
& stream
)
12114 if (!stream
.IsOk())
12120 while (!stream
.Eof())
12122 int ch
= stream
.GetC();
12126 if (ch
== 10 && lastCh
!= 13)
12129 if (ch
> 0 && ch
!= 10)
12136 buffer
->ResetAndClearCommands();
12138 buffer
->AddParagraphs(str
);
12139 buffer
->UpdateRanges();
12144 bool wxRichTextPlainTextHandler::DoSaveFile(wxRichTextBuffer
*buffer
, wxOutputStream
& stream
)
12146 if (!stream
.IsOk())
12149 wxString text
= buffer
->GetText();
12151 wxString newLine
= wxRichTextLineBreakChar
;
12152 text
.Replace(newLine
, wxT("\n"));
12154 wxCharBuffer buf
= text
.ToAscii();
12156 stream
.Write((const char*) buf
, text
.length());
12159 #endif // wxUSE_STREAMS
12162 * Stores information about an image, in binary in-memory form
12165 wxRichTextImageBlock::wxRichTextImageBlock()
12170 wxRichTextImageBlock::wxRichTextImageBlock(const wxRichTextImageBlock
& block
):wxObject()
12176 wxRichTextImageBlock::~wxRichTextImageBlock()
12181 void wxRichTextImageBlock::Init()
12185 m_imageType
= wxBITMAP_TYPE_INVALID
;
12188 void wxRichTextImageBlock::Clear()
12192 m_imageType
= wxBITMAP_TYPE_INVALID
;
12196 // Load the original image into a memory block.
12197 // If the image is not a JPEG, we must convert it into a JPEG
12198 // to conserve space.
12199 // If it's not a JPEG we can make use of 'image', already scaled, so we don't have to
12200 // load the image a 2nd time.
12202 bool wxRichTextImageBlock::MakeImageBlock(const wxString
& filename
, wxBitmapType imageType
,
12203 wxImage
& image
, bool convertToJPEG
)
12205 m_imageType
= imageType
;
12207 wxString
filenameToRead(filename
);
12208 bool removeFile
= false;
12210 if (imageType
== wxBITMAP_TYPE_INVALID
)
12211 return false; // Could not determine image type
12213 if ((imageType
!= wxBITMAP_TYPE_JPEG
) && convertToJPEG
)
12215 wxString tempFile
=
12216 wxFileName::CreateTempFileName(_("image"));
12218 wxASSERT(!tempFile
.IsEmpty());
12220 image
.SaveFile(tempFile
, wxBITMAP_TYPE_JPEG
);
12221 filenameToRead
= tempFile
;
12224 m_imageType
= wxBITMAP_TYPE_JPEG
;
12227 if (!file
.Open(filenameToRead
))
12230 m_dataSize
= (size_t) file
.Length();
12235 m_data
= ReadBlock(filenameToRead
, m_dataSize
);
12238 wxRemoveFile(filenameToRead
);
12240 return (m_data
!= NULL
);
12243 // Make an image block from the wxImage in the given
12245 bool wxRichTextImageBlock::MakeImageBlock(wxImage
& image
, wxBitmapType imageType
, int quality
)
12247 image
.SetOption(wxT("quality"), quality
);
12249 if (imageType
== wxBITMAP_TYPE_INVALID
)
12250 return false; // Could not determine image type
12252 return DoMakeImageBlock(image
, imageType
);
12255 // Uses a const wxImage for efficiency, but can't set quality (only relevant for JPEG)
12256 bool wxRichTextImageBlock::MakeImageBlockDefaultQuality(const wxImage
& image
, wxBitmapType imageType
)
12258 if (imageType
== wxBITMAP_TYPE_INVALID
)
12259 return false; // Could not determine image type
12261 return DoMakeImageBlock(image
, imageType
);
12264 // Makes the image block
12265 bool wxRichTextImageBlock::DoMakeImageBlock(const wxImage
& image
, wxBitmapType imageType
)
12267 wxMemoryOutputStream memStream
;
12268 if (!image
.SaveFile(memStream
, imageType
))
12273 unsigned char* block
= new unsigned char[memStream
.GetSize()];
12281 m_imageType
= imageType
;
12282 m_dataSize
= memStream
.GetSize();
12284 memStream
.CopyTo(m_data
, m_dataSize
);
12286 return (m_data
!= NULL
);
12290 bool wxRichTextImageBlock::Write(const wxString
& filename
)
12292 return WriteBlock(filename
, m_data
, m_dataSize
);
12295 void wxRichTextImageBlock::Copy(const wxRichTextImageBlock
& block
)
12297 m_imageType
= block
.m_imageType
;
12299 m_dataSize
= block
.m_dataSize
;
12300 if (m_dataSize
== 0)
12303 m_data
= new unsigned char[m_dataSize
];
12305 for (i
= 0; i
< m_dataSize
; i
++)
12306 m_data
[i
] = block
.m_data
[i
];
12310 void wxRichTextImageBlock::operator=(const wxRichTextImageBlock
& block
)
12315 // Load a wxImage from the block
12316 bool wxRichTextImageBlock::Load(wxImage
& image
)
12321 // Read in the image.
12323 wxMemoryInputStream
mstream(m_data
, m_dataSize
);
12324 bool success
= image
.LoadFile(mstream
, GetImageType());
12326 wxString tempFile
= wxFileName::CreateTempFileName(_("image"));
12327 wxASSERT(!tempFile
.IsEmpty());
12329 if (!WriteBlock(tempFile
, m_data
, m_dataSize
))
12333 success
= image
.LoadFile(tempFile
, GetImageType());
12334 wxRemoveFile(tempFile
);
12340 // Write data in hex to a stream
12341 bool wxRichTextImageBlock::WriteHex(wxOutputStream
& stream
)
12343 if (m_dataSize
== 0)
12346 int bufSize
= 100000;
12347 if (int(2*m_dataSize
) < bufSize
)
12348 bufSize
= 2*m_dataSize
;
12349 char* buf
= new char[bufSize
+1];
12351 int left
= m_dataSize
;
12356 if (left
*2 > bufSize
)
12358 n
= bufSize
; left
-= (bufSize
/2);
12362 n
= left
*2; left
= 0;
12366 for (i
= 0; i
< (n
/2); i
++)
12368 wxDecToHex(m_data
[j
], b
, b
+1);
12373 stream
.Write((const char*) buf
, n
);
12379 // Read data in hex from a stream
12380 bool wxRichTextImageBlock::ReadHex(wxInputStream
& stream
, int length
, wxBitmapType imageType
)
12382 int dataSize
= length
/2;
12387 // create a null terminated temporary string:
12391 m_data
= new unsigned char[dataSize
];
12393 for (i
= 0; i
< dataSize
; i
++)
12395 str
[0] = (char)stream
.GetC();
12396 str
[1] = (char)stream
.GetC();
12398 m_data
[i
] = (unsigned char)wxHexToDec(str
);
12401 m_dataSize
= dataSize
;
12402 m_imageType
= imageType
;
12407 // Allocate and read from stream as a block of memory
12408 unsigned char* wxRichTextImageBlock::ReadBlock(wxInputStream
& stream
, size_t size
)
12410 unsigned char* block
= new unsigned char[size
];
12414 stream
.Read(block
, size
);
12419 unsigned char* wxRichTextImageBlock::ReadBlock(const wxString
& filename
, size_t size
)
12421 wxFileInputStream
stream(filename
);
12422 if (!stream
.IsOk())
12425 return ReadBlock(stream
, size
);
12428 // Write memory block to stream
12429 bool wxRichTextImageBlock::WriteBlock(wxOutputStream
& stream
, unsigned char* block
, size_t size
)
12431 stream
.Write((void*) block
, size
);
12432 return stream
.IsOk();
12436 // Write memory block to file
12437 bool wxRichTextImageBlock::WriteBlock(const wxString
& filename
, unsigned char* block
, size_t size
)
12439 wxFileOutputStream
outStream(filename
);
12440 if (!outStream
.IsOk())
12443 return WriteBlock(outStream
, block
, size
);
12446 // Gets the extension for the block's type
12447 wxString
wxRichTextImageBlock::GetExtension() const
12449 wxImageHandler
* handler
= wxImage::FindHandler(GetImageType());
12451 return handler
->GetExtension();
12453 return wxEmptyString
;
12459 * The data object for a wxRichTextBuffer
12462 const wxChar
*wxRichTextBufferDataObject::ms_richTextBufferFormatId
= wxT("wxRichText");
12464 wxRichTextBufferDataObject::wxRichTextBufferDataObject(wxRichTextBuffer
* richTextBuffer
)
12466 m_richTextBuffer
= richTextBuffer
;
12468 // this string should uniquely identify our format, but is otherwise
12470 m_formatRichTextBuffer
.SetId(GetRichTextBufferFormatId());
12472 SetFormat(m_formatRichTextBuffer
);
12475 wxRichTextBufferDataObject::~wxRichTextBufferDataObject()
12477 delete m_richTextBuffer
;
12480 // after a call to this function, the richTextBuffer is owned by the caller and it
12481 // is responsible for deleting it!
12482 wxRichTextBuffer
* wxRichTextBufferDataObject::GetRichTextBuffer()
12484 wxRichTextBuffer
* richTextBuffer
= m_richTextBuffer
;
12485 m_richTextBuffer
= NULL
;
12487 return richTextBuffer
;
12490 wxDataFormat
wxRichTextBufferDataObject::GetPreferredFormat(Direction
WXUNUSED(dir
)) const
12492 return m_formatRichTextBuffer
;
12495 size_t wxRichTextBufferDataObject::GetDataSize() const
12497 if (!m_richTextBuffer
)
12503 wxStringOutputStream
stream(& bufXML
);
12504 if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
))
12506 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
12512 wxCharBuffer buffer
= bufXML
.mb_str(wxConvUTF8
);
12513 return strlen(buffer
) + 1;
12515 return bufXML
.Length()+1;
12519 bool wxRichTextBufferDataObject::GetDataHere(void *pBuf
) const
12521 if (!pBuf
|| !m_richTextBuffer
)
12527 wxStringOutputStream
stream(& bufXML
);
12528 if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
))
12530 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
12536 wxCharBuffer buffer
= bufXML
.mb_str(wxConvUTF8
);
12537 size_t len
= strlen(buffer
);
12538 memcpy((char*) pBuf
, (const char*) buffer
, len
);
12539 ((char*) pBuf
)[len
] = 0;
12541 size_t len
= bufXML
.Length();
12542 memcpy((char*) pBuf
, (const char*) bufXML
.c_str(), len
);
12543 ((char*) pBuf
)[len
] = 0;
12549 bool wxRichTextBufferDataObject::SetData(size_t WXUNUSED(len
), const void *buf
)
12551 wxDELETE(m_richTextBuffer
);
12553 wxString
bufXML((const char*) buf
, wxConvUTF8
);
12555 m_richTextBuffer
= new wxRichTextBuffer
;
12557 wxStringInputStream
stream(bufXML
);
12558 if (!m_richTextBuffer
->LoadFile(stream
, wxRICHTEXT_TYPE_XML
))
12560 wxLogError(wxT("Could not read the buffer from an XML stream.\nYou may have forgotten to add the XML file handler."));
12562 wxDELETE(m_richTextBuffer
);
12574 * wxRichTextFontTable
12575 * Manages quick access to a pool of fonts for rendering rich text
12578 WX_DECLARE_STRING_HASH_MAP_WITH_DECL(wxFont
, wxRichTextFontTableHashMap
, class WXDLLIMPEXP_RICHTEXT
);
12580 class wxRichTextFontTableData
: public wxObjectRefData
12583 wxRichTextFontTableData() {}
12585 wxFont
FindFont(const wxRichTextAttr
& fontSpec
, double fontScale
);
12587 wxRichTextFontTableHashMap m_hashMap
;
12590 wxFont
wxRichTextFontTableData::FindFont(const wxRichTextAttr
& fontSpec
, double fontScale
)
12592 wxString
facename(fontSpec
.GetFontFaceName());
12594 int fontSize
= fontSpec
.GetFontSize();
12595 if (fontScale
!= 1.0)
12596 fontSize
= (int) ((double(fontSize
) * fontScale
) + 0.5);
12599 if (fontSpec
.HasFontPixelSize() && !fontSpec
.HasFontPointSize())
12603 wxString spec
= wxString::Format(wxT("%d-%s-%d-%d-%d-%d-%s-%d"),
12604 fontSize
, units
.c_str(), fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), (int) fontSpec
.GetFontUnderlined(), (int) fontSpec
.GetFontStrikethrough(),
12605 facename
.c_str(), (int) fontSpec
.GetFontEncoding());
12607 wxRichTextFontTableHashMap::iterator entry
= m_hashMap
.find(spec
);
12608 if ( entry
== m_hashMap
.end() )
12610 if (fontSpec
.HasFontPixelSize() && !fontSpec
.HasFontPointSize())
12612 wxFont
font(wxSize(0, fontSize
), wxFONTFAMILY_DEFAULT
, fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), fontSpec
.GetFontUnderlined(), facename
);
12613 if (fontSpec
.HasFontStrikethrough() && fontSpec
.GetFontStrikethrough())
12614 font
.SetStrikethrough(true);
12615 m_hashMap
[spec
] = font
;
12620 wxFont
font(fontSize
, wxFONTFAMILY_DEFAULT
, fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), fontSpec
.GetFontUnderlined(), facename
.c_str());
12621 if (fontSpec
.HasFontStrikethrough() && fontSpec
.GetFontStrikethrough())
12622 font
.SetStrikethrough(true);
12624 m_hashMap
[spec
] = font
;
12630 return entry
->second
;
12634 IMPLEMENT_DYNAMIC_CLASS(wxRichTextFontTable
, wxObject
)
12636 wxRichTextFontTable::wxRichTextFontTable()
12638 m_refData
= new wxRichTextFontTableData
;
12642 wxRichTextFontTable::wxRichTextFontTable(const wxRichTextFontTable
& table
)
12648 wxRichTextFontTable::~wxRichTextFontTable()
12653 bool wxRichTextFontTable::operator == (const wxRichTextFontTable
& table
) const
12655 return (m_refData
== table
.m_refData
);
12658 void wxRichTextFontTable::operator= (const wxRichTextFontTable
& table
)
12661 m_fontScale
= table
.m_fontScale
;
12664 wxFont
wxRichTextFontTable::FindFont(const wxRichTextAttr
& fontSpec
)
12666 wxRichTextFontTableData
* data
= (wxRichTextFontTableData
*) m_refData
;
12668 return data
->FindFont(fontSpec
, m_fontScale
);
12673 void wxRichTextFontTable::Clear()
12675 wxRichTextFontTableData
* data
= (wxRichTextFontTableData
*) m_refData
;
12677 data
->m_hashMap
.clear();
12680 void wxRichTextFontTable::SetFontScale(double fontScale
)
12682 if (fontScale
!= m_fontScale
)
12684 m_fontScale
= fontScale
;
12689 void wxTextBoxAttr::Reset()
12692 m_floatMode
= wxTEXT_BOX_ATTR_FLOAT_NONE
;
12693 m_clearMode
= wxTEXT_BOX_ATTR_CLEAR_NONE
;
12694 m_collapseMode
= wxTEXT_BOX_ATTR_COLLAPSE_NONE
;
12695 m_verticalAlignment
= wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_NONE
;
12696 m_boxStyleName
= wxEmptyString
;
12700 m_position
.Reset();
12711 bool wxTextBoxAttr::operator== (const wxTextBoxAttr
& attr
) const
12714 m_flags
== attr
.m_flags
&&
12715 m_floatMode
== attr
.m_floatMode
&&
12716 m_clearMode
== attr
.m_clearMode
&&
12717 m_collapseMode
== attr
.m_collapseMode
&&
12718 m_verticalAlignment
== attr
.m_verticalAlignment
&&
12720 m_margins
== attr
.m_margins
&&
12721 m_padding
== attr
.m_padding
&&
12722 m_position
== attr
.m_position
&&
12724 m_size
== attr
.m_size
&&
12725 m_minSize
== attr
.m_minSize
&&
12726 m_maxSize
== attr
.m_maxSize
&&
12728 m_border
== attr
.m_border
&&
12729 m_outline
== attr
.m_outline
&&
12731 m_boxStyleName
== attr
.m_boxStyleName
12735 // Partial equality test
12736 bool wxTextBoxAttr::EqPartial(const wxTextBoxAttr
& attr
, bool weakTest
) const
12739 ((!HasFloatMode() && attr
.HasFloatMode()) ||
12740 (!HasClearMode() && attr
.HasClearMode()) ||
12741 (!HasCollapseBorders() && attr
.HasCollapseBorders()) ||
12742 (!HasVerticalAlignment() && attr
.HasVerticalAlignment()) ||
12743 (!HasBoxStyleName() && attr
.HasBoxStyleName())))
12747 if (attr
.HasFloatMode() && HasFloatMode() && (GetFloatMode() != attr
.GetFloatMode()))
12750 if (attr
.HasClearMode() && HasClearMode() && (GetClearMode() != attr
.GetClearMode()))
12753 if (attr
.HasCollapseBorders() && HasCollapseBorders() && (attr
.GetCollapseBorders() != GetCollapseBorders()))
12756 if (attr
.HasVerticalAlignment() && HasVerticalAlignment() && (attr
.GetVerticalAlignment() != GetVerticalAlignment()))
12759 if (attr
.HasBoxStyleName() && HasBoxStyleName() && (attr
.GetBoxStyleName() != GetBoxStyleName()))
12764 if (!m_position
.EqPartial(attr
.m_position
, weakTest
))
12769 if (!m_size
.EqPartial(attr
.m_size
, weakTest
))
12771 if (!m_minSize
.EqPartial(attr
.m_minSize
, weakTest
))
12773 if (!m_maxSize
.EqPartial(attr
.m_maxSize
, weakTest
))
12778 if (!m_margins
.EqPartial(attr
.m_margins
, weakTest
))
12783 if (!m_padding
.EqPartial(attr
.m_padding
, weakTest
))
12788 if (!GetBorder().EqPartial(attr
.GetBorder(), weakTest
))
12793 if (!GetOutline().EqPartial(attr
.GetOutline(), weakTest
))
12799 // Merges the given attributes. If compareWith
12800 // is non-NULL, then it will be used to mask out those attributes that are the same in style
12801 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
12802 bool wxTextBoxAttr::Apply(const wxTextBoxAttr
& attr
, const wxTextBoxAttr
* compareWith
)
12804 if (attr
.HasFloatMode())
12806 if (!(compareWith
&& compareWith
->HasFloatMode() && compareWith
->GetFloatMode() == attr
.GetFloatMode()))
12807 SetFloatMode(attr
.GetFloatMode());
12810 if (attr
.HasClearMode())
12812 if (!(compareWith
&& compareWith
->HasClearMode() && compareWith
->GetClearMode() == attr
.GetClearMode()))
12813 SetClearMode(attr
.GetClearMode());
12816 if (attr
.HasCollapseBorders())
12818 if (!(compareWith
&& compareWith
->HasCollapseBorders() && compareWith
->GetCollapseBorders() == attr
.GetCollapseBorders()))
12819 SetCollapseBorders(attr
.GetCollapseBorders());
12822 if (attr
.HasVerticalAlignment())
12824 if (!(compareWith
&& compareWith
->HasVerticalAlignment() && compareWith
->GetVerticalAlignment() == attr
.GetVerticalAlignment()))
12825 SetVerticalAlignment(attr
.GetVerticalAlignment());
12828 if (attr
.HasBoxStyleName())
12830 if (!(compareWith
&& compareWith
->HasBoxStyleName() && compareWith
->GetBoxStyleName() == attr
.GetBoxStyleName()))
12831 SetBoxStyleName(attr
.GetBoxStyleName());
12834 m_margins
.Apply(attr
.m_margins
, compareWith
? (& attr
.m_margins
) : (const wxTextAttrDimensions
*) NULL
);
12835 m_padding
.Apply(attr
.m_padding
, compareWith
? (& attr
.m_padding
) : (const wxTextAttrDimensions
*) NULL
);
12836 m_position
.Apply(attr
.m_position
, compareWith
? (& attr
.m_position
) : (const wxTextAttrDimensions
*) NULL
);
12838 m_size
.Apply(attr
.m_size
, compareWith
? (& attr
.m_size
) : (const wxTextAttrSize
*) NULL
);
12839 m_minSize
.Apply(attr
.m_minSize
, compareWith
? (& attr
.m_minSize
) : (const wxTextAttrSize
*) NULL
);
12840 m_maxSize
.Apply(attr
.m_maxSize
, compareWith
? (& attr
.m_maxSize
) : (const wxTextAttrSize
*) NULL
);
12842 m_border
.Apply(attr
.m_border
, compareWith
? (& attr
.m_border
) : (const wxTextAttrBorders
*) NULL
);
12843 m_outline
.Apply(attr
.m_outline
, compareWith
? (& attr
.m_outline
) : (const wxTextAttrBorders
*) NULL
);
12848 // Remove specified attributes from this object
12849 bool wxTextBoxAttr::RemoveStyle(const wxTextBoxAttr
& attr
)
12851 if (attr
.HasFloatMode())
12852 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT
);
12854 if (attr
.HasClearMode())
12855 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR
);
12857 if (attr
.HasCollapseBorders())
12858 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
12860 if (attr
.HasVerticalAlignment())
12861 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
12863 if (attr
.HasBoxStyleName())
12865 SetBoxStyleName(wxEmptyString
);
12866 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
12869 m_margins
.RemoveStyle(attr
.m_margins
);
12870 m_padding
.RemoveStyle(attr
.m_padding
);
12871 m_position
.RemoveStyle(attr
.m_position
);
12873 m_size
.RemoveStyle(attr
.m_size
);
12874 m_minSize
.RemoveStyle(attr
.m_minSize
);
12875 m_maxSize
.RemoveStyle(attr
.m_maxSize
);
12877 m_border
.RemoveStyle(attr
.m_border
);
12878 m_outline
.RemoveStyle(attr
.m_outline
);
12883 // Collects the attributes that are common to a range of content, building up a note of
12884 // which attributes are absent in some objects and which clash in some objects.
12885 void wxTextBoxAttr::CollectCommonAttributes(const wxTextBoxAttr
& attr
, wxTextBoxAttr
& clashingAttr
, wxTextBoxAttr
& absentAttr
)
12887 if (attr
.HasFloatMode())
12889 if (!clashingAttr
.HasFloatMode() && !absentAttr
.HasFloatMode())
12891 if (HasFloatMode())
12893 if (GetFloatMode() != attr
.GetFloatMode())
12895 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_FLOAT
);
12896 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT
);
12900 SetFloatMode(attr
.GetFloatMode());
12904 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_FLOAT
);
12906 if (attr
.HasClearMode())
12908 if (!clashingAttr
.HasClearMode() && !absentAttr
.HasClearMode())
12910 if (HasClearMode())
12912 if (GetClearMode() != attr
.GetClearMode())
12914 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_CLEAR
);
12915 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR
);
12919 SetClearMode(attr
.GetClearMode());
12923 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_CLEAR
);
12925 if (attr
.HasCollapseBorders())
12927 if (!clashingAttr
.HasCollapseBorders() && !absentAttr
.HasCollapseBorders())
12929 if (HasCollapseBorders())
12931 if (GetCollapseBorders() != attr
.GetCollapseBorders())
12933 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
12934 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
12938 SetCollapseBorders(attr
.GetCollapseBorders());
12942 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
12944 if (attr
.HasVerticalAlignment())
12946 if (!clashingAttr
.HasVerticalAlignment() && !absentAttr
.HasVerticalAlignment())
12948 if (HasVerticalAlignment())
12950 if (GetVerticalAlignment() != attr
.GetVerticalAlignment())
12952 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
12953 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
12957 SetVerticalAlignment(attr
.GetVerticalAlignment());
12961 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
12963 if (attr
.HasBoxStyleName())
12965 if (!clashingAttr
.HasBoxStyleName() && !absentAttr
.HasBoxStyleName())
12967 if (HasBoxStyleName())
12969 if (GetBoxStyleName() != attr
.GetBoxStyleName())
12971 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
12972 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
12976 SetBoxStyleName(attr
.GetBoxStyleName());
12980 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
12982 m_margins
.CollectCommonAttributes(attr
.m_margins
, clashingAttr
.m_margins
, absentAttr
.m_margins
);
12983 m_padding
.CollectCommonAttributes(attr
.m_padding
, clashingAttr
.m_padding
, absentAttr
.m_padding
);
12984 m_position
.CollectCommonAttributes(attr
.m_position
, clashingAttr
.m_position
, absentAttr
.m_position
);
12986 m_size
.CollectCommonAttributes(attr
.m_size
, clashingAttr
.m_size
, absentAttr
.m_size
);
12987 m_minSize
.CollectCommonAttributes(attr
.m_minSize
, clashingAttr
.m_minSize
, absentAttr
.m_minSize
);
12988 m_maxSize
.CollectCommonAttributes(attr
.m_maxSize
, clashingAttr
.m_maxSize
, absentAttr
.m_maxSize
);
12990 m_border
.CollectCommonAttributes(attr
.m_border
, clashingAttr
.m_border
, absentAttr
.m_border
);
12991 m_outline
.CollectCommonAttributes(attr
.m_outline
, clashingAttr
.m_outline
, absentAttr
.m_outline
);
12994 bool wxTextBoxAttr::IsDefault() const
12996 return GetFlags() == 0 && !m_border
.IsValid() && !m_outline
.IsValid() &&
12997 !m_size
.IsValid() && !m_minSize
.IsValid() && !m_maxSize
.IsValid() &&
12998 !m_position
.IsValid() && !m_padding
.IsValid() && !m_margins
.IsValid();
13003 void wxRichTextAttr::Copy(const wxRichTextAttr
& attr
)
13005 wxTextAttr::Copy(attr
);
13007 m_textBoxAttr
= attr
.m_textBoxAttr
;
13010 bool wxRichTextAttr::operator==(const wxRichTextAttr
& attr
) const
13012 if (!(wxTextAttr::operator==(attr
)))
13015 return (m_textBoxAttr
== attr
.m_textBoxAttr
);
13018 // Partial equality test
13019 bool wxRichTextAttr::EqPartial(const wxRichTextAttr
& attr
, bool weakTest
) const
13021 if (!(wxTextAttr::EqPartial(attr
, weakTest
)))
13024 return m_textBoxAttr
.EqPartial(attr
.m_textBoxAttr
, weakTest
);
13027 // Merges the given attributes. If compareWith
13028 // is non-NULL, then it will be used to mask out those attributes that are the same in style
13029 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
13030 bool wxRichTextAttr::Apply(const wxRichTextAttr
& style
, const wxRichTextAttr
* compareWith
)
13032 wxTextAttr::Apply(style
, compareWith
);
13034 return m_textBoxAttr
.Apply(style
.m_textBoxAttr
, compareWith
? (& compareWith
->m_textBoxAttr
) : (const wxTextBoxAttr
*) NULL
);
13037 // Remove specified attributes from this object
13038 bool wxRichTextAttr::RemoveStyle(const wxRichTextAttr
& attr
)
13040 wxTextAttr::RemoveStyle(*this, attr
);
13042 return m_textBoxAttr
.RemoveStyle(attr
.m_textBoxAttr
);
13045 // Collects the attributes that are common to a range of content, building up a note of
13046 // which attributes are absent in some objects and which clash in some objects.
13047 void wxRichTextAttr::CollectCommonAttributes(const wxRichTextAttr
& attr
, wxRichTextAttr
& clashingAttr
, wxRichTextAttr
& absentAttr
)
13049 wxTextAttrCollectCommonAttributes(*this, attr
, clashingAttr
, absentAttr
);
13051 m_textBoxAttr
.CollectCommonAttributes(attr
.m_textBoxAttr
, clashingAttr
.m_textBoxAttr
, absentAttr
.m_textBoxAttr
);
13054 // Partial equality test
13055 bool wxTextAttrBorder::EqPartial(const wxTextAttrBorder
& border
, bool weakTest
) const
13058 ((!HasStyle() && border
.HasStyle()) ||
13059 (!HasColour() && border
.HasColour()) ||
13060 (!HasWidth() && border
.HasWidth())))
13065 if (border
.HasStyle() && HasStyle() && (border
.GetStyle() != GetStyle()))
13068 if (border
.HasColour() && HasColour() && (border
.GetColourLong() != GetColourLong()))
13071 if (border
.HasWidth() && HasWidth() && !(border
.GetWidth() == GetWidth()))
13077 // Apply border to 'this', but not if the same as compareWith
13078 bool wxTextAttrBorder::Apply(const wxTextAttrBorder
& border
, const wxTextAttrBorder
* compareWith
)
13080 if (border
.HasStyle())
13082 if (!(compareWith
&& (border
.GetStyle() == compareWith
->GetStyle())))
13083 SetStyle(border
.GetStyle());
13085 if (border
.HasColour())
13087 if (!(compareWith
&& (border
.GetColourLong() == compareWith
->GetColourLong())))
13088 SetColour(border
.GetColourLong());
13090 if (border
.HasWidth())
13092 if (!(compareWith
&& (border
.GetWidth() == compareWith
->GetWidth())))
13093 SetWidth(border
.GetWidth());
13099 // Remove specified attributes from this object
13100 bool wxTextAttrBorder::RemoveStyle(const wxTextAttrBorder
& attr
)
13102 if (attr
.HasStyle() && HasStyle())
13103 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_STYLE
);
13104 if (attr
.HasColour() && HasColour())
13105 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_COLOUR
);
13106 if (attr
.HasWidth() && HasWidth())
13107 m_borderWidth
.Reset();
13112 // Collects the attributes that are common to a range of content, building up a note of
13113 // which attributes are absent in some objects and which clash in some objects.
13114 void wxTextAttrBorder::CollectCommonAttributes(const wxTextAttrBorder
& attr
, wxTextAttrBorder
& clashingAttr
, wxTextAttrBorder
& absentAttr
)
13116 if (attr
.HasStyle())
13118 if (!clashingAttr
.HasStyle() && !absentAttr
.HasStyle())
13122 if (GetStyle() != attr
.GetStyle())
13124 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
13125 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
13129 SetStyle(attr
.GetStyle());
13133 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
13135 if (attr
.HasColour())
13137 if (!clashingAttr
.HasColour() && !absentAttr
.HasColour())
13141 if (GetColour() != attr
.GetColour())
13143 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
13144 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
13148 SetColour(attr
.GetColourLong());
13152 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
13154 m_borderWidth
.CollectCommonAttributes(attr
.m_borderWidth
, clashingAttr
.m_borderWidth
, absentAttr
.m_borderWidth
);
13157 // Partial equality test
13158 bool wxTextAttrBorders::EqPartial(const wxTextAttrBorders
& borders
, bool weakTest
) const
13160 return m_left
.EqPartial(borders
.m_left
, weakTest
) && m_right
.EqPartial(borders
.m_right
, weakTest
) &&
13161 m_top
.EqPartial(borders
.m_top
, weakTest
) && m_bottom
.EqPartial(borders
.m_bottom
, weakTest
);
13164 // Apply border to 'this', but not if the same as compareWith
13165 bool wxTextAttrBorders::Apply(const wxTextAttrBorders
& borders
, const wxTextAttrBorders
* compareWith
)
13167 m_left
.Apply(borders
.m_left
, compareWith
? (& compareWith
->m_left
) : (const wxTextAttrBorder
*) NULL
);
13168 m_right
.Apply(borders
.m_right
, compareWith
? (& compareWith
->m_right
) : (const wxTextAttrBorder
*) NULL
);
13169 m_top
.Apply(borders
.m_top
, compareWith
? (& compareWith
->m_top
) : (const wxTextAttrBorder
*) NULL
);
13170 m_bottom
.Apply(borders
.m_bottom
, compareWith
? (& compareWith
->m_bottom
) : (const wxTextAttrBorder
*) NULL
);
13174 // Remove specified attributes from this object
13175 bool wxTextAttrBorders::RemoveStyle(const wxTextAttrBorders
& attr
)
13177 m_left
.RemoveStyle(attr
.m_left
);
13178 m_right
.RemoveStyle(attr
.m_right
);
13179 m_top
.RemoveStyle(attr
.m_top
);
13180 m_bottom
.RemoveStyle(attr
.m_bottom
);
13184 // Collects the attributes that are common to a range of content, building up a note of
13185 // which attributes are absent in some objects and which clash in some objects.
13186 void wxTextAttrBorders::CollectCommonAttributes(const wxTextAttrBorders
& attr
, wxTextAttrBorders
& clashingAttr
, wxTextAttrBorders
& absentAttr
)
13188 m_left
.CollectCommonAttributes(attr
.m_left
, clashingAttr
.m_left
, absentAttr
.m_left
);
13189 m_right
.CollectCommonAttributes(attr
.m_right
, clashingAttr
.m_right
, absentAttr
.m_right
);
13190 m_top
.CollectCommonAttributes(attr
.m_top
, clashingAttr
.m_top
, absentAttr
.m_top
);
13191 m_bottom
.CollectCommonAttributes(attr
.m_bottom
, clashingAttr
.m_bottom
, absentAttr
.m_bottom
);
13194 // Set style of all borders
13195 void wxTextAttrBorders::SetStyle(int style
)
13197 m_left
.SetStyle(style
);
13198 m_right
.SetStyle(style
);
13199 m_top
.SetStyle(style
);
13200 m_bottom
.SetStyle(style
);
13203 // Set colour of all borders
13204 void wxTextAttrBorders::SetColour(unsigned long colour
)
13206 m_left
.SetColour(colour
);
13207 m_right
.SetColour(colour
);
13208 m_top
.SetColour(colour
);
13209 m_bottom
.SetColour(colour
);
13212 void wxTextAttrBorders::SetColour(const wxColour
& colour
)
13214 m_left
.SetColour(colour
);
13215 m_right
.SetColour(colour
);
13216 m_top
.SetColour(colour
);
13217 m_bottom
.SetColour(colour
);
13220 // Set width of all borders
13221 void wxTextAttrBorders::SetWidth(const wxTextAttrDimension
& width
)
13223 m_left
.SetWidth(width
);
13224 m_right
.SetWidth(width
);
13225 m_top
.SetWidth(width
);
13226 m_bottom
.SetWidth(width
);
13229 // Partial equality test
13230 bool wxTextAttrDimension::EqPartial(const wxTextAttrDimension
& dim
, bool weakTest
) const
13232 if (!weakTest
&& !IsValid() && dim
.IsValid())
13235 if (dim
.IsValid() && IsValid() && !((*this) == dim
))
13241 bool wxTextAttrDimension::Apply(const wxTextAttrDimension
& dim
, const wxTextAttrDimension
* compareWith
)
13245 if (!(compareWith
&& dim
== (*compareWith
)))
13252 // Collects the attributes that are common to a range of content, building up a note of
13253 // which attributes are absent in some objects and which clash in some objects.
13254 void wxTextAttrDimension::CollectCommonAttributes(const wxTextAttrDimension
& attr
, wxTextAttrDimension
& clashingAttr
, wxTextAttrDimension
& absentAttr
)
13256 if (attr
.IsValid())
13258 if (!clashingAttr
.IsValid() && !absentAttr
.IsValid())
13262 if (!((*this) == attr
))
13264 clashingAttr
.SetValid(true);
13273 absentAttr
.SetValid(true);
13276 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(wxDC
& dc
, double scale
, const wxSize
& parentSize
)
13278 m_ppi
= dc
.GetPPI().x
; m_scale
= scale
; m_parentSize
= parentSize
;
13281 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(int ppi
, double scale
, const wxSize
& parentSize
)
13283 m_ppi
= ppi
; m_scale
= scale
; m_parentSize
= parentSize
;
13286 int wxTextAttrDimensionConverter::ConvertTenthsMMToPixels(int units
) const
13288 return wxRichTextObject::ConvertTenthsMMToPixels(m_ppi
, units
, m_scale
);
13291 int wxTextAttrDimensionConverter::ConvertPixelsToTenthsMM(int pixels
) const
13293 return wxRichTextObject::ConvertPixelsToTenthsMM(m_ppi
, pixels
, m_scale
);
13296 int wxTextAttrDimensionConverter::GetPixels(const wxTextAttrDimension
& dim
, int direction
) const
13298 if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
13299 return ConvertTenthsMMToPixels(dim
.GetValue());
13300 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
13301 return dim
.GetValue();
13302 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
13304 wxASSERT(m_parentSize
!= wxDefaultSize
);
13305 if (direction
== wxHORIZONTAL
)
13306 return (int) (double(m_parentSize
.x
) * double(dim
.GetValue()) / 100.0);
13308 return (int) (double(m_parentSize
.y
) * double(dim
.GetValue()) / 100.0);
13317 int wxTextAttrDimensionConverter::GetTenthsMM(const wxTextAttrDimension
& dim
) const
13319 if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
13320 return dim
.GetValue();
13321 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
13322 return ConvertPixelsToTenthsMM(dim
.GetValue());
13330 // Partial equality test
13331 bool wxTextAttrDimensions::EqPartial(const wxTextAttrDimensions
& dims
, bool weakTest
) const
13333 if (!m_left
.EqPartial(dims
.m_left
, weakTest
))
13336 if (!m_right
.EqPartial(dims
.m_right
, weakTest
))
13339 if (!m_top
.EqPartial(dims
.m_top
, weakTest
))
13342 if (!m_bottom
.EqPartial(dims
.m_bottom
, weakTest
))
13348 // Apply border to 'this', but not if the same as compareWith
13349 bool wxTextAttrDimensions::Apply(const wxTextAttrDimensions
& dims
, const wxTextAttrDimensions
* compareWith
)
13351 m_left
.Apply(dims
.m_left
, compareWith
? (& compareWith
->m_left
) : (const wxTextAttrDimension
*) NULL
);
13352 m_right
.Apply(dims
.m_right
, compareWith
? (& compareWith
->m_right
): (const wxTextAttrDimension
*) NULL
);
13353 m_top
.Apply(dims
.m_top
, compareWith
? (& compareWith
->m_top
): (const wxTextAttrDimension
*) NULL
);
13354 m_bottom
.Apply(dims
.m_bottom
, compareWith
? (& compareWith
->m_bottom
): (const wxTextAttrDimension
*) NULL
);
13359 // Remove specified attributes from this object
13360 bool wxTextAttrDimensions::RemoveStyle(const wxTextAttrDimensions
& attr
)
13362 if (attr
.m_left
.IsValid())
13364 if (attr
.m_right
.IsValid())
13366 if (attr
.m_top
.IsValid())
13368 if (attr
.m_bottom
.IsValid())
13374 // Collects the attributes that are common to a range of content, building up a note of
13375 // which attributes are absent in some objects and which clash in some objects.
13376 void wxTextAttrDimensions::CollectCommonAttributes(const wxTextAttrDimensions
& attr
, wxTextAttrDimensions
& clashingAttr
, wxTextAttrDimensions
& absentAttr
)
13378 m_left
.CollectCommonAttributes(attr
.m_left
, clashingAttr
.m_left
, absentAttr
.m_left
);
13379 m_right
.CollectCommonAttributes(attr
.m_right
, clashingAttr
.m_right
, absentAttr
.m_right
);
13380 m_top
.CollectCommonAttributes(attr
.m_top
, clashingAttr
.m_top
, absentAttr
.m_top
);
13381 m_bottom
.CollectCommonAttributes(attr
.m_bottom
, clashingAttr
.m_bottom
, absentAttr
.m_bottom
);
13384 // Partial equality test
13385 bool wxTextAttrSize::EqPartial(const wxTextAttrSize
& size
, bool weakTest
) const
13387 if (!m_width
.EqPartial(size
.m_width
, weakTest
))
13390 if (!m_height
.EqPartial(size
.m_height
, weakTest
))
13396 // Apply border to 'this', but not if the same as compareWith
13397 bool wxTextAttrSize::Apply(const wxTextAttrSize
& size
, const wxTextAttrSize
* compareWith
)
13399 m_width
.Apply(size
.m_width
, compareWith
? (& compareWith
->m_width
) : (const wxTextAttrDimension
*) NULL
);
13400 m_height
.Apply(size
.m_height
, compareWith
? (& compareWith
->m_height
): (const wxTextAttrDimension
*) NULL
);
13405 // Remove specified attributes from this object
13406 bool wxTextAttrSize::RemoveStyle(const wxTextAttrSize
& attr
)
13408 if (attr
.m_width
.IsValid())
13410 if (attr
.m_height
.IsValid())
13416 // Collects the attributes that are common to a range of content, building up a note of
13417 // which attributes are absent in some objects and which clash in some objects.
13418 void wxTextAttrSize::CollectCommonAttributes(const wxTextAttrSize
& attr
, wxTextAttrSize
& clashingAttr
, wxTextAttrSize
& absentAttr
)
13420 m_width
.CollectCommonAttributes(attr
.m_width
, clashingAttr
.m_width
, absentAttr
.m_width
);
13421 m_height
.CollectCommonAttributes(attr
.m_height
, clashingAttr
.m_height
, absentAttr
.m_height
);
13424 // Collects the attributes that are common to a range of content, building up a note of
13425 // which attributes are absent in some objects and which clash in some objects.
13426 void wxTextAttrCollectCommonAttributes(wxTextAttr
& currentStyle
, const wxTextAttr
& attr
, wxTextAttr
& clashingAttr
, wxTextAttr
& absentAttr
)
13428 absentAttr
.SetFlags(absentAttr
.GetFlags() | (~attr
.GetFlags() & wxTEXT_ATTR_ALL
));
13429 absentAttr
.SetTextEffectFlags(absentAttr
.GetTextEffectFlags() | (~attr
.GetTextEffectFlags() & 0xFFFF));
13431 long forbiddenFlags
= clashingAttr
.GetFlags()|absentAttr
.GetFlags();
13433 // If different font size units are being used, this is a clash.
13434 if (((attr
.GetFlags() & wxTEXT_ATTR_FONT_SIZE
) | (currentStyle
.GetFlags() & wxTEXT_ATTR_FONT_SIZE
)) == wxTEXT_ATTR_FONT_SIZE
)
13436 currentStyle
.SetFontSize(0);
13437 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_SIZE
);
13438 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_SIZE
);
13442 if (attr
.HasFontPointSize() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_POINT_SIZE
))
13444 if (currentStyle
.HasFontPointSize())
13446 if (currentStyle
.GetFontSize() != attr
.GetFontSize())
13448 // Clash of attr - mark as such
13449 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE
);
13450 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE
);
13454 currentStyle
.SetFontSize(attr
.GetFontSize());
13456 else if (!attr
.HasFontPointSize() && currentStyle
.HasFontPointSize())
13458 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE
);
13459 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE
);
13462 if (attr
.HasFontPixelSize() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_PIXEL_SIZE
))
13464 if (currentStyle
.HasFontPixelSize())
13466 if (currentStyle
.GetFontSize() != attr
.GetFontSize())
13468 // Clash of attr - mark as such
13469 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE
);
13470 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE
);
13474 currentStyle
.SetFontPixelSize(attr
.GetFontSize());
13476 else if (!attr
.HasFontPixelSize() && currentStyle
.HasFontPixelSize())
13478 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE
);
13479 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE
);
13483 if (attr
.HasFontItalic() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_ITALIC
))
13485 if (currentStyle
.HasFontItalic())
13487 if (currentStyle
.GetFontStyle() != attr
.GetFontStyle())
13489 // Clash of attr - mark as such
13490 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_ITALIC
);
13491 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC
);
13495 currentStyle
.SetFontStyle(attr
.GetFontStyle());
13497 else if (!attr
.HasFontItalic() && currentStyle
.HasFontItalic())
13499 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_ITALIC
);
13500 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC
);
13503 if (attr
.HasFontFamily() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_FAMILY
))
13505 if (currentStyle
.HasFontFamily())
13507 if (currentStyle
.GetFontFamily() != attr
.GetFontFamily())
13509 // Clash of attr - mark as such
13510 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FAMILY
);
13511 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY
);
13515 currentStyle
.SetFontFamily(attr
.GetFontFamily());
13517 else if (!attr
.HasFontFamily() && currentStyle
.HasFontFamily())
13519 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FAMILY
);
13520 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY
);
13523 if (attr
.HasFontWeight() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_WEIGHT
))
13525 if (currentStyle
.HasFontWeight())
13527 if (currentStyle
.GetFontWeight() != attr
.GetFontWeight())
13529 // Clash of attr - mark as such
13530 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_WEIGHT
);
13531 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT
);
13535 currentStyle
.SetFontWeight(attr
.GetFontWeight());
13537 else if (!attr
.HasFontWeight() && currentStyle
.HasFontWeight())
13539 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_WEIGHT
);
13540 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT
);
13543 if (attr
.HasFontFaceName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_FACE
))
13545 if (currentStyle
.HasFontFaceName())
13547 wxString
faceName1(currentStyle
.GetFontFaceName());
13548 wxString
faceName2(attr
.GetFontFaceName());
13550 if (faceName1
!= faceName2
)
13552 // Clash of attr - mark as such
13553 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FACE
);
13554 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FACE
);
13558 currentStyle
.SetFontFaceName(attr
.GetFontFaceName());
13560 else if (!attr
.HasFontFaceName() && currentStyle
.HasFontFaceName())
13562 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FACE
);
13563 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FACE
);
13566 if (attr
.HasFontUnderlined() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_UNDERLINE
))
13568 if (currentStyle
.HasFontUnderlined())
13570 if (currentStyle
.GetFontUnderlined() != attr
.GetFontUnderlined())
13572 // Clash of attr - mark as such
13573 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
13574 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
13578 currentStyle
.SetFontUnderlined(attr
.GetFontUnderlined());
13580 else if (!attr
.HasFontUnderlined() && currentStyle
.HasFontUnderlined())
13582 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
13583 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
13586 if (attr
.HasFontStrikethrough() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_STRIKETHROUGH
))
13588 if (currentStyle
.HasFontStrikethrough())
13590 if (currentStyle
.GetFontStrikethrough() != attr
.GetFontStrikethrough())
13592 // Clash of attr - mark as such
13593 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH
);
13594 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH
);
13598 currentStyle
.SetFontStrikethrough(attr
.GetFontStrikethrough());
13600 else if (!attr
.HasFontStrikethrough() && currentStyle
.HasFontStrikethrough())
13602 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH
);
13603 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH
);
13606 if (attr
.HasTextColour() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_TEXT_COLOUR
))
13608 if (currentStyle
.HasTextColour())
13610 if (currentStyle
.GetTextColour() != attr
.GetTextColour())
13612 // Clash of attr - mark as such
13613 clashingAttr
.AddFlag(wxTEXT_ATTR_TEXT_COLOUR
);
13614 currentStyle
.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR
);
13618 currentStyle
.SetTextColour(attr
.GetTextColour());
13620 else if (!attr
.HasTextColour() && currentStyle
.HasTextColour())
13622 clashingAttr
.AddFlag(wxTEXT_ATTR_TEXT_COLOUR
);
13623 currentStyle
.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR
);
13626 if (attr
.HasBackgroundColour() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BACKGROUND_COLOUR
))
13628 if (currentStyle
.HasBackgroundColour())
13630 if (currentStyle
.GetBackgroundColour() != attr
.GetBackgroundColour())
13632 // Clash of attr - mark as such
13633 clashingAttr
.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
13634 currentStyle
.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
13638 currentStyle
.SetBackgroundColour(attr
.GetBackgroundColour());
13640 else if (!attr
.HasBackgroundColour() && currentStyle
.HasBackgroundColour())
13642 clashingAttr
.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
13643 currentStyle
.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
13646 if (attr
.HasAlignment() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_ALIGNMENT
))
13648 if (currentStyle
.HasAlignment())
13650 if (currentStyle
.GetAlignment() != attr
.GetAlignment())
13652 // Clash of attr - mark as such
13653 clashingAttr
.AddFlag(wxTEXT_ATTR_ALIGNMENT
);
13654 currentStyle
.RemoveFlag(wxTEXT_ATTR_ALIGNMENT
);
13658 currentStyle
.SetAlignment(attr
.GetAlignment());
13660 else if (!attr
.HasAlignment() && currentStyle
.HasAlignment())
13662 clashingAttr
.AddFlag(wxTEXT_ATTR_ALIGNMENT
);
13663 currentStyle
.RemoveFlag(wxTEXT_ATTR_ALIGNMENT
);
13666 if (attr
.HasTabs() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_TABS
))
13668 if (currentStyle
.HasTabs())
13670 if (!wxRichTextTabsEq(currentStyle
.GetTabs(), attr
.GetTabs()))
13672 // Clash of attr - mark as such
13673 clashingAttr
.AddFlag(wxTEXT_ATTR_TABS
);
13674 currentStyle
.RemoveFlag(wxTEXT_ATTR_TABS
);
13678 currentStyle
.SetTabs(attr
.GetTabs());
13680 else if (!attr
.HasTabs() && currentStyle
.HasTabs())
13682 clashingAttr
.AddFlag(wxTEXT_ATTR_TABS
);
13683 currentStyle
.RemoveFlag(wxTEXT_ATTR_TABS
);
13686 if (attr
.HasLeftIndent() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LEFT_INDENT
))
13688 if (currentStyle
.HasLeftIndent())
13690 if (currentStyle
.GetLeftIndent() != attr
.GetLeftIndent() || currentStyle
.GetLeftSubIndent() != attr
.GetLeftSubIndent())
13692 // Clash of attr - mark as such
13693 clashingAttr
.AddFlag(wxTEXT_ATTR_LEFT_INDENT
);
13694 currentStyle
.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT
);
13698 currentStyle
.SetLeftIndent(attr
.GetLeftIndent(), attr
.GetLeftSubIndent());
13700 else if (!attr
.HasLeftIndent() && currentStyle
.HasLeftIndent())
13702 clashingAttr
.AddFlag(wxTEXT_ATTR_LEFT_INDENT
);
13703 currentStyle
.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT
);
13706 if (attr
.HasRightIndent() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_RIGHT_INDENT
))
13708 if (currentStyle
.HasRightIndent())
13710 if (currentStyle
.GetRightIndent() != attr
.GetRightIndent())
13712 // Clash of attr - mark as such
13713 clashingAttr
.AddFlag(wxTEXT_ATTR_RIGHT_INDENT
);
13714 currentStyle
.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT
);
13718 currentStyle
.SetRightIndent(attr
.GetRightIndent());
13720 else if (!attr
.HasRightIndent() && currentStyle
.HasRightIndent())
13722 clashingAttr
.AddFlag(wxTEXT_ATTR_RIGHT_INDENT
);
13723 currentStyle
.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT
);
13726 if (attr
.HasParagraphSpacingAfter() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARA_SPACING_AFTER
))
13728 if (currentStyle
.HasParagraphSpacingAfter())
13730 if (currentStyle
.GetParagraphSpacingAfter() != attr
.GetParagraphSpacingAfter())
13732 // Clash of attr - mark as such
13733 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
13734 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
13738 currentStyle
.SetParagraphSpacingAfter(attr
.GetParagraphSpacingAfter());
13740 else if (!attr
.HasParagraphSpacingAfter() && currentStyle
.HasParagraphSpacingAfter())
13742 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
13743 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
13746 if (attr
.HasParagraphSpacingBefore() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARA_SPACING_BEFORE
))
13748 if (currentStyle
.HasParagraphSpacingBefore())
13750 if (currentStyle
.GetParagraphSpacingBefore() != attr
.GetParagraphSpacingBefore())
13752 // Clash of attr - mark as such
13753 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
13754 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
13758 currentStyle
.SetParagraphSpacingBefore(attr
.GetParagraphSpacingBefore());
13760 else if (!attr
.HasParagraphSpacingBefore() && currentStyle
.HasParagraphSpacingBefore())
13762 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
13763 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
13766 if (attr
.HasLineSpacing() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LINE_SPACING
))
13768 if (currentStyle
.HasLineSpacing())
13770 if (currentStyle
.GetLineSpacing() != attr
.GetLineSpacing())
13772 // Clash of attr - mark as such
13773 clashingAttr
.AddFlag(wxTEXT_ATTR_LINE_SPACING
);
13774 currentStyle
.RemoveFlag(wxTEXT_ATTR_LINE_SPACING
);
13778 currentStyle
.SetLineSpacing(attr
.GetLineSpacing());
13780 else if (!attr
.HasLineSpacing() && currentStyle
.HasLineSpacing())
13782 clashingAttr
.AddFlag(wxTEXT_ATTR_LINE_SPACING
);
13783 currentStyle
.RemoveFlag(wxTEXT_ATTR_LINE_SPACING
);
13786 if (attr
.HasCharacterStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_CHARACTER_STYLE_NAME
))
13788 if (currentStyle
.HasCharacterStyleName())
13790 if (currentStyle
.GetCharacterStyleName() != attr
.GetCharacterStyleName())
13792 // Clash of attr - mark as such
13793 clashingAttr
.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
13794 currentStyle
.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
13798 currentStyle
.SetCharacterStyleName(attr
.GetCharacterStyleName());
13800 else if (!attr
.HasCharacterStyleName() && currentStyle
.HasCharacterStyleName())
13802 clashingAttr
.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
13803 currentStyle
.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
13806 if (attr
.HasParagraphStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
))
13808 if (currentStyle
.HasParagraphStyleName())
13810 if (currentStyle
.GetParagraphStyleName() != attr
.GetParagraphStyleName())
13812 // Clash of attr - mark as such
13813 clashingAttr
.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
13814 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
13818 currentStyle
.SetParagraphStyleName(attr
.GetParagraphStyleName());
13820 else if (!attr
.HasParagraphStyleName() && currentStyle
.HasParagraphStyleName())
13822 clashingAttr
.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
13823 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
13826 if (attr
.HasListStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LIST_STYLE_NAME
))
13828 if (currentStyle
.HasListStyleName())
13830 if (currentStyle
.GetListStyleName() != attr
.GetListStyleName())
13832 // Clash of attr - mark as such
13833 clashingAttr
.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
13834 currentStyle
.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
13838 currentStyle
.SetListStyleName(attr
.GetListStyleName());
13840 else if (!attr
.HasListStyleName() && currentStyle
.HasListStyleName())
13842 clashingAttr
.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
13843 currentStyle
.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
13846 if (attr
.HasBulletStyle() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_STYLE
))
13848 if (currentStyle
.HasBulletStyle())
13850 if (currentStyle
.GetBulletStyle() != attr
.GetBulletStyle())
13852 // Clash of attr - mark as such
13853 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_STYLE
);
13854 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE
);
13858 currentStyle
.SetBulletStyle(attr
.GetBulletStyle());
13860 else if (!attr
.HasBulletStyle() && currentStyle
.HasBulletStyle())
13862 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_STYLE
);
13863 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE
);
13866 if (attr
.HasBulletNumber() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_NUMBER
))
13868 if (currentStyle
.HasBulletNumber())
13870 if (currentStyle
.GetBulletNumber() != attr
.GetBulletNumber())
13872 // Clash of attr - mark as such
13873 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NUMBER
);
13874 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER
);
13878 currentStyle
.SetBulletNumber(attr
.GetBulletNumber());
13880 else if (!attr
.HasBulletNumber() && currentStyle
.HasBulletNumber())
13882 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NUMBER
);
13883 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER
);
13886 if (attr
.HasBulletText() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_TEXT
))
13888 if (currentStyle
.HasBulletText())
13890 if (currentStyle
.GetBulletText() != attr
.GetBulletText())
13892 // Clash of attr - mark as such
13893 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_TEXT
);
13894 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT
);
13899 currentStyle
.SetBulletText(attr
.GetBulletText());
13900 currentStyle
.SetBulletFont(attr
.GetBulletFont());
13903 else if (!attr
.HasBulletText() && currentStyle
.HasBulletText())
13905 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_TEXT
);
13906 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT
);
13909 if (attr
.HasBulletName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_NAME
))
13911 if (currentStyle
.HasBulletName())
13913 if (currentStyle
.GetBulletName() != attr
.GetBulletName())
13915 // Clash of attr - mark as such
13916 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NAME
);
13917 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NAME
);
13922 currentStyle
.SetBulletName(attr
.GetBulletName());
13925 else if (!attr
.HasBulletName() && currentStyle
.HasBulletName())
13927 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NAME
);
13928 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NAME
);
13931 if (attr
.HasURL() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_URL
))
13933 if (currentStyle
.HasURL())
13935 if (currentStyle
.GetURL() != attr
.GetURL())
13937 // Clash of attr - mark as such
13938 clashingAttr
.AddFlag(wxTEXT_ATTR_URL
);
13939 currentStyle
.RemoveFlag(wxTEXT_ATTR_URL
);
13944 currentStyle
.SetURL(attr
.GetURL());
13947 else if (!attr
.HasURL() && currentStyle
.HasURL())
13949 clashingAttr
.AddFlag(wxTEXT_ATTR_URL
);
13950 currentStyle
.RemoveFlag(wxTEXT_ATTR_URL
);
13953 if (attr
.HasTextEffects() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_EFFECTS
))
13955 if (currentStyle
.HasTextEffects())
13957 // We need to find the bits in the new attr that are different:
13958 // just look at those bits that are specified by the new attr.
13960 // We need to remove the bits and flags that are not common between current attr
13961 // and new attr. In so doing we need to take account of the styles absent from one or more of the
13962 // previous styles.
13964 int currentRelevantTextEffects
= currentStyle
.GetTextEffects() & attr
.GetTextEffectFlags();
13965 int newRelevantTextEffects
= attr
.GetTextEffects() & attr
.GetTextEffectFlags();
13967 if (currentRelevantTextEffects
!= newRelevantTextEffects
)
13969 // Find the text effects that were different, using XOR
13970 int differentEffects
= currentRelevantTextEffects
^ newRelevantTextEffects
;
13972 // Clash of attr - mark as such
13973 clashingAttr
.SetTextEffectFlags(clashingAttr
.GetTextEffectFlags() | differentEffects
);
13974 currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~differentEffects
);
13979 currentStyle
.SetTextEffects(attr
.GetTextEffects());
13980 currentStyle
.SetTextEffectFlags(attr
.GetTextEffectFlags());
13983 // Mask out the flags and values that cannot be common because they were absent in one or more objecrs
13984 // that we've looked at so far
13985 currentStyle
.SetTextEffects(currentStyle
.GetTextEffects() & ~absentAttr
.GetTextEffectFlags());
13986 currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~absentAttr
.GetTextEffectFlags());
13988 if (currentStyle
.GetTextEffectFlags() == 0)
13989 currentStyle
.RemoveFlag(wxTEXT_ATTR_EFFECTS
);
13991 else if (!attr
.HasTextEffects() && currentStyle
.HasTextEffects())
13993 clashingAttr
.AddFlag(wxTEXT_ATTR_EFFECTS
);
13994 currentStyle
.RemoveFlag(wxTEXT_ATTR_EFFECTS
);
13997 if (attr
.HasOutlineLevel() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_OUTLINE_LEVEL
))
13999 if (currentStyle
.HasOutlineLevel())
14001 if (currentStyle
.GetOutlineLevel() != attr
.GetOutlineLevel())
14003 // Clash of attr - mark as such
14004 clashingAttr
.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
14005 currentStyle
.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
14009 currentStyle
.SetOutlineLevel(attr
.GetOutlineLevel());
14011 else if (!attr
.HasOutlineLevel() && currentStyle
.HasOutlineLevel())
14013 clashingAttr
.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
14014 currentStyle
.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
14018 WX_DEFINE_OBJARRAY(wxRichTextVariantArray
);
14019 WX_DEFINE_OBJARRAY(wxRichTextAttrArray
);
14020 WX_DEFINE_OBJARRAY(wxRichTextRectArray
);
14022 IMPLEMENT_DYNAMIC_CLASS(wxRichTextProperties
, wxObject
)
14024 bool wxRichTextProperties::operator==(const wxRichTextProperties
& props
) const
14026 if (m_properties
.GetCount() != props
.GetCount())
14030 for (i
= 0; i
< m_properties
.GetCount(); i
++)
14032 const wxVariant
& var1
= m_properties
[i
];
14033 int idx
= props
.Find(var1
.GetName());
14036 const wxVariant
& var2
= props
.m_properties
[idx
];
14037 if (!(var1
== var2
))
14044 wxArrayString
wxRichTextProperties::GetPropertyNames() const
14048 for (i
= 0; i
< m_properties
.GetCount(); i
++)
14050 arr
.Add(m_properties
[i
].GetName());
14055 int wxRichTextProperties::Find(const wxString
& name
) const
14058 for (i
= 0; i
< m_properties
.GetCount(); i
++)
14060 if (m_properties
[i
].GetName() == name
)
14066 bool wxRichTextProperties::Remove(const wxString
& name
)
14068 int idx
= Find(name
);
14071 m_properties
.RemoveAt(idx
);
14078 wxVariant
* wxRichTextProperties::FindOrCreateProperty(const wxString
& name
)
14080 int idx
= Find(name
);
14081 if (idx
== wxNOT_FOUND
)
14082 SetProperty(name
, wxString());
14084 if (idx
!= wxNOT_FOUND
)
14086 return & (*this)[idx
];
14092 const wxVariant
& wxRichTextProperties::GetProperty(const wxString
& name
) const
14094 static const wxVariant nullVariant
;
14095 int idx
= Find(name
);
14097 return m_properties
[idx
];
14099 return nullVariant
;
14102 wxString
wxRichTextProperties::GetPropertyString(const wxString
& name
) const
14104 return GetProperty(name
).GetString();
14107 long wxRichTextProperties::GetPropertyLong(const wxString
& name
) const
14109 return GetProperty(name
).GetLong();
14112 bool wxRichTextProperties::GetPropertyBool(const wxString
& name
) const
14114 return GetProperty(name
).GetBool();
14117 double wxRichTextProperties::GetPropertyDouble(const wxString
& name
) const
14119 return GetProperty(name
).GetDouble();
14122 void wxRichTextProperties::SetProperty(const wxVariant
& variant
)
14124 wxASSERT(!variant
.GetName().IsEmpty());
14126 int idx
= Find(variant
.GetName());
14129 m_properties
.Add(variant
);
14131 m_properties
[idx
] = variant
;
14134 void wxRichTextProperties::SetProperty(const wxString
& name
, const wxVariant
& variant
)
14136 int idx
= Find(name
);
14137 wxVariant
var(variant
);
14141 m_properties
.Add(var
);
14143 m_properties
[idx
] = var
;
14146 void wxRichTextProperties::SetProperty(const wxString
& name
, const wxString
& value
)
14148 SetProperty(name
, wxVariant(value
, name
));
14151 void wxRichTextProperties::SetProperty(const wxString
& name
, long value
)
14153 SetProperty(name
, wxVariant(value
, name
));
14156 void wxRichTextProperties::SetProperty(const wxString
& name
, double value
)
14158 SetProperty(name
, wxVariant(value
, name
));
14161 void wxRichTextProperties::SetProperty(const wxString
& name
, bool value
)
14163 SetProperty(name
, wxVariant(value
, name
));
14166 void wxRichTextProperties::RemoveProperties(const wxRichTextProperties
& properties
)
14169 for (i
= 0; i
< properties
.GetCount(); i
++)
14171 wxString name
= properties
.GetProperties()[i
].GetName();
14172 if (HasProperty(name
))
14177 void wxRichTextProperties::MergeProperties(const wxRichTextProperties
& properties
)
14180 for (i
= 0; i
< properties
.GetCount(); i
++)
14182 SetProperty(properties
.GetProperties()[i
]);
14186 wxRichTextObject
* wxRichTextObjectAddress::GetObject(wxRichTextParagraphLayoutBox
* topLevelContainer
) const
14188 if (m_address
.GetCount() == 0)
14189 return topLevelContainer
;
14191 wxRichTextCompositeObject
* p
= topLevelContainer
;
14193 while (p
&& i
< m_address
.GetCount())
14195 int pos
= m_address
[i
];
14196 wxASSERT(pos
>= 0 && pos
< (int) p
->GetChildren().GetCount());
14197 if (pos
< 0 || pos
>= (int) p
->GetChildren().GetCount())
14200 wxRichTextObject
* p1
= p
->GetChild(pos
);
14201 if (i
== (m_address
.GetCount()-1))
14204 p
= wxDynamicCast(p1
, wxRichTextCompositeObject
);
14210 bool wxRichTextObjectAddress::Create(wxRichTextParagraphLayoutBox
* topLevelContainer
, wxRichTextObject
* obj
)
14214 if (topLevelContainer
== obj
)
14217 wxRichTextObject
* o
= obj
;
14220 wxRichTextCompositeObject
* p
= wxDynamicCast(o
->GetParent(), wxRichTextCompositeObject
);
14224 int pos
= p
->GetChildren().IndexOf(o
);
14228 m_address
.Insert(pos
, 0);
14230 if (p
== topLevelContainer
)
14239 bool wxRichTextSelection::operator==(const wxRichTextSelection
& sel
) const
14241 if (m_container
!= sel
.m_container
)
14243 if (m_ranges
.GetCount() != sel
.m_ranges
.GetCount())
14246 for (i
= 0; i
< m_ranges
.GetCount(); i
++)
14247 if (!(m_ranges
[i
] == sel
.m_ranges
[i
]))
14252 // Get the selections appropriate to the specified object, if any; returns wxRICHTEXT_NO_SELECTION if none
14253 // or none at the level of the object's container.
14254 wxRichTextRangeArray
wxRichTextSelection::GetSelectionForObject(wxRichTextObject
* obj
) const
14258 wxRichTextParagraphLayoutBox
* container
= obj
->GetParentContainer();
14260 if (container
== m_container
)
14263 container
= obj
->GetContainer();
14266 if (container
->GetParent())
14268 // If we found that our object's container is within the range of
14269 // a selection higher up, then assume the whole original object
14270 // is also selected.
14271 wxRichTextParagraphLayoutBox
* parentContainer
= container
->GetParentContainer();
14272 if (parentContainer
== m_container
)
14274 if (WithinSelection(container
->GetRange().GetStart(), m_ranges
))
14276 wxRichTextRangeArray ranges
;
14277 ranges
.Add(obj
->GetRange());
14282 container
= parentContainer
;
14291 return wxRichTextRangeArray();
14294 // Is the given position within the selection?
14295 bool wxRichTextSelection::WithinSelection(long pos
, wxRichTextObject
* obj
) const
14301 wxRichTextRangeArray selectionRanges
= GetSelectionForObject(obj
);
14302 return WithinSelection(pos
, selectionRanges
);
14306 // Is the given position within the selection range?
14307 bool wxRichTextSelection::WithinSelection(long pos
, const wxRichTextRangeArray
& ranges
)
14310 for (i
= 0; i
< ranges
.GetCount(); i
++)
14312 const wxRichTextRange
& range
= ranges
[i
];
14313 if (pos
>= range
.GetStart() && pos
<= range
.GetEnd())
14319 // Is the given range completely within the selection range?
14320 bool wxRichTextSelection::WithinSelection(const wxRichTextRange
& range
, const wxRichTextRangeArray
& ranges
)
14323 for (i
= 0; i
< ranges
.GetCount(); i
++)
14325 const wxRichTextRange
& eachRange
= ranges
[i
];
14326 if (range
.IsWithin(eachRange
))
14332 IMPLEMENT_CLASS(wxRichTextDrawingHandler
, wxObject
)
14333 IMPLEMENT_CLASS(wxRichTextDrawingContext
, wxObject
)
14335 wxRichTextDrawingContext::wxRichTextDrawingContext(wxRichTextBuffer
* buffer
)
14339 if (m_buffer
&& m_buffer
->GetRichTextCtrl())
14340 EnableVirtualAttributes(m_buffer
->GetRichTextCtrl()->GetVirtualAttributesEnabled());
14343 bool wxRichTextDrawingContext::HasVirtualAttributes(wxRichTextObject
* obj
) const
14345 if (!GetVirtualAttributesEnabled())
14348 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
14351 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
14352 if (handler
->HasVirtualAttributes(obj
))
14355 node
= node
->GetNext();
14360 wxRichTextAttr
wxRichTextDrawingContext::GetVirtualAttributes(wxRichTextObject
* obj
) const
14362 wxRichTextAttr attr
;
14363 if (!GetVirtualAttributesEnabled())
14366 // We apply all handlers, so we can may combine several different attributes
14367 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
14370 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
14371 if (handler
->HasVirtualAttributes(obj
))
14373 bool success
= handler
->GetVirtualAttributes(attr
, obj
);
14375 wxUnusedVar(success
);
14378 node
= node
->GetNext();
14383 bool wxRichTextDrawingContext::ApplyVirtualAttributes(wxRichTextAttr
& attr
, wxRichTextObject
* obj
) const
14385 if (!GetVirtualAttributesEnabled())
14388 if (HasVirtualAttributes(obj
))
14390 wxRichTextAttr
a(GetVirtualAttributes(obj
));
14398 int wxRichTextDrawingContext::GetVirtualSubobjectAttributesCount(wxRichTextObject
* obj
) const
14400 if (!GetVirtualAttributesEnabled())
14403 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
14406 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
14407 int count
= handler
->GetVirtualSubobjectAttributesCount(obj
);
14411 node
= node
->GetNext();
14416 int wxRichTextDrawingContext::GetVirtualSubobjectAttributes(wxRichTextObject
* obj
, wxArrayInt
& positions
, wxRichTextAttrArray
& attributes
) const
14418 if (!GetVirtualAttributesEnabled())
14421 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
14424 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
14425 if (handler
->GetVirtualSubobjectAttributes(obj
, positions
, attributes
))
14426 return positions
.GetCount();
14428 node
= node
->GetNext();
14433 bool wxRichTextDrawingContext::HasVirtualText(const wxRichTextPlainText
* obj
) const
14435 if (!GetVirtualAttributesEnabled())
14438 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
14441 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
14442 if (handler
->HasVirtualText(obj
))
14445 node
= node
->GetNext();
14450 bool wxRichTextDrawingContext::GetVirtualText(const wxRichTextPlainText
* obj
, wxString
& text
) const
14452 if (!GetVirtualAttributesEnabled())
14455 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
14458 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
14459 if (handler
->GetVirtualText(obj
, text
))
14462 node
= node
->GetNext();
14467 /// Adds a handler to the end
14468 void wxRichTextBuffer::AddDrawingHandler(wxRichTextDrawingHandler
*handler
)
14470 sm_drawingHandlers
.Append(handler
);
14473 /// Inserts a handler at the front
14474 void wxRichTextBuffer::InsertDrawingHandler(wxRichTextDrawingHandler
*handler
)
14476 sm_drawingHandlers
.Insert( handler
);
14479 /// Removes a handler
14480 bool wxRichTextBuffer::RemoveDrawingHandler(const wxString
& name
)
14482 wxRichTextDrawingHandler
*handler
= FindDrawingHandler(name
);
14485 sm_drawingHandlers
.DeleteObject(handler
);
14493 wxRichTextDrawingHandler
* wxRichTextBuffer::FindDrawingHandler(const wxString
& name
)
14495 wxList::compatibility_iterator node
= sm_drawingHandlers
.GetFirst();
14498 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
14499 if (handler
->GetName().Lower() == name
.Lower()) return handler
;
14501 node
= node
->GetNext();
14506 void wxRichTextBuffer::CleanUpDrawingHandlers()
14508 wxList::compatibility_iterator node
= sm_drawingHandlers
.GetFirst();
14511 wxRichTextDrawingHandler
* handler
= (wxRichTextDrawingHandler
*)node
->GetData();
14512 wxList::compatibility_iterator next
= node
->GetNext();
14517 sm_drawingHandlers
.Clear();
14520 void wxRichTextBuffer::AddFieldType(wxRichTextFieldType
*fieldType
)
14522 sm_fieldTypes
[fieldType
->GetName()] = fieldType
;
14525 bool wxRichTextBuffer::RemoveFieldType(const wxString
& name
)
14527 wxRichTextFieldTypeHashMap::iterator it
= sm_fieldTypes
.find(name
);
14528 if (it
== sm_fieldTypes
.end())
14532 wxRichTextFieldType
* fieldType
= it
->second
;
14533 sm_fieldTypes
.erase(it
);
14539 wxRichTextFieldType
*wxRichTextBuffer::FindFieldType(const wxString
& name
)
14541 wxRichTextFieldTypeHashMap::iterator it
= sm_fieldTypes
.find(name
);
14542 if (it
== sm_fieldTypes
.end())
14548 void wxRichTextBuffer::CleanUpFieldTypes()
14550 wxRichTextFieldTypeHashMap::iterator it
;
14551 for( it
= sm_fieldTypes
.begin(); it
!= sm_fieldTypes
.end(); ++it
)
14553 wxRichTextFieldType
* fieldType
= it
->second
;
14557 sm_fieldTypes
.clear();