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 (invalidRange
== wxRICHTEXT_NONE
)
1585 child
->Invalidate(wxRICHTEXT_NONE
);
1587 child
->Invalidate(wxRICHTEXT_ALL
); // All children must be invalidated if within parent range
1590 child
->Invalidate(invalidRange
);
1591 node
= node
->GetNext();
1595 // Move the object recursively, by adding the offset from old to new
1596 void wxRichTextCompositeObject::Move(const wxPoint
& pt
)
1598 wxPoint oldPos
= GetPosition();
1600 wxPoint offset
= pt
- oldPos
;
1602 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1605 wxRichTextObject
* child
= node
->GetData();
1606 wxPoint childPos
= child
->GetPosition() + offset
;
1607 child
->Move(childPos
);
1608 node
= node
->GetNext();
1614 * wxRichTextParagraphLayoutBox
1615 * This box knows how to lay out paragraphs.
1618 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraphLayoutBox
, wxRichTextCompositeObject
)
1620 wxRichTextParagraphLayoutBox::wxRichTextParagraphLayoutBox(wxRichTextObject
* parent
):
1621 wxRichTextCompositeObject(parent
)
1626 wxRichTextParagraphLayoutBox::~wxRichTextParagraphLayoutBox()
1628 if (m_floatCollector
)
1630 delete m_floatCollector
;
1631 m_floatCollector
= NULL
;
1635 /// Initialize the object.
1636 void wxRichTextParagraphLayoutBox::Init()
1640 // For now, assume is the only box and has no initial size.
1641 m_range
= wxRichTextRange(0, -1);
1642 m_ownRange
= wxRichTextRange(0, -1);
1644 m_invalidRange
= wxRICHTEXT_ALL
;
1646 m_partialParagraph
= false;
1647 m_floatCollector
= NULL
;
1650 void wxRichTextParagraphLayoutBox::Clear()
1654 if (m_floatCollector
)
1655 delete m_floatCollector
;
1656 m_floatCollector
= NULL
;
1657 m_partialParagraph
= false;
1661 void wxRichTextParagraphLayoutBox::Copy(const wxRichTextParagraphLayoutBox
& obj
)
1665 wxRichTextCompositeObject::Copy(obj
);
1667 m_partialParagraph
= obj
.m_partialParagraph
;
1668 m_defaultAttributes
= obj
.m_defaultAttributes
;
1671 // Gather information about floating objects; only gather floats for those paragraphs that
1672 // will not be formatted again due to optimization, after which floats will be gathered per-paragraph
1674 bool wxRichTextParagraphLayoutBox::UpdateFloatingObjects(const wxRect
& availableRect
, wxRichTextObject
* untilObj
)
1676 if (m_floatCollector
!= NULL
)
1677 delete m_floatCollector
;
1678 m_floatCollector
= new wxRichTextFloatCollector(availableRect
);
1679 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1680 // Only gather floats up to the point we'll start formatting paragraphs.
1681 while (untilObj
&& node
&& node
->GetData() != untilObj
)
1683 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
1684 wxASSERT (child
!= NULL
);
1686 m_floatCollector
->CollectFloat(child
);
1687 node
= node
->GetNext();
1693 // Returns the style sheet associated with the overall buffer.
1694 wxRichTextStyleSheet
* wxRichTextParagraphLayoutBox::GetStyleSheet() const
1696 return GetBuffer() ? GetBuffer()->GetStyleSheet() : (wxRichTextStyleSheet
*) NULL
;
1699 // Get the number of floating objects at this level
1700 int wxRichTextParagraphLayoutBox::GetFloatingObjectCount() const
1702 if (m_floatCollector
)
1703 return m_floatCollector
->GetFloatingObjectCount();
1708 // Get a list of floating objects
1709 bool wxRichTextParagraphLayoutBox::GetFloatingObjects(wxRichTextObjectList
& objects
) const
1711 if (m_floatCollector
)
1713 return m_floatCollector
->GetFloatingObjects(objects
);
1720 void wxRichTextParagraphLayoutBox::UpdateRanges()
1724 start
= GetRange().GetStart();
1726 CalculateRange(start
, end
);
1730 int wxRichTextParagraphLayoutBox::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
1733 return wxRICHTEXT_HITTEST_NONE
;
1735 int ret
= wxRICHTEXT_HITTEST_NONE
;
1736 if (m_floatCollector
&& (flags
& wxRICHTEXT_HITTEST_NO_FLOATING_OBJECTS
) == 0)
1737 ret
= m_floatCollector
->HitTest(dc
, context
, pt
, textPosition
, obj
, flags
);
1739 if (ret
== wxRICHTEXT_HITTEST_NONE
)
1740 return wxRichTextCompositeObject::HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
1748 /// Draw the floating objects
1749 void wxRichTextParagraphLayoutBox::DrawFloats(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
1751 if (m_floatCollector
)
1752 m_floatCollector
->Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
1755 void wxRichTextParagraphLayoutBox::MoveAnchoredObjectToParagraph(wxRichTextParagraph
* from
, wxRichTextParagraph
* to
, wxRichTextObject
* obj
)
1760 from
->RemoveChild(obj
);
1761 to
->AppendChild(obj
);
1765 bool wxRichTextParagraphLayoutBox::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
1770 wxRect
thisRect(GetPosition(), GetCachedSize());
1772 wxRichTextAttr
attr(GetAttributes());
1773 context
.ApplyVirtualAttributes(attr
, this);
1776 if (selection
.IsValid() && GetParentContainer() != this && selection
.WithinSelection(GetRange().GetStart(), GetParentContainer()))
1777 flags
|= wxRICHTEXT_DRAW_SELECTED
;
1779 // Don't draw guidelines if at top level
1780 int theseFlags
= flags
;
1782 theseFlags
&= ~wxRICHTEXT_DRAW_GUIDELINES
;
1783 DrawBoxAttributes(dc
, GetBuffer(), attr
, thisRect
, theseFlags
);
1785 DrawFloats(dc
, context
, range
, selection
, rect
, descent
, style
);
1786 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1789 wxRichTextObject
* child
= node
->GetData();
1791 if (child
&& !child
->GetRange().IsOutside(range
))
1793 wxRect
childRect(child
->GetPosition(), child
->GetCachedSize());
1794 wxRichTextRange childRange
= range
;
1795 if (child
->IsTopLevel())
1797 childRange
= child
->GetOwnRange();
1800 if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) == 0) && childRect
.GetTop() > rect
.GetBottom())
1805 else if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) == 0) && childRect
.GetBottom() < rect
.GetTop())
1810 child
->Draw(dc
, context
, childRange
, selection
, rect
, descent
, style
);
1813 node
= node
->GetNext();
1818 /// Lay the item out
1819 bool wxRichTextParagraphLayoutBox::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
)
1821 SetPosition(rect
.GetPosition());
1826 wxRect availableSpace
;
1827 bool formatRect
= (style
& wxRICHTEXT_LAYOUT_SPECIFIED_RECT
) == wxRICHTEXT_LAYOUT_SPECIFIED_RECT
;
1829 wxRichTextAttr
attr(GetAttributes());
1830 context
.ApplyVirtualAttributes(attr
, this);
1832 // If only laying out a specific area, the passed rect has a different meaning:
1833 // the visible part of the buffer. This is used in wxRichTextCtrl::OnSize,
1834 // so that during a size, only the visible part will be relaid out, or
1835 // it would take too long causing flicker. As an approximation, we assume that
1836 // everything up to the start of the visible area is laid out correctly.
1839 wxRect
rect2(0, 0, rect
.width
, rect
.height
);
1840 availableSpace
= GetAvailableContentArea(dc
, context
, rect2
);
1842 // Invalidate the part of the buffer from the first visible line
1843 // to the end. If other parts of the buffer are currently invalid,
1844 // then they too will be taken into account if they are above
1845 // the visible point.
1847 wxRichTextLine
* line
= GetLineAtYPosition(rect
.y
);
1849 startPos
= line
->GetAbsoluteRange().GetStart();
1851 Invalidate(wxRichTextRange(startPos
, GetOwnRange().GetEnd()));
1855 availableSpace
= GetAvailableContentArea(dc
, context
, rect
);
1858 // Fix the width if we're at the top level
1860 attr
.GetTextBoxAttr().GetWidth().SetValue(rect
.GetWidth(), wxTEXT_ATTR_UNITS_PIXELS
);
1862 int leftMargin
, rightMargin
, topMargin
, bottomMargin
;
1863 wxRichTextObject::GetTotalMargin(dc
, GetBuffer(), attr
, leftMargin
, rightMargin
,
1864 topMargin
, bottomMargin
);
1869 // The maximum paragraph maximum width, so we can set the overall maximum width for this object
1870 int maxMaxWidth
= 0;
1872 // The maximum paragraph minimum width, so we can set the overall minimum width for this object
1873 int maxMinWidth
= 0;
1875 // If we have vertical alignment, we must recalculate everything.
1876 bool hasVerticalAlignment
= (attr
.GetTextBoxAttr().HasVerticalAlignment() &&
1877 (attr
.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP
));
1879 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1881 bool layoutAll
= true;
1883 // Get invalid range, rounding to paragraph start/end.
1884 wxRichTextRange invalidRange
= GetInvalidRange(true);
1886 if (invalidRange
== wxRICHTEXT_NONE
&& !formatRect
)
1889 if (invalidRange
== wxRICHTEXT_ALL
|| hasVerticalAlignment
)
1891 else // If we know what range is affected, start laying out from that point on.
1892 if (invalidRange
.GetStart() >= GetOwnRange().GetStart())
1894 wxRichTextParagraph
* firstParagraph
= GetParagraphAtPosition(invalidRange
.GetStart());
1897 wxRichTextObjectList::compatibility_iterator firstNode
= m_children
.Find(firstParagraph
);
1898 wxRichTextObjectList::compatibility_iterator previousNode
;
1900 previousNode
= firstNode
->GetPrevious();
1905 wxRichTextParagraph
* previousParagraph
= wxDynamicCast(previousNode
->GetData(), wxRichTextParagraph
);
1906 availableSpace
.y
= previousParagraph
->GetPosition().y
+ previousParagraph
->GetCachedSize().y
;
1909 // Now we're going to start iterating from the first affected paragraph.
1917 // Gather information about only those floating objects that will not be formatted,
1918 // after which floats will be gathered per-paragraph during layout.
1919 UpdateFloatingObjects(availableSpace
, node
? node
->GetData() : (wxRichTextObject
*) NULL
);
1921 // A way to force speedy rest-of-buffer layout (the 'else' below)
1922 bool forceQuickLayout
= false;
1924 // First get the size of the paragraphs we won't be laying out
1925 wxRichTextObjectList::compatibility_iterator n
= m_children
.GetFirst();
1926 while (n
&& n
!= node
)
1928 wxRichTextParagraph
* child
= wxDynamicCast(n
->GetData(), wxRichTextParagraph
);
1931 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
1932 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
1933 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
1940 // Assume this box only contains paragraphs
1942 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
1943 // Unsure if this is needed
1944 // wxCHECK_MSG( child, false, wxT("Unknown object in layout") );
1946 if (child
&& child
->IsShown())
1948 // TODO: what if the child hasn't been laid out (e.g. involved in Undo) but still has 'old' lines
1949 if ( !forceQuickLayout
&&
1951 child
->GetLines().IsEmpty() ||
1952 !child
->GetRange().IsOutside(invalidRange
)) )
1954 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1955 // lays out the object again using the minimum size
1956 child
->LayoutToBestSize(dc
, context
, GetBuffer(),
1957 attr
, child
->GetAttributes(), availableSpace
, rect
, style
&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT
);
1959 // Layout must set the cached size
1960 availableSpace
.y
+= child
->GetCachedSize().y
;
1961 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
1962 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
1963 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
1965 // If we're just formatting the visible part of the buffer,
1966 // and we're now past the bottom of the window, and we don't have any
1967 // floating objects (since they may cause wrapping to change for the rest of the
1968 // the buffer), start quick layout.
1969 if (!hasVerticalAlignment
&& formatRect
&& child
->GetPosition().y
> rect
.GetBottom() && GetFloatingObjectCount() == 0)
1970 forceQuickLayout
= true;
1974 // We're outside the immediately affected range, so now let's just
1975 // move everything up or down. This assumes that all the children have previously
1976 // been laid out and have wrapped line lists associated with them.
1977 // TODO: check all paragraphs before the affected range.
1979 int inc
= availableSpace
.y
- child
->GetPosition().y
;
1983 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
1986 if (child
->GetLines().GetCount() == 0)
1988 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1989 // lays out the object again using the minimum size
1990 child
->LayoutToBestSize(dc
, context
, GetBuffer(),
1991 attr
, child
->GetAttributes(), availableSpace
, rect
, style
&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT
);
1993 //child->Layout(dc, availableChildRect, style);
1996 child
->Move(wxPoint(child
->GetPosition().x
, child
->GetPosition().y
+ inc
));
1998 availableSpace
.y
+= child
->GetCachedSize().y
;
1999 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
2000 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
2001 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
2004 node
= node
->GetNext();
2010 node
= node
->GetNext();
2013 node
= m_children
.GetLast();
2014 if (node
&& node
->GetData()->IsShown())
2016 wxRichTextObject
* child
= node
->GetData();
2017 maxHeight
= child
->GetPosition().y
- (GetPosition().y
+ topMargin
) + child
->GetCachedSize().y
;
2020 maxHeight
= 0; // topMargin + bottomMargin;
2022 // Check the bottom edge of any floating object
2023 if (GetFloatCollector() && GetFloatCollector()->HasFloats())
2025 int bottom
= GetFloatCollector()->GetLastRectBottom();
2026 if (bottom
> maxHeight
)
2030 if (attr
.GetTextBoxAttr().GetSize().GetWidth().IsValid())
2032 wxRect r
= AdjustAvailableSpace(dc
, GetBuffer(), wxRichTextAttr() /* not used */, attr
, parentRect
, parentRect
);
2033 int w
= r
.GetWidth();
2035 // Convert external to content rect
2036 w
= w
- leftMargin
- rightMargin
;
2037 maxWidth
= wxMax(maxWidth
, w
);
2038 maxMaxWidth
= wxMax(maxMaxWidth
, w
);
2042 // TODO: Make sure the layout box's position reflects
2043 // the position of the children, but without
2044 // breaking layout of a box within a paragraph.
2047 // TODO: (also in para layout) should set the
2048 // object's size to an absolute one if specified,
2049 // but if not specified, calculate it from content.
2051 // We need to add back the margins etc.
2053 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2054 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxWidth
, maxHeight
));
2055 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2056 SetCachedSize(marginRect
.GetSize());
2059 // The maximum size is the greatest of all maximum widths for all paragraphs.
2061 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2062 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxMaxWidth
, maxHeight
)); // Actually max height is a lie, we can't know it
2063 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2064 SetMaxSize(marginRect
.GetSize());
2067 // The minimum size is the greatest of all minimum widths for all paragraphs.
2069 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2070 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxMinWidth
, maxHeight
)); // Actually max height is a lie, we can't know it
2071 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2072 SetMinSize(marginRect
.GetSize());
2075 if (attr
.GetTextBoxAttr().HasVerticalAlignment() &&
2076 (attr
.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP
))
2079 int leftOverSpace
= availableSpace
.height
- topMargin
- bottomMargin
- maxHeight
;
2080 if (leftOverSpace
> 0)
2082 if (attr
.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_CENTRE
)
2084 yOffset
= (leftOverSpace
/2);
2086 else if (attr
.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_BOTTOM
)
2088 yOffset
= leftOverSpace
;
2092 // Move all the children to vertically align the content
2093 // This doesn't take into account floating objects, unfortunately.
2096 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2099 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2101 child
->Move(wxPoint(child
->GetPosition().x
, child
->GetPosition().y
+ yOffset
));
2103 node
= node
->GetNext();
2108 m_invalidRange
= wxRICHTEXT_NONE
;
2113 /// Get/set the size for the given range.
2114 bool wxRichTextParagraphLayoutBox::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* WXUNUSED(partialExtents
)) const
2118 wxRichTextObjectList::compatibility_iterator startPara
= wxRichTextObjectList::compatibility_iterator();
2119 wxRichTextObjectList::compatibility_iterator endPara
= wxRichTextObjectList::compatibility_iterator();
2121 // First find the first paragraph whose starting position is within the range.
2122 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2125 // child is a paragraph
2126 wxRichTextObject
* child
= node
->GetData();
2127 const wxRichTextRange
& r
= child
->GetRange();
2129 if (r
.GetStart() <= range
.GetStart() && r
.GetEnd() >= range
.GetStart())
2135 node
= node
->GetNext();
2138 // Next find the last paragraph containing part of the range
2139 node
= m_children
.GetFirst();
2142 // child is a paragraph
2143 wxRichTextObject
* child
= node
->GetData();
2144 const wxRichTextRange
& r
= child
->GetRange();
2146 if (r
.GetStart() <= range
.GetEnd() && r
.GetEnd() >= range
.GetEnd())
2152 node
= node
->GetNext();
2155 if (!startPara
|| !endPara
)
2158 // Now we can add up the sizes
2159 for (node
= startPara
; node
; node
= node
->GetNext())
2161 // child is a paragraph
2162 wxRichTextObject
* child
= node
->GetData();
2163 const wxRichTextRange
& childRange
= child
->GetRange();
2164 wxRichTextRange rangeToFind
= range
;
2165 rangeToFind
.LimitTo(childRange
);
2167 if (child
->IsTopLevel())
2168 rangeToFind
= child
->GetOwnRange();
2172 int childDescent
= 0;
2173 child
->GetRangeSize(rangeToFind
, childSize
, childDescent
, dc
, context
, flags
, position
);
2175 descent
= wxMax(childDescent
, descent
);
2177 sz
.x
= wxMax(sz
.x
, childSize
.x
);
2178 sz
.y
+= childSize
.y
;
2180 if (node
== endPara
)
2189 /// Get the paragraph at the given position
2190 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphAtPosition(long pos
, bool caretPosition
) const
2195 // First find the first paragraph whose starting position is within the range.
2196 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2199 // child is a paragraph
2200 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2201 // wxASSERT (child != NULL);
2205 // Return first child in buffer if position is -1
2209 if (child
->GetRange().Contains(pos
))
2213 node
= node
->GetNext();
2218 /// Get the line at the given position
2219 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineAtPosition(long pos
, bool caretPosition
) const
2224 // First find the first paragraph whose starting position is within the range.
2225 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2228 wxRichTextObject
* obj
= (wxRichTextObject
*) node
->GetData();
2229 if (obj
->GetRange().Contains(pos
))
2231 // child is a paragraph
2232 wxRichTextParagraph
* child
= wxDynamicCast(obj
, wxRichTextParagraph
);
2233 // wxASSERT (child != NULL);
2237 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2240 wxRichTextLine
* line
= node2
->GetData();
2242 wxRichTextRange range
= line
->GetAbsoluteRange();
2244 if (range
.Contains(pos
) ||
2246 // If the position is end-of-paragraph, then return the last line of
2247 // of the paragraph.
2248 ((range
.GetEnd() == child
->GetRange().GetEnd()-1) && (pos
== child
->GetRange().GetEnd())))
2251 node2
= node2
->GetNext();
2256 node
= node
->GetNext();
2259 int lineCount
= GetLineCount();
2261 return GetLineForVisibleLineNumber(lineCount
-1);
2266 /// Get the line at the given y pixel position, or the last line.
2267 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineAtYPosition(int y
) const
2269 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2272 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2273 // wxASSERT (child != NULL);
2277 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2280 wxRichTextLine
* line
= node2
->GetData();
2282 wxRect
rect(line
->GetRect());
2284 if (y
<= rect
.GetBottom())
2287 node2
= node2
->GetNext();
2291 node
= node
->GetNext();
2295 int lineCount
= GetLineCount();
2297 return GetLineForVisibleLineNumber(lineCount
-1);
2302 /// Get the number of visible lines
2303 int wxRichTextParagraphLayoutBox::GetLineCount() const
2307 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2310 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2311 // wxASSERT (child != NULL);
2314 count
+= child
->GetLines().GetCount();
2316 node
= node
->GetNext();
2322 /// Get the paragraph for a given line
2323 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphForLine(wxRichTextLine
* line
) const
2325 return GetParagraphAtPosition(line
->GetAbsoluteRange().GetStart());
2328 /// Get the line size at the given position
2329 wxSize
wxRichTextParagraphLayoutBox::GetLineSizeAtPosition(long pos
, bool caretPosition
) const
2331 wxRichTextLine
* line
= GetLineAtPosition(pos
, caretPosition
);
2334 return line
->GetSize();
2337 return wxSize(0, 0);
2341 /// Convenience function to add a paragraph of text
2342 wxRichTextRange
wxRichTextParagraphLayoutBox::AddParagraph(const wxString
& text
, wxRichTextAttr
* paraStyle
)
2344 // Don't use the base style, just the default style, and the base style will
2345 // be combined at display time.
2346 // Divide into paragraph and character styles.
2348 wxRichTextAttr defaultCharStyle
;
2349 wxRichTextAttr defaultParaStyle
;
2351 // If the default style is a named paragraph style, don't apply any character formatting
2352 // to the initial text string.
2353 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2355 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2357 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2360 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2362 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2363 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2365 wxRichTextParagraph
* para
= new wxRichTextParagraph(text
, this, pStyle
, cStyle
);
2366 para
->GetAttributes().GetTextBoxAttr().Reset();
2372 return para
->GetRange();
2375 /// Adds multiple paragraphs, based on newlines.
2376 wxRichTextRange
wxRichTextParagraphLayoutBox::AddParagraphs(const wxString
& text
, wxRichTextAttr
* paraStyle
)
2378 // Don't use the base style, just the default style, and the base style will
2379 // be combined at display time.
2380 // Divide into paragraph and character styles.
2382 wxRichTextAttr defaultCharStyle
;
2383 wxRichTextAttr defaultParaStyle
;
2385 // If the default style is a named paragraph style, don't apply any character formatting
2386 // to the initial text string.
2387 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2389 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2391 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2394 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2396 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2397 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2399 wxRichTextParagraph
* firstPara
= NULL
;
2400 wxRichTextParagraph
* lastPara
= NULL
;
2402 wxRichTextRange
range(-1, -1);
2405 size_t len
= text
.length();
2407 wxRichTextParagraph
* para
= new wxRichTextParagraph(wxEmptyString
, this, pStyle
, cStyle
);
2408 para
->GetAttributes().GetTextBoxAttr().Reset();
2417 wxChar ch
= text
[i
];
2418 if (ch
== wxT('\n') || ch
== wxT('\r'))
2422 wxRichTextPlainText
* plainText
= (wxRichTextPlainText
*) para
->GetChildren().GetFirst()->GetData();
2423 plainText
->SetText(line
);
2425 para
= new wxRichTextParagraph(wxEmptyString
, this, pStyle
, cStyle
);
2426 para
->GetAttributes().GetTextBoxAttr().Reset();
2431 line
= wxEmptyString
;
2442 wxRichTextPlainText
* plainText
= (wxRichTextPlainText
*) para
->GetChildren().GetFirst()->GetData();
2443 plainText
->SetText(line
);
2448 return wxRichTextRange(firstPara
->GetRange().GetStart(), lastPara
->GetRange().GetEnd());
2451 /// Convenience function to add an image
2452 wxRichTextRange
wxRichTextParagraphLayoutBox::AddImage(const wxImage
& image
, wxRichTextAttr
* paraStyle
)
2454 // Don't use the base style, just the default style, and the base style will
2455 // be combined at display time.
2456 // Divide into paragraph and character styles.
2458 wxRichTextAttr defaultCharStyle
;
2459 wxRichTextAttr defaultParaStyle
;
2461 // If the default style is a named paragraph style, don't apply any character formatting
2462 // to the initial text string.
2463 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2465 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2467 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2470 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2472 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2473 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2475 wxRichTextParagraph
* para
= new wxRichTextParagraph(this, pStyle
);
2476 para
->GetAttributes().GetTextBoxAttr().Reset();
2478 para
->AppendChild(new wxRichTextImage(image
, this, cStyle
));
2482 return para
->GetRange();
2486 /// Insert fragment into this box at the given position. If partialParagraph is true,
2487 /// it is assumed that the last (or only) paragraph is just a piece of data with no paragraph
2490 bool wxRichTextParagraphLayoutBox::InsertFragment(long position
, wxRichTextParagraphLayoutBox
& fragment
)
2492 // First, find the first paragraph whose starting position is within the range.
2493 wxRichTextParagraph
* para
= GetParagraphAtPosition(position
);
2496 wxRichTextAttr originalAttr
= para
->GetAttributes();
2498 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(para
);
2500 // Now split at this position, returning the object to insert the new
2501 // ones in front of.
2502 wxRichTextObject
* nextObject
= para
->SplitAt(position
);
2504 // Special case: partial paragraph, just one paragraph. Might be a small amount of
2505 // text, for example, so let's optimize.
2507 if (fragment
.GetPartialParagraph() && fragment
.GetChildren().GetCount() == 1)
2509 // Add the first para to this para...
2510 wxRichTextObjectList::compatibility_iterator firstParaNode
= fragment
.GetChildren().GetFirst();
2514 // Iterate through the fragment paragraph inserting the content into this paragraph.
2515 wxRichTextParagraph
* firstPara
= wxDynamicCast(firstParaNode
->GetData(), wxRichTextParagraph
);
2516 wxASSERT (firstPara
!= NULL
);
2518 wxRichTextObjectList::compatibility_iterator objectNode
= firstPara
->GetChildren().GetFirst();
2521 wxRichTextObject
* newObj
= objectNode
->GetData()->Clone();
2526 para
->AppendChild(newObj
);
2530 // Insert before nextObject
2531 para
->InsertChild(newObj
, nextObject
);
2534 objectNode
= objectNode
->GetNext();
2541 // Procedure for inserting a fragment consisting of a number of
2544 // 1. Remove and save the content that's after the insertion point, for adding
2545 // back once we've added the fragment.
2546 // 2. Add the content from the first fragment paragraph to the current
2548 // 3. Add remaining fragment paragraphs after the current paragraph.
2549 // 4. Add back the saved content from the first paragraph. If partialParagraph
2550 // is true, add it to the last paragraph added and not a new one.
2552 // 1. Remove and save objects after split point.
2553 wxList savedObjects
;
2555 para
->MoveToList(nextObject
, savedObjects
);
2557 // 2. Add the content from the 1st fragment paragraph.
2558 wxRichTextObjectList::compatibility_iterator firstParaNode
= fragment
.GetChildren().GetFirst();
2562 wxRichTextParagraph
* firstPara
= wxDynamicCast(firstParaNode
->GetData(), wxRichTextParagraph
);
2563 wxASSERT(firstPara
!= NULL
);
2565 if (!(fragment
.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
))
2566 para
->SetAttributes(firstPara
->GetAttributes());
2568 // Save empty paragraph attributes for appending later
2569 // These are character attributes deliberately set for a new paragraph. Without this,
2570 // we couldn't pass default attributes when appending a new paragraph.
2571 wxRichTextAttr emptyParagraphAttributes
;
2573 wxRichTextObjectList::compatibility_iterator objectNode
= firstPara
->GetChildren().GetFirst();
2575 if (objectNode
&& firstPara
->GetChildren().GetCount() == 1 && objectNode
->GetData()->IsEmpty())
2576 emptyParagraphAttributes
= objectNode
->GetData()->GetAttributes();
2580 wxRichTextObject
* newObj
= objectNode
->GetData()->Clone();
2583 para
->AppendChild(newObj
);
2585 objectNode
= objectNode
->GetNext();
2588 // 3. Add remaining fragment paragraphs after the current paragraph.
2589 wxRichTextObjectList::compatibility_iterator nextParagraphNode
= node
->GetNext();
2590 wxRichTextObject
* nextParagraph
= NULL
;
2591 if (nextParagraphNode
)
2592 nextParagraph
= nextParagraphNode
->GetData();
2594 wxRichTextObjectList::compatibility_iterator i
= fragment
.GetChildren().GetFirst()->GetNext();
2595 wxRichTextParagraph
* finalPara
= para
;
2597 bool needExtraPara
= (!i
|| !fragment
.GetPartialParagraph());
2599 // If there was only one paragraph, we need to insert a new one.
2602 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2603 wxASSERT( para
!= NULL
);
2605 finalPara
= (wxRichTextParagraph
*) para
->Clone();
2608 InsertChild(finalPara
, nextParagraph
);
2610 AppendChild(finalPara
);
2615 // If there was only one paragraph, or we have full paragraphs in our fragment,
2616 // we need to insert a new one.
2619 finalPara
= new wxRichTextParagraph
;
2622 InsertChild(finalPara
, nextParagraph
);
2624 AppendChild(finalPara
);
2627 // 4. Add back the remaining content.
2631 finalPara
->MoveFromList(savedObjects
);
2633 // Ensure there's at least one object
2634 if (finalPara
->GetChildCount() == 0)
2636 wxRichTextPlainText
* text
= new wxRichTextPlainText(wxEmptyString
);
2637 text
->SetAttributes(emptyParagraphAttributes
);
2639 finalPara
->AppendChild(text
);
2643 if ((fragment
.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
) && firstPara
)
2644 finalPara
->SetAttributes(firstPara
->GetAttributes());
2645 else if (finalPara
&& finalPara
!= para
)
2646 finalPara
->SetAttributes(originalAttr
);
2654 wxRichTextObjectList::compatibility_iterator i
= fragment
.GetChildren().GetFirst();
2657 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2658 wxASSERT( para
!= NULL
);
2660 AppendChild(para
->Clone());
2669 /// Make a copy of the fragment corresponding to the given range, putting it in 'fragment'.
2670 /// If there was an incomplete paragraph at the end, partialParagraph is set to true.
2671 bool wxRichTextParagraphLayoutBox::CopyFragment(const wxRichTextRange
& range
, wxRichTextParagraphLayoutBox
& fragment
)
2673 wxRichTextObjectList::compatibility_iterator i
= GetChildren().GetFirst();
2676 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2677 wxASSERT( para
!= NULL
);
2679 if (!para
->GetRange().IsOutside(range
))
2681 fragment
.AppendChild(para
->Clone());
2686 // Now top and tail the first and last paragraphs in our new fragment (which might be the same).
2687 if (!fragment
.IsEmpty())
2689 wxRichTextParagraph
* firstPara
= wxDynamicCast(fragment
.GetChildren().GetFirst()->GetData(), wxRichTextParagraph
);
2690 wxASSERT( firstPara
!= NULL
);
2692 wxRichTextParagraph
* lastPara
= wxDynamicCast(fragment
.GetChildren().GetLast()->GetData(), wxRichTextParagraph
);
2693 wxASSERT( lastPara
!= NULL
);
2695 if (!firstPara
|| !lastPara
)
2698 bool isFragment
= (range
.GetEnd() < lastPara
->GetRange().GetEnd());
2700 long firstPos
= firstPara
->GetRange().GetStart();
2702 // Adjust for renumbering from zero
2703 wxRichTextRange
topTailRange(range
.GetStart() - firstPos
, range
.GetEnd() - firstPos
);
2706 fragment
.CalculateRange(0, end
);
2708 // Chop off the start of the paragraph
2709 if (topTailRange
.GetStart() > 0)
2711 wxRichTextRange
r(0, topTailRange
.GetStart()-1);
2712 firstPara
->DeleteRange(r
);
2714 // Make sure the numbering is correct
2715 fragment
.CalculateRange(0, end
);
2717 // Now, we've deleted some positions, so adjust the range
2719 topTailRange
.SetStart(range
.GetLength());
2720 topTailRange
.SetEnd(fragment
.GetOwnRange().GetEnd());
2724 topTailRange
.SetStart(range
.GetLength());
2725 topTailRange
.SetEnd(fragment
.GetOwnRange().GetEnd());
2728 if (topTailRange
.GetStart() < lastPara
->GetRange().GetEnd())
2730 lastPara
->DeleteRange(topTailRange
);
2732 // Make sure the numbering is correct
2734 fragment
.CalculateRange(0, end
);
2736 // We only have part of a paragraph at the end
2737 fragment
.SetPartialParagraph(true);
2741 // We have a partial paragraph (don't save last new paragraph marker)
2742 // or complete paragraph
2743 fragment
.SetPartialParagraph(isFragment
);
2750 /// Given a position, get the number of the visible line (potentially many to a paragraph),
2751 /// starting from zero at the start of the buffer.
2752 long wxRichTextParagraphLayoutBox::GetVisibleLineNumber(long pos
, bool caretPosition
, bool startOfLine
) const
2759 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2762 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2763 // wxASSERT( child != NULL );
2767 if (child
->GetRange().Contains(pos
))
2769 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2772 wxRichTextLine
* line
= node2
->GetData();
2773 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
2775 if (lineRange
.Contains(pos
) || pos
== lineRange
.GetStart())
2777 // If the caret is displayed at the end of the previous wrapped line,
2778 // we want to return the line it's _displayed_ at (not the actual line
2779 // containing the position).
2780 if (lineRange
.GetStart() == pos
&& !startOfLine
&& child
->GetRange().GetStart() != pos
)
2781 return lineCount
- 1;
2788 node2
= node2
->GetNext();
2790 // If we didn't find it in the lines, it must be
2791 // the last position of the paragraph. So return the last line.
2795 lineCount
+= child
->GetLines().GetCount();
2798 node
= node
->GetNext();
2805 /// Given a line number, get the corresponding wxRichTextLine object.
2806 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineForVisibleLineNumber(long lineNumber
) const
2810 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2813 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2814 // wxASSERT(child != NULL);
2818 if (lineNumber
< (int) (child
->GetLines().GetCount() + lineCount
))
2820 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2823 wxRichTextLine
* line
= node2
->GetData();
2825 if (lineCount
== lineNumber
)
2830 node2
= node2
->GetNext();
2834 lineCount
+= child
->GetLines().GetCount();
2837 node
= node
->GetNext();
2844 /// Delete range from layout.
2845 bool wxRichTextParagraphLayoutBox::DeleteRange(const wxRichTextRange
& range
)
2847 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2849 wxRichTextParagraph
* firstPara
= NULL
;
2852 wxRichTextParagraph
* obj
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2853 // wxASSERT (obj != NULL);
2855 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
2859 // Delete the range in each paragraph
2861 if (!obj
->GetRange().IsOutside(range
))
2863 // Deletes the content of this object within the given range
2864 obj
->DeleteRange(range
);
2866 wxRichTextRange thisRange
= obj
->GetRange();
2867 wxRichTextAttr thisAttr
= obj
->GetAttributes();
2869 // If the whole paragraph is within the range to delete,
2870 // delete the whole thing.
2871 if (range
.GetStart() <= thisRange
.GetStart() && range
.GetEnd() >= thisRange
.GetEnd())
2873 // Delete the whole object
2874 RemoveChild(obj
, true);
2877 else if (!firstPara
)
2880 // If the range includes the paragraph end, we need to join this
2881 // and the next paragraph.
2882 if (range
.GetEnd() <= thisRange
.GetEnd())
2884 // We need to move the objects from the next paragraph
2885 // to this paragraph
2887 wxRichTextParagraph
* nextParagraph
= NULL
;
2888 if ((range
.GetEnd() < thisRange
.GetEnd()) && obj
)
2889 nextParagraph
= obj
;
2892 // We're ending at the end of the paragraph, so merge the _next_ paragraph.
2894 nextParagraph
= wxDynamicCast(next
->GetData(), wxRichTextParagraph
);
2897 bool applyFinalParagraphStyle
= firstPara
&& nextParagraph
&& nextParagraph
!= firstPara
;
2899 wxRichTextAttr nextParaAttr
;
2900 if (applyFinalParagraphStyle
)
2902 // Special case when deleting the end of a paragraph - use _this_ paragraph's style,
2903 // not the next one.
2904 if (range
.GetStart() == range
.GetEnd() && range
.GetStart() == thisRange
.GetEnd())
2905 nextParaAttr
= thisAttr
;
2907 nextParaAttr
= nextParagraph
->GetAttributes();
2910 if (firstPara
&& nextParagraph
&& firstPara
!= nextParagraph
)
2912 // Move the objects to the previous para
2913 wxRichTextObjectList::compatibility_iterator node1
= nextParagraph
->GetChildren().GetFirst();
2917 wxRichTextObject
* obj1
= node1
->GetData();
2919 firstPara
->AppendChild(obj1
);
2921 wxRichTextObjectList::compatibility_iterator next1
= node1
->GetNext();
2922 nextParagraph
->GetChildren().Erase(node1
);
2927 // Delete the paragraph
2928 RemoveChild(nextParagraph
, true);
2931 // Avoid empty paragraphs
2932 if (firstPara
&& firstPara
->GetChildren().GetCount() == 0)
2934 wxRichTextPlainText
* text
= new wxRichTextPlainText(wxEmptyString
);
2935 firstPara
->AppendChild(text
);
2938 if (applyFinalParagraphStyle
)
2939 firstPara
->SetAttributes(nextParaAttr
);
2952 /// Get any text in this object for the given range
2953 wxString
wxRichTextParagraphLayoutBox::GetTextForRange(const wxRichTextRange
& range
) const
2957 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2960 wxRichTextObject
* child
= node
->GetData();
2961 if (!child
->GetRange().IsOutside(range
))
2963 wxRichTextRange childRange
= range
;
2964 childRange
.LimitTo(child
->GetRange());
2966 wxString childText
= child
->GetTextForRange(childRange
);
2970 if ((childRange
.GetEnd() == child
->GetRange().GetEnd()) && node
->GetNext())
2975 node
= node
->GetNext();
2981 /// Get all the text
2982 wxString
wxRichTextParagraphLayoutBox::GetText() const
2984 return GetTextForRange(GetOwnRange());
2987 /// Get the paragraph by number
2988 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphAtLine(long paragraphNumber
) const
2990 if ((size_t) paragraphNumber
>= GetChildCount())
2993 return (wxRichTextParagraph
*) GetChild((size_t) paragraphNumber
);
2996 /// Get the length of the paragraph
2997 int wxRichTextParagraphLayoutBox::GetParagraphLength(long paragraphNumber
) const
2999 wxRichTextParagraph
* para
= GetParagraphAtLine(paragraphNumber
);
3001 return para
->GetRange().GetLength() - 1; // don't include newline
3006 /// Get the text of the paragraph
3007 wxString
wxRichTextParagraphLayoutBox::GetParagraphText(long paragraphNumber
) const
3009 wxRichTextParagraph
* para
= GetParagraphAtLine(paragraphNumber
);
3011 return para
->GetTextForRange(para
->GetRange());
3013 return wxEmptyString
;
3016 /// Convert zero-based line column and paragraph number to a position.
3017 long wxRichTextParagraphLayoutBox::XYToPosition(long x
, long y
) const
3019 wxRichTextParagraph
* para
= GetParagraphAtLine(y
);
3022 return para
->GetRange().GetStart() + x
;
3028 /// Convert zero-based position to line column and paragraph number
3029 bool wxRichTextParagraphLayoutBox::PositionToXY(long pos
, long* x
, long* y
) const
3031 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
);
3035 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3038 wxRichTextObject
* child
= node
->GetData();
3042 node
= node
->GetNext();
3046 *x
= pos
- para
->GetRange().GetStart();
3054 /// Get the leaf object in a paragraph at this position.
3055 /// Given a line number, get the corresponding wxRichTextLine object.
3056 wxRichTextObject
* wxRichTextParagraphLayoutBox::GetLeafObjectAtPosition(long position
) const
3058 wxRichTextParagraph
* para
= GetParagraphAtPosition(position
);
3061 wxRichTextObjectList::compatibility_iterator node
= para
->GetChildren().GetFirst();
3065 wxRichTextObject
* child
= node
->GetData();
3066 if (child
->GetRange().Contains(position
))
3069 node
= node
->GetNext();
3071 if (position
== para
->GetRange().GetEnd() && para
->GetChildCount() > 0)
3072 return para
->GetChildren().GetLast()->GetData();
3077 /// Set character or paragraph text attributes: apply character styles only to immediate text nodes
3078 bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange
& range
, const wxRichTextAttr
& style
, int flags
)
3080 bool characterStyle
= false;
3081 bool paragraphStyle
= false;
3083 if (style
.IsCharacterStyle())
3084 characterStyle
= true;
3085 if (style
.IsParagraphStyle())
3086 paragraphStyle
= true;
3088 wxRichTextBuffer
* buffer
= GetBuffer();
3090 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
3091 bool applyMinimal
= ((flags
& wxRICHTEXT_SETSTYLE_OPTIMIZE
) != 0);
3092 bool parasOnly
= ((flags
& wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY
) != 0);
3093 bool charactersOnly
= ((flags
& wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY
) != 0);
3094 bool resetExistingStyle
= ((flags
& wxRICHTEXT_SETSTYLE_RESET
) != 0);
3095 bool removeStyle
= ((flags
& wxRICHTEXT_SETSTYLE_REMOVE
) != 0);
3097 // Apply paragraph style first, if any
3098 wxRichTextAttr
wholeStyle(style
);
3100 if (!removeStyle
&& wholeStyle
.HasParagraphStyleName() && buffer
->GetStyleSheet())
3102 wxRichTextParagraphStyleDefinition
* def
= buffer
->GetStyleSheet()->FindParagraphStyle(wholeStyle
.GetParagraphStyleName());
3104 wxRichTextApplyStyle(wholeStyle
, def
->GetStyleMergedWithBase(buffer
->GetStyleSheet()));
3107 // Limit the attributes to be set to the content to only character attributes.
3108 wxRichTextAttr
characterAttributes(wholeStyle
);
3109 characterAttributes
.SetFlags(characterAttributes
.GetFlags() & (wxTEXT_ATTR_CHARACTER
));
3111 if (!removeStyle
&& characterAttributes
.HasCharacterStyleName() && buffer
->GetStyleSheet())
3113 wxRichTextCharacterStyleDefinition
* def
= buffer
->GetStyleSheet()->FindCharacterStyle(characterAttributes
.GetCharacterStyleName());
3115 wxRichTextApplyStyle(characterAttributes
, def
->GetStyleMergedWithBase(buffer
->GetStyleSheet()));
3118 // If we are associated with a control, make undoable; otherwise, apply immediately
3121 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3123 wxRichTextAction
* action
= NULL
;
3125 if (haveControl
&& withUndo
)
3127 action
= new wxRichTextAction(NULL
, _("Change Style"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
3128 action
->SetRange(range
);
3129 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3132 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3135 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3136 // wxASSERT (para != NULL);
3138 if (para
&& para
->GetChildCount() > 0)
3140 // Stop searching if we're beyond the range of interest
3141 if (para
->GetRange().GetStart() > range
.GetEnd())
3144 if (!para
->GetRange().IsOutside(range
))
3146 // We'll be using a copy of the paragraph to make style changes,
3147 // not updating the buffer directly.
3148 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3150 if (haveControl
&& withUndo
)
3152 newPara
= new wxRichTextParagraph(*para
);
3153 action
->GetNewParagraphs().AppendChild(newPara
);
3155 // Also store the old ones for Undo
3156 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3161 // If we're specifying paragraphs only, then we really mean character formatting
3162 // to be included in the paragraph style
3163 if ((paragraphStyle
|| parasOnly
) && !charactersOnly
)
3167 // Removes the given style from the paragraph
3168 wxRichTextRemoveStyle(newPara
->GetAttributes(), style
);
3170 else if (resetExistingStyle
)
3171 newPara
->GetAttributes() = wholeStyle
;
3176 // Only apply attributes that will make a difference to the combined
3177 // style as seen on the display
3178 wxRichTextAttr
combinedAttr(para
->GetCombinedAttributes(true));
3179 wxRichTextApplyStyle(newPara
->GetAttributes(), wholeStyle
, & combinedAttr
);
3182 wxRichTextApplyStyle(newPara
->GetAttributes(), wholeStyle
);
3186 // When applying paragraph styles dynamically, don't change the text objects' attributes
3187 // since they will computed as needed. Only apply the character styling if it's _only_
3188 // character styling. This policy is subject to change and might be put under user control.
3190 // Hm. we might well be applying a mix of paragraph and character styles, in which
3191 // case we _do_ want to apply character styles regardless of what para styles are set.
3192 // But if we're applying a paragraph style, which has some character attributes, but
3193 // we only want the paragraphs to hold this character style, then we _don't_ want to
3194 // apply the character style. So we need to be able to choose.
3196 if (!parasOnly
&& (characterStyle
|charactersOnly
) && range
.GetStart() != newPara
->GetRange().GetEnd())
3198 wxRichTextRange
childRange(range
);
3199 childRange
.LimitTo(newPara
->GetRange());
3201 // Find the starting position and if necessary split it so
3202 // we can start applying a different style.
3203 // TODO: check that the style actually changes or is different
3204 // from style outside of range
3205 wxRichTextObject
* firstObject
wxDUMMY_INITIALIZE(NULL
);
3206 wxRichTextObject
* lastObject
wxDUMMY_INITIALIZE(NULL
);
3208 if (childRange
.GetStart() == newPara
->GetRange().GetStart())
3209 firstObject
= newPara
->GetChildren().GetFirst()->GetData();
3211 firstObject
= newPara
->SplitAt(range
.GetStart());
3213 // Increment by 1 because we're apply the style one _after_ the split point
3214 long splitPoint
= childRange
.GetEnd();
3215 if (splitPoint
!= newPara
->GetRange().GetEnd())
3219 if (splitPoint
== newPara
->GetRange().GetEnd())
3220 lastObject
= newPara
->GetChildren().GetLast()->GetData();
3222 // lastObject is set as a side-effect of splitting. It's
3223 // returned as the object before the new object.
3224 (void) newPara
->SplitAt(splitPoint
, & lastObject
);
3226 wxASSERT(firstObject
!= NULL
);
3227 wxASSERT(lastObject
!= NULL
);
3229 if (!firstObject
|| !lastObject
)
3232 wxRichTextObjectList::compatibility_iterator firstNode
= newPara
->GetChildren().Find(firstObject
);
3233 wxRichTextObjectList::compatibility_iterator lastNode
= newPara
->GetChildren().Find(lastObject
);
3235 wxASSERT(firstNode
);
3238 wxRichTextObjectList::compatibility_iterator node2
= firstNode
;
3242 wxRichTextObject
* child
= node2
->GetData();
3246 // Removes the given style from the paragraph
3247 wxRichTextRemoveStyle(child
->GetAttributes(), style
);
3249 else if (resetExistingStyle
)
3250 child
->GetAttributes() = characterAttributes
;
3255 // Only apply attributes that will make a difference to the combined
3256 // style as seen on the display
3257 wxRichTextAttr
combinedAttr(newPara
->GetCombinedAttributes(child
->GetAttributes(), true));
3258 wxRichTextApplyStyle(child
->GetAttributes(), characterAttributes
, & combinedAttr
);
3261 wxRichTextApplyStyle(child
->GetAttributes(), characterAttributes
);
3264 if (node2
== lastNode
)
3267 node2
= node2
->GetNext();
3273 node
= node
->GetNext();
3276 // Do action, or delay it until end of batch.
3277 if (haveControl
&& withUndo
)
3278 buffer
->SubmitAction(action
);
3283 // Just change the attributes for this single object.
3284 void wxRichTextParagraphLayoutBox::SetStyle(wxRichTextObject
* obj
, const wxRichTextAttr
& textAttr
, int flags
)
3286 wxRichTextBuffer
* buffer
= GetBuffer();
3287 bool withUndo
= flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
;
3288 bool resetExistingStyle
= ((flags
& wxRICHTEXT_SETSTYLE_RESET
) != 0);
3289 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3291 wxRichTextAction
*action
= NULL
;
3292 wxRichTextAttr newAttr
= obj
->GetAttributes();
3293 if (resetExistingStyle
)
3296 newAttr
.Apply(textAttr
);
3298 if (haveControl
&& withUndo
)
3300 action
= new wxRichTextAction(NULL
, _("Change Object Style"), wxRICHTEXT_CHANGE_ATTRIBUTES
, buffer
, obj
->GetContainer(), buffer
->GetRichTextCtrl());
3301 action
->SetRange(obj
->GetRange().FromInternal());
3302 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3303 action
->MakeObject(obj
);
3305 action
->GetAttributes() = newAttr
;
3308 obj
->GetAttributes() = newAttr
;
3310 if (haveControl
&& withUndo
)
3311 buffer
->SubmitAction(action
);
3314 /// Get the text attributes for this position.
3315 bool wxRichTextParagraphLayoutBox::GetStyle(long position
, wxRichTextAttr
& style
)
3317 return DoGetStyle(position
, style
, true);
3320 bool wxRichTextParagraphLayoutBox::GetUncombinedStyle(long position
, wxRichTextAttr
& style
)
3322 return DoGetStyle(position
, style
, false);
3325 /// Implementation helper for GetStyle. If combineStyles is true, combine base, paragraph and
3326 /// context attributes.
3327 bool wxRichTextParagraphLayoutBox::DoGetStyle(long position
, wxRichTextAttr
& style
, bool combineStyles
)
3329 wxRichTextObject
* obj
wxDUMMY_INITIALIZE(NULL
);
3331 if (style
.IsParagraphStyle())
3333 obj
= GetParagraphAtPosition(position
);
3338 // Start with the base style
3339 style
= GetAttributes();
3340 style
.GetTextBoxAttr().Reset();
3342 // Apply the paragraph style
3343 wxRichTextApplyStyle(style
, obj
->GetAttributes());
3346 style
= obj
->GetAttributes();
3353 obj
= GetLeafObjectAtPosition(position
);
3358 wxRichTextParagraph
* para
= wxDynamicCast(obj
->GetParent(), wxRichTextParagraph
);
3359 style
= para
? para
->GetCombinedAttributes(obj
->GetAttributes()) : obj
->GetAttributes();
3362 style
= obj
->GetAttributes();
3370 static bool wxHasStyle(long flags
, long style
)
3372 return (flags
& style
) != 0;
3375 /// Combines 'style' with 'currentStyle' for the purpose of summarising the attributes of a range of
3377 bool wxRichTextParagraphLayoutBox::CollectStyle(wxRichTextAttr
& currentStyle
, const wxRichTextAttr
& style
, wxRichTextAttr
& clashingAttr
, wxRichTextAttr
& absentAttr
)
3379 currentStyle
.CollectCommonAttributes(style
, clashingAttr
, absentAttr
);
3384 /// Get the combined style for a range - if any attribute is different within the range,
3385 /// that attribute is not present within the flags.
3386 /// *** Note that this is not recursive, and so assumes that content inside a paragraph is not itself
3388 bool wxRichTextParagraphLayoutBox::GetStyleForRange(const wxRichTextRange
& range
, wxRichTextAttr
& style
)
3390 style
= wxRichTextAttr();
3392 wxRichTextAttr clashingAttr
;
3393 wxRichTextAttr absentAttrPara
, absentAttrChar
;
3395 wxRichTextObjectList::compatibility_iterator node
= GetChildren().GetFirst();
3398 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3399 if (para
&& !(para
->GetRange().GetStart() > range
.GetEnd() || para
->GetRange().GetEnd() < range
.GetStart()))
3401 if (para
->GetChildren().GetCount() == 0)
3403 wxRichTextAttr paraStyle
= para
->GetCombinedAttributes(true /* use box attributes */);
3405 CollectStyle(style
, paraStyle
, clashingAttr
, absentAttrPara
);
3409 wxRichTextRange
paraRange(para
->GetRange());
3410 paraRange
.LimitTo(range
);
3412 // First collect paragraph attributes only
3413 wxRichTextAttr paraStyle
= para
->GetCombinedAttributes();
3414 paraStyle
.SetFlags(paraStyle
.GetFlags() & wxTEXT_ATTR_PARAGRAPH
);
3415 CollectStyle(style
, paraStyle
, clashingAttr
, absentAttrPara
);
3417 wxRichTextObjectList::compatibility_iterator childNode
= para
->GetChildren().GetFirst();
3421 wxRichTextObject
* child
= childNode
->GetData();
3422 if (!(child
->GetRange().GetStart() > range
.GetEnd() || child
->GetRange().GetEnd() < range
.GetStart()))
3424 wxRichTextAttr childStyle
= para
->GetCombinedAttributes(child
->GetAttributes(), true /* include box attributes */);
3426 // Now collect character attributes only
3427 childStyle
.SetFlags(childStyle
.GetFlags() & wxTEXT_ATTR_CHARACTER
);
3429 CollectStyle(style
, childStyle
, clashingAttr
, absentAttrChar
);
3432 childNode
= childNode
->GetNext();
3436 node
= node
->GetNext();
3441 /// Set default style
3442 bool wxRichTextParagraphLayoutBox::SetDefaultStyle(const wxRichTextAttr
& style
)
3444 m_defaultAttributes
= style
;
3448 /// Test if this whole range has character attributes of the specified kind. If any
3449 /// of the attributes are different within the range, the test fails. You
3450 /// can use this to implement, for example, bold button updating. style must have
3451 /// flags indicating which attributes are of interest.
3452 bool wxRichTextParagraphLayoutBox::HasCharacterAttributes(const wxRichTextRange
& range
, const wxRichTextAttr
& style
) const
3455 int matchingCount
= 0;
3457 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3460 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3461 // wxASSERT (para != NULL);
3465 // Stop searching if we're beyond the range of interest
3466 if (para
->GetRange().GetStart() > range
.GetEnd())
3467 return foundCount
== matchingCount
&& foundCount
!= 0;
3469 if (!para
->GetRange().IsOutside(range
))
3471 wxRichTextObjectList::compatibility_iterator node2
= para
->GetChildren().GetFirst();
3475 wxRichTextObject
* child
= node2
->GetData();
3476 // Allow for empty string if no buffer
3477 wxRichTextRange childRange
= child
->GetRange();
3478 if (childRange
.GetLength() == 0 && GetRange().GetLength() == 1)
3479 childRange
.SetEnd(childRange
.GetEnd()+1);
3481 if (!childRange
.IsOutside(range
) && wxDynamicCast(child
, wxRichTextPlainText
))
3484 wxRichTextAttr textAttr
= para
->GetCombinedAttributes(child
->GetAttributes());
3486 if (textAttr
.EqPartial(style
, false /* strong test - attributes must be valid in both objects */))
3490 node2
= node2
->GetNext();
3495 node
= node
->GetNext();
3498 return foundCount
== matchingCount
&& foundCount
!= 0;
3501 /// Test if this whole range has paragraph attributes of the specified kind. If any
3502 /// of the attributes are different within the range, the test fails. You
3503 /// can use this to implement, for example, centering button updating. style must have
3504 /// flags indicating which attributes are of interest.
3505 bool wxRichTextParagraphLayoutBox::HasParagraphAttributes(const wxRichTextRange
& range
, const wxRichTextAttr
& style
) const
3508 int matchingCount
= 0;
3510 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3513 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3514 // wxASSERT (para != NULL);
3518 // Stop searching if we're beyond the range of interest
3519 if (para
->GetRange().GetStart() > range
.GetEnd())
3520 return foundCount
== matchingCount
&& foundCount
!= 0;
3522 if (!para
->GetRange().IsOutside(range
))
3524 wxRichTextAttr textAttr
= GetAttributes();
3525 // Apply the paragraph style
3526 wxRichTextApplyStyle(textAttr
, para
->GetAttributes());
3529 if (textAttr
.EqPartial(style
, false /* strong test */))
3534 node
= node
->GetNext();
3536 return foundCount
== matchingCount
&& foundCount
!= 0;
3539 void wxRichTextParagraphLayoutBox::PrepareContent(wxRichTextParagraphLayoutBox
& container
)
3541 wxRichTextBuffer
* buffer
= GetBuffer();
3542 if (buffer
&& buffer
->GetRichTextCtrl())
3543 buffer
->GetRichTextCtrl()->PrepareContent(container
);
3546 /// Set character or paragraph properties
3547 bool wxRichTextParagraphLayoutBox::SetProperties(const wxRichTextRange
& range
, const wxRichTextProperties
& properties
, int flags
)
3549 wxRichTextBuffer
* buffer
= GetBuffer();
3551 bool withUndo
= ((flags
& wxRICHTEXT_SETPROPERTIES_WITH_UNDO
) != 0);
3552 bool parasOnly
= ((flags
& wxRICHTEXT_SETPROPERTIES_PARAGRAPHS_ONLY
) != 0);
3553 bool charactersOnly
= ((flags
& wxRICHTEXT_SETPROPERTIES_CHARACTERS_ONLY
) != 0);
3554 bool resetExistingProperties
= ((flags
& wxRICHTEXT_SETPROPERTIES_RESET
) != 0);
3555 bool removeProperties
= ((flags
& wxRICHTEXT_SETPROPERTIES_REMOVE
) != 0);
3557 // If we are associated with a control, make undoable; otherwise, apply immediately
3560 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3562 wxRichTextAction
* action
= NULL
;
3564 if (haveControl
&& withUndo
)
3566 action
= new wxRichTextAction(NULL
, _("Change Properties"), wxRICHTEXT_CHANGE_PROPERTIES
, buffer
, this, buffer
->GetRichTextCtrl());
3567 action
->SetRange(range
);
3568 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3571 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3574 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3575 // wxASSERT (para != NULL);
3577 if (para
&& para
->GetChildCount() > 0)
3579 // Stop searching if we're beyond the range of interest
3580 if (para
->GetRange().GetStart() > range
.GetEnd())
3583 if (!para
->GetRange().IsOutside(range
))
3585 // We'll be using a copy of the paragraph to make style changes,
3586 // not updating the buffer directly.
3587 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3589 if (haveControl
&& withUndo
)
3591 newPara
= new wxRichTextParagraph(*para
);
3592 action
->GetNewParagraphs().AppendChild(newPara
);
3594 // Also store the old ones for Undo
3595 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3602 if (removeProperties
)
3604 // Removes the given style from the paragraph
3606 newPara
->GetProperties().RemoveProperties(properties
);
3608 else if (resetExistingProperties
)
3609 newPara
->GetProperties() = properties
;
3611 newPara
->GetProperties().MergeProperties(properties
);
3614 // When applying paragraph styles dynamically, don't change the text objects' attributes
3615 // since they will computed as needed. Only apply the character styling if it's _only_
3616 // character styling. This policy is subject to change and might be put under user control.
3618 // Hm. we might well be applying a mix of paragraph and character styles, in which
3619 // case we _do_ want to apply character styles regardless of what para styles are set.
3620 // But if we're applying a paragraph style, which has some character attributes, but
3621 // we only want the paragraphs to hold this character style, then we _don't_ want to
3622 // apply the character style. So we need to be able to choose.
3624 if (!parasOnly
&& charactersOnly
&& range
.GetStart() != newPara
->GetRange().GetEnd())
3626 wxRichTextRange
childRange(range
);
3627 childRange
.LimitTo(newPara
->GetRange());
3629 // Find the starting position and if necessary split it so
3630 // we can start applying different properties.
3631 // TODO: check that the properties actually change or are different
3632 // from properties outside of range
3633 wxRichTextObject
* firstObject
wxDUMMY_INITIALIZE(NULL
);
3634 wxRichTextObject
* lastObject
wxDUMMY_INITIALIZE(NULL
);
3636 if (childRange
.GetStart() == newPara
->GetRange().GetStart())
3637 firstObject
= newPara
->GetChildren().GetFirst()->GetData();
3639 firstObject
= newPara
->SplitAt(range
.GetStart());
3641 // Increment by 1 because we're apply the style one _after_ the split point
3642 long splitPoint
= childRange
.GetEnd();
3643 if (splitPoint
!= newPara
->GetRange().GetEnd())
3647 if (splitPoint
== newPara
->GetRange().GetEnd())
3648 lastObject
= newPara
->GetChildren().GetLast()->GetData();
3650 // lastObject is set as a side-effect of splitting. It's
3651 // returned as the object before the new object.
3652 (void) newPara
->SplitAt(splitPoint
, & lastObject
);
3654 wxASSERT(firstObject
!= NULL
);
3655 wxASSERT(lastObject
!= NULL
);
3657 if (!firstObject
|| !lastObject
)
3660 wxRichTextObjectList::compatibility_iterator firstNode
= newPara
->GetChildren().Find(firstObject
);
3661 wxRichTextObjectList::compatibility_iterator lastNode
= newPara
->GetChildren().Find(lastObject
);
3663 wxASSERT(firstNode
);
3666 wxRichTextObjectList::compatibility_iterator node2
= firstNode
;
3670 wxRichTextObject
* child
= node2
->GetData();
3672 if (removeProperties
)
3674 // Removes the given properties from the paragraph
3675 child
->GetProperties().RemoveProperties(properties
);
3677 else if (resetExistingProperties
)
3678 child
->GetProperties() = properties
;
3681 child
->GetProperties().MergeProperties(properties
);
3684 if (node2
== lastNode
)
3687 node2
= node2
->GetNext();
3693 node
= node
->GetNext();
3696 // Do action, or delay it until end of batch.
3697 if (haveControl
&& withUndo
)
3698 buffer
->SubmitAction(action
);
3703 void wxRichTextParagraphLayoutBox::Reset()
3707 wxRichTextBuffer
* buffer
= GetBuffer();
3708 if (buffer
&& buffer
->GetRichTextCtrl())
3710 wxRichTextEvent
event(wxEVT_COMMAND_RICHTEXT_BUFFER_RESET
, buffer
->GetRichTextCtrl()->GetId());
3711 event
.SetEventObject(buffer
->GetRichTextCtrl());
3712 event
.SetContainer(this);
3714 buffer
->SendEvent(event
, true);
3717 AddParagraph(wxEmptyString
);
3719 PrepareContent(*this);
3721 InvalidateHierarchy(wxRICHTEXT_ALL
);
3724 /// Invalidate the buffer. With no argument, invalidates whole buffer.
3725 void wxRichTextParagraphLayoutBox::Invalidate(const wxRichTextRange
& invalidRange
)
3727 wxRichTextCompositeObject::Invalidate(invalidRange
);
3729 DoInvalidate(invalidRange
);
3732 // Do the (in)validation for this object only
3733 void wxRichTextParagraphLayoutBox::DoInvalidate(const wxRichTextRange
& invalidRange
)
3735 if (invalidRange
== wxRICHTEXT_ALL
)
3737 m_invalidRange
= wxRICHTEXT_ALL
;
3739 // Already invalidating everything
3740 else if (m_invalidRange
== wxRICHTEXT_ALL
)
3745 if ((invalidRange
.GetStart() < m_invalidRange
.GetStart()) || m_invalidRange
.GetStart() == -1)
3746 m_invalidRange
.SetStart(invalidRange
.GetStart());
3747 if (invalidRange
.GetEnd() > m_invalidRange
.GetEnd())
3748 m_invalidRange
.SetEnd(invalidRange
.GetEnd());
3752 // Do the (in)validation both up and down the hierarchy
3753 void wxRichTextParagraphLayoutBox::InvalidateHierarchy(const wxRichTextRange
& invalidRange
)
3755 Invalidate(invalidRange
);
3757 if (invalidRange
!= wxRICHTEXT_NONE
)
3759 // Now go up the hierarchy
3760 wxRichTextObject
* thisObj
= this;
3761 wxRichTextObject
* p
= GetParent();
3764 wxRichTextParagraphLayoutBox
* l
= wxDynamicCast(p
, wxRichTextParagraphLayoutBox
);
3766 l
->DoInvalidate(thisObj
->GetRange());
3774 /// Get invalid range, rounding to entire paragraphs if argument is true.
3775 wxRichTextRange
wxRichTextParagraphLayoutBox::GetInvalidRange(bool wholeParagraphs
) const
3777 if (m_invalidRange
== wxRICHTEXT_ALL
|| m_invalidRange
== wxRICHTEXT_NONE
)
3778 return m_invalidRange
;
3780 wxRichTextRange range
= m_invalidRange
;
3782 if (wholeParagraphs
)
3784 wxRichTextParagraph
* para1
= GetParagraphAtPosition(range
.GetStart());
3786 range
.SetStart(para1
->GetRange().GetStart());
3787 // floating layout make all child should be relayout
3788 range
.SetEnd(GetOwnRange().GetEnd());
3793 /// Apply the style sheet to the buffer, for example if the styles have changed.
3794 bool wxRichTextParagraphLayoutBox::ApplyStyleSheet(wxRichTextStyleSheet
* styleSheet
)
3796 wxASSERT(styleSheet
!= NULL
);
3802 wxRichTextAttr
attr(GetBasicStyle());
3803 if (GetBasicStyle().HasParagraphStyleName())
3805 wxRichTextParagraphStyleDefinition
* paraDef
= styleSheet
->FindParagraphStyle(GetBasicStyle().GetParagraphStyleName());
3808 attr
.Apply(paraDef
->GetStyleMergedWithBase(styleSheet
));
3809 SetBasicStyle(attr
);
3814 if (GetBasicStyle().HasCharacterStyleName())
3816 wxRichTextCharacterStyleDefinition
* charDef
= styleSheet
->FindCharacterStyle(GetBasicStyle().GetCharacterStyleName());
3819 attr
.Apply(charDef
->GetStyleMergedWithBase(styleSheet
));
3820 SetBasicStyle(attr
);
3825 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3828 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3829 // wxASSERT (para != NULL);
3833 // Combine paragraph and list styles. If there is a list style in the original attributes,
3834 // the current indentation overrides anything else and is used to find the item indentation.
3835 // Also, for applying paragraph styles, consider having 2 modes: (1) we merge with what we have,
3836 // thereby taking into account all user changes, (2) reset the style completely (except for indentation/list
3837 // exception as above).
3838 // Problem: when changing from one list style to another, there's a danger that the level info will get lost.
3839 // So when changing a list style interactively, could retrieve level based on current style, then
3840 // set appropriate indent and apply new style.
3844 if (para
->GetAttributes().HasOutlineLevel())
3845 outline
= para
->GetAttributes().GetOutlineLevel();
3846 if (para
->GetAttributes().HasBulletNumber())
3847 num
= para
->GetAttributes().GetBulletNumber();
3849 if (!para
->GetAttributes().GetParagraphStyleName().IsEmpty() && !para
->GetAttributes().GetListStyleName().IsEmpty())
3851 int currentIndent
= para
->GetAttributes().GetLeftIndent();
3853 wxRichTextParagraphStyleDefinition
* paraDef
= styleSheet
->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
3854 wxRichTextListStyleDefinition
* listDef
= styleSheet
->FindListStyle(para
->GetAttributes().GetListStyleName());
3855 if (paraDef
&& !listDef
)
3857 para
->GetAttributes() = paraDef
->GetStyleMergedWithBase(styleSheet
);
3860 else if (listDef
&& !paraDef
)
3862 // Set overall style defined for the list style definition
3863 para
->GetAttributes() = listDef
->GetStyleMergedWithBase(styleSheet
);
3865 // Apply the style for this level
3866 wxRichTextApplyStyle(para
->GetAttributes(), * listDef
->GetLevelAttributes(listDef
->FindLevelForIndent(currentIndent
)));
3869 else if (listDef
&& paraDef
)
3871 // Combines overall list style, style for level, and paragraph style
3872 para
->GetAttributes() = listDef
->CombineWithParagraphStyle(currentIndent
, paraDef
->GetStyleMergedWithBase(styleSheet
));
3876 else if (para
->GetAttributes().GetParagraphStyleName().IsEmpty() && !para
->GetAttributes().GetListStyleName().IsEmpty())
3878 int currentIndent
= para
->GetAttributes().GetLeftIndent();
3880 wxRichTextListStyleDefinition
* listDef
= styleSheet
->FindListStyle(para
->GetAttributes().GetListStyleName());
3882 // Overall list definition style
3883 para
->GetAttributes() = listDef
->GetStyleMergedWithBase(styleSheet
);
3885 // Style for this level
3886 wxRichTextApplyStyle(para
->GetAttributes(), * listDef
->GetLevelAttributes(listDef
->FindLevelForIndent(currentIndent
)));
3890 else if (!para
->GetAttributes().GetParagraphStyleName().IsEmpty() && para
->GetAttributes().GetListStyleName().IsEmpty())
3892 wxRichTextParagraphStyleDefinition
* def
= styleSheet
->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
3895 para
->GetAttributes() = def
->GetStyleMergedWithBase(styleSheet
);
3901 para
->GetAttributes().SetOutlineLevel(outline
);
3903 para
->GetAttributes().SetBulletNumber(num
);
3906 node
= node
->GetNext();
3908 return foundCount
!= 0;
3912 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
3914 wxRichTextBuffer
* buffer
= GetBuffer();
3915 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
3917 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
3918 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
3919 bool specifyLevel
= ((flags
& wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL
) != 0);
3920 bool renumber
= ((flags
& wxRICHTEXT_SETSTYLE_RENUMBER
) != 0);
3922 // Current number, if numbering
3925 wxASSERT (!specifyLevel
|| (specifyLevel
&& (specifiedLevel
>= 0)));
3927 // If we are associated with a control, make undoable; otherwise, apply immediately
3930 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3932 wxRichTextAction
* action
= NULL
;
3934 if (haveControl
&& withUndo
)
3936 action
= new wxRichTextAction(NULL
, _("Change List Style"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
3937 action
->SetRange(range
);
3938 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3941 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3944 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3945 // wxASSERT (para != NULL);
3947 if (para
&& para
->GetChildCount() > 0)
3949 // Stop searching if we're beyond the range of interest
3950 if (para
->GetRange().GetStart() > range
.GetEnd())
3953 if (!para
->GetRange().IsOutside(range
))
3955 // We'll be using a copy of the paragraph to make style changes,
3956 // not updating the buffer directly.
3957 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3959 if (haveControl
&& withUndo
)
3961 newPara
= new wxRichTextParagraph(*para
);
3962 action
->GetNewParagraphs().AppendChild(newPara
);
3964 // Also store the old ones for Undo
3965 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3972 int thisIndent
= newPara
->GetAttributes().GetLeftIndent();
3973 int thisLevel
= specifyLevel
? specifiedLevel
: def
->FindLevelForIndent(thisIndent
);
3975 // How is numbering going to work?
3976 // If we are renumbering, or numbering for the first time, we need to keep
3977 // track of the number for each level. But we might be simply applying a different
3979 // In Word, applying a style to several paragraphs, even if at different levels,
3980 // reverts the level back to the same one. So we could do the same here.
3981 // Renumbering will need to be done when we promote/demote a paragraph.
3983 // Apply the overall list style, and item style for this level
3984 wxRichTextAttr
listStyle(def
->GetCombinedStyleForLevel(thisLevel
, styleSheet
));
3985 wxRichTextApplyStyle(newPara
->GetAttributes(), listStyle
);
3987 // Now we need to do numbering
3988 // Preserve the existing list item continuation bullet style, if any
3989 if (para
->GetAttributes().HasBulletStyle() && (para
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
))
3990 newPara
->GetAttributes().SetBulletStyle(newPara
->GetAttributes().GetBulletStyle()|wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
);
3993 // Now we need to do numbering
3996 newPara
->GetAttributes().SetBulletNumber(n
);
4002 else if (!newPara
->GetAttributes().GetListStyleName().IsEmpty())
4004 // if def is NULL, remove list style, applying any associated paragraph style
4005 // to restore the attributes
4007 newPara
->GetAttributes().SetListStyleName(wxEmptyString
);
4008 newPara
->GetAttributes().SetLeftIndent(0, 0);
4009 newPara
->GetAttributes().SetBulletText(wxEmptyString
);
4011 // Eliminate the main list-related attributes
4012 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
);
4014 if (styleSheet
&& !newPara
->GetAttributes().GetParagraphStyleName().IsEmpty())
4016 wxRichTextParagraphStyleDefinition
* def
= styleSheet
->FindParagraphStyle(newPara
->GetAttributes().GetParagraphStyleName());
4019 newPara
->GetAttributes() = def
->GetStyleMergedWithBase(styleSheet
);
4026 node
= node
->GetNext();
4029 // Do action, or delay it until end of batch.
4030 if (haveControl
&& withUndo
)
4031 buffer
->SubmitAction(action
);
4036 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
)
4038 wxRichTextBuffer
* buffer
= GetBuffer();
4039 if (buffer
&& buffer
->GetStyleSheet())
4041 wxRichTextListStyleDefinition
* def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4043 return SetListStyle(range
, def
, flags
, startFrom
, specifiedLevel
);
4048 /// Clear list for given range
4049 bool wxRichTextParagraphLayoutBox::ClearListStyle(const wxRichTextRange
& range
, int flags
)
4051 return SetListStyle(range
, NULL
, flags
);
4054 /// Number/renumber any list elements in the given range
4055 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
4057 return DoNumberList(range
, range
, 0, def
, flags
, startFrom
, specifiedLevel
);
4060 /// Number/renumber any list elements in the given range. Also do promotion or demotion of items, if specified
4061 bool wxRichTextParagraphLayoutBox::DoNumberList(const wxRichTextRange
& range
, const wxRichTextRange
& promotionRange
, int promoteBy
,
4062 wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
4064 wxRichTextBuffer
* buffer
= GetBuffer();
4065 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
4067 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
4068 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
4070 bool specifyLevel
= ((flags
& wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL
) != 0);
4073 bool renumber
= ((flags
& wxRICHTEXT_SETSTYLE_RENUMBER
) != 0);
4075 // Max number of levels
4076 const int maxLevels
= 10;
4078 // The level we're looking at now
4079 int currentLevel
= -1;
4081 // The item number for each level
4082 int levels
[maxLevels
];
4085 // Reset all numbering
4086 for (i
= 0; i
< maxLevels
; i
++)
4088 if (startFrom
!= -1)
4089 levels
[i
] = startFrom
-1;
4090 else if (renumber
) // start again
4093 levels
[i
] = -1; // start from the number we found, if any
4097 wxASSERT(!specifyLevel
|| (specifyLevel
&& (specifiedLevel
>= 0)));
4100 // If we are associated with a control, make undoable; otherwise, apply immediately
4103 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
4105 wxRichTextAction
* action
= NULL
;
4107 if (haveControl
&& withUndo
)
4109 action
= new wxRichTextAction(NULL
, _("Renumber List"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
4110 action
->SetRange(range
);
4111 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
4114 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
4117 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
4118 // wxASSERT (para != NULL);
4120 if (para
&& para
->GetChildCount() > 0)
4122 // Stop searching if we're beyond the range of interest
4123 if (para
->GetRange().GetStart() > range
.GetEnd())
4126 if (!para
->GetRange().IsOutside(range
))
4128 // We'll be using a copy of the paragraph to make style changes,
4129 // not updating the buffer directly.
4130 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
4132 if (haveControl
&& withUndo
)
4134 newPara
= new wxRichTextParagraph(*para
);
4135 action
->GetNewParagraphs().AppendChild(newPara
);
4137 // Also store the old ones for Undo
4138 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
4143 wxRichTextListStyleDefinition
* defToUse
= def
;
4146 if (styleSheet
&& !newPara
->GetAttributes().GetListStyleName().IsEmpty())
4147 defToUse
= styleSheet
->FindListStyle(newPara
->GetAttributes().GetListStyleName());
4152 int thisIndent
= newPara
->GetAttributes().GetLeftIndent();
4153 int thisLevel
= defToUse
->FindLevelForIndent(thisIndent
);
4155 // If we've specified a level to apply to all, change the level.
4156 if (specifiedLevel
!= -1)
4157 thisLevel
= specifiedLevel
;
4159 // Do promotion if specified
4160 if ((promoteBy
!= 0) && !para
->GetRange().IsOutside(promotionRange
))
4162 thisLevel
= thisLevel
- promoteBy
;
4169 // Apply the overall list style, and item style for this level
4170 wxRichTextAttr
listStyle(defToUse
->GetCombinedStyleForLevel(thisLevel
, styleSheet
));
4171 wxRichTextApplyStyle(newPara
->GetAttributes(), listStyle
);
4173 // Preserve the existing list item continuation bullet style, if any
4174 if (para
->GetAttributes().HasBulletStyle() && (para
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
))
4175 newPara
->GetAttributes().SetBulletStyle(newPara
->GetAttributes().GetBulletStyle()|wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
);
4177 // OK, we've (re)applied the style, now let's get the numbering right.
4179 if (currentLevel
== -1)
4180 currentLevel
= thisLevel
;
4182 // Same level as before, do nothing except increment level's number afterwards
4183 if (currentLevel
== thisLevel
)
4186 // A deeper level: start renumbering all levels after current level
4187 else if (thisLevel
> currentLevel
)
4189 for (i
= currentLevel
+1; i
<= thisLevel
; i
++)
4193 currentLevel
= thisLevel
;
4195 else if (thisLevel
< currentLevel
)
4197 currentLevel
= thisLevel
;
4200 // Use the current numbering if -1 and we have a bullet number already
4201 if (levels
[currentLevel
] == -1)
4203 if (newPara
->GetAttributes().HasBulletNumber())
4204 levels
[currentLevel
] = newPara
->GetAttributes().GetBulletNumber();
4206 levels
[currentLevel
] = 1;
4210 if (!(para
->GetAttributes().HasBulletStyle() && (para
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
)))
4211 levels
[currentLevel
] ++;
4214 newPara
->GetAttributes().SetBulletNumber(levels
[currentLevel
]);
4216 // Create the bullet text if an outline list
4217 if (listStyle
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
)
4220 for (i
= 0; i
<= currentLevel
; i
++)
4222 if (!text
.IsEmpty())
4224 text
+= wxString::Format(wxT("%d"), levels
[i
]);
4226 newPara
->GetAttributes().SetBulletText(text
);
4232 node
= node
->GetNext();
4235 // Do action, or delay it until end of batch.
4236 if (haveControl
&& withUndo
)
4237 buffer
->SubmitAction(action
);
4242 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
)
4244 wxRichTextBuffer
* buffer
= GetBuffer();
4245 if (buffer
->GetStyleSheet())
4247 wxRichTextListStyleDefinition
* def
= NULL
;
4248 if (!defName
.IsEmpty())
4249 def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4250 return NumberList(range
, def
, flags
, startFrom
, specifiedLevel
);
4255 /// Promote the list items within the given range. promoteBy can be a positive or negative number, e.g. 1 or -1
4256 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy
, const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int specifiedLevel
)
4259 // One strategy is to first work out the range within which renumbering must occur. Then could pass these two ranges
4260 // to NumberList with a flag indicating promotion is required within one of the ranges.
4261 // Find first and last paragraphs in range. Then for first, calculate new indentation and look back until we find
4262 // a paragraph that either has no list style, or has one that is different or whose indentation is less.
4263 // We start renumbering from the para after that different para we found. We specify that the numbering of that
4264 // list position will start from 1.
4265 // Similarly, we look after the last para in the promote range for an indentation that is less (or no list style).
4266 // We can end the renumbering at this point.
4268 // For now, only renumber within the promotion range.
4270 return DoNumberList(range
, range
, promoteBy
, def
, flags
, 1, specifiedLevel
);
4273 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy
, const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int specifiedLevel
)
4275 wxRichTextBuffer
* buffer
= GetBuffer();
4276 if (buffer
->GetStyleSheet())
4278 wxRichTextListStyleDefinition
* def
= NULL
;
4279 if (!defName
.IsEmpty())
4280 def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4281 return PromoteList(promoteBy
, range
, def
, flags
, specifiedLevel
);
4286 /// Fills in the attributes for numbering a paragraph after previousParagraph. It also finds the
4287 /// position of the paragraph that it had to start looking from.
4288 bool wxRichTextParagraphLayoutBox::FindNextParagraphNumber(wxRichTextParagraph
* previousParagraph
, wxRichTextAttr
& attr
) const
4290 // Search for a paragraph that isn't a continuation paragraph (no bullet)
4291 while (previousParagraph
&& previousParagraph
->GetAttributes().HasBulletStyle() && previousParagraph
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
)
4293 wxRichTextObjectList::compatibility_iterator node
= ((wxRichTextCompositeObject
*) previousParagraph
->GetParent())->GetChildren().Find(previousParagraph
);
4296 node
= node
->GetPrevious();
4298 previousParagraph
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
4300 previousParagraph
= NULL
;
4303 previousParagraph
= NULL
;
4306 if (!previousParagraph
->GetAttributes().HasFlag(wxTEXT_ATTR_BULLET_STYLE
) || previousParagraph
->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
)
4309 wxRichTextBuffer
* buffer
= GetBuffer();
4310 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
4311 if (styleSheet
&& !previousParagraph
->GetAttributes().GetListStyleName().IsEmpty())
4313 wxRichTextListStyleDefinition
* def
= styleSheet
->FindListStyle(previousParagraph
->GetAttributes().GetListStyleName());
4316 // int thisIndent = previousParagraph->GetAttributes().GetLeftIndent();
4317 // int thisLevel = def->FindLevelForIndent(thisIndent);
4319 bool isOutline
= (previousParagraph
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
) != 0;
4321 attr
.SetFlags(previousParagraph
->GetAttributes().GetFlags() & (wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_BULLET_NUMBER
|wxTEXT_ATTR_BULLET_TEXT
|wxTEXT_ATTR_BULLET_NAME
));
4322 if (previousParagraph
->GetAttributes().HasBulletName())
4323 attr
.SetBulletName(previousParagraph
->GetAttributes().GetBulletName());
4324 attr
.SetBulletStyle(previousParagraph
->GetAttributes().GetBulletStyle());
4325 attr
.SetListStyleName(previousParagraph
->GetAttributes().GetListStyleName());
4327 int nextNumber
= previousParagraph
->GetAttributes().GetBulletNumber() + 1;
4328 attr
.SetBulletNumber(nextNumber
);
4332 wxString text
= previousParagraph
->GetAttributes().GetBulletText();
4333 if (!text
.IsEmpty())
4335 int pos
= text
.Find(wxT('.'), true);
4336 if (pos
!= wxNOT_FOUND
)
4338 text
= text
.Mid(0, text
.Length() - pos
- 1);
4341 text
= wxEmptyString
;
4342 if (!text
.IsEmpty())
4344 text
+= wxString::Format(wxT("%d"), nextNumber
);
4345 attr
.SetBulletText(text
);
4359 * wxRichTextParagraph
4360 * This object represents a single paragraph (or in a straight text editor, a line).
4363 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraph
, wxRichTextCompositeObject
)
4365 wxArrayInt
wxRichTextParagraph::sm_defaultTabs
;
4367 wxRichTextParagraph::wxRichTextParagraph(wxRichTextObject
* parent
, wxRichTextAttr
* style
):
4368 wxRichTextCompositeObject(parent
)
4371 SetAttributes(*style
);
4374 wxRichTextParagraph::wxRichTextParagraph(const wxString
& text
, wxRichTextObject
* parent
, wxRichTextAttr
* paraStyle
, wxRichTextAttr
* charStyle
):
4375 wxRichTextCompositeObject(parent
)
4378 SetAttributes(*paraStyle
);
4380 AppendChild(new wxRichTextPlainText(text
, this, charStyle
));
4383 wxRichTextParagraph::~wxRichTextParagraph()
4389 bool wxRichTextParagraph::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int WXUNUSED(descent
), int style
)
4394 // Currently we don't merge these attributes with the parent, but we
4395 // should consider whether we should (e.g. if we set a border colour
4396 // for all paragraphs). But generally box attributes are likely to be
4397 // different for different objects.
4398 wxRect paraRect
= GetRect();
4399 wxRichTextAttr attr
= GetCombinedAttributes();
4400 context
.ApplyVirtualAttributes(attr
, this);
4402 DrawBoxAttributes(dc
, GetBuffer(), attr
, paraRect
);
4404 // Draw the bullet, if any
4405 if ((attr
.GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
) == 0 && (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
) == 0)
4407 if (attr
.GetLeftSubIndent() != 0)
4409 int spaceBeforePara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingBefore());
4410 int leftIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftIndent());
4412 wxRichTextAttr
bulletAttr(attr
);
4414 // Combine with the font of the first piece of content, if one is specified
4415 if (GetChildren().GetCount() > 0)
4417 wxRichTextObject
* firstObj
= (wxRichTextObject
*) GetChildren().GetFirst()->GetData();
4418 if (!firstObj
->IsFloatable() && firstObj
->GetAttributes().HasFont())
4420 wxRichTextApplyStyle(bulletAttr
, firstObj
->GetAttributes());
4424 // Get line height from first line, if any
4425 wxRichTextLine
* line
= m_cachedLines
.GetFirst() ? (wxRichTextLine
* ) m_cachedLines
.GetFirst()->GetData() : NULL
;
4428 int lineHeight
wxDUMMY_INITIALIZE(0);
4431 lineHeight
= line
->GetSize().y
;
4432 linePos
= line
->GetPosition() + GetPosition();
4437 if (bulletAttr
.HasFont() && GetBuffer())
4438 font
= GetBuffer()->GetFontTable().FindFont(bulletAttr
);
4440 font
= (*wxNORMAL_FONT
);
4442 wxCheckSetFont(dc
, font
);
4444 lineHeight
= dc
.GetCharHeight();
4445 linePos
= GetPosition();
4446 linePos
.y
+= spaceBeforePara
;
4449 wxRect
bulletRect(GetPosition().x
+ leftIndent
, linePos
.y
, linePos
.x
- (GetPosition().x
+ leftIndent
), lineHeight
);
4451 if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
)
4453 if (wxRichTextBuffer::GetRenderer())
4454 wxRichTextBuffer::GetRenderer()->DrawBitmapBullet(this, dc
, bulletAttr
, bulletRect
);
4456 else if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_STANDARD
)
4458 if (wxRichTextBuffer::GetRenderer())
4459 wxRichTextBuffer::GetRenderer()->DrawStandardBullet(this, dc
, bulletAttr
, bulletRect
);
4463 wxString bulletText
= GetBulletText();
4465 if (!bulletText
.empty() && wxRichTextBuffer::GetRenderer())
4466 wxRichTextBuffer::GetRenderer()->DrawTextBullet(this, dc
, bulletAttr
, bulletRect
, bulletText
);
4471 // Draw the range for each line, one object at a time.
4473 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
4476 wxRichTextLine
* line
= node
->GetData();
4477 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
4479 // Lines are specified relative to the paragraph
4481 wxPoint linePosition
= line
->GetPosition() + GetPosition();
4483 // Don't draw if off the screen
4484 if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) != 0) || ((linePosition
.y
+ line
->GetSize().y
) >= rect
.y
&& linePosition
.y
<= rect
.y
+ rect
.height
))
4486 wxPoint objectPosition
= linePosition
;
4487 int maxDescent
= line
->GetDescent();
4489 // Loop through objects until we get to the one within range
4490 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
4495 wxRichTextObject
* child
= node2
->GetData();
4497 if (!child
->IsFloating() && child
->GetRange().GetLength() > 0 && !child
->GetRange().IsOutside(lineRange
) && !lineRange
.IsOutside(range
))
4499 // Draw this part of the line at the correct position
4500 wxRichTextRange
objectRange(child
->GetRange());
4501 objectRange
.LimitTo(lineRange
);
4504 if (child
->IsTopLevel())
4506 objectSize
= child
->GetCachedSize();
4507 objectRange
= child
->GetOwnRange();
4511 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING && wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4512 if (i
< (int) line
->GetObjectSizes().GetCount())
4514 objectSize
.x
= line
->GetObjectSizes()[(size_t) i
];
4520 child
->GetRangeSize(objectRange
, objectSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, objectPosition
);
4524 // Use the child object's width, but the whole line's height
4525 wxRect
childRect(objectPosition
, wxSize(objectSize
.x
, line
->GetSize().y
));
4526 child
->Draw(dc
, context
, objectRange
, selection
, childRect
, maxDescent
, style
);
4528 objectPosition
.x
+= objectSize
.x
;
4531 else if (child
->GetRange().GetStart() > lineRange
.GetEnd())
4532 // Can break out of inner loop now since we've passed this line's range
4535 node2
= node2
->GetNext();
4539 node
= node
->GetNext();
4545 // Get the range width using partial extents calculated for the whole paragraph.
4546 static int wxRichTextGetRangeWidth(const wxRichTextParagraph
& para
, const wxRichTextRange
& range
, const wxArrayInt
& partialExtents
)
4548 wxASSERT(partialExtents
.GetCount() >= (size_t) range
.GetLength());
4550 if (partialExtents
.GetCount() < (size_t) range
.GetLength())
4553 int leftMostPos
= 0;
4554 if (range
.GetStart() - para
.GetRange().GetStart() > 0)
4555 leftMostPos
= partialExtents
[range
.GetStart() - para
.GetRange().GetStart() - 1];
4557 int rightMostPos
= partialExtents
[range
.GetEnd() - para
.GetRange().GetStart()];
4559 int w
= rightMostPos
- leftMostPos
;
4564 /// Lay the item out
4565 bool wxRichTextParagraph::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
)
4567 // Deal with floating objects firstly before the normal layout
4568 wxRichTextBuffer
* buffer
= GetBuffer();
4570 wxRichTextFloatCollector
* collector
= GetContainer()->GetFloatCollector();
4571 wxASSERT(collector
);
4572 LayoutFloat(dc
, context
, rect
, style
, collector
);
4574 wxRichTextAttr attr
= GetCombinedAttributes();
4575 context
.ApplyVirtualAttributes(attr
, this);
4579 // Increase the size of the paragraph due to spacing
4580 int spaceBeforePara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingBefore());
4581 int spaceAfterPara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingAfter());
4582 int leftIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftIndent());
4583 int leftSubIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftSubIndent());
4584 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
4586 int lineSpacing
= 0;
4588 // Let's assume line spacing of 10 is normal, 15 is 1.5, 20 is 2, etc.
4589 if (attr
.HasLineSpacing() && attr
.GetLineSpacing() > 0 && attr
.GetFont().IsOk())
4591 wxCheckSetFont(dc
, attr
.GetFont());
4592 lineSpacing
= (int) (double(dc
.GetCharHeight()) * (double(attr
.GetLineSpacing())/10.0 - 1.0));
4595 // Start position for each line relative to the paragraph
4596 int startPositionFirstLine
= leftIndent
;
4597 int startPositionSubsequentLines
= leftIndent
+ leftSubIndent
;
4599 // If we have a bullet in this paragraph, the start position for the first line's text
4600 // is actually leftIndent + leftSubIndent.
4601 if (attr
.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE
)
4602 startPositionFirstLine
= startPositionSubsequentLines
;
4604 long lastEndPos
= GetRange().GetStart()-1;
4605 long lastCompletedEndPos
= lastEndPos
;
4607 int currentWidth
= 0;
4608 SetPosition(rect
.GetPosition());
4610 wxPoint
currentPosition(0, spaceBeforePara
); // We will calculate lines relative to paragraph
4613 int maxHeight
= currentPosition
.y
;
4618 int lineDescent
= 0;
4620 wxRichTextObjectList::compatibility_iterator node
;
4622 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4624 wxArrayInt partialExtents
;
4627 int paraDescent
= 0;
4629 // This calculates the partial text extents
4630 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, rect
.GetPosition(), & partialExtents
);
4632 node
= m_children
.GetFirst();
4635 wxRichTextObject
* child
= node
->GetData();
4637 //child->SetCachedSize(wxDefaultSize);
4638 child
->Layout(dc
, context
, rect
, style
);
4640 node
= node
->GetNext();
4646 // We may need to go back to a previous child, in which case create the new line,
4647 // find the child corresponding to the start position of the string, and
4650 wxRect availableRect
;
4652 node
= m_children
.GetFirst();
4655 wxRichTextObject
* child
= node
->GetData();
4657 // If floating, ignore. We already laid out floats.
4658 // Also ignore if empty object, except if we haven't got any
4660 if (child
->IsFloating() || !child
->IsShown() ||
4661 (child
->GetRange().GetLength() == 0 && maxHeight
> spaceBeforePara
)
4664 node
= node
->GetNext();
4668 // If this is e.g. a composite text box, it will need to be laid out itself.
4669 // But if just a text fragment or image, for example, this will
4670 // do nothing. NB: won't we need to set the position after layout?
4671 // since for example if position is dependent on vertical line size, we
4672 // can't tell the position until the size is determined. So possibly introduce
4673 // another layout phase.
4675 // We may only be looking at part of a child, if we searched back for wrapping
4676 // and found a suitable point some way into the child. So get the size for the fragment
4679 long nextBreakPos
= GetFirstLineBreakPosition(lastEndPos
+1);
4680 long lastPosToUse
= child
->GetRange().GetEnd();
4681 bool lineBreakInThisObject
= (nextBreakPos
> -1 && nextBreakPos
<= child
->GetRange().GetEnd());
4683 if (lineBreakInThisObject
)
4684 lastPosToUse
= nextBreakPos
;
4687 int childDescent
= 0;
4689 int startOffset
= (lineCount
== 0 ? startPositionFirstLine
: startPositionSubsequentLines
);
4690 availableRect
= wxRect(rect
.x
+ startOffset
, rect
.y
+ currentPosition
.y
,
4691 rect
.width
- startOffset
- rightIndent
, rect
.height
);
4693 if (child
->IsTopLevel())
4695 wxSize oldSize
= child
->GetCachedSize();
4697 child
->Invalidate(wxRICHTEXT_ALL
);
4698 child
->SetPosition(wxPoint(0, 0));
4700 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4701 // lays out the object again using the minimum size
4702 // The position will be determined by its location in its line,
4703 // and not by the child's actual position.
4704 child
->LayoutToBestSize(dc
, context
, buffer
,
4705 attr
, child
->GetAttributes(), availableRect
, parentRect
, style
);
4707 if (oldSize
!= child
->GetCachedSize())
4709 partialExtents
.Clear();
4711 // Recalculate the partial text extents since the child object changed size
4712 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), & partialExtents
);
4716 // Problem: we need to layout composites here for which we need the available width,
4717 // but we can't get the available width without using the float collector which
4718 // needs to know the object height.
4720 if ((nextBreakPos
== -1) && (lastEndPos
== child
->GetRange().GetStart() - 1)) // i.e. we want to get the whole thing
4722 childSize
= child
->GetCachedSize();
4723 childDescent
= child
->GetDescent();
4727 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4728 // Get height only, then the width using the partial extents
4729 GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
);
4730 childSize
.x
= wxRichTextGetRangeWidth(*this, wxRichTextRange(lastEndPos
+1, lastPosToUse
), partialExtents
);
4732 GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, rect
.GetPosition());
4737 int loopIterations
= 0;
4739 // If there are nested objects that need to lay themselves out, we have to do this in a
4740 // loop because the height of the object may well depend on the available width.
4741 // And because of floating object positioning, the available width depends on the
4742 // height of the object and whether it will clash with the floating objects.
4743 // So, we see whether the available width changes due to the presence of floating images.
4744 // If it does, then we'll use the new restricted width to find the object height again.
4745 // If this causes another restriction in the available width, we'll try again, until
4746 // either we lose patience or the available width settles down.
4751 wxRect oldAvailableRect
= availableRect
;
4753 // Available width depends on the floating objects and the line height.
4754 // Note: the floating objects may be placed vertically along the two sides of
4755 // buffer, so we may have different available line widths with different
4756 // [startY, endY]. So, we can't determine how wide the available
4757 // space is until we know the exact line height.
4758 if (childDescent
== 0)
4760 lineHeight
= wxMax(lineHeight
, childSize
.y
);
4761 lineDescent
= maxDescent
;
4762 lineAscent
= maxAscent
;
4766 lineDescent
= wxMax(childDescent
, maxDescent
);
4767 lineAscent
= wxMax(childSize
.y
-childDescent
, maxAscent
);
4769 lineHeight
= wxMax(lineHeight
, (lineDescent
+ lineAscent
));
4770 wxRect floatAvailableRect
= collector
->GetAvailableRect(rect
.y
+ currentPosition
.y
, rect
.y
+ currentPosition
.y
+ lineHeight
);
4772 // Adjust availableRect to the space that is available when taking floating objects into account.
4774 if (floatAvailableRect
.x
+ startOffset
> availableRect
.x
)
4776 int newX
= floatAvailableRect
.x
+ startOffset
;
4777 int newW
= availableRect
.width
- (newX
- availableRect
.x
);
4778 availableRect
.x
= newX
;
4779 availableRect
.width
= newW
;
4782 if (floatAvailableRect
.width
< availableRect
.width
)
4783 availableRect
.width
= floatAvailableRect
.width
;
4785 currentPosition
.x
= availableRect
.x
- rect
.x
;
4787 if (child
->IsTopLevel() && loopIterations
<= 20)
4789 if (availableRect
!= oldAvailableRect
)
4791 wxSize oldSize
= child
->GetCachedSize();
4793 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4794 // lays out the object again using the minimum size
4795 child
->Invalidate(wxRICHTEXT_ALL
);
4796 child
->LayoutToBestSize(dc
, context
, buffer
,
4797 attr
, child
->GetAttributes(), availableRect
, parentRect
, style
);
4798 childSize
= child
->GetCachedSize();
4799 childDescent
= child
->GetDescent();
4801 if (oldSize
!= child
->GetCachedSize())
4803 partialExtents
.Clear();
4805 // Recalculate the partial text extents since the child object changed size
4806 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), & partialExtents
);
4809 // Go around the loop finding the available rect for the given floating objects
4819 if (child
->IsTopLevel())
4821 // We can move it to the correct position at this point
4822 // TODO: probably need to add margin
4823 child
->Move(GetPosition() + wxPoint(currentWidth
+ (wxMax(leftIndent
, leftIndent
+ leftSubIndent
)), currentPosition
.y
));
4827 // 1) There was a line break BEFORE the natural break
4828 // 2) There was a line break AFTER the natural break
4829 // 3) It's the last line
4830 // 4) The child still fits (carry on) - 'else' clause
4832 if ((lineBreakInThisObject
&& (childSize
.x
+ currentWidth
<= availableRect
.width
))
4834 (childSize
.x
+ currentWidth
> availableRect
.width
)
4836 ((childSize
.x
+ currentWidth
<= availableRect
.width
) && !node
->GetNext())
4840 long wrapPosition
= 0;
4841 if ((childSize
.x
+ currentWidth
<= availableRect
.width
) && !node
->GetNext() && !lineBreakInThisObject
)
4842 wrapPosition
= child
->GetRange().GetEnd();
4845 // Find a place to wrap. This may walk back to previous children,
4846 // for example if a word spans several objects.
4847 // Note: one object must contains only one wxTextAtrr, so the line height will not
4848 // change inside one object. Thus, we can pass the remain line width to the
4849 // FindWrapPosition function.
4850 if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos
+1, child
->GetRange().GetEnd()), dc
, context
, availableRect
.width
, wrapPosition
, & partialExtents
))
4852 // If the function failed, just cut it off at the end of this child.
4853 wrapPosition
= child
->GetRange().GetEnd();
4856 // FindWrapPosition can still return a value that will put us in an endless wrapping loop
4857 if (wrapPosition
<= lastCompletedEndPos
)
4858 wrapPosition
= wxMax(lastCompletedEndPos
+1,child
->GetRange().GetEnd());
4860 // Line end position shouldn't be the same as the end, or greater.
4861 if (wrapPosition
>= GetRange().GetEnd())
4862 wrapPosition
= wxMax(0, GetRange().GetEnd()-1);
4864 // wxLogDebug(wxT("Split at %ld"), wrapPosition);
4866 // Let's find the actual size of the current line now
4868 wxRichTextRange
actualRange(lastCompletedEndPos
+1, wrapPosition
);
4872 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4873 if (!child
->IsEmpty())
4875 // Get height only, then the width using the partial extents
4876 GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
);
4877 actualSize
.x
= wxRichTextGetRangeWidth(*this, actualRange
, partialExtents
);
4881 GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
4883 currentWidth
= actualSize
.x
;
4885 // The descent for the whole line at this point, is the correct max descent
4886 maxDescent
= childDescent
;
4888 maxAscent
= actualSize
.y
-childDescent
;
4890 // lineHeight is given by the height for the whole line, since it will
4891 // take into account ascend/descend.
4892 lineHeight
= actualSize
.y
;
4894 if (lineHeight
== 0 && buffer
)
4896 wxFont
font(buffer
->GetFontTable().FindFont(attr
));
4897 wxCheckSetFont(dc
, font
);
4898 lineHeight
= dc
.GetCharHeight();
4901 if (maxDescent
== 0)
4904 dc
.GetTextExtent(wxT("X"), & w
, &h
, & maxDescent
);
4908 wxRichTextLine
* line
= AllocateLine(lineCount
);
4910 // Set relative range so we won't have to change line ranges when paragraphs are moved
4911 line
->SetRange(wxRichTextRange(actualRange
.GetStart() - GetRange().GetStart(), actualRange
.GetEnd() - GetRange().GetStart()));
4912 line
->SetPosition(currentPosition
);
4913 line
->SetSize(wxSize(currentWidth
, lineHeight
));
4914 line
->SetDescent(maxDescent
);
4916 maxHeight
= currentPosition
.y
+ lineHeight
;
4918 // Now move down a line. TODO: add margins, spacing
4919 currentPosition
.y
+= lineHeight
;
4920 currentPosition
.y
+= lineSpacing
;
4923 maxWidth
= wxMax(maxWidth
, currentWidth
+startOffset
);
4928 // TODO: account for zero-length objects
4929 // wxASSERT(wrapPosition > lastCompletedEndPos);
4931 lastEndPos
= wrapPosition
;
4932 lastCompletedEndPos
= lastEndPos
;
4936 if (wrapPosition
< GetRange().GetEnd()-1)
4938 // May need to set the node back to a previous one, due to searching back in wrapping
4939 wxRichTextObject
* childAfterWrapPosition
= FindObjectAtPosition(wrapPosition
+1);
4940 if (childAfterWrapPosition
)
4941 node
= m_children
.Find(childAfterWrapPosition
);
4943 node
= node
->GetNext();
4946 node
= node
->GetNext();
4948 // Apply paragraph styles such as alignment to the wrapped line
4949 ApplyParagraphStyle(line
, attr
, availableRect
, dc
);
4953 // We still fit, so don't add a line, and keep going
4954 currentWidth
+= childSize
.x
;
4956 if (childDescent
== 0)
4958 // An object with a zero descend value wants to take up the whole
4959 // height regardless of baseline
4960 lineHeight
= wxMax(lineHeight
, childSize
.y
);
4964 maxDescent
= wxMax(childDescent
, maxDescent
);
4965 maxAscent
= wxMax(childSize
.y
-childDescent
, maxAscent
);
4968 lineHeight
= wxMax(lineHeight
, (maxDescent
+ maxAscent
));
4970 maxWidth
= wxMax(maxWidth
, currentWidth
+startOffset
);
4971 lastEndPos
= child
->GetRange().GetEnd();
4973 node
= node
->GetNext();
4977 //wxASSERT(!(lastCompletedEndPos != -1 && lastCompletedEndPos < GetRange().GetEnd()-1));
4979 // Remove remaining unused line objects, if any
4980 ClearUnusedLines(lineCount
);
4982 // We need to add back the margins etc.
4984 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
4985 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxWidth
, currentPosition
.y
+ spaceAfterPara
));
4986 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
4987 SetCachedSize(marginRect
.GetSize());
4990 // The maximum size is the length of the paragraph stretched out into a line.
4991 // So if there were a single word, or an image, or a fixed-size text box, the object could be shrunk around
4992 // this size. TODO: take into account line breaks.
4994 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
4995 contentRect
= wxRect(wxPoint(0, 0), wxSize(paraSize
.x
+ wxMax(leftIndent
, leftIndent
+ leftSubIndent
) + rightIndent
, currentPosition
.y
+ spaceAfterPara
));
4996 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
4997 SetMaxSize(marginRect
.GetSize());
5000 // Find the greatest minimum size. Currently we only look at non-text objects,
5001 // which isn't ideal but it would be slow to find the maximum word width to
5002 // use as the minimum.
5005 node
= m_children
.GetFirst();
5008 wxRichTextObject
* child
= node
->GetData();
5010 // If floating, ignore. We already laid out floats.
5011 // Also ignore if empty object, except if we haven't got any
5013 if (!child
->IsFloating() && child
->GetRange().GetLength() != 0 && !wxDynamicCast(child
, wxRichTextPlainText
))
5015 if (child
->GetCachedSize().x
> minWidth
)
5016 minWidth
= child
->GetMinSize().x
;
5018 node
= node
->GetNext();
5021 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
5022 contentRect
= wxRect(wxPoint(0, 0), wxSize(minWidth
, currentPosition
.y
+ spaceAfterPara
));
5023 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
5024 SetMinSize(marginRect
.GetSize());
5027 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5028 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
5029 // Use the text extents to calculate the size of each fragment in each line
5030 wxRichTextLineList::compatibility_iterator lineNode
= m_cachedLines
.GetFirst();
5033 wxRichTextLine
* line
= lineNode
->GetData();
5034 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5036 // Loop through objects until we get to the one within range
5037 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
5041 wxRichTextObject
* child
= node2
->GetData();
5043 if (child
->GetRange().GetLength() > 0 && !child
->GetRange().IsOutside(lineRange
))
5045 wxRichTextRange rangeToUse
= lineRange
;
5046 rangeToUse
.LimitTo(child
->GetRange());
5048 // Find the size of the child from the text extents, and store in an array
5049 // for drawing later
5051 if (rangeToUse
.GetStart() > GetRange().GetStart())
5052 left
= partialExtents
[(rangeToUse
.GetStart()-1) - GetRange().GetStart()];
5053 int right
= partialExtents
[rangeToUse
.GetEnd() - GetRange().GetStart()];
5054 int sz
= right
- left
;
5055 line
->GetObjectSizes().Add(sz
);
5057 else if (child
->GetRange().GetStart() > lineRange
.GetEnd())
5058 // Can break out of inner loop now since we've passed this line's range
5061 node2
= node2
->GetNext();
5064 lineNode
= lineNode
->GetNext();
5072 /// Apply paragraph styles, such as centering, to wrapped lines
5073 /// TODO: take into account box attributes, possibly
5074 void wxRichTextParagraph::ApplyParagraphStyle(wxRichTextLine
* line
, const wxRichTextAttr
& attr
, const wxRect
& rect
, wxDC
& dc
)
5076 if (!attr
.HasAlignment())
5079 wxPoint pos
= line
->GetPosition();
5080 wxPoint originalPos
= pos
;
5081 wxSize size
= line
->GetSize();
5083 // centering, right-justification
5084 if (attr
.HasAlignment() && attr
.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE
)
5086 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
5087 pos
.x
= (rect
.GetWidth() - rightIndent
- size
.x
)/2 + pos
.x
;
5088 line
->SetPosition(pos
);
5090 else if (attr
.HasAlignment() && attr
.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT
)
5092 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
5093 pos
.x
= pos
.x
+ rect
.GetWidth() - size
.x
- rightIndent
;
5094 line
->SetPosition(pos
);
5097 if (pos
!= originalPos
)
5099 wxPoint inc
= pos
- originalPos
;
5101 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5105 wxRichTextObject
* child
= node
->GetData();
5106 if (child
->IsTopLevel() && !child
->GetRange().IsOutside(line
->GetAbsoluteRange()))
5107 child
->Move(child
->GetPosition() + inc
);
5109 node
= node
->GetNext();
5114 /// Insert text at the given position
5115 bool wxRichTextParagraph::InsertText(long pos
, const wxString
& text
)
5117 wxRichTextObject
* childToUse
= NULL
;
5118 wxRichTextObjectList::compatibility_iterator nodeToUse
= wxRichTextObjectList::compatibility_iterator();
5120 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5123 wxRichTextObject
* child
= node
->GetData();
5124 if (child
->GetRange().Contains(pos
) && child
->GetRange().GetLength() > 0)
5131 node
= node
->GetNext();
5136 wxRichTextPlainText
* textObject
= wxDynamicCast(childToUse
, wxRichTextPlainText
);
5139 int posInString
= pos
- textObject
->GetRange().GetStart();
5141 wxString newText
= textObject
->GetText().Mid(0, posInString
) +
5142 text
+ textObject
->GetText().Mid(posInString
);
5143 textObject
->SetText(newText
);
5145 int textLength
= text
.length();
5147 textObject
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart(),
5148 textObject
->GetRange().GetEnd() + textLength
));
5150 // Increment the end range of subsequent fragments in this paragraph.
5151 // We'll set the paragraph range itself at a higher level.
5153 wxRichTextObjectList::compatibility_iterator node
= nodeToUse
->GetNext();
5156 wxRichTextObject
* child
= node
->GetData();
5157 child
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart() + textLength
,
5158 textObject
->GetRange().GetEnd() + textLength
));
5160 node
= node
->GetNext();
5167 // TODO: if not a text object, insert at closest position, e.g. in front of it
5173 // Don't pass parent initially to suppress auto-setting of parent range.
5174 // We'll do that at a higher level.
5175 wxRichTextPlainText
* textObject
= new wxRichTextPlainText(text
, this);
5177 AppendChild(textObject
);
5184 void wxRichTextParagraph::Copy(const wxRichTextParagraph
& obj
)
5186 wxRichTextCompositeObject::Copy(obj
);
5189 /// Clear the cached lines
5190 void wxRichTextParagraph::ClearLines()
5192 WX_CLEAR_LIST(wxRichTextLineList
, m_cachedLines
);
5195 /// Get/set the object size for the given range. Returns false if the range
5196 /// is invalid for this object.
5197 bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
5199 if (!range
.IsWithin(GetRange()))
5202 if (flags
& wxRICHTEXT_UNFORMATTED
)
5204 // Just use unformatted data, assume no line breaks
5207 wxArrayInt childExtents
;
5216 int maxLineHeight
= 0;
5218 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5221 wxRichTextObject
* child
= node
->GetData();
5222 if (!child
->GetRange().IsOutside(range
))
5224 // Floating objects have a zero size within the paragraph.
5225 if (child
->IsFloating())
5230 if (partialExtents
->GetCount() > 0)
5231 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5235 partialExtents
->Add(0 /* zero size */ + lastSize
);
5242 wxRichTextRange rangeToUse
= range
;
5243 rangeToUse
.LimitTo(child
->GetRange());
5244 int childDescent
= 0;
5246 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we've already cached the size,
5247 // but it's only going to be used after caching has taken place.
5248 if ((flags
& wxRICHTEXT_HEIGHT_ONLY
) && child
->GetCachedSize().y
!= 0)
5250 childDescent
= child
->GetDescent();
5251 childSize
= child
->GetCachedSize();
5253 if (childDescent
== 0)
5255 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5259 maxDescent
= wxMax(maxDescent
, childDescent
);
5260 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5263 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5265 sz
.y
= wxMax(sz
.y
, maxLineHeight
);
5266 sz
.x
+= childSize
.x
;
5267 descent
= maxDescent
;
5269 else if (child
->IsTopLevel())
5271 childDescent
= child
->GetDescent();
5272 childSize
= child
->GetCachedSize();
5274 if (childDescent
== 0)
5276 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5280 maxDescent
= wxMax(maxDescent
, childDescent
);
5281 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5284 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5286 sz
.y
= wxMax(sz
.y
, maxLineHeight
);
5287 sz
.x
+= childSize
.x
;
5288 descent
= maxDescent
;
5290 // FIXME: this won't change the original values.
5291 // Should we be calling GetRangeSize above instead of using cached values?
5293 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange()))
5295 child
->SetCachedSize(childSize
);
5296 child
->SetDescent(childDescent
);
5303 if (partialExtents
->GetCount() > 0)
5304 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5308 partialExtents
->Add(childSize
.x
+ lastSize
);
5311 else if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, context
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
), p
))
5313 if (childDescent
== 0)
5315 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5319 maxDescent
= wxMax(maxDescent
, childDescent
);
5320 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5323 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5325 sz
.y
= wxMax(sz
.y
, maxLineHeight
);
5326 sz
.x
+= childSize
.x
;
5327 descent
= maxDescent
;
5329 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange()))
5331 child
->SetCachedSize(childSize
);
5332 child
->SetDescent(childDescent
);
5338 if (partialExtents
->GetCount() > 0)
5339 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5344 for (i
= 0; i
< childExtents
.GetCount(); i
++)
5346 partialExtents
->Add(childExtents
[i
] + lastSize
);
5356 node
= node
->GetNext();
5362 // Use formatted data, with line breaks
5365 // We're going to loop through each line, and then for each line,
5366 // call GetRangeSize for the fragment that comprises that line.
5367 // Only we have to do that multiple times within the line, because
5368 // the line may be broken into pieces. For now ignore line break commands
5369 // (so we can assume that getting the unformatted size for a fragment
5370 // within a line is the actual size)
5372 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5375 wxRichTextLine
* line
= node
->GetData();
5376 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5377 if (!lineRange
.IsOutside(range
))
5381 int maxLineHeight
= 0;
5382 int maxLineWidth
= 0;
5384 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
5387 wxRichTextObject
* child
= node2
->GetData();
5389 if (!child
->IsFloating() && !child
->GetRange().IsOutside(lineRange
))
5391 wxRichTextRange rangeToUse
= lineRange
;
5392 rangeToUse
.LimitTo(child
->GetRange());
5393 if (child
->IsTopLevel())
5394 rangeToUse
= child
->GetOwnRange();
5397 int childDescent
= 0;
5398 if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, context
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
)))
5400 if (childDescent
== 0)
5402 // Assume that if descent is zero, this child can occupy the full line height
5403 // and does not need space for the line's maximum descent. So we influence
5404 // the overall max line height only.
5405 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5409 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5410 maxDescent
= wxMax(maxAscent
, childDescent
);
5412 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5413 maxLineWidth
+= childSize
.x
;
5417 node2
= node2
->GetNext();
5420 descent
= wxMax(descent
, maxDescent
);
5422 // Increase size by a line (TODO: paragraph spacing)
5423 sz
.y
+= maxLineHeight
;
5424 sz
.x
= wxMax(sz
.x
, maxLineWidth
);
5426 node
= node
->GetNext();
5433 /// Finds the absolute position and row height for the given character position
5434 bool wxRichTextParagraph::FindPosition(wxDC
& dc
, wxRichTextDrawingContext
& context
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
5438 wxRichTextLine
* line
= ((wxRichTextParagraphLayoutBox
*)GetParent())->GetLineAtPosition(0);
5440 *height
= line
->GetSize().y
;
5442 *height
= dc
.GetCharHeight();
5444 // -1 means 'the start of the buffer'.
5447 pt
= pt
+ line
->GetPosition();
5452 // The final position in a paragraph is taken to mean the position
5453 // at the start of the next paragraph.
5454 if (index
== GetRange().GetEnd())
5456 wxRichTextParagraphLayoutBox
* parent
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
5457 wxASSERT( parent
!= NULL
);
5459 // Find the height at the next paragraph, if any
5460 wxRichTextLine
* line
= parent
->GetLineAtPosition(index
+ 1);
5463 *height
= line
->GetSize().y
;
5464 pt
= line
->GetAbsolutePosition();
5468 *height
= dc
.GetCharHeight();
5469 int indent
= ConvertTenthsMMToPixels(dc
, m_attributes
.GetLeftIndent());
5470 pt
= wxPoint(indent
, GetCachedSize().y
);
5476 if (index
< GetRange().GetStart() || index
> GetRange().GetEnd())
5479 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5482 wxRichTextLine
* line
= node
->GetData();
5483 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5484 if (index
>= lineRange
.GetStart() && index
<= lineRange
.GetEnd())
5486 // If this is the last point in the line, and we're forcing the
5487 // returned value to be the start of the next line, do the required
5489 if (index
== lineRange
.GetEnd() && forceLineStart
)
5491 if (node
->GetNext())
5493 wxRichTextLine
* nextLine
= node
->GetNext()->GetData();
5494 *height
= nextLine
->GetSize().y
;
5495 pt
= nextLine
->GetAbsolutePosition();
5500 pt
.y
= line
->GetPosition().y
+ GetPosition().y
;
5502 wxRichTextRange
r(lineRange
.GetStart(), index
);
5506 // We find the size of the line up to this point,
5507 // then we can add this size to the line start position and
5508 // paragraph start position to find the actual position.
5510 if (GetRangeSize(r
, rangeSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, line
->GetPosition()+ GetPosition()))
5512 pt
.x
= line
->GetPosition().x
+ GetPosition().x
+ rangeSize
.x
;
5513 *height
= line
->GetSize().y
;
5520 node
= node
->GetNext();
5526 /// Hit-testing: returns a flag indicating hit test details, plus
5527 /// information about position
5528 int wxRichTextParagraph::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
5531 return wxRICHTEXT_HITTEST_NONE
;
5533 // If we're in the top-level container, then we can return
5534 // a suitable hit test code even if the point is outside the container area,
5535 // so that we can position the caret sensibly even if we don't
5536 // click on valid content. If we're not at the top-level, and the point
5537 // is not within this paragraph object, then we don't want to stop more
5538 // precise hit-testing from working prematurely, so return immediately.
5539 // NEW STRATEGY: use the parent boundary to test whether we're in the
5540 // right region, not the paragraph, since the paragraph may be positioned
5541 // some way in from where the user clicks.
5544 wxRichTextObject
* tempObj
, *tempContextObj
;
5545 if (GetParent() && GetParent()->wxRichTextObject::HitTest(dc
, context
, pt
, tmpPos
, & tempObj
, & tempContextObj
, flags
) == wxRICHTEXT_HITTEST_NONE
)
5546 return wxRICHTEXT_HITTEST_NONE
;
5549 wxRichTextObjectList::compatibility_iterator objNode
= m_children
.GetFirst();
5552 wxRichTextObject
* child
= objNode
->GetData();
5553 // Don't recurse if we have wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS,
5554 // and also, if this seems composite but actually is marked as atomic,
5556 if (child
->IsTopLevel() && ((flags
& wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS
) == 0) &&
5557 (! (((flags
& wxRICHTEXT_HITTEST_HONOUR_ATOMIC
) != 0) && child
->IsAtomic())))
5560 int hitTest
= child
->HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
);
5561 if (hitTest
!= wxRICHTEXT_HITTEST_NONE
)
5566 objNode
= objNode
->GetNext();
5569 wxPoint paraPos
= GetPosition();
5571 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5574 wxRichTextLine
* line
= node
->GetData();
5575 wxPoint linePos
= paraPos
+ line
->GetPosition();
5576 wxSize lineSize
= line
->GetSize();
5577 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5579 if (pt
.y
<= linePos
.y
+ lineSize
.y
)
5581 if (pt
.x
< linePos
.x
)
5583 textPosition
= lineRange
.GetStart();
5584 *obj
= FindObjectAtPosition(textPosition
);
5585 *contextObj
= GetContainer();
5586 return wxRICHTEXT_HITTEST_BEFORE
|wxRICHTEXT_HITTEST_OUTSIDE
;
5588 else if (pt
.x
>= (linePos
.x
+ lineSize
.x
))
5590 textPosition
= lineRange
.GetEnd();
5591 *obj
= FindObjectAtPosition(textPosition
);
5592 *contextObj
= GetContainer();
5593 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
;
5597 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5598 wxArrayInt partialExtents
;
5603 // This calculates the partial text extents
5604 GetRangeSize(lineRange
, paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, linePos
, & partialExtents
);
5606 int lastX
= linePos
.x
;
5608 for (i
= 0; i
< partialExtents
.GetCount(); i
++)
5610 int nextX
= partialExtents
[i
] + linePos
.x
;
5612 if (pt
.x
>= lastX
&& pt
.x
<= nextX
)
5614 textPosition
= i
+ lineRange
.GetStart(); // minus 1?
5616 *obj
= FindObjectAtPosition(textPosition
);
5617 *contextObj
= GetContainer();
5619 // So now we know it's between i-1 and i.
5620 // Let's see if we can be more precise about
5621 // which side of the position it's on.
5623 int midPoint
= (nextX
+ lastX
)/2;
5624 if (pt
.x
>= midPoint
)
5625 return wxRICHTEXT_HITTEST_AFTER
;
5627 return wxRICHTEXT_HITTEST_BEFORE
;
5634 int lastX
= linePos
.x
;
5635 for (i
= lineRange
.GetStart(); i
<= lineRange
.GetEnd(); i
++)
5640 wxRichTextRange
rangeToUse(lineRange
.GetStart(), i
);
5642 GetRangeSize(rangeToUse
, childSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, linePos
);
5644 int nextX
= childSize
.x
+ linePos
.x
;
5646 if (pt
.x
>= lastX
&& pt
.x
<= nextX
)
5650 *obj
= FindObjectAtPosition(textPosition
);
5651 *contextObj
= GetContainer();
5653 // So now we know it's between i-1 and i.
5654 // Let's see if we can be more precise about
5655 // which side of the position it's on.
5657 int midPoint
= (nextX
+ lastX
)/2;
5658 if (pt
.x
>= midPoint
)
5659 return wxRICHTEXT_HITTEST_AFTER
;
5661 return wxRICHTEXT_HITTEST_BEFORE
;
5672 node
= node
->GetNext();
5675 return wxRICHTEXT_HITTEST_NONE
;
5678 /// Split an object at this position if necessary, and return
5679 /// the previous object, or NULL if inserting at beginning.
5680 wxRichTextObject
* wxRichTextParagraph::SplitAt(long pos
, wxRichTextObject
** previousObject
)
5682 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5685 wxRichTextObject
* child
= node
->GetData();
5687 if (pos
== child
->GetRange().GetStart())
5691 if (node
->GetPrevious())
5692 *previousObject
= node
->GetPrevious()->GetData();
5694 *previousObject
= NULL
;
5700 if (child
->GetRange().Contains(pos
))
5702 // This should create a new object, transferring part of
5703 // the content to the old object and the rest to the new object.
5704 wxRichTextObject
* newObject
= child
->DoSplit(pos
);
5706 // If we couldn't split this object, just insert in front of it.
5709 // Maybe this is an empty string, try the next one
5714 // Insert the new object after 'child'
5715 if (node
->GetNext())
5716 m_children
.Insert(node
->GetNext(), newObject
);
5718 m_children
.Append(newObject
);
5719 newObject
->SetParent(this);
5722 *previousObject
= child
;
5728 node
= node
->GetNext();
5731 *previousObject
= NULL
;
5735 /// Move content to a list from obj on
5736 void wxRichTextParagraph::MoveToList(wxRichTextObject
* obj
, wxList
& list
)
5738 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(obj
);
5741 wxRichTextObject
* child
= node
->GetData();
5744 wxRichTextObjectList::compatibility_iterator oldNode
= node
;
5746 node
= node
->GetNext();
5748 m_children
.DeleteNode(oldNode
);
5752 /// Add content back from list
5753 void wxRichTextParagraph::MoveFromList(wxList
& list
)
5755 for (wxList::compatibility_iterator node
= list
.GetFirst(); node
; node
= node
->GetNext())
5757 AppendChild((wxRichTextObject
*) node
->GetData());
5762 void wxRichTextParagraph::CalculateRange(long start
, long& end
)
5764 wxRichTextCompositeObject::CalculateRange(start
, end
);
5766 // Add one for end of paragraph
5769 m_range
.SetRange(start
, end
);
5772 /// Find the object at the given position
5773 wxRichTextObject
* wxRichTextParagraph::FindObjectAtPosition(long position
)
5775 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5778 wxRichTextObject
* obj
= node
->GetData();
5779 if (obj
->GetRange().Contains(position
) ||
5780 obj
->GetRange().GetStart() == position
||
5781 obj
->GetRange().GetEnd() == position
)
5784 node
= node
->GetNext();
5789 /// Get the plain text searching from the start or end of the range.
5790 /// The resulting string may be shorter than the range given.
5791 bool wxRichTextParagraph::GetContiguousPlainText(wxString
& text
, const wxRichTextRange
& range
, bool fromStart
)
5793 text
= wxEmptyString
;
5797 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5800 wxRichTextObject
* obj
= node
->GetData();
5801 if (!obj
->GetRange().IsOutside(range
))
5803 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5806 text
+= textObj
->GetTextForRange(range
);
5814 node
= node
->GetNext();
5819 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetLast();
5822 wxRichTextObject
* obj
= node
->GetData();
5823 if (!obj
->GetRange().IsOutside(range
))
5825 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5828 text
= textObj
->GetTextForRange(range
) + text
;
5832 text
= wxT(" ") + text
;
5836 node
= node
->GetPrevious();
5843 /// Find a suitable wrap position.
5844 bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange
& range
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int availableSpace
, long& wrapPosition
, wxArrayInt
* partialExtents
)
5846 if (range
.GetLength() <= 0)
5849 // Find the first position where the line exceeds the available space.
5851 long breakPosition
= range
.GetEnd();
5853 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5854 if (partialExtents
&& partialExtents
->GetCount() >= (size_t) (GetRange().GetLength()-1)) // the final position in a paragraph is the newline
5858 if (range
.GetStart() > GetRange().GetStart())
5859 widthBefore
= (*partialExtents
)[range
.GetStart() - GetRange().GetStart() - 1];
5864 for (i
= (size_t) range
.GetStart(); i
<= (size_t) range
.GetEnd(); i
++)
5866 int widthFromStartOfThisRange
= (*partialExtents
)[i
- GetRange().GetStart()] - widthBefore
;
5868 if (widthFromStartOfThisRange
> availableSpace
)
5870 breakPosition
= i
-1;
5878 // Binary chop for speed
5879 long minPos
= range
.GetStart();
5880 long maxPos
= range
.GetEnd();
5883 if (minPos
== maxPos
)
5886 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
5888 if (sz
.x
> availableSpace
)
5889 breakPosition
= minPos
- 1;
5892 else if ((maxPos
- minPos
) == 1)
5895 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
5897 if (sz
.x
> availableSpace
)
5898 breakPosition
= minPos
- 1;
5901 GetRangeSize(wxRichTextRange(range
.GetStart(), maxPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
5902 if (sz
.x
> availableSpace
)
5903 breakPosition
= maxPos
-1;
5909 long nextPos
= minPos
+ ((maxPos
- minPos
) / 2);
5912 GetRangeSize(wxRichTextRange(range
.GetStart(), nextPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
5914 if (sz
.x
> availableSpace
)
5926 // Now we know the last position on the line.
5927 // Let's try to find a word break.
5930 if (GetContiguousPlainText(plainText
, wxRichTextRange(range
.GetStart(), breakPosition
), false))
5932 int newLinePos
= plainText
.Find(wxRichTextLineBreakChar
);
5933 if (newLinePos
!= wxNOT_FOUND
)
5935 breakPosition
= wxMax(0, range
.GetStart() + newLinePos
);
5939 int spacePos
= plainText
.Find(wxT(' '), true);
5940 int tabPos
= plainText
.Find(wxT('\t'), true);
5941 int pos
= wxMax(spacePos
, tabPos
);
5942 if (pos
!= wxNOT_FOUND
)
5944 int positionsFromEndOfString
= plainText
.length() - pos
- 1;
5945 breakPosition
= breakPosition
- positionsFromEndOfString
;
5950 wrapPosition
= breakPosition
;
5955 /// Get the bullet text for this paragraph.
5956 wxString
wxRichTextParagraph::GetBulletText()
5958 if (GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
||
5959 (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
))
5960 return wxEmptyString
;
5962 int number
= GetAttributes().GetBulletNumber();
5965 if ((GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ARABIC
) || (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
))
5967 text
.Printf(wxT("%d"), number
);
5969 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_UPPER
)
5971 // TODO: Unicode, and also check if number > 26
5972 text
.Printf(wxT("%c"), (wxChar
) (number
+64));
5974 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_LOWER
)
5976 // TODO: Unicode, and also check if number > 26
5977 text
.Printf(wxT("%c"), (wxChar
) (number
+96));
5979 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER
)
5981 text
= wxRichTextDecimalToRoman(number
);
5983 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER
)
5985 text
= wxRichTextDecimalToRoman(number
);
5988 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
)
5990 text
= GetAttributes().GetBulletText();
5993 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
)
5995 // The outline style relies on the text being computed statically,
5996 // since it depends on other levels points (e.g. 1.2.1.1). So normally the bullet text
5997 // should be stored in the attributes; if not, just use the number for this
5998 // level, as previously computed.
5999 if (!GetAttributes().GetBulletText().IsEmpty())
6000 text
= GetAttributes().GetBulletText();
6003 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PARENTHESES
)
6005 text
= wxT("(") + text
+ wxT(")");
6007 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_RIGHT_PARENTHESIS
)
6009 text
= text
+ wxT(")");
6012 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PERIOD
)
6020 /// Allocate or reuse a line object
6021 wxRichTextLine
* wxRichTextParagraph::AllocateLine(int pos
)
6023 if (pos
< (int) m_cachedLines
.GetCount())
6025 wxRichTextLine
* line
= m_cachedLines
.Item(pos
)->GetData();
6031 wxRichTextLine
* line
= new wxRichTextLine(this);
6032 m_cachedLines
.Append(line
);
6037 /// Clear remaining unused line objects, if any
6038 bool wxRichTextParagraph::ClearUnusedLines(int lineCount
)
6040 int cachedLineCount
= m_cachedLines
.GetCount();
6041 if ((int) cachedLineCount
> lineCount
)
6043 for (int i
= 0; i
< (int) (cachedLineCount
- lineCount
); i
++)
6045 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetLast();
6046 wxRichTextLine
* line
= node
->GetData();
6047 m_cachedLines
.Erase(node
);
6054 /// Get combined attributes of the base style, paragraph style and character style. We use this to dynamically
6055 /// retrieve the actual style.
6056 wxRichTextAttr
wxRichTextParagraph::GetCombinedAttributes(const wxRichTextAttr
& contentStyle
, bool includingBoxAttr
) const
6058 wxRichTextAttr attr
;
6059 wxRichTextParagraphLayoutBox
* buf
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
6062 attr
= buf
->GetBasicStyle();
6063 if (!includingBoxAttr
)
6065 attr
.GetTextBoxAttr().Reset();
6066 // The background colour will be painted by the container, and we don't
6067 // want to unnecessarily overwrite the background when we're drawing text
6068 // because this may erase the guideline (which appears just under the text
6069 // if there's no padding).
6070 attr
.SetFlags(attr
.GetFlags() & ~wxTEXT_ATTR_BACKGROUND_COLOUR
);
6072 wxRichTextApplyStyle(attr
, GetAttributes());
6075 attr
= GetAttributes();
6077 wxRichTextApplyStyle(attr
, contentStyle
);
6081 /// Get combined attributes of the base style and paragraph style.
6082 wxRichTextAttr
wxRichTextParagraph::GetCombinedAttributes(bool includingBoxAttr
) const
6084 wxRichTextAttr attr
;
6085 wxRichTextParagraphLayoutBox
* buf
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
6088 attr
= buf
->GetBasicStyle();
6089 if (!includingBoxAttr
)
6090 attr
.GetTextBoxAttr().Reset();
6091 wxRichTextApplyStyle(attr
, GetAttributes());
6094 attr
= GetAttributes();
6099 // Create default tabstop array
6100 void wxRichTextParagraph::InitDefaultTabs()
6102 // create a default tab list at 10 mm each.
6103 for (int i
= 0; i
< 20; ++i
)
6105 sm_defaultTabs
.Add(i
*100);
6109 // Clear default tabstop array
6110 void wxRichTextParagraph::ClearDefaultTabs()
6112 sm_defaultTabs
.Clear();
6115 void wxRichTextParagraph::LayoutFloat(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, int style
, wxRichTextFloatCollector
* floatCollector
)
6117 wxRichTextObjectList::compatibility_iterator node
= GetChildren().GetFirst();
6120 wxRichTextObject
* anchored
= node
->GetData();
6121 if (anchored
&& anchored
->IsFloating() && !floatCollector
->HasFloat(anchored
))
6125 anchored
->GetRangeSize(anchored
->GetRange(), size
, descent
, dc
, context
, style
);
6128 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().IsValid())
6130 offsetY
= anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetValue();
6131 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
6133 offsetY
= ConvertTenthsMMToPixels(dc
, offsetY
);
6137 int pos
= floatCollector
->GetFitPosition(anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode(), rect
.y
+ offsetY
, size
.y
);
6139 /* Update the offset */
6140 int newOffsetY
= pos
- rect
.y
;
6141 if (newOffsetY
!= offsetY
)
6143 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
6144 newOffsetY
= ConvertPixelsToTenthsMM(dc
, newOffsetY
);
6145 anchored
->GetAttributes().GetTextBoxAttr().GetTop().SetValue(newOffsetY
);
6148 if (anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT
)
6150 else if (anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT
)
6151 x
= rect
.x
+ rect
.width
- size
.x
;
6153 anchored
->SetPosition(wxPoint(x
, pos
));
6154 anchored
->SetCachedSize(size
);
6155 floatCollector
->CollectFloat(this, anchored
);
6158 node
= node
->GetNext();
6162 // Get the first position from pos that has a line break character.
6163 long wxRichTextParagraph::GetFirstLineBreakPosition(long pos
)
6165 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
6168 wxRichTextObject
* obj
= node
->GetData();
6169 if (pos
>= obj
->GetRange().GetStart() && pos
<= obj
->GetRange().GetEnd())
6171 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
6174 long breakPos
= textObj
->GetFirstLineBreakPosition(pos
);
6179 node
= node
->GetNext();
6186 * This object represents a line in a paragraph, and stores
6187 * offsets from the start of the paragraph representing the
6188 * start and end positions of the line.
6191 wxRichTextLine::wxRichTextLine(wxRichTextParagraph
* parent
)
6197 void wxRichTextLine::Init(wxRichTextParagraph
* parent
)
6200 m_range
.SetRange(-1, -1);
6201 m_pos
= wxPoint(0, 0);
6202 m_size
= wxSize(0, 0);
6204 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6205 m_objectSizes
.Clear();
6210 void wxRichTextLine::Copy(const wxRichTextLine
& obj
)
6212 m_range
= obj
.m_range
;
6213 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6214 m_objectSizes
= obj
.m_objectSizes
;
6218 /// Get the absolute object position
6219 wxPoint
wxRichTextLine::GetAbsolutePosition() const
6221 return m_parent
->GetPosition() + m_pos
;
6224 /// Get the absolute range
6225 wxRichTextRange
wxRichTextLine::GetAbsoluteRange() const
6227 wxRichTextRange
range(m_range
.GetStart() + m_parent
->GetRange().GetStart(), 0);
6228 range
.SetEnd(range
.GetStart() + m_range
.GetLength()-1);
6233 * wxRichTextPlainText
6234 * This object represents a single piece of text.
6237 IMPLEMENT_DYNAMIC_CLASS(wxRichTextPlainText
, wxRichTextObject
)
6239 wxRichTextPlainText::wxRichTextPlainText(const wxString
& text
, wxRichTextObject
* parent
, wxRichTextAttr
* style
):
6240 wxRichTextObject(parent
)
6243 SetAttributes(*style
);
6248 #define USE_KERNING_FIX 1
6250 // If insufficient tabs are defined, this is the tab width used
6251 #define WIDTH_FOR_DEFAULT_TABS 50
6254 bool wxRichTextPlainText::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int WXUNUSED(style
))
6256 wxRichTextParagraph
* para
= wxDynamicCast(GetParent(), wxRichTextParagraph
);
6257 wxASSERT (para
!= NULL
);
6259 wxRichTextAttr
textAttr(para
? para
->GetCombinedAttributes(GetAttributes(), false /* no box attributes */) : GetAttributes());
6260 context
.ApplyVirtualAttributes(textAttr
, this);
6262 // Let's make the assumption for now that for content in a paragraph, including
6263 // text, we never have a discontinuous selection. So we only deal with a
6265 wxRichTextRange selectionRange
;
6266 if (selection
.IsValid())
6268 wxRichTextRangeArray selectionRanges
= selection
.GetSelectionForObject(this);
6269 if (selectionRanges
.GetCount() > 0)
6270 selectionRange
= selectionRanges
[0];
6272 selectionRange
= wxRICHTEXT_NO_SELECTION
;
6275 selectionRange
= wxRICHTEXT_NO_SELECTION
;
6277 int offset
= GetRange().GetStart();
6279 // Replace line break characters with spaces
6280 wxString str
= m_text
;
6281 wxString toRemove
= wxRichTextLineBreakChar
;
6282 str
.Replace(toRemove
, wxT(" "));
6283 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS
|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS
)))
6286 long len
= range
.GetLength();
6287 wxString stringChunk
= str
.Mid(range
.GetStart() - offset
, (size_t) len
);
6289 // Test for the optimized situations where all is selected, or none
6292 wxFont
textFont(GetBuffer()->GetFontTable().FindFont(textAttr
));
6293 wxCheckSetFont(dc
, textFont
);
6294 int charHeight
= dc
.GetCharHeight();
6297 if ( textFont
.IsOk() )
6299 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SMALL_CAPITALS
))
6301 textFont
.SetPointSize((int) (textFont
.GetPointSize()*0.75));
6302 wxCheckSetFont(dc
, textFont
);
6303 charHeight
= dc
.GetCharHeight();
6306 if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
) )
6308 if (textFont
.IsUsingSizeInPixels())
6310 double size
= static_cast<double>(textFont
.GetPixelSize().y
) / wxSCRIPT_MUL_FACTOR
;
6311 textFont
.SetPixelSize(wxSize(0, static_cast<int>(size
)));
6317 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6318 textFont
.SetPointSize(static_cast<int>(size
));
6322 wxCheckSetFont(dc
, textFont
);
6324 else if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) )
6326 if (textFont
.IsUsingSizeInPixels())
6328 double size
= static_cast<double>(textFont
.GetPixelSize().y
) / wxSCRIPT_MUL_FACTOR
;
6329 textFont
.SetPixelSize(wxSize(0, static_cast<int>(size
)));
6331 int sub_height
= static_cast<int>(static_cast<double>(charHeight
) / wxSCRIPT_MUL_FACTOR
);
6332 y
= rect
.y
+ (rect
.height
- sub_height
+ (descent
- m_descent
));
6336 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6337 textFont
.SetPointSize(static_cast<int>(size
));
6339 int sub_height
= static_cast<int>(static_cast<double>(charHeight
) / wxSCRIPT_MUL_FACTOR
);
6340 y
= rect
.y
+ (rect
.height
- sub_height
+ (descent
- m_descent
));
6342 wxCheckSetFont(dc
, textFont
);
6347 y
= rect
.y
+ (rect
.height
- charHeight
- (descent
- m_descent
));
6353 y
= rect
.y
+ (rect
.height
- charHeight
- (descent
- m_descent
));
6356 // TODO: new selection code
6358 // (a) All selected.
6359 if (selectionRange
.GetStart() <= range
.GetStart() && selectionRange
.GetEnd() >= range
.GetEnd())
6361 DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, true);
6363 // (b) None selected.
6364 else if (selectionRange
.GetEnd() < range
.GetStart() || selectionRange
.GetStart() > range
.GetEnd())
6366 // Draw all unselected
6367 DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, false);
6371 // (c) Part selected, part not
6372 // Let's draw unselected chunk, selected chunk, then unselected chunk.
6374 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6376 // 1. Initial unselected chunk, if any, up until start of selection.
6377 if (selectionRange
.GetStart() > range
.GetStart() && selectionRange
.GetStart() <= range
.GetEnd())
6379 int r1
= range
.GetStart();
6380 int s1
= selectionRange
.GetStart()-1;
6381 int fragmentLen
= s1
- r1
+ 1;
6382 if (fragmentLen
< 0)
6384 wxLogDebug(wxT("Mid(%d, %d"), (int)(r1
- offset
), (int)fragmentLen
);
6386 wxString stringFragment
= str
.Mid(r1
- offset
, fragmentLen
);
6388 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false);
6391 if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
)
6393 // Compensate for kerning difference
6394 wxString
stringFragment2(str
.Mid(r1
- offset
, fragmentLen
+1));
6395 wxString
stringFragment3(str
.Mid(r1
- offset
+ fragmentLen
, 1));
6397 wxCoord w1
, h1
, w2
, h2
, w3
, h3
;
6398 dc
.GetTextExtent(stringFragment
, & w1
, & h1
);
6399 dc
.GetTextExtent(stringFragment2
, & w2
, & h2
);
6400 dc
.GetTextExtent(stringFragment3
, & w3
, & h3
);
6402 int kerningDiff
= (w1
+ w3
) - w2
;
6403 x
= x
- kerningDiff
;
6408 // 2. Selected chunk, if any.
6409 if (selectionRange
.GetEnd() >= range
.GetStart())
6411 int s1
= wxMax(selectionRange
.GetStart(), range
.GetStart());
6412 int s2
= wxMin(selectionRange
.GetEnd(), range
.GetEnd());
6414 int fragmentLen
= s2
- s1
+ 1;
6415 if (fragmentLen
< 0)
6417 wxLogDebug(wxT("Mid(%d, %d"), (int)(s1
- offset
), (int)fragmentLen
);
6419 wxString stringFragment
= str
.Mid(s1
- offset
, fragmentLen
);
6421 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, true);
6424 if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
)
6426 // Compensate for kerning difference
6427 wxString
stringFragment2(str
.Mid(s1
- offset
, fragmentLen
+1));
6428 wxString
stringFragment3(str
.Mid(s1
- offset
+ fragmentLen
, 1));
6430 wxCoord w1
, h1
, w2
, h2
, w3
, h3
;
6431 dc
.GetTextExtent(stringFragment
, & w1
, & h1
);
6432 dc
.GetTextExtent(stringFragment2
, & w2
, & h2
);
6433 dc
.GetTextExtent(stringFragment3
, & w3
, & h3
);
6435 int kerningDiff
= (w1
+ w3
) - w2
;
6436 x
= x
- kerningDiff
;
6441 // 3. Remaining unselected chunk, if any
6442 if (selectionRange
.GetEnd() < range
.GetEnd())
6444 int s2
= wxMin(selectionRange
.GetEnd()+1, range
.GetEnd());
6445 int r2
= range
.GetEnd();
6447 int fragmentLen
= r2
- s2
+ 1;
6448 if (fragmentLen
< 0)
6450 wxLogDebug(wxT("Mid(%d, %d"), (int)(s2
- offset
), (int)fragmentLen
);
6452 wxString stringFragment
= str
.Mid(s2
- offset
, fragmentLen
);
6454 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false);
6461 bool wxRichTextPlainText::DrawTabbedString(wxDC
& dc
, const wxRichTextAttr
& attr
, const wxRect
& rect
,wxString
& str
, wxCoord
& x
, wxCoord
& y
, bool selected
)
6463 bool hasTabs
= (str
.Find(wxT('\t')) != wxNOT_FOUND
);
6465 wxArrayInt tabArray
;
6469 if (attr
.GetTabs().IsEmpty())
6470 tabArray
= wxRichTextParagraph::GetDefaultTabs();
6472 tabArray
= attr
.GetTabs();
6473 tabCount
= tabArray
.GetCount();
6475 for (int i
= 0; i
< tabCount
; ++i
)
6477 int pos
= tabArray
[i
];
6478 pos
= ConvertTenthsMMToPixels(dc
, pos
);
6485 int nextTabPos
= -1;
6491 wxColour
highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
));
6492 wxColour
highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
));
6494 wxCheckSetBrush(dc
, wxBrush(highlightColour
));
6495 wxCheckSetPen(dc
, wxPen(highlightColour
));
6496 dc
.SetTextForeground(highlightTextColour
);
6497 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6501 dc
.SetTextForeground(attr
.GetTextColour());
6503 if (attr
.HasFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
) && attr
.GetBackgroundColour().IsOk())
6505 dc
.SetBackgroundMode(wxBRUSHSTYLE_SOLID
);
6506 dc
.SetTextBackground(attr
.GetBackgroundColour());
6509 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6512 wxCoord x_orig
= GetParent()->GetPosition().x
;
6515 // the string has a tab
6516 // break up the string at the Tab
6517 wxString stringChunk
= str
.BeforeFirst(wxT('\t'));
6518 str
= str
.AfterFirst(wxT('\t'));
6519 dc
.GetTextExtent(stringChunk
, & w
, & h
);
6521 bool not_found
= true;
6522 for (int i
= 0; i
< tabCount
&& not_found
; ++i
)
6524 nextTabPos
= tabArray
.Item(i
) + x_orig
;
6526 // Find the next tab position.
6527 // Even if we're at the end of the tab array, we must still draw the chunk.
6529 if (nextTabPos
> tabPos
|| (i
== (tabCount
- 1)))
6531 if (nextTabPos
<= tabPos
)
6533 int defaultTabWidth
= ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
);
6534 nextTabPos
= tabPos
+ defaultTabWidth
;
6541 wxRect
selRect(x
, rect
.y
, w
, rect
.GetHeight());
6542 dc
.DrawRectangle(selRect
);
6544 dc
.DrawText(stringChunk
, x
, y
);
6546 if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
))
6548 wxPen oldPen
= dc
.GetPen();
6549 wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1));
6550 dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5));
6551 wxCheckSetPen(dc
, oldPen
);
6557 hasTabs
= (str
.Find(wxT('\t')) != wxNOT_FOUND
);
6562 dc
.GetTextExtent(str
, & w
, & h
);
6565 wxRect
selRect(x
, rect
.y
, w
, rect
.GetHeight());
6566 dc
.DrawRectangle(selRect
);
6568 dc
.DrawText(str
, x
, y
);
6570 if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
))
6572 wxPen oldPen
= dc
.GetPen();
6573 wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1));
6574 dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5));
6575 wxCheckSetPen(dc
, oldPen
);
6584 /// Lay the item out
6585 bool wxRichTextPlainText::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& WXUNUSED(rect
), const wxRect
& WXUNUSED(parentRect
), int WXUNUSED(style
))
6587 // Only lay out if we haven't already cached the size
6589 GetRangeSize(GetRange(), m_size
, m_descent
, dc
, context
, 0, wxPoint(0, 0));
6591 // Eventually we want to have a reasonable estimate of minimum size.
6592 m_minSize
= wxSize(0, 0);
6597 void wxRichTextPlainText::Copy(const wxRichTextPlainText
& obj
)
6599 wxRichTextObject::Copy(obj
);
6601 m_text
= obj
.m_text
;
6604 /// Get/set the object size for the given range. Returns false if the range
6605 /// is invalid for this object.
6606 bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int WXUNUSED(flags
), wxPoint position
, wxArrayInt
* partialExtents
) const
6608 if (!range
.IsWithin(GetRange()))
6611 wxRichTextParagraph
* para
= wxDynamicCast(GetParent(), wxRichTextParagraph
);
6612 wxASSERT (para
!= NULL
);
6614 int relativeX
= position
.x
- GetParent()->GetPosition().x
;
6616 wxRichTextAttr
textAttr(para
? para
->GetCombinedAttributes(GetAttributes()) : GetAttributes());
6617 context
.ApplyVirtualAttributes(textAttr
, (wxRichTextObject
*) this);
6619 // Always assume unformatted text, since at this level we have no knowledge
6620 // of line breaks - and we don't need it, since we'll calculate size within
6621 // formatted text by doing it in chunks according to the line ranges
6623 bool bScript(false);
6624 wxFont
font(GetBuffer()->GetFontTable().FindFont(textAttr
));
6627 if ( textAttr
.HasTextEffects() && ( (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
)
6628 || (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) ) )
6630 wxFont textFont
= font
;
6631 if (textFont
.IsUsingSizeInPixels())
6633 double size
= static_cast<double>(textFont
.GetPixelSize().y
) / wxSCRIPT_MUL_FACTOR
;
6634 textFont
.SetPixelSize(wxSize(0, static_cast<int>(size
)));
6638 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6639 textFont
.SetPointSize(static_cast<int>(size
));
6641 wxCheckSetFont(dc
, textFont
);
6644 else if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SMALL_CAPITALS
))
6646 wxFont textFont
= font
;
6647 textFont
.SetPointSize((int) (textFont
.GetPointSize()*0.75));
6648 wxCheckSetFont(dc
, textFont
);
6653 wxCheckSetFont(dc
, font
);
6657 bool haveDescent
= false;
6658 int startPos
= range
.GetStart() - GetRange().GetStart();
6659 long len
= range
.GetLength();
6661 wxString
str(m_text
);
6662 wxString toReplace
= wxRichTextLineBreakChar
;
6663 str
.Replace(toReplace
, wxT(" "));
6665 wxString stringChunk
= str
.Mid(startPos
, (size_t) len
);
6667 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS
|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS
)))
6668 stringChunk
.MakeUpper();
6672 if (stringChunk
.Find(wxT('\t')) != wxNOT_FOUND
)
6674 // the string has a tab
6675 wxArrayInt tabArray
;
6676 if (textAttr
.GetTabs().IsEmpty())
6677 tabArray
= wxRichTextParagraph::GetDefaultTabs();
6679 tabArray
= textAttr
.GetTabs();
6681 int tabCount
= tabArray
.GetCount();
6683 for (int i
= 0; i
< tabCount
; ++i
)
6685 int pos
= tabArray
[i
];
6686 pos
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, pos
);
6690 int nextTabPos
= -1;
6692 while (stringChunk
.Find(wxT('\t')) >= 0)
6694 int absoluteWidth
= 0;
6696 // the string has a tab
6697 // break up the string at the Tab
6698 wxString stringFragment
= stringChunk
.BeforeFirst(wxT('\t'));
6699 stringChunk
= stringChunk
.AfterFirst(wxT('\t'));
6704 if (partialExtents
->GetCount() > 0)
6705 oldWidth
= (*partialExtents
)[partialExtents
->GetCount()-1];
6709 // Add these partial extents
6711 dc
.GetPartialTextExtents(stringFragment
, p
);
6713 for (j
= 0; j
< p
.GetCount(); j
++)
6714 partialExtents
->Add(oldWidth
+ p
[j
]);
6716 if (partialExtents
->GetCount() > 0)
6717 absoluteWidth
= (*partialExtents
)[(*partialExtents
).GetCount()-1] + relativeX
;
6719 absoluteWidth
= relativeX
;
6723 dc
.GetTextExtent(stringFragment
, & w
, & h
);
6725 absoluteWidth
= width
+ relativeX
;
6729 bool notFound
= true;
6730 for (int i
= 0; i
< tabCount
&& notFound
; ++i
)
6732 nextTabPos
= tabArray
.Item(i
);
6734 // Find the next tab position.
6735 // Even if we're at the end of the tab array, we must still process the chunk.
6737 if (nextTabPos
> absoluteWidth
|| (i
== (tabCount
- 1)))
6739 if (nextTabPos
<= absoluteWidth
)
6741 int defaultTabWidth
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
);
6742 nextTabPos
= absoluteWidth
+ defaultTabWidth
;
6746 width
= nextTabPos
- relativeX
;
6749 partialExtents
->Add(width
);
6755 if (!stringChunk
.IsEmpty())
6760 if (partialExtents
->GetCount() > 0)
6761 oldWidth
= (*partialExtents
)[partialExtents
->GetCount()-1];
6765 // Add these partial extents
6767 dc
.GetPartialTextExtents(stringChunk
, p
);
6769 for (j
= 0; j
< p
.GetCount(); j
++)
6770 partialExtents
->Add(oldWidth
+ p
[j
]);
6774 dc
.GetTextExtent(stringChunk
, & w
, & h
, & descent
);
6782 int charHeight
= dc
.GetCharHeight();
6783 if ((*partialExtents
).GetCount() > 0)
6784 w
= (*partialExtents
)[partialExtents
->GetCount()-1];
6787 size
= wxSize(w
, charHeight
);
6791 size
= wxSize(width
, dc
.GetCharHeight());
6795 dc
.GetTextExtent(wxT("X"), & w
, & h
, & descent
);
6803 /// Do a split, returning an object containing the second part, and setting
6804 /// the first part in 'this'.
6805 wxRichTextObject
* wxRichTextPlainText::DoSplit(long pos
)
6807 long index
= pos
- GetRange().GetStart();
6809 if (index
< 0 || index
>= (int) m_text
.length())
6812 wxString firstPart
= m_text
.Mid(0, index
);
6813 wxString secondPart
= m_text
.Mid(index
);
6817 wxRichTextPlainText
* newObject
= new wxRichTextPlainText(secondPart
);
6818 newObject
->SetAttributes(GetAttributes());
6819 newObject
->SetProperties(GetProperties());
6821 newObject
->SetRange(wxRichTextRange(pos
, GetRange().GetEnd()));
6822 GetRange().SetEnd(pos
-1);
6828 void wxRichTextPlainText::CalculateRange(long start
, long& end
)
6830 end
= start
+ m_text
.length() - 1;
6831 m_range
.SetRange(start
, end
);
6835 bool wxRichTextPlainText::DeleteRange(const wxRichTextRange
& range
)
6837 wxRichTextRange r
= range
;
6839 r
.LimitTo(GetRange());
6841 if (r
.GetStart() == GetRange().GetStart() && r
.GetEnd() == GetRange().GetEnd())
6847 long startIndex
= r
.GetStart() - GetRange().GetStart();
6848 long len
= r
.GetLength();
6850 m_text
= m_text
.Mid(0, startIndex
) + m_text
.Mid(startIndex
+len
);
6854 /// Get text for the given range.
6855 wxString
wxRichTextPlainText::GetTextForRange(const wxRichTextRange
& range
) const
6857 wxRichTextRange r
= range
;
6859 r
.LimitTo(GetRange());
6861 long startIndex
= r
.GetStart() - GetRange().GetStart();
6862 long len
= r
.GetLength();
6864 return m_text
.Mid(startIndex
, len
);
6867 /// Returns true if this object can merge itself with the given one.
6868 bool wxRichTextPlainText::CanMerge(wxRichTextObject
* object
) const
6870 return object
->GetClassInfo() == wxCLASSINFO(wxRichTextPlainText
) &&
6871 (m_text
.empty() || (wxTextAttrEq(GetAttributes(), object
->GetAttributes()) && m_properties
== object
->GetProperties()));
6874 /// Returns true if this object merged itself with the given one.
6875 /// The calling code will then delete the given object.
6876 bool wxRichTextPlainText::Merge(wxRichTextObject
* object
)
6878 wxRichTextPlainText
* textObject
= wxDynamicCast(object
, wxRichTextPlainText
);
6879 wxASSERT( textObject
!= NULL
);
6883 m_text
+= textObject
->GetText();
6884 wxRichTextApplyStyle(m_attributes
, textObject
->GetAttributes());
6891 /// Dump to output stream for debugging
6892 void wxRichTextPlainText::Dump(wxTextOutputStream
& stream
)
6894 wxRichTextObject::Dump(stream
);
6895 stream
<< m_text
<< wxT("\n");
6898 /// Get the first position from pos that has a line break character.
6899 long wxRichTextPlainText::GetFirstLineBreakPosition(long pos
)
6902 int len
= m_text
.length();
6903 int startPos
= pos
- m_range
.GetStart();
6904 for (i
= startPos
; i
< len
; i
++)
6906 wxChar ch
= m_text
[i
];
6907 if (ch
== wxRichTextLineBreakChar
)
6909 return i
+ m_range
.GetStart();
6917 * This is a kind of box, used to represent the whole buffer
6920 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBuffer
, wxRichTextParagraphLayoutBox
)
6922 wxList
wxRichTextBuffer::sm_handlers
;
6923 wxList
wxRichTextBuffer::sm_drawingHandlers
;
6924 wxRichTextFieldTypeHashMap
wxRichTextBuffer::sm_fieldTypes
;
6925 wxRichTextRenderer
* wxRichTextBuffer::sm_renderer
= NULL
;
6926 int wxRichTextBuffer::sm_bulletRightMargin
= 20;
6927 float wxRichTextBuffer::sm_bulletProportion
= (float) 0.3;
6930 void wxRichTextBuffer::Init()
6932 m_commandProcessor
= new wxCommandProcessor
;
6933 m_styleSheet
= NULL
;
6935 m_batchedCommandDepth
= 0;
6936 m_batchedCommand
= NULL
;
6940 m_dimensionScale
= 1.0;
6946 wxRichTextBuffer::~wxRichTextBuffer()
6948 delete m_commandProcessor
;
6949 delete m_batchedCommand
;
6952 ClearEventHandlers();
6955 void wxRichTextBuffer::ResetAndClearCommands()
6959 GetCommandProcessor()->ClearCommands();
6962 Invalidate(wxRICHTEXT_ALL
);
6965 void wxRichTextBuffer::Copy(const wxRichTextBuffer
& obj
)
6967 wxRichTextParagraphLayoutBox::Copy(obj
);
6969 m_styleSheet
= obj
.m_styleSheet
;
6970 m_modified
= obj
.m_modified
;
6971 m_batchedCommandDepth
= 0;
6972 if (m_batchedCommand
)
6973 delete m_batchedCommand
;
6974 m_batchedCommand
= NULL
;
6975 m_suppressUndo
= obj
.m_suppressUndo
;
6976 m_invalidRange
= obj
.m_invalidRange
;
6977 m_dimensionScale
= obj
.m_dimensionScale
;
6978 m_fontScale
= obj
.m_fontScale
;
6981 /// Push style sheet to top of stack
6982 bool wxRichTextBuffer::PushStyleSheet(wxRichTextStyleSheet
* styleSheet
)
6985 styleSheet
->InsertSheet(m_styleSheet
);
6987 SetStyleSheet(styleSheet
);
6992 /// Pop style sheet from top of stack
6993 wxRichTextStyleSheet
* wxRichTextBuffer::PopStyleSheet()
6997 wxRichTextStyleSheet
* oldSheet
= m_styleSheet
;
6998 m_styleSheet
= oldSheet
->GetNextSheet();
7007 /// Submit command to insert paragraphs
7008 bool wxRichTextBuffer::InsertParagraphsWithUndo(long pos
, const wxRichTextParagraphLayoutBox
& paragraphs
, wxRichTextCtrl
* ctrl
, int flags
)
7010 return ctrl
->GetFocusObject()->InsertParagraphsWithUndo(this, pos
, paragraphs
, ctrl
, flags
);
7013 /// Submit command to insert paragraphs
7014 bool wxRichTextParagraphLayoutBox::InsertParagraphsWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxRichTextParagraphLayoutBox
& paragraphs
, wxRichTextCtrl
* ctrl
, int WXUNUSED(flags
))
7016 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7018 action
->GetNewParagraphs() = paragraphs
;
7020 action
->SetPosition(pos
);
7022 wxRichTextRange range
= wxRichTextRange(pos
, pos
+ paragraphs
.GetOwnRange().GetEnd() - 1);
7023 if (!paragraphs
.GetPartialParagraph())
7024 range
.SetEnd(range
.GetEnd()+1);
7026 // Set the range we'll need to delete in Undo
7027 action
->SetRange(range
);
7029 buffer
->SubmitAction(action
);
7034 /// Submit command to insert the given text
7035 bool wxRichTextBuffer::InsertTextWithUndo(long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
, int flags
)
7037 return ctrl
->GetFocusObject()->InsertTextWithUndo(this, pos
, text
, ctrl
, flags
);
7040 /// Submit command to insert the given text
7041 bool wxRichTextParagraphLayoutBox::InsertTextWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
, int flags
)
7043 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7045 wxRichTextAttr
* p
= NULL
;
7046 wxRichTextAttr paraAttr
;
7047 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7049 // Get appropriate paragraph style
7050 paraAttr
= GetStyleForNewParagraph(buffer
, pos
, false, false);
7051 if (!paraAttr
.IsDefault())
7055 action
->GetNewParagraphs().AddParagraphs(text
, p
);
7057 int length
= action
->GetNewParagraphs().GetOwnRange().GetLength();
7059 if (!text
.empty() && text
.Last() != wxT('\n'))
7061 // Don't count the newline when undoing
7063 action
->GetNewParagraphs().SetPartialParagraph(true);
7065 else if (!text
.empty() && text
.Last() == wxT('\n'))
7068 action
->SetPosition(pos
);
7070 // Set the range we'll need to delete in Undo
7071 action
->SetRange(wxRichTextRange(pos
, pos
+ length
- 1));
7073 buffer
->SubmitAction(action
);
7078 /// Submit command to insert the given text
7079 bool wxRichTextBuffer::InsertNewlineWithUndo(long pos
, wxRichTextCtrl
* ctrl
, int flags
)
7081 return ctrl
->GetFocusObject()->InsertNewlineWithUndo(this, pos
, ctrl
, flags
);
7084 /// Submit command to insert the given text
7085 bool wxRichTextParagraphLayoutBox::InsertNewlineWithUndo(wxRichTextBuffer
* buffer
, long pos
, wxRichTextCtrl
* ctrl
, int flags
)
7087 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7089 wxRichTextAttr
* p
= NULL
;
7090 wxRichTextAttr paraAttr
;
7091 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7093 paraAttr
= GetStyleForNewParagraph(buffer
, pos
, false, true /* look for next paragraph style */);
7094 if (!paraAttr
.IsDefault())
7098 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7099 // Don't include box attributes such as margins
7100 attr
.GetTextBoxAttr().Reset();
7102 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(wxEmptyString
, this, & attr
);
7103 action
->GetNewParagraphs().AppendChild(newPara
);
7104 action
->GetNewParagraphs().UpdateRanges();
7105 action
->GetNewParagraphs().SetPartialParagraph(false);
7106 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
, false);
7110 newPara
->SetAttributes(*p
);
7112 if (flags
& wxRICHTEXT_INSERT_INTERACTIVE
)
7114 if (para
&& para
->GetRange().GetEnd() == pos
)
7117 // Now see if we need to number the paragraph.
7118 if (newPara
->GetAttributes().HasBulletNumber())
7120 wxRichTextAttr numberingAttr
;
7121 if (FindNextParagraphNumber(para
, numberingAttr
))
7122 wxRichTextApplyStyle(newPara
->GetAttributes(), (const wxRichTextAttr
&) numberingAttr
);
7126 action
->SetPosition(pos
);
7128 // Use the default character style
7129 if (!buffer
->GetDefaultStyle().IsDefault() && newPara
->GetChildren().GetFirst())
7131 // Check whether the default style merely reflects the paragraph/basic style,
7132 // in which case don't apply it.
7133 wxRichTextAttr
defaultStyle(buffer
->GetDefaultStyle());
7134 defaultStyle
.GetTextBoxAttr().Reset();
7135 wxRichTextAttr toApply
;
7138 wxRichTextAttr combinedAttr
= para
->GetCombinedAttributes(true /* include box attributes */);
7139 wxRichTextAttr newAttr
;
7140 // This filters out attributes that are accounted for by the current
7141 // paragraph/basic style
7142 wxRichTextApplyStyle(toApply
, defaultStyle
, & combinedAttr
);
7145 toApply
= defaultStyle
;
7147 if (!toApply
.IsDefault())
7148 newPara
->GetChildren().GetFirst()->GetData()->SetAttributes(toApply
);
7151 // Set the range we'll need to delete in Undo
7152 action
->SetRange(wxRichTextRange(pos1
, pos1
));
7154 buffer
->SubmitAction(action
);
7159 /// Submit command to insert the given image
7160 bool wxRichTextBuffer::InsertImageWithUndo(long pos
, const wxRichTextImageBlock
& imageBlock
, wxRichTextCtrl
* ctrl
, int flags
,
7161 const wxRichTextAttr
& textAttr
)
7163 return ctrl
->GetFocusObject()->InsertImageWithUndo(this, pos
, imageBlock
, ctrl
, flags
, textAttr
);
7166 /// Submit command to insert the given image
7167 bool wxRichTextParagraphLayoutBox::InsertImageWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxRichTextImageBlock
& imageBlock
,
7168 wxRichTextCtrl
* ctrl
, int flags
,
7169 const wxRichTextAttr
& textAttr
)
7171 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7173 wxRichTextAttr
* p
= NULL
;
7174 wxRichTextAttr paraAttr
;
7175 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7177 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
7178 if (!paraAttr
.IsDefault())
7182 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7184 // Don't include box attributes such as margins
7185 attr
.GetTextBoxAttr().Reset();
7187 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7189 newPara
->SetAttributes(*p
);
7191 wxRichTextImage
* imageObject
= new wxRichTextImage(imageBlock
, newPara
);
7192 newPara
->AppendChild(imageObject
);
7193 imageObject
->SetAttributes(textAttr
);
7194 action
->GetNewParagraphs().AppendChild(newPara
);
7195 action
->GetNewParagraphs().UpdateRanges();
7197 action
->GetNewParagraphs().SetPartialParagraph(true);
7199 action
->SetPosition(pos
);
7201 // Set the range we'll need to delete in Undo
7202 action
->SetRange(wxRichTextRange(pos
, pos
));
7204 buffer
->SubmitAction(action
);
7209 // Insert an object with no change of it
7210 wxRichTextObject
* wxRichTextBuffer::InsertObjectWithUndo(long pos
, wxRichTextObject
*object
, wxRichTextCtrl
* ctrl
, int flags
)
7212 return ctrl
->GetFocusObject()->InsertObjectWithUndo(this, pos
, object
, ctrl
, flags
);
7215 // Insert an object with no change of it
7216 wxRichTextObject
* wxRichTextParagraphLayoutBox::InsertObjectWithUndo(wxRichTextBuffer
* buffer
, long pos
, wxRichTextObject
*object
, wxRichTextCtrl
* ctrl
, int flags
)
7218 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Object"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7220 wxRichTextAttr
* p
= NULL
;
7221 wxRichTextAttr paraAttr
;
7222 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7224 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
7225 if (!paraAttr
.IsDefault())
7229 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7231 // Don't include box attributes such as margins
7232 attr
.GetTextBoxAttr().Reset();
7234 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7236 newPara
->SetAttributes(*p
);
7238 newPara
->AppendChild(object
);
7239 action
->GetNewParagraphs().AppendChild(newPara
);
7240 action
->GetNewParagraphs().UpdateRanges();
7242 action
->GetNewParagraphs().SetPartialParagraph(true);
7244 action
->SetPosition(pos
);
7246 // Set the range we'll need to delete in Undo
7247 action
->SetRange(wxRichTextRange(pos
, pos
));
7249 buffer
->SubmitAction(action
);
7251 wxRichTextObject
* obj
= GetLeafObjectAtPosition(pos
);
7255 wxRichTextField
* wxRichTextParagraphLayoutBox::InsertFieldWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxString
& fieldType
,
7256 const wxRichTextProperties
& properties
,
7257 wxRichTextCtrl
* ctrl
, int flags
,
7258 const wxRichTextAttr
& textAttr
)
7260 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Field"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7262 wxRichTextAttr
* p
= NULL
;
7263 wxRichTextAttr paraAttr
;
7264 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7266 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
7267 if (!paraAttr
.IsDefault())
7271 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7273 // Don't include box attributes such as margins
7274 attr
.GetTextBoxAttr().Reset();
7276 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7278 newPara
->SetAttributes(*p
);
7280 wxRichTextField
* fieldObject
= new wxRichTextField();
7281 fieldObject
->wxRichTextObject::SetProperties(properties
);
7282 fieldObject
->SetFieldType(fieldType
);
7283 fieldObject
->SetAttributes(textAttr
);
7284 newPara
->AppendChild(fieldObject
);
7285 action
->GetNewParagraphs().AppendChild(newPara
);
7286 action
->GetNewParagraphs().UpdateRanges();
7287 action
->GetNewParagraphs().SetPartialParagraph(true);
7288 action
->SetPosition(pos
);
7290 // Set the range we'll need to delete in Undo
7291 action
->SetRange(wxRichTextRange(pos
, pos
));
7293 buffer
->SubmitAction(action
);
7295 wxRichTextField
* obj
= wxDynamicCast(GetLeafObjectAtPosition(pos
), wxRichTextField
);
7299 /// Get the style that is appropriate for a new paragraph at this position.
7300 /// If the previous paragraph has a paragraph style name, look up the next-paragraph
7302 wxRichTextAttr
wxRichTextParagraphLayoutBox::GetStyleForNewParagraph(wxRichTextBuffer
* buffer
, long pos
, bool caretPosition
, bool lookUpNewParaStyle
) const
7304 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
, caretPosition
);
7307 wxRichTextAttr attr
;
7308 bool foundAttributes
= false;
7310 // Look for a matching paragraph style
7311 if (lookUpNewParaStyle
&& !para
->GetAttributes().GetParagraphStyleName().IsEmpty() && buffer
->GetStyleSheet())
7313 wxRichTextParagraphStyleDefinition
* paraDef
= buffer
->GetStyleSheet()->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
7316 // If we're not at the end of the paragraph, then we apply THIS style, and not the designated next style.
7317 if (para
->GetRange().GetEnd() == pos
&& !paraDef
->GetNextStyle().IsEmpty())
7319 wxRichTextParagraphStyleDefinition
* nextParaDef
= buffer
->GetStyleSheet()->FindParagraphStyle(paraDef
->GetNextStyle());
7322 foundAttributes
= true;
7323 attr
= nextParaDef
->GetStyleMergedWithBase(buffer
->GetStyleSheet());
7327 // If we didn't find the 'next style', use this style instead.
7328 if (!foundAttributes
)
7330 foundAttributes
= true;
7331 attr
= paraDef
->GetStyleMergedWithBase(buffer
->GetStyleSheet());
7336 // Also apply list style if present
7337 if (lookUpNewParaStyle
&& !para
->GetAttributes().GetListStyleName().IsEmpty() && buffer
->GetStyleSheet())
7339 wxRichTextListStyleDefinition
* listDef
= buffer
->GetStyleSheet()->FindListStyle(para
->GetAttributes().GetListStyleName());
7342 int thisIndent
= para
->GetAttributes().GetLeftIndent();
7343 int thisLevel
= para
->GetAttributes().HasOutlineLevel() ? para
->GetAttributes().GetOutlineLevel() : listDef
->FindLevelForIndent(thisIndent
);
7345 // Apply the overall list style, and item style for this level
7346 wxRichTextAttr
listStyle(listDef
->GetCombinedStyleForLevel(thisLevel
, buffer
->GetStyleSheet()));
7347 wxRichTextApplyStyle(attr
, listStyle
);
7348 attr
.SetOutlineLevel(thisLevel
);
7349 if (para
->GetAttributes().HasBulletNumber())
7350 attr
.SetBulletNumber(para
->GetAttributes().GetBulletNumber());
7354 if (!foundAttributes
)
7356 attr
= para
->GetAttributes();
7357 int flags
= attr
.GetFlags();
7359 // Eliminate character styles
7360 flags
&= ( (~ wxTEXT_ATTR_FONT
) |
7361 (~ wxTEXT_ATTR_TEXT_COLOUR
) |
7362 (~ wxTEXT_ATTR_BACKGROUND_COLOUR
) );
7363 attr
.SetFlags(flags
);
7369 return wxRichTextAttr();
7372 /// Submit command to delete this range
7373 bool wxRichTextBuffer::DeleteRangeWithUndo(const wxRichTextRange
& range
, wxRichTextCtrl
* ctrl
)
7375 return ctrl
->GetFocusObject()->DeleteRangeWithUndo(range
, ctrl
, this);
7378 /// Submit command to delete this range
7379 bool wxRichTextParagraphLayoutBox::DeleteRangeWithUndo(const wxRichTextRange
& range
, wxRichTextCtrl
* ctrl
, wxRichTextBuffer
* buffer
)
7381 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Delete"), wxRICHTEXT_DELETE
, buffer
, this, ctrl
);
7383 action
->SetPosition(ctrl
->GetCaretPosition());
7385 // Set the range to delete
7386 action
->SetRange(range
);
7388 // Copy the fragment that we'll need to restore in Undo
7389 CopyFragment(range
, action
->GetOldParagraphs());
7391 // See if we're deleting a paragraph marker, in which case we need to
7392 // make a note not to copy the attributes from the 2nd paragraph to the 1st.
7393 if (range
.GetStart() == range
.GetEnd())
7395 wxRichTextParagraph
* para
= GetParagraphAtPosition(range
.GetStart());
7396 if (para
&& para
->GetRange().GetEnd() == range
.GetEnd())
7398 wxRichTextParagraph
* nextPara
= GetParagraphAtPosition(range
.GetStart()+1);
7399 if (nextPara
&& nextPara
!= para
)
7401 action
->GetOldParagraphs().GetChildren().GetFirst()->GetData()->SetAttributes(nextPara
->GetAttributes());
7402 action
->GetOldParagraphs().GetAttributes().SetFlags(action
->GetOldParagraphs().GetAttributes().GetFlags() | wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
);
7407 buffer
->SubmitAction(action
);
7412 /// Collapse undo/redo commands
7413 bool wxRichTextBuffer::BeginBatchUndo(const wxString
& cmdName
)
7415 if (m_batchedCommandDepth
== 0)
7417 wxASSERT(m_batchedCommand
== NULL
);
7418 if (m_batchedCommand
)
7420 GetCommandProcessor()->Store(m_batchedCommand
);
7422 m_batchedCommand
= new wxRichTextCommand(cmdName
);
7425 m_batchedCommandDepth
++;
7430 /// Collapse undo/redo commands
7431 bool wxRichTextBuffer::EndBatchUndo()
7433 m_batchedCommandDepth
--;
7435 wxASSERT(m_batchedCommandDepth
>= 0);
7436 wxASSERT(m_batchedCommand
!= NULL
);
7438 if (m_batchedCommandDepth
== 0)
7440 GetCommandProcessor()->Store(m_batchedCommand
);
7441 m_batchedCommand
= NULL
;
7447 /// Submit immediately, or delay according to whether collapsing is on
7448 bool wxRichTextBuffer::SubmitAction(wxRichTextAction
* action
)
7450 if (action
&& !action
->GetNewParagraphs().IsEmpty())
7451 PrepareContent(action
->GetNewParagraphs());
7453 if (BatchingUndo() && m_batchedCommand
&& !SuppressingUndo())
7455 wxRichTextCommand
* cmd
= new wxRichTextCommand(action
->GetName());
7456 cmd
->AddAction(action
);
7458 cmd
->GetActions().Clear();
7461 m_batchedCommand
->AddAction(action
);
7465 wxRichTextCommand
* cmd
= new wxRichTextCommand(action
->GetName());
7466 cmd
->AddAction(action
);
7468 // Only store it if we're not suppressing undo.
7469 return GetCommandProcessor()->Submit(cmd
, !SuppressingUndo());
7475 /// Begin suppressing undo/redo commands.
7476 bool wxRichTextBuffer::BeginSuppressUndo()
7483 /// End suppressing undo/redo commands.
7484 bool wxRichTextBuffer::EndSuppressUndo()
7491 /// Begin using a style
7492 bool wxRichTextBuffer::BeginStyle(const wxRichTextAttr
& style
)
7494 wxRichTextAttr
newStyle(GetDefaultStyle());
7495 newStyle
.GetTextBoxAttr().Reset();
7497 // Save the old default style
7498 m_attributeStack
.Append((wxObject
*) new wxRichTextAttr(newStyle
));
7500 wxRichTextApplyStyle(newStyle
, style
);
7501 newStyle
.SetFlags(style
.GetFlags()|newStyle
.GetFlags());
7503 SetDefaultStyle(newStyle
);
7509 bool wxRichTextBuffer::EndStyle()
7511 if (!m_attributeStack
.GetFirst())
7513 wxLogDebug(_("Too many EndStyle calls!"));
7517 wxList::compatibility_iterator node
= m_attributeStack
.GetLast();
7518 wxRichTextAttr
* attr
= (wxRichTextAttr
*)node
->GetData();
7519 m_attributeStack
.Erase(node
);
7521 SetDefaultStyle(*attr
);
7528 bool wxRichTextBuffer::EndAllStyles()
7530 while (m_attributeStack
.GetCount() != 0)
7535 /// Clear the style stack
7536 void wxRichTextBuffer::ClearStyleStack()
7538 for (wxList::compatibility_iterator node
= m_attributeStack
.GetFirst(); node
; node
= node
->GetNext())
7539 delete (wxRichTextAttr
*) node
->GetData();
7540 m_attributeStack
.Clear();
7543 /// Begin using bold
7544 bool wxRichTextBuffer::BeginBold()
7546 wxRichTextAttr attr
;
7547 attr
.SetFontWeight(wxFONTWEIGHT_BOLD
);
7549 return BeginStyle(attr
);
7552 /// Begin using italic
7553 bool wxRichTextBuffer::BeginItalic()
7555 wxRichTextAttr attr
;
7556 attr
.SetFontStyle(wxFONTSTYLE_ITALIC
);
7558 return BeginStyle(attr
);
7561 /// Begin using underline
7562 bool wxRichTextBuffer::BeginUnderline()
7564 wxRichTextAttr attr
;
7565 attr
.SetFontUnderlined(true);
7567 return BeginStyle(attr
);
7570 /// Begin using point size
7571 bool wxRichTextBuffer::BeginFontSize(int pointSize
)
7573 wxRichTextAttr attr
;
7574 attr
.SetFontSize(pointSize
);
7576 return BeginStyle(attr
);
7579 /// Begin using this font
7580 bool wxRichTextBuffer::BeginFont(const wxFont
& font
)
7582 wxRichTextAttr attr
;
7585 return BeginStyle(attr
);
7588 /// Begin using this colour
7589 bool wxRichTextBuffer::BeginTextColour(const wxColour
& colour
)
7591 wxRichTextAttr attr
;
7592 attr
.SetFlags(wxTEXT_ATTR_TEXT_COLOUR
);
7593 attr
.SetTextColour(colour
);
7595 return BeginStyle(attr
);
7598 /// Begin using alignment
7599 bool wxRichTextBuffer::BeginAlignment(wxTextAttrAlignment alignment
)
7601 wxRichTextAttr attr
;
7602 attr
.SetFlags(wxTEXT_ATTR_ALIGNMENT
);
7603 attr
.SetAlignment(alignment
);
7605 return BeginStyle(attr
);
7608 /// Begin left indent
7609 bool wxRichTextBuffer::BeginLeftIndent(int leftIndent
, int leftSubIndent
)
7611 wxRichTextAttr attr
;
7612 attr
.SetFlags(wxTEXT_ATTR_LEFT_INDENT
);
7613 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7615 return BeginStyle(attr
);
7618 /// Begin right indent
7619 bool wxRichTextBuffer::BeginRightIndent(int rightIndent
)
7621 wxRichTextAttr attr
;
7622 attr
.SetFlags(wxTEXT_ATTR_RIGHT_INDENT
);
7623 attr
.SetRightIndent(rightIndent
);
7625 return BeginStyle(attr
);
7628 /// Begin paragraph spacing
7629 bool wxRichTextBuffer::BeginParagraphSpacing(int before
, int after
)
7633 flags
|= wxTEXT_ATTR_PARA_SPACING_BEFORE
;
7635 flags
|= wxTEXT_ATTR_PARA_SPACING_AFTER
;
7637 wxRichTextAttr attr
;
7638 attr
.SetFlags(flags
);
7639 attr
.SetParagraphSpacingBefore(before
);
7640 attr
.SetParagraphSpacingAfter(after
);
7642 return BeginStyle(attr
);
7645 /// Begin line spacing
7646 bool wxRichTextBuffer::BeginLineSpacing(int lineSpacing
)
7648 wxRichTextAttr attr
;
7649 attr
.SetFlags(wxTEXT_ATTR_LINE_SPACING
);
7650 attr
.SetLineSpacing(lineSpacing
);
7652 return BeginStyle(attr
);
7655 /// Begin numbered bullet
7656 bool wxRichTextBuffer::BeginNumberedBullet(int bulletNumber
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7658 wxRichTextAttr attr
;
7659 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7660 attr
.SetBulletStyle(bulletStyle
);
7661 attr
.SetBulletNumber(bulletNumber
);
7662 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7664 return BeginStyle(attr
);
7667 /// Begin symbol bullet
7668 bool wxRichTextBuffer::BeginSymbolBullet(const wxString
& symbol
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7670 wxRichTextAttr attr
;
7671 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7672 attr
.SetBulletStyle(bulletStyle
);
7673 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7674 attr
.SetBulletText(symbol
);
7676 return BeginStyle(attr
);
7679 /// Begin standard bullet
7680 bool wxRichTextBuffer::BeginStandardBullet(const wxString
& bulletName
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7682 wxRichTextAttr attr
;
7683 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7684 attr
.SetBulletStyle(bulletStyle
);
7685 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7686 attr
.SetBulletName(bulletName
);
7688 return BeginStyle(attr
);
7691 /// Begin named character style
7692 bool wxRichTextBuffer::BeginCharacterStyle(const wxString
& characterStyle
)
7694 if (GetStyleSheet())
7696 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->FindCharacterStyle(characterStyle
);
7699 wxRichTextAttr attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7700 return BeginStyle(attr
);
7706 /// Begin named paragraph style
7707 bool wxRichTextBuffer::BeginParagraphStyle(const wxString
& paragraphStyle
)
7709 if (GetStyleSheet())
7711 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(paragraphStyle
);
7714 wxRichTextAttr attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7715 return BeginStyle(attr
);
7721 /// Begin named list style
7722 bool wxRichTextBuffer::BeginListStyle(const wxString
& listStyle
, int level
, int number
)
7724 if (GetStyleSheet())
7726 wxRichTextListStyleDefinition
* def
= GetStyleSheet()->FindListStyle(listStyle
);
7729 wxRichTextAttr
attr(def
->GetCombinedStyleForLevel(level
));
7731 attr
.SetBulletNumber(number
);
7733 return BeginStyle(attr
);
7740 bool wxRichTextBuffer::BeginURL(const wxString
& url
, const wxString
& characterStyle
)
7742 wxRichTextAttr attr
;
7744 if (!characterStyle
.IsEmpty() && GetStyleSheet())
7746 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->FindCharacterStyle(characterStyle
);
7749 attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7754 return BeginStyle(attr
);
7757 /// Adds a handler to the end
7758 void wxRichTextBuffer::AddHandler(wxRichTextFileHandler
*handler
)
7760 sm_handlers
.Append(handler
);
7763 /// Inserts a handler at the front
7764 void wxRichTextBuffer::InsertHandler(wxRichTextFileHandler
*handler
)
7766 sm_handlers
.Insert( handler
);
7769 /// Removes a handler
7770 bool wxRichTextBuffer::RemoveHandler(const wxString
& name
)
7772 wxRichTextFileHandler
*handler
= FindHandler(name
);
7775 sm_handlers
.DeleteObject(handler
);
7783 /// Finds a handler by filename or, if supplied, type
7784 wxRichTextFileHandler
*wxRichTextBuffer::FindHandlerFilenameOrType(const wxString
& filename
,
7785 wxRichTextFileType imageType
)
7787 if (imageType
!= wxRICHTEXT_TYPE_ANY
)
7788 return FindHandler(imageType
);
7789 else if (!filename
.IsEmpty())
7791 wxString path
, file
, ext
;
7792 wxFileName::SplitPath(filename
, & path
, & file
, & ext
);
7793 return FindHandler(ext
, imageType
);
7800 /// Finds a handler by name
7801 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(const wxString
& name
)
7803 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7806 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7807 if (handler
->GetName().Lower() == name
.Lower()) return handler
;
7809 node
= node
->GetNext();
7814 /// Finds a handler by extension and type
7815 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(const wxString
& extension
, wxRichTextFileType type
)
7817 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7820 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7821 if ( handler
->GetExtension().Lower() == extension
.Lower() &&
7822 (type
== wxRICHTEXT_TYPE_ANY
|| handler
->GetType() == type
) )
7824 node
= node
->GetNext();
7829 /// Finds a handler by type
7830 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(wxRichTextFileType type
)
7832 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7835 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7836 if (handler
->GetType() == type
) return handler
;
7837 node
= node
->GetNext();
7842 void wxRichTextBuffer::InitStandardHandlers()
7844 if (!FindHandler(wxRICHTEXT_TYPE_TEXT
))
7845 AddHandler(new wxRichTextPlainTextHandler
);
7848 void wxRichTextBuffer::CleanUpHandlers()
7850 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7853 wxRichTextFileHandler
* handler
= (wxRichTextFileHandler
*)node
->GetData();
7854 wxList::compatibility_iterator next
= node
->GetNext();
7859 sm_handlers
.Clear();
7862 wxString
wxRichTextBuffer::GetExtWildcard(bool combine
, bool save
, wxArrayInt
* types
)
7869 wxList::compatibility_iterator node
= GetHandlers().GetFirst();
7873 wxRichTextFileHandler
* handler
= (wxRichTextFileHandler
*) node
->GetData();
7874 if (handler
->IsVisible() && ((save
&& handler
->CanSave()) || (!save
&& handler
->CanLoad())))
7879 wildcard
+= wxT(";");
7880 wildcard
+= wxT("*.") + handler
->GetExtension();
7885 wildcard
+= wxT("|");
7886 wildcard
+= handler
->GetName();
7887 wildcard
+= wxT(" ");
7888 wildcard
+= _("files");
7889 wildcard
+= wxT(" (*.");
7890 wildcard
+= handler
->GetExtension();
7891 wildcard
+= wxT(")|*.");
7892 wildcard
+= handler
->GetExtension();
7894 types
->Add(handler
->GetType());
7899 node
= node
->GetNext();
7903 wildcard
= wxT("(") + wildcard
+ wxT(")|") + wildcard
;
7908 bool wxRichTextBuffer::LoadFile(const wxString
& filename
, wxRichTextFileType type
)
7910 wxRichTextFileHandler
* handler
= FindHandlerFilenameOrType(filename
, type
);
7913 SetDefaultStyle(wxRichTextAttr());
7914 handler
->SetFlags(GetHandlerFlags());
7915 bool success
= handler
->LoadFile(this, filename
);
7916 Invalidate(wxRICHTEXT_ALL
);
7924 bool wxRichTextBuffer::SaveFile(const wxString
& filename
, wxRichTextFileType type
)
7926 wxRichTextFileHandler
* handler
= FindHandlerFilenameOrType(filename
, type
);
7929 handler
->SetFlags(GetHandlerFlags());
7930 return handler
->SaveFile(this, filename
);
7936 /// Load from a stream
7937 bool wxRichTextBuffer::LoadFile(wxInputStream
& stream
, wxRichTextFileType type
)
7939 wxRichTextFileHandler
* handler
= FindHandler(type
);
7942 SetDefaultStyle(wxRichTextAttr());
7943 handler
->SetFlags(GetHandlerFlags());
7944 bool success
= handler
->LoadFile(this, stream
);
7945 Invalidate(wxRICHTEXT_ALL
);
7952 /// Save to a stream
7953 bool wxRichTextBuffer::SaveFile(wxOutputStream
& stream
, wxRichTextFileType type
)
7955 wxRichTextFileHandler
* handler
= FindHandler(type
);
7958 handler
->SetFlags(GetHandlerFlags());
7959 return handler
->SaveFile(this, stream
);
7965 /// Copy the range to the clipboard
7966 bool wxRichTextBuffer::CopyToClipboard(const wxRichTextRange
& range
)
7968 bool success
= false;
7969 wxRichTextParagraphLayoutBox
* container
= this;
7970 if (GetRichTextCtrl())
7971 container
= GetRichTextCtrl()->GetFocusObject();
7973 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
7975 if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open())
7977 wxTheClipboard
->Clear();
7979 // Add composite object
7981 wxDataObjectComposite
* compositeObject
= new wxDataObjectComposite();
7984 wxString text
= container
->GetTextForRange(range
);
7987 text
= wxTextFile::Translate(text
, wxTextFileType_Dos
);
7990 compositeObject
->Add(new wxTextDataObject(text
), false /* not preferred */);
7993 // Add rich text buffer data object. This needs the XML handler to be present.
7995 if (FindHandler(wxRICHTEXT_TYPE_XML
))
7997 wxRichTextBuffer
* richTextBuf
= new wxRichTextBuffer
;
7998 container
->CopyFragment(range
, *richTextBuf
);
8000 compositeObject
->Add(new wxRichTextBufferDataObject(richTextBuf
), true /* preferred */);
8003 if (wxTheClipboard
->SetData(compositeObject
))
8006 wxTheClipboard
->Close();
8015 /// Paste the clipboard content to the buffer
8016 bool wxRichTextBuffer::PasteFromClipboard(long position
)
8018 bool success
= false;
8019 wxRichTextParagraphLayoutBox
* container
= this;
8020 if (GetRichTextCtrl())
8021 container
= GetRichTextCtrl()->GetFocusObject();
8023 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
8024 if (CanPasteFromClipboard())
8026 if (wxTheClipboard
->Open())
8028 if (wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())))
8030 wxRichTextBufferDataObject data
;
8031 wxTheClipboard
->GetData(data
);
8032 wxRichTextBuffer
* richTextBuffer
= data
.GetRichTextBuffer();
8035 container
->InsertParagraphsWithUndo(this, position
+1, *richTextBuffer
, GetRichTextCtrl(), 0);
8036 if (GetRichTextCtrl())
8037 GetRichTextCtrl()->ShowPosition(position
+ richTextBuffer
->GetOwnRange().GetEnd());
8038 delete richTextBuffer
;
8041 else if (wxTheClipboard
->IsSupported(wxDF_TEXT
)
8043 || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
)
8047 wxTextDataObject data
;
8048 wxTheClipboard
->GetData(data
);
8049 wxString
text(data
.GetText());
8052 text2
.Alloc(text
.Length()+1);
8054 for (i
= 0; i
< text
.Length(); i
++)
8056 wxChar ch
= text
[i
];
8057 if (ch
!= wxT('\r'))
8061 wxString text2
= text
;
8063 container
->InsertTextWithUndo(this, position
+1, text2
, GetRichTextCtrl(), wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
);
8065 if (GetRichTextCtrl())
8066 GetRichTextCtrl()->ShowPosition(position
+ text2
.Length());
8070 else if (wxTheClipboard
->IsSupported(wxDF_BITMAP
))
8072 wxBitmapDataObject data
;
8073 wxTheClipboard
->GetData(data
);
8074 wxBitmap
bitmap(data
.GetBitmap());
8075 wxImage
image(bitmap
.ConvertToImage());
8077 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, this, container
, GetRichTextCtrl(), false);
8079 action
->GetNewParagraphs().AddImage(image
);
8081 if (action
->GetNewParagraphs().GetChildCount() == 1)
8082 action
->GetNewParagraphs().SetPartialParagraph(true);
8084 action
->SetPosition(position
+1);
8086 // Set the range we'll need to delete in Undo
8087 action
->SetRange(wxRichTextRange(position
+1, position
+1));
8089 SubmitAction(action
);
8093 wxTheClipboard
->Close();
8097 wxUnusedVar(position
);
8102 /// Can we paste from the clipboard?
8103 bool wxRichTextBuffer::CanPasteFromClipboard() const
8105 bool canPaste
= false;
8106 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
8107 if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open())
8109 if (wxTheClipboard
->IsSupported(wxDF_TEXT
)
8111 || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
)
8113 || wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())) ||
8114 wxTheClipboard
->IsSupported(wxDF_BITMAP
))
8118 wxTheClipboard
->Close();
8124 /// Dumps contents of buffer for debugging purposes
8125 void wxRichTextBuffer::Dump()
8129 wxStringOutputStream
stream(& text
);
8130 wxTextOutputStream
textStream(stream
);
8137 /// Add an event handler
8138 bool wxRichTextBuffer::AddEventHandler(wxEvtHandler
* handler
)
8140 m_eventHandlers
.Append(handler
);
8144 /// Remove an event handler
8145 bool wxRichTextBuffer::RemoveEventHandler(wxEvtHandler
* handler
, bool deleteHandler
)
8147 wxList::compatibility_iterator node
= m_eventHandlers
.Find(handler
);
8150 m_eventHandlers
.Erase(node
);
8160 /// Clear event handlers
8161 void wxRichTextBuffer::ClearEventHandlers()
8163 m_eventHandlers
.Clear();
8166 /// Send event to event handlers. If sendToAll is true, will send to all event handlers,
8167 /// otherwise will stop at the first successful one.
8168 bool wxRichTextBuffer::SendEvent(wxEvent
& event
, bool sendToAll
)
8170 bool success
= false;
8171 for (wxList::compatibility_iterator node
= m_eventHandlers
.GetFirst(); node
; node
= node
->GetNext())
8173 wxEvtHandler
* handler
= (wxEvtHandler
*) node
->GetData();
8174 if (handler
->ProcessEvent(event
))
8184 /// Set style sheet and notify of the change
8185 bool wxRichTextBuffer::SetStyleSheetAndNotify(wxRichTextStyleSheet
* sheet
)
8187 wxRichTextStyleSheet
* oldSheet
= GetStyleSheet();
8189 wxWindowID winid
= wxID_ANY
;
8190 if (GetRichTextCtrl())
8191 winid
= GetRichTextCtrl()->GetId();
8193 wxRichTextEvent
event(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACING
, winid
);
8194 event
.SetEventObject(GetRichTextCtrl());
8195 event
.SetContainer(GetRichTextCtrl()->GetFocusObject());
8196 event
.SetOldStyleSheet(oldSheet
);
8197 event
.SetNewStyleSheet(sheet
);
8200 if (SendEvent(event
) && !event
.IsAllowed())
8202 if (sheet
!= oldSheet
)
8208 if (oldSheet
&& oldSheet
!= sheet
)
8211 SetStyleSheet(sheet
);
8213 event
.SetEventType(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACED
);
8214 event
.SetOldStyleSheet(NULL
);
8217 return SendEvent(event
);
8220 /// Set renderer, deleting old one
8221 void wxRichTextBuffer::SetRenderer(wxRichTextRenderer
* renderer
)
8225 sm_renderer
= renderer
;
8228 /// Hit-testing: returns a flag indicating hit test details, plus
8229 /// information about position
8230 int wxRichTextBuffer::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
8232 int ret
= wxRichTextParagraphLayoutBox::HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
8233 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
8239 textPosition
= m_ownRange
.GetEnd()-1;
8242 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
;
8246 void wxRichTextBuffer::SetFontScale(double fontScale
)
8248 m_fontScale
= fontScale
;
8249 m_fontTable
.SetFontScale(fontScale
);
8252 void wxRichTextBuffer::SetDimensionScale(double dimScale
)
8254 m_dimensionScale
= dimScale
;
8257 bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxRichTextAttr
& bulletAttr
, const wxRect
& rect
)
8259 if (bulletAttr
.GetTextColour().IsOk())
8261 wxCheckSetPen(dc
, wxPen(bulletAttr
.GetTextColour()));
8262 wxCheckSetBrush(dc
, wxBrush(bulletAttr
.GetTextColour()));
8266 wxCheckSetPen(dc
, *wxBLACK_PEN
);
8267 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
8271 if (bulletAttr
.HasFont())
8273 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(bulletAttr
);
8276 font
= (*wxNORMAL_FONT
);
8278 wxCheckSetFont(dc
, font
);
8280 int charHeight
= dc
.GetCharHeight();
8282 int bulletWidth
= (int) (((float) charHeight
) * wxRichTextBuffer::GetBulletProportion());
8283 int bulletHeight
= bulletWidth
;
8287 // Calculate the top position of the character (as opposed to the whole line height)
8288 int y
= rect
.y
+ (rect
.height
- charHeight
);
8290 // Calculate where the bullet should be positioned
8291 y
= y
+ (charHeight
+1)/2 - (bulletHeight
+1)/2;
8293 // The margin between a bullet and text.
8294 int margin
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer::GetBulletRightMargin());
8296 if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
)
8297 x
= rect
.x
+ rect
.width
- bulletWidth
- margin
;
8298 else if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
)
8299 x
= x
+ (rect
.width
)/2 - bulletWidth
/2;
8301 if (bulletAttr
.GetBulletName() == wxT("standard/square"))
8303 dc
.DrawRectangle(x
, y
, bulletWidth
, bulletHeight
);
8305 else if (bulletAttr
.GetBulletName() == wxT("standard/diamond"))
8308 pts
[0].x
= x
; pts
[0].y
= y
+ bulletHeight
/2;
8309 pts
[1].x
= x
+ bulletWidth
/2; pts
[1].y
= y
;
8310 pts
[2].x
= x
+ bulletWidth
; pts
[2].y
= y
+ bulletHeight
/2;
8311 pts
[3].x
= x
+ bulletWidth
/2; pts
[3].y
= y
+ bulletHeight
;
8313 dc
.DrawPolygon(4, pts
);
8315 else if (bulletAttr
.GetBulletName() == wxT("standard/triangle"))
8318 pts
[0].x
= x
; pts
[0].y
= y
;
8319 pts
[1].x
= x
+ bulletWidth
; pts
[1].y
= y
+ bulletHeight
/2;
8320 pts
[2].x
= x
; pts
[2].y
= y
+ bulletHeight
;
8322 dc
.DrawPolygon(3, pts
);
8324 else if (bulletAttr
.GetBulletName() == wxT("standard/circle-outline"))
8326 wxCheckSetBrush(dc
, *wxTRANSPARENT_BRUSH
);
8327 dc
.DrawEllipse(x
, y
, bulletWidth
, bulletHeight
);
8329 else // "standard/circle", and catch-all
8331 dc
.DrawEllipse(x
, y
, bulletWidth
, bulletHeight
);
8337 bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxRichTextAttr
& attr
, const wxRect
& rect
, const wxString
& text
)
8342 if ((attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
) && !attr
.GetBulletFont().IsEmpty() && attr
.HasFont())
8344 wxRichTextAttr fontAttr
;
8345 if (attr
.HasFontPixelSize())
8346 fontAttr
.SetFontPixelSize(attr
.GetFontSize());
8348 fontAttr
.SetFontPointSize(attr
.GetFontSize());
8349 fontAttr
.SetFontStyle(attr
.GetFontStyle());
8350 fontAttr
.SetFontWeight(attr
.GetFontWeight());
8351 fontAttr
.SetFontUnderlined(attr
.GetFontUnderlined());
8352 fontAttr
.SetFontFaceName(attr
.GetBulletFont());
8353 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(fontAttr
);
8355 else if (attr
.HasFont())
8356 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(attr
);
8358 font
= (*wxNORMAL_FONT
);
8360 wxCheckSetFont(dc
, font
);
8362 if (attr
.GetTextColour().IsOk())
8363 dc
.SetTextForeground(attr
.GetTextColour());
8365 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
8367 int charHeight
= dc
.GetCharHeight();
8369 dc
.GetTextExtent(text
, & tw
, & th
);
8373 // Calculate the top position of the character (as opposed to the whole line height)
8374 int y
= rect
.y
+ (rect
.height
- charHeight
);
8376 // The margin between a bullet and text.
8377 int margin
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer::GetBulletRightMargin());
8379 if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
)
8380 x
= (rect
.x
+ rect
.width
) - tw
- margin
;
8381 else if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
)
8382 x
= x
+ (rect
.width
)/2 - tw
/2;
8384 dc
.DrawText(text
, x
, y
);
8392 bool wxRichTextStdRenderer::DrawBitmapBullet(wxRichTextParagraph
* WXUNUSED(paragraph
), wxDC
& WXUNUSED(dc
), const wxRichTextAttr
& WXUNUSED(attr
), const wxRect
& WXUNUSED(rect
))
8394 // Currently unimplemented. The intention is to store bitmaps by name in a media store associated
8395 // with the buffer. The store will allow retrieval from memory, disk or other means.
8399 /// Enumerate the standard bullet names currently supported
8400 bool wxRichTextStdRenderer::EnumerateStandardBulletNames(wxArrayString
& bulletNames
)
8402 bulletNames
.Add(wxTRANSLATE("standard/circle"));
8403 bulletNames
.Add(wxTRANSLATE("standard/circle-outline"));
8404 bulletNames
.Add(wxTRANSLATE("standard/square"));
8405 bulletNames
.Add(wxTRANSLATE("standard/diamond"));
8406 bulletNames
.Add(wxTRANSLATE("standard/triangle"));
8415 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBox
, wxRichTextParagraphLayoutBox
)
8417 wxRichTextBox::wxRichTextBox(wxRichTextObject
* parent
):
8418 wxRichTextParagraphLayoutBox(parent
)
8423 bool wxRichTextBox::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8428 // TODO: if the active object in the control, draw an indication.
8429 // We need to add the concept of active object, and not just focus object,
8430 // so we can apply commands (properties, delete, ...) to objects such as text boxes and images.
8431 // Ultimately we would like to be able to interactively resize an active object
8432 // using drag handles.
8433 return wxRichTextParagraphLayoutBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8437 void wxRichTextBox::Copy(const wxRichTextBox
& obj
)
8439 wxRichTextParagraphLayoutBox::Copy(obj
);
8442 // Edit properties via a GUI
8443 bool wxRichTextBox::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8445 wxRichTextObjectPropertiesDialog
boxDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Box Properties"));
8446 boxDlg
.SetAttributes(GetAttributes());
8448 if (boxDlg
.ShowModal() == wxID_OK
)
8450 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
8451 // indeterminate in the object.
8452 boxDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
8463 IMPLEMENT_DYNAMIC_CLASS(wxRichTextField
, wxRichTextParagraphLayoutBox
)
8465 wxRichTextField::wxRichTextField(const wxString
& fieldType
, wxRichTextObject
* parent
):
8466 wxRichTextParagraphLayoutBox(parent
)
8468 SetFieldType(fieldType
);
8472 bool wxRichTextField::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8477 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8478 if (fieldType
&& fieldType
->Draw(this, dc
, context
, range
, selection
, rect
, descent
, style
))
8481 // Fallback; but don't draw guidelines.
8482 style
&= ~wxRICHTEXT_DRAW_GUIDELINES
;
8483 return wxRichTextParagraphLayoutBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8486 bool wxRichTextField::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
)
8488 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8489 if (fieldType
&& fieldType
->Layout(this, dc
, context
, rect
, parentRect
, style
))
8493 return wxRichTextParagraphLayoutBox::Layout(dc
, context
, rect
, parentRect
, style
);
8496 bool wxRichTextField::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
8498 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8500 return fieldType
->GetRangeSize((wxRichTextField
*) this, range
, size
, descent
, dc
, context
, flags
, position
, partialExtents
);
8502 return wxRichTextParagraphLayoutBox::GetRangeSize(range
, size
, descent
, dc
, context
, flags
, position
, partialExtents
);
8506 void wxRichTextField::CalculateRange(long start
, long& end
)
8509 wxRichTextParagraphLayoutBox::CalculateRange(start
, end
);
8511 wxRichTextObject::CalculateRange(start
, end
);
8515 void wxRichTextField::Copy(const wxRichTextField
& obj
)
8517 wxRichTextParagraphLayoutBox::Copy(obj
);
8519 UpdateField(GetBuffer());
8522 // Edit properties via a GUI
8523 bool wxRichTextField::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8525 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8527 return fieldType
->EditProperties(this, parent
, buffer
);
8532 bool wxRichTextField::CanEditProperties() const
8534 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8536 return fieldType
->CanEditProperties((wxRichTextField
*) this);
8541 wxString
wxRichTextField::GetPropertiesMenuLabel() const
8543 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8545 return fieldType
->GetPropertiesMenuLabel((wxRichTextField
*) this);
8547 return wxEmptyString
;
8550 bool wxRichTextField::UpdateField(wxRichTextBuffer
* buffer
)
8552 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8554 return fieldType
->UpdateField(buffer
, (wxRichTextField
*) this);
8559 bool wxRichTextField::IsTopLevel() const
8561 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8563 return fieldType
->IsTopLevel((wxRichTextField
*) this);
8568 IMPLEMENT_CLASS(wxRichTextFieldType
, wxObject
)
8570 IMPLEMENT_CLASS(wxRichTextFieldTypeStandard
, wxRichTextFieldType
)
8572 wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString
& name
, const wxString
& label
, int displayStyle
)
8578 SetDisplayStyle(displayStyle
);
8581 wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString
& name
, const wxBitmap
& bitmap
, int displayStyle
)
8587 SetDisplayStyle(displayStyle
);
8590 void wxRichTextFieldTypeStandard::Init()
8592 m_displayStyle
= wxRICHTEXT_FIELD_STYLE_RECTANGLE
;
8593 m_font
= wxFont(6, wxFONTFAMILY_SWISS
, wxFONTSTYLE_NORMAL
, wxFONTWEIGHT_NORMAL
);
8594 m_textColour
= *wxWHITE
;
8595 m_borderColour
= *wxBLACK
;
8596 m_backgroundColour
= *wxBLACK
;
8597 m_verticalPadding
= 1;
8598 m_horizontalPadding
= 3;
8599 m_horizontalMargin
= 2;
8600 m_verticalMargin
= 0;
8603 void wxRichTextFieldTypeStandard::Copy(const wxRichTextFieldTypeStandard
& field
)
8605 wxRichTextFieldType::Copy(field
);
8607 m_label
= field
.m_label
;
8608 m_displayStyle
= field
.m_displayStyle
;
8609 m_font
= field
.m_font
;
8610 m_textColour
= field
.m_textColour
;
8611 m_borderColour
= field
.m_borderColour
;
8612 m_backgroundColour
= field
.m_backgroundColour
;
8613 m_verticalPadding
= field
.m_verticalPadding
;
8614 m_horizontalPadding
= field
.m_horizontalPadding
;
8615 m_horizontalMargin
= field
.m_horizontalMargin
;
8616 m_bitmap
= field
.m_bitmap
;
8619 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
))
8621 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_COMPOSITE
)
8622 return false; // USe default composite drawing
8623 else // if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_RECTANGLE || m_displayStyle == wxRICHTEXT_FIELD_STYLE_NOBORDER)
8627 wxPen
borderPen(m_borderColour
, 1, wxSOLID
);
8628 wxBrush
backgroundBrush(m_backgroundColour
);
8629 wxColour
textColour(m_textColour
);
8631 if (selection
.WithinSelection(obj
->GetRange().GetStart(), obj
))
8633 wxColour
highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
));
8634 wxColour
highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
));
8636 borderPen
= wxPen(highlightTextColour
, 1, wxSOLID
);
8637 backgroundBrush
= wxBrush(highlightColour
);
8639 wxCheckSetBrush(dc
, backgroundBrush
);
8640 wxCheckSetPen(dc
, wxPen(highlightColour
, 1, wxSOLID
));
8641 dc
.DrawRectangle(rect
);
8644 if (m_displayStyle
!= wxRICHTEXT_FIELD_STYLE_NO_BORDER
)
8647 // objectRect is the area where the content is drawn, after margins around it have been taken into account
8648 wxRect objectRect
= wxRect(wxPoint(rect
.x
+ m_horizontalMargin
, rect
.y
+ wxMax(0, rect
.height
- descent
- obj
->GetCachedSize().y
)),
8649 wxSize(obj
->GetCachedSize().x
- 2*m_horizontalMargin
- borderSize
, obj
->GetCachedSize().y
));
8651 // clientArea is where the text is actually written
8652 wxRect clientArea
= objectRect
;
8654 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_RECTANGLE
)
8656 dc
.SetPen(borderPen
);
8657 dc
.SetBrush(backgroundBrush
);
8658 dc
.DrawRoundedRectangle(objectRect
, 4.0);
8660 else if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_START_TAG
)
8662 int arrowLength
= objectRect
.height
/2;
8663 clientArea
.width
-= (arrowLength
- m_horizontalPadding
);
8666 pts
[0].x
= objectRect
.x
; pts
[0].y
= objectRect
.y
;
8667 pts
[1].x
= objectRect
.x
+ objectRect
.width
- arrowLength
; pts
[1].y
= objectRect
.y
;
8668 pts
[2].x
= objectRect
.x
+ objectRect
.width
; pts
[2].y
= objectRect
.y
+ (objectRect
.height
/2);
8669 pts
[3].x
= objectRect
.x
+ objectRect
.width
- arrowLength
; pts
[3].y
= objectRect
.y
+ objectRect
.height
;
8670 pts
[4].x
= objectRect
.x
; pts
[4].y
= objectRect
.y
+ objectRect
.height
;
8671 dc
.SetPen(borderPen
);
8672 dc
.SetBrush(backgroundBrush
);
8673 dc
.DrawPolygon(5, pts
);
8675 else if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_END_TAG
)
8677 int arrowLength
= objectRect
.height
/2;
8678 clientArea
.width
-= (arrowLength
- m_horizontalPadding
);
8679 clientArea
.x
+= (arrowLength
- m_horizontalPadding
);
8682 pts
[0].x
= objectRect
.x
+ objectRect
.width
; pts
[0].y
= objectRect
.y
;
8683 pts
[1].x
= objectRect
.x
+ arrowLength
; pts
[1].y
= objectRect
.y
;
8684 pts
[2].x
= objectRect
.x
; pts
[2].y
= objectRect
.y
+ (objectRect
.height
/2);
8685 pts
[3].x
= objectRect
.x
+ arrowLength
; pts
[3].y
= objectRect
.y
+ objectRect
.height
;
8686 pts
[4].x
= objectRect
.x
+ objectRect
.width
; pts
[4].y
= objectRect
.y
+ objectRect
.height
;
8687 dc
.SetPen(borderPen
);
8688 dc
.SetBrush(backgroundBrush
);
8689 dc
.DrawPolygon(5, pts
);
8692 if (m_bitmap
.IsOk())
8694 int x
= clientArea
.x
+ (clientArea
.width
- m_bitmap
.GetWidth())/2;
8695 int y
= clientArea
.y
+ m_verticalPadding
;
8696 dc
.DrawBitmap(m_bitmap
, x
, y
, true);
8698 if (selection
.WithinSelection(obj
->GetRange().GetStart(), obj
))
8700 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
8701 wxCheckSetPen(dc
, *wxBLACK_PEN
);
8702 dc
.SetLogicalFunction(wxINVERT
);
8703 dc
.DrawRectangle(wxRect(x
, y
, m_bitmap
.GetWidth(), m_bitmap
.GetHeight()));
8704 dc
.SetLogicalFunction(wxCOPY
);
8709 wxString
label(m_label
);
8710 if (label
.IsEmpty())
8712 int w
, h
, maxDescent
;
8714 dc
.GetTextExtent(m_label
, & w
, &h
, & maxDescent
);
8715 dc
.SetTextForeground(textColour
);
8717 int x
= clientArea
.x
+ (clientArea
.width
- w
)/2;
8718 int y
= clientArea
.y
+ (clientArea
.height
- (h
- maxDescent
))/2;
8719 dc
.DrawText(m_label
, x
, y
);
8726 bool wxRichTextFieldTypeStandard::Layout(wxRichTextField
* obj
, wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& WXUNUSED(rect
), const wxRect
& WXUNUSED(parentRect
), int style
)
8728 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_COMPOSITE
)
8729 return false; // USe default composite layout
8731 wxSize size
= GetSize(obj
, dc
, context
, style
);
8732 obj
->SetCachedSize(size
);
8733 obj
->SetMinSize(size
);
8734 obj
->SetMaxSize(size
);
8738 bool wxRichTextFieldTypeStandard::GetRangeSize(wxRichTextField
* obj
, const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
8740 if (IsTopLevel(obj
))
8741 return obj
->wxRichTextParagraphLayoutBox::GetRangeSize(range
, size
, descent
, dc
, context
, flags
, position
);
8744 wxSize sz
= GetSize(obj
, dc
, context
, 0);
8748 if (partialExtents
->GetCount() > 0)
8749 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
8752 partialExtents
->Add(lastSize
+ sz
.x
);
8759 wxSize
wxRichTextFieldTypeStandard::GetSize(wxRichTextField
* WXUNUSED(obj
), wxDC
& dc
, wxRichTextDrawingContext
& WXUNUSED(context
), int WXUNUSED(style
)) const
8762 int w
= 0, h
= 0, maxDescent
= 0;
8765 if (m_bitmap
.IsOk())
8767 w
= m_bitmap
.GetWidth();
8768 h
= m_bitmap
.GetHeight();
8770 sz
= wxSize(w
+ m_horizontalMargin
*2, h
+ m_verticalMargin
*2);
8774 wxString
label(m_label
);
8775 if (label
.IsEmpty())
8778 dc
.GetTextExtent(label
, & w
, &h
, & maxDescent
);
8780 sz
= wxSize(w
+ m_horizontalPadding
*2 + m_horizontalMargin
*2, h
+ m_verticalPadding
*2 + m_verticalMargin
*2);
8783 if (m_displayStyle
!= wxRICHTEXT_FIELD_STYLE_NO_BORDER
)
8785 sz
.x
+= borderSize
*2;
8786 sz
.y
+= borderSize
*2;
8789 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_START_TAG
|| m_displayStyle
== wxRICHTEXT_FIELD_STYLE_END_TAG
)
8791 // Add space for the arrow
8792 sz
.x
+= (sz
.y
/2 - m_horizontalPadding
);
8798 IMPLEMENT_DYNAMIC_CLASS(wxRichTextCell
, wxRichTextBox
)
8800 wxRichTextCell::wxRichTextCell(wxRichTextObject
* parent
):
8801 wxRichTextBox(parent
)
8806 bool wxRichTextCell::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8808 return wxRichTextBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8812 void wxRichTextCell::Copy(const wxRichTextCell
& obj
)
8814 wxRichTextBox::Copy(obj
);
8817 // Edit properties via a GUI
8818 bool wxRichTextCell::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8820 // We need to gather common attributes for all selected cells.
8822 wxRichTextTable
* table
= wxDynamicCast(GetParent(), wxRichTextTable
);
8823 bool multipleCells
= false;
8824 wxRichTextAttr attr
;
8826 if (table
&& buffer
&& buffer
->GetRichTextCtrl() && buffer
->GetRichTextCtrl()->GetSelection().IsValid() &&
8827 buffer
->GetRichTextCtrl()->GetSelection().GetContainer() == GetParent())
8829 wxRichTextAttr clashingAttr
, absentAttr
;
8830 const wxRichTextSelection
& sel
= buffer
->GetRichTextCtrl()->GetSelection();
8832 int selectedCellCount
= 0;
8833 for (i
= 0; i
< sel
.GetCount(); i
++)
8835 const wxRichTextRange
& range
= sel
[i
];
8836 wxRichTextCell
* cell
= table
->GetCell(range
.GetStart());
8839 wxRichTextAttr cellStyle
= cell
->GetAttributes();
8841 CollectStyle(attr
, cellStyle
, clashingAttr
, absentAttr
);
8843 selectedCellCount
++;
8846 multipleCells
= selectedCellCount
> 1;
8850 attr
= GetAttributes();
8855 caption
= _("Multiple Cell Properties");
8857 caption
= _("Cell Properties");
8859 wxRichTextObjectPropertiesDialog
cellDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, caption
);
8860 cellDlg
.SetAttributes(attr
);
8862 wxRichTextSizePage
* sizePage
= wxDynamicCast(cellDlg
.FindPage(wxCLASSINFO(wxRichTextSizePage
)), wxRichTextSizePage
);
8865 // We don't want position and floating controls for a cell.
8866 sizePage
->ShowPositionControls(false);
8867 sizePage
->ShowFloatingControls(false);
8870 if (cellDlg
.ShowModal() == wxID_OK
)
8874 const wxRichTextSelection
& sel
= buffer
->GetRichTextCtrl()->GetSelection();
8875 // Apply the style; we interpret indeterminate attributes as 'don't touch this attribute'
8876 // since it may represent clashing attributes across multiple objects.
8877 table
->SetCellStyle(sel
, attr
);
8880 // For a single object, indeterminate attributes set by the user should be reflected in the
8881 // actual object style, so pass the wxRICHTEXT_SETSTYLE_RESET flag to assign
8882 // the style directly instead of applying (which ignores indeterminate attributes,
8883 // leaving them as they were).
8884 cellDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
8891 WX_DEFINE_OBJARRAY(wxRichTextObjectPtrArrayArray
)
8893 IMPLEMENT_DYNAMIC_CLASS(wxRichTextTable
, wxRichTextBox
)
8895 wxRichTextTable::wxRichTextTable(wxRichTextObject
* parent
): wxRichTextBox(parent
)
8901 // Draws the object.
8902 bool wxRichTextTable::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8904 return wxRichTextBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8907 WX_DECLARE_OBJARRAY(wxRect
, wxRichTextRectArray
);
8908 WX_DEFINE_OBJARRAY(wxRichTextRectArray
);
8910 // Lays the object out. rect is the space available for layout. Often it will
8911 // be the specified overall space for this object, if trying to constrain
8912 // layout to a particular size, or it could be the total space available in the
8913 // parent. rect is the overall size, so we must subtract margins and padding.
8914 // to get the actual available space.
8915 bool wxRichTextTable::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& WXUNUSED(parentRect
), int style
)
8917 SetPosition(rect
.GetPosition());
8919 // TODO: the meaty bit. Calculate sizes of all cells and rows. Try to use
8920 // minimum size if within alloted size, then divide up remaining size
8921 // between rows/cols.
8924 wxRichTextBuffer
* buffer
= GetBuffer();
8925 if (buffer
) scale
= buffer
->GetScale();
8927 wxRect availableSpace
= GetAvailableContentArea(dc
, context
, rect
);
8928 wxTextAttrDimensionConverter
converter(dc
, scale
, availableSpace
.GetSize());
8930 wxRichTextAttr
attr(GetAttributes());
8931 context
.ApplyVirtualAttributes(attr
, this);
8933 // If we have no fixed table size, and assuming we're not pushed for
8934 // space, then we don't have to try to stretch the table to fit the contents.
8935 bool stretchToFitTableWidth
= false;
8937 int tableWidth
= rect
.width
;
8938 if (attr
.GetTextBoxAttr().GetWidth().IsValid())
8940 tableWidth
= converter
.GetPixels(attr
.GetTextBoxAttr().GetWidth());
8942 // Fixed table width, so we do want to stretch columns out if necessary.
8943 stretchToFitTableWidth
= true;
8945 // Shouldn't be able to exceed the size passed to this function
8946 tableWidth
= wxMin(rect
.width
, tableWidth
);
8949 // Get internal padding
8950 int paddingLeft
= 0, paddingTop
= 0;
8951 if (attr
.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
8952 paddingLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetLeft());
8953 if (attr
.GetTextBoxAttr().GetPadding().GetTop().IsValid())
8954 paddingTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetTop());
8956 // Assume that left and top padding are also used for inter-cell padding.
8957 int paddingX
= paddingLeft
;
8958 int paddingY
= paddingTop
;
8960 int totalLeftMargin
= 0, totalRightMargin
= 0, totalTopMargin
= 0, totalBottomMargin
= 0;
8961 GetTotalMargin(dc
, buffer
, attr
, totalLeftMargin
, totalRightMargin
, totalTopMargin
, totalBottomMargin
);
8963 // Internal table width - the area for content
8964 int internalTableWidth
= tableWidth
- totalLeftMargin
- totalRightMargin
;
8966 int rowCount
= m_cells
.GetCount();
8967 if (m_colCount
== 0 || rowCount
== 0)
8969 wxRect
overallRect(rect
.x
, rect
.y
, totalLeftMargin
+ totalRightMargin
, totalTopMargin
+ totalBottomMargin
);
8970 SetCachedSize(overallRect
.GetSize());
8972 // Zero content size
8973 SetMinSize(overallRect
.GetSize());
8974 SetMaxSize(GetMinSize());
8978 // The final calculated widths
8979 wxArrayInt colWidths
;
8980 colWidths
.Add(0, m_colCount
);
8982 wxArrayInt absoluteColWidths
;
8983 absoluteColWidths
.Add(0, m_colCount
);
8985 wxArrayInt percentageColWidths
;
8986 percentageColWidths
.Add(0, m_colCount
);
8987 // wxArrayInt percentageColWidthsSpanning(m_colCount);
8988 // These are only relevant when the first column contains spanning information.
8989 // wxArrayInt columnSpans(m_colCount); // Each contains 1 for non-spanning cell, > 1 for spanning cell.
8990 wxArrayInt maxColWidths
;
8991 maxColWidths
.Add(0, m_colCount
);
8992 wxArrayInt minColWidths
;
8993 minColWidths
.Add(0, m_colCount
);
8995 wxSize
tableSize(tableWidth
, 0);
8999 for (i
= 0; i
< m_colCount
; i
++)
9001 absoluteColWidths
[i
] = 0;
9002 // absoluteColWidthsSpanning[i] = 0;
9003 percentageColWidths
[i
] = -1;
9004 // percentageColWidthsSpanning[i] = -1;
9006 maxColWidths
[i
] = 0;
9007 minColWidths
[i
] = 0;
9008 // columnSpans[i] = 1;
9011 // (0) Determine which cells are visible according to spans
9013 // __________________
9018 // |------------------|
9019 // |__________________| 4
9021 // To calculate cell visibility:
9022 // First find all spanning cells. Build an array of span records with start x, y and end x, y.
9023 // Then for each cell, test whether we're within one of those cells, and unless we're at the start of
9024 // that cell, hide the cell.
9026 // We can also use this array to match the size of spanning cells to the grid. Or just do
9027 // this when we iterate through all cells.
9029 // 0.1: add spanning cells to an array
9030 wxRichTextRectArray rectArray
;
9031 for (j
= 0; j
< m_rowCount
; j
++)
9033 for (i
= 0; i
< m_colCount
; i
++)
9035 wxRichTextBox
* cell
= GetCell(j
, i
);
9036 int colSpan
= 1, rowSpan
= 1;
9037 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9038 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9039 if (cell
->GetProperties().HasProperty(wxT("rowspan")))
9040 rowSpan
= cell
->GetProperties().GetPropertyLong(wxT("rowspan"));
9041 if (colSpan
> 1 || rowSpan
> 1)
9043 rectArray
.Add(wxRect(i
, j
, colSpan
, rowSpan
));
9047 // 0.2: find which cells are subsumed by a spanning cell
9048 for (j
= 0; j
< m_rowCount
; j
++)
9050 for (i
= 0; i
< m_colCount
; i
++)
9052 wxRichTextBox
* cell
= GetCell(j
, i
);
9053 if (rectArray
.GetCount() == 0)
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 // Assume all spanning cells are shown
9072 for (k
= 0; k
< (int) rectArray
.GetCount(); k
++)
9074 if (rectArray
[k
].Contains(wxPoint(i
, j
)))
9086 // TODO: find the first spanned cell in each row that spans the most columns and doesn't
9087 // overlap with a spanned cell starting at a previous column position.
9088 // This means we need to keep an array of rects so we can check. However
9089 // it does also mean that some spans simply may not be taken into account
9090 // where there are different spans happening on different rows. In these cases,
9091 // they will simply be as wide as their constituent columns.
9093 // (1) Do an initial layout for all cells to get minimum and maximum size, and get
9094 // the absolute or percentage width of each column.
9096 for (j
= 0; j
< m_rowCount
; j
++)
9098 // First get the overall margins so we can calculate percentage widths based on
9099 // the available content space for all cells on the row
9101 int overallRowContentMargin
= 0;
9102 int visibleCellCount
= 0;
9104 for (i
= 0; i
< m_colCount
; i
++)
9106 wxRichTextBox
* cell
= GetCell(j
, i
);
9107 if (cell
->IsShown())
9109 int cellTotalLeftMargin
= 0, cellTotalRightMargin
= 0, cellTotalTopMargin
= 0, cellTotalBottomMargin
= 0;
9110 GetTotalMargin(dc
, buffer
, cell
->GetAttributes(), cellTotalLeftMargin
, cellTotalRightMargin
, cellTotalTopMargin
, cellTotalBottomMargin
);
9112 overallRowContentMargin
+= (cellTotalLeftMargin
+ cellTotalRightMargin
);
9113 visibleCellCount
++;
9117 // Add in inter-cell padding
9118 overallRowContentMargin
+= ((visibleCellCount
-1) * paddingX
);
9120 int rowContentWidth
= internalTableWidth
- overallRowContentMargin
;
9121 wxSize
rowTableSize(rowContentWidth
, 0);
9122 wxTextAttrDimensionConverter
converter(dc
, scale
, rowTableSize
);
9124 for (i
= 0; i
< m_colCount
; i
++)
9126 wxRichTextBox
* cell
= GetCell(j
, i
);
9127 if (cell
->IsShown())
9130 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9131 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9133 // Lay out cell to find min/max widths
9134 cell
->Invalidate(wxRICHTEXT_ALL
);
9135 cell
->Layout(dc
, context
, availableSpace
, availableSpace
, style
);
9139 int absoluteCellWidth
= -1;
9140 int percentageCellWidth
= -1;
9142 // I think we need to calculate percentages from the internal table size,
9143 // minus the padding between cells which we'll need to calculate from the
9144 // (number of VISIBLE cells - 1)*paddingX. Then percentages that add up to 100%
9145 // will add up to 100%. In CSS, the width specifies the cell's content rect width,
9146 // so if we want to conform to that we'll need to add in the overall cell margins.
9147 // However, this will make it difficult to specify percentages that add up to
9148 // 100% and still fit within the table width.
9149 // Let's say two cells have 50% width. They have 10 pixels of overall margin each.
9150 // The table content rect is 500 pixels and the inter-cell padding is 20 pixels.
9151 // If we're using internal content size for the width, we would calculate the
9152 // the overall cell width for n cells as:
9153 // (500 - 20*(n-1) - overallCellMargin1 - overallCellMargin2 - ...) * percentage / 100
9154 // + thisOverallCellMargin
9155 // = 500 - 20 - 10 - 10) * 0.5 + 10 = 240 pixels overall cell width.
9156 // Adding this back, we get 240 + 240 + 20 = 500 pixels.
9158 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9160 int w
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetWidth());
9161 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
9163 percentageCellWidth
= w
;
9167 absoluteCellWidth
= w
;
9169 // Override absolute width with minimum width if necessary
9170 if (cell
->GetMinSize().x
> 0 && absoluteCellWidth
!=1 && cell
->GetMinSize().x
> absoluteCellWidth
)
9171 absoluteCellWidth
= cell
->GetMinSize().x
;
9174 if (absoluteCellWidth
!= -1)
9176 if (absoluteCellWidth
> absoluteColWidths
[i
])
9177 absoluteColWidths
[i
] = absoluteCellWidth
;
9180 if (percentageCellWidth
!= -1)
9182 if (percentageCellWidth
> percentageColWidths
[i
])
9183 percentageColWidths
[i
] = percentageCellWidth
;
9186 if (colSpan
== 1 && cell
->GetMinSize().x
&& cell
->GetMinSize().x
> minColWidths
[i
])
9187 minColWidths
[i
] = cell
->GetMinSize().x
;
9188 if (colSpan
== 1 && cell
->GetMaxSize().x
&& cell
->GetMaxSize().x
> maxColWidths
[i
])
9189 maxColWidths
[i
] = cell
->GetMaxSize().x
;
9195 // (2) Allocate initial column widths from minimum widths, absolute values and proportions
9196 // TODO: simply merge this into (1).
9197 for (i
= 0; i
< m_colCount
; i
++)
9199 if (absoluteColWidths
[i
] > 0)
9201 colWidths
[i
] = absoluteColWidths
[i
];
9203 else if (percentageColWidths
[i
] > 0)
9205 colWidths
[i
] = percentageColWidths
[i
];
9207 // This is rubbish - we calculated the absolute widths from percentages, so
9208 // we can't do it again here.
9209 //colWidths[i] = (int) (double(percentageColWidths[i]) * double(tableWidth) / 100.0 + 0.5);
9213 // (3) Process absolute or proportional widths of spanning columns,
9214 // now that we know what our fixed column widths are going to be.
9215 // Spanned cells will try to adjust columns so the span will fit.
9216 // Even existing fixed column widths can be expanded if necessary.
9217 // Actually, currently fixed columns widths aren't adjusted; instead,
9218 // the algorithm favours earlier rows and adjusts unspecified column widths
9219 // the first time only. After that, we can't know whether the column has been
9220 // specified explicitly or not. (We could make a note if necessary.)
9221 for (j
= 0; j
< m_rowCount
; j
++)
9223 // First get the overall margins so we can calculate percentage widths based on
9224 // the available content space for all cells on the row
9226 int overallRowContentMargin
= 0;
9227 int visibleCellCount
= 0;
9229 for (i
= 0; i
< m_colCount
; i
++)
9231 wxRichTextBox
* cell
= GetCell(j
, i
);
9232 if (cell
->IsShown())
9234 int cellTotalLeftMargin
= 0, cellTotalRightMargin
= 0, cellTotalTopMargin
= 0, cellTotalBottomMargin
= 0;
9235 GetTotalMargin(dc
, buffer
, cell
->GetAttributes(), cellTotalLeftMargin
, cellTotalRightMargin
, cellTotalTopMargin
, cellTotalBottomMargin
);
9237 overallRowContentMargin
+= (cellTotalLeftMargin
+ cellTotalRightMargin
);
9238 visibleCellCount
++;
9242 // Add in inter-cell padding
9243 overallRowContentMargin
+= ((visibleCellCount
-1) * paddingX
);
9245 int rowContentWidth
= internalTableWidth
- overallRowContentMargin
;
9246 wxSize
rowTableSize(rowContentWidth
, 0);
9247 wxTextAttrDimensionConverter
converter(dc
, scale
, rowTableSize
);
9249 for (i
= 0; i
< m_colCount
; i
++)
9251 wxRichTextBox
* cell
= GetCell(j
, i
);
9252 if (cell
->IsShown())
9255 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9256 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9260 int spans
= wxMin(colSpan
, m_colCount
- i
);
9264 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9266 cellWidth
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetWidth());
9267 // Override absolute width with minimum width if necessary
9268 if (cell
->GetMinSize().x
> 0 && cellWidth
!=1 && cell
->GetMinSize().x
> cellWidth
)
9269 cellWidth
= cell
->GetMinSize().x
;
9273 // Do we want to do this? It's the only chance we get to
9274 // use the cell's min/max sizes, so we need to work out
9275 // how we're going to balance the unspecified spanning cell
9276 // width with the possibility more-constrained constituent cell widths.
9277 // Say there's a tiny bitmap giving it a max width of 10 pixels. We
9278 // don't want to constraint all the spanned columns to fit into this cell.
9279 // OK, let's say that if any of the constituent columns don't fit,
9280 // then we simply stop constraining the columns; instead, we'll just fit the spanning
9281 // cells to the columns later.
9282 cellWidth
= cell
->GetMinSize().x
;
9283 if (cell
->GetMaxSize().x
> cellWidth
)
9284 cellWidth
= cell
->GetMaxSize().x
;
9287 // Subtract the padding between cells
9288 int spanningWidth
= cellWidth
;
9289 spanningWidth
-= paddingX
* (spans
-1);
9291 if (spanningWidth
> 0)
9293 // Now share the spanning width between columns within that span
9294 // TODO: take into account min widths of columns within the span
9295 int spanningWidthLeft
= spanningWidth
;
9296 int stretchColCount
= 0;
9297 for (k
= i
; k
< (i
+spans
); k
++)
9299 if (colWidths
[k
] > 0) // absolute or proportional width has been specified
9300 spanningWidthLeft
-= colWidths
[k
];
9304 // Now divide what's left between the remaining columns
9306 if (stretchColCount
> 0)
9307 colShare
= spanningWidthLeft
/ stretchColCount
;
9308 int colShareRemainder
= spanningWidthLeft
- (colShare
* stretchColCount
);
9310 // If fixed-width columns are currently too big, then we'll later
9311 // stretch the spanned cell to fit.
9313 if (spanningWidthLeft
> 0)
9315 for (k
= i
; k
< (i
+spans
); k
++)
9317 if (colWidths
[k
] <= 0) // absolute or proportional width has not been specified
9319 int newWidth
= colShare
;
9320 if (k
== (i
+spans
-1))
9321 newWidth
+= colShareRemainder
; // ensure all pixels are filled
9322 colWidths
[k
] = newWidth
;
9333 // (4) Next, share any remaining space out between columns that have not yet been calculated.
9334 // TODO: take into account min widths of columns within the span
9335 int tableWidthMinusPadding
= internalTableWidth
- (m_colCount
-1)*paddingX
;
9336 int widthLeft
= tableWidthMinusPadding
;
9337 int stretchColCount
= 0;
9338 for (i
= 0; i
< m_colCount
; i
++)
9340 // TODO: we need to take into account min widths.
9341 // Subtract min width from width left, then
9342 // add the colShare to the min width
9343 if (colWidths
[i
] > 0) // absolute or proportional width has been specified
9344 widthLeft
-= colWidths
[i
];
9347 if (minColWidths
[i
] > 0)
9348 widthLeft
-= minColWidths
[i
];
9354 // Now divide what's left between the remaining columns
9356 if (stretchColCount
> 0)
9357 colShare
= widthLeft
/ stretchColCount
;
9358 int colShareRemainder
= widthLeft
- (colShare
* stretchColCount
);
9360 // Check we don't have enough space, in which case shrink all columns, overriding
9361 // any absolute/proportional widths
9362 // TODO: actually we would like to divide up the shrinkage according to size.
9363 // How do we calculate the proportions that will achieve this?
9364 // Could first choose an arbitrary value for stretching cells, and then calculate
9365 // factors to multiply each width by.
9366 // TODO: want to record this fact and pass to an iteration that tries e.g. min widths
9367 if (widthLeft
< 0 || (stretchToFitTableWidth
&& (stretchColCount
== 0)))
9369 colShare
= tableWidthMinusPadding
/ m_colCount
;
9370 colShareRemainder
= tableWidthMinusPadding
- (colShare
* m_colCount
);
9371 for (i
= 0; i
< m_colCount
; i
++)
9374 minColWidths
[i
] = 0;
9378 // We have to adjust the columns if either we need to shrink the
9379 // table to fit the parent/table width, or we explicitly set the
9380 // table width and need to stretch out the table.
9381 if (widthLeft
< 0 || stretchToFitTableWidth
)
9383 for (i
= 0; i
< m_colCount
; i
++)
9385 if (colWidths
[i
] <= 0) // absolute or proportional width has not been specified
9387 if (minColWidths
[i
] > 0)
9388 colWidths
[i
] = minColWidths
[i
] + colShare
;
9390 colWidths
[i
] = colShare
;
9391 if (i
== (m_colCount
-1))
9392 colWidths
[i
] += colShareRemainder
; // ensure all pixels are filled
9397 // TODO: if spanned cells have no specified or max width, make them the
9398 // as big as the columns they span. Do this for all spanned cells in all
9399 // rows, of course. Size any spanned cells left over at the end - even if they
9400 // have width > 0, make sure they're limited to the appropriate column edge.
9404 Sort out confusion between content width
9405 and overall width later. For now, assume we specify overall width.
9407 So, now we've laid out the table to fit into the given space
9408 and have used specified widths and minimum widths.
9410 Now we need to consider how we will try to take maximum width into account.
9414 // (??) TODO: take max width into account
9416 // (6) Lay out all cells again with the current values
9419 int y
= availableSpace
.y
;
9420 for (j
= 0; j
< m_rowCount
; j
++)
9422 int x
= availableSpace
.x
; // TODO: take into account centering etc.
9423 int maxCellHeight
= 0;
9424 int maxSpecifiedCellHeight
= 0;
9426 wxArrayInt actualWidths
;
9427 actualWidths
.Add(0, m_colCount
);
9429 wxTextAttrDimensionConverter
converter(dc
, scale
);
9430 for (i
= 0; i
< m_colCount
; i
++)
9432 wxRichTextCell
* cell
= GetCell(j
, i
);
9433 if (cell
->IsShown())
9435 // Get max specified cell height
9436 // Don't handle percentages for height
9437 if (cell
->GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && cell
->GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() != wxTEXT_ATTR_UNITS_PERCENTAGE
)
9439 int h
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetHeight());
9440 if (h
> maxSpecifiedCellHeight
)
9441 maxSpecifiedCellHeight
= h
;
9444 if (colWidths
[i
] > 0) // absolute or proportional width has been specified
9447 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9448 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9450 wxRect availableCellSpace
;
9452 // TODO: take into acount spans
9455 // Calculate the size of this spanning cell from its constituent columns
9457 int spans
= wxMin(colSpan
, m_colCount
- i
);
9458 for (k
= i
; k
< spans
; k
++)
9464 availableCellSpace
= wxRect(x
, y
, xx
, -1);
9467 availableCellSpace
= wxRect(x
, y
, colWidths
[i
], -1);
9469 // Store actual width so we can force cell to be the appropriate width on the final loop
9470 actualWidths
[i
] = availableCellSpace
.GetWidth();
9473 cell
->Invalidate(wxRICHTEXT_ALL
);
9474 cell
->Layout(dc
, context
, availableCellSpace
, availableSpace
, style
);
9476 // TODO: use GetCachedSize().x to compute 'natural' size
9478 x
+= (availableCellSpace
.GetWidth() + paddingX
);
9479 if (cell
->GetCachedSize().y
> maxCellHeight
)
9480 maxCellHeight
= cell
->GetCachedSize().y
;
9485 maxCellHeight
= wxMax(maxCellHeight
, maxSpecifiedCellHeight
);
9487 for (i
= 0; i
< m_colCount
; i
++)
9489 wxRichTextCell
* cell
= GetCell(j
, i
);
9490 if (cell
->IsShown())
9492 wxRect availableCellSpace
= wxRect(cell
->GetPosition(), wxSize(actualWidths
[i
], maxCellHeight
));
9493 // Lay out cell with new height
9494 cell
->Invalidate(wxRICHTEXT_ALL
);
9495 cell
->Layout(dc
, context
, availableCellSpace
, availableSpace
, style
);
9497 // Make sure the cell size really is the appropriate size,
9498 // not the calculated box size
9499 cell
->SetCachedSize(wxSize(actualWidths
[i
], maxCellHeight
));
9501 maxRight
= wxMax(maxRight
, cell
->GetPosition().x
+ cell
->GetCachedSize().x
);
9506 if (j
< (m_rowCount
-1))
9510 // We need to add back the margins etc.
9512 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
9513 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxRight
- availableSpace
.x
, y
- availableSpace
.y
));
9514 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
9515 SetCachedSize(marginRect
.GetSize());
9518 // TODO: calculate max size
9520 SetMaxSize(GetCachedSize());
9523 // TODO: calculate min size
9525 SetMinSize(GetCachedSize());
9528 // TODO: currently we use either a fixed table width or the parent's size.
9529 // We also want to be able to calculate the table width from its content,
9530 // whether using fixed column widths or cell content min/max width.
9531 // Probably need a boolean flag to say whether we need to stretch cells
9532 // to fit the table width, or to simply use min/max cell widths. The
9533 // trouble with this is that if cell widths are not specified, they
9534 // will be tiny; we could use arbitrary defaults but this seems unsatisfactory.
9535 // Anyway, ignoring that problem, we probably need to factor layout into a function
9536 // that can can calculate the maximum unconstrained layout in case table size is
9537 // not specified. Then LayoutToBestSize() can choose to use either parent size to
9538 // constrain Layout(), or the previously-calculated max size to constraint layout.
9543 // Finds the absolute position and row height for the given character position
9544 bool wxRichTextTable::FindPosition(wxDC
& dc
, wxRichTextDrawingContext
& context
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
9546 wxRichTextCell
* child
= GetCell(index
+1);
9549 // Find the position at the start of the child cell, since the table doesn't
9550 // have any caret position of its own.
9551 return child
->FindPosition(dc
, context
, -1, pt
, height
, forceLineStart
);
9557 // Get the cell at the given character position (in the range of the table).
9558 wxRichTextCell
* wxRichTextTable::GetCell(long pos
) const
9560 int row
= 0, col
= 0;
9561 if (GetCellRowColumnPosition(pos
, row
, col
))
9563 return GetCell(row
, col
);
9569 // Get the row/column for a given character position
9570 bool wxRichTextTable::GetCellRowColumnPosition(long pos
, int& row
, int& col
) const
9572 if (m_colCount
== 0 || m_rowCount
== 0)
9575 row
= (int) (pos
/ m_colCount
);
9576 col
= pos
- (row
* m_colCount
);
9578 wxASSERT(row
< m_rowCount
&& col
< m_colCount
);
9580 if (row
< m_rowCount
&& col
< m_colCount
)
9586 // Calculate range, taking row/cell ordering into account instead of relying
9587 // on list ordering.
9588 void wxRichTextTable::CalculateRange(long start
, long& end
)
9590 long current
= start
;
9591 long lastEnd
= current
;
9600 for (i
= 0; i
< m_rowCount
; i
++)
9602 for (j
= 0; j
< m_colCount
; j
++)
9604 wxRichTextCell
* child
= GetCell(i
, j
);
9609 child
->CalculateRange(current
, childEnd
);
9612 current
= childEnd
+ 1;
9617 // A top-level object always has a range of size 1,
9618 // because its children don't count at this level.
9620 m_range
.SetRange(start
, start
);
9622 // An object with no children has zero length
9623 if (m_children
.GetCount() == 0)
9625 m_ownRange
.SetRange(0, lastEnd
);
9628 // Gets the range size.
9629 bool wxRichTextTable::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
9631 return wxRichTextBox::GetRangeSize(range
, size
, descent
, dc
, context
, flags
, position
, partialExtents
);
9634 // Deletes content in the given range.
9635 bool wxRichTextTable::DeleteRange(const wxRichTextRange
& WXUNUSED(range
))
9637 // TODO: implement deletion of cells
9641 // Gets any text in this object for the given range.
9642 wxString
wxRichTextTable::GetTextForRange(const wxRichTextRange
& range
) const
9644 return wxRichTextBox::GetTextForRange(range
);
9647 // Copies this object.
9648 void wxRichTextTable::Copy(const wxRichTextTable
& obj
)
9650 wxRichTextBox::Copy(obj
);
9654 m_rowCount
= obj
.m_rowCount
;
9655 m_colCount
= obj
.m_colCount
;
9657 m_cells
.Add(wxRichTextObjectPtrArray(), m_rowCount
);
9660 for (i
= 0; i
< m_rowCount
; i
++)
9662 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
9663 for (j
= 0; j
< m_colCount
; j
++)
9665 wxRichTextCell
* cell
= wxDynamicCast(obj
.GetCell(i
, j
)->Clone(), wxRichTextCell
);
9673 void wxRichTextTable::ClearTable()
9679 bool wxRichTextTable::CreateTable(int rows
, int cols
)
9686 m_cells
.Add(wxRichTextObjectPtrArray(), rows
);
9689 for (i
= 0; i
< rows
; i
++)
9691 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
9692 for (j
= 0; j
< cols
; j
++)
9694 wxRichTextCell
* cell
= new wxRichTextCell
;
9696 cell
->AddParagraph(wxEmptyString
);
9705 wxRichTextCell
* wxRichTextTable::GetCell(int row
, int col
) const
9707 wxASSERT(row
< m_rowCount
);
9708 wxASSERT(col
< m_colCount
);
9710 if (row
< m_rowCount
&& col
< m_colCount
)
9712 wxRichTextObjectPtrArray
& colArray
= m_cells
[row
];
9713 wxRichTextObject
* obj
= colArray
[col
];
9714 return wxDynamicCast(obj
, wxRichTextCell
);
9720 // Returns a selection object specifying the selections between start and end character positions.
9721 // For example, a table would deduce what cells (of range length 1) are selected when dragging across the table.
9722 wxRichTextSelection
wxRichTextTable::GetSelection(long start
, long end
) const
9724 wxRichTextSelection selection
;
9725 selection
.SetContainer((wxRichTextTable
*) this);
9734 wxASSERT( start
>= 0 && end
< (m_colCount
* m_rowCount
));
9736 if (end
>= (m_colCount
* m_rowCount
))
9739 // We need to find the rectangle of cells that is described by the rectangle
9740 // with start, end as the diagonal. Make sure we don't add cells that are
9741 // not currenty visible because they are overlapped by spanning cells.
9743 --------------------------
9744 | 0 | 1 | 2 | 3 | 4 |
9745 --------------------------
9746 | 5 | 6 | 7 | 8 | 9 |
9747 --------------------------
9748 | 10 | 11 | 12 | 13 | 14 |
9749 --------------------------
9750 | 15 | 16 | 17 | 18 | 19 |
9751 --------------------------
9753 Let's say we select 6 -> 18.
9755 Left and right edge cols of rectangle are 1 and 3 inclusive. Find least/greatest to find
9756 which is left and which is right.
9758 Top and bottom edge rows are 1 and 3 inclusive. Again, find least/greatest to find top and bottom.
9760 Now go through rows from 1 to 3 and only add cells that are (a) within above column range
9766 int leftCol
= start
- m_colCount
* int(start
/m_colCount
);
9767 int rightCol
= end
- m_colCount
* int(end
/m_colCount
);
9769 int topRow
= int(start
/m_colCount
);
9770 int bottomRow
= int(end
/m_colCount
);
9772 if (leftCol
> rightCol
)
9779 if (topRow
> bottomRow
)
9781 int tmp
= bottomRow
;
9787 for (i
= topRow
; i
<= bottomRow
; i
++)
9789 for (j
= leftCol
; j
<= rightCol
; j
++)
9791 wxRichTextCell
* cell
= GetCell(i
, j
);
9792 if (cell
&& cell
->IsShown())
9793 selection
.Add(cell
->GetRange());
9800 // Sets the attributes for the cells specified by the selection.
9801 bool wxRichTextTable::SetCellStyle(const wxRichTextSelection
& selection
, const wxRichTextAttr
& style
, int flags
)
9803 if (selection
.GetContainer() != this)
9806 wxRichTextBuffer
* buffer
= GetBuffer();
9807 bool haveControl
= (buffer
&& buffer
->GetRichTextCtrl() != NULL
);
9808 bool withUndo
= haveControl
&& ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
9811 buffer
->BeginBatchUndo(_("Set Cell Style"));
9813 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
9816 wxRichTextCell
* cell
= wxDynamicCast(node
->GetData(), wxRichTextCell
);
9817 if (cell
&& selection
.WithinSelection(cell
->GetRange().GetStart()))
9818 SetStyle(cell
, style
, flags
);
9819 node
= node
->GetNext();
9822 // Do action, or delay it until end of batch.
9824 buffer
->EndBatchUndo();
9829 bool wxRichTextTable::DeleteRows(int startRow
, int noRows
)
9831 wxASSERT((startRow
+ noRows
) < m_rowCount
);
9832 if ((startRow
+ noRows
) >= m_rowCount
)
9836 for (i
= startRow
; i
< (startRow
+noRows
); i
++)
9838 wxRichTextObjectPtrArray
& colArray
= m_cells
[startRow
];
9839 for (j
= 0; j
< (int) colArray
.GetCount(); j
++)
9841 wxRichTextObject
* cell
= colArray
[j
];
9842 RemoveChild(cell
, true);
9845 // Keep deleting at the same position, since we move all
9847 m_cells
.RemoveAt(startRow
);
9850 m_rowCount
= m_rowCount
- noRows
;
9855 bool wxRichTextTable::DeleteColumns(int startCol
, int noCols
)
9857 wxASSERT((startCol
+ noCols
) < m_colCount
);
9858 if ((startCol
+ noCols
) >= m_colCount
)
9861 bool deleteRows
= (noCols
== m_colCount
);
9864 for (i
= 0; i
< m_rowCount
; i
++)
9866 wxRichTextObjectPtrArray
& colArray
= m_cells
[deleteRows
? 0 : i
];
9867 for (j
= startCol
; j
< (startCol
+noCols
); j
++)
9869 wxRichTextObject
* cell
= colArray
[j
];
9870 RemoveChild(cell
, true);
9874 m_cells
.RemoveAt(0);
9879 m_colCount
= m_colCount
- noCols
;
9884 bool wxRichTextTable::AddRows(int startRow
, int noRows
, const wxRichTextAttr
& attr
)
9886 wxASSERT(startRow
<= m_rowCount
);
9887 if (startRow
> m_rowCount
)
9891 for (i
= 0; i
< noRows
; i
++)
9894 if (startRow
== m_rowCount
)
9896 m_cells
.Add(wxRichTextObjectPtrArray());
9897 idx
= m_cells
.GetCount() - 1;
9901 m_cells
.Insert(wxRichTextObjectPtrArray(), startRow
+i
);
9905 wxRichTextObjectPtrArray
& colArray
= m_cells
[idx
];
9906 for (j
= 0; j
< m_colCount
; j
++)
9908 wxRichTextCell
* cell
= new wxRichTextCell
;
9909 cell
->GetAttributes() = attr
;
9916 m_rowCount
= m_rowCount
+ noRows
;
9920 bool wxRichTextTable::AddColumns(int startCol
, int noCols
, const wxRichTextAttr
& attr
)
9922 wxASSERT(startCol
<= m_colCount
);
9923 if (startCol
> m_colCount
)
9927 for (i
= 0; i
< m_rowCount
; i
++)
9929 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
9930 for (j
= 0; j
< noCols
; j
++)
9932 wxRichTextCell
* cell
= new wxRichTextCell
;
9933 cell
->GetAttributes() = attr
;
9937 if (startCol
== m_colCount
)
9940 colArray
.Insert(cell
, startCol
+j
);
9944 m_colCount
= m_colCount
+ noCols
;
9949 // Edit properties via a GUI
9950 bool wxRichTextTable::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
9952 wxRichTextObjectPropertiesDialog
boxDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Table Properties"));
9953 boxDlg
.SetAttributes(GetAttributes());
9955 if (boxDlg
.ShowModal() == wxID_OK
)
9957 boxDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
9965 * Module to initialise and clean up handlers
9968 class wxRichTextModule
: public wxModule
9970 DECLARE_DYNAMIC_CLASS(wxRichTextModule
)
9972 wxRichTextModule() {}
9975 wxRichTextBuffer::SetRenderer(new wxRichTextStdRenderer
);
9976 wxRichTextBuffer::InitStandardHandlers();
9977 wxRichTextParagraph::InitDefaultTabs();
9979 wxRichTextXMLHandler::RegisterNodeName(wxT("text"), wxT("wxRichTextPlainText"));
9980 wxRichTextXMLHandler::RegisterNodeName(wxT("symbol"), wxT("wxRichTextPlainText"));
9981 wxRichTextXMLHandler::RegisterNodeName(wxT("image"), wxT("wxRichTextImage"));
9982 wxRichTextXMLHandler::RegisterNodeName(wxT("paragraph"), wxT("wxRichTextParagraph"));
9983 wxRichTextXMLHandler::RegisterNodeName(wxT("paragraphlayout"), wxT("wxRichTextParagraphLayoutBox"));
9984 wxRichTextXMLHandler::RegisterNodeName(wxT("textbox"), wxT("wxRichTextBox"));
9985 wxRichTextXMLHandler::RegisterNodeName(wxT("cell"), wxT("wxRichTextCell"));
9986 wxRichTextXMLHandler::RegisterNodeName(wxT("table"), wxT("wxRichTextTable"));
9987 wxRichTextXMLHandler::RegisterNodeName(wxT("field"), wxT("wxRichTextField"));
9993 wxRichTextBuffer::CleanUpHandlers();
9994 wxRichTextBuffer::CleanUpDrawingHandlers();
9995 wxRichTextBuffer::CleanUpFieldTypes();
9996 wxRichTextXMLHandler::ClearNodeToClassMap();
9997 wxRichTextDecimalToRoman(-1);
9998 wxRichTextParagraph::ClearDefaultTabs();
9999 wxRichTextCtrl::ClearAvailableFontNames();
10000 wxRichTextBuffer::SetRenderer(NULL
);
10004 IMPLEMENT_DYNAMIC_CLASS(wxRichTextModule
, wxModule
)
10007 // If the richtext lib is dynamically loaded after the app has already started
10008 // (such as from wxPython) then the built-in module system will not init this
10009 // module. Provide this function to do it manually.
10010 void wxRichTextModuleInit()
10012 wxModule
* module = new wxRichTextModule
;
10014 wxModule::RegisterModule(module);
10019 * Commands for undo/redo
10023 wxRichTextCommand::wxRichTextCommand(const wxString
& name
, wxRichTextCommandId id
, wxRichTextBuffer
* buffer
,
10024 wxRichTextParagraphLayoutBox
* container
, wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
): wxCommand(true, name
)
10026 /* wxRichTextAction* action = */ new wxRichTextAction(this, name
, id
, buffer
, container
, ctrl
, ignoreFirstTime
);
10029 wxRichTextCommand::wxRichTextCommand(const wxString
& name
): wxCommand(true, name
)
10033 wxRichTextCommand::~wxRichTextCommand()
10038 void wxRichTextCommand::AddAction(wxRichTextAction
* action
)
10040 if (!m_actions
.Member(action
))
10041 m_actions
.Append(action
);
10044 bool wxRichTextCommand::Do()
10046 for (wxList::compatibility_iterator node
= m_actions
.GetFirst(); node
; node
= node
->GetNext())
10048 wxRichTextAction
* action
= (wxRichTextAction
*) node
->GetData();
10055 bool wxRichTextCommand::Undo()
10057 for (wxList::compatibility_iterator node
= m_actions
.GetLast(); node
; node
= node
->GetPrevious())
10059 wxRichTextAction
* action
= (wxRichTextAction
*) node
->GetData();
10066 void wxRichTextCommand::ClearActions()
10068 WX_CLEAR_LIST(wxList
, m_actions
);
10072 * Individual action
10076 wxRichTextAction::wxRichTextAction(wxRichTextCommand
* cmd
, const wxString
& name
, wxRichTextCommandId id
,
10077 wxRichTextBuffer
* buffer
, wxRichTextParagraphLayoutBox
* container
,
10078 wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
)
10082 m_containerAddress
.Create(buffer
, container
);
10083 m_ignoreThis
= ignoreFirstTime
;
10088 m_newParagraphs
.SetDefaultStyle(buffer
->GetDefaultStyle());
10089 m_newParagraphs
.SetBasicStyle(buffer
->GetBasicStyle());
10091 cmd
->AddAction(this);
10094 wxRichTextAction::~wxRichTextAction()
10100 // Returns the container that this action refers to, using the container address and top-level buffer.
10101 wxRichTextParagraphLayoutBox
* wxRichTextAction::GetContainer() const
10103 wxRichTextParagraphLayoutBox
* container
= wxDynamicCast(GetContainerAddress().GetObject(m_buffer
), wxRichTextParagraphLayoutBox
);
10108 void wxRichTextAction::CalculateRefreshOptimizations(wxArrayInt
& optimizationLineCharPositions
, wxArrayInt
& optimizationLineYPositions
)
10110 // Store a list of line start character and y positions so we can figure out which area
10111 // we need to refresh
10113 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10114 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10115 wxASSERT(container
!= NULL
);
10119 // NOTE: we're assuming that the buffer is laid out correctly at this point.
10120 // If we had several actions, which only invalidate and leave layout until the
10121 // paint handler is called, then this might not be true. So we may need to switch
10122 // optimisation on only when we're simply adding text and not simultaneously
10123 // deleting a selection, for example. Or, we make sure the buffer is laid out correctly
10124 // first, but of course this means we'll be doing it twice.
10125 if (!m_buffer
->IsDirty() && m_ctrl
) // can only do optimisation if the buffer is already laid out correctly
10127 wxSize clientSize
= m_ctrl
->GetUnscaledSize(m_ctrl
->GetClientSize());
10128 wxPoint firstVisiblePt
= m_ctrl
->GetUnscaledPoint(m_ctrl
->GetFirstVisiblePoint());
10129 int lastY
= firstVisiblePt
.y
+ clientSize
.y
;
10131 wxRichTextParagraph
* para
= container
->GetParagraphAtPosition(GetRange().GetStart());
10132 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(para
);
10135 wxRichTextParagraph
* child
= (wxRichTextParagraph
*) node
->GetData();
10136 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
10139 wxRichTextLine
* line
= node2
->GetData();
10140 wxPoint pt
= line
->GetAbsolutePosition();
10141 wxRichTextRange range
= line
->GetAbsoluteRange();
10145 node2
= wxRichTextLineList::compatibility_iterator();
10146 node
= wxRichTextObjectList::compatibility_iterator();
10148 else if (range
.GetStart() > GetPosition() && pt
.y
>= firstVisiblePt
.y
)
10150 optimizationLineCharPositions
.Add(range
.GetStart());
10151 optimizationLineYPositions
.Add(pt
.y
);
10155 node2
= node2
->GetNext();
10159 node
= node
->GetNext();
10165 bool wxRichTextAction::Do()
10167 m_buffer
->Modify(true);
10169 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10170 wxASSERT(container
!= NULL
);
10176 case wxRICHTEXT_INSERT
:
10178 // Store a list of line start character and y positions so we can figure out which area
10179 // we need to refresh
10180 wxArrayInt optimizationLineCharPositions
;
10181 wxArrayInt optimizationLineYPositions
;
10183 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10184 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
10187 container
->InsertFragment(GetRange().GetStart(), m_newParagraphs
);
10188 container
->UpdateRanges();
10190 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10191 // Layout() would stop prematurely at the top level.
10192 container
->InvalidateHierarchy(wxRichTextRange(wxMax(0, GetRange().GetStart()-1), GetRange().GetEnd()));
10194 long newCaretPosition
= GetPosition() + m_newParagraphs
.GetOwnRange().GetLength();
10196 // Character position to caret position
10197 newCaretPosition
--;
10199 // Don't take into account the last newline
10200 if (m_newParagraphs
.GetPartialParagraph())
10201 newCaretPosition
--;
10203 if (m_newParagraphs
.GetChildren().GetCount() > 1)
10205 wxRichTextObject
* p
= (wxRichTextObject
*) m_newParagraphs
.GetChildren().GetLast()->GetData();
10206 if (p
->GetRange().GetLength() == 1)
10207 newCaretPosition
--;
10210 newCaretPosition
= wxMin(newCaretPosition
, (container
->GetOwnRange().GetEnd()-1));
10212 UpdateAppearance(newCaretPosition
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */);
10214 wxRichTextEvent
cmdEvent(
10215 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED
,
10216 m_ctrl
? m_ctrl
->GetId() : -1);
10217 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10218 cmdEvent
.SetRange(GetRange());
10219 cmdEvent
.SetPosition(GetRange().GetStart());
10220 cmdEvent
.SetContainer(container
);
10222 m_buffer
->SendEvent(cmdEvent
);
10226 case wxRICHTEXT_DELETE
:
10228 wxArrayInt optimizationLineCharPositions
;
10229 wxArrayInt optimizationLineYPositions
;
10231 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10232 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
10235 container
->DeleteRange(GetRange());
10236 container
->UpdateRanges();
10237 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10238 // Layout() would stop prematurely at the top level.
10239 container
->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
10241 long caretPos
= GetRange().GetStart()-1;
10242 if (caretPos
>= container
->GetOwnRange().GetEnd())
10245 UpdateAppearance(caretPos
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */);
10247 wxRichTextEvent
cmdEvent(
10248 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED
,
10249 m_ctrl
? m_ctrl
->GetId() : -1);
10250 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10251 cmdEvent
.SetRange(GetRange());
10252 cmdEvent
.SetPosition(GetRange().GetStart());
10253 cmdEvent
.SetContainer(container
);
10255 m_buffer
->SendEvent(cmdEvent
);
10259 case wxRICHTEXT_CHANGE_STYLE
:
10260 case wxRICHTEXT_CHANGE_PROPERTIES
:
10262 ApplyParagraphs(GetNewParagraphs());
10264 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10265 // Layout() would stop prematurely at the top level.
10266 container
->InvalidateHierarchy(GetRange());
10268 UpdateAppearance(GetPosition());
10270 wxRichTextEvent
cmdEvent(
10271 m_cmdId
== wxRICHTEXT_CHANGE_STYLE
? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
: wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED
,
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_ATTRIBUTES
:
10284 wxRichTextObject
* obj
= m_objectAddress
.GetObject(m_buffer
); // container->GetChildAtPosition(GetRange().GetStart());
10287 wxRichTextAttr oldAttr
= obj
->GetAttributes();
10288 obj
->GetAttributes() = m_attributes
;
10289 m_attributes
= oldAttr
;
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());
10296 UpdateAppearance(GetPosition());
10298 wxRichTextEvent
cmdEvent(
10299 wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
,
10300 m_ctrl
? m_ctrl
->GetId() : -1);
10301 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10302 cmdEvent
.SetRange(GetRange());
10303 cmdEvent
.SetPosition(GetRange().GetStart());
10304 cmdEvent
.SetContainer(container
);
10306 m_buffer
->SendEvent(cmdEvent
);
10310 case wxRICHTEXT_CHANGE_OBJECT
:
10312 wxRichTextObject
* obj
= m_objectAddress
.GetObject(m_buffer
);
10313 // wxRichTextObject* obj = container->GetChildAtPosition(GetRange().GetStart());
10314 if (obj
&& m_object
)
10316 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(obj
);
10319 wxRichTextObject
* obj
= node
->GetData();
10320 node
->SetData(m_object
);
10325 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10326 // Layout() would stop prematurely at the top level.
10327 container
->InvalidateHierarchy(GetRange());
10329 UpdateAppearance(GetPosition());
10331 // TODO: send new kind of modification event
10342 bool wxRichTextAction::Undo()
10344 m_buffer
->Modify(true);
10346 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10347 wxASSERT(container
!= NULL
);
10353 case wxRICHTEXT_INSERT
:
10355 wxArrayInt optimizationLineCharPositions
;
10356 wxArrayInt optimizationLineYPositions
;
10358 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10359 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
10362 container
->DeleteRange(GetRange());
10363 container
->UpdateRanges();
10365 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10366 // Layout() would stop prematurely at the top level.
10367 container
->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
10369 long newCaretPosition
= GetPosition() - 1;
10371 UpdateAppearance(newCaretPosition
, true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */);
10373 wxRichTextEvent
cmdEvent(
10374 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED
,
10375 m_ctrl
? m_ctrl
->GetId() : -1);
10376 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10377 cmdEvent
.SetRange(GetRange());
10378 cmdEvent
.SetPosition(GetRange().GetStart());
10379 cmdEvent
.SetContainer(container
);
10381 m_buffer
->SendEvent(cmdEvent
);
10385 case wxRICHTEXT_DELETE
:
10387 wxArrayInt optimizationLineCharPositions
;
10388 wxArrayInt optimizationLineYPositions
;
10390 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10391 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
10394 container
->InsertFragment(GetRange().GetStart(), m_oldParagraphs
);
10395 container
->UpdateRanges();
10397 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10398 // Layout() would stop prematurely at the top level.
10399 container
->InvalidateHierarchy(GetRange());
10401 UpdateAppearance(GetPosition(), true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */);
10403 wxRichTextEvent
cmdEvent(
10404 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED
,
10405 m_ctrl
? m_ctrl
->GetId() : -1);
10406 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10407 cmdEvent
.SetRange(GetRange());
10408 cmdEvent
.SetPosition(GetRange().GetStart());
10409 cmdEvent
.SetContainer(container
);
10411 m_buffer
->SendEvent(cmdEvent
);
10415 case wxRICHTEXT_CHANGE_STYLE
:
10416 case wxRICHTEXT_CHANGE_PROPERTIES
:
10418 ApplyParagraphs(GetOldParagraphs());
10419 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10420 // Layout() would stop prematurely at the top level.
10421 container
->InvalidateHierarchy(GetRange());
10423 UpdateAppearance(GetPosition());
10425 wxRichTextEvent
cmdEvent(
10426 m_cmdId
== wxRICHTEXT_CHANGE_STYLE
? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
: wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED
,
10427 m_ctrl
? m_ctrl
->GetId() : -1);
10428 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10429 cmdEvent
.SetRange(GetRange());
10430 cmdEvent
.SetPosition(GetRange().GetStart());
10431 cmdEvent
.SetContainer(container
);
10433 m_buffer
->SendEvent(cmdEvent
);
10437 case wxRICHTEXT_CHANGE_ATTRIBUTES
:
10438 case wxRICHTEXT_CHANGE_OBJECT
:
10449 /// Update the control appearance
10450 void wxRichTextAction::UpdateAppearance(long caretPosition
, bool sendUpdateEvent
, wxArrayInt
* optimizationLineCharPositions
, wxArrayInt
* optimizationLineYPositions
, bool isDoCmd
)
10452 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10453 wxASSERT(container
!= NULL
);
10459 m_ctrl
->SetFocusObject(container
);
10460 m_ctrl
->SetCaretPosition(caretPosition
);
10462 if (!m_ctrl
->IsFrozen())
10464 wxRect containerRect
= container
->GetRect();
10466 m_ctrl
->LayoutContent();
10468 // Refresh everything if there were floating objects or the container changed size
10469 // (we can't yet optimize in these cases, since more complex interaction with other content occurs)
10470 if (container
->GetFloatingObjectCount() > 0 || (container
->GetParent() && containerRect
!= container
->GetRect()))
10472 m_ctrl
->Refresh(false);
10476 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10477 // Find refresh rectangle if we are in a position to optimise refresh
10478 if ((m_cmdId
== wxRICHTEXT_INSERT
|| m_cmdId
== wxRICHTEXT_DELETE
) && optimizationLineCharPositions
)
10482 wxSize clientSize
= m_ctrl
->GetUnscaledSize(m_ctrl
->GetClientSize());
10483 wxPoint firstVisiblePt
= m_ctrl
->GetUnscaledPoint(m_ctrl
->GetFirstVisiblePoint());
10485 // Start/end positions
10487 int lastY
= firstVisiblePt
.y
+ clientSize
.y
;
10489 bool foundEnd
= false;
10491 // position offset - how many characters were inserted
10492 int positionOffset
= GetRange().GetLength();
10494 // Determine whether this is Do or Undo, and adjust positionOffset accordingly
10495 if ((m_cmdId
== wxRICHTEXT_DELETE
&& isDoCmd
) || (m_cmdId
== wxRICHTEXT_INSERT
&& !isDoCmd
))
10496 positionOffset
= - positionOffset
;
10498 // find the first line which is being drawn at the same position as it was
10499 // before. Since we're talking about a simple insertion, we can assume
10500 // that the rest of the window does not need to be redrawn.
10502 wxRichTextParagraph
* para
= container
->GetParagraphAtPosition(GetPosition());
10503 // Since we support floating layout, we should redraw the whole para instead of just
10504 // the first line touching the invalid range.
10507 firstY
= para
->GetPosition().y
;
10510 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(para
);
10513 wxRichTextParagraph
* child
= (wxRichTextParagraph
*) node
->GetData();
10514 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
10517 wxRichTextLine
* line
= node2
->GetData();
10518 wxPoint pt
= line
->GetAbsolutePosition();
10519 wxRichTextRange range
= line
->GetAbsoluteRange();
10521 // we want to find the first line that is in the same position
10522 // as before. This will mean we're at the end of the changed text.
10524 if (pt
.y
> lastY
) // going past the end of the window, no more info
10526 node2
= wxRichTextLineList::compatibility_iterator();
10527 node
= wxRichTextObjectList::compatibility_iterator();
10529 // Detect last line in the buffer
10530 else if (!node2
->GetNext() && para
->GetRange().Contains(container
->GetOwnRange().GetEnd()))
10532 // If deleting text, make sure we refresh below as well as above
10533 if (positionOffset
>= 0)
10536 lastY
= pt
.y
+ line
->GetSize().y
;
10539 node2
= wxRichTextLineList::compatibility_iterator();
10540 node
= wxRichTextObjectList::compatibility_iterator();
10546 // search for this line being at the same position as before
10547 for (i
= 0; i
< optimizationLineCharPositions
->GetCount(); i
++)
10549 if (((*optimizationLineCharPositions
)[i
] + positionOffset
== range
.GetStart()) &&
10550 ((*optimizationLineYPositions
)[i
] == pt
.y
))
10552 // Stop, we're now the same as we were
10557 node2
= wxRichTextLineList::compatibility_iterator();
10558 node
= wxRichTextObjectList::compatibility_iterator();
10566 node2
= node2
->GetNext();
10570 node
= node
->GetNext();
10573 firstY
= wxMax(firstVisiblePt
.y
, firstY
);
10575 lastY
= firstVisiblePt
.y
+ clientSize
.y
;
10577 // Convert to device coordinates
10578 wxRect
rect(m_ctrl
->GetPhysicalPoint(m_ctrl
->GetScaledPoint(wxPoint(firstVisiblePt
.x
, firstY
))), m_ctrl
->GetScaledSize(wxSize(clientSize
.x
, lastY
- firstY
)));
10579 m_ctrl
->RefreshRect(rect
);
10583 m_ctrl
->Refresh(false);
10585 m_ctrl
->PositionCaret();
10587 // This causes styles to persist when doing programmatic
10588 // content creation except when Freeze/Thaw is used, so
10589 // disable this and check for the consequences.
10590 // m_ctrl->SetDefaultStyleToCursorStyle();
10592 if (sendUpdateEvent
)
10593 wxTextCtrl::SendTextUpdatedEvent(m_ctrl
);
10598 /// Replace the buffer paragraphs with the new ones.
10599 void wxRichTextAction::ApplyParagraphs(const wxRichTextParagraphLayoutBox
& fragment
)
10601 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10602 wxASSERT(container
!= NULL
);
10606 wxRichTextObjectList::compatibility_iterator node
= fragment
.GetChildren().GetFirst();
10609 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
10610 wxASSERT (para
!= NULL
);
10612 // We'll replace the existing paragraph by finding the paragraph at this position,
10613 // delete its node data, and setting a copy as the new node data.
10614 // TODO: make more efficient by simply swapping old and new paragraph objects.
10616 wxRichTextParagraph
* existingPara
= container
->GetParagraphAtPosition(para
->GetRange().GetStart());
10619 wxRichTextObjectList::compatibility_iterator bufferParaNode
= container
->GetChildren().Find(existingPara
);
10620 if (bufferParaNode
)
10622 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(*para
);
10623 newPara
->SetParent(container
);
10625 bufferParaNode
->SetData(newPara
);
10627 delete existingPara
;
10631 node
= node
->GetNext();
10638 * This stores beginning and end positions for a range of data.
10641 WX_DEFINE_OBJARRAY(wxRichTextRangeArray
);
10643 /// Limit this range to be within 'range'
10644 bool wxRichTextRange::LimitTo(const wxRichTextRange
& range
)
10646 if (m_start
< range
.m_start
)
10647 m_start
= range
.m_start
;
10649 if (m_end
> range
.m_end
)
10650 m_end
= range
.m_end
;
10656 * wxRichTextImage implementation
10657 * This object represents an image.
10660 IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage
, wxRichTextObject
)
10662 wxRichTextImage::wxRichTextImage(const wxImage
& image
, wxRichTextObject
* parent
, wxRichTextAttr
* charStyle
):
10663 wxRichTextObject(parent
)
10666 m_imageBlock
.MakeImageBlockDefaultQuality(image
, wxBITMAP_TYPE_PNG
);
10668 SetAttributes(*charStyle
);
10671 wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock
& imageBlock
, wxRichTextObject
* parent
, wxRichTextAttr
* charStyle
):
10672 wxRichTextObject(parent
)
10675 m_imageBlock
= imageBlock
;
10677 SetAttributes(*charStyle
);
10680 void wxRichTextImage::Init()
10682 m_originalImageSize
= wxSize(-1, -1);
10685 /// Create a cached image at the required size
10686 bool wxRichTextImage::LoadImageCache(wxDC
& dc
, bool resetCache
)
10688 if (!m_imageBlock
.IsOk())
10691 // If we have an original image size, use that to compute the cached bitmap size
10692 // instead of loading the image each time. This way we can avoid loading
10693 // the image so long as the new cached bitmap size hasn't changed.
10696 if (resetCache
|| m_originalImageSize
== wxSize(-1, -1))
10698 m_imageCache
= wxNullBitmap
;
10700 m_imageBlock
.Load(image
);
10704 m_originalImageSize
= wxSize(image
.GetWidth(), image
.GetHeight());
10707 int width
= m_originalImageSize
.GetWidth();
10708 int height
= m_originalImageSize
.GetHeight();
10710 int parentWidth
= 0;
10711 int parentHeight
= 0;
10714 int maxHeight
= -1;
10716 wxRichTextBuffer
* buffer
= GetBuffer();
10720 if (buffer
->GetRichTextCtrl())
10722 // Subtract borders
10723 sz
= buffer
->GetRichTextCtrl()->GetClientSize();
10725 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
10726 marginRect
= wxRect(0, 0, sz
.x
, sz
.y
);
10727 buffer
->GetBoxRects(dc
, buffer
, buffer
->GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
10729 sz
= contentRect
.GetSize();
10731 // Start with a maximum width of the control size, even if not specified by the content,
10732 // to minimize the amount of picture overlapping the right-hand side
10736 sz
= buffer
->GetCachedSize();
10737 parentWidth
= sz
.GetWidth();
10738 parentHeight
= sz
.GetHeight();
10741 if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetWidth().GetValue() > 0)
10743 if (parentWidth
> 0 && GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
10744 width
= (int) ((GetAttributes().GetTextBoxAttr().GetWidth().GetValue() * parentWidth
)/100.0);
10745 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
10746 width
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetWidth().GetValue());
10747 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
10748 width
= GetAttributes().GetTextBoxAttr().GetWidth().GetValue();
10751 // Limit to max width
10753 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() > 0)
10757 if (parentWidth
> 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
10758 mw
= (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() * parentWidth
)/100.0);
10759 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
10760 mw
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue());
10761 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
10762 mw
= GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue();
10764 // If we already have a smaller max width due to the constraints of the control size,
10765 // don't use the larger max width.
10766 if (mw
!= -1 && ((maxWidth
== -1) || (mw
< maxWidth
)))
10770 if (maxWidth
> 0 && width
> maxWidth
)
10773 // Preserve the aspect ratio
10774 if (width
!= m_originalImageSize
.GetWidth())
10775 height
= (int) (float(m_originalImageSize
.GetHeight()) * (float(width
)/float(m_originalImageSize
.GetWidth())));
10777 if (GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetHeight().GetValue() > 0)
10779 if (parentHeight
> 0 && GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
10780 height
= (int) ((GetAttributes().GetTextBoxAttr().GetHeight().GetValue() * parentHeight
)/100.0);
10781 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
10782 height
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetHeight().GetValue());
10783 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
10784 height
= GetAttributes().GetTextBoxAttr().GetHeight().GetValue();
10786 // Preserve the aspect ratio
10787 if (height
!= m_originalImageSize
.GetHeight())
10788 width
= (int) (float(m_originalImageSize
.GetWidth()) * (float(height
)/float(m_originalImageSize
.GetHeight())));
10791 // Limit to max height
10793 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() > 0)
10795 if (parentHeight
> 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
10796 maxHeight
= (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() * parentHeight
)/100.0);
10797 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
10798 maxHeight
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue());
10799 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
10800 maxHeight
= GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue();
10803 if (maxHeight
> 0 && height
> maxHeight
)
10805 height
= maxHeight
;
10807 // Preserve the aspect ratio
10808 if (height
!= m_originalImageSize
.GetHeight())
10809 width
= (int) (float(m_originalImageSize
.GetWidth()) * (float(height
)/float(m_originalImageSize
.GetHeight())));
10812 if (m_imageCache
.IsOk() && m_imageCache
.GetWidth() == width
&& m_imageCache
.GetHeight() == height
)
10814 // Do nothing, we didn't need to change the image cache
10820 m_imageBlock
.Load(image
);
10825 if (image
.GetWidth() == width
&& image
.GetHeight() == height
)
10826 m_imageCache
= wxBitmap(image
);
10829 // If the original width and height is small, e.g. 400 or below,
10830 // scale up and then down to improve image quality. This can make
10831 // a big difference, with not much performance hit.
10832 int upscaleThreshold
= 400;
10834 if (image
.GetWidth() <= upscaleThreshold
|| image
.GetHeight() <= upscaleThreshold
)
10836 img
= image
.Scale(image
.GetWidth()*2, image
.GetHeight()*2);
10837 img
.Rescale(width
, height
, wxIMAGE_QUALITY_HIGH
);
10840 img
= image
.Scale(width
, height
, wxIMAGE_QUALITY_HIGH
);
10841 m_imageCache
= wxBitmap(img
);
10845 return m_imageCache
.IsOk();
10849 bool wxRichTextImage::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& WXUNUSED(range
), const wxRichTextSelection
& selection
, const wxRect
& rect
, int WXUNUSED(descent
), int WXUNUSED(style
))
10854 // Don't need cached size AFAIK
10855 // wxSize size = GetCachedSize();
10856 if (!LoadImageCache(dc
))
10859 wxRichTextAttr
attr(GetAttributes());
10860 context
.ApplyVirtualAttributes(attr
, this);
10862 DrawBoxAttributes(dc
, GetBuffer(), attr
, wxRect(rect
.GetPosition(), GetCachedSize()));
10864 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
10865 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
10866 marginRect
= rect
; // outer rectangle, will calculate contentRect
10867 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
10869 dc
.DrawBitmap(m_imageCache
, contentRect
.x
, contentRect
.y
, true);
10871 if (selection
.WithinSelection(GetRange().GetStart(), this))
10873 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
10874 wxCheckSetPen(dc
, *wxBLACK_PEN
);
10875 dc
.SetLogicalFunction(wxINVERT
);
10876 dc
.DrawRectangle(contentRect
);
10877 dc
.SetLogicalFunction(wxCOPY
);
10883 /// Lay the item out
10884 bool wxRichTextImage::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& WXUNUSED(parentRect
), int WXUNUSED(style
))
10886 if (!LoadImageCache(dc
))
10889 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
10890 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
10891 contentRect
= wxRect(wxPoint(0,0), imageSize
);
10893 wxRichTextAttr
attr(GetAttributes());
10894 context
.ApplyVirtualAttributes(attr
, this);
10896 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
10898 wxSize overallSize
= marginRect
.GetSize();
10900 SetCachedSize(overallSize
);
10901 SetMaxSize(overallSize
);
10902 SetMinSize(overallSize
);
10903 SetPosition(rect
.GetPosition());
10908 /// Get/set the object size for the given range. Returns false if the range
10909 /// is invalid for this object.
10910 bool wxRichTextImage::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& WXUNUSED(descent
), wxDC
& dc
, wxRichTextDrawingContext
& context
, int WXUNUSED(flags
), wxPoint
WXUNUSED(position
), wxArrayInt
* partialExtents
) const
10912 if (!range
.IsWithin(GetRange()))
10915 if (!((wxRichTextImage
*)this)->LoadImageCache(dc
))
10917 size
.x
= 0; size
.y
= 0;
10918 if (partialExtents
)
10919 partialExtents
->Add(0);
10923 wxRichTextAttr
attr(GetAttributes());
10924 context
.ApplyVirtualAttributes(attr
, (wxRichTextObject
*) this);
10926 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
10927 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
10928 contentRect
= wxRect(wxPoint(0,0), imageSize
);
10929 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
10931 wxSize overallSize
= marginRect
.GetSize();
10933 if (partialExtents
)
10934 partialExtents
->Add(overallSize
.x
);
10936 size
= overallSize
;
10941 // Get the 'natural' size for an object. For an image, it would be the
10943 wxTextAttrSize
wxRichTextImage::GetNaturalSize() const
10945 wxTextAttrSize size
;
10946 if (GetImageCache().IsOk())
10948 size
.SetWidth(GetImageCache().GetWidth(), wxTEXT_ATTR_UNITS_PIXELS
);
10949 size
.SetHeight(GetImageCache().GetHeight(), wxTEXT_ATTR_UNITS_PIXELS
);
10956 void wxRichTextImage::Copy(const wxRichTextImage
& obj
)
10958 wxRichTextObject::Copy(obj
);
10960 m_imageBlock
= obj
.m_imageBlock
;
10961 m_originalImageSize
= obj
.m_originalImageSize
;
10964 /// Edit properties via a GUI
10965 bool wxRichTextImage::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
10967 wxRichTextObjectPropertiesDialog
imageDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Picture Properties"));
10968 imageDlg
.SetAttributes(GetAttributes());
10970 if (imageDlg
.ShowModal() == wxID_OK
)
10972 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
10973 // indeterminate in the object.
10974 imageDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
10986 /// Compare two attribute objects
10987 bool wxTextAttrEq(const wxRichTextAttr
& attr1
, const wxRichTextAttr
& attr2
)
10989 return (attr1
== attr2
);
10993 bool wxRichTextTabsEq(const wxArrayInt
& tabs1
, const wxArrayInt
& tabs2
)
10995 if (tabs1
.GetCount() != tabs2
.GetCount())
10999 for (i
= 0; i
< tabs1
.GetCount(); i
++)
11001 if (tabs1
[i
] != tabs2
[i
])
11007 bool wxRichTextApplyStyle(wxRichTextAttr
& destStyle
, const wxRichTextAttr
& style
, wxRichTextAttr
* compareWith
)
11009 return destStyle
.Apply(style
, compareWith
);
11012 // Remove attributes
11013 bool wxRichTextRemoveStyle(wxRichTextAttr
& destStyle
, const wxRichTextAttr
& style
)
11015 return destStyle
.RemoveStyle(style
);
11018 /// Combine two bitlists, specifying the bits of interest with separate flags.
11019 bool wxRichTextCombineBitlists(int& valueA
, int valueB
, int& flagsA
, int flagsB
)
11021 return wxRichTextAttr::CombineBitlists(valueA
, valueB
, flagsA
, flagsB
);
11024 /// Compare two bitlists
11025 bool wxRichTextBitlistsEqPartial(int valueA
, int valueB
, int flags
)
11027 return wxRichTextAttr::BitlistsEqPartial(valueA
, valueB
, flags
);
11030 /// Split into paragraph and character styles
11031 bool wxRichTextSplitParaCharStyles(const wxRichTextAttr
& style
, wxRichTextAttr
& parStyle
, wxRichTextAttr
& charStyle
)
11033 return wxRichTextAttr::SplitParaCharStyles(style
, parStyle
, charStyle
);
11036 /// Convert a decimal to Roman numerals
11037 wxString
wxRichTextDecimalToRoman(long n
)
11039 static wxArrayInt decimalNumbers
;
11040 static wxArrayString romanNumbers
;
11045 decimalNumbers
.Clear();
11046 romanNumbers
.Clear();
11047 return wxEmptyString
;
11050 if (decimalNumbers
.GetCount() == 0)
11052 #define wxRichTextAddDecRom(n, r) decimalNumbers.Add(n); romanNumbers.Add(r);
11054 wxRichTextAddDecRom(1000, wxT("M"));
11055 wxRichTextAddDecRom(900, wxT("CM"));
11056 wxRichTextAddDecRom(500, wxT("D"));
11057 wxRichTextAddDecRom(400, wxT("CD"));
11058 wxRichTextAddDecRom(100, wxT("C"));
11059 wxRichTextAddDecRom(90, wxT("XC"));
11060 wxRichTextAddDecRom(50, wxT("L"));
11061 wxRichTextAddDecRom(40, wxT("XL"));
11062 wxRichTextAddDecRom(10, wxT("X"));
11063 wxRichTextAddDecRom(9, wxT("IX"));
11064 wxRichTextAddDecRom(5, wxT("V"));
11065 wxRichTextAddDecRom(4, wxT("IV"));
11066 wxRichTextAddDecRom(1, wxT("I"));
11072 while (n
> 0 && i
< 13)
11074 if (n
>= decimalNumbers
[i
])
11076 n
-= decimalNumbers
[i
];
11077 roman
+= romanNumbers
[i
];
11084 if (roman
.IsEmpty())
11090 * wxRichTextFileHandler
11091 * Base class for file handlers
11094 IMPLEMENT_CLASS(wxRichTextFileHandler
, wxObject
)
11096 #if wxUSE_FFILE && wxUSE_STREAMS
11097 bool wxRichTextFileHandler::LoadFile(wxRichTextBuffer
*buffer
, const wxString
& filename
)
11099 wxFFileInputStream
stream(filename
);
11101 return LoadFile(buffer
, stream
);
11106 bool wxRichTextFileHandler::SaveFile(wxRichTextBuffer
*buffer
, const wxString
& filename
)
11108 wxFFileOutputStream
stream(filename
);
11110 return SaveFile(buffer
, stream
);
11114 #endif // wxUSE_FFILE && wxUSE_STREAMS
11116 /// Can we handle this filename (if using files)? By default, checks the extension.
11117 bool wxRichTextFileHandler::CanHandle(const wxString
& filename
) const
11119 wxString path
, file
, ext
;
11120 wxFileName::SplitPath(filename
, & path
, & file
, & ext
);
11122 return (ext
.Lower() == GetExtension());
11126 * wxRichTextTextHandler
11127 * Plain text handler
11130 IMPLEMENT_CLASS(wxRichTextPlainTextHandler
, wxRichTextFileHandler
)
11133 bool wxRichTextPlainTextHandler::DoLoadFile(wxRichTextBuffer
*buffer
, wxInputStream
& stream
)
11135 if (!stream
.IsOk())
11141 while (!stream
.Eof())
11143 int ch
= stream
.GetC();
11147 if (ch
== 10 && lastCh
!= 13)
11150 if (ch
> 0 && ch
!= 10)
11157 buffer
->ResetAndClearCommands();
11159 buffer
->AddParagraphs(str
);
11160 buffer
->UpdateRanges();
11165 bool wxRichTextPlainTextHandler::DoSaveFile(wxRichTextBuffer
*buffer
, wxOutputStream
& stream
)
11167 if (!stream
.IsOk())
11170 wxString text
= buffer
->GetText();
11172 wxString newLine
= wxRichTextLineBreakChar
;
11173 text
.Replace(newLine
, wxT("\n"));
11175 wxCharBuffer buf
= text
.ToAscii();
11177 stream
.Write((const char*) buf
, text
.length());
11180 #endif // wxUSE_STREAMS
11183 * Stores information about an image, in binary in-memory form
11186 wxRichTextImageBlock::wxRichTextImageBlock()
11191 wxRichTextImageBlock::wxRichTextImageBlock(const wxRichTextImageBlock
& block
):wxObject()
11197 wxRichTextImageBlock::~wxRichTextImageBlock()
11202 void wxRichTextImageBlock::Init()
11206 m_imageType
= wxBITMAP_TYPE_INVALID
;
11209 void wxRichTextImageBlock::Clear()
11213 m_imageType
= wxBITMAP_TYPE_INVALID
;
11217 // Load the original image into a memory block.
11218 // If the image is not a JPEG, we must convert it into a JPEG
11219 // to conserve space.
11220 // If it's not a JPEG we can make use of 'image', already scaled, so we don't have to
11221 // load the image a 2nd time.
11223 bool wxRichTextImageBlock::MakeImageBlock(const wxString
& filename
, wxBitmapType imageType
,
11224 wxImage
& image
, bool convertToJPEG
)
11226 m_imageType
= imageType
;
11228 wxString
filenameToRead(filename
);
11229 bool removeFile
= false;
11231 if (imageType
== wxBITMAP_TYPE_INVALID
)
11232 return false; // Could not determine image type
11234 if ((imageType
!= wxBITMAP_TYPE_JPEG
) && convertToJPEG
)
11236 wxString tempFile
=
11237 wxFileName::CreateTempFileName(_("image"));
11239 wxASSERT(!tempFile
.IsEmpty());
11241 image
.SaveFile(tempFile
, wxBITMAP_TYPE_JPEG
);
11242 filenameToRead
= tempFile
;
11245 m_imageType
= wxBITMAP_TYPE_JPEG
;
11248 if (!file
.Open(filenameToRead
))
11251 m_dataSize
= (size_t) file
.Length();
11256 m_data
= ReadBlock(filenameToRead
, m_dataSize
);
11259 wxRemoveFile(filenameToRead
);
11261 return (m_data
!= NULL
);
11264 // Make an image block from the wxImage in the given
11266 bool wxRichTextImageBlock::MakeImageBlock(wxImage
& image
, wxBitmapType imageType
, int quality
)
11268 image
.SetOption(wxT("quality"), quality
);
11270 if (imageType
== wxBITMAP_TYPE_INVALID
)
11271 return false; // Could not determine image type
11273 return DoMakeImageBlock(image
, imageType
);
11276 // Uses a const wxImage for efficiency, but can't set quality (only relevant for JPEG)
11277 bool wxRichTextImageBlock::MakeImageBlockDefaultQuality(const wxImage
& image
, wxBitmapType imageType
)
11279 if (imageType
== wxBITMAP_TYPE_INVALID
)
11280 return false; // Could not determine image type
11282 return DoMakeImageBlock(image
, imageType
);
11285 // Makes the image block
11286 bool wxRichTextImageBlock::DoMakeImageBlock(const wxImage
& image
, wxBitmapType imageType
)
11288 wxMemoryOutputStream memStream
;
11289 if (!image
.SaveFile(memStream
, imageType
))
11294 unsigned char* block
= new unsigned char[memStream
.GetSize()];
11302 m_imageType
= imageType
;
11303 m_dataSize
= memStream
.GetSize();
11305 memStream
.CopyTo(m_data
, m_dataSize
);
11307 return (m_data
!= NULL
);
11311 bool wxRichTextImageBlock::Write(const wxString
& filename
)
11313 return WriteBlock(filename
, m_data
, m_dataSize
);
11316 void wxRichTextImageBlock::Copy(const wxRichTextImageBlock
& block
)
11318 m_imageType
= block
.m_imageType
;
11320 m_dataSize
= block
.m_dataSize
;
11321 if (m_dataSize
== 0)
11324 m_data
= new unsigned char[m_dataSize
];
11326 for (i
= 0; i
< m_dataSize
; i
++)
11327 m_data
[i
] = block
.m_data
[i
];
11331 void wxRichTextImageBlock::operator=(const wxRichTextImageBlock
& block
)
11336 // Load a wxImage from the block
11337 bool wxRichTextImageBlock::Load(wxImage
& image
)
11342 // Read in the image.
11344 wxMemoryInputStream
mstream(m_data
, m_dataSize
);
11345 bool success
= image
.LoadFile(mstream
, GetImageType());
11347 wxString tempFile
= wxFileName::CreateTempFileName(_("image"));
11348 wxASSERT(!tempFile
.IsEmpty());
11350 if (!WriteBlock(tempFile
, m_data
, m_dataSize
))
11354 success
= image
.LoadFile(tempFile
, GetImageType());
11355 wxRemoveFile(tempFile
);
11361 // Write data in hex to a stream
11362 bool wxRichTextImageBlock::WriteHex(wxOutputStream
& stream
)
11364 if (m_dataSize
== 0)
11367 int bufSize
= 100000;
11368 if (int(2*m_dataSize
) < bufSize
)
11369 bufSize
= 2*m_dataSize
;
11370 char* buf
= new char[bufSize
+1];
11372 int left
= m_dataSize
;
11377 if (left
*2 > bufSize
)
11379 n
= bufSize
; left
-= (bufSize
/2);
11383 n
= left
*2; left
= 0;
11387 for (i
= 0; i
< (n
/2); i
++)
11389 wxDecToHex(m_data
[j
], b
, b
+1);
11394 stream
.Write((const char*) buf
, n
);
11400 // Read data in hex from a stream
11401 bool wxRichTextImageBlock::ReadHex(wxInputStream
& stream
, int length
, wxBitmapType imageType
)
11403 int dataSize
= length
/2;
11408 // create a null terminated temporary string:
11412 m_data
= new unsigned char[dataSize
];
11414 for (i
= 0; i
< dataSize
; i
++)
11416 str
[0] = (char)stream
.GetC();
11417 str
[1] = (char)stream
.GetC();
11419 m_data
[i
] = (unsigned char)wxHexToDec(str
);
11422 m_dataSize
= dataSize
;
11423 m_imageType
= imageType
;
11428 // Allocate and read from stream as a block of memory
11429 unsigned char* wxRichTextImageBlock::ReadBlock(wxInputStream
& stream
, size_t size
)
11431 unsigned char* block
= new unsigned char[size
];
11435 stream
.Read(block
, size
);
11440 unsigned char* wxRichTextImageBlock::ReadBlock(const wxString
& filename
, size_t size
)
11442 wxFileInputStream
stream(filename
);
11443 if (!stream
.IsOk())
11446 return ReadBlock(stream
, size
);
11449 // Write memory block to stream
11450 bool wxRichTextImageBlock::WriteBlock(wxOutputStream
& stream
, unsigned char* block
, size_t size
)
11452 stream
.Write((void*) block
, size
);
11453 return stream
.IsOk();
11457 // Write memory block to file
11458 bool wxRichTextImageBlock::WriteBlock(const wxString
& filename
, unsigned char* block
, size_t size
)
11460 wxFileOutputStream
outStream(filename
);
11461 if (!outStream
.IsOk())
11464 return WriteBlock(outStream
, block
, size
);
11467 // Gets the extension for the block's type
11468 wxString
wxRichTextImageBlock::GetExtension() const
11470 wxImageHandler
* handler
= wxImage::FindHandler(GetImageType());
11472 return handler
->GetExtension();
11474 return wxEmptyString
;
11480 * The data object for a wxRichTextBuffer
11483 const wxChar
*wxRichTextBufferDataObject::ms_richTextBufferFormatId
= wxT("wxShape");
11485 wxRichTextBufferDataObject::wxRichTextBufferDataObject(wxRichTextBuffer
* richTextBuffer
)
11487 m_richTextBuffer
= richTextBuffer
;
11489 // this string should uniquely identify our format, but is otherwise
11491 m_formatRichTextBuffer
.SetId(GetRichTextBufferFormatId());
11493 SetFormat(m_formatRichTextBuffer
);
11496 wxRichTextBufferDataObject::~wxRichTextBufferDataObject()
11498 delete m_richTextBuffer
;
11501 // after a call to this function, the richTextBuffer is owned by the caller and it
11502 // is responsible for deleting it!
11503 wxRichTextBuffer
* wxRichTextBufferDataObject::GetRichTextBuffer()
11505 wxRichTextBuffer
* richTextBuffer
= m_richTextBuffer
;
11506 m_richTextBuffer
= NULL
;
11508 return richTextBuffer
;
11511 wxDataFormat
wxRichTextBufferDataObject::GetPreferredFormat(Direction
WXUNUSED(dir
)) const
11513 return m_formatRichTextBuffer
;
11516 size_t wxRichTextBufferDataObject::GetDataSize() const
11518 if (!m_richTextBuffer
)
11524 wxStringOutputStream
stream(& bufXML
);
11525 if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
))
11527 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
11533 wxCharBuffer buffer
= bufXML
.mb_str(wxConvUTF8
);
11534 return strlen(buffer
) + 1;
11536 return bufXML
.Length()+1;
11540 bool wxRichTextBufferDataObject::GetDataHere(void *pBuf
) const
11542 if (!pBuf
|| !m_richTextBuffer
)
11548 wxStringOutputStream
stream(& bufXML
);
11549 if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
))
11551 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
11557 wxCharBuffer buffer
= bufXML
.mb_str(wxConvUTF8
);
11558 size_t len
= strlen(buffer
);
11559 memcpy((char*) pBuf
, (const char*) buffer
, len
);
11560 ((char*) pBuf
)[len
] = 0;
11562 size_t len
= bufXML
.Length();
11563 memcpy((char*) pBuf
, (const char*) bufXML
.c_str(), len
);
11564 ((char*) pBuf
)[len
] = 0;
11570 bool wxRichTextBufferDataObject::SetData(size_t WXUNUSED(len
), const void *buf
)
11572 wxDELETE(m_richTextBuffer
);
11574 wxString
bufXML((const char*) buf
, wxConvUTF8
);
11576 m_richTextBuffer
= new wxRichTextBuffer
;
11578 wxStringInputStream
stream(bufXML
);
11579 if (!m_richTextBuffer
->LoadFile(stream
, wxRICHTEXT_TYPE_XML
))
11581 wxLogError(wxT("Could not read the buffer from an XML stream.\nYou may have forgotten to add the XML file handler."));
11583 wxDELETE(m_richTextBuffer
);
11595 * wxRichTextFontTable
11596 * Manages quick access to a pool of fonts for rendering rich text
11599 WX_DECLARE_STRING_HASH_MAP_WITH_DECL(wxFont
, wxRichTextFontTableHashMap
, class WXDLLIMPEXP_RICHTEXT
);
11601 class wxRichTextFontTableData
: public wxObjectRefData
11604 wxRichTextFontTableData() {}
11606 wxFont
FindFont(const wxRichTextAttr
& fontSpec
, double fontScale
);
11608 wxRichTextFontTableHashMap m_hashMap
;
11611 wxFont
wxRichTextFontTableData::FindFont(const wxRichTextAttr
& fontSpec
, double fontScale
)
11613 wxString
facename(fontSpec
.GetFontFaceName());
11615 int fontSize
= fontSpec
.GetFontSize();
11616 if (fontScale
!= 1.0)
11617 fontSize
= (int) ((double(fontSize
) * fontScale
) + 0.5);
11620 if (fontSpec
.HasFontPixelSize() && !fontSpec
.HasFontPointSize())
11624 wxString spec
= wxString::Format(wxT("%d-%s-%d-%d-%d-%d-%s-%d"),
11625 fontSize
, units
.c_str(), fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), (int) fontSpec
.GetFontUnderlined(), (int) fontSpec
.GetFontStrikethrough(),
11626 facename
.c_str(), (int) fontSpec
.GetFontEncoding());
11628 wxRichTextFontTableHashMap::iterator entry
= m_hashMap
.find(spec
);
11629 if ( entry
== m_hashMap
.end() )
11631 if (fontSpec
.HasFontPixelSize() && !fontSpec
.HasFontPointSize())
11633 wxFont
font(wxSize(0, fontSize
), wxFONTFAMILY_DEFAULT
, fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), fontSpec
.GetFontUnderlined(), facename
);
11634 if (fontSpec
.HasFontStrikethrough() && fontSpec
.GetFontStrikethrough())
11635 font
.SetStrikethrough(true);
11636 m_hashMap
[spec
] = font
;
11641 wxFont
font(fontSize
, wxFONTFAMILY_DEFAULT
, fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), fontSpec
.GetFontUnderlined(), facename
.c_str());
11642 if (fontSpec
.HasFontStrikethrough() && fontSpec
.GetFontStrikethrough())
11643 font
.SetStrikethrough(true);
11645 m_hashMap
[spec
] = font
;
11651 return entry
->second
;
11655 IMPLEMENT_DYNAMIC_CLASS(wxRichTextFontTable
, wxObject
)
11657 wxRichTextFontTable::wxRichTextFontTable()
11659 m_refData
= new wxRichTextFontTableData
;
11663 wxRichTextFontTable::wxRichTextFontTable(const wxRichTextFontTable
& table
)
11669 wxRichTextFontTable::~wxRichTextFontTable()
11674 bool wxRichTextFontTable::operator == (const wxRichTextFontTable
& table
) const
11676 return (m_refData
== table
.m_refData
);
11679 void wxRichTextFontTable::operator= (const wxRichTextFontTable
& table
)
11682 m_fontScale
= table
.m_fontScale
;
11685 wxFont
wxRichTextFontTable::FindFont(const wxRichTextAttr
& fontSpec
)
11687 wxRichTextFontTableData
* data
= (wxRichTextFontTableData
*) m_refData
;
11689 return data
->FindFont(fontSpec
, m_fontScale
);
11694 void wxRichTextFontTable::Clear()
11696 wxRichTextFontTableData
* data
= (wxRichTextFontTableData
*) m_refData
;
11698 data
->m_hashMap
.clear();
11701 void wxRichTextFontTable::SetFontScale(double fontScale
)
11703 if (fontScale
!= m_fontScale
)
11705 m_fontScale
= fontScale
;
11710 void wxTextBoxAttr::Reset()
11713 m_floatMode
= wxTEXT_BOX_ATTR_FLOAT_NONE
;
11714 m_clearMode
= wxTEXT_BOX_ATTR_CLEAR_NONE
;
11715 m_collapseMode
= wxTEXT_BOX_ATTR_COLLAPSE_NONE
;
11716 m_verticalAlignment
= wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_NONE
;
11717 m_boxStyleName
= wxEmptyString
;
11721 m_position
.Reset();
11732 bool wxTextBoxAttr::operator== (const wxTextBoxAttr
& attr
) const
11735 m_flags
== attr
.m_flags
&&
11736 m_floatMode
== attr
.m_floatMode
&&
11737 m_clearMode
== attr
.m_clearMode
&&
11738 m_collapseMode
== attr
.m_collapseMode
&&
11739 m_verticalAlignment
== attr
.m_verticalAlignment
&&
11741 m_margins
== attr
.m_margins
&&
11742 m_padding
== attr
.m_padding
&&
11743 m_position
== attr
.m_position
&&
11745 m_size
== attr
.m_size
&&
11746 m_minSize
== attr
.m_minSize
&&
11747 m_maxSize
== attr
.m_maxSize
&&
11749 m_border
== attr
.m_border
&&
11750 m_outline
== attr
.m_outline
&&
11752 m_boxStyleName
== attr
.m_boxStyleName
11756 // Partial equality test
11757 bool wxTextBoxAttr::EqPartial(const wxTextBoxAttr
& attr
, bool weakTest
) const
11760 ((!HasFloatMode() && attr
.HasFloatMode()) ||
11761 (!HasClearMode() && attr
.HasClearMode()) ||
11762 (!HasCollapseBorders() && attr
.HasCollapseBorders()) ||
11763 (!HasVerticalAlignment() && attr
.HasVerticalAlignment()) ||
11764 (!HasBoxStyleName() && attr
.HasBoxStyleName())))
11768 if (attr
.HasFloatMode() && HasFloatMode() && (GetFloatMode() != attr
.GetFloatMode()))
11771 if (attr
.HasClearMode() && HasClearMode() && (GetClearMode() != attr
.GetClearMode()))
11774 if (attr
.HasCollapseBorders() && HasCollapseBorders() && (attr
.GetCollapseBorders() != GetCollapseBorders()))
11777 if (attr
.HasVerticalAlignment() && HasVerticalAlignment() && (attr
.GetVerticalAlignment() != GetVerticalAlignment()))
11780 if (attr
.HasBoxStyleName() && HasBoxStyleName() && (attr
.GetBoxStyleName() != GetBoxStyleName()))
11785 if (!m_position
.EqPartial(attr
.m_position
, weakTest
))
11790 if (!m_size
.EqPartial(attr
.m_size
, weakTest
))
11792 if (!m_minSize
.EqPartial(attr
.m_minSize
, weakTest
))
11794 if (!m_maxSize
.EqPartial(attr
.m_maxSize
, weakTest
))
11799 if (!m_margins
.EqPartial(attr
.m_margins
, weakTest
))
11804 if (!m_padding
.EqPartial(attr
.m_padding
, weakTest
))
11809 if (!GetBorder().EqPartial(attr
.GetBorder(), weakTest
))
11814 if (!GetOutline().EqPartial(attr
.GetOutline(), weakTest
))
11820 // Merges the given attributes. If compareWith
11821 // is non-NULL, then it will be used to mask out those attributes that are the same in style
11822 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
11823 bool wxTextBoxAttr::Apply(const wxTextBoxAttr
& attr
, const wxTextBoxAttr
* compareWith
)
11825 if (attr
.HasFloatMode())
11827 if (!(compareWith
&& compareWith
->HasFloatMode() && compareWith
->GetFloatMode() == attr
.GetFloatMode()))
11828 SetFloatMode(attr
.GetFloatMode());
11831 if (attr
.HasClearMode())
11833 if (!(compareWith
&& compareWith
->HasClearMode() && compareWith
->GetClearMode() == attr
.GetClearMode()))
11834 SetClearMode(attr
.GetClearMode());
11837 if (attr
.HasCollapseBorders())
11839 if (!(compareWith
&& compareWith
->HasCollapseBorders() && compareWith
->GetCollapseBorders() == attr
.GetCollapseBorders()))
11840 SetCollapseBorders(attr
.GetCollapseBorders());
11843 if (attr
.HasVerticalAlignment())
11845 if (!(compareWith
&& compareWith
->HasVerticalAlignment() && compareWith
->GetVerticalAlignment() == attr
.GetVerticalAlignment()))
11846 SetVerticalAlignment(attr
.GetVerticalAlignment());
11849 if (attr
.HasBoxStyleName())
11851 if (!(compareWith
&& compareWith
->HasBoxStyleName() && compareWith
->GetBoxStyleName() == attr
.GetBoxStyleName()))
11852 SetBoxStyleName(attr
.GetBoxStyleName());
11855 m_margins
.Apply(attr
.m_margins
, compareWith
? (& attr
.m_margins
) : (const wxTextAttrDimensions
*) NULL
);
11856 m_padding
.Apply(attr
.m_padding
, compareWith
? (& attr
.m_padding
) : (const wxTextAttrDimensions
*) NULL
);
11857 m_position
.Apply(attr
.m_position
, compareWith
? (& attr
.m_position
) : (const wxTextAttrDimensions
*) NULL
);
11859 m_size
.Apply(attr
.m_size
, compareWith
? (& attr
.m_size
) : (const wxTextAttrSize
*) NULL
);
11860 m_minSize
.Apply(attr
.m_minSize
, compareWith
? (& attr
.m_minSize
) : (const wxTextAttrSize
*) NULL
);
11861 m_maxSize
.Apply(attr
.m_maxSize
, compareWith
? (& attr
.m_maxSize
) : (const wxTextAttrSize
*) NULL
);
11863 m_border
.Apply(attr
.m_border
, compareWith
? (& attr
.m_border
) : (const wxTextAttrBorders
*) NULL
);
11864 m_outline
.Apply(attr
.m_outline
, compareWith
? (& attr
.m_outline
) : (const wxTextAttrBorders
*) NULL
);
11869 // Remove specified attributes from this object
11870 bool wxTextBoxAttr::RemoveStyle(const wxTextBoxAttr
& attr
)
11872 if (attr
.HasFloatMode())
11873 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT
);
11875 if (attr
.HasClearMode())
11876 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR
);
11878 if (attr
.HasCollapseBorders())
11879 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
11881 if (attr
.HasVerticalAlignment())
11882 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
11884 if (attr
.HasBoxStyleName())
11886 SetBoxStyleName(wxEmptyString
);
11887 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
11890 m_margins
.RemoveStyle(attr
.m_margins
);
11891 m_padding
.RemoveStyle(attr
.m_padding
);
11892 m_position
.RemoveStyle(attr
.m_position
);
11894 m_size
.RemoveStyle(attr
.m_size
);
11895 m_minSize
.RemoveStyle(attr
.m_minSize
);
11896 m_maxSize
.RemoveStyle(attr
.m_maxSize
);
11898 m_border
.RemoveStyle(attr
.m_border
);
11899 m_outline
.RemoveStyle(attr
.m_outline
);
11904 // Collects the attributes that are common to a range of content, building up a note of
11905 // which attributes are absent in some objects and which clash in some objects.
11906 void wxTextBoxAttr::CollectCommonAttributes(const wxTextBoxAttr
& attr
, wxTextBoxAttr
& clashingAttr
, wxTextBoxAttr
& absentAttr
)
11908 if (attr
.HasFloatMode())
11910 if (!clashingAttr
.HasFloatMode() && !absentAttr
.HasFloatMode())
11912 if (HasFloatMode())
11914 if (GetFloatMode() != attr
.GetFloatMode())
11916 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_FLOAT
);
11917 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT
);
11921 SetFloatMode(attr
.GetFloatMode());
11925 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_FLOAT
);
11927 if (attr
.HasClearMode())
11929 if (!clashingAttr
.HasClearMode() && !absentAttr
.HasClearMode())
11931 if (HasClearMode())
11933 if (GetClearMode() != attr
.GetClearMode())
11935 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_CLEAR
);
11936 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR
);
11940 SetClearMode(attr
.GetClearMode());
11944 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_CLEAR
);
11946 if (attr
.HasCollapseBorders())
11948 if (!clashingAttr
.HasCollapseBorders() && !absentAttr
.HasCollapseBorders())
11950 if (HasCollapseBorders())
11952 if (GetCollapseBorders() != attr
.GetCollapseBorders())
11954 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
11955 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
11959 SetCollapseBorders(attr
.GetCollapseBorders());
11963 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
11965 if (attr
.HasVerticalAlignment())
11967 if (!clashingAttr
.HasVerticalAlignment() && !absentAttr
.HasVerticalAlignment())
11969 if (HasVerticalAlignment())
11971 if (GetVerticalAlignment() != attr
.GetVerticalAlignment())
11973 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
11974 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
11978 SetVerticalAlignment(attr
.GetVerticalAlignment());
11982 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
11984 if (attr
.HasBoxStyleName())
11986 if (!clashingAttr
.HasBoxStyleName() && !absentAttr
.HasBoxStyleName())
11988 if (HasBoxStyleName())
11990 if (GetBoxStyleName() != attr
.GetBoxStyleName())
11992 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
11993 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
11997 SetBoxStyleName(attr
.GetBoxStyleName());
12001 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
12003 m_margins
.CollectCommonAttributes(attr
.m_margins
, clashingAttr
.m_margins
, absentAttr
.m_margins
);
12004 m_padding
.CollectCommonAttributes(attr
.m_padding
, clashingAttr
.m_padding
, absentAttr
.m_padding
);
12005 m_position
.CollectCommonAttributes(attr
.m_position
, clashingAttr
.m_position
, absentAttr
.m_position
);
12007 m_size
.CollectCommonAttributes(attr
.m_size
, clashingAttr
.m_size
, absentAttr
.m_size
);
12008 m_minSize
.CollectCommonAttributes(attr
.m_minSize
, clashingAttr
.m_minSize
, absentAttr
.m_minSize
);
12009 m_maxSize
.CollectCommonAttributes(attr
.m_maxSize
, clashingAttr
.m_maxSize
, absentAttr
.m_maxSize
);
12011 m_border
.CollectCommonAttributes(attr
.m_border
, clashingAttr
.m_border
, absentAttr
.m_border
);
12012 m_outline
.CollectCommonAttributes(attr
.m_outline
, clashingAttr
.m_outline
, absentAttr
.m_outline
);
12015 bool wxTextBoxAttr::IsDefault() const
12017 return GetFlags() == 0 && !m_border
.IsValid() && !m_outline
.IsValid() &&
12018 !m_size
.IsValid() && !m_minSize
.IsValid() && !m_maxSize
.IsValid() &&
12019 !m_position
.IsValid() && !m_padding
.IsValid() && !m_margins
.IsValid();
12024 void wxRichTextAttr::Copy(const wxRichTextAttr
& attr
)
12026 wxTextAttr::Copy(attr
);
12028 m_textBoxAttr
= attr
.m_textBoxAttr
;
12031 bool wxRichTextAttr::operator==(const wxRichTextAttr
& attr
) const
12033 if (!(wxTextAttr::operator==(attr
)))
12036 return (m_textBoxAttr
== attr
.m_textBoxAttr
);
12039 // Partial equality test
12040 bool wxRichTextAttr::EqPartial(const wxRichTextAttr
& attr
, bool weakTest
) const
12042 if (!(wxTextAttr::EqPartial(attr
, weakTest
)))
12045 return m_textBoxAttr
.EqPartial(attr
.m_textBoxAttr
, weakTest
);
12048 // Merges the given attributes. If compareWith
12049 // is non-NULL, then it will be used to mask out those attributes that are the same in style
12050 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
12051 bool wxRichTextAttr::Apply(const wxRichTextAttr
& style
, const wxRichTextAttr
* compareWith
)
12053 wxTextAttr::Apply(style
, compareWith
);
12055 return m_textBoxAttr
.Apply(style
.m_textBoxAttr
, compareWith
? (& compareWith
->m_textBoxAttr
) : (const wxTextBoxAttr
*) NULL
);
12058 // Remove specified attributes from this object
12059 bool wxRichTextAttr::RemoveStyle(const wxRichTextAttr
& attr
)
12061 wxTextAttr::RemoveStyle(*this, attr
);
12063 return m_textBoxAttr
.RemoveStyle(attr
.m_textBoxAttr
);
12066 // Collects the attributes that are common to a range of content, building up a note of
12067 // which attributes are absent in some objects and which clash in some objects.
12068 void wxRichTextAttr::CollectCommonAttributes(const wxRichTextAttr
& attr
, wxRichTextAttr
& clashingAttr
, wxRichTextAttr
& absentAttr
)
12070 wxTextAttrCollectCommonAttributes(*this, attr
, clashingAttr
, absentAttr
);
12072 m_textBoxAttr
.CollectCommonAttributes(attr
.m_textBoxAttr
, clashingAttr
.m_textBoxAttr
, absentAttr
.m_textBoxAttr
);
12075 // Partial equality test
12076 bool wxTextAttrBorder::EqPartial(const wxTextAttrBorder
& border
, bool weakTest
) const
12079 ((!HasStyle() && border
.HasStyle()) ||
12080 (!HasColour() && border
.HasColour()) ||
12081 (!HasWidth() && border
.HasWidth())))
12086 if (border
.HasStyle() && HasStyle() && (border
.GetStyle() != GetStyle()))
12089 if (border
.HasColour() && HasColour() && (border
.GetColourLong() != GetColourLong()))
12092 if (border
.HasWidth() && HasWidth() && !(border
.GetWidth() == GetWidth()))
12098 // Apply border to 'this', but not if the same as compareWith
12099 bool wxTextAttrBorder::Apply(const wxTextAttrBorder
& border
, const wxTextAttrBorder
* compareWith
)
12101 if (border
.HasStyle())
12103 if (!(compareWith
&& (border
.GetStyle() == compareWith
->GetStyle())))
12104 SetStyle(border
.GetStyle());
12106 if (border
.HasColour())
12108 if (!(compareWith
&& (border
.GetColourLong() == compareWith
->GetColourLong())))
12109 SetColour(border
.GetColourLong());
12111 if (border
.HasWidth())
12113 if (!(compareWith
&& (border
.GetWidth() == compareWith
->GetWidth())))
12114 SetWidth(border
.GetWidth());
12120 // Remove specified attributes from this object
12121 bool wxTextAttrBorder::RemoveStyle(const wxTextAttrBorder
& attr
)
12123 if (attr
.HasStyle() && HasStyle())
12124 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_STYLE
);
12125 if (attr
.HasColour() && HasColour())
12126 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_COLOUR
);
12127 if (attr
.HasWidth() && HasWidth())
12128 m_borderWidth
.Reset();
12133 // Collects the attributes that are common to a range of content, building up a note of
12134 // which attributes are absent in some objects and which clash in some objects.
12135 void wxTextAttrBorder::CollectCommonAttributes(const wxTextAttrBorder
& attr
, wxTextAttrBorder
& clashingAttr
, wxTextAttrBorder
& absentAttr
)
12137 if (attr
.HasStyle())
12139 if (!clashingAttr
.HasStyle() && !absentAttr
.HasStyle())
12143 if (GetStyle() != attr
.GetStyle())
12145 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
12146 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
12150 SetStyle(attr
.GetStyle());
12154 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
12156 if (attr
.HasColour())
12158 if (!clashingAttr
.HasColour() && !absentAttr
.HasColour())
12162 if (GetColour() != attr
.GetColour())
12164 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
12165 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
12169 SetColour(attr
.GetColourLong());
12173 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
12175 m_borderWidth
.CollectCommonAttributes(attr
.m_borderWidth
, clashingAttr
.m_borderWidth
, absentAttr
.m_borderWidth
);
12178 // Partial equality test
12179 bool wxTextAttrBorders::EqPartial(const wxTextAttrBorders
& borders
, bool weakTest
) const
12181 return m_left
.EqPartial(borders
.m_left
, weakTest
) && m_right
.EqPartial(borders
.m_right
, weakTest
) &&
12182 m_top
.EqPartial(borders
.m_top
, weakTest
) && m_bottom
.EqPartial(borders
.m_bottom
, weakTest
);
12185 // Apply border to 'this', but not if the same as compareWith
12186 bool wxTextAttrBorders::Apply(const wxTextAttrBorders
& borders
, const wxTextAttrBorders
* compareWith
)
12188 m_left
.Apply(borders
.m_left
, compareWith
? (& compareWith
->m_left
) : (const wxTextAttrBorder
*) NULL
);
12189 m_right
.Apply(borders
.m_right
, compareWith
? (& compareWith
->m_right
) : (const wxTextAttrBorder
*) NULL
);
12190 m_top
.Apply(borders
.m_top
, compareWith
? (& compareWith
->m_top
) : (const wxTextAttrBorder
*) NULL
);
12191 m_bottom
.Apply(borders
.m_bottom
, compareWith
? (& compareWith
->m_bottom
) : (const wxTextAttrBorder
*) NULL
);
12195 // Remove specified attributes from this object
12196 bool wxTextAttrBorders::RemoveStyle(const wxTextAttrBorders
& attr
)
12198 m_left
.RemoveStyle(attr
.m_left
);
12199 m_right
.RemoveStyle(attr
.m_right
);
12200 m_top
.RemoveStyle(attr
.m_top
);
12201 m_bottom
.RemoveStyle(attr
.m_bottom
);
12205 // Collects the attributes that are common to a range of content, building up a note of
12206 // which attributes are absent in some objects and which clash in some objects.
12207 void wxTextAttrBorders::CollectCommonAttributes(const wxTextAttrBorders
& attr
, wxTextAttrBorders
& clashingAttr
, wxTextAttrBorders
& absentAttr
)
12209 m_left
.CollectCommonAttributes(attr
.m_left
, clashingAttr
.m_left
, absentAttr
.m_left
);
12210 m_right
.CollectCommonAttributes(attr
.m_right
, clashingAttr
.m_right
, absentAttr
.m_right
);
12211 m_top
.CollectCommonAttributes(attr
.m_top
, clashingAttr
.m_top
, absentAttr
.m_top
);
12212 m_bottom
.CollectCommonAttributes(attr
.m_bottom
, clashingAttr
.m_bottom
, absentAttr
.m_bottom
);
12215 // Set style of all borders
12216 void wxTextAttrBorders::SetStyle(int style
)
12218 m_left
.SetStyle(style
);
12219 m_right
.SetStyle(style
);
12220 m_top
.SetStyle(style
);
12221 m_bottom
.SetStyle(style
);
12224 // Set colour of all borders
12225 void wxTextAttrBorders::SetColour(unsigned long colour
)
12227 m_left
.SetColour(colour
);
12228 m_right
.SetColour(colour
);
12229 m_top
.SetColour(colour
);
12230 m_bottom
.SetColour(colour
);
12233 void wxTextAttrBorders::SetColour(const wxColour
& colour
)
12235 m_left
.SetColour(colour
);
12236 m_right
.SetColour(colour
);
12237 m_top
.SetColour(colour
);
12238 m_bottom
.SetColour(colour
);
12241 // Set width of all borders
12242 void wxTextAttrBorders::SetWidth(const wxTextAttrDimension
& width
)
12244 m_left
.SetWidth(width
);
12245 m_right
.SetWidth(width
);
12246 m_top
.SetWidth(width
);
12247 m_bottom
.SetWidth(width
);
12250 // Partial equality test
12251 bool wxTextAttrDimension::EqPartial(const wxTextAttrDimension
& dim
, bool weakTest
) const
12253 if (!weakTest
&& !IsValid() && dim
.IsValid())
12256 if (dim
.IsValid() && IsValid() && !((*this) == dim
))
12262 bool wxTextAttrDimension::Apply(const wxTextAttrDimension
& dim
, const wxTextAttrDimension
* compareWith
)
12266 if (!(compareWith
&& dim
== (*compareWith
)))
12273 // Collects the attributes that are common to a range of content, building up a note of
12274 // which attributes are absent in some objects and which clash in some objects.
12275 void wxTextAttrDimension::CollectCommonAttributes(const wxTextAttrDimension
& attr
, wxTextAttrDimension
& clashingAttr
, wxTextAttrDimension
& absentAttr
)
12277 if (attr
.IsValid())
12279 if (!clashingAttr
.IsValid() && !absentAttr
.IsValid())
12283 if (!((*this) == attr
))
12285 clashingAttr
.SetValid(true);
12294 absentAttr
.SetValid(true);
12297 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(wxDC
& dc
, double scale
, const wxSize
& parentSize
)
12299 m_ppi
= dc
.GetPPI().x
; m_scale
= scale
; m_parentSize
= parentSize
;
12302 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(int ppi
, double scale
, const wxSize
& parentSize
)
12304 m_ppi
= ppi
; m_scale
= scale
; m_parentSize
= parentSize
;
12307 int wxTextAttrDimensionConverter::ConvertTenthsMMToPixels(int units
) const
12309 return wxRichTextObject::ConvertTenthsMMToPixels(m_ppi
, units
, m_scale
);
12312 int wxTextAttrDimensionConverter::ConvertPixelsToTenthsMM(int pixels
) const
12314 return wxRichTextObject::ConvertPixelsToTenthsMM(m_ppi
, pixels
, m_scale
);
12317 int wxTextAttrDimensionConverter::GetPixels(const wxTextAttrDimension
& dim
, int direction
) const
12319 if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
12320 return ConvertTenthsMMToPixels(dim
.GetValue());
12321 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
12322 return dim
.GetValue();
12323 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
12325 wxASSERT(m_parentSize
!= wxDefaultSize
);
12326 if (direction
== wxHORIZONTAL
)
12327 return (int) (double(m_parentSize
.x
) * double(dim
.GetValue()) / 100.0);
12329 return (int) (double(m_parentSize
.y
) * double(dim
.GetValue()) / 100.0);
12338 int wxTextAttrDimensionConverter::GetTenthsMM(const wxTextAttrDimension
& dim
) const
12340 if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
12341 return dim
.GetValue();
12342 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
12343 return ConvertPixelsToTenthsMM(dim
.GetValue());
12351 // Partial equality test
12352 bool wxTextAttrDimensions::EqPartial(const wxTextAttrDimensions
& dims
, bool weakTest
) const
12354 if (!m_left
.EqPartial(dims
.m_left
, weakTest
))
12357 if (!m_right
.EqPartial(dims
.m_right
, weakTest
))
12360 if (!m_top
.EqPartial(dims
.m_top
, weakTest
))
12363 if (!m_bottom
.EqPartial(dims
.m_bottom
, weakTest
))
12369 // Apply border to 'this', but not if the same as compareWith
12370 bool wxTextAttrDimensions::Apply(const wxTextAttrDimensions
& dims
, const wxTextAttrDimensions
* compareWith
)
12372 m_left
.Apply(dims
.m_left
, compareWith
? (& compareWith
->m_left
) : (const wxTextAttrDimension
*) NULL
);
12373 m_right
.Apply(dims
.m_right
, compareWith
? (& compareWith
->m_right
): (const wxTextAttrDimension
*) NULL
);
12374 m_top
.Apply(dims
.m_top
, compareWith
? (& compareWith
->m_top
): (const wxTextAttrDimension
*) NULL
);
12375 m_bottom
.Apply(dims
.m_bottom
, compareWith
? (& compareWith
->m_bottom
): (const wxTextAttrDimension
*) NULL
);
12380 // Remove specified attributes from this object
12381 bool wxTextAttrDimensions::RemoveStyle(const wxTextAttrDimensions
& attr
)
12383 if (attr
.m_left
.IsValid())
12385 if (attr
.m_right
.IsValid())
12387 if (attr
.m_top
.IsValid())
12389 if (attr
.m_bottom
.IsValid())
12395 // Collects the attributes that are common to a range of content, building up a note of
12396 // which attributes are absent in some objects and which clash in some objects.
12397 void wxTextAttrDimensions::CollectCommonAttributes(const wxTextAttrDimensions
& attr
, wxTextAttrDimensions
& clashingAttr
, wxTextAttrDimensions
& absentAttr
)
12399 m_left
.CollectCommonAttributes(attr
.m_left
, clashingAttr
.m_left
, absentAttr
.m_left
);
12400 m_right
.CollectCommonAttributes(attr
.m_right
, clashingAttr
.m_right
, absentAttr
.m_right
);
12401 m_top
.CollectCommonAttributes(attr
.m_top
, clashingAttr
.m_top
, absentAttr
.m_top
);
12402 m_bottom
.CollectCommonAttributes(attr
.m_bottom
, clashingAttr
.m_bottom
, absentAttr
.m_bottom
);
12405 // Partial equality test
12406 bool wxTextAttrSize::EqPartial(const wxTextAttrSize
& size
, bool weakTest
) const
12408 if (!m_width
.EqPartial(size
.m_width
, weakTest
))
12411 if (!m_height
.EqPartial(size
.m_height
, weakTest
))
12417 // Apply border to 'this', but not if the same as compareWith
12418 bool wxTextAttrSize::Apply(const wxTextAttrSize
& size
, const wxTextAttrSize
* compareWith
)
12420 m_width
.Apply(size
.m_width
, compareWith
? (& compareWith
->m_width
) : (const wxTextAttrDimension
*) NULL
);
12421 m_height
.Apply(size
.m_height
, compareWith
? (& compareWith
->m_height
): (const wxTextAttrDimension
*) NULL
);
12426 // Remove specified attributes from this object
12427 bool wxTextAttrSize::RemoveStyle(const wxTextAttrSize
& attr
)
12429 if (attr
.m_width
.IsValid())
12431 if (attr
.m_height
.IsValid())
12437 // Collects the attributes that are common to a range of content, building up a note of
12438 // which attributes are absent in some objects and which clash in some objects.
12439 void wxTextAttrSize::CollectCommonAttributes(const wxTextAttrSize
& attr
, wxTextAttrSize
& clashingAttr
, wxTextAttrSize
& absentAttr
)
12441 m_width
.CollectCommonAttributes(attr
.m_width
, clashingAttr
.m_width
, absentAttr
.m_width
);
12442 m_height
.CollectCommonAttributes(attr
.m_height
, clashingAttr
.m_height
, absentAttr
.m_height
);
12445 // Collects the attributes that are common to a range of content, building up a note of
12446 // which attributes are absent in some objects and which clash in some objects.
12447 void wxTextAttrCollectCommonAttributes(wxTextAttr
& currentStyle
, const wxTextAttr
& attr
, wxTextAttr
& clashingAttr
, wxTextAttr
& absentAttr
)
12449 absentAttr
.SetFlags(absentAttr
.GetFlags() | (~attr
.GetFlags() & wxTEXT_ATTR_ALL
));
12450 absentAttr
.SetTextEffectFlags(absentAttr
.GetTextEffectFlags() | (~attr
.GetTextEffectFlags() & 0xFFFF));
12452 long forbiddenFlags
= clashingAttr
.GetFlags()|absentAttr
.GetFlags();
12454 if (attr
.HasFont())
12456 // If different font size units are being used, this is a clash.
12457 if (((attr
.GetFlags() & wxTEXT_ATTR_FONT_SIZE
) | (currentStyle
.GetFlags() & wxTEXT_ATTR_FONT_SIZE
)) == wxTEXT_ATTR_FONT_SIZE
)
12459 currentStyle
.SetFontSize(0);
12460 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_SIZE
);
12461 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_SIZE
);
12465 if (attr
.HasFontPointSize() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_POINT_SIZE
))
12467 if (currentStyle
.HasFontPointSize())
12469 if (currentStyle
.GetFontSize() != attr
.GetFontSize())
12471 // Clash of attr - mark as such
12472 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE
);
12473 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE
);
12477 currentStyle
.SetFontSize(attr
.GetFontSize());
12480 if (attr
.HasFontPixelSize() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_PIXEL_SIZE
))
12482 if (currentStyle
.HasFontPixelSize())
12484 if (currentStyle
.GetFontSize() != attr
.GetFontSize())
12486 // Clash of attr - mark as such
12487 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE
);
12488 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE
);
12492 currentStyle
.SetFontPixelSize(attr
.GetFontSize());
12496 if (attr
.HasFontItalic() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_ITALIC
))
12498 if (currentStyle
.HasFontItalic())
12500 if (currentStyle
.GetFontStyle() != attr
.GetFontStyle())
12502 // Clash of attr - mark as such
12503 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_ITALIC
);
12504 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC
);
12508 currentStyle
.SetFontStyle(attr
.GetFontStyle());
12511 if (attr
.HasFontFamily() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_FAMILY
))
12513 if (currentStyle
.HasFontFamily())
12515 if (currentStyle
.GetFontFamily() != attr
.GetFontFamily())
12517 // Clash of attr - mark as such
12518 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FAMILY
);
12519 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY
);
12523 currentStyle
.SetFontFamily(attr
.GetFontFamily());
12526 if (attr
.HasFontWeight() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_WEIGHT
))
12528 if (currentStyle
.HasFontWeight())
12530 if (currentStyle
.GetFontWeight() != attr
.GetFontWeight())
12532 // Clash of attr - mark as such
12533 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_WEIGHT
);
12534 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT
);
12538 currentStyle
.SetFontWeight(attr
.GetFontWeight());
12541 if (attr
.HasFontFaceName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_FACE
))
12543 if (currentStyle
.HasFontFaceName())
12545 wxString
faceName1(currentStyle
.GetFontFaceName());
12546 wxString
faceName2(attr
.GetFontFaceName());
12548 if (faceName1
!= faceName2
)
12550 // Clash of attr - mark as such
12551 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FACE
);
12552 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FACE
);
12556 currentStyle
.SetFontFaceName(attr
.GetFontFaceName());
12559 if (attr
.HasFontUnderlined() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_UNDERLINE
))
12561 if (currentStyle
.HasFontUnderlined())
12563 if (currentStyle
.GetFontUnderlined() != attr
.GetFontUnderlined())
12565 // Clash of attr - mark as such
12566 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
12567 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
12571 currentStyle
.SetFontUnderlined(attr
.GetFontUnderlined());
12574 if (attr
.HasFontStrikethrough() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_STRIKETHROUGH
))
12576 if (currentStyle
.HasFontStrikethrough())
12578 if (currentStyle
.GetFontStrikethrough() != attr
.GetFontStrikethrough())
12580 // Clash of attr - mark as such
12581 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH
);
12582 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH
);
12586 currentStyle
.SetFontStrikethrough(attr
.GetFontStrikethrough());
12590 if (attr
.HasTextColour() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_TEXT_COLOUR
))
12592 if (currentStyle
.HasTextColour())
12594 if (currentStyle
.GetTextColour() != attr
.GetTextColour())
12596 // Clash of attr - mark as such
12597 clashingAttr
.AddFlag(wxTEXT_ATTR_TEXT_COLOUR
);
12598 currentStyle
.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR
);
12602 currentStyle
.SetTextColour(attr
.GetTextColour());
12605 if (attr
.HasBackgroundColour() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BACKGROUND_COLOUR
))
12607 if (currentStyle
.HasBackgroundColour())
12609 if (currentStyle
.GetBackgroundColour() != attr
.GetBackgroundColour())
12611 // Clash of attr - mark as such
12612 clashingAttr
.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
12613 currentStyle
.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
12617 currentStyle
.SetBackgroundColour(attr
.GetBackgroundColour());
12620 if (attr
.HasAlignment() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_ALIGNMENT
))
12622 if (currentStyle
.HasAlignment())
12624 if (currentStyle
.GetAlignment() != attr
.GetAlignment())
12626 // Clash of attr - mark as such
12627 clashingAttr
.AddFlag(wxTEXT_ATTR_ALIGNMENT
);
12628 currentStyle
.RemoveFlag(wxTEXT_ATTR_ALIGNMENT
);
12632 currentStyle
.SetAlignment(attr
.GetAlignment());
12635 if (attr
.HasTabs() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_TABS
))
12637 if (currentStyle
.HasTabs())
12639 if (!wxRichTextTabsEq(currentStyle
.GetTabs(), attr
.GetTabs()))
12641 // Clash of attr - mark as such
12642 clashingAttr
.AddFlag(wxTEXT_ATTR_TABS
);
12643 currentStyle
.RemoveFlag(wxTEXT_ATTR_TABS
);
12647 currentStyle
.SetTabs(attr
.GetTabs());
12650 if (attr
.HasLeftIndent() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LEFT_INDENT
))
12652 if (currentStyle
.HasLeftIndent())
12654 if (currentStyle
.GetLeftIndent() != attr
.GetLeftIndent() || currentStyle
.GetLeftSubIndent() != attr
.GetLeftSubIndent())
12656 // Clash of attr - mark as such
12657 clashingAttr
.AddFlag(wxTEXT_ATTR_LEFT_INDENT
);
12658 currentStyle
.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT
);
12662 currentStyle
.SetLeftIndent(attr
.GetLeftIndent(), attr
.GetLeftSubIndent());
12665 if (attr
.HasRightIndent() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_RIGHT_INDENT
))
12667 if (currentStyle
.HasRightIndent())
12669 if (currentStyle
.GetRightIndent() != attr
.GetRightIndent())
12671 // Clash of attr - mark as such
12672 clashingAttr
.AddFlag(wxTEXT_ATTR_RIGHT_INDENT
);
12673 currentStyle
.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT
);
12677 currentStyle
.SetRightIndent(attr
.GetRightIndent());
12680 if (attr
.HasParagraphSpacingAfter() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARA_SPACING_AFTER
))
12682 if (currentStyle
.HasParagraphSpacingAfter())
12684 if (currentStyle
.GetParagraphSpacingAfter() != attr
.GetParagraphSpacingAfter())
12686 // Clash of attr - mark as such
12687 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
12688 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
12692 currentStyle
.SetParagraphSpacingAfter(attr
.GetParagraphSpacingAfter());
12695 if (attr
.HasParagraphSpacingBefore() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARA_SPACING_BEFORE
))
12697 if (currentStyle
.HasParagraphSpacingBefore())
12699 if (currentStyle
.GetParagraphSpacingBefore() != attr
.GetParagraphSpacingBefore())
12701 // Clash of attr - mark as such
12702 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
12703 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
12707 currentStyle
.SetParagraphSpacingBefore(attr
.GetParagraphSpacingBefore());
12710 if (attr
.HasLineSpacing() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LINE_SPACING
))
12712 if (currentStyle
.HasLineSpacing())
12714 if (currentStyle
.GetLineSpacing() != attr
.GetLineSpacing())
12716 // Clash of attr - mark as such
12717 clashingAttr
.AddFlag(wxTEXT_ATTR_LINE_SPACING
);
12718 currentStyle
.RemoveFlag(wxTEXT_ATTR_LINE_SPACING
);
12722 currentStyle
.SetLineSpacing(attr
.GetLineSpacing());
12725 if (attr
.HasCharacterStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_CHARACTER_STYLE_NAME
))
12727 if (currentStyle
.HasCharacterStyleName())
12729 if (currentStyle
.GetCharacterStyleName() != attr
.GetCharacterStyleName())
12731 // Clash of attr - mark as such
12732 clashingAttr
.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
12733 currentStyle
.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
12737 currentStyle
.SetCharacterStyleName(attr
.GetCharacterStyleName());
12740 if (attr
.HasParagraphStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
))
12742 if (currentStyle
.HasParagraphStyleName())
12744 if (currentStyle
.GetParagraphStyleName() != attr
.GetParagraphStyleName())
12746 // Clash of attr - mark as such
12747 clashingAttr
.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
12748 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
12752 currentStyle
.SetParagraphStyleName(attr
.GetParagraphStyleName());
12755 if (attr
.HasListStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LIST_STYLE_NAME
))
12757 if (currentStyle
.HasListStyleName())
12759 if (currentStyle
.GetListStyleName() != attr
.GetListStyleName())
12761 // Clash of attr - mark as such
12762 clashingAttr
.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
12763 currentStyle
.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
12767 currentStyle
.SetListStyleName(attr
.GetListStyleName());
12770 if (attr
.HasBulletStyle() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_STYLE
))
12772 if (currentStyle
.HasBulletStyle())
12774 if (currentStyle
.GetBulletStyle() != attr
.GetBulletStyle())
12776 // Clash of attr - mark as such
12777 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_STYLE
);
12778 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE
);
12782 currentStyle
.SetBulletStyle(attr
.GetBulletStyle());
12785 if (attr
.HasBulletNumber() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_NUMBER
))
12787 if (currentStyle
.HasBulletNumber())
12789 if (currentStyle
.GetBulletNumber() != attr
.GetBulletNumber())
12791 // Clash of attr - mark as such
12792 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NUMBER
);
12793 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER
);
12797 currentStyle
.SetBulletNumber(attr
.GetBulletNumber());
12800 if (attr
.HasBulletText() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_TEXT
))
12802 if (currentStyle
.HasBulletText())
12804 if (currentStyle
.GetBulletText() != attr
.GetBulletText())
12806 // Clash of attr - mark as such
12807 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_TEXT
);
12808 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT
);
12813 currentStyle
.SetBulletText(attr
.GetBulletText());
12814 currentStyle
.SetBulletFont(attr
.GetBulletFont());
12818 if (attr
.HasBulletName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_NAME
))
12820 if (currentStyle
.HasBulletName())
12822 if (currentStyle
.GetBulletName() != attr
.GetBulletName())
12824 // Clash of attr - mark as such
12825 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NAME
);
12826 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NAME
);
12831 currentStyle
.SetBulletName(attr
.GetBulletName());
12835 if (attr
.HasURL() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_URL
))
12837 if (currentStyle
.HasURL())
12839 if (currentStyle
.GetURL() != attr
.GetURL())
12841 // Clash of attr - mark as such
12842 clashingAttr
.AddFlag(wxTEXT_ATTR_URL
);
12843 currentStyle
.RemoveFlag(wxTEXT_ATTR_URL
);
12848 currentStyle
.SetURL(attr
.GetURL());
12852 if (attr
.HasTextEffects() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_EFFECTS
))
12854 if (currentStyle
.HasTextEffects())
12856 // We need to find the bits in the new attr that are different:
12857 // just look at those bits that are specified by the new attr.
12859 // We need to remove the bits and flags that are not common between current attr
12860 // and new attr. In so doing we need to take account of the styles absent from one or more of the
12861 // previous styles.
12863 int currentRelevantTextEffects
= currentStyle
.GetTextEffects() & attr
.GetTextEffectFlags();
12864 int newRelevantTextEffects
= attr
.GetTextEffects() & attr
.GetTextEffectFlags();
12866 if (currentRelevantTextEffects
!= newRelevantTextEffects
)
12868 // Find the text effects that were different, using XOR
12869 int differentEffects
= currentRelevantTextEffects
^ newRelevantTextEffects
;
12871 // Clash of attr - mark as such
12872 clashingAttr
.SetTextEffectFlags(clashingAttr
.GetTextEffectFlags() | differentEffects
);
12873 currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~differentEffects
);
12878 currentStyle
.SetTextEffects(attr
.GetTextEffects());
12879 currentStyle
.SetTextEffectFlags(attr
.GetTextEffectFlags());
12882 // Mask out the flags and values that cannot be common because they were absent in one or more objecrs
12883 // that we've looked at so far
12884 currentStyle
.SetTextEffects(currentStyle
.GetTextEffects() & ~absentAttr
.GetTextEffectFlags());
12885 currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~absentAttr
.GetTextEffectFlags());
12887 if (currentStyle
.GetTextEffectFlags() == 0)
12888 currentStyle
.RemoveFlag(wxTEXT_ATTR_EFFECTS
);
12891 if (attr
.HasOutlineLevel() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_OUTLINE_LEVEL
))
12893 if (currentStyle
.HasOutlineLevel())
12895 if (currentStyle
.GetOutlineLevel() != attr
.GetOutlineLevel())
12897 // Clash of attr - mark as such
12898 clashingAttr
.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
12899 currentStyle
.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
12903 currentStyle
.SetOutlineLevel(attr
.GetOutlineLevel());
12907 WX_DEFINE_OBJARRAY(wxRichTextVariantArray
);
12909 IMPLEMENT_DYNAMIC_CLASS(wxRichTextProperties
, wxObject
)
12911 bool wxRichTextProperties::operator==(const wxRichTextProperties
& props
) const
12913 if (m_properties
.GetCount() != props
.GetCount())
12917 for (i
= 0; i
< m_properties
.GetCount(); i
++)
12919 const wxVariant
& var1
= m_properties
[i
];
12920 int idx
= props
.Find(var1
.GetName());
12923 const wxVariant
& var2
= props
.m_properties
[idx
];
12924 if (!(var1
== var2
))
12931 wxArrayString
wxRichTextProperties::GetPropertyNames() const
12935 for (i
= 0; i
< m_properties
.GetCount(); i
++)
12937 arr
.Add(m_properties
[i
].GetName());
12942 int wxRichTextProperties::Find(const wxString
& name
) const
12945 for (i
= 0; i
< m_properties
.GetCount(); i
++)
12947 if (m_properties
[i
].GetName() == name
)
12953 bool wxRichTextProperties::Remove(const wxString
& name
)
12955 int idx
= Find(name
);
12958 m_properties
.RemoveAt(idx
);
12965 wxVariant
* wxRichTextProperties::FindOrCreateProperty(const wxString
& name
)
12967 int idx
= Find(name
);
12968 if (idx
== wxNOT_FOUND
)
12969 SetProperty(name
, wxString());
12971 if (idx
!= wxNOT_FOUND
)
12973 return & (*this)[idx
];
12979 const wxVariant
& wxRichTextProperties::GetProperty(const wxString
& name
) const
12981 static const wxVariant nullVariant
;
12982 int idx
= Find(name
);
12984 return m_properties
[idx
];
12986 return nullVariant
;
12989 wxString
wxRichTextProperties::GetPropertyString(const wxString
& name
) const
12991 return GetProperty(name
).GetString();
12994 long wxRichTextProperties::GetPropertyLong(const wxString
& name
) const
12996 return GetProperty(name
).GetLong();
12999 bool wxRichTextProperties::GetPropertyBool(const wxString
& name
) const
13001 return GetProperty(name
).GetBool();
13004 double wxRichTextProperties::GetPropertyDouble(const wxString
& name
) const
13006 return GetProperty(name
).GetDouble();
13009 void wxRichTextProperties::SetProperty(const wxVariant
& variant
)
13011 wxASSERT(!variant
.GetName().IsEmpty());
13013 int idx
= Find(variant
.GetName());
13016 m_properties
.Add(variant
);
13018 m_properties
[idx
] = variant
;
13021 void wxRichTextProperties::SetProperty(const wxString
& name
, const wxVariant
& variant
)
13023 int idx
= Find(name
);
13024 wxVariant
var(variant
);
13028 m_properties
.Add(var
);
13030 m_properties
[idx
] = var
;
13033 void wxRichTextProperties::SetProperty(const wxString
& name
, const wxString
& value
)
13035 SetProperty(name
, wxVariant(value
, name
));
13038 void wxRichTextProperties::SetProperty(const wxString
& name
, long value
)
13040 SetProperty(name
, wxVariant(value
, name
));
13043 void wxRichTextProperties::SetProperty(const wxString
& name
, double value
)
13045 SetProperty(name
, wxVariant(value
, name
));
13048 void wxRichTextProperties::SetProperty(const wxString
& name
, bool value
)
13050 SetProperty(name
, wxVariant(value
, name
));
13053 void wxRichTextProperties::RemoveProperties(const wxRichTextProperties
& properties
)
13056 for (i
= 0; i
< properties
.GetCount(); i
++)
13058 wxString name
= properties
.GetProperties()[i
].GetName();
13059 if (HasProperty(name
))
13064 void wxRichTextProperties::MergeProperties(const wxRichTextProperties
& properties
)
13067 for (i
= 0; i
< properties
.GetCount(); i
++)
13069 SetProperty(properties
.GetProperties()[i
]);
13073 wxRichTextObject
* wxRichTextObjectAddress::GetObject(wxRichTextParagraphLayoutBox
* topLevelContainer
) const
13075 if (m_address
.GetCount() == 0)
13076 return topLevelContainer
;
13078 wxRichTextCompositeObject
* p
= topLevelContainer
;
13080 while (p
&& i
< m_address
.GetCount())
13082 int pos
= m_address
[i
];
13083 wxASSERT(pos
>= 0 && pos
< (int) p
->GetChildren().GetCount());
13084 if (pos
< 0 || pos
>= (int) p
->GetChildren().GetCount())
13087 wxRichTextObject
* p1
= p
->GetChild(pos
);
13088 if (i
== (m_address
.GetCount()-1))
13091 p
= wxDynamicCast(p1
, wxRichTextCompositeObject
);
13097 bool wxRichTextObjectAddress::Create(wxRichTextParagraphLayoutBox
* topLevelContainer
, wxRichTextObject
* obj
)
13101 if (topLevelContainer
== obj
)
13104 wxRichTextObject
* o
= obj
;
13107 wxRichTextCompositeObject
* p
= wxDynamicCast(o
->GetParent(), wxRichTextCompositeObject
);
13111 int pos
= p
->GetChildren().IndexOf(o
);
13115 m_address
.Insert(pos
, 0);
13117 if (p
== topLevelContainer
)
13126 bool wxRichTextSelection::operator==(const wxRichTextSelection
& sel
) const
13128 if (m_container
!= sel
.m_container
)
13130 if (m_ranges
.GetCount() != sel
.m_ranges
.GetCount())
13133 for (i
= 0; i
< m_ranges
.GetCount(); i
++)
13134 if (!(m_ranges
[i
] == sel
.m_ranges
[i
]))
13139 // Get the selections appropriate to the specified object, if any; returns wxRICHTEXT_NO_SELECTION if none
13140 // or none at the level of the object's container.
13141 wxRichTextRangeArray
wxRichTextSelection::GetSelectionForObject(wxRichTextObject
* obj
) const
13145 wxRichTextParagraphLayoutBox
* container
= obj
->GetParentContainer();
13147 if (container
== m_container
)
13150 container
= obj
->GetContainer();
13153 if (container
->GetParent())
13155 // If we found that our object's container is within the range of
13156 // a selection higher up, then assume the whole original object
13157 // is also selected.
13158 wxRichTextParagraphLayoutBox
* parentContainer
= container
->GetParentContainer();
13159 if (parentContainer
== m_container
)
13161 if (WithinSelection(container
->GetRange().GetStart(), m_ranges
))
13163 wxRichTextRangeArray ranges
;
13164 ranges
.Add(obj
->GetRange());
13169 container
= parentContainer
;
13178 return wxRichTextRangeArray();
13181 // Is the given position within the selection?
13182 bool wxRichTextSelection::WithinSelection(long pos
, wxRichTextObject
* obj
) const
13188 wxRichTextRangeArray selectionRanges
= GetSelectionForObject(obj
);
13189 return WithinSelection(pos
, selectionRanges
);
13193 // Is the given position within the selection range?
13194 bool wxRichTextSelection::WithinSelection(long pos
, const wxRichTextRangeArray
& ranges
)
13197 for (i
= 0; i
< ranges
.GetCount(); i
++)
13199 const wxRichTextRange
& range
= ranges
[i
];
13200 if (pos
>= range
.GetStart() && pos
<= range
.GetEnd())
13206 // Is the given range completely within the selection range?
13207 bool wxRichTextSelection::WithinSelection(const wxRichTextRange
& range
, const wxRichTextRangeArray
& ranges
)
13210 for (i
= 0; i
< ranges
.GetCount(); i
++)
13212 const wxRichTextRange
& eachRange
= ranges
[i
];
13213 if (range
.IsWithin(eachRange
))
13219 IMPLEMENT_CLASS(wxRichTextDrawingHandler
, wxObject
)
13220 IMPLEMENT_CLASS(wxRichTextDrawingContext
, wxObject
)
13222 bool wxRichTextDrawingContext::HasVirtualAttributes(wxRichTextObject
* obj
) const
13224 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
13227 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13228 if (handler
->HasVirtualAttributes(obj
))
13231 node
= node
->GetNext();
13236 wxRichTextAttr
wxRichTextDrawingContext::GetVirtualAttributes(wxRichTextObject
* obj
) const
13238 wxRichTextAttr attr
;
13239 // We apply all handlers, so we can may combine several different attributes
13240 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
13243 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13244 if (handler
->HasVirtualAttributes(obj
))
13246 bool success
= handler
->GetVirtualAttributes(attr
, obj
);
13248 wxUnusedVar(success
);
13251 node
= node
->GetNext();
13256 bool wxRichTextDrawingContext::ApplyVirtualAttributes(wxRichTextAttr
& attr
, wxRichTextObject
* obj
) const
13258 if (HasVirtualAttributes(obj
))
13260 wxRichTextAttr
a(GetVirtualAttributes(obj
));
13268 /// Adds a handler to the end
13269 void wxRichTextBuffer::AddDrawingHandler(wxRichTextDrawingHandler
*handler
)
13271 sm_drawingHandlers
.Append(handler
);
13274 /// Inserts a handler at the front
13275 void wxRichTextBuffer::InsertDrawingHandler(wxRichTextDrawingHandler
*handler
)
13277 sm_drawingHandlers
.Insert( handler
);
13280 /// Removes a handler
13281 bool wxRichTextBuffer::RemoveDrawingHandler(const wxString
& name
)
13283 wxRichTextDrawingHandler
*handler
= FindDrawingHandler(name
);
13286 sm_drawingHandlers
.DeleteObject(handler
);
13294 wxRichTextDrawingHandler
* wxRichTextBuffer::FindDrawingHandler(const wxString
& name
)
13296 wxList::compatibility_iterator node
= sm_drawingHandlers
.GetFirst();
13299 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13300 if (handler
->GetName().Lower() == name
.Lower()) return handler
;
13302 node
= node
->GetNext();
13307 void wxRichTextBuffer::CleanUpDrawingHandlers()
13309 wxList::compatibility_iterator node
= sm_drawingHandlers
.GetFirst();
13312 wxRichTextDrawingHandler
* handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13313 wxList::compatibility_iterator next
= node
->GetNext();
13318 sm_drawingHandlers
.Clear();
13321 void wxRichTextBuffer::AddFieldType(wxRichTextFieldType
*fieldType
)
13323 sm_fieldTypes
[fieldType
->GetName()] = fieldType
;
13326 bool wxRichTextBuffer::RemoveFieldType(const wxString
& name
)
13328 wxRichTextFieldTypeHashMap::iterator it
= sm_fieldTypes
.find(name
);
13329 if (it
== sm_fieldTypes
.end())
13333 wxRichTextFieldType
* fieldType
= it
->second
;
13334 sm_fieldTypes
.erase(it
);
13340 wxRichTextFieldType
*wxRichTextBuffer::FindFieldType(const wxString
& name
)
13342 wxRichTextFieldTypeHashMap::iterator it
= sm_fieldTypes
.find(name
);
13343 if (it
== sm_fieldTypes
.end())
13349 void wxRichTextBuffer::CleanUpFieldTypes()
13351 wxRichTextFieldTypeHashMap::iterator it
;
13352 for( it
= sm_fieldTypes
.begin(); it
!= sm_fieldTypes
.end(); ++it
)
13354 wxRichTextFieldType
* fieldType
= it
->second
;
13358 sm_fieldTypes
.clear();