1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/richtext/richtextbuffer.cpp
3 // Purpose: Buffer for wxRichTextCtrl
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // For compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
21 #include "wx/richtext/richtextbuffer.h"
27 #include "wx/dataobj.h"
28 #include "wx/module.h"
31 #include "wx/settings.h"
32 #include "wx/filename.h"
33 #include "wx/clipbrd.h"
34 #include "wx/wfstream.h"
35 #include "wx/mstream.h"
36 #include "wx/sstream.h"
37 #include "wx/textfile.h"
38 #include "wx/hashmap.h"
39 #include "wx/dynarray.h"
41 #include "wx/richtext/richtextctrl.h"
42 #include "wx/richtext/richtextstyles.h"
43 #include "wx/richtext/richtextimagedlg.h"
44 #include "wx/richtext/richtextsizepage.h"
45 #include "wx/richtext/richtextxml.h"
47 #include "wx/listimpl.cpp"
48 #include "wx/arrimpl.cpp"
50 WX_DEFINE_LIST(wxRichTextObjectList
)
51 WX_DEFINE_LIST(wxRichTextLineList
)
53 // Switch off if the platform doesn't like it for some reason
54 #define wxRICHTEXT_USE_OPTIMIZED_DRAWING 1
56 // Use GetPartialTextExtents for platforms that support it natively
57 #define wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS 1
59 const wxChar wxRichTextLineBreakChar
= (wxChar
) 29;
61 // Helper classes for floating layout
62 struct wxRichTextFloatRectMap
64 wxRichTextFloatRectMap(int sY
, int eY
, int w
, wxRichTextObject
* obj
)
74 wxRichTextObject
* anchor
;
77 WX_DEFINE_SORTED_ARRAY(wxRichTextFloatRectMap
*, wxRichTextFloatRectMapArray
);
79 int wxRichTextFloatRectMapCmp(wxRichTextFloatRectMap
* r1
, wxRichTextFloatRectMap
* r2
)
81 return r1
->startY
- r2
->startY
;
84 class wxRichTextFloatCollector
87 wxRichTextFloatCollector(const wxRect
& availableRect
);
88 ~wxRichTextFloatCollector();
90 // Collect the floating objects info in the given paragraph
91 void CollectFloat(wxRichTextParagraph
* para
);
92 void CollectFloat(wxRichTextParagraph
* para
, wxRichTextObject
* floating
);
94 // Return the last paragraph we collected
95 wxRichTextParagraph
* LastParagraph();
97 // Given the start y position and the height of the line,
98 // find out how wide the line can be
99 wxRect
GetAvailableRect(int startY
, int endY
);
101 // Given a floating box, find its fit position
102 int GetFitPosition(int direction
, int start
, int height
) const;
103 int GetFitPosition(const wxRichTextFloatRectMapArray
& array
, int start
, int height
) const;
105 // Find the last y position
106 int GetLastRectBottom();
108 // Draw the floats inside a rect
109 void Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
);
111 // HitTest the floats
112 int HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, int flags
);
114 // Get floating object count
115 int GetFloatingObjectCount() const { return m_left
.GetCount() + m_right
.GetCount(); }
117 // Get floating objects
118 bool GetFloatingObjects(wxRichTextObjectList
& objects
) const;
121 bool DeleteFloat(wxRichTextObject
* obj
);
123 // Do we have this float already?
124 bool HasFloat(wxRichTextObject
* obj
);
126 bool HasFloats() const { return m_left
.GetCount() >0 || m_right
.GetCount() > 0; }
128 static int SearchAdjacentRect(const wxRichTextFloatRectMapArray
& array
, int point
);
130 static int GetWidthFromFloatRect(const wxRichTextFloatRectMapArray
& array
, int index
, int startY
, int endY
);
132 static void FreeFloatRectMapArray(wxRichTextFloatRectMapArray
& array
);
134 static void DrawFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
);
136 static int HitTestFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, int flags
);
139 wxRichTextFloatRectMapArray m_left
;
140 wxRichTextFloatRectMapArray m_right
;
142 wxRect m_availableRect
;
143 wxRichTextParagraph
* m_para
;
147 bool wxRichTextFloatCollector::DeleteFloat(wxRichTextObject
* obj
)
150 for (i
= 0; i
< m_left
.GetCount(); i
++)
152 if (m_left
[i
]->anchor
== obj
)
158 for (i
= 0; i
< m_right
.GetCount(); i
++)
160 if (m_right
[i
]->anchor
== obj
)
169 // Do we have this float already?
170 bool wxRichTextFloatCollector::HasFloat(wxRichTextObject
* obj
)
173 for (i
= 0; i
< m_left
.GetCount(); i
++)
175 if (m_left
[i
]->anchor
== obj
)
180 for (i
= 0; i
< m_right
.GetCount(); i
++)
182 if (m_right
[i
]->anchor
== obj
)
190 // Get floating objects
191 bool wxRichTextFloatCollector::GetFloatingObjects(wxRichTextObjectList
& objects
) const
194 for (i
= 0; i
< m_left
.GetCount(); i
++)
195 objects
.Append(m_left
[i
]->anchor
);
196 for (i
= 0; i
< m_right
.GetCount(); i
++)
197 objects
.Append(m_right
[i
]->anchor
);
203 * Binary search helper function
204 * The argument point is the Y coordinate, and this fuction
205 * always return the floating rect that contain this coordinate
206 * or under this coordinate.
208 int wxRichTextFloatCollector::SearchAdjacentRect(const wxRichTextFloatRectMapArray
& array
, int point
)
210 int end
= array
.GetCount() - 1;
223 int mid
= (start
+ end
) / 2;
224 if (array
[mid
]->startY
<= point
&& array
[mid
]->endY
>= point
)
226 else if (array
[mid
]->startY
> point
)
231 else if (array
[mid
]->endY
< point
)
241 int wxRichTextFloatCollector::GetWidthFromFloatRect(const wxRichTextFloatRectMapArray
& array
, int index
, int startY
, int endY
)
244 int len
= array
.GetCount();
246 wxASSERT(index
>= 0 && index
< len
);
248 if (array
[index
]->startY
< startY
&& array
[index
]->endY
> startY
)
249 ret
= ret
< array
[index
]->width
? array
[index
]->width
: ret
;
250 while (index
< len
&& array
[index
]->startY
<= endY
)
252 ret
= ret
< array
[index
]->width
? array
[index
]->width
: ret
;
259 wxRichTextFloatCollector::wxRichTextFloatCollector(const wxRect
& rect
) : m_left(wxRichTextFloatRectMapCmp
), m_right(wxRichTextFloatRectMapCmp
)
261 m_availableRect
= rect
;
265 void wxRichTextFloatCollector::FreeFloatRectMapArray(wxRichTextFloatRectMapArray
& array
)
267 int len
= array
.GetCount();
268 for (int i
= 0; i
< len
; i
++)
272 wxRichTextFloatCollector::~wxRichTextFloatCollector()
274 FreeFloatRectMapArray(m_left
);
275 FreeFloatRectMapArray(m_right
);
278 int wxRichTextFloatCollector::GetFitPosition(const wxRichTextFloatRectMapArray
& array
, int start
, int height
) const
280 if (array
.GetCount() == 0)
283 int i
= SearchAdjacentRect(array
, start
);
285 while (i
< (int) array
.GetCount())
287 if (array
[i
]->startY
- last
>= height
)
289 last
= array
[i
]->endY
;
296 int wxRichTextFloatCollector::GetFitPosition(int direction
, int start
, int height
) const
298 if (direction
== wxTEXT_BOX_ATTR_FLOAT_LEFT
)
299 return GetFitPosition(m_left
, start
, height
);
300 else if (direction
== wxTEXT_BOX_ATTR_FLOAT_RIGHT
)
301 return GetFitPosition(m_right
, start
, height
);
304 wxASSERT("Never should be here");
309 // Adds a floating image to the float collector.
310 // The actual positioning is done by wxRichTextParagraph::LayoutFloat.
311 void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph
* para
, wxRichTextObject
* floating
)
313 int direction
= floating
->GetFloatDirection();
315 wxPoint pos
= floating
->GetPosition();
316 wxSize size
= floating
->GetCachedSize();
317 wxRichTextFloatRectMap
*map
= new wxRichTextFloatRectMap(pos
.y
, pos
.y
+ size
.y
, size
.x
, floating
);
320 case wxTEXT_BOX_ATTR_FLOAT_NONE
:
323 case wxTEXT_BOX_ATTR_FLOAT_LEFT
:
324 // Just a not-enough simple assertion
325 wxASSERT (m_left
.Index(map
) == wxNOT_FOUND
);
328 case wxTEXT_BOX_ATTR_FLOAT_RIGHT
:
329 wxASSERT (m_right
.Index(map
) == wxNOT_FOUND
);
334 wxASSERT("Unrecognised float attribute.");
340 void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph
* para
)
342 wxRichTextObjectList::compatibility_iterator node
= para
->GetChildren().GetFirst();
345 wxRichTextObject
* floating
= node
->GetData();
347 if (floating
->IsFloating())
349 CollectFloat(para
, floating
);
352 node
= node
->GetNext();
358 wxRichTextParagraph
* wxRichTextFloatCollector::LastParagraph()
363 wxRect
wxRichTextFloatCollector::GetAvailableRect(int startY
, int endY
)
365 int widthLeft
= 0, widthRight
= 0;
366 if (m_left
.GetCount() != 0)
368 int i
= SearchAdjacentRect(m_left
, startY
);
369 if (i
< (int) m_left
.GetCount())
370 widthLeft
= GetWidthFromFloatRect(m_left
, i
, startY
, endY
);
372 if (m_right
.GetCount() != 0)
374 int j
= SearchAdjacentRect(m_right
, startY
);
375 if (j
< (int) m_right
.GetCount())
376 widthRight
= GetWidthFromFloatRect(m_right
, j
, startY
, endY
);
379 // TODO: actually we want to use the actual image positions to find the
380 // available remaining space, since the image might not be right up against
381 // the left or right edge of the container.
382 return wxRect(widthLeft
+ m_availableRect
.x
, 0, m_availableRect
.width
- widthLeft
- widthRight
, 0);
385 int wxRichTextFloatCollector::GetLastRectBottom()
388 int len
= m_left
.GetCount();
390 ret
= ret
> m_left
[len
-1]->endY
? ret
: m_left
[len
-1]->endY
;
392 len
= m_right
.GetCount();
394 ret
= ret
> m_right
[len
-1]->endY
? ret
: m_right
[len
-1]->endY
;
400 void wxRichTextFloatCollector::DrawFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& WXUNUSED(range
), const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
403 int end
= rect
.y
+ rect
.height
;
405 i
= SearchAdjacentRect(array
, start
);
406 if (i
< 0 || i
>= (int) array
.GetCount())
408 j
= SearchAdjacentRect(array
, end
);
409 if (j
< 0 || j
>= (int) array
.GetCount())
410 j
= array
.GetCount() - 1;
413 wxRichTextObject
* obj
= array
[i
]->anchor
;
414 wxRichTextRange r
= obj
->GetRange();
415 obj
->Draw(dc
, context
, r
, selection
, wxRect(obj
->GetPosition(), obj
->GetCachedSize()), descent
, style
);
420 void wxRichTextFloatCollector::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
422 if (m_left
.GetCount() > 0)
423 DrawFloat(m_left
, dc
, context
, range
, selection
, rect
, descent
, style
);
424 if (m_right
.GetCount() > 0)
425 DrawFloat(m_right
, dc
, context
, range
, selection
, rect
, descent
, style
);
428 int wxRichTextFloatCollector::HitTestFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& WXUNUSED(dc
), wxRichTextDrawingContext
& WXUNUSED(context
), const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, int WXUNUSED(flags
))
431 if (array
.GetCount() == 0)
432 return wxRICHTEXT_HITTEST_NONE
;
433 i
= SearchAdjacentRect(array
, pt
.y
);
434 if (i
< 0 || i
>= (int) array
.GetCount())
435 return wxRICHTEXT_HITTEST_NONE
;
436 if (!array
[i
]->anchor
->IsShown())
437 return wxRICHTEXT_HITTEST_NONE
;
439 wxPoint point
= array
[i
]->anchor
->GetPosition();
440 wxSize size
= array
[i
]->anchor
->GetCachedSize();
441 if (point
.x
<= pt
.x
&& point
.x
+ size
.x
>= pt
.x
442 && point
.y
<= pt
.y
&& point
.y
+ size
.y
>= pt
.y
)
444 textPosition
= array
[i
]->anchor
->GetRange().GetStart();
445 * obj
= array
[i
]->anchor
;
446 if (pt
.x
> (pt
.x
+ pt
.x
+ size
.x
) / 2)
447 return wxRICHTEXT_HITTEST_BEFORE
;
449 return wxRICHTEXT_HITTEST_AFTER
;
452 return wxRICHTEXT_HITTEST_NONE
;
455 int wxRichTextFloatCollector::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, int flags
)
457 int ret
= HitTestFloat(m_left
, dc
, context
, pt
, textPosition
, obj
, flags
);
458 if (ret
== wxRICHTEXT_HITTEST_NONE
)
460 ret
= HitTestFloat(m_right
, dc
, context
, pt
, textPosition
, obj
, flags
);
465 // Helpers for efficiency
466 inline void wxCheckSetFont(wxDC
& dc
, const wxFont
& font
)
471 inline void wxCheckSetPen(wxDC
& dc
, const wxPen
& pen
)
473 const wxPen
& pen1
= dc
.GetPen();
474 if (pen1
.IsOk() && pen
.IsOk())
476 if (pen1
.GetWidth() == pen
.GetWidth() &&
477 pen1
.GetStyle() == pen
.GetStyle() &&
478 pen1
.GetColour() == pen
.GetColour())
484 inline void wxCheckSetBrush(wxDC
& dc
, const wxBrush
& brush
)
486 const wxBrush
& brush1
= dc
.GetBrush();
487 if (brush1
.IsOk() && brush
.IsOk())
489 if (brush1
.GetStyle() == brush
.GetStyle() &&
490 brush1
.GetColour() == brush
.GetColour())
498 * This is the base for drawable objects.
501 IMPLEMENT_CLASS(wxRichTextObject
, wxObject
)
503 wxRichTextObject::wxRichTextObject(wxRichTextObject
* parent
)
511 wxRichTextObject::~wxRichTextObject()
515 void wxRichTextObject::Dereference()
523 void wxRichTextObject::Copy(const wxRichTextObject
& obj
)
526 m_maxSize
= obj
.m_maxSize
;
527 m_minSize
= obj
.m_minSize
;
529 m_range
= obj
.m_range
;
530 m_ownRange
= obj
.m_ownRange
;
531 m_attributes
= obj
.m_attributes
;
532 m_properties
= obj
.m_properties
;
533 m_descent
= obj
.m_descent
;
537 // Get/set the top-level container of this object.
538 wxRichTextParagraphLayoutBox
* wxRichTextObject::GetContainer() const
540 const wxRichTextObject
* p
= this;
545 return wxDynamicCast(p
, wxRichTextParagraphLayoutBox
);
552 void wxRichTextObject::SetMargins(int margin
)
554 SetMargins(margin
, margin
, margin
, margin
);
557 void wxRichTextObject::SetMargins(int leftMargin
, int rightMargin
, int topMargin
, int bottomMargin
)
559 GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().SetValue(leftMargin
, wxTEXT_ATTR_UNITS_PIXELS
);
560 GetAttributes().GetTextBoxAttr().GetMargins().GetRight().SetValue(rightMargin
, wxTEXT_ATTR_UNITS_PIXELS
);
561 GetAttributes().GetTextBoxAttr().GetMargins().GetTop().SetValue(topMargin
, wxTEXT_ATTR_UNITS_PIXELS
);
562 GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().SetValue(bottomMargin
, wxTEXT_ATTR_UNITS_PIXELS
);
565 int wxRichTextObject::GetLeftMargin() const
567 return GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().GetValue();
570 int wxRichTextObject::GetRightMargin() const
572 return GetAttributes().GetTextBoxAttr().GetMargins().GetRight().GetValue();
575 int wxRichTextObject::GetTopMargin() const
577 return GetAttributes().GetTextBoxAttr().GetMargins().GetTop().GetValue();
580 int wxRichTextObject::GetBottomMargin() const
582 return GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().GetValue();
585 // Calculate the available content space in the given rectangle, given the
586 // margins, border and padding specified in the object's attributes.
587 wxRect
wxRichTextObject::GetAvailableContentArea(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& outerRect
) const
589 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
590 marginRect
= outerRect
;
591 wxRichTextAttr
attr(GetAttributes());
592 context
.ApplyVirtualAttributes(attr
, (wxRichTextObject
*) this);
593 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
597 // Invalidate the buffer. With no argument, invalidates whole buffer.
598 void wxRichTextObject::Invalidate(const wxRichTextRange
& invalidRange
)
600 if (invalidRange
!= wxRICHTEXT_NONE
)
602 // If this is a floating object, size may not be recalculated
603 // after floats have been collected in an early stage of Layout.
604 // So avoid resetting the cache for floating objects during layout.
606 SetCachedSize(wxDefaultSize
);
607 SetMaxSize(wxDefaultSize
);
608 SetMinSize(wxDefaultSize
);
612 // Convert units in tenths of a millimetre to device units
613 int wxRichTextObject::ConvertTenthsMMToPixels(wxDC
& dc
, int units
) const
618 scale
= GetBuffer()->GetScale() / GetBuffer()->GetDimensionScale();
619 int p
= ConvertTenthsMMToPixels(dc
.GetPPI().x
, units
, scale
);
624 // Convert units in tenths of a millimetre to device units
625 int wxRichTextObject::ConvertTenthsMMToPixels(int ppi
, int units
, double scale
)
627 // There are ppi pixels in 254.1 "1/10 mm"
629 double pixels
= ((double) units
* (double)ppi
) / 254.1;
633 // If the result is very small, make it at least one pixel in size.
634 if (pixels
== 0 && units
> 0)
640 // Convert units in pixels to tenths of a millimetre
641 int wxRichTextObject::ConvertPixelsToTenthsMM(wxDC
& dc
, int pixels
) const
646 scale
= GetBuffer()->GetScale();
648 return ConvertPixelsToTenthsMM(dc
.GetPPI().x
, p
, scale
);
651 int wxRichTextObject::ConvertPixelsToTenthsMM(int ppi
, int pixels
, double scale
)
653 // There are ppi pixels in 254.1 "1/10 mm"
655 double p
= double(pixels
);
660 int units
= int( p
* 254.1 / (double) ppi
);
664 // Draw the borders and background for the given rectangle and attributes.
665 // Width and height are taken to be the outer margin size, not the content.
666 bool wxRichTextObject::DrawBoxAttributes(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& attr
, const wxRect
& boxRect
, int flags
)
668 // Assume boxRect is the area around the content
669 wxRect marginRect
= boxRect
;
670 wxRect contentRect
, borderRect
, paddingRect
, outlineRect
;
672 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
674 // Margin is transparent. Draw background from margin.
675 if (attr
.HasBackgroundColour() || (flags
& wxRICHTEXT_DRAW_SELECTED
))
678 if (flags
& wxRICHTEXT_DRAW_SELECTED
)
680 // TODO: get selection colour from control?
681 colour
= wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
);
684 colour
= attr
.GetBackgroundColour();
687 wxBrush
brush(colour
);
691 dc
.DrawRectangle(borderRect
);
694 if (flags
& wxRICHTEXT_DRAW_GUIDELINES
)
696 wxRichTextAttr editBorderAttr
= attr
;
697 // TODO: make guideline colour configurable
698 editBorderAttr
.GetTextBoxAttr().GetBorder().SetColour(*wxLIGHT_GREY
);
699 editBorderAttr
.GetTextBoxAttr().GetBorder().SetWidth(1, wxTEXT_ATTR_UNITS_PIXELS
);
700 editBorderAttr
.GetTextBoxAttr().GetBorder().SetStyle(wxTEXT_BOX_ATTR_BORDER_SOLID
);
702 DrawBorder(dc
, buffer
, editBorderAttr
.GetTextBoxAttr().GetBorder(), borderRect
, flags
);
705 if (attr
.GetTextBoxAttr().GetBorder().IsValid())
706 DrawBorder(dc
, buffer
, attr
.GetTextBoxAttr().GetBorder(), borderRect
);
708 if (attr
.GetTextBoxAttr().GetOutline().IsValid())
709 DrawBorder(dc
, buffer
, attr
.GetTextBoxAttr().GetOutline(), outlineRect
);
715 bool wxRichTextObject::DrawBorder(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxTextAttrBorders
& attr
, const wxRect
& rect
, int WXUNUSED(flags
))
717 int borderLeft
= 0, borderRight
= 0, borderTop
= 0, borderBottom
= 0;
718 wxTextAttrDimensionConverter
converter(dc
, buffer
? buffer
->GetScale() : 1.0);
720 if (attr
.GetLeft().IsValid() && attr
.GetLeft().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
722 borderLeft
= converter
.GetPixels(attr
.GetLeft().GetWidth());
723 wxColour
col(attr
.GetLeft().GetColour());
725 // If pen width is > 1, resorts to a solid rectangle.
728 int penStyle
= wxSOLID
;
729 if (attr
.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
731 else if (attr
.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
732 penStyle
= wxLONG_DASH
;
733 wxPen
pen(col
, 1, penStyle
);
735 dc
.DrawLine(rect
.x
, rect
.y
, rect
.x
, rect
.y
+ rect
.height
);
738 else if (borderLeft
> 1)
744 dc
.DrawRectangle(rect
.x
, rect
.y
, borderLeft
, rect
.height
);
748 if (attr
.GetRight().IsValid() && attr
.GetRight().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
750 borderRight
= converter
.GetPixels(attr
.GetRight().GetWidth());
752 wxColour
col(attr
.GetRight().GetColour());
754 // If pen width is > 1, resorts to a solid rectangle.
755 if (borderRight
== 1)
757 int penStyle
= wxSOLID
;
758 if (attr
.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
760 else if (attr
.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
761 penStyle
= wxLONG_DASH
;
762 wxPen
pen(col
, 1, penStyle
);
764 dc
.DrawLine(rect
.x
+ rect
.width
, rect
.y
, rect
.x
+ rect
.width
, rect
.y
+ rect
.height
+ 1);
767 else if (borderRight
> 1)
773 dc
.DrawRectangle(rect
.x
+ rect
.width
- borderRight
, rect
.y
, borderRight
, rect
.height
);
777 if (attr
.GetTop().IsValid() && attr
.GetTop().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
779 borderTop
= converter
.GetPixels(attr
.GetTop().GetWidth());
781 wxColour
col(attr
.GetTop().GetColour());
783 // If pen width is > 1, resorts to a solid rectangle.
786 int penStyle
= wxSOLID
;
787 if (attr
.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
789 else if (attr
.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
790 penStyle
= wxLONG_DASH
;
791 wxPen
pen(col
, 1, penStyle
);
793 dc
.DrawLine(rect
.x
, rect
.y
, rect
.x
+ rect
.width
, rect
.y
);
796 else if (borderTop
> 1)
802 dc
.DrawRectangle(rect
.x
, rect
.y
, rect
.width
, borderTop
);
806 if (attr
.GetBottom().IsValid() && attr
.GetBottom().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
808 borderBottom
= converter
.GetPixels(attr
.GetBottom().GetWidth());
809 wxColour
col(attr
.GetTop().GetColour());
811 // If pen width is > 1, resorts to a solid rectangle.
812 if (borderBottom
== 1)
814 int penStyle
= wxSOLID
;
815 if (attr
.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
817 else if (attr
.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
818 penStyle
= wxLONG_DASH
;
819 wxPen
pen(col
, 1, penStyle
);
821 dc
.DrawLine(rect
.x
, rect
.y
+ rect
.height
, rect
.x
+ rect
.width
, rect
.y
+ rect
.height
);
824 else if (borderBottom
> 1)
830 dc
.DrawRectangle(rect
.x
, rect
.y
+ rect
.height
- borderBottom
, rect
.width
, borderBottom
);
837 // Get the various rectangles of the box model in pixels. You can either specify contentRect (inner)
838 // or marginRect (outer), and the other must be the default rectangle (no width or height).
839 // Note that the outline doesn't affect the position of the rectangle, it's drawn in whatever space
842 // | Margin | Border | Padding | CONTENT | Padding | Border | Margin |
844 bool wxRichTextObject::GetBoxRects(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& attr
, wxRect
& marginRect
, wxRect
& borderRect
, wxRect
& contentRect
, wxRect
& paddingRect
, wxRect
& outlineRect
)
846 int borderLeft
= 0, borderRight
= 0, borderTop
= 0, borderBottom
= 0;
847 int outlineLeft
= 0, outlineRight
= 0, outlineTop
= 0, outlineBottom
= 0;
848 int paddingLeft
= 0, paddingRight
= 0, paddingTop
= 0, paddingBottom
= 0;
849 int marginLeft
= 0, marginRight
= 0, marginTop
= 0, marginBottom
= 0;
851 wxTextAttrDimensionConverter
converter(dc
, buffer
? buffer
->GetScale() : 1.0);
853 if (attr
.GetTextBoxAttr().GetMargins().GetLeft().IsValid())
854 marginLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetLeft());
855 if (attr
.GetTextBoxAttr().GetMargins().GetRight().IsValid())
856 marginRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetRight());
857 if (attr
.GetTextBoxAttr().GetMargins().GetTop().IsValid())
858 marginTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetTop());
859 if (attr
.GetTextBoxAttr().GetMargins().GetBottom().IsValid())
860 marginBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetBottom());
862 if (attr
.GetTextBoxAttr().GetBorder().GetLeft().GetWidth().IsValid())
863 borderLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetLeft().GetWidth());
864 if (attr
.GetTextBoxAttr().GetBorder().GetRight().GetWidth().IsValid())
865 borderRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetRight().GetWidth());
866 if (attr
.GetTextBoxAttr().GetBorder().GetTop().GetWidth().IsValid())
867 borderTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetTop().GetWidth());
868 if (attr
.GetTextBoxAttr().GetBorder().GetBottom().GetWidth().IsValid())
869 borderBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetBottom().GetWidth());
871 if (attr
.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
872 paddingLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetLeft());
873 if (attr
.GetTextBoxAttr().GetPadding().GetRight().IsValid())
874 paddingRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetRight());
875 if (attr
.GetTextBoxAttr().GetPadding().GetTop().IsValid())
876 paddingTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetTop());
877 if (attr
.GetTextBoxAttr().GetPadding().GetBottom().IsValid())
878 paddingBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetBottom());
880 if (attr
.GetTextBoxAttr().GetOutline().GetLeft().GetWidth().IsValid())
881 outlineLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetLeft().GetWidth());
882 if (attr
.GetTextBoxAttr().GetOutline().GetRight().GetWidth().IsValid())
883 outlineRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetRight().GetWidth());
884 if (attr
.GetTextBoxAttr().GetOutline().GetTop().GetWidth().IsValid())
885 outlineTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetTop().GetWidth());
886 if (attr
.GetTextBoxAttr().GetOutline().GetBottom().GetWidth().IsValid())
887 outlineBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetBottom().GetWidth());
889 int leftTotal
= marginLeft
+ borderLeft
+ paddingLeft
;
890 int rightTotal
= marginRight
+ borderRight
+ paddingRight
;
891 int topTotal
= marginTop
+ borderTop
+ paddingTop
;
892 int bottomTotal
= marginBottom
+ borderBottom
+ paddingBottom
;
894 if (marginRect
!= wxRect())
896 contentRect
.x
= marginRect
.x
+ leftTotal
;
897 contentRect
.y
= marginRect
.y
+ topTotal
;
898 contentRect
.width
= marginRect
.width
- (leftTotal
+ rightTotal
);
899 contentRect
.height
= marginRect
.height
- (topTotal
+ bottomTotal
);
903 marginRect
.x
= contentRect
.x
- leftTotal
;
904 marginRect
.y
= contentRect
.y
- topTotal
;
905 marginRect
.width
= contentRect
.width
+ (leftTotal
+ rightTotal
);
906 marginRect
.height
= contentRect
.height
+ (topTotal
+ bottomTotal
);
909 borderRect
.x
= marginRect
.x
+ marginLeft
;
910 borderRect
.y
= marginRect
.y
+ marginTop
;
911 borderRect
.width
= marginRect
.width
- (marginLeft
+ marginRight
);
912 borderRect
.height
= marginRect
.height
- (marginTop
+ marginBottom
);
914 paddingRect
.x
= marginRect
.x
+ marginLeft
+ borderLeft
;
915 paddingRect
.y
= marginRect
.y
+ marginTop
+ borderTop
;
916 paddingRect
.width
= marginRect
.width
- (marginLeft
+ marginRight
+ borderLeft
+ borderRight
);
917 paddingRect
.height
= marginRect
.height
- (marginTop
+ marginBottom
+ borderTop
+ borderBottom
);
919 // The outline is outside the margin and doesn't influence the overall box position or content size.
920 outlineRect
.x
= marginRect
.x
- outlineLeft
;
921 outlineRect
.y
= marginRect
.y
- outlineTop
;
922 outlineRect
.width
= marginRect
.width
+ (outlineLeft
+ outlineRight
);
923 outlineRect
.height
= marginRect
.height
+ (outlineTop
+ outlineBottom
);
928 // Get the total margin for the object in pixels, taking into account margin, padding and border size
929 bool wxRichTextObject::GetTotalMargin(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& attr
, int& leftMargin
, int& rightMargin
,
930 int& topMargin
, int& bottomMargin
)
932 // Assume boxRect is the area around the content
933 wxRect contentRect
, marginRect
, borderRect
, paddingRect
, outlineRect
;
934 marginRect
= wxRect(0, 0, 1000, 1000);
936 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
938 leftMargin
= contentRect
.GetLeft() - marginRect
.GetLeft();
939 rightMargin
= marginRect
.GetRight() - contentRect
.GetRight();
940 topMargin
= contentRect
.GetTop() - marginRect
.GetTop();
941 bottomMargin
= marginRect
.GetBottom() - contentRect
.GetBottom();
946 // Returns the rectangle which the child has available to it given restrictions specified in the
947 // child attribute, e.g. 50% width of the parent, 400 pixels, x position 20% of the parent, etc.
948 // availableContainerSpace might be a parent that the cell has to compute its width relative to.
949 // E.g. a cell that's 50% of its parent.
950 wxRect
wxRichTextObject::AdjustAvailableSpace(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& WXUNUSED(parentAttr
), const wxRichTextAttr
& childAttr
, const wxRect
& availableParentSpace
, const wxRect
& availableContainerSpace
)
952 wxRect rect
= availableParentSpace
;
955 scale
= buffer
->GetScale();
957 wxTextAttrDimensionConverter
converter(dc
, scale
, availableContainerSpace
.GetSize());
959 if (childAttr
.GetTextBoxAttr().GetWidth().IsValid())
960 rect
.width
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetWidth());
962 if (childAttr
.GetTextBoxAttr().GetHeight().IsValid())
963 rect
.height
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetHeight());
965 // Can specify either left or right for the position (we're assuming we can't
966 // set the left and right edges to effectively set the size. Would we want to do that?)
967 if (childAttr
.GetTextBoxAttr().GetPosition().GetLeft().IsValid())
969 rect
.x
= rect
.x
+ converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetLeft());
971 else if (childAttr
.GetTextBoxAttr().GetPosition().GetRight().IsValid())
973 int x
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetRight());
974 if (childAttr
.GetTextBoxAttr().GetPosition().GetRight().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE
)
975 rect
.x
= availableContainerSpace
.x
+ availableContainerSpace
.width
- rect
.width
;
980 if (childAttr
.GetTextBoxAttr().GetPosition().GetTop().IsValid())
982 rect
.y
= rect
.y
+ converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetTop());
984 else if (childAttr
.GetTextBoxAttr().GetPosition().GetBottom().IsValid())
986 int y
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetBottom());
987 if (childAttr
.GetTextBoxAttr().GetPosition().GetBottom().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE
)
988 rect
.y
= availableContainerSpace
.y
+ availableContainerSpace
.height
- rect
.height
;
993 if (rect
.GetWidth() > availableParentSpace
.GetWidth())
994 rect
.SetWidth(availableParentSpace
.GetWidth());
999 // Dump to output stream for debugging
1000 void wxRichTextObject::Dump(wxTextOutputStream
& stream
)
1002 stream
<< GetClassInfo()->GetClassName() << wxT("\n");
1003 stream
<< wxString::Format(wxT("Size: %d,%d. Position: %d,%d, Range: %ld,%ld"), m_size
.x
, m_size
.y
, m_pos
.x
, m_pos
.y
, m_range
.GetStart(), m_range
.GetEnd()) << wxT("\n");
1004 stream
<< wxString::Format(wxT("Text colour: %d,%d,%d."), (int) m_attributes
.GetTextColour().Red(), (int) m_attributes
.GetTextColour().Green(), (int) m_attributes
.GetTextColour().Blue()) << wxT("\n");
1007 // Gets the containing buffer
1008 wxRichTextBuffer
* wxRichTextObject::GetBuffer() const
1010 const wxRichTextObject
* obj
= this;
1011 while (obj
&& !wxDynamicCast(obj
, wxRichTextBuffer
))
1012 obj
= obj
->GetParent();
1013 return wxDynamicCast(obj
, wxRichTextBuffer
);
1016 // Get the absolute object position, by traversing up the child/parent hierarchy
1017 wxPoint
wxRichTextObject::GetAbsolutePosition() const
1019 wxPoint pt
= GetPosition();
1021 wxRichTextObject
* p
= GetParent();
1024 pt
= pt
+ p
->GetPosition();
1031 // Hit-testing: returns a flag indicating hit test details, plus
1032 // information about position
1033 int wxRichTextObject::HitTest(wxDC
& WXUNUSED(dc
), wxRichTextDrawingContext
& WXUNUSED(context
), const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int WXUNUSED(flags
))
1036 return wxRICHTEXT_HITTEST_NONE
;
1038 wxRect rect
= GetRect();
1039 if (pt
.x
>= rect
.x
&& pt
.x
< rect
.x
+ rect
.width
&&
1040 pt
.y
>= rect
.y
&& pt
.y
< rect
.y
+ rect
.height
)
1043 *contextObj
= GetParentContainer();
1044 textPosition
= GetRange().GetStart();
1045 return wxRICHTEXT_HITTEST_ON
;
1048 return wxRICHTEXT_HITTEST_NONE
;
1051 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1052 // lays out the object again using the maximum ('best') size
1053 bool wxRichTextObject::LayoutToBestSize(wxDC
& dc
, wxRichTextDrawingContext
& context
, wxRichTextBuffer
* buffer
,
1054 const wxRichTextAttr
& parentAttr
, const wxRichTextAttr
& attr
,
1055 const wxRect
& availableParentSpace
, const wxRect
& availableContainerSpace
,
1058 wxRect availableChildRect
= AdjustAvailableSpace(dc
, buffer
, parentAttr
, attr
, availableParentSpace
, availableContainerSpace
);
1059 wxRect originalAvailableRect
= availableChildRect
;
1060 Layout(dc
, context
, availableChildRect
, availableContainerSpace
, style
);
1062 wxSize maxSize
= GetMaxSize();
1064 // Don't ignore if maxSize.x is zero, since we need to redo the paragraph's lines
1066 if (!attr
.GetTextBoxAttr().GetWidth().IsValid() && maxSize
.x
< availableChildRect
.width
)
1068 // Redo the layout with a fixed, minimum size this time.
1069 Invalidate(wxRICHTEXT_ALL
);
1070 wxRichTextAttr
newAttr(attr
);
1071 newAttr
.GetTextBoxAttr().GetWidth().SetValue(maxSize
.x
, wxTEXT_ATTR_UNITS_PIXELS
);
1072 newAttr
.GetTextBoxAttr().GetWidth().SetPosition(wxTEXT_BOX_ATTR_POSITION_ABSOLUTE
);
1074 availableChildRect
= AdjustAvailableSpace(dc
, buffer
, parentAttr
, newAttr
, availableParentSpace
, availableContainerSpace
);
1076 // If a paragraph, align the whole paragraph.
1077 // Problem with this: if we're limited by a floating object, a line may be centered
1078 // w.r.t. the smaller resulting box rather than the actual available width.
1079 if (attr
.HasAlignment() && !GetContainer()->GetFloatCollector()->HasFloats()) // FIXME: aligning whole paragraph not compatible with floating objects
1081 // centering, right-justification
1082 if (attr
.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE
)
1084 availableChildRect
.x
= (originalAvailableRect
.GetWidth() - availableChildRect
.GetWidth())/2 + availableChildRect
.x
;
1086 else if (attr
.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT
)
1088 availableChildRect
.x
= availableChildRect
.x
+ originalAvailableRect
.GetWidth() - availableChildRect
.GetWidth();
1092 Layout(dc
, context
, availableChildRect
, availableContainerSpace
, style
);
1106 // Move the object recursively, by adding the offset from old to new
1107 void wxRichTextObject::Move(const wxPoint
& pt
)
1114 * wxRichTextCompositeObject
1115 * This is the base for drawable objects.
1118 IMPLEMENT_CLASS(wxRichTextCompositeObject
, wxRichTextObject
)
1120 wxRichTextCompositeObject::wxRichTextCompositeObject(wxRichTextObject
* parent
):
1121 wxRichTextObject(parent
)
1125 wxRichTextCompositeObject::~wxRichTextCompositeObject()
1130 /// Get the nth child
1131 wxRichTextObject
* wxRichTextCompositeObject::GetChild(size_t n
) const
1133 wxASSERT ( n
< m_children
.GetCount() );
1135 return m_children
.Item(n
)->GetData();
1138 /// Append a child, returning the position
1139 size_t wxRichTextCompositeObject::AppendChild(wxRichTextObject
* child
)
1141 m_children
.Append(child
);
1142 child
->SetParent(this);
1143 return m_children
.GetCount() - 1;
1146 /// Insert the child in front of the given object, or at the beginning
1147 bool wxRichTextCompositeObject::InsertChild(wxRichTextObject
* child
, wxRichTextObject
* inFrontOf
)
1151 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(inFrontOf
);
1152 m_children
.Insert(node
, child
);
1155 m_children
.Insert(child
);
1156 child
->SetParent(this);
1161 /// Delete the child
1162 bool wxRichTextCompositeObject::RemoveChild(wxRichTextObject
* child
, bool deleteChild
)
1164 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(child
);
1167 wxRichTextObject
* obj
= node
->GetData();
1168 m_children
.Erase(node
);
1177 /// Delete all children
1178 bool wxRichTextCompositeObject::DeleteChildren()
1180 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1183 wxRichTextObjectList::compatibility_iterator oldNode
= node
;
1185 wxRichTextObject
* child
= node
->GetData();
1186 child
->Dereference(); // Only delete if reference count is zero
1188 node
= node
->GetNext();
1189 m_children
.Erase(oldNode
);
1195 /// Get the child count
1196 size_t wxRichTextCompositeObject::GetChildCount() const
1198 return m_children
.GetCount();
1202 void wxRichTextCompositeObject::Copy(const wxRichTextCompositeObject
& obj
)
1204 wxRichTextObject::Copy(obj
);
1208 wxRichTextObjectList::compatibility_iterator node
= obj
.m_children
.GetFirst();
1211 wxRichTextObject
* child
= node
->GetData();
1212 wxRichTextObject
* newChild
= child
->Clone();
1213 newChild
->SetParent(this);
1214 m_children
.Append(newChild
);
1216 node
= node
->GetNext();
1220 /// Hit-testing: returns a flag indicating hit test details, plus
1221 /// information about position
1222 int wxRichTextCompositeObject::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
1225 return wxRICHTEXT_HITTEST_NONE
;
1227 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1230 wxRichTextObject
* child
= node
->GetData();
1232 if (child
->IsShown() && child
->IsTopLevel() && (flags
& wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS
))
1234 // Just check if we hit the overall object
1235 int ret
= child
->wxRichTextObject::HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
1236 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
1239 else if (child
->IsShown())
1241 int ret
= child
->HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
1242 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
1246 node
= node
->GetNext();
1249 return wxRICHTEXT_HITTEST_NONE
;
1252 /// Finds the absolute position and row height for the given character position
1253 bool wxRichTextCompositeObject::FindPosition(wxDC
& dc
, wxRichTextDrawingContext
& context
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
1255 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1258 wxRichTextObject
* child
= node
->GetData();
1260 // Don't recurse if the child is a top-level object,
1261 // such as a text box, because the character position will no longer
1262 // apply. By definition, a top-level object has its own range of
1263 // character positions.
1264 if (!child
->IsTopLevel() && child
->FindPosition(dc
, context
, index
, pt
, height
, forceLineStart
))
1267 node
= node
->GetNext();
1274 void wxRichTextCompositeObject::CalculateRange(long start
, long& end
)
1276 long current
= start
;
1277 long lastEnd
= current
;
1285 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1288 wxRichTextObject
* child
= node
->GetData();
1291 child
->CalculateRange(current
, childEnd
);
1294 current
= childEnd
+ 1;
1296 node
= node
->GetNext();
1301 // A top-level object always has a range of size 1,
1302 // because its children don't count at this level.
1304 m_range
.SetRange(start
, start
);
1306 // An object with no children has zero length
1307 if (m_children
.GetCount() == 0)
1309 m_ownRange
.SetRange(0, lastEnd
);
1315 // An object with no children has zero length
1316 if (m_children
.GetCount() == 0)
1319 m_range
.SetRange(start
, end
);
1323 /// Delete range from layout.
1324 bool wxRichTextCompositeObject::DeleteRange(const wxRichTextRange
& range
)
1326 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1330 wxRichTextObject
* obj
= (wxRichTextObject
*) node
->GetData();
1331 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
1333 // Delete the range in each paragraph
1335 // When a chunk has been deleted, internally the content does not
1336 // now match the ranges.
1337 // However, so long as deletion is not done on the same object twice this is OK.
1338 // If you may delete content from the same object twice, recalculate
1339 // the ranges inbetween DeleteRange calls by calling CalculateRanges, and
1340 // adjust the range you're deleting accordingly.
1342 if (!obj
->GetRange().IsOutside(range
))
1344 // No need to delete within a top-level object; just removing this object will do fine
1345 if (!obj
->IsTopLevel())
1346 obj
->DeleteRange(range
);
1348 // Delete an empty object, or paragraph within this range.
1349 if (obj
->IsEmpty() ||
1350 (range
.GetStart() <= obj
->GetRange().GetStart() && range
.GetEnd() >= obj
->GetRange().GetEnd()))
1352 // An empty paragraph has length 1, so won't be deleted unless the
1353 // whole range is deleted.
1354 RemoveChild(obj
, true);
1364 /// Get any text in this object for the given range
1365 wxString
wxRichTextCompositeObject::GetTextForRange(const wxRichTextRange
& range
) const
1368 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1371 wxRichTextObject
* child
= node
->GetData();
1372 wxRichTextRange childRange
= range
;
1373 if (!child
->GetRange().IsOutside(range
))
1375 childRange
.LimitTo(child
->GetRange());
1377 wxString childText
= child
->GetTextForRange(childRange
);
1381 node
= node
->GetNext();
1387 /// Get the child object at the given character position
1388 wxRichTextObject
* wxRichTextCompositeObject::GetChildAtPosition(long pos
) const
1390 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1393 wxRichTextObject
* child
= node
->GetData();
1394 if (child
->GetRange().GetStart() == pos
)
1396 node
= node
->GetNext();
1401 /// Recursively merge all pieces that can be merged.
1402 bool wxRichTextCompositeObject::Defragment(const wxRichTextRange
& range
)
1404 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1407 wxRichTextObject
* child
= node
->GetData();
1408 if (range
== wxRICHTEXT_ALL
|| !child
->GetRange().IsOutside(range
))
1410 wxRichTextCompositeObject
* composite
= wxDynamicCast(child
, wxRichTextCompositeObject
);
1412 composite
->Defragment();
1414 if (node
->GetNext())
1416 wxRichTextObject
* nextChild
= node
->GetNext()->GetData();
1417 if (child
->CanMerge(nextChild
) && child
->Merge(nextChild
))
1419 nextChild
->Dereference();
1420 m_children
.Erase(node
->GetNext());
1422 // Don't set node -- we'll see if we can merge again with the next
1426 node
= node
->GetNext();
1429 node
= node
->GetNext();
1432 node
= node
->GetNext();
1435 // Delete any remaining empty objects, but leave at least one empty object per composite object.
1436 if (GetChildCount() > 1)
1438 node
= m_children
.GetFirst();
1441 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
1442 wxRichTextObject
* child
= node
->GetData();
1443 if (range
== wxRICHTEXT_ALL
|| !child
->GetRange().IsOutside(range
))
1445 if (child
->IsEmpty())
1447 child
->Dereference();
1448 m_children
.Erase(node
);
1453 node
= node
->GetNext();
1460 /// Dump to output stream for debugging
1461 void wxRichTextCompositeObject::Dump(wxTextOutputStream
& stream
)
1463 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1466 wxRichTextObject
* child
= node
->GetData();
1467 child
->Dump(stream
);
1468 node
= node
->GetNext();
1472 /// Get/set the object size for the given range. Returns false if the range
1473 /// is invalid for this object.
1474 bool wxRichTextCompositeObject::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
1476 if (!range
.IsWithin(GetRange()))
1481 wxArrayInt childExtents
;
1488 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1491 wxRichTextObject
* child
= node
->GetData();
1492 if (!child
->GetRange().IsOutside(range
))
1494 // Floating objects have a zero size within the paragraph.
1495 if (child
->IsFloating())
1500 if (partialExtents
->GetCount() > 0)
1501 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
1505 partialExtents
->Add(0 /* zero size */ + lastSize
);
1512 wxRichTextRange rangeToUse
= range
;
1513 rangeToUse
.LimitTo(child
->GetRange());
1514 if (child
->IsTopLevel())
1515 rangeToUse
= child
->GetOwnRange();
1517 int childDescent
= 0;
1519 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we're already cached the size,
1520 // but it's only going to be used after caching has taken place.
1521 if ((flags
& wxRICHTEXT_HEIGHT_ONLY
) && child
->GetCachedSize().y
!= 0)
1523 childDescent
= child
->GetDescent();
1524 childSize
= child
->GetCachedSize();
1526 sz
.y
= wxMax(sz
.y
, childSize
.y
);
1527 sz
.x
+= childSize
.x
;
1528 descent
= wxMax(descent
, childDescent
);
1530 else if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, context
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
), p
))
1532 sz
.y
= wxMax(sz
.y
, childSize
.y
);
1533 sz
.x
+= childSize
.x
;
1534 descent
= wxMax(descent
, childDescent
);
1536 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange() || child
->IsTopLevel()))
1538 child
->SetCachedSize(childSize
);
1539 child
->SetDescent(childDescent
);
1545 if (partialExtents
->GetCount() > 0)
1546 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
1551 for (i
= 0; i
< childExtents
.GetCount(); i
++)
1553 partialExtents
->Add(childExtents
[i
] + lastSize
);
1563 node
= node
->GetNext();
1569 // Invalidate the buffer. With no argument, invalidates whole buffer.
1570 void wxRichTextCompositeObject::Invalidate(const wxRichTextRange
& invalidRange
)
1572 wxRichTextObject::Invalidate(invalidRange
);
1574 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1577 wxRichTextObject
* child
= node
->GetData();
1578 if (invalidRange
!= wxRICHTEXT_ALL
&& invalidRange
!= wxRICHTEXT_NONE
&& child
->GetRange().IsOutside(invalidRange
))
1582 else if (child
->IsTopLevel())
1584 if (child
->IsFloating() && GetBuffer()->GetFloatCollector() && GetBuffer()->GetFloatCollector()->HasFloat(child
))
1586 // Don't invalidate subhierarchy if we've already been laid out
1590 if (invalidRange
== wxRICHTEXT_NONE
)
1591 child
->Invalidate(wxRICHTEXT_NONE
);
1593 child
->Invalidate(wxRICHTEXT_ALL
); // All children must be invalidated if within parent range
1597 child
->Invalidate(invalidRange
);
1598 node
= node
->GetNext();
1602 // Move the object recursively, by adding the offset from old to new
1603 void wxRichTextCompositeObject::Move(const wxPoint
& pt
)
1605 wxPoint oldPos
= GetPosition();
1607 wxPoint offset
= pt
- oldPos
;
1609 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1612 wxRichTextObject
* child
= node
->GetData();
1613 wxPoint childPos
= child
->GetPosition() + offset
;
1614 child
->Move(childPos
);
1615 node
= node
->GetNext();
1621 * wxRichTextParagraphLayoutBox
1622 * This box knows how to lay out paragraphs.
1625 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraphLayoutBox
, wxRichTextCompositeObject
)
1627 wxRichTextParagraphLayoutBox::wxRichTextParagraphLayoutBox(wxRichTextObject
* parent
):
1628 wxRichTextCompositeObject(parent
)
1633 wxRichTextParagraphLayoutBox::~wxRichTextParagraphLayoutBox()
1635 if (m_floatCollector
)
1637 delete m_floatCollector
;
1638 m_floatCollector
= NULL
;
1642 /// Initialize the object.
1643 void wxRichTextParagraphLayoutBox::Init()
1647 // For now, assume is the only box and has no initial size.
1648 m_range
= wxRichTextRange(0, -1);
1649 m_ownRange
= wxRichTextRange(0, -1);
1651 m_invalidRange
= wxRICHTEXT_ALL
;
1653 m_partialParagraph
= false;
1654 m_floatCollector
= NULL
;
1657 void wxRichTextParagraphLayoutBox::Clear()
1661 if (m_floatCollector
)
1662 delete m_floatCollector
;
1663 m_floatCollector
= NULL
;
1664 m_partialParagraph
= false;
1668 void wxRichTextParagraphLayoutBox::Copy(const wxRichTextParagraphLayoutBox
& obj
)
1672 wxRichTextCompositeObject::Copy(obj
);
1674 m_partialParagraph
= obj
.m_partialParagraph
;
1675 m_defaultAttributes
= obj
.m_defaultAttributes
;
1678 // Gather information about floating objects; only gather floats for those paragraphs that
1679 // will not be formatted again due to optimization, after which floats will be gathered per-paragraph
1681 bool wxRichTextParagraphLayoutBox::UpdateFloatingObjects(const wxRect
& availableRect
, wxRichTextObject
* untilObj
)
1683 if (m_floatCollector
!= NULL
)
1684 delete m_floatCollector
;
1685 m_floatCollector
= new wxRichTextFloatCollector(availableRect
);
1686 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1687 // Only gather floats up to the point we'll start formatting paragraphs.
1688 while (untilObj
&& node
&& node
->GetData() != untilObj
)
1690 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
1691 wxASSERT (child
!= NULL
);
1693 m_floatCollector
->CollectFloat(child
);
1694 node
= node
->GetNext();
1700 // Returns the style sheet associated with the overall buffer.
1701 wxRichTextStyleSheet
* wxRichTextParagraphLayoutBox::GetStyleSheet() const
1703 return GetBuffer() ? GetBuffer()->GetStyleSheet() : (wxRichTextStyleSheet
*) NULL
;
1706 // Get the number of floating objects at this level
1707 int wxRichTextParagraphLayoutBox::GetFloatingObjectCount() const
1709 if (m_floatCollector
)
1710 return m_floatCollector
->GetFloatingObjectCount();
1715 // Get a list of floating objects
1716 bool wxRichTextParagraphLayoutBox::GetFloatingObjects(wxRichTextObjectList
& objects
) const
1718 if (m_floatCollector
)
1720 return m_floatCollector
->GetFloatingObjects(objects
);
1727 void wxRichTextParagraphLayoutBox::UpdateRanges()
1731 start
= GetRange().GetStart();
1733 CalculateRange(start
, end
);
1737 int wxRichTextParagraphLayoutBox::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
1740 return wxRICHTEXT_HITTEST_NONE
;
1742 int ret
= wxRICHTEXT_HITTEST_NONE
;
1743 if (m_floatCollector
&& (flags
& wxRICHTEXT_HITTEST_NO_FLOATING_OBJECTS
) == 0)
1744 ret
= m_floatCollector
->HitTest(dc
, context
, pt
, textPosition
, obj
, flags
);
1746 if (ret
== wxRICHTEXT_HITTEST_NONE
)
1747 return wxRichTextCompositeObject::HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
1755 /// Draw the floating objects
1756 void wxRichTextParagraphLayoutBox::DrawFloats(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
1758 if (m_floatCollector
)
1759 m_floatCollector
->Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
1762 void wxRichTextParagraphLayoutBox::MoveAnchoredObjectToParagraph(wxRichTextParagraph
* from
, wxRichTextParagraph
* to
, wxRichTextObject
* obj
)
1767 from
->RemoveChild(obj
);
1768 to
->AppendChild(obj
);
1772 bool wxRichTextParagraphLayoutBox::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
1777 wxRect
thisRect(GetPosition(), GetCachedSize());
1779 wxRichTextAttr
attr(GetAttributes());
1780 context
.ApplyVirtualAttributes(attr
, this);
1783 if (selection
.IsValid() && GetParentContainer() != this && selection
.WithinSelection(GetRange().GetStart(), GetParentContainer()))
1784 flags
|= wxRICHTEXT_DRAW_SELECTED
;
1786 // Don't draw guidelines if at top level
1787 int theseFlags
= flags
;
1789 theseFlags
&= ~wxRICHTEXT_DRAW_GUIDELINES
;
1790 DrawBoxAttributes(dc
, GetBuffer(), attr
, thisRect
, theseFlags
);
1792 DrawFloats(dc
, context
, range
, selection
, rect
, descent
, style
);
1793 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1796 wxRichTextObject
* child
= node
->GetData();
1798 if (child
&& !child
->GetRange().IsOutside(range
))
1800 wxRect
childRect(child
->GetPosition(), child
->GetCachedSize());
1801 wxRichTextRange childRange
= range
;
1802 if (child
->IsTopLevel())
1804 childRange
= child
->GetOwnRange();
1807 if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) == 0) && childRect
.GetTop() > rect
.GetBottom())
1812 else if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) == 0) && childRect
.GetBottom() < rect
.GetTop())
1817 child
->Draw(dc
, context
, childRange
, selection
, rect
, descent
, style
);
1820 node
= node
->GetNext();
1825 /// Lay the item out
1826 bool wxRichTextParagraphLayoutBox::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
)
1828 SetPosition(rect
.GetPosition());
1833 wxRect availableSpace
;
1834 bool formatRect
= (style
& wxRICHTEXT_LAYOUT_SPECIFIED_RECT
) == wxRICHTEXT_LAYOUT_SPECIFIED_RECT
;
1836 wxRichTextAttr
attr(GetAttributes());
1837 context
.ApplyVirtualAttributes(attr
, this);
1839 // If only laying out a specific area, the passed rect has a different meaning:
1840 // the visible part of the buffer. This is used in wxRichTextCtrl::OnSize,
1841 // so that during a size, only the visible part will be relaid out, or
1842 // it would take too long causing flicker. As an approximation, we assume that
1843 // everything up to the start of the visible area is laid out correctly.
1846 wxRect
rect2(0, 0, rect
.width
, rect
.height
);
1847 availableSpace
= GetAvailableContentArea(dc
, context
, rect2
);
1849 // Invalidate the part of the buffer from the first visible line
1850 // to the end. If other parts of the buffer are currently invalid,
1851 // then they too will be taken into account if they are above
1852 // the visible point.
1854 wxRichTextLine
* line
= GetLineAtYPosition(rect
.y
);
1856 startPos
= line
->GetAbsoluteRange().GetStart();
1858 Invalidate(wxRichTextRange(startPos
, GetOwnRange().GetEnd()));
1862 availableSpace
= GetAvailableContentArea(dc
, context
, rect
);
1865 // Fix the width if we're at the top level
1867 attr
.GetTextBoxAttr().GetWidth().SetValue(rect
.GetWidth(), wxTEXT_ATTR_UNITS_PIXELS
);
1869 int leftMargin
, rightMargin
, topMargin
, bottomMargin
;
1870 wxRichTextObject::GetTotalMargin(dc
, GetBuffer(), attr
, leftMargin
, rightMargin
,
1871 topMargin
, bottomMargin
);
1876 // The maximum paragraph maximum width, so we can set the overall maximum width for this object
1877 int maxMaxWidth
= 0;
1879 // The maximum paragraph minimum width, so we can set the overall minimum width for this object
1880 int maxMinWidth
= 0;
1882 // If we have vertical alignment, we must recalculate everything.
1883 bool hasVerticalAlignment
= (attr
.GetTextBoxAttr().HasVerticalAlignment() &&
1884 (attr
.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP
));
1886 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1888 bool layoutAll
= true;
1890 // Get invalid range, rounding to paragraph start/end.
1891 wxRichTextRange invalidRange
= GetInvalidRange(true);
1893 if (invalidRange
== wxRICHTEXT_NONE
&& !formatRect
)
1896 if (invalidRange
== wxRICHTEXT_ALL
|| hasVerticalAlignment
)
1898 else // If we know what range is affected, start laying out from that point on.
1899 if (invalidRange
.GetStart() >= GetOwnRange().GetStart())
1901 wxRichTextParagraph
* firstParagraph
= GetParagraphAtPosition(invalidRange
.GetStart());
1904 wxRichTextObjectList::compatibility_iterator firstNode
= m_children
.Find(firstParagraph
);
1905 wxRichTextObjectList::compatibility_iterator previousNode
;
1907 previousNode
= firstNode
->GetPrevious();
1912 wxRichTextParagraph
* previousParagraph
= wxDynamicCast(previousNode
->GetData(), wxRichTextParagraph
);
1913 availableSpace
.y
= previousParagraph
->GetPosition().y
+ previousParagraph
->GetCachedSize().y
;
1916 // Now we're going to start iterating from the first affected paragraph.
1924 // Gather information about only those floating objects that will not be formatted,
1925 // after which floats will be gathered per-paragraph during layout.
1926 UpdateFloatingObjects(availableSpace
, node
? node
->GetData() : (wxRichTextObject
*) NULL
);
1928 // A way to force speedy rest-of-buffer layout (the 'else' below)
1929 bool forceQuickLayout
= false;
1931 // First get the size of the paragraphs we won't be laying out
1932 wxRichTextObjectList::compatibility_iterator n
= m_children
.GetFirst();
1933 while (n
&& n
!= node
)
1935 wxRichTextParagraph
* child
= wxDynamicCast(n
->GetData(), wxRichTextParagraph
);
1938 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
1939 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
1940 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
1947 // Assume this box only contains paragraphs
1949 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
1950 // Unsure if this is needed
1951 // wxCHECK_MSG( child, false, wxT("Unknown object in layout") );
1953 if (child
&& child
->IsShown())
1955 // TODO: what if the child hasn't been laid out (e.g. involved in Undo) but still has 'old' lines
1956 if ( !forceQuickLayout
&&
1958 child
->GetLines().IsEmpty() ||
1959 !child
->GetRange().IsOutside(invalidRange
)) )
1961 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1962 // lays out the object again using the minimum size
1963 child
->LayoutToBestSize(dc
, context
, GetBuffer(),
1964 attr
, child
->GetAttributes(), availableSpace
, rect
, style
&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT
);
1966 // Layout must set the cached size
1967 availableSpace
.y
+= child
->GetCachedSize().y
;
1968 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
1969 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
1970 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
1972 // If we're just formatting the visible part of the buffer,
1973 // and we're now past the bottom of the window, and we don't have any
1974 // floating objects (since they may cause wrapping to change for the rest of the
1975 // the buffer), start quick layout.
1976 if (!hasVerticalAlignment
&& formatRect
&& child
->GetPosition().y
> rect
.GetBottom() && GetFloatingObjectCount() == 0)
1977 forceQuickLayout
= true;
1981 // We're outside the immediately affected range, so now let's just
1982 // move everything up or down. This assumes that all the children have previously
1983 // been laid out and have wrapped line lists associated with them.
1984 // TODO: check all paragraphs before the affected range.
1986 int inc
= availableSpace
.y
- child
->GetPosition().y
;
1990 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
1993 if (child
->GetLines().GetCount() == 0)
1995 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1996 // lays out the object again using the minimum size
1997 child
->LayoutToBestSize(dc
, context
, GetBuffer(),
1998 attr
, child
->GetAttributes(), availableSpace
, rect
, style
&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT
);
2000 //child->Layout(dc, availableChildRect, style);
2003 child
->Move(wxPoint(child
->GetPosition().x
, child
->GetPosition().y
+ inc
));
2005 availableSpace
.y
+= child
->GetCachedSize().y
;
2006 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
2007 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
2008 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
2011 node
= node
->GetNext();
2017 node
= node
->GetNext();
2020 node
= m_children
.GetLast();
2021 if (node
&& node
->GetData()->IsShown())
2023 wxRichTextObject
* child
= node
->GetData();
2024 maxHeight
= child
->GetPosition().y
- (GetPosition().y
+ topMargin
) + child
->GetCachedSize().y
;
2027 maxHeight
= 0; // topMargin + bottomMargin;
2029 // Check the bottom edge of any floating object
2030 if (GetFloatCollector() && GetFloatCollector()->HasFloats())
2032 int bottom
= GetFloatCollector()->GetLastRectBottom();
2033 if (bottom
> maxHeight
)
2037 if (attr
.GetTextBoxAttr().GetSize().GetWidth().IsValid())
2039 wxRect r
= AdjustAvailableSpace(dc
, GetBuffer(), wxRichTextAttr() /* not used */, attr
, parentRect
, parentRect
);
2040 int w
= r
.GetWidth();
2042 // Convert external to content rect
2043 w
= w
- leftMargin
- rightMargin
;
2044 maxWidth
= wxMax(maxWidth
, w
);
2045 maxMaxWidth
= wxMax(maxMaxWidth
, w
);
2049 // TODO: Make sure the layout box's position reflects
2050 // the position of the children, but without
2051 // breaking layout of a box within a paragraph.
2054 // TODO: (also in para layout) should set the
2055 // object's size to an absolute one if specified,
2056 // but if not specified, calculate it from content.
2058 // We need to add back the margins etc.
2060 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2061 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxWidth
, maxHeight
));
2062 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2063 SetCachedSize(marginRect
.GetSize());
2066 // The maximum size is the greatest of all maximum widths for all paragraphs.
2068 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2069 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxMaxWidth
, maxHeight
)); // Actually max height is a lie, we can't know it
2070 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2071 SetMaxSize(marginRect
.GetSize());
2074 // The minimum size is the greatest of all minimum widths for all paragraphs.
2076 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2077 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxMinWidth
, maxHeight
)); // Actually max height is a lie, we can't know it
2078 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2079 SetMinSize(marginRect
.GetSize());
2082 if (attr
.GetTextBoxAttr().HasVerticalAlignment() &&
2083 (attr
.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP
))
2086 int leftOverSpace
= availableSpace
.height
- topMargin
- bottomMargin
- maxHeight
;
2087 if (leftOverSpace
> 0)
2089 if (attr
.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_CENTRE
)
2091 yOffset
= (leftOverSpace
/2);
2093 else if (attr
.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_BOTTOM
)
2095 yOffset
= leftOverSpace
;
2099 // Move all the children to vertically align the content
2100 // This doesn't take into account floating objects, unfortunately.
2103 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2106 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2108 child
->Move(wxPoint(child
->GetPosition().x
, child
->GetPosition().y
+ yOffset
));
2110 node
= node
->GetNext();
2115 m_invalidRange
= wxRICHTEXT_NONE
;
2120 /// Get/set the size for the given range.
2121 bool wxRichTextParagraphLayoutBox::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* WXUNUSED(partialExtents
)) const
2125 wxRichTextObjectList::compatibility_iterator startPara
= wxRichTextObjectList::compatibility_iterator();
2126 wxRichTextObjectList::compatibility_iterator endPara
= wxRichTextObjectList::compatibility_iterator();
2128 // First find the first paragraph whose starting position is within the range.
2129 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2132 // child is a paragraph
2133 wxRichTextObject
* child
= node
->GetData();
2134 const wxRichTextRange
& r
= child
->GetRange();
2136 if (r
.GetStart() <= range
.GetStart() && r
.GetEnd() >= range
.GetStart())
2142 node
= node
->GetNext();
2145 // Next find the last paragraph containing part of the range
2146 node
= m_children
.GetFirst();
2149 // child is a paragraph
2150 wxRichTextObject
* child
= node
->GetData();
2151 const wxRichTextRange
& r
= child
->GetRange();
2153 if (r
.GetStart() <= range
.GetEnd() && r
.GetEnd() >= range
.GetEnd())
2159 node
= node
->GetNext();
2162 if (!startPara
|| !endPara
)
2165 // Now we can add up the sizes
2166 for (node
= startPara
; node
; node
= node
->GetNext())
2168 // child is a paragraph
2169 wxRichTextObject
* child
= node
->GetData();
2170 const wxRichTextRange
& childRange
= child
->GetRange();
2171 wxRichTextRange rangeToFind
= range
;
2172 rangeToFind
.LimitTo(childRange
);
2174 if (child
->IsTopLevel())
2175 rangeToFind
= child
->GetOwnRange();
2179 int childDescent
= 0;
2180 child
->GetRangeSize(rangeToFind
, childSize
, childDescent
, dc
, context
, flags
, position
);
2182 descent
= wxMax(childDescent
, descent
);
2184 sz
.x
= wxMax(sz
.x
, childSize
.x
);
2185 sz
.y
+= childSize
.y
;
2187 if (node
== endPara
)
2196 /// Get the paragraph at the given position
2197 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphAtPosition(long pos
, bool caretPosition
) const
2202 // First find the first paragraph whose starting position is within the range.
2203 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2206 // child is a paragraph
2207 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2208 // wxASSERT (child != NULL);
2212 // Return first child in buffer if position is -1
2216 if (child
->GetRange().Contains(pos
))
2220 node
= node
->GetNext();
2225 /// Get the line at the given position
2226 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineAtPosition(long pos
, bool caretPosition
) const
2231 // First find the first paragraph whose starting position is within the range.
2232 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2235 wxRichTextObject
* obj
= (wxRichTextObject
*) node
->GetData();
2236 if (obj
->GetRange().Contains(pos
))
2238 // child is a paragraph
2239 wxRichTextParagraph
* child
= wxDynamicCast(obj
, wxRichTextParagraph
);
2240 // wxASSERT (child != NULL);
2244 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2247 wxRichTextLine
* line
= node2
->GetData();
2249 wxRichTextRange range
= line
->GetAbsoluteRange();
2251 if (range
.Contains(pos
) ||
2253 // If the position is end-of-paragraph, then return the last line of
2254 // of the paragraph.
2255 ((range
.GetEnd() == child
->GetRange().GetEnd()-1) && (pos
== child
->GetRange().GetEnd())))
2258 node2
= node2
->GetNext();
2263 node
= node
->GetNext();
2266 int lineCount
= GetLineCount();
2268 return GetLineForVisibleLineNumber(lineCount
-1);
2273 /// Get the line at the given y pixel position, or the last line.
2274 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineAtYPosition(int y
) const
2276 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2279 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2280 // wxASSERT (child != NULL);
2284 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2287 wxRichTextLine
* line
= node2
->GetData();
2289 wxRect
rect(line
->GetRect());
2291 if (y
<= rect
.GetBottom())
2294 node2
= node2
->GetNext();
2298 node
= node
->GetNext();
2302 int lineCount
= GetLineCount();
2304 return GetLineForVisibleLineNumber(lineCount
-1);
2309 /// Get the number of visible lines
2310 int wxRichTextParagraphLayoutBox::GetLineCount() const
2314 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2317 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2318 // wxASSERT (child != NULL);
2321 count
+= child
->GetLines().GetCount();
2323 node
= node
->GetNext();
2329 /// Get the paragraph for a given line
2330 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphForLine(wxRichTextLine
* line
) const
2332 return GetParagraphAtPosition(line
->GetAbsoluteRange().GetStart());
2335 /// Get the line size at the given position
2336 wxSize
wxRichTextParagraphLayoutBox::GetLineSizeAtPosition(long pos
, bool caretPosition
) const
2338 wxRichTextLine
* line
= GetLineAtPosition(pos
, caretPosition
);
2341 return line
->GetSize();
2344 return wxSize(0, 0);
2348 /// Convenience function to add a paragraph of text
2349 wxRichTextRange
wxRichTextParagraphLayoutBox::AddParagraph(const wxString
& text
, wxRichTextAttr
* paraStyle
)
2351 // Don't use the base style, just the default style, and the base style will
2352 // be combined at display time.
2353 // Divide into paragraph and character styles.
2355 wxRichTextAttr defaultCharStyle
;
2356 wxRichTextAttr defaultParaStyle
;
2358 // If the default style is a named paragraph style, don't apply any character formatting
2359 // to the initial text string.
2360 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2362 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2364 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2367 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2369 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2370 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2372 wxRichTextParagraph
* para
= new wxRichTextParagraph(text
, this, pStyle
, cStyle
);
2373 para
->GetAttributes().GetTextBoxAttr().Reset();
2379 return para
->GetRange();
2382 /// Adds multiple paragraphs, based on newlines.
2383 wxRichTextRange
wxRichTextParagraphLayoutBox::AddParagraphs(const wxString
& text
, wxRichTextAttr
* paraStyle
)
2385 // Don't use the base style, just the default style, and the base style will
2386 // be combined at display time.
2387 // Divide into paragraph and character styles.
2389 wxRichTextAttr defaultCharStyle
;
2390 wxRichTextAttr defaultParaStyle
;
2392 // If the default style is a named paragraph style, don't apply any character formatting
2393 // to the initial text string.
2394 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2396 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2398 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2401 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2403 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2404 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2406 wxRichTextParagraph
* firstPara
= NULL
;
2407 wxRichTextParagraph
* lastPara
= NULL
;
2409 wxRichTextRange
range(-1, -1);
2412 size_t len
= text
.length();
2414 wxRichTextParagraph
* para
= new wxRichTextParagraph(wxEmptyString
, this, pStyle
, cStyle
);
2415 para
->GetAttributes().GetTextBoxAttr().Reset();
2424 wxChar ch
= text
[i
];
2425 if (ch
== wxT('\n') || ch
== wxT('\r'))
2429 wxRichTextPlainText
* plainText
= (wxRichTextPlainText
*) para
->GetChildren().GetFirst()->GetData();
2430 plainText
->SetText(line
);
2432 para
= new wxRichTextParagraph(wxEmptyString
, this, pStyle
, cStyle
);
2433 para
->GetAttributes().GetTextBoxAttr().Reset();
2438 line
= wxEmptyString
;
2449 wxRichTextPlainText
* plainText
= (wxRichTextPlainText
*) para
->GetChildren().GetFirst()->GetData();
2450 plainText
->SetText(line
);
2455 return wxRichTextRange(firstPara
->GetRange().GetStart(), lastPara
->GetRange().GetEnd());
2458 /// Convenience function to add an image
2459 wxRichTextRange
wxRichTextParagraphLayoutBox::AddImage(const wxImage
& image
, wxRichTextAttr
* paraStyle
)
2461 // Don't use the base style, just the default style, and the base style will
2462 // be combined at display time.
2463 // Divide into paragraph and character styles.
2465 wxRichTextAttr defaultCharStyle
;
2466 wxRichTextAttr defaultParaStyle
;
2468 // If the default style is a named paragraph style, don't apply any character formatting
2469 // to the initial text string.
2470 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2472 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2474 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2477 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2479 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2480 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2482 wxRichTextParagraph
* para
= new wxRichTextParagraph(this, pStyle
);
2483 para
->GetAttributes().GetTextBoxAttr().Reset();
2485 para
->AppendChild(new wxRichTextImage(image
, this, cStyle
));
2489 return para
->GetRange();
2493 /// Insert fragment into this box at the given position. If partialParagraph is true,
2494 /// it is assumed that the last (or only) paragraph is just a piece of data with no paragraph
2497 bool wxRichTextParagraphLayoutBox::InsertFragment(long position
, wxRichTextParagraphLayoutBox
& fragment
)
2499 // First, find the first paragraph whose starting position is within the range.
2500 wxRichTextParagraph
* para
= GetParagraphAtPosition(position
);
2503 wxRichTextAttr originalAttr
= para
->GetAttributes();
2505 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(para
);
2507 // Now split at this position, returning the object to insert the new
2508 // ones in front of.
2509 wxRichTextObject
* nextObject
= para
->SplitAt(position
);
2511 // Special case: partial paragraph, just one paragraph. Might be a small amount of
2512 // text, for example, so let's optimize.
2514 if (fragment
.GetPartialParagraph() && fragment
.GetChildren().GetCount() == 1)
2516 // Add the first para to this para...
2517 wxRichTextObjectList::compatibility_iterator firstParaNode
= fragment
.GetChildren().GetFirst();
2521 // Iterate through the fragment paragraph inserting the content into this paragraph.
2522 wxRichTextParagraph
* firstPara
= wxDynamicCast(firstParaNode
->GetData(), wxRichTextParagraph
);
2523 wxASSERT (firstPara
!= NULL
);
2525 wxRichTextObjectList::compatibility_iterator objectNode
= firstPara
->GetChildren().GetFirst();
2528 wxRichTextObject
* newObj
= objectNode
->GetData()->Clone();
2533 para
->AppendChild(newObj
);
2537 // Insert before nextObject
2538 para
->InsertChild(newObj
, nextObject
);
2541 objectNode
= objectNode
->GetNext();
2548 // Procedure for inserting a fragment consisting of a number of
2551 // 1. Remove and save the content that's after the insertion point, for adding
2552 // back once we've added the fragment.
2553 // 2. Add the content from the first fragment paragraph to the current
2555 // 3. Add remaining fragment paragraphs after the current paragraph.
2556 // 4. Add back the saved content from the first paragraph. If partialParagraph
2557 // is true, add it to the last paragraph added and not a new one.
2559 // 1. Remove and save objects after split point.
2560 wxList savedObjects
;
2562 para
->MoveToList(nextObject
, savedObjects
);
2564 // 2. Add the content from the 1st fragment paragraph.
2565 wxRichTextObjectList::compatibility_iterator firstParaNode
= fragment
.GetChildren().GetFirst();
2569 wxRichTextParagraph
* firstPara
= wxDynamicCast(firstParaNode
->GetData(), wxRichTextParagraph
);
2570 wxASSERT(firstPara
!= NULL
);
2572 if (!(fragment
.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
))
2573 para
->SetAttributes(firstPara
->GetAttributes());
2575 // Save empty paragraph attributes for appending later
2576 // These are character attributes deliberately set for a new paragraph. Without this,
2577 // we couldn't pass default attributes when appending a new paragraph.
2578 wxRichTextAttr emptyParagraphAttributes
;
2580 wxRichTextObjectList::compatibility_iterator objectNode
= firstPara
->GetChildren().GetFirst();
2582 if (objectNode
&& firstPara
->GetChildren().GetCount() == 1 && objectNode
->GetData()->IsEmpty())
2583 emptyParagraphAttributes
= objectNode
->GetData()->GetAttributes();
2587 wxRichTextObject
* newObj
= objectNode
->GetData()->Clone();
2590 para
->AppendChild(newObj
);
2592 objectNode
= objectNode
->GetNext();
2595 // 3. Add remaining fragment paragraphs after the current paragraph.
2596 wxRichTextObjectList::compatibility_iterator nextParagraphNode
= node
->GetNext();
2597 wxRichTextObject
* nextParagraph
= NULL
;
2598 if (nextParagraphNode
)
2599 nextParagraph
= nextParagraphNode
->GetData();
2601 wxRichTextObjectList::compatibility_iterator i
= fragment
.GetChildren().GetFirst()->GetNext();
2602 wxRichTextParagraph
* finalPara
= para
;
2604 bool needExtraPara
= (!i
|| !fragment
.GetPartialParagraph());
2606 // If there was only one paragraph, we need to insert a new one.
2609 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2610 wxASSERT( para
!= NULL
);
2612 finalPara
= (wxRichTextParagraph
*) para
->Clone();
2615 InsertChild(finalPara
, nextParagraph
);
2617 AppendChild(finalPara
);
2622 // If there was only one paragraph, or we have full paragraphs in our fragment,
2623 // we need to insert a new one.
2626 finalPara
= new wxRichTextParagraph
;
2629 InsertChild(finalPara
, nextParagraph
);
2631 AppendChild(finalPara
);
2634 // 4. Add back the remaining content.
2638 finalPara
->MoveFromList(savedObjects
);
2640 // Ensure there's at least one object
2641 if (finalPara
->GetChildCount() == 0)
2643 wxRichTextPlainText
* text
= new wxRichTextPlainText(wxEmptyString
);
2644 text
->SetAttributes(emptyParagraphAttributes
);
2646 finalPara
->AppendChild(text
);
2650 if ((fragment
.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
) && firstPara
)
2651 finalPara
->SetAttributes(firstPara
->GetAttributes());
2652 else if (finalPara
&& finalPara
!= para
)
2653 finalPara
->SetAttributes(originalAttr
);
2661 wxRichTextObjectList::compatibility_iterator i
= fragment
.GetChildren().GetFirst();
2664 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2665 wxASSERT( para
!= NULL
);
2667 AppendChild(para
->Clone());
2676 /// Make a copy of the fragment corresponding to the given range, putting it in 'fragment'.
2677 /// If there was an incomplete paragraph at the end, partialParagraph is set to true.
2678 bool wxRichTextParagraphLayoutBox::CopyFragment(const wxRichTextRange
& range
, wxRichTextParagraphLayoutBox
& fragment
)
2680 wxRichTextObjectList::compatibility_iterator i
= GetChildren().GetFirst();
2683 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2684 wxASSERT( para
!= NULL
);
2686 if (!para
->GetRange().IsOutside(range
))
2688 fragment
.AppendChild(para
->Clone());
2693 // Now top and tail the first and last paragraphs in our new fragment (which might be the same).
2694 if (!fragment
.IsEmpty())
2696 wxRichTextParagraph
* firstPara
= wxDynamicCast(fragment
.GetChildren().GetFirst()->GetData(), wxRichTextParagraph
);
2697 wxASSERT( firstPara
!= NULL
);
2699 wxRichTextParagraph
* lastPara
= wxDynamicCast(fragment
.GetChildren().GetLast()->GetData(), wxRichTextParagraph
);
2700 wxASSERT( lastPara
!= NULL
);
2702 if (!firstPara
|| !lastPara
)
2705 bool isFragment
= (range
.GetEnd() < lastPara
->GetRange().GetEnd());
2707 long firstPos
= firstPara
->GetRange().GetStart();
2709 // Adjust for renumbering from zero
2710 wxRichTextRange
topTailRange(range
.GetStart() - firstPos
, range
.GetEnd() - firstPos
);
2713 fragment
.CalculateRange(0, end
);
2715 // Chop off the start of the paragraph
2716 if (topTailRange
.GetStart() > 0)
2718 wxRichTextRange
r(0, topTailRange
.GetStart()-1);
2719 firstPara
->DeleteRange(r
);
2721 // Make sure the numbering is correct
2722 fragment
.CalculateRange(0, end
);
2724 // Now, we've deleted some positions, so adjust the range
2726 topTailRange
.SetStart(range
.GetLength());
2727 topTailRange
.SetEnd(fragment
.GetOwnRange().GetEnd());
2731 topTailRange
.SetStart(range
.GetLength());
2732 topTailRange
.SetEnd(fragment
.GetOwnRange().GetEnd());
2735 if (topTailRange
.GetStart() < lastPara
->GetRange().GetEnd())
2737 lastPara
->DeleteRange(topTailRange
);
2739 // Make sure the numbering is correct
2741 fragment
.CalculateRange(0, end
);
2743 // We only have part of a paragraph at the end
2744 fragment
.SetPartialParagraph(true);
2748 // We have a partial paragraph (don't save last new paragraph marker)
2749 // or complete paragraph
2750 fragment
.SetPartialParagraph(isFragment
);
2757 /// Given a position, get the number of the visible line (potentially many to a paragraph),
2758 /// starting from zero at the start of the buffer.
2759 long wxRichTextParagraphLayoutBox::GetVisibleLineNumber(long pos
, bool caretPosition
, bool startOfLine
) const
2766 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2769 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2770 // wxASSERT( child != NULL );
2774 if (child
->GetRange().Contains(pos
))
2776 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2779 wxRichTextLine
* line
= node2
->GetData();
2780 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
2782 if (lineRange
.Contains(pos
) || pos
== lineRange
.GetStart())
2784 // If the caret is displayed at the end of the previous wrapped line,
2785 // we want to return the line it's _displayed_ at (not the actual line
2786 // containing the position).
2787 if (lineRange
.GetStart() == pos
&& !startOfLine
&& child
->GetRange().GetStart() != pos
)
2788 return lineCount
- 1;
2795 node2
= node2
->GetNext();
2797 // If we didn't find it in the lines, it must be
2798 // the last position of the paragraph. So return the last line.
2802 lineCount
+= child
->GetLines().GetCount();
2805 node
= node
->GetNext();
2812 /// Given a line number, get the corresponding wxRichTextLine object.
2813 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineForVisibleLineNumber(long lineNumber
) const
2817 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2820 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2821 // wxASSERT(child != NULL);
2825 if (lineNumber
< (int) (child
->GetLines().GetCount() + lineCount
))
2827 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2830 wxRichTextLine
* line
= node2
->GetData();
2832 if (lineCount
== lineNumber
)
2837 node2
= node2
->GetNext();
2841 lineCount
+= child
->GetLines().GetCount();
2844 node
= node
->GetNext();
2851 /// Delete range from layout.
2852 bool wxRichTextParagraphLayoutBox::DeleteRange(const wxRichTextRange
& range
)
2854 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2856 wxRichTextParagraph
* firstPara
= NULL
;
2859 wxRichTextParagraph
* obj
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2860 // wxASSERT (obj != NULL);
2862 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
2866 // Delete the range in each paragraph
2868 if (!obj
->GetRange().IsOutside(range
))
2870 // Deletes the content of this object within the given range
2871 obj
->DeleteRange(range
);
2873 wxRichTextRange thisRange
= obj
->GetRange();
2874 wxRichTextAttr thisAttr
= obj
->GetAttributes();
2876 // If the whole paragraph is within the range to delete,
2877 // delete the whole thing.
2878 if (range
.GetStart() <= thisRange
.GetStart() && range
.GetEnd() >= thisRange
.GetEnd())
2880 // Delete the whole object
2881 RemoveChild(obj
, true);
2884 else if (!firstPara
)
2887 // If the range includes the paragraph end, we need to join this
2888 // and the next paragraph.
2889 if (range
.GetEnd() <= thisRange
.GetEnd())
2891 // We need to move the objects from the next paragraph
2892 // to this paragraph
2894 wxRichTextParagraph
* nextParagraph
= NULL
;
2895 if ((range
.GetEnd() < thisRange
.GetEnd()) && obj
)
2896 nextParagraph
= obj
;
2899 // We're ending at the end of the paragraph, so merge the _next_ paragraph.
2901 nextParagraph
= wxDynamicCast(next
->GetData(), wxRichTextParagraph
);
2904 bool applyFinalParagraphStyle
= firstPara
&& nextParagraph
&& nextParagraph
!= firstPara
;
2906 wxRichTextAttr nextParaAttr
;
2907 if (applyFinalParagraphStyle
)
2909 // Special case when deleting the end of a paragraph - use _this_ paragraph's style,
2910 // not the next one.
2911 if (range
.GetStart() == range
.GetEnd() && range
.GetStart() == thisRange
.GetEnd())
2912 nextParaAttr
= thisAttr
;
2914 nextParaAttr
= nextParagraph
->GetAttributes();
2917 if (firstPara
&& nextParagraph
&& firstPara
!= nextParagraph
)
2919 // Move the objects to the previous para
2920 wxRichTextObjectList::compatibility_iterator node1
= nextParagraph
->GetChildren().GetFirst();
2924 wxRichTextObject
* obj1
= node1
->GetData();
2926 firstPara
->AppendChild(obj1
);
2928 wxRichTextObjectList::compatibility_iterator next1
= node1
->GetNext();
2929 nextParagraph
->GetChildren().Erase(node1
);
2934 // Delete the paragraph
2935 RemoveChild(nextParagraph
, true);
2938 // Avoid empty paragraphs
2939 if (firstPara
&& firstPara
->GetChildren().GetCount() == 0)
2941 wxRichTextPlainText
* text
= new wxRichTextPlainText(wxEmptyString
);
2942 firstPara
->AppendChild(text
);
2945 if (applyFinalParagraphStyle
)
2946 firstPara
->SetAttributes(nextParaAttr
);
2959 /// Get any text in this object for the given range
2960 wxString
wxRichTextParagraphLayoutBox::GetTextForRange(const wxRichTextRange
& range
) const
2964 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2967 wxRichTextObject
* child
= node
->GetData();
2968 if (!child
->GetRange().IsOutside(range
))
2970 wxRichTextRange childRange
= range
;
2971 childRange
.LimitTo(child
->GetRange());
2973 wxString childText
= child
->GetTextForRange(childRange
);
2977 if ((childRange
.GetEnd() == child
->GetRange().GetEnd()) && node
->GetNext())
2982 node
= node
->GetNext();
2988 /// Get all the text
2989 wxString
wxRichTextParagraphLayoutBox::GetText() const
2991 return GetTextForRange(GetOwnRange());
2994 /// Get the paragraph by number
2995 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphAtLine(long paragraphNumber
) const
2997 if ((size_t) paragraphNumber
>= GetChildCount())
3000 return (wxRichTextParagraph
*) GetChild((size_t) paragraphNumber
);
3003 /// Get the length of the paragraph
3004 int wxRichTextParagraphLayoutBox::GetParagraphLength(long paragraphNumber
) const
3006 wxRichTextParagraph
* para
= GetParagraphAtLine(paragraphNumber
);
3008 return para
->GetRange().GetLength() - 1; // don't include newline
3013 /// Get the text of the paragraph
3014 wxString
wxRichTextParagraphLayoutBox::GetParagraphText(long paragraphNumber
) const
3016 wxRichTextParagraph
* para
= GetParagraphAtLine(paragraphNumber
);
3018 return para
->GetTextForRange(para
->GetRange());
3020 return wxEmptyString
;
3023 /// Convert zero-based line column and paragraph number to a position.
3024 long wxRichTextParagraphLayoutBox::XYToPosition(long x
, long y
) const
3026 wxRichTextParagraph
* para
= GetParagraphAtLine(y
);
3029 return para
->GetRange().GetStart() + x
;
3035 /// Convert zero-based position to line column and paragraph number
3036 bool wxRichTextParagraphLayoutBox::PositionToXY(long pos
, long* x
, long* y
) const
3038 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
);
3042 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3045 wxRichTextObject
* child
= node
->GetData();
3049 node
= node
->GetNext();
3053 *x
= pos
- para
->GetRange().GetStart();
3061 /// Get the leaf object in a paragraph at this position.
3062 /// Given a line number, get the corresponding wxRichTextLine object.
3063 wxRichTextObject
* wxRichTextParagraphLayoutBox::GetLeafObjectAtPosition(long position
) const
3065 wxRichTextParagraph
* para
= GetParagraphAtPosition(position
);
3068 wxRichTextObjectList::compatibility_iterator node
= para
->GetChildren().GetFirst();
3072 wxRichTextObject
* child
= node
->GetData();
3073 if (child
->GetRange().Contains(position
))
3076 node
= node
->GetNext();
3078 if (position
== para
->GetRange().GetEnd() && para
->GetChildCount() > 0)
3079 return para
->GetChildren().GetLast()->GetData();
3084 /// Set character or paragraph text attributes: apply character styles only to immediate text nodes
3085 bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange
& range
, const wxRichTextAttr
& style
, int flags
)
3087 bool characterStyle
= false;
3088 bool paragraphStyle
= false;
3090 if (style
.IsCharacterStyle())
3091 characterStyle
= true;
3092 if (style
.IsParagraphStyle())
3093 paragraphStyle
= true;
3095 wxRichTextBuffer
* buffer
= GetBuffer();
3097 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
3098 bool applyMinimal
= ((flags
& wxRICHTEXT_SETSTYLE_OPTIMIZE
) != 0);
3099 bool parasOnly
= ((flags
& wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY
) != 0);
3100 bool charactersOnly
= ((flags
& wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY
) != 0);
3101 bool resetExistingStyle
= ((flags
& wxRICHTEXT_SETSTYLE_RESET
) != 0);
3102 bool removeStyle
= ((flags
& wxRICHTEXT_SETSTYLE_REMOVE
) != 0);
3104 // Apply paragraph style first, if any
3105 wxRichTextAttr
wholeStyle(style
);
3107 if (!removeStyle
&& wholeStyle
.HasParagraphStyleName() && buffer
->GetStyleSheet())
3109 wxRichTextParagraphStyleDefinition
* def
= buffer
->GetStyleSheet()->FindParagraphStyle(wholeStyle
.GetParagraphStyleName());
3111 wxRichTextApplyStyle(wholeStyle
, def
->GetStyleMergedWithBase(buffer
->GetStyleSheet()));
3114 // Limit the attributes to be set to the content to only character attributes.
3115 wxRichTextAttr
characterAttributes(wholeStyle
);
3116 characterAttributes
.SetFlags(characterAttributes
.GetFlags() & (wxTEXT_ATTR_CHARACTER
));
3118 if (!removeStyle
&& characterAttributes
.HasCharacterStyleName() && buffer
->GetStyleSheet())
3120 wxRichTextCharacterStyleDefinition
* def
= buffer
->GetStyleSheet()->FindCharacterStyle(characterAttributes
.GetCharacterStyleName());
3122 wxRichTextApplyStyle(characterAttributes
, def
->GetStyleMergedWithBase(buffer
->GetStyleSheet()));
3125 // If we are associated with a control, make undoable; otherwise, apply immediately
3128 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3130 wxRichTextAction
* action
= NULL
;
3132 if (haveControl
&& withUndo
)
3134 action
= new wxRichTextAction(NULL
, _("Change Style"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
3135 action
->SetRange(range
);
3136 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3139 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3142 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3143 // wxASSERT (para != NULL);
3145 if (para
&& para
->GetChildCount() > 0)
3147 // Stop searching if we're beyond the range of interest
3148 if (para
->GetRange().GetStart() > range
.GetEnd())
3151 if (!para
->GetRange().IsOutside(range
))
3153 // We'll be using a copy of the paragraph to make style changes,
3154 // not updating the buffer directly.
3155 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3157 if (haveControl
&& withUndo
)
3159 newPara
= new wxRichTextParagraph(*para
);
3160 action
->GetNewParagraphs().AppendChild(newPara
);
3162 // Also store the old ones for Undo
3163 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3168 // If we're specifying paragraphs only, then we really mean character formatting
3169 // to be included in the paragraph style
3170 if ((paragraphStyle
|| parasOnly
) && !charactersOnly
)
3174 // Removes the given style from the paragraph
3175 wxRichTextRemoveStyle(newPara
->GetAttributes(), style
);
3177 else if (resetExistingStyle
)
3178 newPara
->GetAttributes() = wholeStyle
;
3183 // Only apply attributes that will make a difference to the combined
3184 // style as seen on the display
3185 wxRichTextAttr
combinedAttr(para
->GetCombinedAttributes(true));
3186 wxRichTextApplyStyle(newPara
->GetAttributes(), wholeStyle
, & combinedAttr
);
3189 wxRichTextApplyStyle(newPara
->GetAttributes(), wholeStyle
);
3193 // When applying paragraph styles dynamically, don't change the text objects' attributes
3194 // since they will computed as needed. Only apply the character styling if it's _only_
3195 // character styling. This policy is subject to change and might be put under user control.
3197 // Hm. we might well be applying a mix of paragraph and character styles, in which
3198 // case we _do_ want to apply character styles regardless of what para styles are set.
3199 // But if we're applying a paragraph style, which has some character attributes, but
3200 // we only want the paragraphs to hold this character style, then we _don't_ want to
3201 // apply the character style. So we need to be able to choose.
3203 if (!parasOnly
&& (characterStyle
|charactersOnly
) && range
.GetStart() != newPara
->GetRange().GetEnd())
3205 wxRichTextRange
childRange(range
);
3206 childRange
.LimitTo(newPara
->GetRange());
3208 // Find the starting position and if necessary split it so
3209 // we can start applying a different style.
3210 // TODO: check that the style actually changes or is different
3211 // from style outside of range
3212 wxRichTextObject
* firstObject
wxDUMMY_INITIALIZE(NULL
);
3213 wxRichTextObject
* lastObject
wxDUMMY_INITIALIZE(NULL
);
3215 if (childRange
.GetStart() == newPara
->GetRange().GetStart())
3216 firstObject
= newPara
->GetChildren().GetFirst()->GetData();
3218 firstObject
= newPara
->SplitAt(range
.GetStart());
3220 // Increment by 1 because we're apply the style one _after_ the split point
3221 long splitPoint
= childRange
.GetEnd();
3222 if (splitPoint
!= newPara
->GetRange().GetEnd())
3226 if (splitPoint
== newPara
->GetRange().GetEnd())
3227 lastObject
= newPara
->GetChildren().GetLast()->GetData();
3229 // lastObject is set as a side-effect of splitting. It's
3230 // returned as the object before the new object.
3231 (void) newPara
->SplitAt(splitPoint
, & lastObject
);
3233 wxASSERT(firstObject
!= NULL
);
3234 wxASSERT(lastObject
!= NULL
);
3236 if (!firstObject
|| !lastObject
)
3239 wxRichTextObjectList::compatibility_iterator firstNode
= newPara
->GetChildren().Find(firstObject
);
3240 wxRichTextObjectList::compatibility_iterator lastNode
= newPara
->GetChildren().Find(lastObject
);
3242 wxASSERT(firstNode
);
3245 wxRichTextObjectList::compatibility_iterator node2
= firstNode
;
3249 wxRichTextObject
* child
= node2
->GetData();
3253 // Removes the given style from the paragraph
3254 wxRichTextRemoveStyle(child
->GetAttributes(), style
);
3256 else if (resetExistingStyle
)
3257 child
->GetAttributes() = characterAttributes
;
3262 // Only apply attributes that will make a difference to the combined
3263 // style as seen on the display
3264 wxRichTextAttr
combinedAttr(newPara
->GetCombinedAttributes(child
->GetAttributes(), true));
3265 wxRichTextApplyStyle(child
->GetAttributes(), characterAttributes
, & combinedAttr
);
3268 wxRichTextApplyStyle(child
->GetAttributes(), characterAttributes
);
3271 if (node2
== lastNode
)
3274 node2
= node2
->GetNext();
3280 node
= node
->GetNext();
3283 // Do action, or delay it until end of batch.
3284 if (haveControl
&& withUndo
)
3285 buffer
->SubmitAction(action
);
3290 // Just change the attributes for this single object.
3291 void wxRichTextParagraphLayoutBox::SetStyle(wxRichTextObject
* obj
, const wxRichTextAttr
& textAttr
, int flags
)
3293 wxRichTextBuffer
* buffer
= GetBuffer();
3294 bool withUndo
= flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
;
3295 bool resetExistingStyle
= ((flags
& wxRICHTEXT_SETSTYLE_RESET
) != 0);
3296 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3298 wxRichTextAction
*action
= NULL
;
3299 wxRichTextAttr newAttr
= obj
->GetAttributes();
3300 if (resetExistingStyle
)
3303 newAttr
.Apply(textAttr
);
3305 if (haveControl
&& withUndo
)
3307 action
= new wxRichTextAction(NULL
, _("Change Object Style"), wxRICHTEXT_CHANGE_ATTRIBUTES
, buffer
, obj
->GetContainer(), buffer
->GetRichTextCtrl());
3308 action
->SetRange(obj
->GetRange().FromInternal());
3309 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3310 action
->MakeObject(obj
);
3312 action
->GetAttributes() = newAttr
;
3315 obj
->GetAttributes() = newAttr
;
3317 if (haveControl
&& withUndo
)
3318 buffer
->SubmitAction(action
);
3321 /// Get the text attributes for this position.
3322 bool wxRichTextParagraphLayoutBox::GetStyle(long position
, wxRichTextAttr
& style
)
3324 return DoGetStyle(position
, style
, true);
3327 bool wxRichTextParagraphLayoutBox::GetUncombinedStyle(long position
, wxRichTextAttr
& style
)
3329 return DoGetStyle(position
, style
, false);
3332 /// Implementation helper for GetStyle. If combineStyles is true, combine base, paragraph and
3333 /// context attributes.
3334 bool wxRichTextParagraphLayoutBox::DoGetStyle(long position
, wxRichTextAttr
& style
, bool combineStyles
)
3336 wxRichTextObject
* obj
wxDUMMY_INITIALIZE(NULL
);
3338 if (style
.IsParagraphStyle())
3340 obj
= GetParagraphAtPosition(position
);
3345 // Start with the base style
3346 style
= GetAttributes();
3347 style
.GetTextBoxAttr().Reset();
3349 // Apply the paragraph style
3350 wxRichTextApplyStyle(style
, obj
->GetAttributes());
3353 style
= obj
->GetAttributes();
3360 obj
= GetLeafObjectAtPosition(position
);
3365 wxRichTextParagraph
* para
= wxDynamicCast(obj
->GetParent(), wxRichTextParagraph
);
3366 style
= para
? para
->GetCombinedAttributes(obj
->GetAttributes()) : obj
->GetAttributes();
3369 style
= obj
->GetAttributes();
3377 static bool wxHasStyle(long flags
, long style
)
3379 return (flags
& style
) != 0;
3382 /// Combines 'style' with 'currentStyle' for the purpose of summarising the attributes of a range of
3384 bool wxRichTextParagraphLayoutBox::CollectStyle(wxRichTextAttr
& currentStyle
, const wxRichTextAttr
& style
, wxRichTextAttr
& clashingAttr
, wxRichTextAttr
& absentAttr
)
3386 currentStyle
.CollectCommonAttributes(style
, clashingAttr
, absentAttr
);
3391 /// Get the combined style for a range - if any attribute is different within the range,
3392 /// that attribute is not present within the flags.
3393 /// *** Note that this is not recursive, and so assumes that content inside a paragraph is not itself
3395 bool wxRichTextParagraphLayoutBox::GetStyleForRange(const wxRichTextRange
& range
, wxRichTextAttr
& style
)
3397 style
= wxRichTextAttr();
3399 wxRichTextAttr clashingAttrPara
, clashingAttrChar
;
3400 wxRichTextAttr absentAttrPara
, absentAttrChar
;
3402 wxRichTextObjectList::compatibility_iterator node
= GetChildren().GetFirst();
3405 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3406 if (para
&& !(para
->GetRange().GetStart() > range
.GetEnd() || para
->GetRange().GetEnd() < range
.GetStart()))
3408 if (para
->GetChildren().GetCount() == 0)
3410 wxRichTextAttr paraStyle
= para
->GetCombinedAttributes(true /* use box attributes */);
3412 CollectStyle(style
, paraStyle
, clashingAttrPara
, absentAttrPara
);
3416 wxRichTextRange
paraRange(para
->GetRange());
3417 paraRange
.LimitTo(range
);
3419 // First collect paragraph attributes only
3420 wxRichTextAttr paraStyle
= para
->GetCombinedAttributes();
3421 paraStyle
.SetFlags(paraStyle
.GetFlags() & wxTEXT_ATTR_PARAGRAPH
);
3422 CollectStyle(style
, paraStyle
, clashingAttrPara
, absentAttrPara
);
3424 wxRichTextObjectList::compatibility_iterator childNode
= para
->GetChildren().GetFirst();
3428 wxRichTextObject
* child
= childNode
->GetData();
3429 if (!(child
->GetRange().GetStart() > range
.GetEnd() || child
->GetRange().GetEnd() < range
.GetStart()))
3431 wxRichTextAttr childStyle
= para
->GetCombinedAttributes(child
->GetAttributes(), true /* include box attributes */);
3433 // Now collect character attributes only
3434 childStyle
.SetFlags(childStyle
.GetFlags() & wxTEXT_ATTR_CHARACTER
);
3436 CollectStyle(style
, childStyle
, clashingAttrChar
, absentAttrChar
);
3439 childNode
= childNode
->GetNext();
3443 node
= node
->GetNext();
3448 /// Set default style
3449 bool wxRichTextParagraphLayoutBox::SetDefaultStyle(const wxRichTextAttr
& style
)
3451 m_defaultAttributes
= style
;
3455 /// Test if this whole range has character attributes of the specified kind. If any
3456 /// of the attributes are different within the range, the test fails. You
3457 /// can use this to implement, for example, bold button updating. style must have
3458 /// flags indicating which attributes are of interest.
3459 bool wxRichTextParagraphLayoutBox::HasCharacterAttributes(const wxRichTextRange
& range
, const wxRichTextAttr
& style
) const
3462 int matchingCount
= 0;
3464 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3467 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3468 // wxASSERT (para != NULL);
3472 // Stop searching if we're beyond the range of interest
3473 if (para
->GetRange().GetStart() > range
.GetEnd())
3474 return foundCount
== matchingCount
&& foundCount
!= 0;
3476 if (!para
->GetRange().IsOutside(range
))
3478 wxRichTextObjectList::compatibility_iterator node2
= para
->GetChildren().GetFirst();
3482 wxRichTextObject
* child
= node2
->GetData();
3483 // Allow for empty string if no buffer
3484 wxRichTextRange childRange
= child
->GetRange();
3485 if (childRange
.GetLength() == 0 && GetRange().GetLength() == 1)
3486 childRange
.SetEnd(childRange
.GetEnd()+1);
3488 if (!childRange
.IsOutside(range
) && wxDynamicCast(child
, wxRichTextPlainText
))
3491 wxRichTextAttr textAttr
= para
->GetCombinedAttributes(child
->GetAttributes());
3493 if (textAttr
.EqPartial(style
, false /* strong test - attributes must be valid in both objects */))
3497 node2
= node2
->GetNext();
3502 node
= node
->GetNext();
3505 return foundCount
== matchingCount
&& foundCount
!= 0;
3508 /// Test if this whole range has paragraph attributes of the specified kind. If any
3509 /// of the attributes are different within the range, the test fails. You
3510 /// can use this to implement, for example, centering button updating. style must have
3511 /// flags indicating which attributes are of interest.
3512 bool wxRichTextParagraphLayoutBox::HasParagraphAttributes(const wxRichTextRange
& range
, const wxRichTextAttr
& style
) const
3515 int matchingCount
= 0;
3517 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3520 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3521 // wxASSERT (para != NULL);
3525 // Stop searching if we're beyond the range of interest
3526 if (para
->GetRange().GetStart() > range
.GetEnd())
3527 return foundCount
== matchingCount
&& foundCount
!= 0;
3529 if (!para
->GetRange().IsOutside(range
))
3531 wxRichTextAttr textAttr
= GetAttributes();
3532 // Apply the paragraph style
3533 wxRichTextApplyStyle(textAttr
, para
->GetAttributes());
3536 if (textAttr
.EqPartial(style
, false /* strong test */))
3541 node
= node
->GetNext();
3543 return foundCount
== matchingCount
&& foundCount
!= 0;
3546 void wxRichTextParagraphLayoutBox::PrepareContent(wxRichTextParagraphLayoutBox
& container
)
3548 wxRichTextBuffer
* buffer
= GetBuffer();
3549 if (buffer
&& buffer
->GetRichTextCtrl())
3550 buffer
->GetRichTextCtrl()->PrepareContent(container
);
3553 /// Set character or paragraph properties
3554 bool wxRichTextParagraphLayoutBox::SetProperties(const wxRichTextRange
& range
, const wxRichTextProperties
& properties
, int flags
)
3556 wxRichTextBuffer
* buffer
= GetBuffer();
3558 bool withUndo
= ((flags
& wxRICHTEXT_SETPROPERTIES_WITH_UNDO
) != 0);
3559 bool parasOnly
= ((flags
& wxRICHTEXT_SETPROPERTIES_PARAGRAPHS_ONLY
) != 0);
3560 bool charactersOnly
= ((flags
& wxRICHTEXT_SETPROPERTIES_CHARACTERS_ONLY
) != 0);
3561 bool resetExistingProperties
= ((flags
& wxRICHTEXT_SETPROPERTIES_RESET
) != 0);
3562 bool removeProperties
= ((flags
& wxRICHTEXT_SETPROPERTIES_REMOVE
) != 0);
3564 // If we are associated with a control, make undoable; otherwise, apply immediately
3567 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3569 wxRichTextAction
* action
= NULL
;
3571 if (haveControl
&& withUndo
)
3573 action
= new wxRichTextAction(NULL
, _("Change Properties"), wxRICHTEXT_CHANGE_PROPERTIES
, buffer
, this, buffer
->GetRichTextCtrl());
3574 action
->SetRange(range
);
3575 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3578 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3581 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3582 // wxASSERT (para != NULL);
3584 if (para
&& para
->GetChildCount() > 0)
3586 // Stop searching if we're beyond the range of interest
3587 if (para
->GetRange().GetStart() > range
.GetEnd())
3590 if (!para
->GetRange().IsOutside(range
))
3592 // We'll be using a copy of the paragraph to make style changes,
3593 // not updating the buffer directly.
3594 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3596 if (haveControl
&& withUndo
)
3598 newPara
= new wxRichTextParagraph(*para
);
3599 action
->GetNewParagraphs().AppendChild(newPara
);
3601 // Also store the old ones for Undo
3602 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3609 if (removeProperties
)
3611 // Removes the given style from the paragraph
3613 newPara
->GetProperties().RemoveProperties(properties
);
3615 else if (resetExistingProperties
)
3616 newPara
->GetProperties() = properties
;
3618 newPara
->GetProperties().MergeProperties(properties
);
3621 // When applying paragraph styles dynamically, don't change the text objects' attributes
3622 // since they will computed as needed. Only apply the character styling if it's _only_
3623 // character styling. This policy is subject to change and might be put under user control.
3625 // Hm. we might well be applying a mix of paragraph and character styles, in which
3626 // case we _do_ want to apply character styles regardless of what para styles are set.
3627 // But if we're applying a paragraph style, which has some character attributes, but
3628 // we only want the paragraphs to hold this character style, then we _don't_ want to
3629 // apply the character style. So we need to be able to choose.
3631 if (!parasOnly
&& charactersOnly
&& range
.GetStart() != newPara
->GetRange().GetEnd())
3633 wxRichTextRange
childRange(range
);
3634 childRange
.LimitTo(newPara
->GetRange());
3636 // Find the starting position and if necessary split it so
3637 // we can start applying different properties.
3638 // TODO: check that the properties actually change or are different
3639 // from properties outside of range
3640 wxRichTextObject
* firstObject
wxDUMMY_INITIALIZE(NULL
);
3641 wxRichTextObject
* lastObject
wxDUMMY_INITIALIZE(NULL
);
3643 if (childRange
.GetStart() == newPara
->GetRange().GetStart())
3644 firstObject
= newPara
->GetChildren().GetFirst()->GetData();
3646 firstObject
= newPara
->SplitAt(range
.GetStart());
3648 // Increment by 1 because we're apply the style one _after_ the split point
3649 long splitPoint
= childRange
.GetEnd();
3650 if (splitPoint
!= newPara
->GetRange().GetEnd())
3654 if (splitPoint
== newPara
->GetRange().GetEnd())
3655 lastObject
= newPara
->GetChildren().GetLast()->GetData();
3657 // lastObject is set as a side-effect of splitting. It's
3658 // returned as the object before the new object.
3659 (void) newPara
->SplitAt(splitPoint
, & lastObject
);
3661 wxASSERT(firstObject
!= NULL
);
3662 wxASSERT(lastObject
!= NULL
);
3664 if (!firstObject
|| !lastObject
)
3667 wxRichTextObjectList::compatibility_iterator firstNode
= newPara
->GetChildren().Find(firstObject
);
3668 wxRichTextObjectList::compatibility_iterator lastNode
= newPara
->GetChildren().Find(lastObject
);
3670 wxASSERT(firstNode
);
3673 wxRichTextObjectList::compatibility_iterator node2
= firstNode
;
3677 wxRichTextObject
* child
= node2
->GetData();
3679 if (removeProperties
)
3681 // Removes the given properties from the paragraph
3682 child
->GetProperties().RemoveProperties(properties
);
3684 else if (resetExistingProperties
)
3685 child
->GetProperties() = properties
;
3688 child
->GetProperties().MergeProperties(properties
);
3691 if (node2
== lastNode
)
3694 node2
= node2
->GetNext();
3700 node
= node
->GetNext();
3703 // Do action, or delay it until end of batch.
3704 if (haveControl
&& withUndo
)
3705 buffer
->SubmitAction(action
);
3710 void wxRichTextParagraphLayoutBox::Reset()
3714 wxRichTextBuffer
* buffer
= GetBuffer();
3715 if (buffer
&& buffer
->GetRichTextCtrl())
3717 wxRichTextEvent
event(wxEVT_COMMAND_RICHTEXT_BUFFER_RESET
, buffer
->GetRichTextCtrl()->GetId());
3718 event
.SetEventObject(buffer
->GetRichTextCtrl());
3719 event
.SetContainer(this);
3721 buffer
->SendEvent(event
, true);
3724 AddParagraph(wxEmptyString
);
3726 PrepareContent(*this);
3728 InvalidateHierarchy(wxRICHTEXT_ALL
);
3731 /// Invalidate the buffer. With no argument, invalidates whole buffer.
3732 void wxRichTextParagraphLayoutBox::Invalidate(const wxRichTextRange
& invalidRange
)
3734 wxRichTextCompositeObject::Invalidate(invalidRange
);
3736 DoInvalidate(invalidRange
);
3739 // Do the (in)validation for this object only
3740 void wxRichTextParagraphLayoutBox::DoInvalidate(const wxRichTextRange
& invalidRange
)
3742 if (invalidRange
== wxRICHTEXT_ALL
)
3744 m_invalidRange
= wxRICHTEXT_ALL
;
3746 // Already invalidating everything
3747 else if (m_invalidRange
== wxRICHTEXT_ALL
)
3752 if ((invalidRange
.GetStart() < m_invalidRange
.GetStart()) || m_invalidRange
.GetStart() == -1)
3753 m_invalidRange
.SetStart(invalidRange
.GetStart());
3754 if (invalidRange
.GetEnd() > m_invalidRange
.GetEnd())
3755 m_invalidRange
.SetEnd(invalidRange
.GetEnd());
3759 // Do the (in)validation both up and down the hierarchy
3760 void wxRichTextParagraphLayoutBox::InvalidateHierarchy(const wxRichTextRange
& invalidRange
)
3762 Invalidate(invalidRange
);
3764 if (invalidRange
!= wxRICHTEXT_NONE
)
3766 // Now go up the hierarchy
3767 wxRichTextObject
* thisObj
= this;
3768 wxRichTextObject
* p
= GetParent();
3771 wxRichTextParagraphLayoutBox
* l
= wxDynamicCast(p
, wxRichTextParagraphLayoutBox
);
3773 l
->DoInvalidate(thisObj
->GetRange());
3781 /// Get invalid range, rounding to entire paragraphs if argument is true.
3782 wxRichTextRange
wxRichTextParagraphLayoutBox::GetInvalidRange(bool wholeParagraphs
) const
3784 if (m_invalidRange
== wxRICHTEXT_ALL
|| m_invalidRange
== wxRICHTEXT_NONE
)
3785 return m_invalidRange
;
3787 wxRichTextRange range
= m_invalidRange
;
3789 if (wholeParagraphs
)
3791 wxRichTextParagraph
* para1
= GetParagraphAtPosition(range
.GetStart());
3793 range
.SetStart(para1
->GetRange().GetStart());
3794 // floating layout make all child should be relayout
3795 range
.SetEnd(GetOwnRange().GetEnd());
3800 /// Apply the style sheet to the buffer, for example if the styles have changed.
3801 bool wxRichTextParagraphLayoutBox::ApplyStyleSheet(wxRichTextStyleSheet
* styleSheet
)
3803 wxASSERT(styleSheet
!= NULL
);
3809 wxRichTextAttr
attr(GetBasicStyle());
3810 if (GetBasicStyle().HasParagraphStyleName())
3812 wxRichTextParagraphStyleDefinition
* paraDef
= styleSheet
->FindParagraphStyle(GetBasicStyle().GetParagraphStyleName());
3815 attr
.Apply(paraDef
->GetStyleMergedWithBase(styleSheet
));
3816 SetBasicStyle(attr
);
3821 if (GetBasicStyle().HasCharacterStyleName())
3823 wxRichTextCharacterStyleDefinition
* charDef
= styleSheet
->FindCharacterStyle(GetBasicStyle().GetCharacterStyleName());
3826 attr
.Apply(charDef
->GetStyleMergedWithBase(styleSheet
));
3827 SetBasicStyle(attr
);
3832 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3835 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3836 // wxASSERT (para != NULL);
3840 // Combine paragraph and list styles. If there is a list style in the original attributes,
3841 // the current indentation overrides anything else and is used to find the item indentation.
3842 // Also, for applying paragraph styles, consider having 2 modes: (1) we merge with what we have,
3843 // thereby taking into account all user changes, (2) reset the style completely (except for indentation/list
3844 // exception as above).
3845 // Problem: when changing from one list style to another, there's a danger that the level info will get lost.
3846 // So when changing a list style interactively, could retrieve level based on current style, then
3847 // set appropriate indent and apply new style.
3851 if (para
->GetAttributes().HasOutlineLevel())
3852 outline
= para
->GetAttributes().GetOutlineLevel();
3853 if (para
->GetAttributes().HasBulletNumber())
3854 num
= para
->GetAttributes().GetBulletNumber();
3856 if (!para
->GetAttributes().GetParagraphStyleName().IsEmpty() && !para
->GetAttributes().GetListStyleName().IsEmpty())
3858 int currentIndent
= para
->GetAttributes().GetLeftIndent();
3860 wxRichTextParagraphStyleDefinition
* paraDef
= styleSheet
->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
3861 wxRichTextListStyleDefinition
* listDef
= styleSheet
->FindListStyle(para
->GetAttributes().GetListStyleName());
3862 if (paraDef
&& !listDef
)
3864 para
->GetAttributes() = paraDef
->GetStyleMergedWithBase(styleSheet
);
3867 else if (listDef
&& !paraDef
)
3869 // Set overall style defined for the list style definition
3870 para
->GetAttributes() = listDef
->GetStyleMergedWithBase(styleSheet
);
3872 // Apply the style for this level
3873 wxRichTextApplyStyle(para
->GetAttributes(), * listDef
->GetLevelAttributes(listDef
->FindLevelForIndent(currentIndent
)));
3876 else if (listDef
&& paraDef
)
3878 // Combines overall list style, style for level, and paragraph style
3879 para
->GetAttributes() = listDef
->CombineWithParagraphStyle(currentIndent
, paraDef
->GetStyleMergedWithBase(styleSheet
));
3883 else if (para
->GetAttributes().GetParagraphStyleName().IsEmpty() && !para
->GetAttributes().GetListStyleName().IsEmpty())
3885 int currentIndent
= para
->GetAttributes().GetLeftIndent();
3887 wxRichTextListStyleDefinition
* listDef
= styleSheet
->FindListStyle(para
->GetAttributes().GetListStyleName());
3889 // Overall list definition style
3890 para
->GetAttributes() = listDef
->GetStyleMergedWithBase(styleSheet
);
3892 // Style for this level
3893 wxRichTextApplyStyle(para
->GetAttributes(), * listDef
->GetLevelAttributes(listDef
->FindLevelForIndent(currentIndent
)));
3897 else if (!para
->GetAttributes().GetParagraphStyleName().IsEmpty() && para
->GetAttributes().GetListStyleName().IsEmpty())
3899 wxRichTextParagraphStyleDefinition
* def
= styleSheet
->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
3902 para
->GetAttributes() = def
->GetStyleMergedWithBase(styleSheet
);
3908 para
->GetAttributes().SetOutlineLevel(outline
);
3910 para
->GetAttributes().SetBulletNumber(num
);
3913 node
= node
->GetNext();
3915 return foundCount
!= 0;
3919 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
3921 wxRichTextBuffer
* buffer
= GetBuffer();
3922 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
3924 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
3925 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
3926 bool specifyLevel
= ((flags
& wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL
) != 0);
3927 bool renumber
= ((flags
& wxRICHTEXT_SETSTYLE_RENUMBER
) != 0);
3929 // Current number, if numbering
3932 wxASSERT (!specifyLevel
|| (specifyLevel
&& (specifiedLevel
>= 0)));
3934 // If we are associated with a control, make undoable; otherwise, apply immediately
3937 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3939 wxRichTextAction
* action
= NULL
;
3941 if (haveControl
&& withUndo
)
3943 action
= new wxRichTextAction(NULL
, _("Change List Style"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
3944 action
->SetRange(range
);
3945 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3948 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3951 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3952 // wxASSERT (para != NULL);
3954 if (para
&& para
->GetChildCount() > 0)
3956 // Stop searching if we're beyond the range of interest
3957 if (para
->GetRange().GetStart() > range
.GetEnd())
3960 if (!para
->GetRange().IsOutside(range
))
3962 // We'll be using a copy of the paragraph to make style changes,
3963 // not updating the buffer directly.
3964 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3966 if (haveControl
&& withUndo
)
3968 newPara
= new wxRichTextParagraph(*para
);
3969 action
->GetNewParagraphs().AppendChild(newPara
);
3971 // Also store the old ones for Undo
3972 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3979 int thisIndent
= newPara
->GetAttributes().GetLeftIndent();
3980 int thisLevel
= specifyLevel
? specifiedLevel
: def
->FindLevelForIndent(thisIndent
);
3982 // How is numbering going to work?
3983 // If we are renumbering, or numbering for the first time, we need to keep
3984 // track of the number for each level. But we might be simply applying a different
3986 // In Word, applying a style to several paragraphs, even if at different levels,
3987 // reverts the level back to the same one. So we could do the same here.
3988 // Renumbering will need to be done when we promote/demote a paragraph.
3990 // Apply the overall list style, and item style for this level
3991 wxRichTextAttr
listStyle(def
->GetCombinedStyleForLevel(thisLevel
, styleSheet
));
3992 wxRichTextApplyStyle(newPara
->GetAttributes(), listStyle
);
3994 // Now we need to do numbering
3995 // Preserve the existing list item continuation bullet style, if any
3996 if (para
->GetAttributes().HasBulletStyle() && (para
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
))
3997 newPara
->GetAttributes().SetBulletStyle(newPara
->GetAttributes().GetBulletStyle()|wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
);
4002 newPara
->GetAttributes().SetBulletNumber(n
);
4008 else if (!newPara
->GetAttributes().GetListStyleName().IsEmpty())
4010 // if def is NULL, remove list style, applying any associated paragraph style
4011 // to restore the attributes
4013 newPara
->GetAttributes().SetListStyleName(wxEmptyString
);
4014 newPara
->GetAttributes().SetLeftIndent(0, 0);
4015 newPara
->GetAttributes().SetBulletText(wxEmptyString
);
4016 newPara
->GetAttributes().SetBulletStyle(0);
4018 // Eliminate the main list-related attributes
4019 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
);
4021 if (styleSheet
&& !newPara
->GetAttributes().GetParagraphStyleName().IsEmpty())
4023 wxRichTextParagraphStyleDefinition
* def
= styleSheet
->FindParagraphStyle(newPara
->GetAttributes().GetParagraphStyleName());
4026 newPara
->GetAttributes() = def
->GetStyleMergedWithBase(styleSheet
);
4033 node
= node
->GetNext();
4036 // Do action, or delay it until end of batch.
4037 if (haveControl
&& withUndo
)
4038 buffer
->SubmitAction(action
);
4043 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
)
4045 wxRichTextBuffer
* buffer
= GetBuffer();
4046 if (buffer
&& buffer
->GetStyleSheet())
4048 wxRichTextListStyleDefinition
* def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4050 return SetListStyle(range
, def
, flags
, startFrom
, specifiedLevel
);
4055 /// Clear list for given range
4056 bool wxRichTextParagraphLayoutBox::ClearListStyle(const wxRichTextRange
& range
, int flags
)
4058 return SetListStyle(range
, NULL
, flags
);
4061 /// Number/renumber any list elements in the given range
4062 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
4064 return DoNumberList(range
, range
, 0, def
, flags
, startFrom
, specifiedLevel
);
4067 /// Number/renumber any list elements in the given range. Also do promotion or demotion of items, if specified
4068 bool wxRichTextParagraphLayoutBox::DoNumberList(const wxRichTextRange
& range
, const wxRichTextRange
& promotionRange
, int promoteBy
,
4069 wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
4071 wxRichTextBuffer
* buffer
= GetBuffer();
4072 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
4074 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
4075 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
4077 bool specifyLevel
= ((flags
& wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL
) != 0);
4080 bool renumber
= ((flags
& wxRICHTEXT_SETSTYLE_RENUMBER
) != 0);
4082 // Max number of levels
4083 const int maxLevels
= 10;
4085 // The level we're looking at now
4086 int currentLevel
= -1;
4088 // The item number for each level
4089 int levels
[maxLevels
];
4092 // Reset all numbering
4093 for (i
= 0; i
< maxLevels
; i
++)
4095 if (startFrom
!= -1)
4096 levels
[i
] = startFrom
-1;
4097 else if (renumber
) // start again
4100 levels
[i
] = -1; // start from the number we found, if any
4104 wxASSERT(!specifyLevel
|| (specifyLevel
&& (specifiedLevel
>= 0)));
4107 // If we are associated with a control, make undoable; otherwise, apply immediately
4110 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
4112 wxRichTextAction
* action
= NULL
;
4114 if (haveControl
&& withUndo
)
4116 action
= new wxRichTextAction(NULL
, _("Renumber List"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
4117 action
->SetRange(range
);
4118 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
4121 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
4124 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
4125 // wxASSERT (para != NULL);
4127 if (para
&& para
->GetChildCount() > 0)
4129 // Stop searching if we're beyond the range of interest
4130 if (para
->GetRange().GetStart() > range
.GetEnd())
4133 if (!para
->GetRange().IsOutside(range
))
4135 // We'll be using a copy of the paragraph to make style changes,
4136 // not updating the buffer directly.
4137 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
4139 if (haveControl
&& withUndo
)
4141 newPara
= new wxRichTextParagraph(*para
);
4142 action
->GetNewParagraphs().AppendChild(newPara
);
4144 // Also store the old ones for Undo
4145 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
4150 wxRichTextListStyleDefinition
* defToUse
= def
;
4153 if (styleSheet
&& !newPara
->GetAttributes().GetListStyleName().IsEmpty())
4154 defToUse
= styleSheet
->FindListStyle(newPara
->GetAttributes().GetListStyleName());
4159 int thisIndent
= newPara
->GetAttributes().GetLeftIndent();
4160 int thisLevel
= defToUse
->FindLevelForIndent(thisIndent
);
4162 // If we've specified a level to apply to all, change the level.
4163 if (specifiedLevel
!= -1)
4164 thisLevel
= specifiedLevel
;
4166 // Do promotion if specified
4167 if ((promoteBy
!= 0) && !para
->GetRange().IsOutside(promotionRange
))
4169 thisLevel
= thisLevel
- promoteBy
;
4176 // Apply the overall list style, and item style for this level
4177 wxRichTextAttr
listStyle(defToUse
->GetCombinedStyleForLevel(thisLevel
, styleSheet
));
4178 wxRichTextApplyStyle(newPara
->GetAttributes(), listStyle
);
4180 // Preserve the existing list item continuation bullet style, if any
4181 if (para
->GetAttributes().HasBulletStyle() && (para
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
))
4182 newPara
->GetAttributes().SetBulletStyle(newPara
->GetAttributes().GetBulletStyle()|wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
);
4184 // OK, we've (re)applied the style, now let's get the numbering right.
4186 if (currentLevel
== -1)
4187 currentLevel
= thisLevel
;
4189 // Same level as before, do nothing except increment level's number afterwards
4190 if (currentLevel
== thisLevel
)
4193 // A deeper level: start renumbering all levels after current level
4194 else if (thisLevel
> currentLevel
)
4196 for (i
= currentLevel
+1; i
<= thisLevel
; i
++)
4200 currentLevel
= thisLevel
;
4202 else if (thisLevel
< currentLevel
)
4204 currentLevel
= thisLevel
;
4207 // Use the current numbering if -1 and we have a bullet number already
4208 if (levels
[currentLevel
] == -1)
4210 if (newPara
->GetAttributes().HasBulletNumber())
4211 levels
[currentLevel
] = newPara
->GetAttributes().GetBulletNumber();
4213 levels
[currentLevel
] = 1;
4217 if (!(para
->GetAttributes().HasBulletStyle() && (para
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
)))
4218 levels
[currentLevel
] ++;
4221 newPara
->GetAttributes().SetBulletNumber(levels
[currentLevel
]);
4223 // Create the bullet text if an outline list
4224 if (listStyle
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
)
4227 for (i
= 0; i
<= currentLevel
; i
++)
4229 if (!text
.IsEmpty())
4231 text
+= wxString::Format(wxT("%d"), levels
[i
]);
4233 newPara
->GetAttributes().SetBulletText(text
);
4239 node
= node
->GetNext();
4242 // Do action, or delay it until end of batch.
4243 if (haveControl
&& withUndo
)
4244 buffer
->SubmitAction(action
);
4249 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
)
4251 wxRichTextBuffer
* buffer
= GetBuffer();
4252 if (buffer
->GetStyleSheet())
4254 wxRichTextListStyleDefinition
* def
= NULL
;
4255 if (!defName
.IsEmpty())
4256 def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4257 return NumberList(range
, def
, flags
, startFrom
, specifiedLevel
);
4262 /// Promote the list items within the given range. promoteBy can be a positive or negative number, e.g. 1 or -1
4263 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy
, const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int specifiedLevel
)
4266 // One strategy is to first work out the range within which renumbering must occur. Then could pass these two ranges
4267 // to NumberList with a flag indicating promotion is required within one of the ranges.
4268 // Find first and last paragraphs in range. Then for first, calculate new indentation and look back until we find
4269 // a paragraph that either has no list style, or has one that is different or whose indentation is less.
4270 // We start renumbering from the para after that different para we found. We specify that the numbering of that
4271 // list position will start from 1.
4272 // Similarly, we look after the last para in the promote range for an indentation that is less (or no list style).
4273 // We can end the renumbering at this point.
4275 // For now, only renumber within the promotion range.
4277 return DoNumberList(range
, range
, promoteBy
, def
, flags
, 1, specifiedLevel
);
4280 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy
, const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int specifiedLevel
)
4282 wxRichTextBuffer
* buffer
= GetBuffer();
4283 if (buffer
->GetStyleSheet())
4285 wxRichTextListStyleDefinition
* def
= NULL
;
4286 if (!defName
.IsEmpty())
4287 def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4288 return PromoteList(promoteBy
, range
, def
, flags
, specifiedLevel
);
4293 /// Fills in the attributes for numbering a paragraph after previousParagraph. It also finds the
4294 /// position of the paragraph that it had to start looking from.
4295 bool wxRichTextParagraphLayoutBox::FindNextParagraphNumber(wxRichTextParagraph
* previousParagraph
, wxRichTextAttr
& attr
) const
4297 // TODO: add GetNextChild/GetPreviousChild to composite
4298 // Search for a paragraph that isn't a continuation paragraph (no bullet)
4299 while (previousParagraph
&& previousParagraph
->GetAttributes().HasBulletStyle() && previousParagraph
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
)
4301 wxRichTextObjectList::compatibility_iterator node
= ((wxRichTextCompositeObject
*) previousParagraph
->GetParent())->GetChildren().Find(previousParagraph
);
4304 node
= node
->GetPrevious();
4306 previousParagraph
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
4308 previousParagraph
= NULL
;
4311 previousParagraph
= NULL
;
4314 if (!previousParagraph
|| !previousParagraph
->GetAttributes().HasFlag(wxTEXT_ATTR_BULLET_STYLE
) || previousParagraph
->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
)
4317 wxRichTextBuffer
* buffer
= GetBuffer();
4318 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
4319 if (styleSheet
&& !previousParagraph
->GetAttributes().GetListStyleName().IsEmpty())
4321 wxRichTextListStyleDefinition
* def
= styleSheet
->FindListStyle(previousParagraph
->GetAttributes().GetListStyleName());
4324 // int thisIndent = previousParagraph->GetAttributes().GetLeftIndent();
4325 // int thisLevel = def->FindLevelForIndent(thisIndent);
4327 bool isOutline
= (previousParagraph
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
) != 0;
4329 attr
.SetFlags(previousParagraph
->GetAttributes().GetFlags() & (wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_BULLET_NUMBER
|wxTEXT_ATTR_BULLET_TEXT
|wxTEXT_ATTR_BULLET_NAME
));
4330 if (previousParagraph
->GetAttributes().HasBulletName())
4331 attr
.SetBulletName(previousParagraph
->GetAttributes().GetBulletName());
4332 attr
.SetBulletStyle(previousParagraph
->GetAttributes().GetBulletStyle());
4333 attr
.SetListStyleName(previousParagraph
->GetAttributes().GetListStyleName());
4335 int nextNumber
= previousParagraph
->GetAttributes().GetBulletNumber() + 1;
4336 attr
.SetBulletNumber(nextNumber
);
4340 wxString text
= previousParagraph
->GetAttributes().GetBulletText();
4341 if (!text
.IsEmpty())
4343 int pos
= text
.Find(wxT('.'), true);
4344 if (pos
!= wxNOT_FOUND
)
4346 text
= text
.Mid(0, text
.Length() - pos
- 1);
4349 text
= wxEmptyString
;
4350 if (!text
.IsEmpty())
4352 text
+= wxString::Format(wxT("%d"), nextNumber
);
4353 attr
.SetBulletText(text
);
4367 * wxRichTextParagraph
4368 * This object represents a single paragraph (or in a straight text editor, a line).
4371 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraph
, wxRichTextCompositeObject
)
4373 wxArrayInt
wxRichTextParagraph::sm_defaultTabs
;
4375 wxRichTextParagraph::wxRichTextParagraph(wxRichTextObject
* parent
, wxRichTextAttr
* style
):
4376 wxRichTextCompositeObject(parent
)
4379 SetAttributes(*style
);
4382 wxRichTextParagraph::wxRichTextParagraph(const wxString
& text
, wxRichTextObject
* parent
, wxRichTextAttr
* paraStyle
, wxRichTextAttr
* charStyle
):
4383 wxRichTextCompositeObject(parent
)
4386 SetAttributes(*paraStyle
);
4388 AppendChild(new wxRichTextPlainText(text
, this, charStyle
));
4391 wxRichTextParagraph::~wxRichTextParagraph()
4397 bool wxRichTextParagraph::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int WXUNUSED(descent
), int style
)
4402 // Currently we don't merge these attributes with the parent, but we
4403 // should consider whether we should (e.g. if we set a border colour
4404 // for all paragraphs). But generally box attributes are likely to be
4405 // different for different objects.
4406 wxRect paraRect
= GetRect();
4407 wxRichTextAttr attr
= GetCombinedAttributes();
4408 context
.ApplyVirtualAttributes(attr
, this);
4410 DrawBoxAttributes(dc
, GetBuffer(), attr
, paraRect
);
4412 // Draw the bullet, if any
4413 if ((attr
.GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
) == 0 && (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
) == 0)
4415 if (attr
.GetLeftSubIndent() != 0)
4417 int spaceBeforePara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingBefore());
4418 int leftIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftIndent());
4420 wxRichTextAttr
bulletAttr(attr
);
4422 // Combine with the font of the first piece of content, if one is specified
4423 if (GetChildren().GetCount() > 0)
4425 wxRichTextObject
* firstObj
= (wxRichTextObject
*) GetChildren().GetFirst()->GetData();
4426 if (!firstObj
->IsFloatable() && firstObj
->GetAttributes().HasFont())
4428 wxRichTextApplyStyle(bulletAttr
, firstObj
->GetAttributes());
4432 // Get line height from first line, if any
4433 wxRichTextLine
* line
= m_cachedLines
.GetFirst() ? (wxRichTextLine
* ) m_cachedLines
.GetFirst()->GetData() : NULL
;
4436 int lineHeight
wxDUMMY_INITIALIZE(0);
4439 lineHeight
= line
->GetSize().y
;
4440 linePos
= line
->GetPosition() + GetPosition();
4445 if (bulletAttr
.HasFont() && GetBuffer())
4446 font
= GetBuffer()->GetFontTable().FindFont(bulletAttr
);
4448 font
= (*wxNORMAL_FONT
);
4450 wxCheckSetFont(dc
, font
);
4452 lineHeight
= dc
.GetCharHeight();
4453 linePos
= GetPosition();
4454 linePos
.y
+= spaceBeforePara
;
4457 wxRect
bulletRect(GetPosition().x
+ leftIndent
, linePos
.y
, linePos
.x
- (GetPosition().x
+ leftIndent
), lineHeight
);
4459 if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
)
4461 if (wxRichTextBuffer::GetRenderer())
4462 wxRichTextBuffer::GetRenderer()->DrawBitmapBullet(this, dc
, bulletAttr
, bulletRect
);
4464 else if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_STANDARD
)
4466 if (wxRichTextBuffer::GetRenderer())
4467 wxRichTextBuffer::GetRenderer()->DrawStandardBullet(this, dc
, bulletAttr
, bulletRect
);
4471 wxString bulletText
= GetBulletText();
4473 if (!bulletText
.empty() && wxRichTextBuffer::GetRenderer())
4474 wxRichTextBuffer::GetRenderer()->DrawTextBullet(this, dc
, bulletAttr
, bulletRect
, bulletText
);
4479 // Draw the range for each line, one object at a time.
4481 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
4484 wxRichTextLine
* line
= node
->GetData();
4485 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
4487 // Lines are specified relative to the paragraph
4489 wxPoint linePosition
= line
->GetPosition() + GetPosition();
4491 // Don't draw if off the screen
4492 if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) != 0) || ((linePosition
.y
+ line
->GetSize().y
) >= rect
.y
&& linePosition
.y
<= rect
.y
+ rect
.height
))
4494 wxPoint objectPosition
= linePosition
;
4495 int maxDescent
= line
->GetDescent();
4497 // Loop through objects until we get to the one within range
4498 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
4503 wxRichTextObject
* child
= node2
->GetData();
4505 if (!child
->IsFloating() && child
->GetRange().GetLength() > 0 && !child
->GetRange().IsOutside(lineRange
) && !lineRange
.IsOutside(range
))
4507 // Draw this part of the line at the correct position
4508 wxRichTextRange
objectRange(child
->GetRange());
4509 objectRange
.LimitTo(lineRange
);
4512 if (child
->IsTopLevel())
4514 objectSize
= child
->GetCachedSize();
4515 objectRange
= child
->GetOwnRange();
4519 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING && wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4520 if (i
< (int) line
->GetObjectSizes().GetCount())
4522 objectSize
.x
= line
->GetObjectSizes()[(size_t) i
];
4528 child
->GetRangeSize(objectRange
, objectSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, objectPosition
);
4532 // Use the child object's width, but the whole line's height
4533 wxRect
childRect(objectPosition
, wxSize(objectSize
.x
, line
->GetSize().y
));
4534 child
->Draw(dc
, context
, objectRange
, selection
, childRect
, maxDescent
, style
);
4536 objectPosition
.x
+= objectSize
.x
;
4539 else if (child
->GetRange().GetStart() > lineRange
.GetEnd())
4540 // Can break out of inner loop now since we've passed this line's range
4543 node2
= node2
->GetNext();
4547 node
= node
->GetNext();
4553 // Get the range width using partial extents calculated for the whole paragraph.
4554 static int wxRichTextGetRangeWidth(const wxRichTextParagraph
& para
, const wxRichTextRange
& range
, const wxArrayInt
& partialExtents
)
4556 wxASSERT(partialExtents
.GetCount() >= (size_t) range
.GetLength());
4558 if (partialExtents
.GetCount() < (size_t) range
.GetLength())
4561 int leftMostPos
= 0;
4562 if (range
.GetStart() - para
.GetRange().GetStart() > 0)
4563 leftMostPos
= partialExtents
[range
.GetStart() - para
.GetRange().GetStart() - 1];
4565 int rightMostPos
= partialExtents
[range
.GetEnd() - para
.GetRange().GetStart()];
4567 int w
= rightMostPos
- leftMostPos
;
4572 /// Lay the item out
4573 bool wxRichTextParagraph::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
)
4575 // Deal with floating objects firstly before the normal layout
4576 wxRichTextBuffer
* buffer
= GetBuffer();
4578 wxRichTextFloatCollector
* collector
= GetContainer()->GetFloatCollector();
4579 wxASSERT(collector
);
4580 LayoutFloat(dc
, context
, rect
, parentRect
, style
, collector
);
4582 wxRichTextAttr attr
= GetCombinedAttributes();
4583 context
.ApplyVirtualAttributes(attr
, this);
4587 // Increase the size of the paragraph due to spacing
4588 int spaceBeforePara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingBefore());
4589 int spaceAfterPara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingAfter());
4590 int leftIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftIndent());
4591 int leftSubIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftSubIndent());
4592 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
4594 int lineSpacing
= 0;
4596 // Let's assume line spacing of 10 is normal, 15 is 1.5, 20 is 2, etc.
4597 if (attr
.HasLineSpacing() && attr
.GetLineSpacing() > 0 && attr
.GetFont().IsOk())
4599 wxCheckSetFont(dc
, attr
.GetFont());
4600 lineSpacing
= (int) (double(dc
.GetCharHeight()) * (double(attr
.GetLineSpacing())/10.0 - 1.0));
4603 // Start position for each line relative to the paragraph
4604 int startPositionFirstLine
= leftIndent
;
4605 int startPositionSubsequentLines
= leftIndent
+ leftSubIndent
;
4607 // If we have a bullet in this paragraph, the start position for the first line's text
4608 // is actually leftIndent + leftSubIndent.
4609 if (attr
.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE
)
4610 startPositionFirstLine
= startPositionSubsequentLines
;
4612 long lastEndPos
= GetRange().GetStart()-1;
4613 long lastCompletedEndPos
= lastEndPos
;
4615 int currentWidth
= 0;
4616 SetPosition(rect
.GetPosition());
4618 wxPoint
currentPosition(0, spaceBeforePara
); // We will calculate lines relative to paragraph
4621 int maxHeight
= currentPosition
.y
;
4626 int lineDescent
= 0;
4628 wxRichTextObjectList::compatibility_iterator node
;
4630 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4632 wxArrayInt partialExtents
;
4635 int paraDescent
= 0;
4637 // This calculates the partial text extents
4638 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, rect
.GetPosition(), & partialExtents
);
4640 node
= m_children
.GetFirst();
4643 wxRichTextObject
* child
= node
->GetData();
4645 //child->SetCachedSize(wxDefaultSize);
4646 child
->Layout(dc
, context
, rect
, style
);
4648 node
= node
->GetNext();
4654 // We may need to go back to a previous child, in which case create the new line,
4655 // find the child corresponding to the start position of the string, and
4658 wxRect availableRect
;
4660 node
= m_children
.GetFirst();
4663 wxRichTextObject
* child
= node
->GetData();
4665 // If floating, ignore. We already laid out floats.
4666 // Also ignore if empty object, except if we haven't got any
4668 if (child
->IsFloating() || !child
->IsShown() ||
4669 (child
->GetRange().GetLength() == 0 && maxHeight
> spaceBeforePara
)
4672 node
= node
->GetNext();
4676 // If this is e.g. a composite text box, it will need to be laid out itself.
4677 // But if just a text fragment or image, for example, this will
4678 // do nothing. NB: won't we need to set the position after layout?
4679 // since for example if position is dependent on vertical line size, we
4680 // can't tell the position until the size is determined. So possibly introduce
4681 // another layout phase.
4683 // We may only be looking at part of a child, if we searched back for wrapping
4684 // and found a suitable point some way into the child. So get the size for the fragment
4687 long nextBreakPos
= GetFirstLineBreakPosition(lastEndPos
+1);
4688 long lastPosToUse
= child
->GetRange().GetEnd();
4689 bool lineBreakInThisObject
= (nextBreakPos
> -1 && nextBreakPos
<= child
->GetRange().GetEnd());
4691 if (lineBreakInThisObject
)
4692 lastPosToUse
= nextBreakPos
;
4695 int childDescent
= 0;
4697 int startOffset
= (lineCount
== 0 ? startPositionFirstLine
: startPositionSubsequentLines
);
4698 availableRect
= wxRect(rect
.x
+ startOffset
, rect
.y
+ currentPosition
.y
,
4699 rect
.width
- startOffset
- rightIndent
, rect
.height
);
4701 if (child
->IsTopLevel())
4703 wxSize oldSize
= child
->GetCachedSize();
4705 child
->Invalidate(wxRICHTEXT_ALL
);
4706 child
->SetPosition(wxPoint(0, 0));
4708 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4709 // lays out the object again using the minimum size
4710 // The position will be determined by its location in its line,
4711 // and not by the child's actual position.
4712 child
->LayoutToBestSize(dc
, context
, buffer
,
4713 attr
, child
->GetAttributes(), availableRect
, parentRect
, style
);
4715 if (oldSize
!= child
->GetCachedSize())
4717 partialExtents
.Clear();
4719 // Recalculate the partial text extents since the child object changed size
4720 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), & partialExtents
);
4724 // Problem: we need to layout composites here for which we need the available width,
4725 // but we can't get the available width without using the float collector which
4726 // needs to know the object height.
4728 if ((nextBreakPos
== -1) && (lastEndPos
== child
->GetRange().GetStart() - 1)) // i.e. we want to get the whole thing
4730 childSize
= child
->GetCachedSize();
4731 childDescent
= child
->GetDescent();
4735 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4736 // Get height only, then the width using the partial extents
4737 GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
);
4738 childSize
.x
= wxRichTextGetRangeWidth(*this, wxRichTextRange(lastEndPos
+1, lastPosToUse
), partialExtents
);
4740 GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, rect
.GetPosition());
4745 int loopIterations
= 0;
4747 // If there are nested objects that need to lay themselves out, we have to do this in a
4748 // loop because the height of the object may well depend on the available width.
4749 // And because of floating object positioning, the available width depends on the
4750 // height of the object and whether it will clash with the floating objects.
4751 // So, we see whether the available width changes due to the presence of floating images.
4752 // If it does, then we'll use the new restricted width to find the object height again.
4753 // If this causes another restriction in the available width, we'll try again, until
4754 // either we lose patience or the available width settles down.
4759 wxRect oldAvailableRect
= availableRect
;
4761 // Available width depends on the floating objects and the line height.
4762 // Note: the floating objects may be placed vertically along the two sides of
4763 // buffer, so we may have different available line widths with different
4764 // [startY, endY]. So, we can't determine how wide the available
4765 // space is until we know the exact line height.
4766 if (childDescent
== 0)
4768 lineHeight
= wxMax(lineHeight
, childSize
.y
);
4769 lineDescent
= maxDescent
;
4770 lineAscent
= maxAscent
;
4774 lineDescent
= wxMax(childDescent
, maxDescent
);
4775 lineAscent
= wxMax(childSize
.y
-childDescent
, maxAscent
);
4777 lineHeight
= wxMax(lineHeight
, (lineDescent
+ lineAscent
));
4778 wxRect floatAvailableRect
= collector
->GetAvailableRect(rect
.y
+ currentPosition
.y
, rect
.y
+ currentPosition
.y
+ lineHeight
);
4780 // Adjust availableRect to the space that is available when taking floating objects into account.
4782 if (floatAvailableRect
.x
+ startOffset
> availableRect
.x
)
4784 int newX
= floatAvailableRect
.x
+ startOffset
;
4785 int newW
= availableRect
.width
- (newX
- availableRect
.x
);
4786 availableRect
.x
= newX
;
4787 availableRect
.width
= newW
;
4790 if (floatAvailableRect
.width
< availableRect
.width
)
4791 availableRect
.width
= floatAvailableRect
.width
;
4793 currentPosition
.x
= availableRect
.x
- rect
.x
;
4795 if (child
->IsTopLevel() && loopIterations
<= 20)
4797 if (availableRect
!= oldAvailableRect
)
4799 wxSize oldSize
= child
->GetCachedSize();
4801 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4802 // lays out the object again using the minimum size
4803 child
->Invalidate(wxRICHTEXT_ALL
);
4804 child
->LayoutToBestSize(dc
, context
, buffer
,
4805 attr
, child
->GetAttributes(), availableRect
, parentRect
, style
);
4806 childSize
= child
->GetCachedSize();
4807 childDescent
= child
->GetDescent();
4809 if (oldSize
!= child
->GetCachedSize())
4811 partialExtents
.Clear();
4813 // Recalculate the partial text extents since the child object changed size
4814 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), & partialExtents
);
4817 // Go around the loop finding the available rect for the given floating objects
4827 if (child
->IsTopLevel())
4829 // We can move it to the correct position at this point
4830 // TODO: probably need to add margin
4831 child
->Move(GetPosition() + wxPoint(currentWidth
+ (wxMax(leftIndent
, leftIndent
+ leftSubIndent
)), currentPosition
.y
));
4835 // 1) There was a line break BEFORE the natural break
4836 // 2) There was a line break AFTER the natural break
4837 // 3) It's the last line
4838 // 4) The child still fits (carry on) - 'else' clause
4840 if ((lineBreakInThisObject
&& (childSize
.x
+ currentWidth
<= availableRect
.width
))
4842 (childSize
.x
+ currentWidth
> availableRect
.width
)
4844 ((childSize
.x
+ currentWidth
<= availableRect
.width
) && !node
->GetNext())
4848 long wrapPosition
= 0;
4849 if ((childSize
.x
+ currentWidth
<= availableRect
.width
) && !node
->GetNext() && !lineBreakInThisObject
)
4850 wrapPosition
= child
->GetRange().GetEnd();
4853 // Find a place to wrap. This may walk back to previous children,
4854 // for example if a word spans several objects.
4855 // Note: one object must contains only one wxTextAtrr, so the line height will not
4856 // change inside one object. Thus, we can pass the remain line width to the
4857 // FindWrapPosition function.
4858 if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos
+1, child
->GetRange().GetEnd()), dc
, context
, availableRect
.width
, wrapPosition
, & partialExtents
))
4860 // If the function failed, just cut it off at the end of this child.
4861 wrapPosition
= child
->GetRange().GetEnd();
4864 // FindWrapPosition can still return a value that will put us in an endless wrapping loop
4865 if (wrapPosition
<= lastCompletedEndPos
)
4866 wrapPosition
= wxMax(lastCompletedEndPos
+1,child
->GetRange().GetEnd());
4868 // Line end position shouldn't be the same as the end, or greater.
4869 if (wrapPosition
>= GetRange().GetEnd())
4870 wrapPosition
= wxMax(0, GetRange().GetEnd()-1);
4872 // wxLogDebug(wxT("Split at %ld"), wrapPosition);
4874 // Let's find the actual size of the current line now
4876 wxRichTextRange
actualRange(lastCompletedEndPos
+1, wrapPosition
);
4880 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4881 if (!child
->IsEmpty())
4883 // Get height only, then the width using the partial extents
4884 GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
);
4885 actualSize
.x
= wxRichTextGetRangeWidth(*this, actualRange
, partialExtents
);
4889 GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
4891 currentWidth
= actualSize
.x
;
4893 // The descent for the whole line at this point, is the correct max descent
4894 maxDescent
= childDescent
;
4896 maxAscent
= actualSize
.y
-childDescent
;
4898 // lineHeight is given by the height for the whole line, since it will
4899 // take into account ascend/descend.
4900 lineHeight
= actualSize
.y
;
4902 if (lineHeight
== 0 && buffer
)
4904 wxFont
font(buffer
->GetFontTable().FindFont(attr
));
4905 wxCheckSetFont(dc
, font
);
4906 lineHeight
= dc
.GetCharHeight();
4909 if (maxDescent
== 0)
4912 dc
.GetTextExtent(wxT("X"), & w
, &h
, & maxDescent
);
4916 wxRichTextLine
* line
= AllocateLine(lineCount
);
4918 // Set relative range so we won't have to change line ranges when paragraphs are moved
4919 line
->SetRange(wxRichTextRange(actualRange
.GetStart() - GetRange().GetStart(), actualRange
.GetEnd() - GetRange().GetStart()));
4920 line
->SetPosition(currentPosition
);
4921 line
->SetSize(wxSize(currentWidth
, lineHeight
));
4922 line
->SetDescent(maxDescent
);
4924 maxHeight
= currentPosition
.y
+ lineHeight
;
4926 // Now move down a line. TODO: add margins, spacing
4927 currentPosition
.y
+= lineHeight
;
4928 currentPosition
.y
+= lineSpacing
;
4931 maxWidth
= wxMax(maxWidth
, currentWidth
+startOffset
);
4936 // TODO: account for zero-length objects
4937 // wxASSERT(wrapPosition > lastCompletedEndPos);
4939 lastEndPos
= wrapPosition
;
4940 lastCompletedEndPos
= lastEndPos
;
4944 if (wrapPosition
< GetRange().GetEnd()-1)
4946 // May need to set the node back to a previous one, due to searching back in wrapping
4947 wxRichTextObject
* childAfterWrapPosition
= FindObjectAtPosition(wrapPosition
+1);
4948 if (childAfterWrapPosition
)
4949 node
= m_children
.Find(childAfterWrapPosition
);
4951 node
= node
->GetNext();
4954 node
= node
->GetNext();
4956 // Apply paragraph styles such as alignment to the wrapped line
4957 ApplyParagraphStyle(line
, attr
, availableRect
, dc
);
4961 // We still fit, so don't add a line, and keep going
4962 currentWidth
+= childSize
.x
;
4964 if (childDescent
== 0)
4966 // An object with a zero descend value wants to take up the whole
4967 // height regardless of baseline
4968 lineHeight
= wxMax(lineHeight
, childSize
.y
);
4972 maxDescent
= wxMax(childDescent
, maxDescent
);
4973 maxAscent
= wxMax(childSize
.y
-childDescent
, maxAscent
);
4976 lineHeight
= wxMax(lineHeight
, (maxDescent
+ maxAscent
));
4978 maxWidth
= wxMax(maxWidth
, currentWidth
+startOffset
);
4979 lastEndPos
= child
->GetRange().GetEnd();
4981 node
= node
->GetNext();
4985 //wxASSERT(!(lastCompletedEndPos != -1 && lastCompletedEndPos < GetRange().GetEnd()-1));
4987 // Remove remaining unused line objects, if any
4988 ClearUnusedLines(lineCount
);
4990 // We need to add back the margins etc.
4992 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
4993 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxWidth
, currentPosition
.y
+ spaceAfterPara
));
4994 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
4995 SetCachedSize(marginRect
.GetSize());
4998 // The maximum size is the length of the paragraph stretched out into a line.
4999 // So if there were a single word, or an image, or a fixed-size text box, the object could be shrunk around
5000 // this size. TODO: take into account line breaks.
5002 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
5003 contentRect
= wxRect(wxPoint(0, 0), wxSize(paraSize
.x
+ wxMax(leftIndent
, leftIndent
+ leftSubIndent
) + rightIndent
, currentPosition
.y
+ spaceAfterPara
));
5004 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
5005 SetMaxSize(marginRect
.GetSize());
5008 // Find the greatest minimum size. Currently we only look at non-text objects,
5009 // which isn't ideal but it would be slow to find the maximum word width to
5010 // use as the minimum.
5013 node
= m_children
.GetFirst();
5016 wxRichTextObject
* child
= node
->GetData();
5018 // If floating, ignore. We already laid out floats.
5019 // Also ignore if empty object, except if we haven't got any
5021 if (!child
->IsFloating() && child
->GetRange().GetLength() != 0 && !wxDynamicCast(child
, wxRichTextPlainText
))
5023 if (child
->GetCachedSize().x
> minWidth
)
5024 minWidth
= child
->GetMinSize().x
;
5026 node
= node
->GetNext();
5029 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
5030 contentRect
= wxRect(wxPoint(0, 0), wxSize(minWidth
, currentPosition
.y
+ spaceAfterPara
));
5031 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
5032 SetMinSize(marginRect
.GetSize());
5035 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5036 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
5037 // Use the text extents to calculate the size of each fragment in each line
5038 wxRichTextLineList::compatibility_iterator lineNode
= m_cachedLines
.GetFirst();
5041 wxRichTextLine
* line
= lineNode
->GetData();
5042 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5044 // Loop through objects until we get to the one within range
5045 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
5049 wxRichTextObject
* child
= node2
->GetData();
5051 if (child
->GetRange().GetLength() > 0 && !child
->GetRange().IsOutside(lineRange
))
5053 wxRichTextRange rangeToUse
= lineRange
;
5054 rangeToUse
.LimitTo(child
->GetRange());
5056 // Find the size of the child from the text extents, and store in an array
5057 // for drawing later
5059 if (rangeToUse
.GetStart() > GetRange().GetStart())
5060 left
= partialExtents
[(rangeToUse
.GetStart()-1) - GetRange().GetStart()];
5061 int right
= partialExtents
[rangeToUse
.GetEnd() - GetRange().GetStart()];
5062 int sz
= right
- left
;
5063 line
->GetObjectSizes().Add(sz
);
5065 else if (child
->GetRange().GetStart() > lineRange
.GetEnd())
5066 // Can break out of inner loop now since we've passed this line's range
5069 node2
= node2
->GetNext();
5072 lineNode
= lineNode
->GetNext();
5080 /// Apply paragraph styles, such as centering, to wrapped lines
5081 /// TODO: take into account box attributes, possibly
5082 void wxRichTextParagraph::ApplyParagraphStyle(wxRichTextLine
* line
, const wxRichTextAttr
& attr
, const wxRect
& rect
, wxDC
& dc
)
5084 if (!attr
.HasAlignment())
5087 wxPoint pos
= line
->GetPosition();
5088 wxPoint originalPos
= pos
;
5089 wxSize size
= line
->GetSize();
5091 // centering, right-justification
5092 if (attr
.HasAlignment() && attr
.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE
)
5094 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
5095 pos
.x
= (rect
.GetWidth() - rightIndent
- size
.x
)/2 + pos
.x
;
5096 line
->SetPosition(pos
);
5098 else if (attr
.HasAlignment() && attr
.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT
)
5100 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
5101 pos
.x
= pos
.x
+ rect
.GetWidth() - size
.x
- rightIndent
;
5102 line
->SetPosition(pos
);
5105 if (pos
!= originalPos
)
5107 wxPoint inc
= pos
- originalPos
;
5109 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5113 wxRichTextObject
* child
= node
->GetData();
5114 if (child
->IsTopLevel() && !child
->GetRange().IsOutside(line
->GetAbsoluteRange()))
5115 child
->Move(child
->GetPosition() + inc
);
5117 node
= node
->GetNext();
5122 /// Insert text at the given position
5123 bool wxRichTextParagraph::InsertText(long pos
, const wxString
& text
)
5125 wxRichTextObject
* childToUse
= NULL
;
5126 wxRichTextObjectList::compatibility_iterator nodeToUse
= wxRichTextObjectList::compatibility_iterator();
5128 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5131 wxRichTextObject
* child
= node
->GetData();
5132 if (child
->GetRange().Contains(pos
) && child
->GetRange().GetLength() > 0)
5139 node
= node
->GetNext();
5144 wxRichTextPlainText
* textObject
= wxDynamicCast(childToUse
, wxRichTextPlainText
);
5147 int posInString
= pos
- textObject
->GetRange().GetStart();
5149 wxString newText
= textObject
->GetText().Mid(0, posInString
) +
5150 text
+ textObject
->GetText().Mid(posInString
);
5151 textObject
->SetText(newText
);
5153 int textLength
= text
.length();
5155 textObject
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart(),
5156 textObject
->GetRange().GetEnd() + textLength
));
5158 // Increment the end range of subsequent fragments in this paragraph.
5159 // We'll set the paragraph range itself at a higher level.
5161 wxRichTextObjectList::compatibility_iterator node
= nodeToUse
->GetNext();
5164 wxRichTextObject
* child
= node
->GetData();
5165 child
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart() + textLength
,
5166 textObject
->GetRange().GetEnd() + textLength
));
5168 node
= node
->GetNext();
5175 // TODO: if not a text object, insert at closest position, e.g. in front of it
5181 // Don't pass parent initially to suppress auto-setting of parent range.
5182 // We'll do that at a higher level.
5183 wxRichTextPlainText
* textObject
= new wxRichTextPlainText(text
, this);
5185 AppendChild(textObject
);
5192 void wxRichTextParagraph::Copy(const wxRichTextParagraph
& obj
)
5194 wxRichTextCompositeObject::Copy(obj
);
5197 /// Clear the cached lines
5198 void wxRichTextParagraph::ClearLines()
5200 WX_CLEAR_LIST(wxRichTextLineList
, m_cachedLines
);
5203 /// Get/set the object size for the given range. Returns false if the range
5204 /// is invalid for this object.
5205 bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
5207 if (!range
.IsWithin(GetRange()))
5210 if (flags
& wxRICHTEXT_UNFORMATTED
)
5212 // Just use unformatted data, assume no line breaks
5215 wxArrayInt childExtents
;
5224 int maxLineHeight
= 0;
5226 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5229 wxRichTextObject
* child
= node
->GetData();
5230 if (!child
->GetRange().IsOutside(range
))
5232 // Floating objects have a zero size within the paragraph.
5233 if (child
->IsFloating())
5238 if (partialExtents
->GetCount() > 0)
5239 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5243 partialExtents
->Add(0 /* zero size */ + lastSize
);
5250 wxRichTextRange rangeToUse
= range
;
5251 rangeToUse
.LimitTo(child
->GetRange());
5252 int childDescent
= 0;
5254 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we've already cached the size,
5255 // but it's only going to be used after caching has taken place.
5256 if ((flags
& wxRICHTEXT_HEIGHT_ONLY
) && child
->GetCachedSize().y
!= 0)
5258 childDescent
= child
->GetDescent();
5259 childSize
= child
->GetCachedSize();
5261 if (childDescent
== 0)
5263 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5267 maxDescent
= wxMax(maxDescent
, childDescent
);
5268 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5271 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5273 sz
.y
= wxMax(sz
.y
, maxLineHeight
);
5274 sz
.x
+= childSize
.x
;
5275 descent
= maxDescent
;
5277 else if (child
->IsTopLevel())
5279 childDescent
= child
->GetDescent();
5280 childSize
= child
->GetCachedSize();
5282 if (childDescent
== 0)
5284 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5288 maxDescent
= wxMax(maxDescent
, childDescent
);
5289 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5292 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5294 sz
.y
= wxMax(sz
.y
, maxLineHeight
);
5295 sz
.x
+= childSize
.x
;
5296 descent
= maxDescent
;
5298 // FIXME: this won't change the original values.
5299 // Should we be calling GetRangeSize above instead of using cached values?
5301 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange()))
5303 child
->SetCachedSize(childSize
);
5304 child
->SetDescent(childDescent
);
5311 if (partialExtents
->GetCount() > 0)
5312 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5316 partialExtents
->Add(childSize
.x
+ lastSize
);
5319 else if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, context
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
), p
))
5321 if (childDescent
== 0)
5323 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5327 maxDescent
= wxMax(maxDescent
, childDescent
);
5328 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5331 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5333 sz
.y
= wxMax(sz
.y
, maxLineHeight
);
5334 sz
.x
+= childSize
.x
;
5335 descent
= maxDescent
;
5337 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange()))
5339 child
->SetCachedSize(childSize
);
5340 child
->SetDescent(childDescent
);
5346 if (partialExtents
->GetCount() > 0)
5347 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5352 for (i
= 0; i
< childExtents
.GetCount(); i
++)
5354 partialExtents
->Add(childExtents
[i
] + lastSize
);
5364 node
= node
->GetNext();
5370 // Use formatted data, with line breaks
5373 // We're going to loop through each line, and then for each line,
5374 // call GetRangeSize for the fragment that comprises that line.
5375 // Only we have to do that multiple times within the line, because
5376 // the line may be broken into pieces. For now ignore line break commands
5377 // (so we can assume that getting the unformatted size for a fragment
5378 // within a line is the actual size)
5380 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5383 wxRichTextLine
* line
= node
->GetData();
5384 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5385 if (!lineRange
.IsOutside(range
))
5389 int maxLineHeight
= 0;
5390 int maxLineWidth
= 0;
5392 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
5395 wxRichTextObject
* child
= node2
->GetData();
5397 if (!child
->IsFloating() && !child
->GetRange().IsOutside(lineRange
))
5399 wxRichTextRange rangeToUse
= lineRange
;
5400 rangeToUse
.LimitTo(child
->GetRange());
5401 if (child
->IsTopLevel())
5402 rangeToUse
= child
->GetOwnRange();
5405 int childDescent
= 0;
5406 if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, context
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
)))
5408 if (childDescent
== 0)
5410 // Assume that if descent is zero, this child can occupy the full line height
5411 // and does not need space for the line's maximum descent. So we influence
5412 // the overall max line height only.
5413 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5417 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5418 maxDescent
= wxMax(maxAscent
, childDescent
);
5420 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5421 maxLineWidth
+= childSize
.x
;
5425 node2
= node2
->GetNext();
5428 descent
= wxMax(descent
, maxDescent
);
5430 // Increase size by a line (TODO: paragraph spacing)
5431 sz
.y
+= maxLineHeight
;
5432 sz
.x
= wxMax(sz
.x
, maxLineWidth
);
5434 node
= node
->GetNext();
5441 /// Finds the absolute position and row height for the given character position
5442 bool wxRichTextParagraph::FindPosition(wxDC
& dc
, wxRichTextDrawingContext
& context
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
5446 wxRichTextLine
* line
= ((wxRichTextParagraphLayoutBox
*)GetParent())->GetLineAtPosition(0);
5448 *height
= line
->GetSize().y
;
5450 *height
= dc
.GetCharHeight();
5452 // -1 means 'the start of the buffer'.
5455 pt
= pt
+ line
->GetPosition();
5460 // The final position in a paragraph is taken to mean the position
5461 // at the start of the next paragraph.
5462 if (index
== GetRange().GetEnd())
5464 wxRichTextParagraphLayoutBox
* parent
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
5465 wxASSERT( parent
!= NULL
);
5467 // Find the height at the next paragraph, if any
5468 wxRichTextLine
* line
= parent
->GetLineAtPosition(index
+ 1);
5471 *height
= line
->GetSize().y
;
5472 pt
= line
->GetAbsolutePosition();
5476 *height
= dc
.GetCharHeight();
5477 int indent
= ConvertTenthsMMToPixels(dc
, m_attributes
.GetLeftIndent());
5478 pt
= wxPoint(indent
, GetCachedSize().y
);
5484 if (index
< GetRange().GetStart() || index
> GetRange().GetEnd())
5487 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5490 wxRichTextLine
* line
= node
->GetData();
5491 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5492 if (index
>= lineRange
.GetStart() && index
<= lineRange
.GetEnd())
5494 // If this is the last point in the line, and we're forcing the
5495 // returned value to be the start of the next line, do the required
5497 if (index
== lineRange
.GetEnd() && forceLineStart
)
5499 if (node
->GetNext())
5501 wxRichTextLine
* nextLine
= node
->GetNext()->GetData();
5502 *height
= nextLine
->GetSize().y
;
5503 pt
= nextLine
->GetAbsolutePosition();
5508 pt
.y
= line
->GetPosition().y
+ GetPosition().y
;
5510 wxRichTextRange
r(lineRange
.GetStart(), index
);
5514 // We find the size of the line up to this point,
5515 // then we can add this size to the line start position and
5516 // paragraph start position to find the actual position.
5518 if (GetRangeSize(r
, rangeSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, line
->GetPosition()+ GetPosition()))
5520 pt
.x
= line
->GetPosition().x
+ GetPosition().x
+ rangeSize
.x
;
5521 *height
= line
->GetSize().y
;
5528 node
= node
->GetNext();
5534 /// Hit-testing: returns a flag indicating hit test details, plus
5535 /// information about position
5536 int wxRichTextParagraph::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
5539 return wxRICHTEXT_HITTEST_NONE
;
5541 // If we're in the top-level container, then we can return
5542 // a suitable hit test code even if the point is outside the container area,
5543 // so that we can position the caret sensibly even if we don't
5544 // click on valid content. If we're not at the top-level, and the point
5545 // is not within this paragraph object, then we don't want to stop more
5546 // precise hit-testing from working prematurely, so return immediately.
5547 // NEW STRATEGY: use the parent boundary to test whether we're in the
5548 // right region, not the paragraph, since the paragraph may be positioned
5549 // some way in from where the user clicks.
5552 wxRichTextObject
* tempObj
, *tempContextObj
;
5553 if (GetParent() && GetParent()->wxRichTextObject::HitTest(dc
, context
, pt
, tmpPos
, & tempObj
, & tempContextObj
, flags
) == wxRICHTEXT_HITTEST_NONE
)
5554 return wxRICHTEXT_HITTEST_NONE
;
5557 wxRichTextObjectList::compatibility_iterator objNode
= m_children
.GetFirst();
5560 wxRichTextObject
* child
= objNode
->GetData();
5561 // Don't recurse if we have wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS,
5562 // and also, if this seems composite but actually is marked as atomic,
5564 if (child
->IsTopLevel() && ((flags
& wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS
) == 0) &&
5565 (! (((flags
& wxRICHTEXT_HITTEST_HONOUR_ATOMIC
) != 0) && child
->IsAtomic())))
5568 int hitTest
= child
->HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
);
5569 if (hitTest
!= wxRICHTEXT_HITTEST_NONE
)
5574 objNode
= objNode
->GetNext();
5577 wxPoint paraPos
= GetPosition();
5579 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5582 wxRichTextLine
* line
= node
->GetData();
5583 wxPoint linePos
= paraPos
+ line
->GetPosition();
5584 wxSize lineSize
= line
->GetSize();
5585 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5587 if (pt
.y
<= linePos
.y
+ lineSize
.y
)
5589 if (pt
.x
< linePos
.x
)
5591 textPosition
= lineRange
.GetStart();
5592 *obj
= FindObjectAtPosition(textPosition
);
5593 *contextObj
= GetContainer();
5594 return wxRICHTEXT_HITTEST_BEFORE
|wxRICHTEXT_HITTEST_OUTSIDE
;
5596 else if (pt
.x
>= (linePos
.x
+ lineSize
.x
))
5598 textPosition
= lineRange
.GetEnd();
5599 *obj
= FindObjectAtPosition(textPosition
);
5600 *contextObj
= GetContainer();
5601 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
;
5605 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5606 wxArrayInt partialExtents
;
5611 // This calculates the partial text extents
5612 GetRangeSize(lineRange
, paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, linePos
, & partialExtents
);
5614 int lastX
= linePos
.x
;
5616 for (i
= 0; i
< partialExtents
.GetCount(); i
++)
5618 int nextX
= partialExtents
[i
] + linePos
.x
;
5620 if (pt
.x
>= lastX
&& pt
.x
<= nextX
)
5622 textPosition
= i
+ lineRange
.GetStart(); // minus 1?
5624 *obj
= FindObjectAtPosition(textPosition
);
5625 *contextObj
= GetContainer();
5627 // So now we know it's between i-1 and i.
5628 // Let's see if we can be more precise about
5629 // which side of the position it's on.
5631 int midPoint
= (nextX
+ lastX
)/2;
5632 if (pt
.x
>= midPoint
)
5633 return wxRICHTEXT_HITTEST_AFTER
;
5635 return wxRICHTEXT_HITTEST_BEFORE
;
5642 int lastX
= linePos
.x
;
5643 for (i
= lineRange
.GetStart(); i
<= lineRange
.GetEnd(); i
++)
5648 wxRichTextRange
rangeToUse(lineRange
.GetStart(), i
);
5650 GetRangeSize(rangeToUse
, childSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, linePos
);
5652 int nextX
= childSize
.x
+ linePos
.x
;
5654 if (pt
.x
>= lastX
&& pt
.x
<= nextX
)
5658 *obj
= FindObjectAtPosition(textPosition
);
5659 *contextObj
= GetContainer();
5661 // So now we know it's between i-1 and i.
5662 // Let's see if we can be more precise about
5663 // which side of the position it's on.
5665 int midPoint
= (nextX
+ lastX
)/2;
5666 if (pt
.x
>= midPoint
)
5667 return wxRICHTEXT_HITTEST_AFTER
;
5669 return wxRICHTEXT_HITTEST_BEFORE
;
5680 node
= node
->GetNext();
5683 return wxRICHTEXT_HITTEST_NONE
;
5686 /// Split an object at this position if necessary, and return
5687 /// the previous object, or NULL if inserting at beginning.
5688 wxRichTextObject
* wxRichTextParagraph::SplitAt(long pos
, wxRichTextObject
** previousObject
)
5690 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5693 wxRichTextObject
* child
= node
->GetData();
5695 if (pos
== child
->GetRange().GetStart())
5699 if (node
->GetPrevious())
5700 *previousObject
= node
->GetPrevious()->GetData();
5702 *previousObject
= NULL
;
5708 if (child
->GetRange().Contains(pos
))
5710 // This should create a new object, transferring part of
5711 // the content to the old object and the rest to the new object.
5712 wxRichTextObject
* newObject
= child
->DoSplit(pos
);
5714 // If we couldn't split this object, just insert in front of it.
5717 // Maybe this is an empty string, try the next one
5722 // Insert the new object after 'child'
5723 if (node
->GetNext())
5724 m_children
.Insert(node
->GetNext(), newObject
);
5726 m_children
.Append(newObject
);
5727 newObject
->SetParent(this);
5730 *previousObject
= child
;
5736 node
= node
->GetNext();
5739 *previousObject
= NULL
;
5743 /// Move content to a list from obj on
5744 void wxRichTextParagraph::MoveToList(wxRichTextObject
* obj
, wxList
& list
)
5746 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(obj
);
5749 wxRichTextObject
* child
= node
->GetData();
5752 wxRichTextObjectList::compatibility_iterator oldNode
= node
;
5754 node
= node
->GetNext();
5756 m_children
.DeleteNode(oldNode
);
5760 /// Add content back from list
5761 void wxRichTextParagraph::MoveFromList(wxList
& list
)
5763 for (wxList::compatibility_iterator node
= list
.GetFirst(); node
; node
= node
->GetNext())
5765 AppendChild((wxRichTextObject
*) node
->GetData());
5770 void wxRichTextParagraph::CalculateRange(long start
, long& end
)
5772 wxRichTextCompositeObject::CalculateRange(start
, end
);
5774 // Add one for end of paragraph
5777 m_range
.SetRange(start
, end
);
5780 /// Find the object at the given position
5781 wxRichTextObject
* wxRichTextParagraph::FindObjectAtPosition(long position
)
5783 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5786 wxRichTextObject
* obj
= node
->GetData();
5787 if (obj
->GetRange().Contains(position
) ||
5788 obj
->GetRange().GetStart() == position
||
5789 obj
->GetRange().GetEnd() == position
)
5792 node
= node
->GetNext();
5797 /// Get the plain text searching from the start or end of the range.
5798 /// The resulting string may be shorter than the range given.
5799 bool wxRichTextParagraph::GetContiguousPlainText(wxString
& text
, const wxRichTextRange
& range
, bool fromStart
)
5801 text
= wxEmptyString
;
5805 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5808 wxRichTextObject
* obj
= node
->GetData();
5809 if (!obj
->GetRange().IsOutside(range
))
5811 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5814 text
+= textObj
->GetTextForRange(range
);
5822 node
= node
->GetNext();
5827 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetLast();
5830 wxRichTextObject
* obj
= node
->GetData();
5831 if (!obj
->GetRange().IsOutside(range
))
5833 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5836 text
= textObj
->GetTextForRange(range
) + text
;
5840 text
= wxT(" ") + text
;
5844 node
= node
->GetPrevious();
5851 /// Find a suitable wrap position.
5852 bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange
& range
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int availableSpace
, long& wrapPosition
, wxArrayInt
* partialExtents
)
5854 if (range
.GetLength() <= 0)
5857 // Find the first position where the line exceeds the available space.
5859 long breakPosition
= range
.GetEnd();
5861 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5862 if (partialExtents
&& partialExtents
->GetCount() >= (size_t) (GetRange().GetLength()-1)) // the final position in a paragraph is the newline
5866 if (range
.GetStart() > GetRange().GetStart())
5867 widthBefore
= (*partialExtents
)[range
.GetStart() - GetRange().GetStart() - 1];
5872 for (i
= (size_t) range
.GetStart(); i
<= (size_t) range
.GetEnd(); i
++)
5874 int widthFromStartOfThisRange
= (*partialExtents
)[i
- GetRange().GetStart()] - widthBefore
;
5876 if (widthFromStartOfThisRange
> availableSpace
)
5878 breakPosition
= i
-1;
5886 // Binary chop for speed
5887 long minPos
= range
.GetStart();
5888 long maxPos
= range
.GetEnd();
5891 if (minPos
== maxPos
)
5894 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
5896 if (sz
.x
> availableSpace
)
5897 breakPosition
= minPos
- 1;
5900 else if ((maxPos
- minPos
) == 1)
5903 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
5905 if (sz
.x
> availableSpace
)
5906 breakPosition
= minPos
- 1;
5909 GetRangeSize(wxRichTextRange(range
.GetStart(), maxPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
5910 if (sz
.x
> availableSpace
)
5911 breakPosition
= maxPos
-1;
5917 long nextPos
= minPos
+ ((maxPos
- minPos
) / 2);
5920 GetRangeSize(wxRichTextRange(range
.GetStart(), nextPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
5922 if (sz
.x
> availableSpace
)
5934 // Now we know the last position on the line.
5935 // Let's try to find a word break.
5938 if (GetContiguousPlainText(plainText
, wxRichTextRange(range
.GetStart(), breakPosition
), false))
5940 int newLinePos
= plainText
.Find(wxRichTextLineBreakChar
);
5941 if (newLinePos
!= wxNOT_FOUND
)
5943 breakPosition
= wxMax(0, range
.GetStart() + newLinePos
);
5947 int spacePos
= plainText
.Find(wxT(' '), true);
5948 int tabPos
= plainText
.Find(wxT('\t'), true);
5949 int pos
= wxMax(spacePos
, tabPos
);
5950 if (pos
!= wxNOT_FOUND
)
5952 int positionsFromEndOfString
= plainText
.length() - pos
- 1;
5953 breakPosition
= breakPosition
- positionsFromEndOfString
;
5958 wrapPosition
= breakPosition
;
5963 /// Get the bullet text for this paragraph.
5964 wxString
wxRichTextParagraph::GetBulletText()
5966 if (GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
||
5967 (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
))
5968 return wxEmptyString
;
5970 int number
= GetAttributes().GetBulletNumber();
5973 if ((GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ARABIC
) || (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
))
5975 text
.Printf(wxT("%d"), number
);
5977 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_UPPER
)
5979 // TODO: Unicode, and also check if number > 26
5980 text
.Printf(wxT("%c"), (wxChar
) (number
+64));
5982 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_LOWER
)
5984 // TODO: Unicode, and also check if number > 26
5985 text
.Printf(wxT("%c"), (wxChar
) (number
+96));
5987 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER
)
5989 text
= wxRichTextDecimalToRoman(number
);
5991 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER
)
5993 text
= wxRichTextDecimalToRoman(number
);
5996 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
)
5998 text
= GetAttributes().GetBulletText();
6001 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
)
6003 // The outline style relies on the text being computed statically,
6004 // since it depends on other levels points (e.g. 1.2.1.1). So normally the bullet text
6005 // should be stored in the attributes; if not, just use the number for this
6006 // level, as previously computed.
6007 if (!GetAttributes().GetBulletText().IsEmpty())
6008 text
= GetAttributes().GetBulletText();
6011 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PARENTHESES
)
6013 text
= wxT("(") + text
+ wxT(")");
6015 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_RIGHT_PARENTHESIS
)
6017 text
= text
+ wxT(")");
6020 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PERIOD
)
6028 /// Allocate or reuse a line object
6029 wxRichTextLine
* wxRichTextParagraph::AllocateLine(int pos
)
6031 if (pos
< (int) m_cachedLines
.GetCount())
6033 wxRichTextLine
* line
= m_cachedLines
.Item(pos
)->GetData();
6039 wxRichTextLine
* line
= new wxRichTextLine(this);
6040 m_cachedLines
.Append(line
);
6045 /// Clear remaining unused line objects, if any
6046 bool wxRichTextParagraph::ClearUnusedLines(int lineCount
)
6048 int cachedLineCount
= m_cachedLines
.GetCount();
6049 if ((int) cachedLineCount
> lineCount
)
6051 for (int i
= 0; i
< (int) (cachedLineCount
- lineCount
); i
++)
6053 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetLast();
6054 wxRichTextLine
* line
= node
->GetData();
6055 m_cachedLines
.Erase(node
);
6062 /// Get combined attributes of the base style, paragraph style and character style. We use this to dynamically
6063 /// retrieve the actual style.
6064 wxRichTextAttr
wxRichTextParagraph::GetCombinedAttributes(const wxRichTextAttr
& contentStyle
, bool includingBoxAttr
) const
6066 wxRichTextAttr attr
;
6067 wxRichTextParagraphLayoutBox
* buf
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
6070 attr
= buf
->GetBasicStyle();
6071 if (!includingBoxAttr
)
6073 attr
.GetTextBoxAttr().Reset();
6074 // The background colour will be painted by the container, and we don't
6075 // want to unnecessarily overwrite the background when we're drawing text
6076 // because this may erase the guideline (which appears just under the text
6077 // if there's no padding).
6078 attr
.SetFlags(attr
.GetFlags() & ~wxTEXT_ATTR_BACKGROUND_COLOUR
);
6080 wxRichTextApplyStyle(attr
, GetAttributes());
6083 attr
= GetAttributes();
6085 wxRichTextApplyStyle(attr
, contentStyle
);
6089 /// Get combined attributes of the base style and paragraph style.
6090 wxRichTextAttr
wxRichTextParagraph::GetCombinedAttributes(bool includingBoxAttr
) const
6092 wxRichTextAttr attr
;
6093 wxRichTextParagraphLayoutBox
* buf
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
6096 attr
= buf
->GetBasicStyle();
6097 if (!includingBoxAttr
)
6098 attr
.GetTextBoxAttr().Reset();
6099 wxRichTextApplyStyle(attr
, GetAttributes());
6102 attr
= GetAttributes();
6107 // Create default tabstop array
6108 void wxRichTextParagraph::InitDefaultTabs()
6110 // create a default tab list at 10 mm each.
6111 for (int i
= 0; i
< 20; ++i
)
6113 sm_defaultTabs
.Add(i
*100);
6117 // Clear default tabstop array
6118 void wxRichTextParagraph::ClearDefaultTabs()
6120 sm_defaultTabs
.Clear();
6123 void wxRichTextParagraph::LayoutFloat(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
, wxRichTextFloatCollector
* floatCollector
)
6125 wxRichTextObjectList::compatibility_iterator node
= GetChildren().GetFirst();
6128 wxRichTextObject
* anchored
= node
->GetData();
6129 if (anchored
&& anchored
->IsFloating() && !floatCollector
->HasFloat(anchored
))
6132 wxRichTextAttr
parentAttr(GetAttributes());
6133 context
.ApplyVirtualAttributes(parentAttr
, this);
6136 wxRect availableSpace
= GetParent()->GetAvailableContentArea(dc
, context
, rect
);
6138 anchored
->LayoutToBestSize(dc
, context
, GetBuffer(),
6139 parentAttr
, anchored
->GetAttributes(),
6140 parentRect
, availableSpace
,
6142 wxSize size
= anchored
->GetCachedSize();
6146 anchored
->GetRangeSize(anchored
->GetRange(), size
, descent
, dc
, context
, style
);
6150 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().IsValid())
6152 offsetY
= anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetValue();
6153 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
6155 offsetY
= ConvertTenthsMMToPixels(dc
, offsetY
);
6159 int pos
= floatCollector
->GetFitPosition(anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode(), rect
.y
+ offsetY
, size
.y
);
6161 /* Update the offset */
6162 int newOffsetY
= pos
- rect
.y
;
6163 if (newOffsetY
!= offsetY
)
6165 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
6166 newOffsetY
= ConvertPixelsToTenthsMM(dc
, newOffsetY
);
6167 anchored
->GetAttributes().GetTextBoxAttr().GetTop().SetValue(newOffsetY
);
6170 if (anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT
)
6172 else if (anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT
)
6173 x
= rect
.x
+ rect
.width
- size
.x
;
6175 //anchored->SetPosition(wxPoint(x, pos));
6176 anchored
->Move(wxPoint(x
, pos
)); // should move children
6177 anchored
->SetCachedSize(size
);
6178 floatCollector
->CollectFloat(this, anchored
);
6181 node
= node
->GetNext();
6185 // Get the first position from pos that has a line break character.
6186 long wxRichTextParagraph::GetFirstLineBreakPosition(long pos
)
6188 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
6191 wxRichTextObject
* obj
= node
->GetData();
6192 if (pos
>= obj
->GetRange().GetStart() && pos
<= obj
->GetRange().GetEnd())
6194 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
6197 long breakPos
= textObj
->GetFirstLineBreakPosition(pos
);
6202 node
= node
->GetNext();
6209 * This object represents a line in a paragraph, and stores
6210 * offsets from the start of the paragraph representing the
6211 * start and end positions of the line.
6214 wxRichTextLine::wxRichTextLine(wxRichTextParagraph
* parent
)
6220 void wxRichTextLine::Init(wxRichTextParagraph
* parent
)
6223 m_range
.SetRange(-1, -1);
6224 m_pos
= wxPoint(0, 0);
6225 m_size
= wxSize(0, 0);
6227 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6228 m_objectSizes
.Clear();
6233 void wxRichTextLine::Copy(const wxRichTextLine
& obj
)
6235 m_range
= obj
.m_range
;
6236 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6237 m_objectSizes
= obj
.m_objectSizes
;
6241 /// Get the absolute object position
6242 wxPoint
wxRichTextLine::GetAbsolutePosition() const
6244 return m_parent
->GetPosition() + m_pos
;
6247 /// Get the absolute range
6248 wxRichTextRange
wxRichTextLine::GetAbsoluteRange() const
6250 wxRichTextRange
range(m_range
.GetStart() + m_parent
->GetRange().GetStart(), 0);
6251 range
.SetEnd(range
.GetStart() + m_range
.GetLength()-1);
6256 * wxRichTextPlainText
6257 * This object represents a single piece of text.
6260 IMPLEMENT_DYNAMIC_CLASS(wxRichTextPlainText
, wxRichTextObject
)
6262 wxRichTextPlainText::wxRichTextPlainText(const wxString
& text
, wxRichTextObject
* parent
, wxRichTextAttr
* style
):
6263 wxRichTextObject(parent
)
6266 SetAttributes(*style
);
6271 #define USE_KERNING_FIX 1
6273 // If insufficient tabs are defined, this is the tab width used
6274 #define WIDTH_FOR_DEFAULT_TABS 50
6277 bool wxRichTextPlainText::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int WXUNUSED(style
))
6279 wxRichTextParagraph
* para
= wxDynamicCast(GetParent(), wxRichTextParagraph
);
6280 wxASSERT (para
!= NULL
);
6282 wxRichTextAttr
textAttr(para
? para
->GetCombinedAttributes(GetAttributes(), false /* no box attributes */) : GetAttributes());
6283 context
.ApplyVirtualAttributes(textAttr
, this);
6285 // Let's make the assumption for now that for content in a paragraph, including
6286 // text, we never have a discontinuous selection. So we only deal with a
6288 wxRichTextRange selectionRange
;
6289 if (selection
.IsValid())
6291 wxRichTextRangeArray selectionRanges
= selection
.GetSelectionForObject(this);
6292 if (selectionRanges
.GetCount() > 0)
6293 selectionRange
= selectionRanges
[0];
6295 selectionRange
= wxRICHTEXT_NO_SELECTION
;
6298 selectionRange
= wxRICHTEXT_NO_SELECTION
;
6300 int offset
= GetRange().GetStart();
6302 // Replace line break characters with spaces
6303 wxString str
= m_text
;
6304 wxString toRemove
= wxRichTextLineBreakChar
;
6305 str
.Replace(toRemove
, wxT(" "));
6306 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS
|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS
)))
6309 long len
= range
.GetLength();
6310 wxString stringChunk
= str
.Mid(range
.GetStart() - offset
, (size_t) len
);
6312 // Test for the optimized situations where all is selected, or none
6315 wxFont
textFont(GetBuffer()->GetFontTable().FindFont(textAttr
));
6316 wxCheckSetFont(dc
, textFont
);
6317 int charHeight
= dc
.GetCharHeight();
6320 if ( textFont
.IsOk() )
6322 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SMALL_CAPITALS
))
6324 textFont
.SetPointSize((int) (textFont
.GetPointSize()*0.75));
6325 wxCheckSetFont(dc
, textFont
);
6326 charHeight
= dc
.GetCharHeight();
6329 if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
) )
6331 if (textFont
.IsUsingSizeInPixels())
6333 double size
= static_cast<double>(textFont
.GetPixelSize().y
) / wxSCRIPT_MUL_FACTOR
;
6334 textFont
.SetPixelSize(wxSize(0, static_cast<int>(size
)));
6340 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6341 textFont
.SetPointSize(static_cast<int>(size
));
6345 wxCheckSetFont(dc
, textFont
);
6347 else if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) )
6349 if (textFont
.IsUsingSizeInPixels())
6351 double size
= static_cast<double>(textFont
.GetPixelSize().y
) / wxSCRIPT_MUL_FACTOR
;
6352 textFont
.SetPixelSize(wxSize(0, static_cast<int>(size
)));
6354 int sub_height
= static_cast<int>(static_cast<double>(charHeight
) / wxSCRIPT_MUL_FACTOR
);
6355 y
= rect
.y
+ (rect
.height
- sub_height
+ (descent
- m_descent
));
6359 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6360 textFont
.SetPointSize(static_cast<int>(size
));
6362 int sub_height
= static_cast<int>(static_cast<double>(charHeight
) / wxSCRIPT_MUL_FACTOR
);
6363 y
= rect
.y
+ (rect
.height
- sub_height
+ (descent
- m_descent
));
6365 wxCheckSetFont(dc
, textFont
);
6370 y
= rect
.y
+ (rect
.height
- charHeight
- (descent
- m_descent
));
6376 y
= rect
.y
+ (rect
.height
- charHeight
- (descent
- m_descent
));
6379 // TODO: new selection code
6381 // (a) All selected.
6382 if (selectionRange
.GetStart() <= range
.GetStart() && selectionRange
.GetEnd() >= range
.GetEnd())
6384 DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, true);
6386 // (b) None selected.
6387 else if (selectionRange
.GetEnd() < range
.GetStart() || selectionRange
.GetStart() > range
.GetEnd())
6389 // Draw all unselected
6390 DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, false);
6394 // (c) Part selected, part not
6395 // Let's draw unselected chunk, selected chunk, then unselected chunk.
6397 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6399 // 1. Initial unselected chunk, if any, up until start of selection.
6400 if (selectionRange
.GetStart() > range
.GetStart() && selectionRange
.GetStart() <= range
.GetEnd())
6402 int r1
= range
.GetStart();
6403 int s1
= selectionRange
.GetStart()-1;
6404 int fragmentLen
= s1
- r1
+ 1;
6405 if (fragmentLen
< 0)
6407 wxLogDebug(wxT("Mid(%d, %d"), (int)(r1
- offset
), (int)fragmentLen
);
6409 wxString stringFragment
= str
.Mid(r1
- offset
, fragmentLen
);
6411 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false);
6414 if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
)
6416 // Compensate for kerning difference
6417 wxString
stringFragment2(str
.Mid(r1
- offset
, fragmentLen
+1));
6418 wxString
stringFragment3(str
.Mid(r1
- offset
+ fragmentLen
, 1));
6420 wxCoord w1
, h1
, w2
, h2
, w3
, h3
;
6421 dc
.GetTextExtent(stringFragment
, & w1
, & h1
);
6422 dc
.GetTextExtent(stringFragment2
, & w2
, & h2
);
6423 dc
.GetTextExtent(stringFragment3
, & w3
, & h3
);
6425 int kerningDiff
= (w1
+ w3
) - w2
;
6426 x
= x
- kerningDiff
;
6431 // 2. Selected chunk, if any.
6432 if (selectionRange
.GetEnd() >= range
.GetStart())
6434 int s1
= wxMax(selectionRange
.GetStart(), range
.GetStart());
6435 int s2
= wxMin(selectionRange
.GetEnd(), range
.GetEnd());
6437 int fragmentLen
= s2
- s1
+ 1;
6438 if (fragmentLen
< 0)
6440 wxLogDebug(wxT("Mid(%d, %d"), (int)(s1
- offset
), (int)fragmentLen
);
6442 wxString stringFragment
= str
.Mid(s1
- offset
, fragmentLen
);
6444 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, true);
6447 if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
)
6449 // Compensate for kerning difference
6450 wxString
stringFragment2(str
.Mid(s1
- offset
, fragmentLen
+1));
6451 wxString
stringFragment3(str
.Mid(s1
- offset
+ fragmentLen
, 1));
6453 wxCoord w1
, h1
, w2
, h2
, w3
, h3
;
6454 dc
.GetTextExtent(stringFragment
, & w1
, & h1
);
6455 dc
.GetTextExtent(stringFragment2
, & w2
, & h2
);
6456 dc
.GetTextExtent(stringFragment3
, & w3
, & h3
);
6458 int kerningDiff
= (w1
+ w3
) - w2
;
6459 x
= x
- kerningDiff
;
6464 // 3. Remaining unselected chunk, if any
6465 if (selectionRange
.GetEnd() < range
.GetEnd())
6467 int s2
= wxMin(selectionRange
.GetEnd()+1, range
.GetEnd());
6468 int r2
= range
.GetEnd();
6470 int fragmentLen
= r2
- s2
+ 1;
6471 if (fragmentLen
< 0)
6473 wxLogDebug(wxT("Mid(%d, %d"), (int)(s2
- offset
), (int)fragmentLen
);
6475 wxString stringFragment
= str
.Mid(s2
- offset
, fragmentLen
);
6477 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false);
6484 bool wxRichTextPlainText::DrawTabbedString(wxDC
& dc
, const wxRichTextAttr
& attr
, const wxRect
& rect
,wxString
& str
, wxCoord
& x
, wxCoord
& y
, bool selected
)
6486 bool hasTabs
= (str
.Find(wxT('\t')) != wxNOT_FOUND
);
6488 wxArrayInt tabArray
;
6492 if (attr
.GetTabs().IsEmpty())
6493 tabArray
= wxRichTextParagraph::GetDefaultTabs();
6495 tabArray
= attr
.GetTabs();
6496 tabCount
= tabArray
.GetCount();
6498 for (int i
= 0; i
< tabCount
; ++i
)
6500 int pos
= tabArray
[i
];
6501 pos
= ConvertTenthsMMToPixels(dc
, pos
);
6508 int nextTabPos
= -1;
6514 wxColour
highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
));
6515 wxColour
highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
));
6517 wxCheckSetBrush(dc
, wxBrush(highlightColour
));
6518 wxCheckSetPen(dc
, wxPen(highlightColour
));
6519 dc
.SetTextForeground(highlightTextColour
);
6520 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6524 dc
.SetTextForeground(attr
.GetTextColour());
6526 if (attr
.HasFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
) && attr
.GetBackgroundColour().IsOk())
6528 dc
.SetBackgroundMode(wxBRUSHSTYLE_SOLID
);
6529 dc
.SetTextBackground(attr
.GetBackgroundColour());
6532 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6535 wxCoord x_orig
= GetParent()->GetPosition().x
;
6538 // the string has a tab
6539 // break up the string at the Tab
6540 wxString stringChunk
= str
.BeforeFirst(wxT('\t'));
6541 str
= str
.AfterFirst(wxT('\t'));
6542 dc
.GetTextExtent(stringChunk
, & w
, & h
);
6544 bool not_found
= true;
6545 for (int i
= 0; i
< tabCount
&& not_found
; ++i
)
6547 nextTabPos
= tabArray
.Item(i
) + x_orig
;
6549 // Find the next tab position.
6550 // Even if we're at the end of the tab array, we must still draw the chunk.
6552 if (nextTabPos
> tabPos
|| (i
== (tabCount
- 1)))
6554 if (nextTabPos
<= tabPos
)
6556 int defaultTabWidth
= ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
);
6557 nextTabPos
= tabPos
+ defaultTabWidth
;
6564 wxRect
selRect(x
, rect
.y
, w
, rect
.GetHeight());
6565 dc
.DrawRectangle(selRect
);
6567 dc
.DrawText(stringChunk
, x
, y
);
6569 if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
))
6571 wxPen oldPen
= dc
.GetPen();
6572 wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1));
6573 dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5));
6574 wxCheckSetPen(dc
, oldPen
);
6580 hasTabs
= (str
.Find(wxT('\t')) != wxNOT_FOUND
);
6585 dc
.GetTextExtent(str
, & w
, & h
);
6588 wxRect
selRect(x
, rect
.y
, w
, rect
.GetHeight());
6589 dc
.DrawRectangle(selRect
);
6591 dc
.DrawText(str
, x
, y
);
6593 if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
))
6595 wxPen oldPen
= dc
.GetPen();
6596 wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1));
6597 dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5));
6598 wxCheckSetPen(dc
, oldPen
);
6607 /// Lay the item out
6608 bool wxRichTextPlainText::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& WXUNUSED(rect
), const wxRect
& WXUNUSED(parentRect
), int WXUNUSED(style
))
6610 // Only lay out if we haven't already cached the size
6612 GetRangeSize(GetRange(), m_size
, m_descent
, dc
, context
, 0, wxPoint(0, 0));
6614 // Eventually we want to have a reasonable estimate of minimum size.
6615 m_minSize
= wxSize(0, 0);
6620 void wxRichTextPlainText::Copy(const wxRichTextPlainText
& obj
)
6622 wxRichTextObject::Copy(obj
);
6624 m_text
= obj
.m_text
;
6627 /// Get/set the object size for the given range. Returns false if the range
6628 /// is invalid for this object.
6629 bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int WXUNUSED(flags
), wxPoint position
, wxArrayInt
* partialExtents
) const
6631 if (!range
.IsWithin(GetRange()))
6634 wxRichTextParagraph
* para
= wxDynamicCast(GetParent(), wxRichTextParagraph
);
6635 wxASSERT (para
!= NULL
);
6637 int relativeX
= position
.x
- GetParent()->GetPosition().x
;
6639 wxRichTextAttr
textAttr(para
? para
->GetCombinedAttributes(GetAttributes()) : GetAttributes());
6640 context
.ApplyVirtualAttributes(textAttr
, (wxRichTextObject
*) this);
6642 // Always assume unformatted text, since at this level we have no knowledge
6643 // of line breaks - and we don't need it, since we'll calculate size within
6644 // formatted text by doing it in chunks according to the line ranges
6646 bool bScript(false);
6647 wxFont
font(GetBuffer()->GetFontTable().FindFont(textAttr
));
6650 if ( textAttr
.HasTextEffects() && ( (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
)
6651 || (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) ) )
6653 wxFont textFont
= font
;
6654 if (textFont
.IsUsingSizeInPixels())
6656 double size
= static_cast<double>(textFont
.GetPixelSize().y
) / wxSCRIPT_MUL_FACTOR
;
6657 textFont
.SetPixelSize(wxSize(0, static_cast<int>(size
)));
6661 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6662 textFont
.SetPointSize(static_cast<int>(size
));
6664 wxCheckSetFont(dc
, textFont
);
6667 else if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SMALL_CAPITALS
))
6669 wxFont textFont
= font
;
6670 textFont
.SetPointSize((int) (textFont
.GetPointSize()*0.75));
6671 wxCheckSetFont(dc
, textFont
);
6676 wxCheckSetFont(dc
, font
);
6680 bool haveDescent
= false;
6681 int startPos
= range
.GetStart() - GetRange().GetStart();
6682 long len
= range
.GetLength();
6684 wxString
str(m_text
);
6685 wxString toReplace
= wxRichTextLineBreakChar
;
6686 str
.Replace(toReplace
, wxT(" "));
6688 wxString stringChunk
= str
.Mid(startPos
, (size_t) len
);
6690 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS
|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS
)))
6691 stringChunk
.MakeUpper();
6695 if (stringChunk
.Find(wxT('\t')) != wxNOT_FOUND
)
6697 // the string has a tab
6698 wxArrayInt tabArray
;
6699 if (textAttr
.GetTabs().IsEmpty())
6700 tabArray
= wxRichTextParagraph::GetDefaultTabs();
6702 tabArray
= textAttr
.GetTabs();
6704 int tabCount
= tabArray
.GetCount();
6706 for (int i
= 0; i
< tabCount
; ++i
)
6708 int pos
= tabArray
[i
];
6709 pos
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, pos
);
6713 int nextTabPos
= -1;
6715 while (stringChunk
.Find(wxT('\t')) >= 0)
6717 int absoluteWidth
= 0;
6719 // the string has a tab
6720 // break up the string at the Tab
6721 wxString stringFragment
= stringChunk
.BeforeFirst(wxT('\t'));
6722 stringChunk
= stringChunk
.AfterFirst(wxT('\t'));
6727 if (partialExtents
->GetCount() > 0)
6728 oldWidth
= (*partialExtents
)[partialExtents
->GetCount()-1];
6732 // Add these partial extents
6734 dc
.GetPartialTextExtents(stringFragment
, p
);
6736 for (j
= 0; j
< p
.GetCount(); j
++)
6737 partialExtents
->Add(oldWidth
+ p
[j
]);
6739 if (partialExtents
->GetCount() > 0)
6740 absoluteWidth
= (*partialExtents
)[(*partialExtents
).GetCount()-1] + relativeX
;
6742 absoluteWidth
= relativeX
;
6746 dc
.GetTextExtent(stringFragment
, & w
, & h
);
6748 absoluteWidth
= width
+ relativeX
;
6752 bool notFound
= true;
6753 for (int i
= 0; i
< tabCount
&& notFound
; ++i
)
6755 nextTabPos
= tabArray
.Item(i
);
6757 // Find the next tab position.
6758 // Even if we're at the end of the tab array, we must still process the chunk.
6760 if (nextTabPos
> absoluteWidth
|| (i
== (tabCount
- 1)))
6762 if (nextTabPos
<= absoluteWidth
)
6764 int defaultTabWidth
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
);
6765 nextTabPos
= absoluteWidth
+ defaultTabWidth
;
6769 width
= nextTabPos
- relativeX
;
6772 partialExtents
->Add(width
);
6778 if (!stringChunk
.IsEmpty())
6783 if (partialExtents
->GetCount() > 0)
6784 oldWidth
= (*partialExtents
)[partialExtents
->GetCount()-1];
6788 // Add these partial extents
6790 dc
.GetPartialTextExtents(stringChunk
, p
);
6792 for (j
= 0; j
< p
.GetCount(); j
++)
6793 partialExtents
->Add(oldWidth
+ p
[j
]);
6797 dc
.GetTextExtent(stringChunk
, & w
, & h
, & descent
);
6805 int charHeight
= dc
.GetCharHeight();
6806 if ((*partialExtents
).GetCount() > 0)
6807 w
= (*partialExtents
)[partialExtents
->GetCount()-1];
6810 size
= wxSize(w
, charHeight
);
6814 size
= wxSize(width
, dc
.GetCharHeight());
6818 dc
.GetTextExtent(wxT("X"), & w
, & h
, & descent
);
6826 /// Do a split, returning an object containing the second part, and setting
6827 /// the first part in 'this'.
6828 wxRichTextObject
* wxRichTextPlainText::DoSplit(long pos
)
6830 long index
= pos
- GetRange().GetStart();
6832 if (index
< 0 || index
>= (int) m_text
.length())
6835 wxString firstPart
= m_text
.Mid(0, index
);
6836 wxString secondPart
= m_text
.Mid(index
);
6840 wxRichTextPlainText
* newObject
= new wxRichTextPlainText(secondPart
);
6841 newObject
->SetAttributes(GetAttributes());
6842 newObject
->SetProperties(GetProperties());
6844 newObject
->SetRange(wxRichTextRange(pos
, GetRange().GetEnd()));
6845 GetRange().SetEnd(pos
-1);
6851 void wxRichTextPlainText::CalculateRange(long start
, long& end
)
6853 end
= start
+ m_text
.length() - 1;
6854 m_range
.SetRange(start
, end
);
6858 bool wxRichTextPlainText::DeleteRange(const wxRichTextRange
& range
)
6860 wxRichTextRange r
= range
;
6862 r
.LimitTo(GetRange());
6864 if (r
.GetStart() == GetRange().GetStart() && r
.GetEnd() == GetRange().GetEnd())
6870 long startIndex
= r
.GetStart() - GetRange().GetStart();
6871 long len
= r
.GetLength();
6873 m_text
= m_text
.Mid(0, startIndex
) + m_text
.Mid(startIndex
+len
);
6877 /// Get text for the given range.
6878 wxString
wxRichTextPlainText::GetTextForRange(const wxRichTextRange
& range
) const
6880 wxRichTextRange r
= range
;
6882 r
.LimitTo(GetRange());
6884 long startIndex
= r
.GetStart() - GetRange().GetStart();
6885 long len
= r
.GetLength();
6887 return m_text
.Mid(startIndex
, len
);
6890 /// Returns true if this object can merge itself with the given one.
6891 bool wxRichTextPlainText::CanMerge(wxRichTextObject
* object
) const
6893 return object
->GetClassInfo() == wxCLASSINFO(wxRichTextPlainText
) &&
6894 (m_text
.empty() || (wxTextAttrEq(GetAttributes(), object
->GetAttributes()) && m_properties
== object
->GetProperties()));
6897 /// Returns true if this object merged itself with the given one.
6898 /// The calling code will then delete the given object.
6899 bool wxRichTextPlainText::Merge(wxRichTextObject
* object
)
6901 wxRichTextPlainText
* textObject
= wxDynamicCast(object
, wxRichTextPlainText
);
6902 wxASSERT( textObject
!= NULL
);
6906 m_text
+= textObject
->GetText();
6907 wxRichTextApplyStyle(m_attributes
, textObject
->GetAttributes());
6914 /// Dump to output stream for debugging
6915 void wxRichTextPlainText::Dump(wxTextOutputStream
& stream
)
6917 wxRichTextObject::Dump(stream
);
6918 stream
<< m_text
<< wxT("\n");
6921 /// Get the first position from pos that has a line break character.
6922 long wxRichTextPlainText::GetFirstLineBreakPosition(long pos
)
6925 int len
= m_text
.length();
6926 int startPos
= pos
- m_range
.GetStart();
6927 for (i
= startPos
; i
< len
; i
++)
6929 wxChar ch
= m_text
[i
];
6930 if (ch
== wxRichTextLineBreakChar
)
6932 return i
+ m_range
.GetStart();
6940 * This is a kind of box, used to represent the whole buffer
6943 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBuffer
, wxRichTextParagraphLayoutBox
)
6945 wxList
wxRichTextBuffer::sm_handlers
;
6946 wxList
wxRichTextBuffer::sm_drawingHandlers
;
6947 wxRichTextFieldTypeHashMap
wxRichTextBuffer::sm_fieldTypes
;
6948 wxRichTextRenderer
* wxRichTextBuffer::sm_renderer
= NULL
;
6949 int wxRichTextBuffer::sm_bulletRightMargin
= 20;
6950 float wxRichTextBuffer::sm_bulletProportion
= (float) 0.3;
6953 void wxRichTextBuffer::Init()
6955 m_commandProcessor
= new wxCommandProcessor
;
6956 m_styleSheet
= NULL
;
6958 m_batchedCommandDepth
= 0;
6959 m_batchedCommand
= NULL
;
6963 m_dimensionScale
= 1.0;
6969 wxRichTextBuffer::~wxRichTextBuffer()
6971 delete m_commandProcessor
;
6972 delete m_batchedCommand
;
6975 ClearEventHandlers();
6978 void wxRichTextBuffer::ResetAndClearCommands()
6982 GetCommandProcessor()->ClearCommands();
6985 Invalidate(wxRICHTEXT_ALL
);
6988 void wxRichTextBuffer::Copy(const wxRichTextBuffer
& obj
)
6990 wxRichTextParagraphLayoutBox::Copy(obj
);
6992 m_styleSheet
= obj
.m_styleSheet
;
6993 m_modified
= obj
.m_modified
;
6994 m_batchedCommandDepth
= 0;
6995 if (m_batchedCommand
)
6996 delete m_batchedCommand
;
6997 m_batchedCommand
= NULL
;
6998 m_suppressUndo
= obj
.m_suppressUndo
;
6999 m_invalidRange
= obj
.m_invalidRange
;
7000 m_dimensionScale
= obj
.m_dimensionScale
;
7001 m_fontScale
= obj
.m_fontScale
;
7004 /// Push style sheet to top of stack
7005 bool wxRichTextBuffer::PushStyleSheet(wxRichTextStyleSheet
* styleSheet
)
7008 styleSheet
->InsertSheet(m_styleSheet
);
7010 SetStyleSheet(styleSheet
);
7015 /// Pop style sheet from top of stack
7016 wxRichTextStyleSheet
* wxRichTextBuffer::PopStyleSheet()
7020 wxRichTextStyleSheet
* oldSheet
= m_styleSheet
;
7021 m_styleSheet
= oldSheet
->GetNextSheet();
7030 /// Submit command to insert paragraphs
7031 bool wxRichTextBuffer::InsertParagraphsWithUndo(long pos
, const wxRichTextParagraphLayoutBox
& paragraphs
, wxRichTextCtrl
* ctrl
, int flags
)
7033 return ctrl
->GetFocusObject()->InsertParagraphsWithUndo(this, pos
, paragraphs
, ctrl
, flags
);
7036 /// Submit command to insert paragraphs
7037 bool wxRichTextParagraphLayoutBox::InsertParagraphsWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxRichTextParagraphLayoutBox
& paragraphs
, wxRichTextCtrl
* ctrl
, int WXUNUSED(flags
))
7039 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7041 action
->GetNewParagraphs() = paragraphs
;
7043 action
->SetPosition(pos
);
7045 wxRichTextRange range
= wxRichTextRange(pos
, pos
+ paragraphs
.GetOwnRange().GetEnd() - 1);
7046 if (!paragraphs
.GetPartialParagraph())
7047 range
.SetEnd(range
.GetEnd()+1);
7049 // Set the range we'll need to delete in Undo
7050 action
->SetRange(range
);
7052 buffer
->SubmitAction(action
);
7057 /// Submit command to insert the given text
7058 bool wxRichTextBuffer::InsertTextWithUndo(long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
, int flags
)
7060 return ctrl
->GetFocusObject()->InsertTextWithUndo(this, pos
, text
, ctrl
, flags
);
7063 /// Submit command to insert the given text
7064 bool wxRichTextParagraphLayoutBox::InsertTextWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
, int flags
)
7066 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7068 wxRichTextAttr
* p
= NULL
;
7069 wxRichTextAttr paraAttr
;
7070 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7072 // Get appropriate paragraph style
7073 paraAttr
= GetStyleForNewParagraph(buffer
, pos
, false, false);
7074 if (!paraAttr
.IsDefault())
7078 action
->GetNewParagraphs().AddParagraphs(text
, p
);
7080 int length
= action
->GetNewParagraphs().GetOwnRange().GetLength();
7082 if (!text
.empty() && text
.Last() != wxT('\n'))
7084 // Don't count the newline when undoing
7086 action
->GetNewParagraphs().SetPartialParagraph(true);
7088 else if (!text
.empty() && text
.Last() == wxT('\n'))
7091 action
->SetPosition(pos
);
7093 // Set the range we'll need to delete in Undo
7094 action
->SetRange(wxRichTextRange(pos
, pos
+ length
- 1));
7096 buffer
->SubmitAction(action
);
7101 /// Submit command to insert the given text
7102 bool wxRichTextBuffer::InsertNewlineWithUndo(long pos
, wxRichTextCtrl
* ctrl
, int flags
)
7104 return ctrl
->GetFocusObject()->InsertNewlineWithUndo(this, pos
, ctrl
, flags
);
7107 /// Submit command to insert the given text
7108 bool wxRichTextParagraphLayoutBox::InsertNewlineWithUndo(wxRichTextBuffer
* buffer
, long pos
, wxRichTextCtrl
* ctrl
, int flags
)
7110 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7112 wxRichTextAttr
* p
= NULL
;
7113 wxRichTextAttr paraAttr
;
7114 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7116 paraAttr
= GetStyleForNewParagraph(buffer
, pos
, false, true /* look for next paragraph style */);
7117 if (!paraAttr
.IsDefault())
7121 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7122 // Don't include box attributes such as margins
7123 attr
.GetTextBoxAttr().Reset();
7125 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(wxEmptyString
, this, & attr
);
7126 action
->GetNewParagraphs().AppendChild(newPara
);
7127 action
->GetNewParagraphs().UpdateRanges();
7128 action
->GetNewParagraphs().SetPartialParagraph(false);
7129 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
, false);
7133 newPara
->SetAttributes(*p
);
7135 if (flags
& wxRICHTEXT_INSERT_INTERACTIVE
)
7137 if (para
&& para
->GetRange().GetEnd() == pos
)
7140 // Now see if we need to number the paragraph.
7141 if (newPara
->GetAttributes().HasBulletNumber())
7143 wxRichTextAttr numberingAttr
;
7144 if (FindNextParagraphNumber(para
, numberingAttr
))
7145 wxRichTextApplyStyle(newPara
->GetAttributes(), (const wxRichTextAttr
&) numberingAttr
);
7149 action
->SetPosition(pos
);
7151 // Use the default character style
7152 if (!buffer
->GetDefaultStyle().IsDefault() && newPara
->GetChildren().GetFirst())
7154 // Check whether the default style merely reflects the paragraph/basic style,
7155 // in which case don't apply it.
7156 wxRichTextAttr
defaultStyle(buffer
->GetDefaultStyle());
7157 defaultStyle
.GetTextBoxAttr().Reset();
7158 wxRichTextAttr toApply
;
7161 wxRichTextAttr combinedAttr
= para
->GetCombinedAttributes(true /* include box attributes */);
7162 wxRichTextAttr newAttr
;
7163 // This filters out attributes that are accounted for by the current
7164 // paragraph/basic style
7165 wxRichTextApplyStyle(toApply
, defaultStyle
, & combinedAttr
);
7168 toApply
= defaultStyle
;
7170 if (!toApply
.IsDefault())
7171 newPara
->GetChildren().GetFirst()->GetData()->SetAttributes(toApply
);
7174 // Set the range we'll need to delete in Undo
7175 action
->SetRange(wxRichTextRange(pos1
, pos1
));
7177 buffer
->SubmitAction(action
);
7182 /// Submit command to insert the given image
7183 bool wxRichTextBuffer::InsertImageWithUndo(long pos
, const wxRichTextImageBlock
& imageBlock
, wxRichTextCtrl
* ctrl
, int flags
,
7184 const wxRichTextAttr
& textAttr
)
7186 return ctrl
->GetFocusObject()->InsertImageWithUndo(this, pos
, imageBlock
, ctrl
, flags
, textAttr
);
7189 /// Submit command to insert the given image
7190 bool wxRichTextParagraphLayoutBox::InsertImageWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxRichTextImageBlock
& imageBlock
,
7191 wxRichTextCtrl
* ctrl
, int flags
,
7192 const wxRichTextAttr
& textAttr
)
7194 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7196 wxRichTextAttr
* p
= NULL
;
7197 wxRichTextAttr paraAttr
;
7198 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7200 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
7201 if (!paraAttr
.IsDefault())
7205 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7207 // Don't include box attributes such as margins
7208 attr
.GetTextBoxAttr().Reset();
7210 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7212 newPara
->SetAttributes(*p
);
7214 wxRichTextImage
* imageObject
= new wxRichTextImage(imageBlock
, newPara
);
7215 newPara
->AppendChild(imageObject
);
7216 imageObject
->SetAttributes(textAttr
);
7217 action
->GetNewParagraphs().AppendChild(newPara
);
7218 action
->GetNewParagraphs().UpdateRanges();
7220 action
->GetNewParagraphs().SetPartialParagraph(true);
7222 action
->SetPosition(pos
);
7224 // Set the range we'll need to delete in Undo
7225 action
->SetRange(wxRichTextRange(pos
, pos
));
7227 buffer
->SubmitAction(action
);
7232 // Insert an object with no change of it
7233 wxRichTextObject
* wxRichTextBuffer::InsertObjectWithUndo(long pos
, wxRichTextObject
*object
, wxRichTextCtrl
* ctrl
, int flags
)
7235 return ctrl
->GetFocusObject()->InsertObjectWithUndo(this, pos
, object
, ctrl
, flags
);
7238 // Insert an object with no change of it
7239 wxRichTextObject
* wxRichTextParagraphLayoutBox::InsertObjectWithUndo(wxRichTextBuffer
* buffer
, long pos
, wxRichTextObject
*object
, wxRichTextCtrl
* ctrl
, int flags
)
7241 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Object"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7243 wxRichTextAttr
* p
= NULL
;
7244 wxRichTextAttr paraAttr
;
7245 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7247 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
7248 if (!paraAttr
.IsDefault())
7252 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7254 // Don't include box attributes such as margins
7255 attr
.GetTextBoxAttr().Reset();
7257 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7259 newPara
->SetAttributes(*p
);
7261 newPara
->AppendChild(object
);
7262 action
->GetNewParagraphs().AppendChild(newPara
);
7263 action
->GetNewParagraphs().UpdateRanges();
7265 action
->GetNewParagraphs().SetPartialParagraph(true);
7267 action
->SetPosition(pos
);
7269 // Set the range we'll need to delete in Undo
7270 action
->SetRange(wxRichTextRange(pos
, pos
));
7272 buffer
->SubmitAction(action
);
7274 wxRichTextObject
* obj
= GetLeafObjectAtPosition(pos
);
7278 wxRichTextField
* wxRichTextParagraphLayoutBox::InsertFieldWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxString
& fieldType
,
7279 const wxRichTextProperties
& properties
,
7280 wxRichTextCtrl
* ctrl
, int flags
,
7281 const wxRichTextAttr
& textAttr
)
7283 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Field"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7285 wxRichTextAttr
* p
= NULL
;
7286 wxRichTextAttr paraAttr
;
7287 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7289 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
7290 if (!paraAttr
.IsDefault())
7294 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7296 // Don't include box attributes such as margins
7297 attr
.GetTextBoxAttr().Reset();
7299 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7301 newPara
->SetAttributes(*p
);
7303 wxRichTextField
* fieldObject
= new wxRichTextField();
7304 fieldObject
->wxRichTextObject::SetProperties(properties
);
7305 fieldObject
->SetFieldType(fieldType
);
7306 fieldObject
->SetAttributes(textAttr
);
7307 newPara
->AppendChild(fieldObject
);
7308 action
->GetNewParagraphs().AppendChild(newPara
);
7309 action
->GetNewParagraphs().UpdateRanges();
7310 action
->GetNewParagraphs().SetPartialParagraph(true);
7311 action
->SetPosition(pos
);
7313 // Set the range we'll need to delete in Undo
7314 action
->SetRange(wxRichTextRange(pos
, pos
));
7316 buffer
->SubmitAction(action
);
7318 wxRichTextField
* obj
= wxDynamicCast(GetLeafObjectAtPosition(pos
), wxRichTextField
);
7322 /// Get the style that is appropriate for a new paragraph at this position.
7323 /// If the previous paragraph has a paragraph style name, look up the next-paragraph
7325 wxRichTextAttr
wxRichTextParagraphLayoutBox::GetStyleForNewParagraph(wxRichTextBuffer
* buffer
, long pos
, bool caretPosition
, bool lookUpNewParaStyle
) const
7327 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
, caretPosition
);
7330 wxRichTextAttr attr
;
7331 bool foundAttributes
= false;
7333 // Look for a matching paragraph style
7334 if (lookUpNewParaStyle
&& !para
->GetAttributes().GetParagraphStyleName().IsEmpty() && buffer
->GetStyleSheet())
7336 wxRichTextParagraphStyleDefinition
* paraDef
= buffer
->GetStyleSheet()->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
7339 // If we're not at the end of the paragraph, then we apply THIS style, and not the designated next style.
7340 if (para
->GetRange().GetEnd() == pos
&& !paraDef
->GetNextStyle().IsEmpty())
7342 wxRichTextParagraphStyleDefinition
* nextParaDef
= buffer
->GetStyleSheet()->FindParagraphStyle(paraDef
->GetNextStyle());
7345 foundAttributes
= true;
7346 attr
= nextParaDef
->GetStyleMergedWithBase(buffer
->GetStyleSheet());
7350 // If we didn't find the 'next style', use this style instead.
7351 if (!foundAttributes
)
7353 foundAttributes
= true;
7354 attr
= paraDef
->GetStyleMergedWithBase(buffer
->GetStyleSheet());
7359 // Also apply list style if present
7360 if (lookUpNewParaStyle
&& !para
->GetAttributes().GetListStyleName().IsEmpty() && buffer
->GetStyleSheet())
7362 wxRichTextListStyleDefinition
* listDef
= buffer
->GetStyleSheet()->FindListStyle(para
->GetAttributes().GetListStyleName());
7365 int thisIndent
= para
->GetAttributes().GetLeftIndent();
7366 int thisLevel
= para
->GetAttributes().HasOutlineLevel() ? para
->GetAttributes().GetOutlineLevel() : listDef
->FindLevelForIndent(thisIndent
);
7368 // Apply the overall list style, and item style for this level
7369 wxRichTextAttr
listStyle(listDef
->GetCombinedStyleForLevel(thisLevel
, buffer
->GetStyleSheet()));
7370 wxRichTextApplyStyle(attr
, listStyle
);
7371 attr
.SetOutlineLevel(thisLevel
);
7372 if (para
->GetAttributes().HasBulletNumber())
7373 attr
.SetBulletNumber(para
->GetAttributes().GetBulletNumber());
7377 if (!foundAttributes
)
7379 attr
= para
->GetAttributes();
7380 int flags
= attr
.GetFlags();
7382 // Eliminate character styles
7383 flags
&= ( (~ wxTEXT_ATTR_FONT
) |
7384 (~ wxTEXT_ATTR_TEXT_COLOUR
) |
7385 (~ wxTEXT_ATTR_BACKGROUND_COLOUR
) );
7386 attr
.SetFlags(flags
);
7392 return wxRichTextAttr();
7395 /// Submit command to delete this range
7396 bool wxRichTextBuffer::DeleteRangeWithUndo(const wxRichTextRange
& range
, wxRichTextCtrl
* ctrl
)
7398 return ctrl
->GetFocusObject()->DeleteRangeWithUndo(range
, ctrl
, this);
7401 /// Submit command to delete this range
7402 bool wxRichTextParagraphLayoutBox::DeleteRangeWithUndo(const wxRichTextRange
& range
, wxRichTextCtrl
* ctrl
, wxRichTextBuffer
* buffer
)
7404 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Delete"), wxRICHTEXT_DELETE
, buffer
, this, ctrl
);
7406 action
->SetPosition(ctrl
->GetCaretPosition());
7408 // Set the range to delete
7409 action
->SetRange(range
);
7411 // Copy the fragment that we'll need to restore in Undo
7412 CopyFragment(range
, action
->GetOldParagraphs());
7414 // See if we're deleting a paragraph marker, in which case we need to
7415 // make a note not to copy the attributes from the 2nd paragraph to the 1st.
7416 if (range
.GetStart() == range
.GetEnd())
7418 wxRichTextParagraph
* para
= GetParagraphAtPosition(range
.GetStart());
7419 if (para
&& para
->GetRange().GetEnd() == range
.GetEnd())
7421 wxRichTextParagraph
* nextPara
= GetParagraphAtPosition(range
.GetStart()+1);
7422 if (nextPara
&& nextPara
!= para
)
7424 action
->GetOldParagraphs().GetChildren().GetFirst()->GetData()->SetAttributes(nextPara
->GetAttributes());
7425 action
->GetOldParagraphs().GetAttributes().SetFlags(action
->GetOldParagraphs().GetAttributes().GetFlags() | wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
);
7430 buffer
->SubmitAction(action
);
7435 /// Collapse undo/redo commands
7436 bool wxRichTextBuffer::BeginBatchUndo(const wxString
& cmdName
)
7438 if (m_batchedCommandDepth
== 0)
7440 wxASSERT(m_batchedCommand
== NULL
);
7441 if (m_batchedCommand
)
7443 GetCommandProcessor()->Store(m_batchedCommand
);
7445 m_batchedCommand
= new wxRichTextCommand(cmdName
);
7448 m_batchedCommandDepth
++;
7453 /// Collapse undo/redo commands
7454 bool wxRichTextBuffer::EndBatchUndo()
7456 m_batchedCommandDepth
--;
7458 wxASSERT(m_batchedCommandDepth
>= 0);
7459 wxASSERT(m_batchedCommand
!= NULL
);
7461 if (m_batchedCommandDepth
== 0)
7463 GetCommandProcessor()->Store(m_batchedCommand
);
7464 m_batchedCommand
= NULL
;
7470 /// Submit immediately, or delay according to whether collapsing is on
7471 bool wxRichTextBuffer::SubmitAction(wxRichTextAction
* action
)
7473 if (action
&& !action
->GetNewParagraphs().IsEmpty())
7474 PrepareContent(action
->GetNewParagraphs());
7476 if (BatchingUndo() && m_batchedCommand
&& !SuppressingUndo())
7478 wxRichTextCommand
* cmd
= new wxRichTextCommand(action
->GetName());
7479 cmd
->AddAction(action
);
7481 cmd
->GetActions().Clear();
7484 m_batchedCommand
->AddAction(action
);
7488 wxRichTextCommand
* cmd
= new wxRichTextCommand(action
->GetName());
7489 cmd
->AddAction(action
);
7491 // Only store it if we're not suppressing undo.
7492 return GetCommandProcessor()->Submit(cmd
, !SuppressingUndo());
7498 /// Begin suppressing undo/redo commands.
7499 bool wxRichTextBuffer::BeginSuppressUndo()
7506 /// End suppressing undo/redo commands.
7507 bool wxRichTextBuffer::EndSuppressUndo()
7514 /// Begin using a style
7515 bool wxRichTextBuffer::BeginStyle(const wxRichTextAttr
& style
)
7517 wxRichTextAttr
newStyle(GetDefaultStyle());
7518 newStyle
.GetTextBoxAttr().Reset();
7520 // Save the old default style
7521 m_attributeStack
.Append((wxObject
*) new wxRichTextAttr(newStyle
));
7523 wxRichTextApplyStyle(newStyle
, style
);
7524 newStyle
.SetFlags(style
.GetFlags()|newStyle
.GetFlags());
7526 SetDefaultStyle(newStyle
);
7532 bool wxRichTextBuffer::EndStyle()
7534 if (!m_attributeStack
.GetFirst())
7536 wxLogDebug(_("Too many EndStyle calls!"));
7540 wxList::compatibility_iterator node
= m_attributeStack
.GetLast();
7541 wxRichTextAttr
* attr
= (wxRichTextAttr
*)node
->GetData();
7542 m_attributeStack
.Erase(node
);
7544 SetDefaultStyle(*attr
);
7551 bool wxRichTextBuffer::EndAllStyles()
7553 while (m_attributeStack
.GetCount() != 0)
7558 /// Clear the style stack
7559 void wxRichTextBuffer::ClearStyleStack()
7561 for (wxList::compatibility_iterator node
= m_attributeStack
.GetFirst(); node
; node
= node
->GetNext())
7562 delete (wxRichTextAttr
*) node
->GetData();
7563 m_attributeStack
.Clear();
7566 /// Begin using bold
7567 bool wxRichTextBuffer::BeginBold()
7569 wxRichTextAttr attr
;
7570 attr
.SetFontWeight(wxFONTWEIGHT_BOLD
);
7572 return BeginStyle(attr
);
7575 /// Begin using italic
7576 bool wxRichTextBuffer::BeginItalic()
7578 wxRichTextAttr attr
;
7579 attr
.SetFontStyle(wxFONTSTYLE_ITALIC
);
7581 return BeginStyle(attr
);
7584 /// Begin using underline
7585 bool wxRichTextBuffer::BeginUnderline()
7587 wxRichTextAttr attr
;
7588 attr
.SetFontUnderlined(true);
7590 return BeginStyle(attr
);
7593 /// Begin using point size
7594 bool wxRichTextBuffer::BeginFontSize(int pointSize
)
7596 wxRichTextAttr attr
;
7597 attr
.SetFontSize(pointSize
);
7599 return BeginStyle(attr
);
7602 /// Begin using this font
7603 bool wxRichTextBuffer::BeginFont(const wxFont
& font
)
7605 wxRichTextAttr attr
;
7608 return BeginStyle(attr
);
7611 /// Begin using this colour
7612 bool wxRichTextBuffer::BeginTextColour(const wxColour
& colour
)
7614 wxRichTextAttr attr
;
7615 attr
.SetFlags(wxTEXT_ATTR_TEXT_COLOUR
);
7616 attr
.SetTextColour(colour
);
7618 return BeginStyle(attr
);
7621 /// Begin using alignment
7622 bool wxRichTextBuffer::BeginAlignment(wxTextAttrAlignment alignment
)
7624 wxRichTextAttr attr
;
7625 attr
.SetFlags(wxTEXT_ATTR_ALIGNMENT
);
7626 attr
.SetAlignment(alignment
);
7628 return BeginStyle(attr
);
7631 /// Begin left indent
7632 bool wxRichTextBuffer::BeginLeftIndent(int leftIndent
, int leftSubIndent
)
7634 wxRichTextAttr attr
;
7635 attr
.SetFlags(wxTEXT_ATTR_LEFT_INDENT
);
7636 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7638 return BeginStyle(attr
);
7641 /// Begin right indent
7642 bool wxRichTextBuffer::BeginRightIndent(int rightIndent
)
7644 wxRichTextAttr attr
;
7645 attr
.SetFlags(wxTEXT_ATTR_RIGHT_INDENT
);
7646 attr
.SetRightIndent(rightIndent
);
7648 return BeginStyle(attr
);
7651 /// Begin paragraph spacing
7652 bool wxRichTextBuffer::BeginParagraphSpacing(int before
, int after
)
7656 flags
|= wxTEXT_ATTR_PARA_SPACING_BEFORE
;
7658 flags
|= wxTEXT_ATTR_PARA_SPACING_AFTER
;
7660 wxRichTextAttr attr
;
7661 attr
.SetFlags(flags
);
7662 attr
.SetParagraphSpacingBefore(before
);
7663 attr
.SetParagraphSpacingAfter(after
);
7665 return BeginStyle(attr
);
7668 /// Begin line spacing
7669 bool wxRichTextBuffer::BeginLineSpacing(int lineSpacing
)
7671 wxRichTextAttr attr
;
7672 attr
.SetFlags(wxTEXT_ATTR_LINE_SPACING
);
7673 attr
.SetLineSpacing(lineSpacing
);
7675 return BeginStyle(attr
);
7678 /// Begin numbered bullet
7679 bool wxRichTextBuffer::BeginNumberedBullet(int bulletNumber
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7681 wxRichTextAttr attr
;
7682 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7683 attr
.SetBulletStyle(bulletStyle
);
7684 attr
.SetBulletNumber(bulletNumber
);
7685 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7687 return BeginStyle(attr
);
7690 /// Begin symbol bullet
7691 bool wxRichTextBuffer::BeginSymbolBullet(const wxString
& symbol
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7693 wxRichTextAttr attr
;
7694 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7695 attr
.SetBulletStyle(bulletStyle
);
7696 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7697 attr
.SetBulletText(symbol
);
7699 return BeginStyle(attr
);
7702 /// Begin standard bullet
7703 bool wxRichTextBuffer::BeginStandardBullet(const wxString
& bulletName
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7705 wxRichTextAttr attr
;
7706 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7707 attr
.SetBulletStyle(bulletStyle
);
7708 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7709 attr
.SetBulletName(bulletName
);
7711 return BeginStyle(attr
);
7714 /// Begin named character style
7715 bool wxRichTextBuffer::BeginCharacterStyle(const wxString
& characterStyle
)
7717 if (GetStyleSheet())
7719 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->FindCharacterStyle(characterStyle
);
7722 wxRichTextAttr attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7723 return BeginStyle(attr
);
7729 /// Begin named paragraph style
7730 bool wxRichTextBuffer::BeginParagraphStyle(const wxString
& paragraphStyle
)
7732 if (GetStyleSheet())
7734 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(paragraphStyle
);
7737 wxRichTextAttr attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7738 return BeginStyle(attr
);
7744 /// Begin named list style
7745 bool wxRichTextBuffer::BeginListStyle(const wxString
& listStyle
, int level
, int number
)
7747 if (GetStyleSheet())
7749 wxRichTextListStyleDefinition
* def
= GetStyleSheet()->FindListStyle(listStyle
);
7752 wxRichTextAttr
attr(def
->GetCombinedStyleForLevel(level
));
7754 attr
.SetBulletNumber(number
);
7756 return BeginStyle(attr
);
7763 bool wxRichTextBuffer::BeginURL(const wxString
& url
, const wxString
& characterStyle
)
7765 wxRichTextAttr attr
;
7767 if (!characterStyle
.IsEmpty() && GetStyleSheet())
7769 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->FindCharacterStyle(characterStyle
);
7772 attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7777 return BeginStyle(attr
);
7780 /// Adds a handler to the end
7781 void wxRichTextBuffer::AddHandler(wxRichTextFileHandler
*handler
)
7783 sm_handlers
.Append(handler
);
7786 /// Inserts a handler at the front
7787 void wxRichTextBuffer::InsertHandler(wxRichTextFileHandler
*handler
)
7789 sm_handlers
.Insert( handler
);
7792 /// Removes a handler
7793 bool wxRichTextBuffer::RemoveHandler(const wxString
& name
)
7795 wxRichTextFileHandler
*handler
= FindHandler(name
);
7798 sm_handlers
.DeleteObject(handler
);
7806 /// Finds a handler by filename or, if supplied, type
7807 wxRichTextFileHandler
*wxRichTextBuffer::FindHandlerFilenameOrType(const wxString
& filename
,
7808 wxRichTextFileType imageType
)
7810 if (imageType
!= wxRICHTEXT_TYPE_ANY
)
7811 return FindHandler(imageType
);
7812 else if (!filename
.IsEmpty())
7814 wxString path
, file
, ext
;
7815 wxFileName::SplitPath(filename
, & path
, & file
, & ext
);
7816 return FindHandler(ext
, imageType
);
7823 /// Finds a handler by name
7824 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(const wxString
& name
)
7826 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7829 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7830 if (handler
->GetName().Lower() == name
.Lower()) return handler
;
7832 node
= node
->GetNext();
7837 /// Finds a handler by extension and type
7838 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(const wxString
& extension
, wxRichTextFileType type
)
7840 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7843 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7844 if ( handler
->GetExtension().Lower() == extension
.Lower() &&
7845 (type
== wxRICHTEXT_TYPE_ANY
|| handler
->GetType() == type
) )
7847 node
= node
->GetNext();
7852 /// Finds a handler by type
7853 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(wxRichTextFileType type
)
7855 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7858 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7859 if (handler
->GetType() == type
) return handler
;
7860 node
= node
->GetNext();
7865 void wxRichTextBuffer::InitStandardHandlers()
7867 if (!FindHandler(wxRICHTEXT_TYPE_TEXT
))
7868 AddHandler(new wxRichTextPlainTextHandler
);
7871 void wxRichTextBuffer::CleanUpHandlers()
7873 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7876 wxRichTextFileHandler
* handler
= (wxRichTextFileHandler
*)node
->GetData();
7877 wxList::compatibility_iterator next
= node
->GetNext();
7882 sm_handlers
.Clear();
7885 wxString
wxRichTextBuffer::GetExtWildcard(bool combine
, bool save
, wxArrayInt
* types
)
7892 wxList::compatibility_iterator node
= GetHandlers().GetFirst();
7896 wxRichTextFileHandler
* handler
= (wxRichTextFileHandler
*) node
->GetData();
7897 if (handler
->IsVisible() && ((save
&& handler
->CanSave()) || (!save
&& handler
->CanLoad())))
7902 wildcard
+= wxT(";");
7903 wildcard
+= wxT("*.") + handler
->GetExtension();
7908 wildcard
+= wxT("|");
7909 wildcard
+= handler
->GetName();
7910 wildcard
+= wxT(" ");
7911 wildcard
+= _("files");
7912 wildcard
+= wxT(" (*.");
7913 wildcard
+= handler
->GetExtension();
7914 wildcard
+= wxT(")|*.");
7915 wildcard
+= handler
->GetExtension();
7917 types
->Add(handler
->GetType());
7922 node
= node
->GetNext();
7926 wildcard
= wxT("(") + wildcard
+ wxT(")|") + wildcard
;
7931 bool wxRichTextBuffer::LoadFile(const wxString
& filename
, wxRichTextFileType type
)
7933 wxRichTextFileHandler
* handler
= FindHandlerFilenameOrType(filename
, type
);
7936 SetDefaultStyle(wxRichTextAttr());
7937 handler
->SetFlags(GetHandlerFlags());
7938 bool success
= handler
->LoadFile(this, filename
);
7939 Invalidate(wxRICHTEXT_ALL
);
7947 bool wxRichTextBuffer::SaveFile(const wxString
& filename
, wxRichTextFileType type
)
7949 wxRichTextFileHandler
* handler
= FindHandlerFilenameOrType(filename
, type
);
7952 handler
->SetFlags(GetHandlerFlags());
7953 return handler
->SaveFile(this, filename
);
7959 /// Load from a stream
7960 bool wxRichTextBuffer::LoadFile(wxInputStream
& stream
, wxRichTextFileType type
)
7962 wxRichTextFileHandler
* handler
= FindHandler(type
);
7965 SetDefaultStyle(wxRichTextAttr());
7966 handler
->SetFlags(GetHandlerFlags());
7967 bool success
= handler
->LoadFile(this, stream
);
7968 Invalidate(wxRICHTEXT_ALL
);
7975 /// Save to a stream
7976 bool wxRichTextBuffer::SaveFile(wxOutputStream
& stream
, wxRichTextFileType type
)
7978 wxRichTextFileHandler
* handler
= FindHandler(type
);
7981 handler
->SetFlags(GetHandlerFlags());
7982 return handler
->SaveFile(this, stream
);
7988 /// Copy the range to the clipboard
7989 bool wxRichTextBuffer::CopyToClipboard(const wxRichTextRange
& range
)
7991 bool success
= false;
7992 wxRichTextParagraphLayoutBox
* container
= this;
7993 if (GetRichTextCtrl())
7994 container
= GetRichTextCtrl()->GetFocusObject();
7996 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
7998 if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open())
8000 wxTheClipboard
->Clear();
8002 // Add composite object
8004 wxDataObjectComposite
* compositeObject
= new wxDataObjectComposite();
8007 wxString text
= container
->GetTextForRange(range
);
8010 text
= wxTextFile::Translate(text
, wxTextFileType_Dos
);
8013 compositeObject
->Add(new wxTextDataObject(text
), false /* not preferred */);
8016 // Add rich text buffer data object. This needs the XML handler to be present.
8018 if (FindHandler(wxRICHTEXT_TYPE_XML
))
8020 wxRichTextBuffer
* richTextBuf
= new wxRichTextBuffer
;
8021 container
->CopyFragment(range
, *richTextBuf
);
8023 compositeObject
->Add(new wxRichTextBufferDataObject(richTextBuf
), true /* preferred */);
8026 if (wxTheClipboard
->SetData(compositeObject
))
8029 wxTheClipboard
->Close();
8038 /// Paste the clipboard content to the buffer
8039 bool wxRichTextBuffer::PasteFromClipboard(long position
)
8041 bool success
= false;
8042 wxRichTextParagraphLayoutBox
* container
= this;
8043 if (GetRichTextCtrl())
8044 container
= GetRichTextCtrl()->GetFocusObject();
8046 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
8047 if (CanPasteFromClipboard())
8049 if (wxTheClipboard
->Open())
8051 if (wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())))
8053 wxRichTextBufferDataObject data
;
8054 wxTheClipboard
->GetData(data
);
8055 wxRichTextBuffer
* richTextBuffer
= data
.GetRichTextBuffer();
8058 container
->InsertParagraphsWithUndo(this, position
+1, *richTextBuffer
, GetRichTextCtrl(), 0);
8059 if (GetRichTextCtrl())
8060 GetRichTextCtrl()->ShowPosition(position
+ richTextBuffer
->GetOwnRange().GetEnd());
8061 delete richTextBuffer
;
8064 else if (wxTheClipboard
->IsSupported(wxDF_TEXT
)
8066 || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
)
8070 wxTextDataObject data
;
8071 wxTheClipboard
->GetData(data
);
8072 wxString
text(data
.GetText());
8075 text2
.Alloc(text
.Length()+1);
8077 for (i
= 0; i
< text
.Length(); i
++)
8079 wxChar ch
= text
[i
];
8080 if (ch
!= wxT('\r'))
8084 wxString text2
= text
;
8086 container
->InsertTextWithUndo(this, position
+1, text2
, GetRichTextCtrl(), wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
);
8088 if (GetRichTextCtrl())
8089 GetRichTextCtrl()->ShowPosition(position
+ text2
.Length());
8093 else if (wxTheClipboard
->IsSupported(wxDF_BITMAP
))
8095 wxBitmapDataObject data
;
8096 wxTheClipboard
->GetData(data
);
8097 wxBitmap
bitmap(data
.GetBitmap());
8098 wxImage
image(bitmap
.ConvertToImage());
8100 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, this, container
, GetRichTextCtrl(), false);
8102 action
->GetNewParagraphs().AddImage(image
);
8104 if (action
->GetNewParagraphs().GetChildCount() == 1)
8105 action
->GetNewParagraphs().SetPartialParagraph(true);
8107 action
->SetPosition(position
+1);
8109 // Set the range we'll need to delete in Undo
8110 action
->SetRange(wxRichTextRange(position
+1, position
+1));
8112 SubmitAction(action
);
8116 wxTheClipboard
->Close();
8120 wxUnusedVar(position
);
8125 /// Can we paste from the clipboard?
8126 bool wxRichTextBuffer::CanPasteFromClipboard() const
8128 bool canPaste
= false;
8129 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
8130 if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open())
8132 if (wxTheClipboard
->IsSupported(wxDF_TEXT
)
8134 || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
)
8136 || wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())) ||
8137 wxTheClipboard
->IsSupported(wxDF_BITMAP
))
8141 wxTheClipboard
->Close();
8147 /// Dumps contents of buffer for debugging purposes
8148 void wxRichTextBuffer::Dump()
8152 wxStringOutputStream
stream(& text
);
8153 wxTextOutputStream
textStream(stream
);
8160 /// Add an event handler
8161 bool wxRichTextBuffer::AddEventHandler(wxEvtHandler
* handler
)
8163 m_eventHandlers
.Append(handler
);
8167 /// Remove an event handler
8168 bool wxRichTextBuffer::RemoveEventHandler(wxEvtHandler
* handler
, bool deleteHandler
)
8170 wxList::compatibility_iterator node
= m_eventHandlers
.Find(handler
);
8173 m_eventHandlers
.Erase(node
);
8183 /// Clear event handlers
8184 void wxRichTextBuffer::ClearEventHandlers()
8186 m_eventHandlers
.Clear();
8189 /// Send event to event handlers. If sendToAll is true, will send to all event handlers,
8190 /// otherwise will stop at the first successful one.
8191 bool wxRichTextBuffer::SendEvent(wxEvent
& event
, bool sendToAll
)
8193 bool success
= false;
8194 for (wxList::compatibility_iterator node
= m_eventHandlers
.GetFirst(); node
; node
= node
->GetNext())
8196 wxEvtHandler
* handler
= (wxEvtHandler
*) node
->GetData();
8197 if (handler
->ProcessEvent(event
))
8207 /// Set style sheet and notify of the change
8208 bool wxRichTextBuffer::SetStyleSheetAndNotify(wxRichTextStyleSheet
* sheet
)
8210 wxRichTextStyleSheet
* oldSheet
= GetStyleSheet();
8212 wxWindowID winid
= wxID_ANY
;
8213 if (GetRichTextCtrl())
8214 winid
= GetRichTextCtrl()->GetId();
8216 wxRichTextEvent
event(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACING
, winid
);
8217 event
.SetEventObject(GetRichTextCtrl());
8218 event
.SetContainer(GetRichTextCtrl()->GetFocusObject());
8219 event
.SetOldStyleSheet(oldSheet
);
8220 event
.SetNewStyleSheet(sheet
);
8223 if (SendEvent(event
) && !event
.IsAllowed())
8225 if (sheet
!= oldSheet
)
8231 if (oldSheet
&& oldSheet
!= sheet
)
8234 SetStyleSheet(sheet
);
8236 event
.SetEventType(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACED
);
8237 event
.SetOldStyleSheet(NULL
);
8240 return SendEvent(event
);
8243 /// Set renderer, deleting old one
8244 void wxRichTextBuffer::SetRenderer(wxRichTextRenderer
* renderer
)
8248 sm_renderer
= renderer
;
8251 /// Hit-testing: returns a flag indicating hit test details, plus
8252 /// information about position
8253 int wxRichTextBuffer::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
8255 int ret
= wxRichTextParagraphLayoutBox::HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
8256 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
8262 textPosition
= m_ownRange
.GetEnd()-1;
8265 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
;
8269 void wxRichTextBuffer::SetFontScale(double fontScale
)
8271 m_fontScale
= fontScale
;
8272 m_fontTable
.SetFontScale(fontScale
);
8275 void wxRichTextBuffer::SetDimensionScale(double dimScale
)
8277 m_dimensionScale
= dimScale
;
8280 bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxRichTextAttr
& bulletAttr
, const wxRect
& rect
)
8282 if (bulletAttr
.GetTextColour().IsOk())
8284 wxCheckSetPen(dc
, wxPen(bulletAttr
.GetTextColour()));
8285 wxCheckSetBrush(dc
, wxBrush(bulletAttr
.GetTextColour()));
8289 wxCheckSetPen(dc
, *wxBLACK_PEN
);
8290 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
8294 if (bulletAttr
.HasFont())
8296 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(bulletAttr
);
8299 font
= (*wxNORMAL_FONT
);
8301 wxCheckSetFont(dc
, font
);
8303 int charHeight
= dc
.GetCharHeight();
8305 int bulletWidth
= (int) (((float) charHeight
) * wxRichTextBuffer::GetBulletProportion());
8306 int bulletHeight
= bulletWidth
;
8310 // Calculate the top position of the character (as opposed to the whole line height)
8311 int y
= rect
.y
+ (rect
.height
- charHeight
);
8313 // Calculate where the bullet should be positioned
8314 y
= y
+ (charHeight
+1)/2 - (bulletHeight
+1)/2;
8316 // The margin between a bullet and text.
8317 int margin
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer::GetBulletRightMargin());
8319 if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
)
8320 x
= rect
.x
+ rect
.width
- bulletWidth
- margin
;
8321 else if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
)
8322 x
= x
+ (rect
.width
)/2 - bulletWidth
/2;
8324 if (bulletAttr
.GetBulletName() == wxT("standard/square"))
8326 dc
.DrawRectangle(x
, y
, bulletWidth
, bulletHeight
);
8328 else if (bulletAttr
.GetBulletName() == wxT("standard/diamond"))
8331 pts
[0].x
= x
; pts
[0].y
= y
+ bulletHeight
/2;
8332 pts
[1].x
= x
+ bulletWidth
/2; pts
[1].y
= y
;
8333 pts
[2].x
= x
+ bulletWidth
; pts
[2].y
= y
+ bulletHeight
/2;
8334 pts
[3].x
= x
+ bulletWidth
/2; pts
[3].y
= y
+ bulletHeight
;
8336 dc
.DrawPolygon(4, pts
);
8338 else if (bulletAttr
.GetBulletName() == wxT("standard/triangle"))
8341 pts
[0].x
= x
; pts
[0].y
= y
;
8342 pts
[1].x
= x
+ bulletWidth
; pts
[1].y
= y
+ bulletHeight
/2;
8343 pts
[2].x
= x
; pts
[2].y
= y
+ bulletHeight
;
8345 dc
.DrawPolygon(3, pts
);
8347 else if (bulletAttr
.GetBulletName() == wxT("standard/circle-outline"))
8349 wxCheckSetBrush(dc
, *wxTRANSPARENT_BRUSH
);
8350 dc
.DrawEllipse(x
, y
, bulletWidth
, bulletHeight
);
8352 else // "standard/circle", and catch-all
8354 dc
.DrawEllipse(x
, y
, bulletWidth
, bulletHeight
);
8360 bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxRichTextAttr
& attr
, const wxRect
& rect
, const wxString
& text
)
8365 if ((attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
) && !attr
.GetBulletFont().IsEmpty() && attr
.HasFont())
8367 wxRichTextAttr fontAttr
;
8368 if (attr
.HasFontPixelSize())
8369 fontAttr
.SetFontPixelSize(attr
.GetFontSize());
8371 fontAttr
.SetFontPointSize(attr
.GetFontSize());
8372 fontAttr
.SetFontStyle(attr
.GetFontStyle());
8373 fontAttr
.SetFontWeight(attr
.GetFontWeight());
8374 fontAttr
.SetFontUnderlined(attr
.GetFontUnderlined());
8375 fontAttr
.SetFontFaceName(attr
.GetBulletFont());
8376 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(fontAttr
);
8378 else if (attr
.HasFont())
8379 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(attr
);
8381 font
= (*wxNORMAL_FONT
);
8383 wxCheckSetFont(dc
, font
);
8385 if (attr
.GetTextColour().IsOk())
8386 dc
.SetTextForeground(attr
.GetTextColour());
8388 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
8390 int charHeight
= dc
.GetCharHeight();
8392 dc
.GetTextExtent(text
, & tw
, & th
);
8396 // Calculate the top position of the character (as opposed to the whole line height)
8397 int y
= rect
.y
+ (rect
.height
- charHeight
);
8399 // The margin between a bullet and text.
8400 int margin
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer::GetBulletRightMargin());
8402 if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
)
8403 x
= (rect
.x
+ rect
.width
) - tw
- margin
;
8404 else if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
)
8405 x
= x
+ (rect
.width
)/2 - tw
/2;
8407 dc
.DrawText(text
, x
, y
);
8415 bool wxRichTextStdRenderer::DrawBitmapBullet(wxRichTextParagraph
* WXUNUSED(paragraph
), wxDC
& WXUNUSED(dc
), const wxRichTextAttr
& WXUNUSED(attr
), const wxRect
& WXUNUSED(rect
))
8417 // Currently unimplemented. The intention is to store bitmaps by name in a media store associated
8418 // with the buffer. The store will allow retrieval from memory, disk or other means.
8422 /// Enumerate the standard bullet names currently supported
8423 bool wxRichTextStdRenderer::EnumerateStandardBulletNames(wxArrayString
& bulletNames
)
8425 bulletNames
.Add(wxTRANSLATE("standard/circle"));
8426 bulletNames
.Add(wxTRANSLATE("standard/circle-outline"));
8427 bulletNames
.Add(wxTRANSLATE("standard/square"));
8428 bulletNames
.Add(wxTRANSLATE("standard/diamond"));
8429 bulletNames
.Add(wxTRANSLATE("standard/triangle"));
8438 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBox
, wxRichTextParagraphLayoutBox
)
8440 wxRichTextBox::wxRichTextBox(wxRichTextObject
* parent
):
8441 wxRichTextParagraphLayoutBox(parent
)
8446 bool wxRichTextBox::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8451 // TODO: if the active object in the control, draw an indication.
8452 // We need to add the concept of active object, and not just focus object,
8453 // so we can apply commands (properties, delete, ...) to objects such as text boxes and images.
8454 // Ultimately we would like to be able to interactively resize an active object
8455 // using drag handles.
8456 return wxRichTextParagraphLayoutBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8460 void wxRichTextBox::Copy(const wxRichTextBox
& obj
)
8462 wxRichTextParagraphLayoutBox::Copy(obj
);
8465 // Edit properties via a GUI
8466 bool wxRichTextBox::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8468 wxRichTextObjectPropertiesDialog
boxDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Box Properties"));
8469 boxDlg
.SetAttributes(GetAttributes());
8471 if (boxDlg
.ShowModal() == wxID_OK
)
8473 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
8474 // indeterminate in the object.
8475 boxDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
8486 IMPLEMENT_DYNAMIC_CLASS(wxRichTextField
, wxRichTextParagraphLayoutBox
)
8488 wxRichTextField::wxRichTextField(const wxString
& fieldType
, wxRichTextObject
* parent
):
8489 wxRichTextParagraphLayoutBox(parent
)
8491 SetFieldType(fieldType
);
8495 bool wxRichTextField::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8500 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8501 if (fieldType
&& fieldType
->Draw(this, dc
, context
, range
, selection
, rect
, descent
, style
))
8504 // Fallback; but don't draw guidelines.
8505 style
&= ~wxRICHTEXT_DRAW_GUIDELINES
;
8506 return wxRichTextParagraphLayoutBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8509 bool wxRichTextField::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
)
8511 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8512 if (fieldType
&& fieldType
->Layout(this, dc
, context
, rect
, parentRect
, style
))
8516 return wxRichTextParagraphLayoutBox::Layout(dc
, context
, rect
, parentRect
, style
);
8519 bool wxRichTextField::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
8521 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8523 return fieldType
->GetRangeSize((wxRichTextField
*) this, range
, size
, descent
, dc
, context
, flags
, position
, partialExtents
);
8525 return wxRichTextParagraphLayoutBox::GetRangeSize(range
, size
, descent
, dc
, context
, flags
, position
, partialExtents
);
8529 void wxRichTextField::CalculateRange(long start
, long& end
)
8532 wxRichTextParagraphLayoutBox::CalculateRange(start
, end
);
8534 wxRichTextObject::CalculateRange(start
, end
);
8538 void wxRichTextField::Copy(const wxRichTextField
& obj
)
8540 wxRichTextParagraphLayoutBox::Copy(obj
);
8542 UpdateField(GetBuffer());
8545 // Edit properties via a GUI
8546 bool wxRichTextField::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8548 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8550 return fieldType
->EditProperties(this, parent
, buffer
);
8555 bool wxRichTextField::CanEditProperties() const
8557 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8559 return fieldType
->CanEditProperties((wxRichTextField
*) this);
8564 wxString
wxRichTextField::GetPropertiesMenuLabel() const
8566 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8568 return fieldType
->GetPropertiesMenuLabel((wxRichTextField
*) this);
8570 return wxEmptyString
;
8573 bool wxRichTextField::UpdateField(wxRichTextBuffer
* buffer
)
8575 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8577 return fieldType
->UpdateField(buffer
, (wxRichTextField
*) this);
8582 bool wxRichTextField::IsTopLevel() const
8584 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8586 return fieldType
->IsTopLevel((wxRichTextField
*) this);
8591 IMPLEMENT_CLASS(wxRichTextFieldType
, wxObject
)
8593 IMPLEMENT_CLASS(wxRichTextFieldTypeStandard
, wxRichTextFieldType
)
8595 wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString
& name
, const wxString
& label
, int displayStyle
)
8601 SetDisplayStyle(displayStyle
);
8604 wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString
& name
, const wxBitmap
& bitmap
, int displayStyle
)
8610 SetDisplayStyle(displayStyle
);
8613 void wxRichTextFieldTypeStandard::Init()
8615 m_displayStyle
= wxRICHTEXT_FIELD_STYLE_RECTANGLE
;
8616 m_font
= wxFont(6, wxFONTFAMILY_SWISS
, wxFONTSTYLE_NORMAL
, wxFONTWEIGHT_NORMAL
);
8617 m_textColour
= *wxWHITE
;
8618 m_borderColour
= *wxBLACK
;
8619 m_backgroundColour
= *wxBLACK
;
8620 m_verticalPadding
= 1;
8621 m_horizontalPadding
= 3;
8622 m_horizontalMargin
= 2;
8623 m_verticalMargin
= 0;
8626 void wxRichTextFieldTypeStandard::Copy(const wxRichTextFieldTypeStandard
& field
)
8628 wxRichTextFieldType::Copy(field
);
8630 m_label
= field
.m_label
;
8631 m_displayStyle
= field
.m_displayStyle
;
8632 m_font
= field
.m_font
;
8633 m_textColour
= field
.m_textColour
;
8634 m_borderColour
= field
.m_borderColour
;
8635 m_backgroundColour
= field
.m_backgroundColour
;
8636 m_verticalPadding
= field
.m_verticalPadding
;
8637 m_horizontalPadding
= field
.m_horizontalPadding
;
8638 m_horizontalMargin
= field
.m_horizontalMargin
;
8639 m_bitmap
= field
.m_bitmap
;
8642 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
))
8644 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_COMPOSITE
)
8645 return false; // USe default composite drawing
8646 else // if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_RECTANGLE || m_displayStyle == wxRICHTEXT_FIELD_STYLE_NOBORDER)
8650 wxPen
borderPen(m_borderColour
, 1, wxSOLID
);
8651 wxBrush
backgroundBrush(m_backgroundColour
);
8652 wxColour
textColour(m_textColour
);
8654 if (selection
.WithinSelection(obj
->GetRange().GetStart(), obj
))
8656 wxColour
highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
));
8657 wxColour
highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
));
8659 borderPen
= wxPen(highlightTextColour
, 1, wxSOLID
);
8660 backgroundBrush
= wxBrush(highlightColour
);
8662 wxCheckSetBrush(dc
, backgroundBrush
);
8663 wxCheckSetPen(dc
, wxPen(highlightColour
, 1, wxSOLID
));
8664 dc
.DrawRectangle(rect
);
8667 if (m_displayStyle
!= wxRICHTEXT_FIELD_STYLE_NO_BORDER
)
8670 // objectRect is the area where the content is drawn, after margins around it have been taken into account
8671 wxRect objectRect
= wxRect(wxPoint(rect
.x
+ m_horizontalMargin
, rect
.y
+ wxMax(0, rect
.height
- descent
- obj
->GetCachedSize().y
)),
8672 wxSize(obj
->GetCachedSize().x
- 2*m_horizontalMargin
- borderSize
, obj
->GetCachedSize().y
));
8674 // clientArea is where the text is actually written
8675 wxRect clientArea
= objectRect
;
8677 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_RECTANGLE
)
8679 dc
.SetPen(borderPen
);
8680 dc
.SetBrush(backgroundBrush
);
8681 dc
.DrawRoundedRectangle(objectRect
, 4.0);
8683 else if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_START_TAG
)
8685 int arrowLength
= objectRect
.height
/2;
8686 clientArea
.width
-= (arrowLength
- m_horizontalPadding
);
8689 pts
[0].x
= objectRect
.x
; pts
[0].y
= objectRect
.y
;
8690 pts
[1].x
= objectRect
.x
+ objectRect
.width
- arrowLength
; pts
[1].y
= objectRect
.y
;
8691 pts
[2].x
= objectRect
.x
+ objectRect
.width
; pts
[2].y
= objectRect
.y
+ (objectRect
.height
/2);
8692 pts
[3].x
= objectRect
.x
+ objectRect
.width
- arrowLength
; pts
[3].y
= objectRect
.y
+ objectRect
.height
;
8693 pts
[4].x
= objectRect
.x
; pts
[4].y
= objectRect
.y
+ objectRect
.height
;
8694 dc
.SetPen(borderPen
);
8695 dc
.SetBrush(backgroundBrush
);
8696 dc
.DrawPolygon(5, pts
);
8698 else if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_END_TAG
)
8700 int arrowLength
= objectRect
.height
/2;
8701 clientArea
.width
-= (arrowLength
- m_horizontalPadding
);
8702 clientArea
.x
+= (arrowLength
- m_horizontalPadding
);
8705 pts
[0].x
= objectRect
.x
+ objectRect
.width
; pts
[0].y
= objectRect
.y
;
8706 pts
[1].x
= objectRect
.x
+ arrowLength
; pts
[1].y
= objectRect
.y
;
8707 pts
[2].x
= objectRect
.x
; pts
[2].y
= objectRect
.y
+ (objectRect
.height
/2);
8708 pts
[3].x
= objectRect
.x
+ arrowLength
; pts
[3].y
= objectRect
.y
+ objectRect
.height
;
8709 pts
[4].x
= objectRect
.x
+ objectRect
.width
; pts
[4].y
= objectRect
.y
+ objectRect
.height
;
8710 dc
.SetPen(borderPen
);
8711 dc
.SetBrush(backgroundBrush
);
8712 dc
.DrawPolygon(5, pts
);
8715 if (m_bitmap
.IsOk())
8717 int x
= clientArea
.x
+ (clientArea
.width
- m_bitmap
.GetWidth())/2;
8718 int y
= clientArea
.y
+ m_verticalPadding
;
8719 dc
.DrawBitmap(m_bitmap
, x
, y
, true);
8721 if (selection
.WithinSelection(obj
->GetRange().GetStart(), obj
))
8723 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
8724 wxCheckSetPen(dc
, *wxBLACK_PEN
);
8725 dc
.SetLogicalFunction(wxINVERT
);
8726 dc
.DrawRectangle(wxRect(x
, y
, m_bitmap
.GetWidth(), m_bitmap
.GetHeight()));
8727 dc
.SetLogicalFunction(wxCOPY
);
8732 wxString
label(m_label
);
8733 if (label
.IsEmpty())
8735 int w
, h
, maxDescent
;
8737 dc
.GetTextExtent(m_label
, & w
, &h
, & maxDescent
);
8738 dc
.SetTextForeground(textColour
);
8740 int x
= clientArea
.x
+ (clientArea
.width
- w
)/2;
8741 int y
= clientArea
.y
+ (clientArea
.height
- (h
- maxDescent
))/2;
8742 dc
.DrawText(m_label
, x
, y
);
8749 bool wxRichTextFieldTypeStandard::Layout(wxRichTextField
* obj
, wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& WXUNUSED(rect
), const wxRect
& WXUNUSED(parentRect
), int style
)
8751 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_COMPOSITE
)
8752 return false; // USe default composite layout
8754 wxSize size
= GetSize(obj
, dc
, context
, style
);
8755 obj
->SetCachedSize(size
);
8756 obj
->SetMinSize(size
);
8757 obj
->SetMaxSize(size
);
8761 bool wxRichTextFieldTypeStandard::GetRangeSize(wxRichTextField
* obj
, const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
8763 if (IsTopLevel(obj
))
8764 return obj
->wxRichTextParagraphLayoutBox::GetRangeSize(range
, size
, descent
, dc
, context
, flags
, position
);
8767 wxSize sz
= GetSize(obj
, dc
, context
, 0);
8771 if (partialExtents
->GetCount() > 0)
8772 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
8775 partialExtents
->Add(lastSize
+ sz
.x
);
8782 wxSize
wxRichTextFieldTypeStandard::GetSize(wxRichTextField
* WXUNUSED(obj
), wxDC
& dc
, wxRichTextDrawingContext
& WXUNUSED(context
), int WXUNUSED(style
)) const
8785 int w
= 0, h
= 0, maxDescent
= 0;
8788 if (m_bitmap
.IsOk())
8790 w
= m_bitmap
.GetWidth();
8791 h
= m_bitmap
.GetHeight();
8793 sz
= wxSize(w
+ m_horizontalMargin
*2, h
+ m_verticalMargin
*2);
8797 wxString
label(m_label
);
8798 if (label
.IsEmpty())
8801 dc
.GetTextExtent(label
, & w
, &h
, & maxDescent
);
8803 sz
= wxSize(w
+ m_horizontalPadding
*2 + m_horizontalMargin
*2, h
+ m_verticalPadding
*2 + m_verticalMargin
*2);
8806 if (m_displayStyle
!= wxRICHTEXT_FIELD_STYLE_NO_BORDER
)
8808 sz
.x
+= borderSize
*2;
8809 sz
.y
+= borderSize
*2;
8812 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_START_TAG
|| m_displayStyle
== wxRICHTEXT_FIELD_STYLE_END_TAG
)
8814 // Add space for the arrow
8815 sz
.x
+= (sz
.y
/2 - m_horizontalPadding
);
8821 IMPLEMENT_DYNAMIC_CLASS(wxRichTextCell
, wxRichTextBox
)
8823 wxRichTextCell::wxRichTextCell(wxRichTextObject
* parent
):
8824 wxRichTextBox(parent
)
8829 bool wxRichTextCell::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8831 return wxRichTextBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8835 void wxRichTextCell::Copy(const wxRichTextCell
& obj
)
8837 wxRichTextBox::Copy(obj
);
8840 // Edit properties via a GUI
8841 bool wxRichTextCell::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8843 // We need to gather common attributes for all selected cells.
8845 wxRichTextTable
* table
= wxDynamicCast(GetParent(), wxRichTextTable
);
8846 bool multipleCells
= false;
8847 wxRichTextAttr attr
;
8849 if (table
&& buffer
&& buffer
->GetRichTextCtrl() && buffer
->GetRichTextCtrl()->GetSelection().IsValid() &&
8850 buffer
->GetRichTextCtrl()->GetSelection().GetContainer() == GetParent())
8852 wxRichTextAttr clashingAttr
, absentAttr
;
8853 const wxRichTextSelection
& sel
= buffer
->GetRichTextCtrl()->GetSelection();
8855 int selectedCellCount
= 0;
8856 for (i
= 0; i
< sel
.GetCount(); i
++)
8858 const wxRichTextRange
& range
= sel
[i
];
8859 wxRichTextCell
* cell
= table
->GetCell(range
.GetStart());
8862 wxRichTextAttr cellStyle
= cell
->GetAttributes();
8864 CollectStyle(attr
, cellStyle
, clashingAttr
, absentAttr
);
8866 selectedCellCount
++;
8869 multipleCells
= selectedCellCount
> 1;
8873 attr
= GetAttributes();
8878 caption
= _("Multiple Cell Properties");
8880 caption
= _("Cell Properties");
8882 wxRichTextObjectPropertiesDialog
cellDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, caption
);
8883 cellDlg
.SetAttributes(attr
);
8885 wxRichTextSizePage
* sizePage
= wxDynamicCast(cellDlg
.FindPage(wxCLASSINFO(wxRichTextSizePage
)), wxRichTextSizePage
);
8888 // We don't want position and floating controls for a cell.
8889 sizePage
->ShowPositionControls(false);
8890 sizePage
->ShowFloatingControls(false);
8893 if (cellDlg
.ShowModal() == wxID_OK
)
8897 const wxRichTextSelection
& sel
= buffer
->GetRichTextCtrl()->GetSelection();
8898 // Apply the style; we interpret indeterminate attributes as 'don't touch this attribute'
8899 // since it may represent clashing attributes across multiple objects.
8900 table
->SetCellStyle(sel
, attr
);
8903 // For a single object, indeterminate attributes set by the user should be reflected in the
8904 // actual object style, so pass the wxRICHTEXT_SETSTYLE_RESET flag to assign
8905 // the style directly instead of applying (which ignores indeterminate attributes,
8906 // leaving them as they were).
8907 cellDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
8914 WX_DEFINE_OBJARRAY(wxRichTextObjectPtrArrayArray
)
8916 IMPLEMENT_DYNAMIC_CLASS(wxRichTextTable
, wxRichTextBox
)
8918 wxRichTextTable::wxRichTextTable(wxRichTextObject
* parent
): wxRichTextBox(parent
)
8924 // Draws the object.
8925 bool wxRichTextTable::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8927 return wxRichTextBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8930 WX_DECLARE_OBJARRAY(wxRect
, wxRichTextRectArray
);
8931 WX_DEFINE_OBJARRAY(wxRichTextRectArray
);
8933 // Lays the object out. rect is the space available for layout. Often it will
8934 // be the specified overall space for this object, if trying to constrain
8935 // layout to a particular size, or it could be the total space available in the
8936 // parent. rect is the overall size, so we must subtract margins and padding.
8937 // to get the actual available space.
8938 bool wxRichTextTable::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& WXUNUSED(parentRect
), int style
)
8940 SetPosition(rect
.GetPosition());
8942 // TODO: the meaty bit. Calculate sizes of all cells and rows. Try to use
8943 // minimum size if within alloted size, then divide up remaining size
8944 // between rows/cols.
8947 wxRichTextBuffer
* buffer
= GetBuffer();
8948 if (buffer
) scale
= buffer
->GetScale();
8950 wxRect availableSpace
= GetAvailableContentArea(dc
, context
, rect
);
8951 wxTextAttrDimensionConverter
converter(dc
, scale
, availableSpace
.GetSize());
8953 wxRichTextAttr
attr(GetAttributes());
8954 context
.ApplyVirtualAttributes(attr
, this);
8956 // If we have no fixed table size, and assuming we're not pushed for
8957 // space, then we don't have to try to stretch the table to fit the contents.
8958 bool stretchToFitTableWidth
= false;
8960 int tableWidth
= rect
.width
;
8961 if (attr
.GetTextBoxAttr().GetWidth().IsValid())
8963 tableWidth
= converter
.GetPixels(attr
.GetTextBoxAttr().GetWidth());
8965 // Fixed table width, so we do want to stretch columns out if necessary.
8966 stretchToFitTableWidth
= true;
8968 // Shouldn't be able to exceed the size passed to this function
8969 tableWidth
= wxMin(rect
.width
, tableWidth
);
8972 // Get internal padding
8973 int paddingLeft
= 0, paddingTop
= 0;
8974 if (attr
.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
8975 paddingLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetLeft());
8976 if (attr
.GetTextBoxAttr().GetPadding().GetTop().IsValid())
8977 paddingTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetTop());
8979 // Assume that left and top padding are also used for inter-cell padding.
8980 int paddingX
= paddingLeft
;
8981 int paddingY
= paddingTop
;
8983 int totalLeftMargin
= 0, totalRightMargin
= 0, totalTopMargin
= 0, totalBottomMargin
= 0;
8984 GetTotalMargin(dc
, buffer
, attr
, totalLeftMargin
, totalRightMargin
, totalTopMargin
, totalBottomMargin
);
8986 // Internal table width - the area for content
8987 int internalTableWidth
= tableWidth
- totalLeftMargin
- totalRightMargin
;
8989 int rowCount
= m_cells
.GetCount();
8990 if (m_colCount
== 0 || rowCount
== 0)
8992 wxRect
overallRect(rect
.x
, rect
.y
, totalLeftMargin
+ totalRightMargin
, totalTopMargin
+ totalBottomMargin
);
8993 SetCachedSize(overallRect
.GetSize());
8995 // Zero content size
8996 SetMinSize(overallRect
.GetSize());
8997 SetMaxSize(GetMinSize());
9001 // The final calculated widths
9002 wxArrayInt colWidths
;
9003 colWidths
.Add(0, m_colCount
);
9005 wxArrayInt absoluteColWidths
;
9006 absoluteColWidths
.Add(0, m_colCount
);
9008 wxArrayInt percentageColWidths
;
9009 percentageColWidths
.Add(0, m_colCount
);
9010 // wxArrayInt percentageColWidthsSpanning(m_colCount);
9011 // These are only relevant when the first column contains spanning information.
9012 // wxArrayInt columnSpans(m_colCount); // Each contains 1 for non-spanning cell, > 1 for spanning cell.
9013 wxArrayInt maxColWidths
;
9014 maxColWidths
.Add(0, m_colCount
);
9015 wxArrayInt minColWidths
;
9016 minColWidths
.Add(0, m_colCount
);
9018 wxSize
tableSize(tableWidth
, 0);
9022 for (i
= 0; i
< m_colCount
; i
++)
9024 absoluteColWidths
[i
] = 0;
9025 // absoluteColWidthsSpanning[i] = 0;
9026 percentageColWidths
[i
] = -1;
9027 // percentageColWidthsSpanning[i] = -1;
9029 maxColWidths
[i
] = 0;
9030 minColWidths
[i
] = 0;
9031 // columnSpans[i] = 1;
9034 // (0) Determine which cells are visible according to spans
9036 // __________________
9041 // |------------------|
9042 // |__________________| 4
9044 // To calculate cell visibility:
9045 // First find all spanning cells. Build an array of span records with start x, y and end x, y.
9046 // Then for each cell, test whether we're within one of those cells, and unless we're at the start of
9047 // that cell, hide the cell.
9049 // We can also use this array to match the size of spanning cells to the grid. Or just do
9050 // this when we iterate through all cells.
9052 // 0.1: add spanning cells to an array
9053 wxRichTextRectArray rectArray
;
9054 for (j
= 0; j
< m_rowCount
; j
++)
9056 for (i
= 0; i
< m_colCount
; i
++)
9058 wxRichTextBox
* cell
= GetCell(j
, i
);
9059 int colSpan
= 1, rowSpan
= 1;
9060 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9061 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9062 if (cell
->GetProperties().HasProperty(wxT("rowspan")))
9063 rowSpan
= cell
->GetProperties().GetPropertyLong(wxT("rowspan"));
9064 if (colSpan
> 1 || rowSpan
> 1)
9066 rectArray
.Add(wxRect(i
, j
, colSpan
, rowSpan
));
9070 // 0.2: find which cells are subsumed by a spanning cell
9071 for (j
= 0; j
< m_rowCount
; j
++)
9073 for (i
= 0; i
< m_colCount
; i
++)
9075 wxRichTextBox
* cell
= GetCell(j
, i
);
9076 if (rectArray
.GetCount() == 0)
9082 int colSpan
= 1, rowSpan
= 1;
9083 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9084 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9085 if (cell
->GetProperties().HasProperty(wxT("rowspan")))
9086 rowSpan
= cell
->GetProperties().GetPropertyLong(wxT("rowspan"));
9087 if (colSpan
> 1 || rowSpan
> 1)
9089 // Assume all spanning cells are shown
9095 for (k
= 0; k
< (int) rectArray
.GetCount(); k
++)
9097 if (rectArray
[k
].Contains(wxPoint(i
, j
)))
9109 // TODO: find the first spanned cell in each row that spans the most columns and doesn't
9110 // overlap with a spanned cell starting at a previous column position.
9111 // This means we need to keep an array of rects so we can check. However
9112 // it does also mean that some spans simply may not be taken into account
9113 // where there are different spans happening on different rows. In these cases,
9114 // they will simply be as wide as their constituent columns.
9116 // (1) Do an initial layout for all cells to get minimum and maximum size, and get
9117 // the absolute or percentage width of each column.
9119 for (j
= 0; j
< m_rowCount
; j
++)
9121 // First get the overall margins so we can calculate percentage widths based on
9122 // the available content space for all cells on the row
9124 int overallRowContentMargin
= 0;
9125 int visibleCellCount
= 0;
9127 for (i
= 0; i
< m_colCount
; i
++)
9129 wxRichTextBox
* cell
= GetCell(j
, i
);
9130 if (cell
->IsShown())
9132 int cellTotalLeftMargin
= 0, cellTotalRightMargin
= 0, cellTotalTopMargin
= 0, cellTotalBottomMargin
= 0;
9133 GetTotalMargin(dc
, buffer
, cell
->GetAttributes(), cellTotalLeftMargin
, cellTotalRightMargin
, cellTotalTopMargin
, cellTotalBottomMargin
);
9135 overallRowContentMargin
+= (cellTotalLeftMargin
+ cellTotalRightMargin
);
9136 visibleCellCount
++;
9140 // Add in inter-cell padding
9141 overallRowContentMargin
+= ((visibleCellCount
-1) * paddingX
);
9143 int rowContentWidth
= internalTableWidth
- overallRowContentMargin
;
9144 wxSize
rowTableSize(rowContentWidth
, 0);
9145 wxTextAttrDimensionConverter
converter(dc
, scale
, rowTableSize
);
9147 for (i
= 0; i
< m_colCount
; i
++)
9149 wxRichTextBox
* cell
= GetCell(j
, i
);
9150 if (cell
->IsShown())
9153 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9154 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9156 // Lay out cell to find min/max widths
9157 cell
->Invalidate(wxRICHTEXT_ALL
);
9158 cell
->Layout(dc
, context
, availableSpace
, availableSpace
, style
);
9162 int absoluteCellWidth
= -1;
9163 int percentageCellWidth
= -1;
9165 // I think we need to calculate percentages from the internal table size,
9166 // minus the padding between cells which we'll need to calculate from the
9167 // (number of VISIBLE cells - 1)*paddingX. Then percentages that add up to 100%
9168 // will add up to 100%. In CSS, the width specifies the cell's content rect width,
9169 // so if we want to conform to that we'll need to add in the overall cell margins.
9170 // However, this will make it difficult to specify percentages that add up to
9171 // 100% and still fit within the table width.
9172 // Let's say two cells have 50% width. They have 10 pixels of overall margin each.
9173 // The table content rect is 500 pixels and the inter-cell padding is 20 pixels.
9174 // If we're using internal content size for the width, we would calculate the
9175 // the overall cell width for n cells as:
9176 // (500 - 20*(n-1) - overallCellMargin1 - overallCellMargin2 - ...) * percentage / 100
9177 // + thisOverallCellMargin
9178 // = 500 - 20 - 10 - 10) * 0.5 + 10 = 240 pixels overall cell width.
9179 // Adding this back, we get 240 + 240 + 20 = 500 pixels.
9181 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9183 int w
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetWidth());
9184 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
9186 percentageCellWidth
= w
;
9190 absoluteCellWidth
= w
;
9192 // Override absolute width with minimum width if necessary
9193 if (cell
->GetMinSize().x
> 0 && absoluteCellWidth
!=1 && cell
->GetMinSize().x
> absoluteCellWidth
)
9194 absoluteCellWidth
= cell
->GetMinSize().x
;
9197 if (absoluteCellWidth
!= -1)
9199 if (absoluteCellWidth
> absoluteColWidths
[i
])
9200 absoluteColWidths
[i
] = absoluteCellWidth
;
9203 if (percentageCellWidth
!= -1)
9205 if (percentageCellWidth
> percentageColWidths
[i
])
9206 percentageColWidths
[i
] = percentageCellWidth
;
9209 if (colSpan
== 1 && cell
->GetMinSize().x
&& cell
->GetMinSize().x
> minColWidths
[i
])
9210 minColWidths
[i
] = cell
->GetMinSize().x
;
9211 if (colSpan
== 1 && cell
->GetMaxSize().x
&& cell
->GetMaxSize().x
> maxColWidths
[i
])
9212 maxColWidths
[i
] = cell
->GetMaxSize().x
;
9218 // (2) Allocate initial column widths from minimum widths, absolute values and proportions
9219 // TODO: simply merge this into (1).
9220 for (i
= 0; i
< m_colCount
; i
++)
9222 if (absoluteColWidths
[i
] > 0)
9224 colWidths
[i
] = absoluteColWidths
[i
];
9226 else if (percentageColWidths
[i
] > 0)
9228 colWidths
[i
] = percentageColWidths
[i
];
9230 // This is rubbish - we calculated the absolute widths from percentages, so
9231 // we can't do it again here.
9232 //colWidths[i] = (int) (double(percentageColWidths[i]) * double(tableWidth) / 100.0 + 0.5);
9236 // (3) Process absolute or proportional widths of spanning columns,
9237 // now that we know what our fixed column widths are going to be.
9238 // Spanned cells will try to adjust columns so the span will fit.
9239 // Even existing fixed column widths can be expanded if necessary.
9240 // Actually, currently fixed columns widths aren't adjusted; instead,
9241 // the algorithm favours earlier rows and adjusts unspecified column widths
9242 // the first time only. After that, we can't know whether the column has been
9243 // specified explicitly or not. (We could make a note if necessary.)
9244 for (j
= 0; j
< m_rowCount
; j
++)
9246 // First get the overall margins so we can calculate percentage widths based on
9247 // the available content space for all cells on the row
9249 int overallRowContentMargin
= 0;
9250 int visibleCellCount
= 0;
9252 for (i
= 0; i
< m_colCount
; i
++)
9254 wxRichTextBox
* cell
= GetCell(j
, i
);
9255 if (cell
->IsShown())
9257 int cellTotalLeftMargin
= 0, cellTotalRightMargin
= 0, cellTotalTopMargin
= 0, cellTotalBottomMargin
= 0;
9258 GetTotalMargin(dc
, buffer
, cell
->GetAttributes(), cellTotalLeftMargin
, cellTotalRightMargin
, cellTotalTopMargin
, cellTotalBottomMargin
);
9260 overallRowContentMargin
+= (cellTotalLeftMargin
+ cellTotalRightMargin
);
9261 visibleCellCount
++;
9265 // Add in inter-cell padding
9266 overallRowContentMargin
+= ((visibleCellCount
-1) * paddingX
);
9268 int rowContentWidth
= internalTableWidth
- overallRowContentMargin
;
9269 wxSize
rowTableSize(rowContentWidth
, 0);
9270 wxTextAttrDimensionConverter
converter(dc
, scale
, rowTableSize
);
9272 for (i
= 0; i
< m_colCount
; i
++)
9274 wxRichTextBox
* cell
= GetCell(j
, i
);
9275 if (cell
->IsShown())
9278 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9279 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9283 int spans
= wxMin(colSpan
, m_colCount
- i
);
9287 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9289 cellWidth
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetWidth());
9290 // Override absolute width with minimum width if necessary
9291 if (cell
->GetMinSize().x
> 0 && cellWidth
!=1 && cell
->GetMinSize().x
> cellWidth
)
9292 cellWidth
= cell
->GetMinSize().x
;
9296 // Do we want to do this? It's the only chance we get to
9297 // use the cell's min/max sizes, so we need to work out
9298 // how we're going to balance the unspecified spanning cell
9299 // width with the possibility more-constrained constituent cell widths.
9300 // Say there's a tiny bitmap giving it a max width of 10 pixels. We
9301 // don't want to constraint all the spanned columns to fit into this cell.
9302 // OK, let's say that if any of the constituent columns don't fit,
9303 // then we simply stop constraining the columns; instead, we'll just fit the spanning
9304 // cells to the columns later.
9305 cellWidth
= cell
->GetMinSize().x
;
9306 if (cell
->GetMaxSize().x
> cellWidth
)
9307 cellWidth
= cell
->GetMaxSize().x
;
9310 // Subtract the padding between cells
9311 int spanningWidth
= cellWidth
;
9312 spanningWidth
-= paddingX
* (spans
-1);
9314 if (spanningWidth
> 0)
9316 // Now share the spanning width between columns within that span
9317 // TODO: take into account min widths of columns within the span
9318 int spanningWidthLeft
= spanningWidth
;
9319 int stretchColCount
= 0;
9320 for (k
= i
; k
< (i
+spans
); k
++)
9322 if (colWidths
[k
] > 0) // absolute or proportional width has been specified
9323 spanningWidthLeft
-= colWidths
[k
];
9327 // Now divide what's left between the remaining columns
9329 if (stretchColCount
> 0)
9330 colShare
= spanningWidthLeft
/ stretchColCount
;
9331 int colShareRemainder
= spanningWidthLeft
- (colShare
* stretchColCount
);
9333 // If fixed-width columns are currently too big, then we'll later
9334 // stretch the spanned cell to fit.
9336 if (spanningWidthLeft
> 0)
9338 for (k
= i
; k
< (i
+spans
); k
++)
9340 if (colWidths
[k
] <= 0) // absolute or proportional width has not been specified
9342 int newWidth
= colShare
;
9343 if (k
== (i
+spans
-1))
9344 newWidth
+= colShareRemainder
; // ensure all pixels are filled
9345 colWidths
[k
] = newWidth
;
9356 // (4) Next, share any remaining space out between columns that have not yet been calculated.
9357 // TODO: take into account min widths of columns within the span
9358 int tableWidthMinusPadding
= internalTableWidth
- (m_colCount
-1)*paddingX
;
9359 int widthLeft
= tableWidthMinusPadding
;
9360 int stretchColCount
= 0;
9361 for (i
= 0; i
< m_colCount
; i
++)
9363 // TODO: we need to take into account min widths.
9364 // Subtract min width from width left, then
9365 // add the colShare to the min width
9366 if (colWidths
[i
] > 0) // absolute or proportional width has been specified
9367 widthLeft
-= colWidths
[i
];
9370 if (minColWidths
[i
] > 0)
9371 widthLeft
-= minColWidths
[i
];
9377 // Now divide what's left between the remaining columns
9379 if (stretchColCount
> 0)
9380 colShare
= widthLeft
/ stretchColCount
;
9381 int colShareRemainder
= widthLeft
- (colShare
* stretchColCount
);
9383 // Check we don't have enough space, in which case shrink all columns, overriding
9384 // any absolute/proportional widths
9385 // TODO: actually we would like to divide up the shrinkage according to size.
9386 // How do we calculate the proportions that will achieve this?
9387 // Could first choose an arbitrary value for stretching cells, and then calculate
9388 // factors to multiply each width by.
9389 // TODO: want to record this fact and pass to an iteration that tries e.g. min widths
9390 if (widthLeft
< 0 || (stretchToFitTableWidth
&& (stretchColCount
== 0)))
9392 colShare
= tableWidthMinusPadding
/ m_colCount
;
9393 colShareRemainder
= tableWidthMinusPadding
- (colShare
* m_colCount
);
9394 for (i
= 0; i
< m_colCount
; i
++)
9397 minColWidths
[i
] = 0;
9401 // We have to adjust the columns if either we need to shrink the
9402 // table to fit the parent/table width, or we explicitly set the
9403 // table width and need to stretch out the table.
9404 if (widthLeft
< 0 || stretchToFitTableWidth
)
9406 for (i
= 0; i
< m_colCount
; i
++)
9408 if (colWidths
[i
] <= 0) // absolute or proportional width has not been specified
9410 if (minColWidths
[i
] > 0)
9411 colWidths
[i
] = minColWidths
[i
] + colShare
;
9413 colWidths
[i
] = colShare
;
9414 if (i
== (m_colCount
-1))
9415 colWidths
[i
] += colShareRemainder
; // ensure all pixels are filled
9420 // TODO: if spanned cells have no specified or max width, make them the
9421 // as big as the columns they span. Do this for all spanned cells in all
9422 // rows, of course. Size any spanned cells left over at the end - even if they
9423 // have width > 0, make sure they're limited to the appropriate column edge.
9427 Sort out confusion between content width
9428 and overall width later. For now, assume we specify overall width.
9430 So, now we've laid out the table to fit into the given space
9431 and have used specified widths and minimum widths.
9433 Now we need to consider how we will try to take maximum width into account.
9437 // (??) TODO: take max width into account
9439 // (6) Lay out all cells again with the current values
9442 int y
= availableSpace
.y
;
9443 for (j
= 0; j
< m_rowCount
; j
++)
9445 int x
= availableSpace
.x
; // TODO: take into account centering etc.
9446 int maxCellHeight
= 0;
9447 int maxSpecifiedCellHeight
= 0;
9449 wxArrayInt actualWidths
;
9450 actualWidths
.Add(0, m_colCount
);
9452 wxTextAttrDimensionConverter
converter(dc
, scale
);
9453 for (i
= 0; i
< m_colCount
; i
++)
9455 wxRichTextCell
* cell
= GetCell(j
, i
);
9456 if (cell
->IsShown())
9458 // Get max specified cell height
9459 // Don't handle percentages for height
9460 if (cell
->GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && cell
->GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() != wxTEXT_ATTR_UNITS_PERCENTAGE
)
9462 int h
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetHeight());
9463 if (h
> maxSpecifiedCellHeight
)
9464 maxSpecifiedCellHeight
= h
;
9467 if (colWidths
[i
] > 0) // absolute or proportional width has been specified
9470 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9471 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9473 wxRect availableCellSpace
;
9475 // TODO: take into acount spans
9478 // Calculate the size of this spanning cell from its constituent columns
9480 int spans
= wxMin(colSpan
, m_colCount
- i
);
9481 for (k
= i
; k
< spans
; k
++)
9487 availableCellSpace
= wxRect(x
, y
, xx
, -1);
9490 availableCellSpace
= wxRect(x
, y
, colWidths
[i
], -1);
9492 // Store actual width so we can force cell to be the appropriate width on the final loop
9493 actualWidths
[i
] = availableCellSpace
.GetWidth();
9496 cell
->Invalidate(wxRICHTEXT_ALL
);
9497 cell
->Layout(dc
, context
, availableCellSpace
, availableSpace
, style
);
9499 // TODO: use GetCachedSize().x to compute 'natural' size
9501 x
+= (availableCellSpace
.GetWidth() + paddingX
);
9502 if (cell
->GetCachedSize().y
> maxCellHeight
)
9503 maxCellHeight
= cell
->GetCachedSize().y
;
9508 maxCellHeight
= wxMax(maxCellHeight
, maxSpecifiedCellHeight
);
9510 for (i
= 0; i
< m_colCount
; i
++)
9512 wxRichTextCell
* cell
= GetCell(j
, i
);
9513 if (cell
->IsShown())
9515 wxRect availableCellSpace
= wxRect(cell
->GetPosition(), wxSize(actualWidths
[i
], maxCellHeight
));
9516 // Lay out cell with new height
9517 cell
->Invalidate(wxRICHTEXT_ALL
);
9518 cell
->Layout(dc
, context
, availableCellSpace
, availableSpace
, style
);
9520 // Make sure the cell size really is the appropriate size,
9521 // not the calculated box size
9522 cell
->SetCachedSize(wxSize(actualWidths
[i
], maxCellHeight
));
9524 maxRight
= wxMax(maxRight
, cell
->GetPosition().x
+ cell
->GetCachedSize().x
);
9529 if (j
< (m_rowCount
-1))
9533 // We need to add back the margins etc.
9535 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
9536 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxRight
- availableSpace
.x
, y
- availableSpace
.y
));
9537 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
9538 SetCachedSize(marginRect
.GetSize());
9541 // TODO: calculate max size
9543 SetMaxSize(GetCachedSize());
9546 // TODO: calculate min size
9548 SetMinSize(GetCachedSize());
9551 // TODO: currently we use either a fixed table width or the parent's size.
9552 // We also want to be able to calculate the table width from its content,
9553 // whether using fixed column widths or cell content min/max width.
9554 // Probably need a boolean flag to say whether we need to stretch cells
9555 // to fit the table width, or to simply use min/max cell widths. The
9556 // trouble with this is that if cell widths are not specified, they
9557 // will be tiny; we could use arbitrary defaults but this seems unsatisfactory.
9558 // Anyway, ignoring that problem, we probably need to factor layout into a function
9559 // that can can calculate the maximum unconstrained layout in case table size is
9560 // not specified. Then LayoutToBestSize() can choose to use either parent size to
9561 // constrain Layout(), or the previously-calculated max size to constraint layout.
9566 // Finds the absolute position and row height for the given character position
9567 bool wxRichTextTable::FindPosition(wxDC
& dc
, wxRichTextDrawingContext
& context
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
9569 wxRichTextCell
* child
= GetCell(index
+1);
9572 // Find the position at the start of the child cell, since the table doesn't
9573 // have any caret position of its own.
9574 return child
->FindPosition(dc
, context
, -1, pt
, height
, forceLineStart
);
9580 // Get the cell at the given character position (in the range of the table).
9581 wxRichTextCell
* wxRichTextTable::GetCell(long pos
) const
9583 int row
= 0, col
= 0;
9584 if (GetCellRowColumnPosition(pos
, row
, col
))
9586 return GetCell(row
, col
);
9592 // Get the row/column for a given character position
9593 bool wxRichTextTable::GetCellRowColumnPosition(long pos
, int& row
, int& col
) const
9595 if (m_colCount
== 0 || m_rowCount
== 0)
9598 row
= (int) (pos
/ m_colCount
);
9599 col
= pos
- (row
* m_colCount
);
9601 wxASSERT(row
< m_rowCount
&& col
< m_colCount
);
9603 if (row
< m_rowCount
&& col
< m_colCount
)
9609 // Calculate range, taking row/cell ordering into account instead of relying
9610 // on list ordering.
9611 void wxRichTextTable::CalculateRange(long start
, long& end
)
9613 long current
= start
;
9614 long lastEnd
= current
;
9623 for (i
= 0; i
< m_rowCount
; i
++)
9625 for (j
= 0; j
< m_colCount
; j
++)
9627 wxRichTextCell
* child
= GetCell(i
, j
);
9632 child
->CalculateRange(current
, childEnd
);
9635 current
= childEnd
+ 1;
9640 // A top-level object always has a range of size 1,
9641 // because its children don't count at this level.
9643 m_range
.SetRange(start
, start
);
9645 // An object with no children has zero length
9646 if (m_children
.GetCount() == 0)
9648 m_ownRange
.SetRange(0, lastEnd
);
9651 // Gets the range size.
9652 bool wxRichTextTable::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
9654 return wxRichTextBox::GetRangeSize(range
, size
, descent
, dc
, context
, flags
, position
, partialExtents
);
9657 // Deletes content in the given range.
9658 bool wxRichTextTable::DeleteRange(const wxRichTextRange
& WXUNUSED(range
))
9660 // TODO: implement deletion of cells
9664 // Gets any text in this object for the given range.
9665 wxString
wxRichTextTable::GetTextForRange(const wxRichTextRange
& range
) const
9667 return wxRichTextBox::GetTextForRange(range
);
9670 // Copies this object.
9671 void wxRichTextTable::Copy(const wxRichTextTable
& obj
)
9673 wxRichTextBox::Copy(obj
);
9677 m_rowCount
= obj
.m_rowCount
;
9678 m_colCount
= obj
.m_colCount
;
9680 m_cells
.Add(wxRichTextObjectPtrArray(), m_rowCount
);
9683 for (i
= 0; i
< m_rowCount
; i
++)
9685 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
9686 for (j
= 0; j
< m_colCount
; j
++)
9688 wxRichTextCell
* cell
= wxDynamicCast(obj
.GetCell(i
, j
)->Clone(), wxRichTextCell
);
9696 void wxRichTextTable::ClearTable()
9702 bool wxRichTextTable::CreateTable(int rows
, int cols
)
9709 m_cells
.Add(wxRichTextObjectPtrArray(), rows
);
9712 for (i
= 0; i
< rows
; i
++)
9714 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
9715 for (j
= 0; j
< cols
; j
++)
9717 wxRichTextCell
* cell
= new wxRichTextCell
;
9719 cell
->AddParagraph(wxEmptyString
);
9728 wxRichTextCell
* wxRichTextTable::GetCell(int row
, int col
) const
9730 wxASSERT(row
< m_rowCount
);
9731 wxASSERT(col
< m_colCount
);
9733 if (row
< m_rowCount
&& col
< m_colCount
)
9735 wxRichTextObjectPtrArray
& colArray
= m_cells
[row
];
9736 wxRichTextObject
* obj
= colArray
[col
];
9737 return wxDynamicCast(obj
, wxRichTextCell
);
9743 // Returns a selection object specifying the selections between start and end character positions.
9744 // For example, a table would deduce what cells (of range length 1) are selected when dragging across the table.
9745 wxRichTextSelection
wxRichTextTable::GetSelection(long start
, long end
) const
9747 wxRichTextSelection selection
;
9748 selection
.SetContainer((wxRichTextTable
*) this);
9757 wxASSERT( start
>= 0 && end
< (m_colCount
* m_rowCount
));
9759 if (end
>= (m_colCount
* m_rowCount
))
9762 // We need to find the rectangle of cells that is described by the rectangle
9763 // with start, end as the diagonal. Make sure we don't add cells that are
9764 // not currenty visible because they are overlapped by spanning cells.
9766 --------------------------
9767 | 0 | 1 | 2 | 3 | 4 |
9768 --------------------------
9769 | 5 | 6 | 7 | 8 | 9 |
9770 --------------------------
9771 | 10 | 11 | 12 | 13 | 14 |
9772 --------------------------
9773 | 15 | 16 | 17 | 18 | 19 |
9774 --------------------------
9776 Let's say we select 6 -> 18.
9778 Left and right edge cols of rectangle are 1 and 3 inclusive. Find least/greatest to find
9779 which is left and which is right.
9781 Top and bottom edge rows are 1 and 3 inclusive. Again, find least/greatest to find top and bottom.
9783 Now go through rows from 1 to 3 and only add cells that are (a) within above column range
9789 int leftCol
= start
- m_colCount
* int(start
/m_colCount
);
9790 int rightCol
= end
- m_colCount
* int(end
/m_colCount
);
9792 int topRow
= int(start
/m_colCount
);
9793 int bottomRow
= int(end
/m_colCount
);
9795 if (leftCol
> rightCol
)
9802 if (topRow
> bottomRow
)
9804 int tmp
= bottomRow
;
9810 for (i
= topRow
; i
<= bottomRow
; i
++)
9812 for (j
= leftCol
; j
<= rightCol
; j
++)
9814 wxRichTextCell
* cell
= GetCell(i
, j
);
9815 if (cell
&& cell
->IsShown())
9816 selection
.Add(cell
->GetRange());
9823 // Sets the attributes for the cells specified by the selection.
9824 bool wxRichTextTable::SetCellStyle(const wxRichTextSelection
& selection
, const wxRichTextAttr
& style
, int flags
)
9826 if (selection
.GetContainer() != this)
9829 wxRichTextBuffer
* buffer
= GetBuffer();
9830 bool haveControl
= (buffer
&& buffer
->GetRichTextCtrl() != NULL
);
9831 bool withUndo
= haveControl
&& ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
9834 buffer
->BeginBatchUndo(_("Set Cell Style"));
9836 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
9839 wxRichTextCell
* cell
= wxDynamicCast(node
->GetData(), wxRichTextCell
);
9840 if (cell
&& selection
.WithinSelection(cell
->GetRange().GetStart()))
9841 SetStyle(cell
, style
, flags
);
9842 node
= node
->GetNext();
9845 // Do action, or delay it until end of batch.
9847 buffer
->EndBatchUndo();
9852 bool wxRichTextTable::DeleteRows(int startRow
, int noRows
)
9854 wxASSERT((startRow
+ noRows
) < m_rowCount
);
9855 if ((startRow
+ noRows
) >= m_rowCount
)
9859 for (i
= startRow
; i
< (startRow
+noRows
); i
++)
9861 wxRichTextObjectPtrArray
& colArray
= m_cells
[startRow
];
9862 for (j
= 0; j
< (int) colArray
.GetCount(); j
++)
9864 wxRichTextObject
* cell
= colArray
[j
];
9865 RemoveChild(cell
, true);
9868 // Keep deleting at the same position, since we move all
9870 m_cells
.RemoveAt(startRow
);
9873 m_rowCount
= m_rowCount
- noRows
;
9878 bool wxRichTextTable::DeleteColumns(int startCol
, int noCols
)
9880 wxASSERT((startCol
+ noCols
) < m_colCount
);
9881 if ((startCol
+ noCols
) >= m_colCount
)
9884 bool deleteRows
= (noCols
== m_colCount
);
9887 for (i
= 0; i
< m_rowCount
; i
++)
9889 wxRichTextObjectPtrArray
& colArray
= m_cells
[deleteRows
? 0 : i
];
9890 for (j
= startCol
; j
< (startCol
+noCols
); j
++)
9892 wxRichTextObject
* cell
= colArray
[j
];
9893 RemoveChild(cell
, true);
9897 m_cells
.RemoveAt(0);
9902 m_colCount
= m_colCount
- noCols
;
9907 bool wxRichTextTable::AddRows(int startRow
, int noRows
, const wxRichTextAttr
& attr
)
9909 wxASSERT(startRow
<= m_rowCount
);
9910 if (startRow
> m_rowCount
)
9914 for (i
= 0; i
< noRows
; i
++)
9917 if (startRow
== m_rowCount
)
9919 m_cells
.Add(wxRichTextObjectPtrArray());
9920 idx
= m_cells
.GetCount() - 1;
9924 m_cells
.Insert(wxRichTextObjectPtrArray(), startRow
+i
);
9928 wxRichTextObjectPtrArray
& colArray
= m_cells
[idx
];
9929 for (j
= 0; j
< m_colCount
; j
++)
9931 wxRichTextCell
* cell
= new wxRichTextCell
;
9932 cell
->GetAttributes() = attr
;
9939 m_rowCount
= m_rowCount
+ noRows
;
9943 bool wxRichTextTable::AddColumns(int startCol
, int noCols
, const wxRichTextAttr
& attr
)
9945 wxASSERT(startCol
<= m_colCount
);
9946 if (startCol
> m_colCount
)
9950 for (i
= 0; i
< m_rowCount
; i
++)
9952 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
9953 for (j
= 0; j
< noCols
; j
++)
9955 wxRichTextCell
* cell
= new wxRichTextCell
;
9956 cell
->GetAttributes() = attr
;
9960 if (startCol
== m_colCount
)
9963 colArray
.Insert(cell
, startCol
+j
);
9967 m_colCount
= m_colCount
+ noCols
;
9972 // Edit properties via a GUI
9973 bool wxRichTextTable::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
9975 wxRichTextObjectPropertiesDialog
boxDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Table Properties"));
9976 boxDlg
.SetAttributes(GetAttributes());
9978 if (boxDlg
.ShowModal() == wxID_OK
)
9980 boxDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
9988 * Module to initialise and clean up handlers
9991 class wxRichTextModule
: public wxModule
9993 DECLARE_DYNAMIC_CLASS(wxRichTextModule
)
9995 wxRichTextModule() {}
9998 wxRichTextBuffer::SetRenderer(new wxRichTextStdRenderer
);
9999 wxRichTextBuffer::InitStandardHandlers();
10000 wxRichTextParagraph::InitDefaultTabs();
10002 wxRichTextXMLHandler::RegisterNodeName(wxT("text"), wxT("wxRichTextPlainText"));
10003 wxRichTextXMLHandler::RegisterNodeName(wxT("symbol"), wxT("wxRichTextPlainText"));
10004 wxRichTextXMLHandler::RegisterNodeName(wxT("image"), wxT("wxRichTextImage"));
10005 wxRichTextXMLHandler::RegisterNodeName(wxT("paragraph"), wxT("wxRichTextParagraph"));
10006 wxRichTextXMLHandler::RegisterNodeName(wxT("paragraphlayout"), wxT("wxRichTextParagraphLayoutBox"));
10007 wxRichTextXMLHandler::RegisterNodeName(wxT("textbox"), wxT("wxRichTextBox"));
10008 wxRichTextXMLHandler::RegisterNodeName(wxT("cell"), wxT("wxRichTextCell"));
10009 wxRichTextXMLHandler::RegisterNodeName(wxT("table"), wxT("wxRichTextTable"));
10010 wxRichTextXMLHandler::RegisterNodeName(wxT("field"), wxT("wxRichTextField"));
10016 wxRichTextBuffer::CleanUpHandlers();
10017 wxRichTextBuffer::CleanUpDrawingHandlers();
10018 wxRichTextBuffer::CleanUpFieldTypes();
10019 wxRichTextXMLHandler::ClearNodeToClassMap();
10020 wxRichTextDecimalToRoman(-1);
10021 wxRichTextParagraph::ClearDefaultTabs();
10022 wxRichTextCtrl::ClearAvailableFontNames();
10023 wxRichTextBuffer::SetRenderer(NULL
);
10027 IMPLEMENT_DYNAMIC_CLASS(wxRichTextModule
, wxModule
)
10030 // If the richtext lib is dynamically loaded after the app has already started
10031 // (such as from wxPython) then the built-in module system will not init this
10032 // module. Provide this function to do it manually.
10033 void wxRichTextModuleInit()
10035 wxModule
* module = new wxRichTextModule
;
10037 wxModule::RegisterModule(module);
10042 * Commands for undo/redo
10046 wxRichTextCommand::wxRichTextCommand(const wxString
& name
, wxRichTextCommandId id
, wxRichTextBuffer
* buffer
,
10047 wxRichTextParagraphLayoutBox
* container
, wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
): wxCommand(true, name
)
10049 /* wxRichTextAction* action = */ new wxRichTextAction(this, name
, id
, buffer
, container
, ctrl
, ignoreFirstTime
);
10052 wxRichTextCommand::wxRichTextCommand(const wxString
& name
): wxCommand(true, name
)
10056 wxRichTextCommand::~wxRichTextCommand()
10061 void wxRichTextCommand::AddAction(wxRichTextAction
* action
)
10063 if (!m_actions
.Member(action
))
10064 m_actions
.Append(action
);
10067 bool wxRichTextCommand::Do()
10069 for (wxList::compatibility_iterator node
= m_actions
.GetFirst(); node
; node
= node
->GetNext())
10071 wxRichTextAction
* action
= (wxRichTextAction
*) node
->GetData();
10078 bool wxRichTextCommand::Undo()
10080 for (wxList::compatibility_iterator node
= m_actions
.GetLast(); node
; node
= node
->GetPrevious())
10082 wxRichTextAction
* action
= (wxRichTextAction
*) node
->GetData();
10089 void wxRichTextCommand::ClearActions()
10091 WX_CLEAR_LIST(wxList
, m_actions
);
10095 * Individual action
10099 wxRichTextAction::wxRichTextAction(wxRichTextCommand
* cmd
, const wxString
& name
, wxRichTextCommandId id
,
10100 wxRichTextBuffer
* buffer
, wxRichTextParagraphLayoutBox
* container
,
10101 wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
)
10105 m_containerAddress
.Create(buffer
, container
);
10106 m_ignoreThis
= ignoreFirstTime
;
10111 m_newParagraphs
.SetDefaultStyle(buffer
->GetDefaultStyle());
10112 m_newParagraphs
.SetBasicStyle(buffer
->GetBasicStyle());
10114 cmd
->AddAction(this);
10117 wxRichTextAction::~wxRichTextAction()
10123 // Returns the container that this action refers to, using the container address and top-level buffer.
10124 wxRichTextParagraphLayoutBox
* wxRichTextAction::GetContainer() const
10126 wxRichTextParagraphLayoutBox
* container
= wxDynamicCast(GetContainerAddress().GetObject(m_buffer
), wxRichTextParagraphLayoutBox
);
10131 void wxRichTextAction::CalculateRefreshOptimizations(wxArrayInt
& optimizationLineCharPositions
, wxArrayInt
& optimizationLineYPositions
)
10133 // Store a list of line start character and y positions so we can figure out which area
10134 // we need to refresh
10136 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10137 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10138 wxASSERT(container
!= NULL
);
10142 // NOTE: we're assuming that the buffer is laid out correctly at this point.
10143 // If we had several actions, which only invalidate and leave layout until the
10144 // paint handler is called, then this might not be true. So we may need to switch
10145 // optimisation on only when we're simply adding text and not simultaneously
10146 // deleting a selection, for example. Or, we make sure the buffer is laid out correctly
10147 // first, but of course this means we'll be doing it twice.
10148 if (!m_buffer
->IsDirty() && m_ctrl
) // can only do optimisation if the buffer is already laid out correctly
10150 wxSize clientSize
= m_ctrl
->GetUnscaledSize(m_ctrl
->GetClientSize());
10151 wxPoint firstVisiblePt
= m_ctrl
->GetUnscaledPoint(m_ctrl
->GetFirstVisiblePoint());
10152 int lastY
= firstVisiblePt
.y
+ clientSize
.y
;
10154 wxRichTextParagraph
* para
= container
->GetParagraphAtPosition(GetRange().GetStart());
10155 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(para
);
10158 wxRichTextParagraph
* child
= (wxRichTextParagraph
*) node
->GetData();
10159 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
10162 wxRichTextLine
* line
= node2
->GetData();
10163 wxPoint pt
= line
->GetAbsolutePosition();
10164 wxRichTextRange range
= line
->GetAbsoluteRange();
10168 node2
= wxRichTextLineList::compatibility_iterator();
10169 node
= wxRichTextObjectList::compatibility_iterator();
10171 else if (range
.GetStart() > GetPosition() && pt
.y
>= firstVisiblePt
.y
)
10173 optimizationLineCharPositions
.Add(range
.GetStart());
10174 optimizationLineYPositions
.Add(pt
.y
);
10178 node2
= node2
->GetNext();
10182 node
= node
->GetNext();
10188 bool wxRichTextAction::Do()
10190 m_buffer
->Modify(true);
10192 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10193 wxASSERT(container
!= NULL
);
10199 case wxRICHTEXT_INSERT
:
10201 // Store a list of line start character and y positions so we can figure out which area
10202 // we need to refresh
10203 wxArrayInt optimizationLineCharPositions
;
10204 wxArrayInt optimizationLineYPositions
;
10206 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10207 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
10210 container
->InsertFragment(GetRange().GetStart(), m_newParagraphs
);
10211 container
->UpdateRanges();
10213 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10214 // Layout() would stop prematurely at the top level.
10215 container
->InvalidateHierarchy(wxRichTextRange(wxMax(0, GetRange().GetStart()-1), GetRange().GetEnd()));
10217 long newCaretPosition
= GetPosition() + m_newParagraphs
.GetOwnRange().GetLength();
10219 // Character position to caret position
10220 newCaretPosition
--;
10222 // Don't take into account the last newline
10223 if (m_newParagraphs
.GetPartialParagraph())
10224 newCaretPosition
--;
10226 if (m_newParagraphs
.GetChildren().GetCount() > 1)
10228 wxRichTextObject
* p
= (wxRichTextObject
*) m_newParagraphs
.GetChildren().GetLast()->GetData();
10229 if (p
->GetRange().GetLength() == 1)
10230 newCaretPosition
--;
10233 newCaretPosition
= wxMin(newCaretPosition
, (container
->GetOwnRange().GetEnd()-1));
10235 UpdateAppearance(newCaretPosition
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */);
10237 wxRichTextEvent
cmdEvent(
10238 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED
,
10239 m_ctrl
? m_ctrl
->GetId() : -1);
10240 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10241 cmdEvent
.SetRange(GetRange());
10242 cmdEvent
.SetPosition(GetRange().GetStart());
10243 cmdEvent
.SetContainer(container
);
10245 m_buffer
->SendEvent(cmdEvent
);
10249 case wxRICHTEXT_DELETE
:
10251 wxArrayInt optimizationLineCharPositions
;
10252 wxArrayInt optimizationLineYPositions
;
10254 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10255 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
10258 container
->DeleteRange(GetRange());
10259 container
->UpdateRanges();
10260 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10261 // Layout() would stop prematurely at the top level.
10262 container
->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
10264 long caretPos
= GetRange().GetStart()-1;
10265 if (caretPos
>= container
->GetOwnRange().GetEnd())
10268 UpdateAppearance(caretPos
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */);
10270 wxRichTextEvent
cmdEvent(
10271 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED
,
10272 m_ctrl
? m_ctrl
->GetId() : -1);
10273 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10274 cmdEvent
.SetRange(GetRange());
10275 cmdEvent
.SetPosition(GetRange().GetStart());
10276 cmdEvent
.SetContainer(container
);
10278 m_buffer
->SendEvent(cmdEvent
);
10282 case wxRICHTEXT_CHANGE_STYLE
:
10283 case wxRICHTEXT_CHANGE_PROPERTIES
:
10285 ApplyParagraphs(GetNewParagraphs());
10287 // Invalidate the whole buffer if there were floating objects
10288 if (container
->GetFloatingObjectCount() > 0)
10289 m_buffer
->InvalidateHierarchy(wxRICHTEXT_ALL
);
10292 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10293 // Layout() would stop prematurely at the top level.
10294 container
->InvalidateHierarchy(GetRange());
10297 UpdateAppearance(GetPosition());
10299 wxRichTextEvent
cmdEvent(
10300 m_cmdId
== wxRICHTEXT_CHANGE_STYLE
? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
: wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED
,
10301 m_ctrl
? m_ctrl
->GetId() : -1);
10302 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10303 cmdEvent
.SetRange(GetRange());
10304 cmdEvent
.SetPosition(GetRange().GetStart());
10305 cmdEvent
.SetContainer(container
);
10307 m_buffer
->SendEvent(cmdEvent
);
10311 case wxRICHTEXT_CHANGE_ATTRIBUTES
:
10313 wxRichTextObject
* obj
= m_objectAddress
.GetObject(m_buffer
); // container->GetChildAtPosition(GetRange().GetStart());
10316 wxRichTextAttr oldAttr
= obj
->GetAttributes();
10317 obj
->GetAttributes() = m_attributes
;
10318 m_attributes
= oldAttr
;
10321 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10322 // Layout() would stop prematurely at the top level.
10323 // Invalidate the whole buffer if there were floating objects
10324 if (container
->GetFloatingObjectCount() > 0)
10325 m_buffer
->InvalidateHierarchy(wxRICHTEXT_ALL
);
10327 container
->InvalidateHierarchy(GetRange());
10329 UpdateAppearance(GetPosition());
10331 wxRichTextEvent
cmdEvent(
10332 wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
,
10333 m_ctrl
? m_ctrl
->GetId() : -1);
10334 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10335 cmdEvent
.SetRange(GetRange());
10336 cmdEvent
.SetPosition(GetRange().GetStart());
10337 cmdEvent
.SetContainer(container
);
10339 m_buffer
->SendEvent(cmdEvent
);
10343 case wxRICHTEXT_CHANGE_OBJECT
:
10345 wxRichTextObject
* obj
= m_objectAddress
.GetObject(m_buffer
);
10346 // wxRichTextObject* obj = container->GetChildAtPosition(GetRange().GetStart());
10347 if (obj
&& m_object
)
10349 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(obj
);
10352 wxRichTextObject
* obj
= node
->GetData();
10353 node
->SetData(m_object
);
10358 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10359 // Layout() would stop prematurely at the top level.
10360 // Invalidate the whole buffer if there were floating objects
10361 if (container
->GetFloatingObjectCount() > 0)
10362 m_buffer
->InvalidateHierarchy(wxRICHTEXT_ALL
);
10364 container
->InvalidateHierarchy(GetRange());
10366 UpdateAppearance(GetPosition());
10368 // TODO: send new kind of modification event
10379 bool wxRichTextAction::Undo()
10381 m_buffer
->Modify(true);
10383 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10384 wxASSERT(container
!= NULL
);
10390 case wxRICHTEXT_INSERT
:
10392 wxArrayInt optimizationLineCharPositions
;
10393 wxArrayInt optimizationLineYPositions
;
10395 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10396 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
10399 container
->DeleteRange(GetRange());
10400 container
->UpdateRanges();
10402 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10403 // Layout() would stop prematurely at the top level.
10404 container
->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
10406 long newCaretPosition
= GetPosition() - 1;
10408 UpdateAppearance(newCaretPosition
, true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */);
10410 wxRichTextEvent
cmdEvent(
10411 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED
,
10412 m_ctrl
? m_ctrl
->GetId() : -1);
10413 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10414 cmdEvent
.SetRange(GetRange());
10415 cmdEvent
.SetPosition(GetRange().GetStart());
10416 cmdEvent
.SetContainer(container
);
10418 m_buffer
->SendEvent(cmdEvent
);
10422 case wxRICHTEXT_DELETE
:
10424 wxArrayInt optimizationLineCharPositions
;
10425 wxArrayInt optimizationLineYPositions
;
10427 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10428 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
10431 container
->InsertFragment(GetRange().GetStart(), m_oldParagraphs
);
10432 container
->UpdateRanges();
10434 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10435 // Layout() would stop prematurely at the top level.
10436 container
->InvalidateHierarchy(GetRange());
10438 UpdateAppearance(GetPosition(), true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */);
10440 wxRichTextEvent
cmdEvent(
10441 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED
,
10442 m_ctrl
? m_ctrl
->GetId() : -1);
10443 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10444 cmdEvent
.SetRange(GetRange());
10445 cmdEvent
.SetPosition(GetRange().GetStart());
10446 cmdEvent
.SetContainer(container
);
10448 m_buffer
->SendEvent(cmdEvent
);
10452 case wxRICHTEXT_CHANGE_STYLE
:
10453 case wxRICHTEXT_CHANGE_PROPERTIES
:
10455 ApplyParagraphs(GetOldParagraphs());
10456 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10457 // Layout() would stop prematurely at the top level.
10458 container
->InvalidateHierarchy(GetRange());
10460 UpdateAppearance(GetPosition());
10462 wxRichTextEvent
cmdEvent(
10463 m_cmdId
== wxRICHTEXT_CHANGE_STYLE
? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
: wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED
,
10464 m_ctrl
? m_ctrl
->GetId() : -1);
10465 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10466 cmdEvent
.SetRange(GetRange());
10467 cmdEvent
.SetPosition(GetRange().GetStart());
10468 cmdEvent
.SetContainer(container
);
10470 m_buffer
->SendEvent(cmdEvent
);
10474 case wxRICHTEXT_CHANGE_ATTRIBUTES
:
10475 case wxRICHTEXT_CHANGE_OBJECT
:
10486 /// Update the control appearance
10487 void wxRichTextAction::UpdateAppearance(long caretPosition
, bool sendUpdateEvent
, wxArrayInt
* optimizationLineCharPositions
, wxArrayInt
* optimizationLineYPositions
, bool isDoCmd
)
10489 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10490 wxASSERT(container
!= NULL
);
10496 m_ctrl
->SetFocusObject(container
);
10497 m_ctrl
->SetCaretPosition(caretPosition
);
10499 if (!m_ctrl
->IsFrozen())
10501 wxRect containerRect
= container
->GetRect();
10503 m_ctrl
->LayoutContent();
10505 // Refresh everything if there were floating objects or the container changed size
10506 // (we can't yet optimize in these cases, since more complex interaction with other content occurs)
10507 if (container
->GetFloatingObjectCount() > 0 || (container
->GetParent() && containerRect
!= container
->GetRect()))
10509 m_ctrl
->Refresh(false);
10513 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10514 // Find refresh rectangle if we are in a position to optimise refresh
10515 if ((m_cmdId
== wxRICHTEXT_INSERT
|| m_cmdId
== wxRICHTEXT_DELETE
) && optimizationLineCharPositions
)
10519 wxSize clientSize
= m_ctrl
->GetUnscaledSize(m_ctrl
->GetClientSize());
10520 wxPoint firstVisiblePt
= m_ctrl
->GetUnscaledPoint(m_ctrl
->GetFirstVisiblePoint());
10522 // Start/end positions
10524 int lastY
= firstVisiblePt
.y
+ clientSize
.y
;
10526 bool foundEnd
= false;
10528 // position offset - how many characters were inserted
10529 int positionOffset
= GetRange().GetLength();
10531 // Determine whether this is Do or Undo, and adjust positionOffset accordingly
10532 if ((m_cmdId
== wxRICHTEXT_DELETE
&& isDoCmd
) || (m_cmdId
== wxRICHTEXT_INSERT
&& !isDoCmd
))
10533 positionOffset
= - positionOffset
;
10535 // find the first line which is being drawn at the same position as it was
10536 // before. Since we're talking about a simple insertion, we can assume
10537 // that the rest of the window does not need to be redrawn.
10539 wxRichTextParagraph
* para
= container
->GetParagraphAtPosition(GetPosition());
10540 // Since we support floating layout, we should redraw the whole para instead of just
10541 // the first line touching the invalid range.
10544 firstY
= para
->GetPosition().y
;
10547 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(para
);
10550 wxRichTextParagraph
* child
= (wxRichTextParagraph
*) node
->GetData();
10551 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
10554 wxRichTextLine
* line
= node2
->GetData();
10555 wxPoint pt
= line
->GetAbsolutePosition();
10556 wxRichTextRange range
= line
->GetAbsoluteRange();
10558 // we want to find the first line that is in the same position
10559 // as before. This will mean we're at the end of the changed text.
10561 if (pt
.y
> lastY
) // going past the end of the window, no more info
10563 node2
= wxRichTextLineList::compatibility_iterator();
10564 node
= wxRichTextObjectList::compatibility_iterator();
10566 // Detect last line in the buffer
10567 else if (!node2
->GetNext() && para
->GetRange().Contains(container
->GetOwnRange().GetEnd()))
10569 // If deleting text, make sure we refresh below as well as above
10570 if (positionOffset
>= 0)
10573 lastY
= pt
.y
+ line
->GetSize().y
;
10576 node2
= wxRichTextLineList::compatibility_iterator();
10577 node
= wxRichTextObjectList::compatibility_iterator();
10583 // search for this line being at the same position as before
10584 for (i
= 0; i
< optimizationLineCharPositions
->GetCount(); i
++)
10586 if (((*optimizationLineCharPositions
)[i
] + positionOffset
== range
.GetStart()) &&
10587 ((*optimizationLineYPositions
)[i
] == pt
.y
))
10589 // Stop, we're now the same as we were
10594 node2
= wxRichTextLineList::compatibility_iterator();
10595 node
= wxRichTextObjectList::compatibility_iterator();
10603 node2
= node2
->GetNext();
10607 node
= node
->GetNext();
10610 firstY
= wxMax(firstVisiblePt
.y
, firstY
);
10612 lastY
= firstVisiblePt
.y
+ clientSize
.y
;
10614 // Convert to device coordinates
10615 wxRect
rect(m_ctrl
->GetPhysicalPoint(m_ctrl
->GetScaledPoint(wxPoint(firstVisiblePt
.x
, firstY
))), m_ctrl
->GetScaledSize(wxSize(clientSize
.x
, lastY
- firstY
)));
10616 m_ctrl
->RefreshRect(rect
);
10620 m_ctrl
->Refresh(false);
10622 m_ctrl
->PositionCaret();
10624 // This causes styles to persist when doing programmatic
10625 // content creation except when Freeze/Thaw is used, so
10626 // disable this and check for the consequences.
10627 // m_ctrl->SetDefaultStyleToCursorStyle();
10629 if (sendUpdateEvent
)
10630 wxTextCtrl::SendTextUpdatedEvent(m_ctrl
);
10635 /// Replace the buffer paragraphs with the new ones.
10636 void wxRichTextAction::ApplyParagraphs(const wxRichTextParagraphLayoutBox
& fragment
)
10638 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10639 wxASSERT(container
!= NULL
);
10643 wxRichTextObjectList::compatibility_iterator node
= fragment
.GetChildren().GetFirst();
10646 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
10647 wxASSERT (para
!= NULL
);
10649 // We'll replace the existing paragraph by finding the paragraph at this position,
10650 // delete its node data, and setting a copy as the new node data.
10651 // TODO: make more efficient by simply swapping old and new paragraph objects.
10653 wxRichTextParagraph
* existingPara
= container
->GetParagraphAtPosition(para
->GetRange().GetStart());
10656 wxRichTextObjectList::compatibility_iterator bufferParaNode
= container
->GetChildren().Find(existingPara
);
10657 if (bufferParaNode
)
10659 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(*para
);
10660 newPara
->SetParent(container
);
10662 bufferParaNode
->SetData(newPara
);
10664 delete existingPara
;
10668 node
= node
->GetNext();
10675 * This stores beginning and end positions for a range of data.
10678 WX_DEFINE_OBJARRAY(wxRichTextRangeArray
);
10680 /// Limit this range to be within 'range'
10681 bool wxRichTextRange::LimitTo(const wxRichTextRange
& range
)
10683 if (m_start
< range
.m_start
)
10684 m_start
= range
.m_start
;
10686 if (m_end
> range
.m_end
)
10687 m_end
= range
.m_end
;
10693 * wxRichTextImage implementation
10694 * This object represents an image.
10697 IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage
, wxRichTextObject
)
10699 wxRichTextImage::wxRichTextImage(const wxImage
& image
, wxRichTextObject
* parent
, wxRichTextAttr
* charStyle
):
10700 wxRichTextObject(parent
)
10703 m_imageBlock
.MakeImageBlockDefaultQuality(image
, wxBITMAP_TYPE_PNG
);
10705 SetAttributes(*charStyle
);
10708 wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock
& imageBlock
, wxRichTextObject
* parent
, wxRichTextAttr
* charStyle
):
10709 wxRichTextObject(parent
)
10712 m_imageBlock
= imageBlock
;
10714 SetAttributes(*charStyle
);
10717 void wxRichTextImage::Init()
10719 m_originalImageSize
= wxSize(-1, -1);
10722 /// Create a cached image at the required size
10723 bool wxRichTextImage::LoadImageCache(wxDC
& dc
, bool resetCache
)
10725 if (!m_imageBlock
.IsOk())
10728 // If we have an original image size, use that to compute the cached bitmap size
10729 // instead of loading the image each time. This way we can avoid loading
10730 // the image so long as the new cached bitmap size hasn't changed.
10733 if (resetCache
|| m_originalImageSize
== wxSize(-1, -1))
10735 m_imageCache
= wxNullBitmap
;
10737 m_imageBlock
.Load(image
);
10741 m_originalImageSize
= wxSize(image
.GetWidth(), image
.GetHeight());
10744 int width
= m_originalImageSize
.GetWidth();
10745 int height
= m_originalImageSize
.GetHeight();
10747 int parentWidth
= 0;
10748 int parentHeight
= 0;
10751 int maxHeight
= -1;
10753 wxRichTextBuffer
* buffer
= GetBuffer();
10757 if (buffer
->GetRichTextCtrl())
10759 // Subtract borders
10760 sz
= buffer
->GetRichTextCtrl()->GetClientSize();
10762 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
10763 marginRect
= wxRect(0, 0, sz
.x
, sz
.y
);
10764 buffer
->GetBoxRects(dc
, buffer
, buffer
->GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
10766 sz
= contentRect
.GetSize();
10768 // Start with a maximum width of the control size, even if not specified by the content,
10769 // to minimize the amount of picture overlapping the right-hand side
10773 sz
= buffer
->GetCachedSize();
10774 parentWidth
= sz
.GetWidth();
10775 parentHeight
= sz
.GetHeight();
10778 if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetWidth().GetValue() > 0)
10780 if (parentWidth
> 0 && GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
10781 width
= (int) ((GetAttributes().GetTextBoxAttr().GetWidth().GetValue() * parentWidth
)/100.0);
10782 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
10783 width
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetWidth().GetValue());
10784 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
10785 width
= GetAttributes().GetTextBoxAttr().GetWidth().GetValue();
10788 // Limit to max width
10790 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() > 0)
10794 if (parentWidth
> 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
10795 mw
= (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() * parentWidth
)/100.0);
10796 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
10797 mw
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue());
10798 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
10799 mw
= GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue();
10801 // If we already have a smaller max width due to the constraints of the control size,
10802 // don't use the larger max width.
10803 if (mw
!= -1 && ((maxWidth
== -1) || (mw
< maxWidth
)))
10807 if (maxWidth
> 0 && width
> maxWidth
)
10810 // Preserve the aspect ratio
10811 if (width
!= m_originalImageSize
.GetWidth())
10812 height
= (int) (float(m_originalImageSize
.GetHeight()) * (float(width
)/float(m_originalImageSize
.GetWidth())));
10814 if (GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetHeight().GetValue() > 0)
10816 if (parentHeight
> 0 && GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
10817 height
= (int) ((GetAttributes().GetTextBoxAttr().GetHeight().GetValue() * parentHeight
)/100.0);
10818 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
10819 height
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetHeight().GetValue());
10820 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
10821 height
= GetAttributes().GetTextBoxAttr().GetHeight().GetValue();
10823 // Preserve the aspect ratio
10824 if (height
!= m_originalImageSize
.GetHeight())
10825 width
= (int) (float(m_originalImageSize
.GetWidth()) * (float(height
)/float(m_originalImageSize
.GetHeight())));
10828 // Limit to max height
10830 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() > 0)
10832 if (parentHeight
> 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
10833 maxHeight
= (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() * parentHeight
)/100.0);
10834 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
10835 maxHeight
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue());
10836 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
10837 maxHeight
= GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue();
10840 if (maxHeight
> 0 && height
> maxHeight
)
10842 height
= maxHeight
;
10844 // Preserve the aspect ratio
10845 if (height
!= m_originalImageSize
.GetHeight())
10846 width
= (int) (float(m_originalImageSize
.GetWidth()) * (float(height
)/float(m_originalImageSize
.GetHeight())));
10849 if (m_imageCache
.IsOk() && m_imageCache
.GetWidth() == width
&& m_imageCache
.GetHeight() == height
)
10851 // Do nothing, we didn't need to change the image cache
10857 m_imageBlock
.Load(image
);
10862 if (image
.GetWidth() == width
&& image
.GetHeight() == height
)
10863 m_imageCache
= wxBitmap(image
);
10866 // If the original width and height is small, e.g. 400 or below,
10867 // scale up and then down to improve image quality. This can make
10868 // a big difference, with not much performance hit.
10869 int upscaleThreshold
= 400;
10871 if (image
.GetWidth() <= upscaleThreshold
|| image
.GetHeight() <= upscaleThreshold
)
10873 img
= image
.Scale(image
.GetWidth()*2, image
.GetHeight()*2);
10874 img
.Rescale(width
, height
, wxIMAGE_QUALITY_HIGH
);
10877 img
= image
.Scale(width
, height
, wxIMAGE_QUALITY_HIGH
);
10878 m_imageCache
= wxBitmap(img
);
10882 return m_imageCache
.IsOk();
10886 bool wxRichTextImage::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& WXUNUSED(range
), const wxRichTextSelection
& selection
, const wxRect
& rect
, int WXUNUSED(descent
), int WXUNUSED(style
))
10891 // Don't need cached size AFAIK
10892 // wxSize size = GetCachedSize();
10893 if (!LoadImageCache(dc
))
10896 wxRichTextAttr
attr(GetAttributes());
10897 context
.ApplyVirtualAttributes(attr
, this);
10899 DrawBoxAttributes(dc
, GetBuffer(), attr
, wxRect(rect
.GetPosition(), GetCachedSize()));
10901 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
10902 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
10903 marginRect
= rect
; // outer rectangle, will calculate contentRect
10904 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
10906 dc
.DrawBitmap(m_imageCache
, contentRect
.x
, contentRect
.y
, true);
10908 if (selection
.WithinSelection(GetRange().GetStart(), this))
10910 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
10911 wxCheckSetPen(dc
, *wxBLACK_PEN
);
10912 dc
.SetLogicalFunction(wxINVERT
);
10913 dc
.DrawRectangle(contentRect
);
10914 dc
.SetLogicalFunction(wxCOPY
);
10920 /// Lay the item out
10921 bool wxRichTextImage::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& WXUNUSED(parentRect
), int WXUNUSED(style
))
10923 if (!LoadImageCache(dc
))
10926 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
10927 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
10928 contentRect
= wxRect(wxPoint(0,0), imageSize
);
10930 wxRichTextAttr
attr(GetAttributes());
10931 context
.ApplyVirtualAttributes(attr
, this);
10933 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
10935 wxSize overallSize
= marginRect
.GetSize();
10937 SetCachedSize(overallSize
);
10938 SetMaxSize(overallSize
);
10939 SetMinSize(overallSize
);
10940 SetPosition(rect
.GetPosition());
10945 /// Get/set the object size for the given range. Returns false if the range
10946 /// is invalid for this object.
10947 bool wxRichTextImage::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& WXUNUSED(descent
), wxDC
& dc
, wxRichTextDrawingContext
& context
, int WXUNUSED(flags
), wxPoint
WXUNUSED(position
), wxArrayInt
* partialExtents
) const
10949 if (!range
.IsWithin(GetRange()))
10952 if (!((wxRichTextImage
*)this)->LoadImageCache(dc
))
10954 size
.x
= 0; size
.y
= 0;
10955 if (partialExtents
)
10956 partialExtents
->Add(0);
10960 wxRichTextAttr
attr(GetAttributes());
10961 context
.ApplyVirtualAttributes(attr
, (wxRichTextObject
*) this);
10963 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
10964 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
10965 contentRect
= wxRect(wxPoint(0,0), imageSize
);
10966 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
10968 wxSize overallSize
= marginRect
.GetSize();
10970 if (partialExtents
)
10971 partialExtents
->Add(overallSize
.x
);
10973 size
= overallSize
;
10978 // Get the 'natural' size for an object. For an image, it would be the
10980 wxTextAttrSize
wxRichTextImage::GetNaturalSize() const
10982 wxTextAttrSize size
;
10983 if (GetImageCache().IsOk())
10985 size
.SetWidth(GetImageCache().GetWidth(), wxTEXT_ATTR_UNITS_PIXELS
);
10986 size
.SetHeight(GetImageCache().GetHeight(), wxTEXT_ATTR_UNITS_PIXELS
);
10993 void wxRichTextImage::Copy(const wxRichTextImage
& obj
)
10995 wxRichTextObject::Copy(obj
);
10997 m_imageBlock
= obj
.m_imageBlock
;
10998 m_originalImageSize
= obj
.m_originalImageSize
;
11001 /// Edit properties via a GUI
11002 bool wxRichTextImage::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
11004 wxRichTextObjectPropertiesDialog
imageDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Picture Properties"));
11005 imageDlg
.SetAttributes(GetAttributes());
11007 if (imageDlg
.ShowModal() == wxID_OK
)
11009 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
11010 // indeterminate in the object.
11011 imageDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
11023 /// Compare two attribute objects
11024 bool wxTextAttrEq(const wxRichTextAttr
& attr1
, const wxRichTextAttr
& attr2
)
11026 return (attr1
== attr2
);
11030 bool wxRichTextTabsEq(const wxArrayInt
& tabs1
, const wxArrayInt
& tabs2
)
11032 if (tabs1
.GetCount() != tabs2
.GetCount())
11036 for (i
= 0; i
< tabs1
.GetCount(); i
++)
11038 if (tabs1
[i
] != tabs2
[i
])
11044 bool wxRichTextApplyStyle(wxRichTextAttr
& destStyle
, const wxRichTextAttr
& style
, wxRichTextAttr
* compareWith
)
11046 return destStyle
.Apply(style
, compareWith
);
11049 // Remove attributes
11050 bool wxRichTextRemoveStyle(wxRichTextAttr
& destStyle
, const wxRichTextAttr
& style
)
11052 return destStyle
.RemoveStyle(style
);
11055 /// Combine two bitlists, specifying the bits of interest with separate flags.
11056 bool wxRichTextCombineBitlists(int& valueA
, int valueB
, int& flagsA
, int flagsB
)
11058 return wxRichTextAttr::CombineBitlists(valueA
, valueB
, flagsA
, flagsB
);
11061 /// Compare two bitlists
11062 bool wxRichTextBitlistsEqPartial(int valueA
, int valueB
, int flags
)
11064 return wxRichTextAttr::BitlistsEqPartial(valueA
, valueB
, flags
);
11067 /// Split into paragraph and character styles
11068 bool wxRichTextSplitParaCharStyles(const wxRichTextAttr
& style
, wxRichTextAttr
& parStyle
, wxRichTextAttr
& charStyle
)
11070 return wxRichTextAttr::SplitParaCharStyles(style
, parStyle
, charStyle
);
11073 /// Convert a decimal to Roman numerals
11074 wxString
wxRichTextDecimalToRoman(long n
)
11076 static wxArrayInt decimalNumbers
;
11077 static wxArrayString romanNumbers
;
11082 decimalNumbers
.Clear();
11083 romanNumbers
.Clear();
11084 return wxEmptyString
;
11087 if (decimalNumbers
.GetCount() == 0)
11089 #define wxRichTextAddDecRom(n, r) decimalNumbers.Add(n); romanNumbers.Add(r);
11091 wxRichTextAddDecRom(1000, wxT("M"));
11092 wxRichTextAddDecRom(900, wxT("CM"));
11093 wxRichTextAddDecRom(500, wxT("D"));
11094 wxRichTextAddDecRom(400, wxT("CD"));
11095 wxRichTextAddDecRom(100, wxT("C"));
11096 wxRichTextAddDecRom(90, wxT("XC"));
11097 wxRichTextAddDecRom(50, wxT("L"));
11098 wxRichTextAddDecRom(40, wxT("XL"));
11099 wxRichTextAddDecRom(10, wxT("X"));
11100 wxRichTextAddDecRom(9, wxT("IX"));
11101 wxRichTextAddDecRom(5, wxT("V"));
11102 wxRichTextAddDecRom(4, wxT("IV"));
11103 wxRichTextAddDecRom(1, wxT("I"));
11109 while (n
> 0 && i
< 13)
11111 if (n
>= decimalNumbers
[i
])
11113 n
-= decimalNumbers
[i
];
11114 roman
+= romanNumbers
[i
];
11121 if (roman
.IsEmpty())
11127 * wxRichTextFileHandler
11128 * Base class for file handlers
11131 IMPLEMENT_CLASS(wxRichTextFileHandler
, wxObject
)
11133 #if wxUSE_FFILE && wxUSE_STREAMS
11134 bool wxRichTextFileHandler::LoadFile(wxRichTextBuffer
*buffer
, const wxString
& filename
)
11136 wxFFileInputStream
stream(filename
);
11138 return LoadFile(buffer
, stream
);
11143 bool wxRichTextFileHandler::SaveFile(wxRichTextBuffer
*buffer
, const wxString
& filename
)
11145 wxFFileOutputStream
stream(filename
);
11147 return SaveFile(buffer
, stream
);
11151 #endif // wxUSE_FFILE && wxUSE_STREAMS
11153 /// Can we handle this filename (if using files)? By default, checks the extension.
11154 bool wxRichTextFileHandler::CanHandle(const wxString
& filename
) const
11156 wxString path
, file
, ext
;
11157 wxFileName::SplitPath(filename
, & path
, & file
, & ext
);
11159 return (ext
.Lower() == GetExtension());
11163 * wxRichTextTextHandler
11164 * Plain text handler
11167 IMPLEMENT_CLASS(wxRichTextPlainTextHandler
, wxRichTextFileHandler
)
11170 bool wxRichTextPlainTextHandler::DoLoadFile(wxRichTextBuffer
*buffer
, wxInputStream
& stream
)
11172 if (!stream
.IsOk())
11178 while (!stream
.Eof())
11180 int ch
= stream
.GetC();
11184 if (ch
== 10 && lastCh
!= 13)
11187 if (ch
> 0 && ch
!= 10)
11194 buffer
->ResetAndClearCommands();
11196 buffer
->AddParagraphs(str
);
11197 buffer
->UpdateRanges();
11202 bool wxRichTextPlainTextHandler::DoSaveFile(wxRichTextBuffer
*buffer
, wxOutputStream
& stream
)
11204 if (!stream
.IsOk())
11207 wxString text
= buffer
->GetText();
11209 wxString newLine
= wxRichTextLineBreakChar
;
11210 text
.Replace(newLine
, wxT("\n"));
11212 wxCharBuffer buf
= text
.ToAscii();
11214 stream
.Write((const char*) buf
, text
.length());
11217 #endif // wxUSE_STREAMS
11220 * Stores information about an image, in binary in-memory form
11223 wxRichTextImageBlock::wxRichTextImageBlock()
11228 wxRichTextImageBlock::wxRichTextImageBlock(const wxRichTextImageBlock
& block
):wxObject()
11234 wxRichTextImageBlock::~wxRichTextImageBlock()
11239 void wxRichTextImageBlock::Init()
11243 m_imageType
= wxBITMAP_TYPE_INVALID
;
11246 void wxRichTextImageBlock::Clear()
11250 m_imageType
= wxBITMAP_TYPE_INVALID
;
11254 // Load the original image into a memory block.
11255 // If the image is not a JPEG, we must convert it into a JPEG
11256 // to conserve space.
11257 // If it's not a JPEG we can make use of 'image', already scaled, so we don't have to
11258 // load the image a 2nd time.
11260 bool wxRichTextImageBlock::MakeImageBlock(const wxString
& filename
, wxBitmapType imageType
,
11261 wxImage
& image
, bool convertToJPEG
)
11263 m_imageType
= imageType
;
11265 wxString
filenameToRead(filename
);
11266 bool removeFile
= false;
11268 if (imageType
== wxBITMAP_TYPE_INVALID
)
11269 return false; // Could not determine image type
11271 if ((imageType
!= wxBITMAP_TYPE_JPEG
) && convertToJPEG
)
11273 wxString tempFile
=
11274 wxFileName::CreateTempFileName(_("image"));
11276 wxASSERT(!tempFile
.IsEmpty());
11278 image
.SaveFile(tempFile
, wxBITMAP_TYPE_JPEG
);
11279 filenameToRead
= tempFile
;
11282 m_imageType
= wxBITMAP_TYPE_JPEG
;
11285 if (!file
.Open(filenameToRead
))
11288 m_dataSize
= (size_t) file
.Length();
11293 m_data
= ReadBlock(filenameToRead
, m_dataSize
);
11296 wxRemoveFile(filenameToRead
);
11298 return (m_data
!= NULL
);
11301 // Make an image block from the wxImage in the given
11303 bool wxRichTextImageBlock::MakeImageBlock(wxImage
& image
, wxBitmapType imageType
, int quality
)
11305 image
.SetOption(wxT("quality"), quality
);
11307 if (imageType
== wxBITMAP_TYPE_INVALID
)
11308 return false; // Could not determine image type
11310 return DoMakeImageBlock(image
, imageType
);
11313 // Uses a const wxImage for efficiency, but can't set quality (only relevant for JPEG)
11314 bool wxRichTextImageBlock::MakeImageBlockDefaultQuality(const wxImage
& image
, wxBitmapType imageType
)
11316 if (imageType
== wxBITMAP_TYPE_INVALID
)
11317 return false; // Could not determine image type
11319 return DoMakeImageBlock(image
, imageType
);
11322 // Makes the image block
11323 bool wxRichTextImageBlock::DoMakeImageBlock(const wxImage
& image
, wxBitmapType imageType
)
11325 wxMemoryOutputStream memStream
;
11326 if (!image
.SaveFile(memStream
, imageType
))
11331 unsigned char* block
= new unsigned char[memStream
.GetSize()];
11339 m_imageType
= imageType
;
11340 m_dataSize
= memStream
.GetSize();
11342 memStream
.CopyTo(m_data
, m_dataSize
);
11344 return (m_data
!= NULL
);
11348 bool wxRichTextImageBlock::Write(const wxString
& filename
)
11350 return WriteBlock(filename
, m_data
, m_dataSize
);
11353 void wxRichTextImageBlock::Copy(const wxRichTextImageBlock
& block
)
11355 m_imageType
= block
.m_imageType
;
11357 m_dataSize
= block
.m_dataSize
;
11358 if (m_dataSize
== 0)
11361 m_data
= new unsigned char[m_dataSize
];
11363 for (i
= 0; i
< m_dataSize
; i
++)
11364 m_data
[i
] = block
.m_data
[i
];
11368 void wxRichTextImageBlock::operator=(const wxRichTextImageBlock
& block
)
11373 // Load a wxImage from the block
11374 bool wxRichTextImageBlock::Load(wxImage
& image
)
11379 // Read in the image.
11381 wxMemoryInputStream
mstream(m_data
, m_dataSize
);
11382 bool success
= image
.LoadFile(mstream
, GetImageType());
11384 wxString tempFile
= wxFileName::CreateTempFileName(_("image"));
11385 wxASSERT(!tempFile
.IsEmpty());
11387 if (!WriteBlock(tempFile
, m_data
, m_dataSize
))
11391 success
= image
.LoadFile(tempFile
, GetImageType());
11392 wxRemoveFile(tempFile
);
11398 // Write data in hex to a stream
11399 bool wxRichTextImageBlock::WriteHex(wxOutputStream
& stream
)
11401 if (m_dataSize
== 0)
11404 int bufSize
= 100000;
11405 if (int(2*m_dataSize
) < bufSize
)
11406 bufSize
= 2*m_dataSize
;
11407 char* buf
= new char[bufSize
+1];
11409 int left
= m_dataSize
;
11414 if (left
*2 > bufSize
)
11416 n
= bufSize
; left
-= (bufSize
/2);
11420 n
= left
*2; left
= 0;
11424 for (i
= 0; i
< (n
/2); i
++)
11426 wxDecToHex(m_data
[j
], b
, b
+1);
11431 stream
.Write((const char*) buf
, n
);
11437 // Read data in hex from a stream
11438 bool wxRichTextImageBlock::ReadHex(wxInputStream
& stream
, int length
, wxBitmapType imageType
)
11440 int dataSize
= length
/2;
11445 // create a null terminated temporary string:
11449 m_data
= new unsigned char[dataSize
];
11451 for (i
= 0; i
< dataSize
; i
++)
11453 str
[0] = (char)stream
.GetC();
11454 str
[1] = (char)stream
.GetC();
11456 m_data
[i
] = (unsigned char)wxHexToDec(str
);
11459 m_dataSize
= dataSize
;
11460 m_imageType
= imageType
;
11465 // Allocate and read from stream as a block of memory
11466 unsigned char* wxRichTextImageBlock::ReadBlock(wxInputStream
& stream
, size_t size
)
11468 unsigned char* block
= new unsigned char[size
];
11472 stream
.Read(block
, size
);
11477 unsigned char* wxRichTextImageBlock::ReadBlock(const wxString
& filename
, size_t size
)
11479 wxFileInputStream
stream(filename
);
11480 if (!stream
.IsOk())
11483 return ReadBlock(stream
, size
);
11486 // Write memory block to stream
11487 bool wxRichTextImageBlock::WriteBlock(wxOutputStream
& stream
, unsigned char* block
, size_t size
)
11489 stream
.Write((void*) block
, size
);
11490 return stream
.IsOk();
11494 // Write memory block to file
11495 bool wxRichTextImageBlock::WriteBlock(const wxString
& filename
, unsigned char* block
, size_t size
)
11497 wxFileOutputStream
outStream(filename
);
11498 if (!outStream
.IsOk())
11501 return WriteBlock(outStream
, block
, size
);
11504 // Gets the extension for the block's type
11505 wxString
wxRichTextImageBlock::GetExtension() const
11507 wxImageHandler
* handler
= wxImage::FindHandler(GetImageType());
11509 return handler
->GetExtension();
11511 return wxEmptyString
;
11517 * The data object for a wxRichTextBuffer
11520 const wxChar
*wxRichTextBufferDataObject::ms_richTextBufferFormatId
= wxT("wxShape");
11522 wxRichTextBufferDataObject::wxRichTextBufferDataObject(wxRichTextBuffer
* richTextBuffer
)
11524 m_richTextBuffer
= richTextBuffer
;
11526 // this string should uniquely identify our format, but is otherwise
11528 m_formatRichTextBuffer
.SetId(GetRichTextBufferFormatId());
11530 SetFormat(m_formatRichTextBuffer
);
11533 wxRichTextBufferDataObject::~wxRichTextBufferDataObject()
11535 delete m_richTextBuffer
;
11538 // after a call to this function, the richTextBuffer is owned by the caller and it
11539 // is responsible for deleting it!
11540 wxRichTextBuffer
* wxRichTextBufferDataObject::GetRichTextBuffer()
11542 wxRichTextBuffer
* richTextBuffer
= m_richTextBuffer
;
11543 m_richTextBuffer
= NULL
;
11545 return richTextBuffer
;
11548 wxDataFormat
wxRichTextBufferDataObject::GetPreferredFormat(Direction
WXUNUSED(dir
)) const
11550 return m_formatRichTextBuffer
;
11553 size_t wxRichTextBufferDataObject::GetDataSize() const
11555 if (!m_richTextBuffer
)
11561 wxStringOutputStream
stream(& bufXML
);
11562 if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
))
11564 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
11570 wxCharBuffer buffer
= bufXML
.mb_str(wxConvUTF8
);
11571 return strlen(buffer
) + 1;
11573 return bufXML
.Length()+1;
11577 bool wxRichTextBufferDataObject::GetDataHere(void *pBuf
) const
11579 if (!pBuf
|| !m_richTextBuffer
)
11585 wxStringOutputStream
stream(& bufXML
);
11586 if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
))
11588 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
11594 wxCharBuffer buffer
= bufXML
.mb_str(wxConvUTF8
);
11595 size_t len
= strlen(buffer
);
11596 memcpy((char*) pBuf
, (const char*) buffer
, len
);
11597 ((char*) pBuf
)[len
] = 0;
11599 size_t len
= bufXML
.Length();
11600 memcpy((char*) pBuf
, (const char*) bufXML
.c_str(), len
);
11601 ((char*) pBuf
)[len
] = 0;
11607 bool wxRichTextBufferDataObject::SetData(size_t WXUNUSED(len
), const void *buf
)
11609 wxDELETE(m_richTextBuffer
);
11611 wxString
bufXML((const char*) buf
, wxConvUTF8
);
11613 m_richTextBuffer
= new wxRichTextBuffer
;
11615 wxStringInputStream
stream(bufXML
);
11616 if (!m_richTextBuffer
->LoadFile(stream
, wxRICHTEXT_TYPE_XML
))
11618 wxLogError(wxT("Could not read the buffer from an XML stream.\nYou may have forgotten to add the XML file handler."));
11620 wxDELETE(m_richTextBuffer
);
11632 * wxRichTextFontTable
11633 * Manages quick access to a pool of fonts for rendering rich text
11636 WX_DECLARE_STRING_HASH_MAP_WITH_DECL(wxFont
, wxRichTextFontTableHashMap
, class WXDLLIMPEXP_RICHTEXT
);
11638 class wxRichTextFontTableData
: public wxObjectRefData
11641 wxRichTextFontTableData() {}
11643 wxFont
FindFont(const wxRichTextAttr
& fontSpec
, double fontScale
);
11645 wxRichTextFontTableHashMap m_hashMap
;
11648 wxFont
wxRichTextFontTableData::FindFont(const wxRichTextAttr
& fontSpec
, double fontScale
)
11650 wxString
facename(fontSpec
.GetFontFaceName());
11652 int fontSize
= fontSpec
.GetFontSize();
11653 if (fontScale
!= 1.0)
11654 fontSize
= (int) ((double(fontSize
) * fontScale
) + 0.5);
11657 if (fontSpec
.HasFontPixelSize() && !fontSpec
.HasFontPointSize())
11661 wxString spec
= wxString::Format(wxT("%d-%s-%d-%d-%d-%d-%s-%d"),
11662 fontSize
, units
.c_str(), fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), (int) fontSpec
.GetFontUnderlined(), (int) fontSpec
.GetFontStrikethrough(),
11663 facename
.c_str(), (int) fontSpec
.GetFontEncoding());
11665 wxRichTextFontTableHashMap::iterator entry
= m_hashMap
.find(spec
);
11666 if ( entry
== m_hashMap
.end() )
11668 if (fontSpec
.HasFontPixelSize() && !fontSpec
.HasFontPointSize())
11670 wxFont
font(wxSize(0, fontSize
), wxFONTFAMILY_DEFAULT
, fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), fontSpec
.GetFontUnderlined(), facename
);
11671 if (fontSpec
.HasFontStrikethrough() && fontSpec
.GetFontStrikethrough())
11672 font
.SetStrikethrough(true);
11673 m_hashMap
[spec
] = font
;
11678 wxFont
font(fontSize
, wxFONTFAMILY_DEFAULT
, fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), fontSpec
.GetFontUnderlined(), facename
.c_str());
11679 if (fontSpec
.HasFontStrikethrough() && fontSpec
.GetFontStrikethrough())
11680 font
.SetStrikethrough(true);
11682 m_hashMap
[spec
] = font
;
11688 return entry
->second
;
11692 IMPLEMENT_DYNAMIC_CLASS(wxRichTextFontTable
, wxObject
)
11694 wxRichTextFontTable::wxRichTextFontTable()
11696 m_refData
= new wxRichTextFontTableData
;
11700 wxRichTextFontTable::wxRichTextFontTable(const wxRichTextFontTable
& table
)
11706 wxRichTextFontTable::~wxRichTextFontTable()
11711 bool wxRichTextFontTable::operator == (const wxRichTextFontTable
& table
) const
11713 return (m_refData
== table
.m_refData
);
11716 void wxRichTextFontTable::operator= (const wxRichTextFontTable
& table
)
11719 m_fontScale
= table
.m_fontScale
;
11722 wxFont
wxRichTextFontTable::FindFont(const wxRichTextAttr
& fontSpec
)
11724 wxRichTextFontTableData
* data
= (wxRichTextFontTableData
*) m_refData
;
11726 return data
->FindFont(fontSpec
, m_fontScale
);
11731 void wxRichTextFontTable::Clear()
11733 wxRichTextFontTableData
* data
= (wxRichTextFontTableData
*) m_refData
;
11735 data
->m_hashMap
.clear();
11738 void wxRichTextFontTable::SetFontScale(double fontScale
)
11740 if (fontScale
!= m_fontScale
)
11742 m_fontScale
= fontScale
;
11747 void wxTextBoxAttr::Reset()
11750 m_floatMode
= wxTEXT_BOX_ATTR_FLOAT_NONE
;
11751 m_clearMode
= wxTEXT_BOX_ATTR_CLEAR_NONE
;
11752 m_collapseMode
= wxTEXT_BOX_ATTR_COLLAPSE_NONE
;
11753 m_verticalAlignment
= wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_NONE
;
11754 m_boxStyleName
= wxEmptyString
;
11758 m_position
.Reset();
11769 bool wxTextBoxAttr::operator== (const wxTextBoxAttr
& attr
) const
11772 m_flags
== attr
.m_flags
&&
11773 m_floatMode
== attr
.m_floatMode
&&
11774 m_clearMode
== attr
.m_clearMode
&&
11775 m_collapseMode
== attr
.m_collapseMode
&&
11776 m_verticalAlignment
== attr
.m_verticalAlignment
&&
11778 m_margins
== attr
.m_margins
&&
11779 m_padding
== attr
.m_padding
&&
11780 m_position
== attr
.m_position
&&
11782 m_size
== attr
.m_size
&&
11783 m_minSize
== attr
.m_minSize
&&
11784 m_maxSize
== attr
.m_maxSize
&&
11786 m_border
== attr
.m_border
&&
11787 m_outline
== attr
.m_outline
&&
11789 m_boxStyleName
== attr
.m_boxStyleName
11793 // Partial equality test
11794 bool wxTextBoxAttr::EqPartial(const wxTextBoxAttr
& attr
, bool weakTest
) const
11797 ((!HasFloatMode() && attr
.HasFloatMode()) ||
11798 (!HasClearMode() && attr
.HasClearMode()) ||
11799 (!HasCollapseBorders() && attr
.HasCollapseBorders()) ||
11800 (!HasVerticalAlignment() && attr
.HasVerticalAlignment()) ||
11801 (!HasBoxStyleName() && attr
.HasBoxStyleName())))
11805 if (attr
.HasFloatMode() && HasFloatMode() && (GetFloatMode() != attr
.GetFloatMode()))
11808 if (attr
.HasClearMode() && HasClearMode() && (GetClearMode() != attr
.GetClearMode()))
11811 if (attr
.HasCollapseBorders() && HasCollapseBorders() && (attr
.GetCollapseBorders() != GetCollapseBorders()))
11814 if (attr
.HasVerticalAlignment() && HasVerticalAlignment() && (attr
.GetVerticalAlignment() != GetVerticalAlignment()))
11817 if (attr
.HasBoxStyleName() && HasBoxStyleName() && (attr
.GetBoxStyleName() != GetBoxStyleName()))
11822 if (!m_position
.EqPartial(attr
.m_position
, weakTest
))
11827 if (!m_size
.EqPartial(attr
.m_size
, weakTest
))
11829 if (!m_minSize
.EqPartial(attr
.m_minSize
, weakTest
))
11831 if (!m_maxSize
.EqPartial(attr
.m_maxSize
, weakTest
))
11836 if (!m_margins
.EqPartial(attr
.m_margins
, weakTest
))
11841 if (!m_padding
.EqPartial(attr
.m_padding
, weakTest
))
11846 if (!GetBorder().EqPartial(attr
.GetBorder(), weakTest
))
11851 if (!GetOutline().EqPartial(attr
.GetOutline(), weakTest
))
11857 // Merges the given attributes. If compareWith
11858 // is non-NULL, then it will be used to mask out those attributes that are the same in style
11859 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
11860 bool wxTextBoxAttr::Apply(const wxTextBoxAttr
& attr
, const wxTextBoxAttr
* compareWith
)
11862 if (attr
.HasFloatMode())
11864 if (!(compareWith
&& compareWith
->HasFloatMode() && compareWith
->GetFloatMode() == attr
.GetFloatMode()))
11865 SetFloatMode(attr
.GetFloatMode());
11868 if (attr
.HasClearMode())
11870 if (!(compareWith
&& compareWith
->HasClearMode() && compareWith
->GetClearMode() == attr
.GetClearMode()))
11871 SetClearMode(attr
.GetClearMode());
11874 if (attr
.HasCollapseBorders())
11876 if (!(compareWith
&& compareWith
->HasCollapseBorders() && compareWith
->GetCollapseBorders() == attr
.GetCollapseBorders()))
11877 SetCollapseBorders(attr
.GetCollapseBorders());
11880 if (attr
.HasVerticalAlignment())
11882 if (!(compareWith
&& compareWith
->HasVerticalAlignment() && compareWith
->GetVerticalAlignment() == attr
.GetVerticalAlignment()))
11883 SetVerticalAlignment(attr
.GetVerticalAlignment());
11886 if (attr
.HasBoxStyleName())
11888 if (!(compareWith
&& compareWith
->HasBoxStyleName() && compareWith
->GetBoxStyleName() == attr
.GetBoxStyleName()))
11889 SetBoxStyleName(attr
.GetBoxStyleName());
11892 m_margins
.Apply(attr
.m_margins
, compareWith
? (& attr
.m_margins
) : (const wxTextAttrDimensions
*) NULL
);
11893 m_padding
.Apply(attr
.m_padding
, compareWith
? (& attr
.m_padding
) : (const wxTextAttrDimensions
*) NULL
);
11894 m_position
.Apply(attr
.m_position
, compareWith
? (& attr
.m_position
) : (const wxTextAttrDimensions
*) NULL
);
11896 m_size
.Apply(attr
.m_size
, compareWith
? (& attr
.m_size
) : (const wxTextAttrSize
*) NULL
);
11897 m_minSize
.Apply(attr
.m_minSize
, compareWith
? (& attr
.m_minSize
) : (const wxTextAttrSize
*) NULL
);
11898 m_maxSize
.Apply(attr
.m_maxSize
, compareWith
? (& attr
.m_maxSize
) : (const wxTextAttrSize
*) NULL
);
11900 m_border
.Apply(attr
.m_border
, compareWith
? (& attr
.m_border
) : (const wxTextAttrBorders
*) NULL
);
11901 m_outline
.Apply(attr
.m_outline
, compareWith
? (& attr
.m_outline
) : (const wxTextAttrBorders
*) NULL
);
11906 // Remove specified attributes from this object
11907 bool wxTextBoxAttr::RemoveStyle(const wxTextBoxAttr
& attr
)
11909 if (attr
.HasFloatMode())
11910 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT
);
11912 if (attr
.HasClearMode())
11913 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR
);
11915 if (attr
.HasCollapseBorders())
11916 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
11918 if (attr
.HasVerticalAlignment())
11919 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
11921 if (attr
.HasBoxStyleName())
11923 SetBoxStyleName(wxEmptyString
);
11924 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
11927 m_margins
.RemoveStyle(attr
.m_margins
);
11928 m_padding
.RemoveStyle(attr
.m_padding
);
11929 m_position
.RemoveStyle(attr
.m_position
);
11931 m_size
.RemoveStyle(attr
.m_size
);
11932 m_minSize
.RemoveStyle(attr
.m_minSize
);
11933 m_maxSize
.RemoveStyle(attr
.m_maxSize
);
11935 m_border
.RemoveStyle(attr
.m_border
);
11936 m_outline
.RemoveStyle(attr
.m_outline
);
11941 // Collects the attributes that are common to a range of content, building up a note of
11942 // which attributes are absent in some objects and which clash in some objects.
11943 void wxTextBoxAttr::CollectCommonAttributes(const wxTextBoxAttr
& attr
, wxTextBoxAttr
& clashingAttr
, wxTextBoxAttr
& absentAttr
)
11945 if (attr
.HasFloatMode())
11947 if (!clashingAttr
.HasFloatMode() && !absentAttr
.HasFloatMode())
11949 if (HasFloatMode())
11951 if (GetFloatMode() != attr
.GetFloatMode())
11953 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_FLOAT
);
11954 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT
);
11958 SetFloatMode(attr
.GetFloatMode());
11962 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_FLOAT
);
11964 if (attr
.HasClearMode())
11966 if (!clashingAttr
.HasClearMode() && !absentAttr
.HasClearMode())
11968 if (HasClearMode())
11970 if (GetClearMode() != attr
.GetClearMode())
11972 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_CLEAR
);
11973 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR
);
11977 SetClearMode(attr
.GetClearMode());
11981 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_CLEAR
);
11983 if (attr
.HasCollapseBorders())
11985 if (!clashingAttr
.HasCollapseBorders() && !absentAttr
.HasCollapseBorders())
11987 if (HasCollapseBorders())
11989 if (GetCollapseBorders() != attr
.GetCollapseBorders())
11991 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
11992 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
11996 SetCollapseBorders(attr
.GetCollapseBorders());
12000 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
12002 if (attr
.HasVerticalAlignment())
12004 if (!clashingAttr
.HasVerticalAlignment() && !absentAttr
.HasVerticalAlignment())
12006 if (HasVerticalAlignment())
12008 if (GetVerticalAlignment() != attr
.GetVerticalAlignment())
12010 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
12011 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
12015 SetVerticalAlignment(attr
.GetVerticalAlignment());
12019 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
12021 if (attr
.HasBoxStyleName())
12023 if (!clashingAttr
.HasBoxStyleName() && !absentAttr
.HasBoxStyleName())
12025 if (HasBoxStyleName())
12027 if (GetBoxStyleName() != attr
.GetBoxStyleName())
12029 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
12030 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
12034 SetBoxStyleName(attr
.GetBoxStyleName());
12038 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
12040 m_margins
.CollectCommonAttributes(attr
.m_margins
, clashingAttr
.m_margins
, absentAttr
.m_margins
);
12041 m_padding
.CollectCommonAttributes(attr
.m_padding
, clashingAttr
.m_padding
, absentAttr
.m_padding
);
12042 m_position
.CollectCommonAttributes(attr
.m_position
, clashingAttr
.m_position
, absentAttr
.m_position
);
12044 m_size
.CollectCommonAttributes(attr
.m_size
, clashingAttr
.m_size
, absentAttr
.m_size
);
12045 m_minSize
.CollectCommonAttributes(attr
.m_minSize
, clashingAttr
.m_minSize
, absentAttr
.m_minSize
);
12046 m_maxSize
.CollectCommonAttributes(attr
.m_maxSize
, clashingAttr
.m_maxSize
, absentAttr
.m_maxSize
);
12048 m_border
.CollectCommonAttributes(attr
.m_border
, clashingAttr
.m_border
, absentAttr
.m_border
);
12049 m_outline
.CollectCommonAttributes(attr
.m_outline
, clashingAttr
.m_outline
, absentAttr
.m_outline
);
12052 bool wxTextBoxAttr::IsDefault() const
12054 return GetFlags() == 0 && !m_border
.IsValid() && !m_outline
.IsValid() &&
12055 !m_size
.IsValid() && !m_minSize
.IsValid() && !m_maxSize
.IsValid() &&
12056 !m_position
.IsValid() && !m_padding
.IsValid() && !m_margins
.IsValid();
12061 void wxRichTextAttr::Copy(const wxRichTextAttr
& attr
)
12063 wxTextAttr::Copy(attr
);
12065 m_textBoxAttr
= attr
.m_textBoxAttr
;
12068 bool wxRichTextAttr::operator==(const wxRichTextAttr
& attr
) const
12070 if (!(wxTextAttr::operator==(attr
)))
12073 return (m_textBoxAttr
== attr
.m_textBoxAttr
);
12076 // Partial equality test
12077 bool wxRichTextAttr::EqPartial(const wxRichTextAttr
& attr
, bool weakTest
) const
12079 if (!(wxTextAttr::EqPartial(attr
, weakTest
)))
12082 return m_textBoxAttr
.EqPartial(attr
.m_textBoxAttr
, weakTest
);
12085 // Merges the given attributes. If compareWith
12086 // is non-NULL, then it will be used to mask out those attributes that are the same in style
12087 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
12088 bool wxRichTextAttr::Apply(const wxRichTextAttr
& style
, const wxRichTextAttr
* compareWith
)
12090 wxTextAttr::Apply(style
, compareWith
);
12092 return m_textBoxAttr
.Apply(style
.m_textBoxAttr
, compareWith
? (& compareWith
->m_textBoxAttr
) : (const wxTextBoxAttr
*) NULL
);
12095 // Remove specified attributes from this object
12096 bool wxRichTextAttr::RemoveStyle(const wxRichTextAttr
& attr
)
12098 wxTextAttr::RemoveStyle(*this, attr
);
12100 return m_textBoxAttr
.RemoveStyle(attr
.m_textBoxAttr
);
12103 // Collects the attributes that are common to a range of content, building up a note of
12104 // which attributes are absent in some objects and which clash in some objects.
12105 void wxRichTextAttr::CollectCommonAttributes(const wxRichTextAttr
& attr
, wxRichTextAttr
& clashingAttr
, wxRichTextAttr
& absentAttr
)
12107 wxTextAttrCollectCommonAttributes(*this, attr
, clashingAttr
, absentAttr
);
12109 m_textBoxAttr
.CollectCommonAttributes(attr
.m_textBoxAttr
, clashingAttr
.m_textBoxAttr
, absentAttr
.m_textBoxAttr
);
12112 // Partial equality test
12113 bool wxTextAttrBorder::EqPartial(const wxTextAttrBorder
& border
, bool weakTest
) const
12116 ((!HasStyle() && border
.HasStyle()) ||
12117 (!HasColour() && border
.HasColour()) ||
12118 (!HasWidth() && border
.HasWidth())))
12123 if (border
.HasStyle() && HasStyle() && (border
.GetStyle() != GetStyle()))
12126 if (border
.HasColour() && HasColour() && (border
.GetColourLong() != GetColourLong()))
12129 if (border
.HasWidth() && HasWidth() && !(border
.GetWidth() == GetWidth()))
12135 // Apply border to 'this', but not if the same as compareWith
12136 bool wxTextAttrBorder::Apply(const wxTextAttrBorder
& border
, const wxTextAttrBorder
* compareWith
)
12138 if (border
.HasStyle())
12140 if (!(compareWith
&& (border
.GetStyle() == compareWith
->GetStyle())))
12141 SetStyle(border
.GetStyle());
12143 if (border
.HasColour())
12145 if (!(compareWith
&& (border
.GetColourLong() == compareWith
->GetColourLong())))
12146 SetColour(border
.GetColourLong());
12148 if (border
.HasWidth())
12150 if (!(compareWith
&& (border
.GetWidth() == compareWith
->GetWidth())))
12151 SetWidth(border
.GetWidth());
12157 // Remove specified attributes from this object
12158 bool wxTextAttrBorder::RemoveStyle(const wxTextAttrBorder
& attr
)
12160 if (attr
.HasStyle() && HasStyle())
12161 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_STYLE
);
12162 if (attr
.HasColour() && HasColour())
12163 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_COLOUR
);
12164 if (attr
.HasWidth() && HasWidth())
12165 m_borderWidth
.Reset();
12170 // Collects the attributes that are common to a range of content, building up a note of
12171 // which attributes are absent in some objects and which clash in some objects.
12172 void wxTextAttrBorder::CollectCommonAttributes(const wxTextAttrBorder
& attr
, wxTextAttrBorder
& clashingAttr
, wxTextAttrBorder
& absentAttr
)
12174 if (attr
.HasStyle())
12176 if (!clashingAttr
.HasStyle() && !absentAttr
.HasStyle())
12180 if (GetStyle() != attr
.GetStyle())
12182 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
12183 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
12187 SetStyle(attr
.GetStyle());
12191 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
12193 if (attr
.HasColour())
12195 if (!clashingAttr
.HasColour() && !absentAttr
.HasColour())
12199 if (GetColour() != attr
.GetColour())
12201 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
12202 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
12206 SetColour(attr
.GetColourLong());
12210 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
12212 m_borderWidth
.CollectCommonAttributes(attr
.m_borderWidth
, clashingAttr
.m_borderWidth
, absentAttr
.m_borderWidth
);
12215 // Partial equality test
12216 bool wxTextAttrBorders::EqPartial(const wxTextAttrBorders
& borders
, bool weakTest
) const
12218 return m_left
.EqPartial(borders
.m_left
, weakTest
) && m_right
.EqPartial(borders
.m_right
, weakTest
) &&
12219 m_top
.EqPartial(borders
.m_top
, weakTest
) && m_bottom
.EqPartial(borders
.m_bottom
, weakTest
);
12222 // Apply border to 'this', but not if the same as compareWith
12223 bool wxTextAttrBorders::Apply(const wxTextAttrBorders
& borders
, const wxTextAttrBorders
* compareWith
)
12225 m_left
.Apply(borders
.m_left
, compareWith
? (& compareWith
->m_left
) : (const wxTextAttrBorder
*) NULL
);
12226 m_right
.Apply(borders
.m_right
, compareWith
? (& compareWith
->m_right
) : (const wxTextAttrBorder
*) NULL
);
12227 m_top
.Apply(borders
.m_top
, compareWith
? (& compareWith
->m_top
) : (const wxTextAttrBorder
*) NULL
);
12228 m_bottom
.Apply(borders
.m_bottom
, compareWith
? (& compareWith
->m_bottom
) : (const wxTextAttrBorder
*) NULL
);
12232 // Remove specified attributes from this object
12233 bool wxTextAttrBorders::RemoveStyle(const wxTextAttrBorders
& attr
)
12235 m_left
.RemoveStyle(attr
.m_left
);
12236 m_right
.RemoveStyle(attr
.m_right
);
12237 m_top
.RemoveStyle(attr
.m_top
);
12238 m_bottom
.RemoveStyle(attr
.m_bottom
);
12242 // Collects the attributes that are common to a range of content, building up a note of
12243 // which attributes are absent in some objects and which clash in some objects.
12244 void wxTextAttrBorders::CollectCommonAttributes(const wxTextAttrBorders
& attr
, wxTextAttrBorders
& clashingAttr
, wxTextAttrBorders
& absentAttr
)
12246 m_left
.CollectCommonAttributes(attr
.m_left
, clashingAttr
.m_left
, absentAttr
.m_left
);
12247 m_right
.CollectCommonAttributes(attr
.m_right
, clashingAttr
.m_right
, absentAttr
.m_right
);
12248 m_top
.CollectCommonAttributes(attr
.m_top
, clashingAttr
.m_top
, absentAttr
.m_top
);
12249 m_bottom
.CollectCommonAttributes(attr
.m_bottom
, clashingAttr
.m_bottom
, absentAttr
.m_bottom
);
12252 // Set style of all borders
12253 void wxTextAttrBorders::SetStyle(int style
)
12255 m_left
.SetStyle(style
);
12256 m_right
.SetStyle(style
);
12257 m_top
.SetStyle(style
);
12258 m_bottom
.SetStyle(style
);
12261 // Set colour of all borders
12262 void wxTextAttrBorders::SetColour(unsigned long colour
)
12264 m_left
.SetColour(colour
);
12265 m_right
.SetColour(colour
);
12266 m_top
.SetColour(colour
);
12267 m_bottom
.SetColour(colour
);
12270 void wxTextAttrBorders::SetColour(const wxColour
& colour
)
12272 m_left
.SetColour(colour
);
12273 m_right
.SetColour(colour
);
12274 m_top
.SetColour(colour
);
12275 m_bottom
.SetColour(colour
);
12278 // Set width of all borders
12279 void wxTextAttrBorders::SetWidth(const wxTextAttrDimension
& width
)
12281 m_left
.SetWidth(width
);
12282 m_right
.SetWidth(width
);
12283 m_top
.SetWidth(width
);
12284 m_bottom
.SetWidth(width
);
12287 // Partial equality test
12288 bool wxTextAttrDimension::EqPartial(const wxTextAttrDimension
& dim
, bool weakTest
) const
12290 if (!weakTest
&& !IsValid() && dim
.IsValid())
12293 if (dim
.IsValid() && IsValid() && !((*this) == dim
))
12299 bool wxTextAttrDimension::Apply(const wxTextAttrDimension
& dim
, const wxTextAttrDimension
* compareWith
)
12303 if (!(compareWith
&& dim
== (*compareWith
)))
12310 // Collects the attributes that are common to a range of content, building up a note of
12311 // which attributes are absent in some objects and which clash in some objects.
12312 void wxTextAttrDimension::CollectCommonAttributes(const wxTextAttrDimension
& attr
, wxTextAttrDimension
& clashingAttr
, wxTextAttrDimension
& absentAttr
)
12314 if (attr
.IsValid())
12316 if (!clashingAttr
.IsValid() && !absentAttr
.IsValid())
12320 if (!((*this) == attr
))
12322 clashingAttr
.SetValid(true);
12331 absentAttr
.SetValid(true);
12334 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(wxDC
& dc
, double scale
, const wxSize
& parentSize
)
12336 m_ppi
= dc
.GetPPI().x
; m_scale
= scale
; m_parentSize
= parentSize
;
12339 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(int ppi
, double scale
, const wxSize
& parentSize
)
12341 m_ppi
= ppi
; m_scale
= scale
; m_parentSize
= parentSize
;
12344 int wxTextAttrDimensionConverter::ConvertTenthsMMToPixels(int units
) const
12346 return wxRichTextObject::ConvertTenthsMMToPixels(m_ppi
, units
, m_scale
);
12349 int wxTextAttrDimensionConverter::ConvertPixelsToTenthsMM(int pixels
) const
12351 return wxRichTextObject::ConvertPixelsToTenthsMM(m_ppi
, pixels
, m_scale
);
12354 int wxTextAttrDimensionConverter::GetPixels(const wxTextAttrDimension
& dim
, int direction
) const
12356 if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
12357 return ConvertTenthsMMToPixels(dim
.GetValue());
12358 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
12359 return dim
.GetValue();
12360 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
12362 wxASSERT(m_parentSize
!= wxDefaultSize
);
12363 if (direction
== wxHORIZONTAL
)
12364 return (int) (double(m_parentSize
.x
) * double(dim
.GetValue()) / 100.0);
12366 return (int) (double(m_parentSize
.y
) * double(dim
.GetValue()) / 100.0);
12375 int wxTextAttrDimensionConverter::GetTenthsMM(const wxTextAttrDimension
& dim
) const
12377 if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
12378 return dim
.GetValue();
12379 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
12380 return ConvertPixelsToTenthsMM(dim
.GetValue());
12388 // Partial equality test
12389 bool wxTextAttrDimensions::EqPartial(const wxTextAttrDimensions
& dims
, bool weakTest
) const
12391 if (!m_left
.EqPartial(dims
.m_left
, weakTest
))
12394 if (!m_right
.EqPartial(dims
.m_right
, weakTest
))
12397 if (!m_top
.EqPartial(dims
.m_top
, weakTest
))
12400 if (!m_bottom
.EqPartial(dims
.m_bottom
, weakTest
))
12406 // Apply border to 'this', but not if the same as compareWith
12407 bool wxTextAttrDimensions::Apply(const wxTextAttrDimensions
& dims
, const wxTextAttrDimensions
* compareWith
)
12409 m_left
.Apply(dims
.m_left
, compareWith
? (& compareWith
->m_left
) : (const wxTextAttrDimension
*) NULL
);
12410 m_right
.Apply(dims
.m_right
, compareWith
? (& compareWith
->m_right
): (const wxTextAttrDimension
*) NULL
);
12411 m_top
.Apply(dims
.m_top
, compareWith
? (& compareWith
->m_top
): (const wxTextAttrDimension
*) NULL
);
12412 m_bottom
.Apply(dims
.m_bottom
, compareWith
? (& compareWith
->m_bottom
): (const wxTextAttrDimension
*) NULL
);
12417 // Remove specified attributes from this object
12418 bool wxTextAttrDimensions::RemoveStyle(const wxTextAttrDimensions
& attr
)
12420 if (attr
.m_left
.IsValid())
12422 if (attr
.m_right
.IsValid())
12424 if (attr
.m_top
.IsValid())
12426 if (attr
.m_bottom
.IsValid())
12432 // Collects the attributes that are common to a range of content, building up a note of
12433 // which attributes are absent in some objects and which clash in some objects.
12434 void wxTextAttrDimensions::CollectCommonAttributes(const wxTextAttrDimensions
& attr
, wxTextAttrDimensions
& clashingAttr
, wxTextAttrDimensions
& absentAttr
)
12436 m_left
.CollectCommonAttributes(attr
.m_left
, clashingAttr
.m_left
, absentAttr
.m_left
);
12437 m_right
.CollectCommonAttributes(attr
.m_right
, clashingAttr
.m_right
, absentAttr
.m_right
);
12438 m_top
.CollectCommonAttributes(attr
.m_top
, clashingAttr
.m_top
, absentAttr
.m_top
);
12439 m_bottom
.CollectCommonAttributes(attr
.m_bottom
, clashingAttr
.m_bottom
, absentAttr
.m_bottom
);
12442 // Partial equality test
12443 bool wxTextAttrSize::EqPartial(const wxTextAttrSize
& size
, bool weakTest
) const
12445 if (!m_width
.EqPartial(size
.m_width
, weakTest
))
12448 if (!m_height
.EqPartial(size
.m_height
, weakTest
))
12454 // Apply border to 'this', but not if the same as compareWith
12455 bool wxTextAttrSize::Apply(const wxTextAttrSize
& size
, const wxTextAttrSize
* compareWith
)
12457 m_width
.Apply(size
.m_width
, compareWith
? (& compareWith
->m_width
) : (const wxTextAttrDimension
*) NULL
);
12458 m_height
.Apply(size
.m_height
, compareWith
? (& compareWith
->m_height
): (const wxTextAttrDimension
*) NULL
);
12463 // Remove specified attributes from this object
12464 bool wxTextAttrSize::RemoveStyle(const wxTextAttrSize
& attr
)
12466 if (attr
.m_width
.IsValid())
12468 if (attr
.m_height
.IsValid())
12474 // Collects the attributes that are common to a range of content, building up a note of
12475 // which attributes are absent in some objects and which clash in some objects.
12476 void wxTextAttrSize::CollectCommonAttributes(const wxTextAttrSize
& attr
, wxTextAttrSize
& clashingAttr
, wxTextAttrSize
& absentAttr
)
12478 m_width
.CollectCommonAttributes(attr
.m_width
, clashingAttr
.m_width
, absentAttr
.m_width
);
12479 m_height
.CollectCommonAttributes(attr
.m_height
, clashingAttr
.m_height
, absentAttr
.m_height
);
12482 // Collects the attributes that are common to a range of content, building up a note of
12483 // which attributes are absent in some objects and which clash in some objects.
12484 void wxTextAttrCollectCommonAttributes(wxTextAttr
& currentStyle
, const wxTextAttr
& attr
, wxTextAttr
& clashingAttr
, wxTextAttr
& absentAttr
)
12486 absentAttr
.SetFlags(absentAttr
.GetFlags() | (~attr
.GetFlags() & wxTEXT_ATTR_ALL
));
12487 absentAttr
.SetTextEffectFlags(absentAttr
.GetTextEffectFlags() | (~attr
.GetTextEffectFlags() & 0xFFFF));
12489 long forbiddenFlags
= clashingAttr
.GetFlags()|absentAttr
.GetFlags();
12491 // If different font size units are being used, this is a clash.
12492 if (((attr
.GetFlags() & wxTEXT_ATTR_FONT_SIZE
) | (currentStyle
.GetFlags() & wxTEXT_ATTR_FONT_SIZE
)) == wxTEXT_ATTR_FONT_SIZE
)
12494 currentStyle
.SetFontSize(0);
12495 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_SIZE
);
12496 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_SIZE
);
12500 if (attr
.HasFontPointSize() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_POINT_SIZE
))
12502 if (currentStyle
.HasFontPointSize())
12504 if (currentStyle
.GetFontSize() != attr
.GetFontSize())
12506 // Clash of attr - mark as such
12507 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE
);
12508 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE
);
12512 currentStyle
.SetFontSize(attr
.GetFontSize());
12514 else if (!attr
.HasFontPointSize() && currentStyle
.HasFontPointSize())
12516 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE
);
12517 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE
);
12520 if (attr
.HasFontPixelSize() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_PIXEL_SIZE
))
12522 if (currentStyle
.HasFontPixelSize())
12524 if (currentStyle
.GetFontSize() != attr
.GetFontSize())
12526 // Clash of attr - mark as such
12527 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE
);
12528 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE
);
12532 currentStyle
.SetFontPixelSize(attr
.GetFontSize());
12534 else if (!attr
.HasFontPixelSize() && currentStyle
.HasFontPixelSize())
12536 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE
);
12537 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE
);
12541 if (attr
.HasFontItalic() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_ITALIC
))
12543 if (currentStyle
.HasFontItalic())
12545 if (currentStyle
.GetFontStyle() != attr
.GetFontStyle())
12547 // Clash of attr - mark as such
12548 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_ITALIC
);
12549 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC
);
12553 currentStyle
.SetFontStyle(attr
.GetFontStyle());
12555 else if (!attr
.HasFontItalic() && currentStyle
.HasFontItalic())
12557 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_ITALIC
);
12558 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC
);
12561 if (attr
.HasFontFamily() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_FAMILY
))
12563 if (currentStyle
.HasFontFamily())
12565 if (currentStyle
.GetFontFamily() != attr
.GetFontFamily())
12567 // Clash of attr - mark as such
12568 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FAMILY
);
12569 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY
);
12573 currentStyle
.SetFontFamily(attr
.GetFontFamily());
12575 else if (!attr
.HasFontFamily() && currentStyle
.HasFontFamily())
12577 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FAMILY
);
12578 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY
);
12581 if (attr
.HasFontWeight() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_WEIGHT
))
12583 if (currentStyle
.HasFontWeight())
12585 if (currentStyle
.GetFontWeight() != attr
.GetFontWeight())
12587 // Clash of attr - mark as such
12588 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_WEIGHT
);
12589 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT
);
12593 currentStyle
.SetFontWeight(attr
.GetFontWeight());
12595 else if (!attr
.HasFontWeight() && currentStyle
.HasFontWeight())
12597 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_WEIGHT
);
12598 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT
);
12601 if (attr
.HasFontFaceName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_FACE
))
12603 if (currentStyle
.HasFontFaceName())
12605 wxString
faceName1(currentStyle
.GetFontFaceName());
12606 wxString
faceName2(attr
.GetFontFaceName());
12608 if (faceName1
!= faceName2
)
12610 // Clash of attr - mark as such
12611 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FACE
);
12612 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FACE
);
12616 currentStyle
.SetFontFaceName(attr
.GetFontFaceName());
12618 else if (!attr
.HasFontFaceName() && currentStyle
.HasFontFaceName())
12620 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FACE
);
12621 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FACE
);
12624 if (attr
.HasFontUnderlined() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_UNDERLINE
))
12626 if (currentStyle
.HasFontUnderlined())
12628 if (currentStyle
.GetFontUnderlined() != attr
.GetFontUnderlined())
12630 // Clash of attr - mark as such
12631 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
12632 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
12636 currentStyle
.SetFontUnderlined(attr
.GetFontUnderlined());
12638 else if (!attr
.HasFontUnderlined() && currentStyle
.HasFontUnderlined())
12640 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
12641 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
12644 if (attr
.HasFontStrikethrough() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_STRIKETHROUGH
))
12646 if (currentStyle
.HasFontStrikethrough())
12648 if (currentStyle
.GetFontStrikethrough() != attr
.GetFontStrikethrough())
12650 // Clash of attr - mark as such
12651 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH
);
12652 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH
);
12656 currentStyle
.SetFontStrikethrough(attr
.GetFontStrikethrough());
12658 else if (!attr
.HasFontStrikethrough() && currentStyle
.HasFontStrikethrough())
12660 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH
);
12661 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH
);
12664 if (attr
.HasTextColour() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_TEXT_COLOUR
))
12666 if (currentStyle
.HasTextColour())
12668 if (currentStyle
.GetTextColour() != attr
.GetTextColour())
12670 // Clash of attr - mark as such
12671 clashingAttr
.AddFlag(wxTEXT_ATTR_TEXT_COLOUR
);
12672 currentStyle
.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR
);
12676 currentStyle
.SetTextColour(attr
.GetTextColour());
12678 else if (!attr
.HasTextColour() && currentStyle
.HasTextColour())
12680 clashingAttr
.AddFlag(wxTEXT_ATTR_TEXT_COLOUR
);
12681 currentStyle
.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR
);
12684 if (attr
.HasBackgroundColour() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BACKGROUND_COLOUR
))
12686 if (currentStyle
.HasBackgroundColour())
12688 if (currentStyle
.GetBackgroundColour() != attr
.GetBackgroundColour())
12690 // Clash of attr - mark as such
12691 clashingAttr
.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
12692 currentStyle
.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
12696 currentStyle
.SetBackgroundColour(attr
.GetBackgroundColour());
12698 else if (!attr
.HasBackgroundColour() && currentStyle
.HasBackgroundColour())
12700 clashingAttr
.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
12701 currentStyle
.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
12704 if (attr
.HasAlignment() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_ALIGNMENT
))
12706 if (currentStyle
.HasAlignment())
12708 if (currentStyle
.GetAlignment() != attr
.GetAlignment())
12710 // Clash of attr - mark as such
12711 clashingAttr
.AddFlag(wxTEXT_ATTR_ALIGNMENT
);
12712 currentStyle
.RemoveFlag(wxTEXT_ATTR_ALIGNMENT
);
12716 currentStyle
.SetAlignment(attr
.GetAlignment());
12718 else if (!attr
.HasAlignment() && currentStyle
.HasAlignment())
12720 clashingAttr
.AddFlag(wxTEXT_ATTR_ALIGNMENT
);
12721 currentStyle
.RemoveFlag(wxTEXT_ATTR_ALIGNMENT
);
12724 if (attr
.HasTabs() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_TABS
))
12726 if (currentStyle
.HasTabs())
12728 if (!wxRichTextTabsEq(currentStyle
.GetTabs(), attr
.GetTabs()))
12730 // Clash of attr - mark as such
12731 clashingAttr
.AddFlag(wxTEXT_ATTR_TABS
);
12732 currentStyle
.RemoveFlag(wxTEXT_ATTR_TABS
);
12736 currentStyle
.SetTabs(attr
.GetTabs());
12738 else if (!attr
.HasTabs() && currentStyle
.HasTabs())
12740 clashingAttr
.AddFlag(wxTEXT_ATTR_TABS
);
12741 currentStyle
.RemoveFlag(wxTEXT_ATTR_TABS
);
12744 if (attr
.HasLeftIndent() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LEFT_INDENT
))
12746 if (currentStyle
.HasLeftIndent())
12748 if (currentStyle
.GetLeftIndent() != attr
.GetLeftIndent() || currentStyle
.GetLeftSubIndent() != attr
.GetLeftSubIndent())
12750 // Clash of attr - mark as such
12751 clashingAttr
.AddFlag(wxTEXT_ATTR_LEFT_INDENT
);
12752 currentStyle
.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT
);
12756 currentStyle
.SetLeftIndent(attr
.GetLeftIndent(), attr
.GetLeftSubIndent());
12758 else if (!attr
.HasLeftIndent() && currentStyle
.HasLeftIndent())
12760 clashingAttr
.AddFlag(wxTEXT_ATTR_LEFT_INDENT
);
12761 currentStyle
.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT
);
12764 if (attr
.HasRightIndent() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_RIGHT_INDENT
))
12766 if (currentStyle
.HasRightIndent())
12768 if (currentStyle
.GetRightIndent() != attr
.GetRightIndent())
12770 // Clash of attr - mark as such
12771 clashingAttr
.AddFlag(wxTEXT_ATTR_RIGHT_INDENT
);
12772 currentStyle
.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT
);
12776 currentStyle
.SetRightIndent(attr
.GetRightIndent());
12778 else if (!attr
.HasRightIndent() && currentStyle
.HasRightIndent())
12780 clashingAttr
.AddFlag(wxTEXT_ATTR_RIGHT_INDENT
);
12781 currentStyle
.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT
);
12784 if (attr
.HasParagraphSpacingAfter() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARA_SPACING_AFTER
))
12786 if (currentStyle
.HasParagraphSpacingAfter())
12788 if (currentStyle
.GetParagraphSpacingAfter() != attr
.GetParagraphSpacingAfter())
12790 // Clash of attr - mark as such
12791 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
12792 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
12796 currentStyle
.SetParagraphSpacingAfter(attr
.GetParagraphSpacingAfter());
12798 else if (!attr
.HasParagraphSpacingAfter() && currentStyle
.HasParagraphSpacingAfter())
12800 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
12801 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
12804 if (attr
.HasParagraphSpacingBefore() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARA_SPACING_BEFORE
))
12806 if (currentStyle
.HasParagraphSpacingBefore())
12808 if (currentStyle
.GetParagraphSpacingBefore() != attr
.GetParagraphSpacingBefore())
12810 // Clash of attr - mark as such
12811 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
12812 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
12816 currentStyle
.SetParagraphSpacingBefore(attr
.GetParagraphSpacingBefore());
12818 else if (!attr
.HasParagraphSpacingBefore() && currentStyle
.HasParagraphSpacingBefore())
12820 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
12821 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
12824 if (attr
.HasLineSpacing() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LINE_SPACING
))
12826 if (currentStyle
.HasLineSpacing())
12828 if (currentStyle
.GetLineSpacing() != attr
.GetLineSpacing())
12830 // Clash of attr - mark as such
12831 clashingAttr
.AddFlag(wxTEXT_ATTR_LINE_SPACING
);
12832 currentStyle
.RemoveFlag(wxTEXT_ATTR_LINE_SPACING
);
12836 currentStyle
.SetLineSpacing(attr
.GetLineSpacing());
12838 else if (!attr
.HasLineSpacing() && currentStyle
.HasLineSpacing())
12840 clashingAttr
.AddFlag(wxTEXT_ATTR_LINE_SPACING
);
12841 currentStyle
.RemoveFlag(wxTEXT_ATTR_LINE_SPACING
);
12844 if (attr
.HasCharacterStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_CHARACTER_STYLE_NAME
))
12846 if (currentStyle
.HasCharacterStyleName())
12848 if (currentStyle
.GetCharacterStyleName() != attr
.GetCharacterStyleName())
12850 // Clash of attr - mark as such
12851 clashingAttr
.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
12852 currentStyle
.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
12856 currentStyle
.SetCharacterStyleName(attr
.GetCharacterStyleName());
12858 else if (!attr
.HasCharacterStyleName() && currentStyle
.HasCharacterStyleName())
12860 clashingAttr
.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
12861 currentStyle
.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
12864 if (attr
.HasParagraphStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
))
12866 if (currentStyle
.HasParagraphStyleName())
12868 if (currentStyle
.GetParagraphStyleName() != attr
.GetParagraphStyleName())
12870 // Clash of attr - mark as such
12871 clashingAttr
.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
12872 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
12876 currentStyle
.SetParagraphStyleName(attr
.GetParagraphStyleName());
12878 else if (!attr
.HasParagraphStyleName() && currentStyle
.HasParagraphStyleName())
12880 clashingAttr
.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
12881 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
12884 if (attr
.HasListStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LIST_STYLE_NAME
))
12886 if (currentStyle
.HasListStyleName())
12888 if (currentStyle
.GetListStyleName() != attr
.GetListStyleName())
12890 // Clash of attr - mark as such
12891 clashingAttr
.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
12892 currentStyle
.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
12896 currentStyle
.SetListStyleName(attr
.GetListStyleName());
12898 else if (!attr
.HasListStyleName() && currentStyle
.HasListStyleName())
12900 clashingAttr
.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
12901 currentStyle
.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
12904 if (attr
.HasBulletStyle() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_STYLE
))
12906 if (currentStyle
.HasBulletStyle())
12908 if (currentStyle
.GetBulletStyle() != attr
.GetBulletStyle())
12910 // Clash of attr - mark as such
12911 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_STYLE
);
12912 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE
);
12916 currentStyle
.SetBulletStyle(attr
.GetBulletStyle());
12918 else if (!attr
.HasBulletStyle() && currentStyle
.HasBulletStyle())
12920 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_STYLE
);
12921 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE
);
12924 if (attr
.HasBulletNumber() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_NUMBER
))
12926 if (currentStyle
.HasBulletNumber())
12928 if (currentStyle
.GetBulletNumber() != attr
.GetBulletNumber())
12930 // Clash of attr - mark as such
12931 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NUMBER
);
12932 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER
);
12936 currentStyle
.SetBulletNumber(attr
.GetBulletNumber());
12938 else if (!attr
.HasBulletNumber() && currentStyle
.HasBulletNumber())
12940 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NUMBER
);
12941 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER
);
12944 if (attr
.HasBulletText() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_TEXT
))
12946 if (currentStyle
.HasBulletText())
12948 if (currentStyle
.GetBulletText() != attr
.GetBulletText())
12950 // Clash of attr - mark as such
12951 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_TEXT
);
12952 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT
);
12957 currentStyle
.SetBulletText(attr
.GetBulletText());
12958 currentStyle
.SetBulletFont(attr
.GetBulletFont());
12961 else if (!attr
.HasBulletText() && currentStyle
.HasBulletText())
12963 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_TEXT
);
12964 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT
);
12967 if (attr
.HasBulletName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_NAME
))
12969 if (currentStyle
.HasBulletName())
12971 if (currentStyle
.GetBulletName() != attr
.GetBulletName())
12973 // Clash of attr - mark as such
12974 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NAME
);
12975 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NAME
);
12980 currentStyle
.SetBulletName(attr
.GetBulletName());
12983 else if (!attr
.HasBulletName() && currentStyle
.HasBulletName())
12985 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NAME
);
12986 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NAME
);
12989 if (attr
.HasURL() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_URL
))
12991 if (currentStyle
.HasURL())
12993 if (currentStyle
.GetURL() != attr
.GetURL())
12995 // Clash of attr - mark as such
12996 clashingAttr
.AddFlag(wxTEXT_ATTR_URL
);
12997 currentStyle
.RemoveFlag(wxTEXT_ATTR_URL
);
13002 currentStyle
.SetURL(attr
.GetURL());
13005 else if (!attr
.HasURL() && currentStyle
.HasURL())
13007 clashingAttr
.AddFlag(wxTEXT_ATTR_URL
);
13008 currentStyle
.RemoveFlag(wxTEXT_ATTR_URL
);
13011 if (attr
.HasTextEffects() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_EFFECTS
))
13013 if (currentStyle
.HasTextEffects())
13015 // We need to find the bits in the new attr that are different:
13016 // just look at those bits that are specified by the new attr.
13018 // We need to remove the bits and flags that are not common between current attr
13019 // and new attr. In so doing we need to take account of the styles absent from one or more of the
13020 // previous styles.
13022 int currentRelevantTextEffects
= currentStyle
.GetTextEffects() & attr
.GetTextEffectFlags();
13023 int newRelevantTextEffects
= attr
.GetTextEffects() & attr
.GetTextEffectFlags();
13025 if (currentRelevantTextEffects
!= newRelevantTextEffects
)
13027 // Find the text effects that were different, using XOR
13028 int differentEffects
= currentRelevantTextEffects
^ newRelevantTextEffects
;
13030 // Clash of attr - mark as such
13031 clashingAttr
.SetTextEffectFlags(clashingAttr
.GetTextEffectFlags() | differentEffects
);
13032 currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~differentEffects
);
13037 currentStyle
.SetTextEffects(attr
.GetTextEffects());
13038 currentStyle
.SetTextEffectFlags(attr
.GetTextEffectFlags());
13041 // Mask out the flags and values that cannot be common because they were absent in one or more objecrs
13042 // that we've looked at so far
13043 currentStyle
.SetTextEffects(currentStyle
.GetTextEffects() & ~absentAttr
.GetTextEffectFlags());
13044 currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~absentAttr
.GetTextEffectFlags());
13046 if (currentStyle
.GetTextEffectFlags() == 0)
13047 currentStyle
.RemoveFlag(wxTEXT_ATTR_EFFECTS
);
13049 else if (!attr
.HasTextEffects() && currentStyle
.HasTextEffects())
13051 clashingAttr
.AddFlag(wxTEXT_ATTR_EFFECTS
);
13052 currentStyle
.RemoveFlag(wxTEXT_ATTR_EFFECTS
);
13055 if (attr
.HasOutlineLevel() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_OUTLINE_LEVEL
))
13057 if (currentStyle
.HasOutlineLevel())
13059 if (currentStyle
.GetOutlineLevel() != attr
.GetOutlineLevel())
13061 // Clash of attr - mark as such
13062 clashingAttr
.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
13063 currentStyle
.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
13067 currentStyle
.SetOutlineLevel(attr
.GetOutlineLevel());
13069 else if (!attr
.HasOutlineLevel() && currentStyle
.HasOutlineLevel())
13071 clashingAttr
.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
13072 currentStyle
.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
13076 WX_DEFINE_OBJARRAY(wxRichTextVariantArray
);
13078 IMPLEMENT_DYNAMIC_CLASS(wxRichTextProperties
, wxObject
)
13080 bool wxRichTextProperties::operator==(const wxRichTextProperties
& props
) const
13082 if (m_properties
.GetCount() != props
.GetCount())
13086 for (i
= 0; i
< m_properties
.GetCount(); i
++)
13088 const wxVariant
& var1
= m_properties
[i
];
13089 int idx
= props
.Find(var1
.GetName());
13092 const wxVariant
& var2
= props
.m_properties
[idx
];
13093 if (!(var1
== var2
))
13100 wxArrayString
wxRichTextProperties::GetPropertyNames() const
13104 for (i
= 0; i
< m_properties
.GetCount(); i
++)
13106 arr
.Add(m_properties
[i
].GetName());
13111 int wxRichTextProperties::Find(const wxString
& name
) const
13114 for (i
= 0; i
< m_properties
.GetCount(); i
++)
13116 if (m_properties
[i
].GetName() == name
)
13122 bool wxRichTextProperties::Remove(const wxString
& name
)
13124 int idx
= Find(name
);
13127 m_properties
.RemoveAt(idx
);
13134 wxVariant
* wxRichTextProperties::FindOrCreateProperty(const wxString
& name
)
13136 int idx
= Find(name
);
13137 if (idx
== wxNOT_FOUND
)
13138 SetProperty(name
, wxString());
13140 if (idx
!= wxNOT_FOUND
)
13142 return & (*this)[idx
];
13148 const wxVariant
& wxRichTextProperties::GetProperty(const wxString
& name
) const
13150 static const wxVariant nullVariant
;
13151 int idx
= Find(name
);
13153 return m_properties
[idx
];
13155 return nullVariant
;
13158 wxString
wxRichTextProperties::GetPropertyString(const wxString
& name
) const
13160 return GetProperty(name
).GetString();
13163 long wxRichTextProperties::GetPropertyLong(const wxString
& name
) const
13165 return GetProperty(name
).GetLong();
13168 bool wxRichTextProperties::GetPropertyBool(const wxString
& name
) const
13170 return GetProperty(name
).GetBool();
13173 double wxRichTextProperties::GetPropertyDouble(const wxString
& name
) const
13175 return GetProperty(name
).GetDouble();
13178 void wxRichTextProperties::SetProperty(const wxVariant
& variant
)
13180 wxASSERT(!variant
.GetName().IsEmpty());
13182 int idx
= Find(variant
.GetName());
13185 m_properties
.Add(variant
);
13187 m_properties
[idx
] = variant
;
13190 void wxRichTextProperties::SetProperty(const wxString
& name
, const wxVariant
& variant
)
13192 int idx
= Find(name
);
13193 wxVariant
var(variant
);
13197 m_properties
.Add(var
);
13199 m_properties
[idx
] = var
;
13202 void wxRichTextProperties::SetProperty(const wxString
& name
, const wxString
& value
)
13204 SetProperty(name
, wxVariant(value
, name
));
13207 void wxRichTextProperties::SetProperty(const wxString
& name
, long value
)
13209 SetProperty(name
, wxVariant(value
, name
));
13212 void wxRichTextProperties::SetProperty(const wxString
& name
, double value
)
13214 SetProperty(name
, wxVariant(value
, name
));
13217 void wxRichTextProperties::SetProperty(const wxString
& name
, bool value
)
13219 SetProperty(name
, wxVariant(value
, name
));
13222 void wxRichTextProperties::RemoveProperties(const wxRichTextProperties
& properties
)
13225 for (i
= 0; i
< properties
.GetCount(); i
++)
13227 wxString name
= properties
.GetProperties()[i
].GetName();
13228 if (HasProperty(name
))
13233 void wxRichTextProperties::MergeProperties(const wxRichTextProperties
& properties
)
13236 for (i
= 0; i
< properties
.GetCount(); i
++)
13238 SetProperty(properties
.GetProperties()[i
]);
13242 wxRichTextObject
* wxRichTextObjectAddress::GetObject(wxRichTextParagraphLayoutBox
* topLevelContainer
) const
13244 if (m_address
.GetCount() == 0)
13245 return topLevelContainer
;
13247 wxRichTextCompositeObject
* p
= topLevelContainer
;
13249 while (p
&& i
< m_address
.GetCount())
13251 int pos
= m_address
[i
];
13252 wxASSERT(pos
>= 0 && pos
< (int) p
->GetChildren().GetCount());
13253 if (pos
< 0 || pos
>= (int) p
->GetChildren().GetCount())
13256 wxRichTextObject
* p1
= p
->GetChild(pos
);
13257 if (i
== (m_address
.GetCount()-1))
13260 p
= wxDynamicCast(p1
, wxRichTextCompositeObject
);
13266 bool wxRichTextObjectAddress::Create(wxRichTextParagraphLayoutBox
* topLevelContainer
, wxRichTextObject
* obj
)
13270 if (topLevelContainer
== obj
)
13273 wxRichTextObject
* o
= obj
;
13276 wxRichTextCompositeObject
* p
= wxDynamicCast(o
->GetParent(), wxRichTextCompositeObject
);
13280 int pos
= p
->GetChildren().IndexOf(o
);
13284 m_address
.Insert(pos
, 0);
13286 if (p
== topLevelContainer
)
13295 bool wxRichTextSelection::operator==(const wxRichTextSelection
& sel
) const
13297 if (m_container
!= sel
.m_container
)
13299 if (m_ranges
.GetCount() != sel
.m_ranges
.GetCount())
13302 for (i
= 0; i
< m_ranges
.GetCount(); i
++)
13303 if (!(m_ranges
[i
] == sel
.m_ranges
[i
]))
13308 // Get the selections appropriate to the specified object, if any; returns wxRICHTEXT_NO_SELECTION if none
13309 // or none at the level of the object's container.
13310 wxRichTextRangeArray
wxRichTextSelection::GetSelectionForObject(wxRichTextObject
* obj
) const
13314 wxRichTextParagraphLayoutBox
* container
= obj
->GetParentContainer();
13316 if (container
== m_container
)
13319 container
= obj
->GetContainer();
13322 if (container
->GetParent())
13324 // If we found that our object's container is within the range of
13325 // a selection higher up, then assume the whole original object
13326 // is also selected.
13327 wxRichTextParagraphLayoutBox
* parentContainer
= container
->GetParentContainer();
13328 if (parentContainer
== m_container
)
13330 if (WithinSelection(container
->GetRange().GetStart(), m_ranges
))
13332 wxRichTextRangeArray ranges
;
13333 ranges
.Add(obj
->GetRange());
13338 container
= parentContainer
;
13347 return wxRichTextRangeArray();
13350 // Is the given position within the selection?
13351 bool wxRichTextSelection::WithinSelection(long pos
, wxRichTextObject
* obj
) const
13357 wxRichTextRangeArray selectionRanges
= GetSelectionForObject(obj
);
13358 return WithinSelection(pos
, selectionRanges
);
13362 // Is the given position within the selection range?
13363 bool wxRichTextSelection::WithinSelection(long pos
, const wxRichTextRangeArray
& ranges
)
13366 for (i
= 0; i
< ranges
.GetCount(); i
++)
13368 const wxRichTextRange
& range
= ranges
[i
];
13369 if (pos
>= range
.GetStart() && pos
<= range
.GetEnd())
13375 // Is the given range completely within the selection range?
13376 bool wxRichTextSelection::WithinSelection(const wxRichTextRange
& range
, const wxRichTextRangeArray
& ranges
)
13379 for (i
= 0; i
< ranges
.GetCount(); i
++)
13381 const wxRichTextRange
& eachRange
= ranges
[i
];
13382 if (range
.IsWithin(eachRange
))
13388 IMPLEMENT_CLASS(wxRichTextDrawingHandler
, wxObject
)
13389 IMPLEMENT_CLASS(wxRichTextDrawingContext
, wxObject
)
13391 bool wxRichTextDrawingContext::HasVirtualAttributes(wxRichTextObject
* obj
) const
13393 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
13396 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13397 if (handler
->HasVirtualAttributes(obj
))
13400 node
= node
->GetNext();
13405 wxRichTextAttr
wxRichTextDrawingContext::GetVirtualAttributes(wxRichTextObject
* obj
) const
13407 wxRichTextAttr attr
;
13408 // We apply all handlers, so we can may combine several different attributes
13409 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
13412 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13413 if (handler
->HasVirtualAttributes(obj
))
13415 bool success
= handler
->GetVirtualAttributes(attr
, obj
);
13417 wxUnusedVar(success
);
13420 node
= node
->GetNext();
13425 bool wxRichTextDrawingContext::ApplyVirtualAttributes(wxRichTextAttr
& attr
, wxRichTextObject
* obj
) const
13427 if (HasVirtualAttributes(obj
))
13429 wxRichTextAttr
a(GetVirtualAttributes(obj
));
13437 /// Adds a handler to the end
13438 void wxRichTextBuffer::AddDrawingHandler(wxRichTextDrawingHandler
*handler
)
13440 sm_drawingHandlers
.Append(handler
);
13443 /// Inserts a handler at the front
13444 void wxRichTextBuffer::InsertDrawingHandler(wxRichTextDrawingHandler
*handler
)
13446 sm_drawingHandlers
.Insert( handler
);
13449 /// Removes a handler
13450 bool wxRichTextBuffer::RemoveDrawingHandler(const wxString
& name
)
13452 wxRichTextDrawingHandler
*handler
= FindDrawingHandler(name
);
13455 sm_drawingHandlers
.DeleteObject(handler
);
13463 wxRichTextDrawingHandler
* wxRichTextBuffer::FindDrawingHandler(const wxString
& name
)
13465 wxList::compatibility_iterator node
= sm_drawingHandlers
.GetFirst();
13468 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13469 if (handler
->GetName().Lower() == name
.Lower()) return handler
;
13471 node
= node
->GetNext();
13476 void wxRichTextBuffer::CleanUpDrawingHandlers()
13478 wxList::compatibility_iterator node
= sm_drawingHandlers
.GetFirst();
13481 wxRichTextDrawingHandler
* handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13482 wxList::compatibility_iterator next
= node
->GetNext();
13487 sm_drawingHandlers
.Clear();
13490 void wxRichTextBuffer::AddFieldType(wxRichTextFieldType
*fieldType
)
13492 sm_fieldTypes
[fieldType
->GetName()] = fieldType
;
13495 bool wxRichTextBuffer::RemoveFieldType(const wxString
& name
)
13497 wxRichTextFieldTypeHashMap::iterator it
= sm_fieldTypes
.find(name
);
13498 if (it
== sm_fieldTypes
.end())
13502 wxRichTextFieldType
* fieldType
= it
->second
;
13503 sm_fieldTypes
.erase(it
);
13509 wxRichTextFieldType
*wxRichTextBuffer::FindFieldType(const wxString
& name
)
13511 wxRichTextFieldTypeHashMap::iterator it
= sm_fieldTypes
.find(name
);
13512 if (it
== sm_fieldTypes
.end())
13518 void wxRichTextBuffer::CleanUpFieldTypes()
13520 wxRichTextFieldTypeHashMap::iterator it
;
13521 for( it
= sm_fieldTypes
.begin(); it
!= sm_fieldTypes
.end(); ++it
)
13523 wxRichTextFieldType
* fieldType
= it
->second
;
13527 sm_fieldTypes
.clear();