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"
46 #include "wx/listimpl.cpp"
47 #include "wx/arrimpl.cpp"
49 WX_DEFINE_LIST(wxRichTextObjectList
)
50 WX_DEFINE_LIST(wxRichTextLineList
)
52 // Switch off if the platform doesn't like it for some reason
53 #define wxRICHTEXT_USE_OPTIMIZED_DRAWING 1
55 // Use GetPartialTextExtents for platforms that support it natively
56 #define wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS 1
58 const wxChar wxRichTextLineBreakChar
= (wxChar
) 29;
60 // Helper classes for floating layout
61 struct wxRichTextFloatRectMap
63 wxRichTextFloatRectMap(int sY
, int eY
, int w
, wxRichTextObject
* obj
)
73 wxRichTextObject
* anchor
;
76 WX_DEFINE_SORTED_ARRAY(wxRichTextFloatRectMap
*, wxRichTextFloatRectMapArray
);
78 int wxRichTextFloatRectMapCmp(wxRichTextFloatRectMap
* r1
, wxRichTextFloatRectMap
* r2
)
80 return r1
->startY
- r2
->startY
;
83 class wxRichTextFloatCollector
86 wxRichTextFloatCollector(const wxRect
& availableRect
);
87 ~wxRichTextFloatCollector();
89 // Collect the floating objects info in the given paragraph
90 void CollectFloat(wxRichTextParagraph
* para
);
91 void CollectFloat(wxRichTextParagraph
* para
, wxRichTextObject
* floating
);
93 // Return the last paragraph we collected
94 wxRichTextParagraph
* LastParagraph();
96 // Given the start y position and the height of the line,
97 // find out how wide the line can be
98 wxRect
GetAvailableRect(int startY
, int endY
);
100 // Given a floating box, find its fit position
101 int GetFitPosition(int direction
, int start
, int height
) const;
102 int GetFitPosition(const wxRichTextFloatRectMapArray
& array
, int start
, int height
) const;
104 // Find the last y position
105 int GetLastRectBottom();
107 // Draw the floats inside a rect
108 void Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
);
110 // HitTest the floats
111 int HitTest(wxDC
& dc
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, int flags
);
113 // Get floating object count
114 int GetFloatingObjectCount() const { return m_left
.GetCount() + m_right
.GetCount(); }
116 // Get floating objects
117 bool GetFloatingObjects(wxRichTextObjectList
& objects
) const;
120 bool DeleteFloat(wxRichTextObject
* obj
);
122 // Do we have this float already?
123 bool HasFloat(wxRichTextObject
* obj
);
125 bool HasFloats() const { return m_left
.GetCount() >0 || m_right
.GetCount() > 0; }
127 static int SearchAdjacentRect(const wxRichTextFloatRectMapArray
& array
, int point
);
129 static int GetWidthFromFloatRect(const wxRichTextFloatRectMapArray
& array
, int index
, int startY
, int endY
);
131 static void FreeFloatRectMapArray(wxRichTextFloatRectMapArray
& array
);
133 static void DrawFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
);
135 static int HitTestFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& WXUNUSED(dc
), const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, int flags
);
138 wxRichTextFloatRectMapArray m_left
;
139 wxRichTextFloatRectMapArray m_right
;
141 wxRect m_availableRect
;
142 wxRichTextParagraph
* m_para
;
146 bool wxRichTextFloatCollector::DeleteFloat(wxRichTextObject
* obj
)
149 for (i
= 0; i
< m_left
.GetCount(); i
++)
151 if (m_left
[i
]->anchor
== obj
)
157 for (i
= 0; i
< m_right
.GetCount(); i
++)
159 if (m_right
[i
]->anchor
== obj
)
168 // Do we have this float already?
169 bool wxRichTextFloatCollector::HasFloat(wxRichTextObject
* obj
)
172 for (i
= 0; i
< m_left
.GetCount(); i
++)
174 if (m_left
[i
]->anchor
== obj
)
179 for (i
= 0; i
< m_right
.GetCount(); i
++)
181 if (m_right
[i
]->anchor
== obj
)
189 // Get floating objects
190 bool wxRichTextFloatCollector::GetFloatingObjects(wxRichTextObjectList
& objects
) const
193 for (i
= 0; i
< m_left
.GetCount(); i
++)
194 objects
.Append(m_left
[i
]->anchor
);
195 for (i
= 0; i
< m_right
.GetCount(); i
++)
196 objects
.Append(m_right
[i
]->anchor
);
202 * Binary search helper function
203 * The argument point is the Y coordinate, and this fuction
204 * always return the floating rect that contain this coordinate
205 * or under this coordinate.
207 int wxRichTextFloatCollector::SearchAdjacentRect(const wxRichTextFloatRectMapArray
& array
, int point
)
209 int end
= array
.GetCount() - 1;
222 int mid
= (start
+ end
) / 2;
223 if (array
[mid
]->startY
<= point
&& array
[mid
]->endY
>= point
)
225 else if (array
[mid
]->startY
> point
)
230 else if (array
[mid
]->endY
< point
)
240 int wxRichTextFloatCollector::GetWidthFromFloatRect(const wxRichTextFloatRectMapArray
& array
, int index
, int startY
, int endY
)
243 int len
= array
.GetCount();
245 wxASSERT(index
>= 0 && index
< len
);
247 if (array
[index
]->startY
< startY
&& array
[index
]->endY
> startY
)
248 ret
= ret
< array
[index
]->width
? array
[index
]->width
: ret
;
249 while (index
< len
&& array
[index
]->startY
<= endY
)
251 ret
= ret
< array
[index
]->width
? array
[index
]->width
: ret
;
258 wxRichTextFloatCollector::wxRichTextFloatCollector(const wxRect
& rect
) : m_left(wxRichTextFloatRectMapCmp
), m_right(wxRichTextFloatRectMapCmp
)
260 m_availableRect
= rect
;
264 void wxRichTextFloatCollector::FreeFloatRectMapArray(wxRichTextFloatRectMapArray
& array
)
266 int len
= array
.GetCount();
267 for (int i
= 0; i
< len
; i
++)
271 wxRichTextFloatCollector::~wxRichTextFloatCollector()
273 FreeFloatRectMapArray(m_left
);
274 FreeFloatRectMapArray(m_right
);
277 int wxRichTextFloatCollector::GetFitPosition(const wxRichTextFloatRectMapArray
& array
, int start
, int height
) const
279 if (array
.GetCount() == 0)
282 int i
= SearchAdjacentRect(array
, start
);
284 while (i
< (int) array
.GetCount())
286 if (array
[i
]->startY
- last
>= height
)
288 last
= array
[i
]->endY
;
295 int wxRichTextFloatCollector::GetFitPosition(int direction
, int start
, int height
) const
297 if (direction
== wxTEXT_BOX_ATTR_FLOAT_LEFT
)
298 return GetFitPosition(m_left
, start
, height
);
299 else if (direction
== wxTEXT_BOX_ATTR_FLOAT_RIGHT
)
300 return GetFitPosition(m_right
, start
, height
);
303 wxASSERT("Never should be here");
308 // Adds a floating image to the float collector.
309 // The actual positioning is done by wxRichTextParagraph::LayoutFloat.
310 void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph
* para
, wxRichTextObject
* floating
)
312 int direction
= floating
->GetFloatDirection();
314 wxPoint pos
= floating
->GetPosition();
315 wxSize size
= floating
->GetCachedSize();
316 wxRichTextFloatRectMap
*map
= new wxRichTextFloatRectMap(pos
.y
, pos
.y
+ size
.y
, size
.x
, floating
);
319 case wxTEXT_BOX_ATTR_FLOAT_NONE
:
322 case wxTEXT_BOX_ATTR_FLOAT_LEFT
:
323 // Just a not-enough simple assertion
324 wxASSERT (m_left
.Index(map
) == wxNOT_FOUND
);
327 case wxTEXT_BOX_ATTR_FLOAT_RIGHT
:
328 wxASSERT (m_right
.Index(map
) == wxNOT_FOUND
);
333 wxASSERT("Unrecognised float attribute.");
339 void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph
* para
)
341 wxRichTextObjectList::compatibility_iterator node
= para
->GetChildren().GetFirst();
344 wxRichTextObject
* floating
= node
->GetData();
346 if (floating
->IsFloating())
348 CollectFloat(para
, floating
);
351 node
= node
->GetNext();
357 wxRichTextParagraph
* wxRichTextFloatCollector::LastParagraph()
362 wxRect
wxRichTextFloatCollector::GetAvailableRect(int startY
, int endY
)
364 int widthLeft
= 0, widthRight
= 0;
365 if (m_left
.GetCount() != 0)
367 int i
= SearchAdjacentRect(m_left
, startY
);
368 if (i
< (int) m_left
.GetCount())
369 widthLeft
= GetWidthFromFloatRect(m_left
, i
, startY
, endY
);
371 if (m_right
.GetCount() != 0)
373 int j
= SearchAdjacentRect(m_right
, startY
);
374 if (j
< (int) m_right
.GetCount())
375 widthRight
= GetWidthFromFloatRect(m_right
, j
, startY
, endY
);
378 // TODO: actually we want to use the actual image positions to find the
379 // available remaining space, since the image might not be right up against
380 // the left or right edge of the container.
381 return wxRect(widthLeft
+ m_availableRect
.x
, 0, m_availableRect
.width
- widthLeft
- widthRight
, 0);
384 int wxRichTextFloatCollector::GetLastRectBottom()
387 int len
= m_left
.GetCount();
389 ret
= ret
> m_left
[len
-1]->endY
? ret
: m_left
[len
-1]->endY
;
391 len
= m_right
.GetCount();
393 ret
= ret
> m_right
[len
-1]->endY
? ret
: m_right
[len
-1]->endY
;
399 void wxRichTextFloatCollector::DrawFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& dc
, const wxRichTextRange
& WXUNUSED(range
), const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
402 int end
= rect
.y
+ rect
.height
;
404 i
= SearchAdjacentRect(array
, start
);
405 if (i
< 0 || i
>= (int) array
.GetCount())
407 j
= SearchAdjacentRect(array
, end
);
408 if (j
< 0 || j
>= (int) array
.GetCount())
409 j
= array
.GetCount() - 1;
412 wxRichTextObject
* obj
= array
[i
]->anchor
;
413 wxRichTextRange r
= obj
->GetRange();
414 obj
->Draw(dc
, r
, selection
, wxRect(obj
->GetPosition(), obj
->GetCachedSize()), descent
, style
);
419 void wxRichTextFloatCollector::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
421 if (m_left
.GetCount() > 0)
422 DrawFloat(m_left
, dc
, range
, selection
, rect
, descent
, style
);
423 if (m_right
.GetCount() > 0)
424 DrawFloat(m_right
, dc
, range
, selection
, rect
, descent
, style
);
427 int wxRichTextFloatCollector::HitTestFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& WXUNUSED(dc
), const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, int WXUNUSED(flags
))
430 if (array
.GetCount() == 0)
431 return wxRICHTEXT_HITTEST_NONE
;
432 i
= SearchAdjacentRect(array
, pt
.y
);
433 if (i
< 0 || i
>= (int) array
.GetCount())
434 return wxRICHTEXT_HITTEST_NONE
;
435 if (!array
[i
]->anchor
->IsShown())
436 return wxRICHTEXT_HITTEST_NONE
;
438 wxPoint point
= array
[i
]->anchor
->GetPosition();
439 wxSize size
= array
[i
]->anchor
->GetCachedSize();
440 if (point
.x
<= pt
.x
&& point
.x
+ size
.x
>= pt
.x
441 && point
.y
<= pt
.y
&& point
.y
+ size
.y
>= pt
.y
)
443 textPosition
= array
[i
]->anchor
->GetRange().GetStart();
444 * obj
= array
[i
]->anchor
;
445 if (pt
.x
> (pt
.x
+ pt
.x
+ size
.x
) / 2)
446 return wxRICHTEXT_HITTEST_BEFORE
;
448 return wxRICHTEXT_HITTEST_AFTER
;
451 return wxRICHTEXT_HITTEST_NONE
;
454 int wxRichTextFloatCollector::HitTest(wxDC
& dc
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, int flags
)
456 int ret
= HitTestFloat(m_left
, dc
, pt
, textPosition
, obj
, flags
);
457 if (ret
== wxRICHTEXT_HITTEST_NONE
)
459 ret
= HitTestFloat(m_right
, dc
, pt
, textPosition
, obj
, flags
);
464 // Helpers for efficiency
465 inline void wxCheckSetFont(wxDC
& dc
, const wxFont
& font
)
467 // JACS: did I do this some time ago when testing? Should we re-enable it?
469 const wxFont
& font1
= dc
.GetFont();
470 if (font1
.IsOk() && font
.IsOk())
472 if (font1
.GetPointSize() == font
.GetPointSize() &&
473 font1
.GetFamily() == font
.GetFamily() &&
474 font1
.GetStyle() == font
.GetStyle() &&
475 font1
.GetWeight() == font
.GetWeight() &&
476 font1
.GetUnderlined() == font
.GetUnderlined() &&
477 font1
.GetFamily() == font
.GetFamily() &&
478 font1
.GetFaceName() == font
.GetFaceName())
485 inline void wxCheckSetPen(wxDC
& dc
, const wxPen
& pen
)
487 const wxPen
& pen1
= dc
.GetPen();
488 if (pen1
.IsOk() && pen
.IsOk())
490 if (pen1
.GetWidth() == pen
.GetWidth() &&
491 pen1
.GetStyle() == pen
.GetStyle() &&
492 pen1
.GetColour() == pen
.GetColour())
498 inline void wxCheckSetBrush(wxDC
& dc
, const wxBrush
& brush
)
500 const wxBrush
& brush1
= dc
.GetBrush();
501 if (brush1
.IsOk() && brush
.IsOk())
503 if (brush1
.GetStyle() == brush
.GetStyle() &&
504 brush1
.GetColour() == brush
.GetColour())
512 * This is the base for drawable objects.
515 IMPLEMENT_CLASS(wxRichTextObject
, wxObject
)
517 wxRichTextObject::wxRichTextObject(wxRichTextObject
* parent
)
525 wxRichTextObject::~wxRichTextObject()
529 void wxRichTextObject::Dereference()
537 void wxRichTextObject::Copy(const wxRichTextObject
& obj
)
540 m_maxSize
= obj
.m_maxSize
;
541 m_minSize
= obj
.m_minSize
;
543 m_range
= obj
.m_range
;
544 m_ownRange
= obj
.m_ownRange
;
545 m_attributes
= obj
.m_attributes
;
546 m_properties
= obj
.m_properties
;
547 m_descent
= obj
.m_descent
;
551 // Get/set the top-level container of this object.
552 wxRichTextParagraphLayoutBox
* wxRichTextObject::GetContainer() const
554 const wxRichTextObject
* p
= this;
559 return wxDynamicCast(p
, wxRichTextParagraphLayoutBox
);
566 void wxRichTextObject::SetMargins(int margin
)
568 SetMargins(margin
, margin
, margin
, margin
);
571 void wxRichTextObject::SetMargins(int leftMargin
, int rightMargin
, int topMargin
, int bottomMargin
)
573 GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().SetValue(leftMargin
, wxTEXT_ATTR_UNITS_PIXELS
);
574 GetAttributes().GetTextBoxAttr().GetMargins().GetRight().SetValue(rightMargin
, wxTEXT_ATTR_UNITS_PIXELS
);
575 GetAttributes().GetTextBoxAttr().GetMargins().GetTop().SetValue(topMargin
, wxTEXT_ATTR_UNITS_PIXELS
);
576 GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().SetValue(bottomMargin
, wxTEXT_ATTR_UNITS_PIXELS
);
579 int wxRichTextObject::GetLeftMargin() const
581 return GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().GetValue();
584 int wxRichTextObject::GetRightMargin() const
586 return GetAttributes().GetTextBoxAttr().GetMargins().GetRight().GetValue();
589 int wxRichTextObject::GetTopMargin() const
591 return GetAttributes().GetTextBoxAttr().GetMargins().GetTop().GetValue();
594 int wxRichTextObject::GetBottomMargin() const
596 return GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().GetValue();
599 // Calculate the available content space in the given rectangle, given the
600 // margins, border and padding specified in the object's attributes.
601 wxRect
wxRichTextObject::GetAvailableContentArea(wxDC
& dc
, const wxRect
& outerRect
) const
603 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
604 marginRect
= outerRect
;
605 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
609 // Invalidate the buffer. With no argument, invalidates whole buffer.
610 void wxRichTextObject::Invalidate(const wxRichTextRange
& invalidRange
)
612 if (invalidRange
!= wxRICHTEXT_NONE
)
614 SetCachedSize(wxDefaultSize
);
615 SetMaxSize(wxDefaultSize
);
616 SetMinSize(wxDefaultSize
);
620 // Convert units in tenths of a millimetre to device units
621 int wxRichTextObject::ConvertTenthsMMToPixels(wxDC
& dc
, int units
) const
626 scale
= GetBuffer()->GetScale();
627 int p
= ConvertTenthsMMToPixels(dc
.GetPPI().x
, units
, scale
);
632 // Convert units in tenths of a millimetre to device units
633 int wxRichTextObject::ConvertTenthsMMToPixels(int ppi
, int units
, double scale
)
635 // There are ppi pixels in 254.1 "1/10 mm"
637 double pixels
= ((double) units
* (double)ppi
) / 254.1;
641 // If the result is very small, make it at least one pixel in size.
642 if (pixels
== 0 && units
> 0)
648 // Convert units in pixels to tenths of a millimetre
649 int wxRichTextObject::ConvertPixelsToTenthsMM(wxDC
& dc
, int pixels
) const
654 scale
= GetBuffer()->GetScale();
656 return ConvertPixelsToTenthsMM(dc
.GetPPI().x
, p
, scale
);
659 int wxRichTextObject::ConvertPixelsToTenthsMM(int ppi
, int pixels
, double scale
)
661 // There are ppi pixels in 254.1 "1/10 mm"
663 double p
= double(pixels
);
668 int units
= int( p
* 254.1 / (double) ppi
);
672 // Draw the borders and background for the given rectangle and attributes.
673 // Width and height are taken to be the outer margin size, not the content.
674 bool wxRichTextObject::DrawBoxAttributes(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& attr
, const wxRect
& boxRect
, int flags
)
676 // Assume boxRect is the area around the content
677 wxRect marginRect
= boxRect
;
678 wxRect contentRect
, borderRect
, paddingRect
, outlineRect
;
680 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
682 // Margin is transparent. Draw background from margin.
683 if (attr
.HasBackgroundColour() || (flags
& wxRICHTEXT_DRAW_SELECTED
))
686 if (flags
& wxRICHTEXT_DRAW_SELECTED
)
688 // TODO: get selection colour from control?
689 colour
= wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
);
692 colour
= attr
.GetBackgroundColour();
695 wxBrush
brush(colour
);
699 dc
.DrawRectangle(borderRect
);
702 if (flags
& wxRICHTEXT_DRAW_GUIDELINES
)
704 wxRichTextAttr editBorderAttr
= attr
;
705 // TODO: make guideline colour configurable
706 editBorderAttr
.GetTextBoxAttr().GetBorder().SetColour(*wxLIGHT_GREY
);
707 editBorderAttr
.GetTextBoxAttr().GetBorder().SetWidth(1, wxTEXT_ATTR_UNITS_PIXELS
);
708 editBorderAttr
.GetTextBoxAttr().GetBorder().SetStyle(wxTEXT_BOX_ATTR_BORDER_SOLID
);
710 DrawBorder(dc
, buffer
, editBorderAttr
.GetTextBoxAttr().GetBorder(), borderRect
, flags
);
713 if (attr
.GetTextBoxAttr().GetBorder().IsValid())
714 DrawBorder(dc
, buffer
, attr
.GetTextBoxAttr().GetBorder(), borderRect
);
716 if (attr
.GetTextBoxAttr().GetOutline().IsValid())
717 DrawBorder(dc
, buffer
, attr
.GetTextBoxAttr().GetOutline(), outlineRect
);
723 bool wxRichTextObject::DrawBorder(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxTextAttrBorders
& attr
, const wxRect
& rect
, int WXUNUSED(flags
))
725 int borderLeft
= 0, borderRight
= 0, borderTop
= 0, borderBottom
= 0;
726 wxTextAttrDimensionConverter
converter(dc
, buffer
? buffer
->GetScale() : 1.0);
728 if (attr
.GetLeft().IsValid() && attr
.GetLeft().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
730 borderLeft
= converter
.GetPixels(attr
.GetLeft().GetWidth());
731 wxColour
col(attr
.GetLeft().GetColour());
733 // If pen width is > 1, resorts to a solid rectangle.
736 int penStyle
= wxSOLID
;
737 if (attr
.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
739 else if (attr
.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
740 penStyle
= wxLONG_DASH
;
741 wxPen
pen(col
, 1, penStyle
);
743 dc
.DrawLine(rect
.x
, rect
.y
, rect
.x
, rect
.y
+ rect
.height
);
746 else if (borderLeft
> 1)
752 dc
.DrawRectangle(rect
.x
, rect
.y
, borderLeft
, rect
.height
);
756 if (attr
.GetRight().IsValid() && attr
.GetRight().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
758 borderRight
= converter
.GetPixels(attr
.GetRight().GetWidth());
760 wxColour
col(attr
.GetRight().GetColour());
762 // If pen width is > 1, resorts to a solid rectangle.
763 if (borderRight
== 1)
765 int penStyle
= wxSOLID
;
766 if (attr
.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
768 else if (attr
.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
769 penStyle
= wxLONG_DASH
;
770 wxPen
pen(col
, 1, penStyle
);
772 dc
.DrawLine(rect
.x
+ rect
.width
, rect
.y
, rect
.x
+ rect
.width
, rect
.y
+ rect
.height
+ 1);
775 else if (borderRight
> 1)
781 dc
.DrawRectangle(rect
.x
+ rect
.width
- borderRight
, rect
.y
, borderRight
, rect
.height
);
785 if (attr
.GetTop().IsValid() && attr
.GetTop().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
787 borderTop
= converter
.GetPixels(attr
.GetTop().GetWidth());
789 wxColour
col(attr
.GetTop().GetColour());
791 // If pen width is > 1, resorts to a solid rectangle.
794 int penStyle
= wxSOLID
;
795 if (attr
.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
797 else if (attr
.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
798 penStyle
= wxLONG_DASH
;
799 wxPen
pen(col
, 1, penStyle
);
801 dc
.DrawLine(rect
.x
, rect
.y
, rect
.x
+ rect
.width
, rect
.y
);
804 else if (borderTop
> 1)
810 dc
.DrawRectangle(rect
.x
, rect
.y
, rect
.width
, borderTop
);
814 if (attr
.GetBottom().IsValid() && attr
.GetBottom().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
816 borderBottom
= converter
.GetPixels(attr
.GetBottom().GetWidth());
817 wxColour
col(attr
.GetTop().GetColour());
819 // If pen width is > 1, resorts to a solid rectangle.
820 if (borderBottom
== 1)
822 int penStyle
= wxSOLID
;
823 if (attr
.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
825 else if (attr
.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
826 penStyle
= wxLONG_DASH
;
827 wxPen
pen(col
, 1, penStyle
);
829 dc
.DrawLine(rect
.x
, rect
.y
+ rect
.height
, rect
.x
+ rect
.width
, rect
.y
+ rect
.height
);
832 else if (borderBottom
> 1)
838 dc
.DrawRectangle(rect
.x
, rect
.y
+ rect
.height
- borderBottom
, rect
.width
, borderBottom
);
845 // Get the various rectangles of the box model in pixels. You can either specify contentRect (inner)
846 // or marginRect (outer), and the other must be the default rectangle (no width or height).
847 // Note that the outline doesn't affect the position of the rectangle, it's drawn in whatever space
850 // | Margin | Border | Padding | CONTENT | Padding | Border | Margin |
852 bool wxRichTextObject::GetBoxRects(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& attr
, wxRect
& marginRect
, wxRect
& borderRect
, wxRect
& contentRect
, wxRect
& paddingRect
, wxRect
& outlineRect
)
854 int borderLeft
= 0, borderRight
= 0, borderTop
= 0, borderBottom
= 0;
855 int outlineLeft
= 0, outlineRight
= 0, outlineTop
= 0, outlineBottom
= 0;
856 int paddingLeft
= 0, paddingRight
= 0, paddingTop
= 0, paddingBottom
= 0;
857 int marginLeft
= 0, marginRight
= 0, marginTop
= 0, marginBottom
= 0;
859 wxTextAttrDimensionConverter
converter(dc
, buffer
? buffer
->GetScale() : 1.0);
861 if (attr
.GetTextBoxAttr().GetMargins().GetLeft().IsValid())
862 marginLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetLeft());
863 if (attr
.GetTextBoxAttr().GetMargins().GetRight().IsValid())
864 marginRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetRight());
865 if (attr
.GetTextBoxAttr().GetMargins().GetTop().IsValid())
866 marginTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetTop());
867 if (attr
.GetTextBoxAttr().GetMargins().GetBottom().IsValid())
868 marginBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetBottom());
870 if (attr
.GetTextBoxAttr().GetBorder().GetLeft().GetWidth().IsValid())
871 borderLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetLeft().GetWidth());
872 if (attr
.GetTextBoxAttr().GetBorder().GetRight().GetWidth().IsValid())
873 borderRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetRight().GetWidth());
874 if (attr
.GetTextBoxAttr().GetBorder().GetTop().GetWidth().IsValid())
875 borderTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetTop().GetWidth());
876 if (attr
.GetTextBoxAttr().GetBorder().GetBottom().GetWidth().IsValid())
877 borderBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetBottom().GetWidth());
879 if (attr
.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
880 paddingLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetLeft());
881 if (attr
.GetTextBoxAttr().GetPadding().GetRight().IsValid())
882 paddingRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetRight());
883 if (attr
.GetTextBoxAttr().GetPadding().GetTop().IsValid())
884 paddingTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetTop());
885 if (attr
.GetTextBoxAttr().GetPadding().GetBottom().IsValid())
886 paddingBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetBottom());
888 if (attr
.GetTextBoxAttr().GetOutline().GetLeft().GetWidth().IsValid())
889 outlineLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetLeft().GetWidth());
890 if (attr
.GetTextBoxAttr().GetOutline().GetRight().GetWidth().IsValid())
891 outlineRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetRight().GetWidth());
892 if (attr
.GetTextBoxAttr().GetOutline().GetTop().GetWidth().IsValid())
893 outlineTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetTop().GetWidth());
894 if (attr
.GetTextBoxAttr().GetOutline().GetBottom().GetWidth().IsValid())
895 outlineBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetBottom().GetWidth());
897 int leftTotal
= marginLeft
+ borderLeft
+ paddingLeft
;
898 int rightTotal
= marginRight
+ borderRight
+ paddingRight
;
899 int topTotal
= marginTop
+ borderTop
+ paddingTop
;
900 int bottomTotal
= marginBottom
+ borderBottom
+ paddingBottom
;
902 if (marginRect
!= wxRect())
904 contentRect
.x
= marginRect
.x
+ leftTotal
;
905 contentRect
.y
= marginRect
.y
+ topTotal
;
906 contentRect
.width
= marginRect
.width
- (leftTotal
+ rightTotal
);
907 contentRect
.height
= marginRect
.height
- (topTotal
+ bottomTotal
);
911 marginRect
.x
= contentRect
.x
- leftTotal
;
912 marginRect
.y
= contentRect
.y
- topTotal
;
913 marginRect
.width
= contentRect
.width
+ (leftTotal
+ rightTotal
);
914 marginRect
.height
= contentRect
.height
+ (topTotal
+ bottomTotal
);
917 borderRect
.x
= marginRect
.x
+ marginLeft
;
918 borderRect
.y
= marginRect
.y
+ marginTop
;
919 borderRect
.width
= marginRect
.width
- (marginLeft
+ marginRight
);
920 borderRect
.height
= marginRect
.height
- (marginTop
+ marginBottom
);
922 paddingRect
.x
= marginRect
.x
+ marginLeft
+ borderLeft
;
923 paddingRect
.y
= marginRect
.y
+ marginTop
+ borderTop
;
924 paddingRect
.width
= marginRect
.width
- (marginLeft
+ marginRight
+ borderLeft
+ borderRight
);
925 paddingRect
.height
= marginRect
.height
- (marginTop
+ marginBottom
+ borderTop
+ borderBottom
);
927 // The outline is outside the margin and doesn't influence the overall box position or content size.
928 outlineRect
.x
= marginRect
.x
- outlineLeft
;
929 outlineRect
.y
= marginRect
.y
- outlineTop
;
930 outlineRect
.width
= marginRect
.width
+ (outlineLeft
+ outlineRight
);
931 outlineRect
.height
= marginRect
.height
+ (outlineTop
+ outlineBottom
);
936 // Get the total margin for the object in pixels, taking into account margin, padding and border size
937 bool wxRichTextObject::GetTotalMargin(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& attr
, int& leftMargin
, int& rightMargin
,
938 int& topMargin
, int& bottomMargin
)
940 // Assume boxRect is the area around the content
941 wxRect contentRect
, marginRect
, borderRect
, paddingRect
, outlineRect
;
942 marginRect
= wxRect(0, 0, 1000, 1000);
944 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
946 leftMargin
= contentRect
.GetLeft() - marginRect
.GetLeft();
947 rightMargin
= marginRect
.GetRight() - contentRect
.GetRight();
948 topMargin
= contentRect
.GetTop() - marginRect
.GetTop();
949 bottomMargin
= marginRect
.GetBottom() - contentRect
.GetBottom();
954 // Returns the rectangle which the child has available to it given restrictions specified in the
955 // child attribute, e.g. 50% width of the parent, 400 pixels, x position 20% of the parent, etc.
956 // availableContainerSpace might be a parent that the cell has to compute its width relative to.
957 // E.g. a cell that's 50% of its parent.
958 wxRect
wxRichTextObject::AdjustAvailableSpace(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& WXUNUSED(parentAttr
), const wxRichTextAttr
& childAttr
, const wxRect
& availableParentSpace
, const wxRect
& availableContainerSpace
)
960 wxRect rect
= availableParentSpace
;
963 scale
= buffer
->GetScale();
965 wxTextAttrDimensionConverter
converter(dc
, scale
, availableContainerSpace
.GetSize());
967 if (childAttr
.GetTextBoxAttr().GetWidth().IsValid())
968 rect
.width
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetWidth());
970 if (childAttr
.GetTextBoxAttr().GetHeight().IsValid())
971 rect
.height
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetHeight());
973 // Can specify either left or right for the position (we're assuming we can't
974 // set the left and right edges to effectively set the size. Would we want to do that?)
975 if (childAttr
.GetTextBoxAttr().GetPosition().GetLeft().IsValid())
977 rect
.x
= rect
.x
+ converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetLeft());
979 else if (childAttr
.GetTextBoxAttr().GetPosition().GetRight().IsValid())
981 int x
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetRight());
982 if (childAttr
.GetTextBoxAttr().GetPosition().GetRight().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE
)
983 rect
.x
= availableContainerSpace
.x
+ availableContainerSpace
.width
- rect
.width
;
988 if (childAttr
.GetTextBoxAttr().GetPosition().GetTop().IsValid())
990 rect
.y
= rect
.y
+ converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetTop());
992 else if (childAttr
.GetTextBoxAttr().GetPosition().GetBottom().IsValid())
994 int y
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetBottom());
995 if (childAttr
.GetTextBoxAttr().GetPosition().GetBottom().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE
)
996 rect
.y
= availableContainerSpace
.y
+ availableContainerSpace
.height
- rect
.height
;
1001 if (rect
.GetWidth() > availableParentSpace
.GetWidth())
1002 rect
.SetWidth(availableParentSpace
.GetWidth());
1007 // Dump to output stream for debugging
1008 void wxRichTextObject::Dump(wxTextOutputStream
& stream
)
1010 stream
<< GetClassInfo()->GetClassName() << wxT("\n");
1011 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");
1012 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");
1015 // Gets the containing buffer
1016 wxRichTextBuffer
* wxRichTextObject::GetBuffer() const
1018 const wxRichTextObject
* obj
= this;
1019 while (obj
&& !obj
->IsKindOf(CLASSINFO(wxRichTextBuffer
)))
1020 obj
= obj
->GetParent();
1021 return wxDynamicCast(obj
, wxRichTextBuffer
);
1024 // Get the absolute object position, by traversing up the child/parent hierarchy
1025 wxPoint
wxRichTextObject::GetAbsolutePosition() const
1027 wxPoint pt
= GetPosition();
1029 wxRichTextObject
* p
= GetParent();
1032 pt
= pt
+ p
->GetPosition();
1039 // Hit-testing: returns a flag indicating hit test details, plus
1040 // information about position
1041 int wxRichTextObject::HitTest(wxDC
& WXUNUSED(dc
), const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int WXUNUSED(flags
))
1044 return wxRICHTEXT_HITTEST_NONE
;
1046 wxRect rect
= GetRect();
1047 if (pt
.x
>= rect
.x
&& pt
.x
< rect
.x
+ rect
.width
&&
1048 pt
.y
>= rect
.y
&& pt
.y
< rect
.y
+ rect
.height
)
1051 *contextObj
= GetParentContainer();
1052 textPosition
= GetRange().GetStart();
1053 return wxRICHTEXT_HITTEST_ON
;
1056 return wxRICHTEXT_HITTEST_NONE
;
1059 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1060 // lays out the object again using the maximum ('best') size
1061 bool wxRichTextObject::LayoutToBestSize(wxDC
& dc
, wxRichTextBuffer
* buffer
,
1062 const wxRichTextAttr
& parentAttr
, const wxRichTextAttr
& attr
,
1063 const wxRect
& availableParentSpace
, const wxRect
& availableContainerSpace
,
1066 wxRect availableChildRect
= AdjustAvailableSpace(dc
, buffer
, parentAttr
, attr
, availableParentSpace
, availableContainerSpace
);
1067 wxRect originalAvailableRect
= availableChildRect
;
1068 Layout(dc
, availableChildRect
, availableContainerSpace
, style
);
1070 wxSize maxSize
= GetMaxSize();
1072 // Don't ignore if maxSize.x is zero, since we need to redo the paragraph's lines
1074 if (!attr
.GetTextBoxAttr().GetWidth().IsValid() && maxSize
.x
< availableChildRect
.width
)
1076 // Redo the layout with a fixed, minimum size this time.
1077 Invalidate(wxRICHTEXT_ALL
);
1078 wxRichTextAttr
newAttr(attr
);
1079 newAttr
.GetTextBoxAttr().GetWidth().SetValue(maxSize
.x
, wxTEXT_ATTR_UNITS_PIXELS
);
1080 newAttr
.GetTextBoxAttr().GetWidth().SetPosition(wxTEXT_BOX_ATTR_POSITION_ABSOLUTE
);
1082 availableChildRect
= AdjustAvailableSpace(dc
, buffer
, parentAttr
, newAttr
, availableParentSpace
, availableContainerSpace
);
1084 // If a paragraph, align the whole paragraph.
1085 // Problem with this: if we're limited by a floating object, a line may be centered
1086 // w.r.t. the smaller resulting box rather than the actual available width.
1087 if (attr
.HasAlignment() && !GetContainer()->GetFloatCollector()->HasFloats()) // FIXME: aligning whole paragraph not compatible with floating objects
1089 // centering, right-justification
1090 if (GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_CENTRE
)
1092 availableChildRect
.x
= (originalAvailableRect
.GetWidth() - availableChildRect
.GetWidth())/2 + availableChildRect
.x
;
1094 else if (GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_RIGHT
)
1096 availableChildRect
.x
= availableChildRect
.x
+ originalAvailableRect
.GetWidth() - availableChildRect
.GetWidth();
1100 Layout(dc
, availableChildRect
, availableContainerSpace
, style
);
1114 // Move the object recursively, by adding the offset from old to new
1115 void wxRichTextObject::Move(const wxPoint
& pt
)
1122 * wxRichTextCompositeObject
1123 * This is the base for drawable objects.
1126 IMPLEMENT_CLASS(wxRichTextCompositeObject
, wxRichTextObject
)
1128 wxRichTextCompositeObject::wxRichTextCompositeObject(wxRichTextObject
* parent
):
1129 wxRichTextObject(parent
)
1133 wxRichTextCompositeObject::~wxRichTextCompositeObject()
1138 /// Get the nth child
1139 wxRichTextObject
* wxRichTextCompositeObject::GetChild(size_t n
) const
1141 wxASSERT ( n
< m_children
.GetCount() );
1143 return m_children
.Item(n
)->GetData();
1146 /// Append a child, returning the position
1147 size_t wxRichTextCompositeObject::AppendChild(wxRichTextObject
* child
)
1149 m_children
.Append(child
);
1150 child
->SetParent(this);
1151 return m_children
.GetCount() - 1;
1154 /// Insert the child in front of the given object, or at the beginning
1155 bool wxRichTextCompositeObject::InsertChild(wxRichTextObject
* child
, wxRichTextObject
* inFrontOf
)
1159 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(inFrontOf
);
1160 m_children
.Insert(node
, child
);
1163 m_children
.Insert(child
);
1164 child
->SetParent(this);
1169 /// Delete the child
1170 bool wxRichTextCompositeObject::RemoveChild(wxRichTextObject
* child
, bool deleteChild
)
1172 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(child
);
1175 wxRichTextObject
* obj
= node
->GetData();
1176 m_children
.Erase(node
);
1185 /// Delete all children
1186 bool wxRichTextCompositeObject::DeleteChildren()
1188 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1191 wxRichTextObjectList::compatibility_iterator oldNode
= node
;
1193 wxRichTextObject
* child
= node
->GetData();
1194 child
->Dereference(); // Only delete if reference count is zero
1196 node
= node
->GetNext();
1197 m_children
.Erase(oldNode
);
1203 /// Get the child count
1204 size_t wxRichTextCompositeObject::GetChildCount() const
1206 return m_children
.GetCount();
1210 void wxRichTextCompositeObject::Copy(const wxRichTextCompositeObject
& obj
)
1212 wxRichTextObject::Copy(obj
);
1216 wxRichTextObjectList::compatibility_iterator node
= obj
.m_children
.GetFirst();
1219 wxRichTextObject
* child
= node
->GetData();
1220 wxRichTextObject
* newChild
= child
->Clone();
1221 newChild
->SetParent(this);
1222 m_children
.Append(newChild
);
1224 node
= node
->GetNext();
1228 /// Hit-testing: returns a flag indicating hit test details, plus
1229 /// information about position
1230 int wxRichTextCompositeObject::HitTest(wxDC
& dc
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
1233 return wxRICHTEXT_HITTEST_NONE
;
1235 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1238 wxRichTextObject
* child
= node
->GetData();
1240 if (child
->IsShown() && child
->IsTopLevel() && (flags
& wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS
))
1242 // Just check if we hit the overall object
1243 int ret
= child
->wxRichTextObject::HitTest(dc
, pt
, textPosition
, obj
, contextObj
, flags
);
1244 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
1247 else if (child
->IsShown())
1249 int ret
= child
->HitTest(dc
, pt
, textPosition
, obj
, contextObj
, flags
);
1250 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
1254 node
= node
->GetNext();
1257 return wxRICHTEXT_HITTEST_NONE
;
1260 /// Finds the absolute position and row height for the given character position
1261 bool wxRichTextCompositeObject::FindPosition(wxDC
& dc
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
1263 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1266 wxRichTextObject
* child
= node
->GetData();
1268 // Don't recurse if the child is a top-level object,
1269 // such as a text box, because the character position will no longer
1270 // apply. By definition, a top-level object has its own range of
1271 // character positions.
1272 if (!child
->IsTopLevel() && child
->FindPosition(dc
, index
, pt
, height
, forceLineStart
))
1275 node
= node
->GetNext();
1282 void wxRichTextCompositeObject::CalculateRange(long start
, long& end
)
1284 long current
= start
;
1285 long lastEnd
= current
;
1293 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1296 wxRichTextObject
* child
= node
->GetData();
1299 child
->CalculateRange(current
, childEnd
);
1302 current
= childEnd
+ 1;
1304 node
= node
->GetNext();
1309 // A top-level object always has a range of size 1,
1310 // because its children don't count at this level.
1312 m_range
.SetRange(start
, start
);
1314 // An object with no children has zero length
1315 if (m_children
.GetCount() == 0)
1317 m_ownRange
.SetRange(0, lastEnd
);
1323 // An object with no children has zero length
1324 if (m_children
.GetCount() == 0)
1327 m_range
.SetRange(start
, end
);
1331 /// Delete range from layout.
1332 bool wxRichTextCompositeObject::DeleteRange(const wxRichTextRange
& range
)
1334 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1338 wxRichTextObject
* obj
= (wxRichTextObject
*) node
->GetData();
1339 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
1341 // Delete the range in each paragraph
1343 // When a chunk has been deleted, internally the content does not
1344 // now match the ranges.
1345 // However, so long as deletion is not done on the same object twice this is OK.
1346 // If you may delete content from the same object twice, recalculate
1347 // the ranges inbetween DeleteRange calls by calling CalculateRanges, and
1348 // adjust the range you're deleting accordingly.
1350 if (!obj
->GetRange().IsOutside(range
))
1352 // No need to delete within a top-level object; just removing this object will do fine
1353 if (!obj
->IsTopLevel())
1354 obj
->DeleteRange(range
);
1356 // Delete an empty object, or paragraph within this range.
1357 if (obj
->IsEmpty() ||
1358 (range
.GetStart() <= obj
->GetRange().GetStart() && range
.GetEnd() >= obj
->GetRange().GetEnd()))
1360 // An empty paragraph has length 1, so won't be deleted unless the
1361 // whole range is deleted.
1362 RemoveChild(obj
, true);
1372 /// Get any text in this object for the given range
1373 wxString
wxRichTextCompositeObject::GetTextForRange(const wxRichTextRange
& range
) const
1376 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1379 wxRichTextObject
* child
= node
->GetData();
1380 wxRichTextRange childRange
= range
;
1381 if (!child
->GetRange().IsOutside(range
))
1383 childRange
.LimitTo(child
->GetRange());
1385 wxString childText
= child
->GetTextForRange(childRange
);
1389 node
= node
->GetNext();
1395 /// Get the child object at the given character position
1396 wxRichTextObject
* wxRichTextCompositeObject::GetChildAtPosition(long pos
) const
1398 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1401 wxRichTextObject
* child
= node
->GetData();
1402 if (child
->GetRange().GetStart() == pos
)
1404 node
= node
->GetNext();
1409 /// Recursively merge all pieces that can be merged.
1410 bool wxRichTextCompositeObject::Defragment(const wxRichTextRange
& range
)
1412 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1415 wxRichTextObject
* child
= node
->GetData();
1416 if (range
== wxRICHTEXT_ALL
|| !child
->GetRange().IsOutside(range
))
1418 wxRichTextCompositeObject
* composite
= wxDynamicCast(child
, wxRichTextCompositeObject
);
1420 composite
->Defragment();
1422 if (node
->GetNext())
1424 wxRichTextObject
* nextChild
= node
->GetNext()->GetData();
1425 if (child
->CanMerge(nextChild
) && child
->Merge(nextChild
))
1427 nextChild
->Dereference();
1428 m_children
.Erase(node
->GetNext());
1430 // Don't set node -- we'll see if we can merge again with the next
1434 node
= node
->GetNext();
1437 node
= node
->GetNext();
1440 node
= node
->GetNext();
1443 // Delete any remaining empty objects, but leave at least one empty object per composite object.
1444 if (GetChildCount() > 1)
1446 node
= m_children
.GetFirst();
1449 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
1450 wxRichTextObject
* child
= node
->GetData();
1451 if (range
== wxRICHTEXT_ALL
|| !child
->GetRange().IsOutside(range
))
1453 if (child
->IsEmpty())
1455 child
->Dereference();
1456 m_children
.Erase(node
);
1461 node
= node
->GetNext();
1468 /// Dump to output stream for debugging
1469 void wxRichTextCompositeObject::Dump(wxTextOutputStream
& stream
)
1471 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1474 wxRichTextObject
* child
= node
->GetData();
1475 child
->Dump(stream
);
1476 node
= node
->GetNext();
1480 /// Get/set the object size for the given range. Returns false if the range
1481 /// is invalid for this object.
1482 bool wxRichTextCompositeObject::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
1484 if (!range
.IsWithin(GetRange()))
1489 wxArrayInt childExtents
;
1496 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1499 wxRichTextObject
* child
= node
->GetData();
1500 if (!child
->GetRange().IsOutside(range
))
1502 // Floating objects have a zero size within the paragraph.
1503 if (child
->IsFloating())
1508 if (partialExtents
->GetCount() > 0)
1509 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
1513 partialExtents
->Add(0 /* zero size */ + lastSize
);
1520 wxRichTextRange rangeToUse
= range
;
1521 rangeToUse
.LimitTo(child
->GetRange());
1522 if (child
->IsTopLevel())
1523 rangeToUse
= child
->GetOwnRange();
1525 int childDescent
= 0;
1527 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we're already cached the size,
1528 // but it's only going to be used after caching has taken place.
1529 if ((flags
& wxRICHTEXT_HEIGHT_ONLY
) && child
->GetCachedSize().y
!= 0)
1531 childDescent
= child
->GetDescent();
1532 childSize
= child
->GetCachedSize();
1534 sz
.y
= wxMax(sz
.y
, childSize
.y
);
1535 sz
.x
+= childSize
.x
;
1536 descent
= wxMax(descent
, childDescent
);
1538 else if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
), p
))
1540 sz
.y
= wxMax(sz
.y
, childSize
.y
);
1541 sz
.x
+= childSize
.x
;
1542 descent
= wxMax(descent
, childDescent
);
1544 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange() || child
->IsTopLevel()))
1546 child
->SetCachedSize(childSize
);
1547 child
->SetDescent(childDescent
);
1553 if (partialExtents
->GetCount() > 0)
1554 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
1559 for (i
= 0; i
< childExtents
.GetCount(); i
++)
1561 partialExtents
->Add(childExtents
[i
] + lastSize
);
1571 node
= node
->GetNext();
1577 // Invalidate the buffer. With no argument, invalidates whole buffer.
1578 void wxRichTextCompositeObject::Invalidate(const wxRichTextRange
& invalidRange
)
1580 wxRichTextObject::Invalidate(invalidRange
);
1582 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1585 wxRichTextObject
* child
= node
->GetData();
1586 if (invalidRange
!= wxRICHTEXT_ALL
&& invalidRange
!= wxRICHTEXT_NONE
&& child
->GetRange().IsOutside(invalidRange
))
1590 else if (child
->IsTopLevel())
1592 if (invalidRange
== wxRICHTEXT_NONE
)
1593 child
->Invalidate(wxRICHTEXT_NONE
);
1595 child
->Invalidate(wxRICHTEXT_ALL
); // All children must be invalidated if within parent range
1598 child
->Invalidate(invalidRange
);
1599 node
= node
->GetNext();
1603 // Move the object recursively, by adding the offset from old to new
1604 void wxRichTextCompositeObject::Move(const wxPoint
& pt
)
1606 wxPoint oldPos
= GetPosition();
1608 wxPoint offset
= pt
- oldPos
;
1610 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1613 wxRichTextObject
* child
= node
->GetData();
1614 wxPoint childPos
= child
->GetPosition() + offset
;
1615 child
->Move(childPos
);
1616 node
= node
->GetNext();
1622 * wxRichTextParagraphLayoutBox
1623 * This box knows how to lay out paragraphs.
1626 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraphLayoutBox
, wxRichTextCompositeObject
)
1628 wxRichTextParagraphLayoutBox::wxRichTextParagraphLayoutBox(wxRichTextObject
* parent
):
1629 wxRichTextCompositeObject(parent
)
1634 wxRichTextParagraphLayoutBox::~wxRichTextParagraphLayoutBox()
1636 if (m_floatCollector
)
1638 delete m_floatCollector
;
1639 m_floatCollector
= NULL
;
1643 /// Initialize the object.
1644 void wxRichTextParagraphLayoutBox::Init()
1648 // For now, assume is the only box and has no initial size.
1649 m_range
= wxRichTextRange(0, -1);
1650 m_ownRange
= wxRichTextRange(0, -1);
1652 m_invalidRange
= wxRICHTEXT_ALL
;
1655 m_partialParagraph
= false;
1656 m_floatCollector
= NULL
;
1659 void wxRichTextParagraphLayoutBox::Clear()
1663 if (m_floatCollector
)
1664 delete m_floatCollector
;
1665 m_floatCollector
= NULL
;
1666 m_partialParagraph
= false;
1670 void wxRichTextParagraphLayoutBox::Copy(const wxRichTextParagraphLayoutBox
& obj
)
1674 wxRichTextCompositeObject::Copy(obj
);
1676 m_partialParagraph
= obj
.m_partialParagraph
;
1677 m_defaultAttributes
= obj
.m_defaultAttributes
;
1680 // Gather information about floating objects; only gather floats for those paragraphs that
1681 // will not be formatted again due to optimization, after which floats will be gathered per-paragraph
1683 bool wxRichTextParagraphLayoutBox::UpdateFloatingObjects(const wxRect
& availableRect
, wxRichTextObject
* untilObj
)
1685 if (m_floatCollector
!= NULL
)
1686 delete m_floatCollector
;
1687 m_floatCollector
= new wxRichTextFloatCollector(availableRect
);
1688 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1689 // Only gather floats up to the point we'll start formatting paragraphs.
1690 while (untilObj
&& node
&& node
->GetData() != untilObj
)
1692 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
1693 wxASSERT (child
!= NULL
);
1695 m_floatCollector
->CollectFloat(child
);
1696 node
= node
->GetNext();
1702 // Returns the style sheet associated with the overall buffer.
1703 wxRichTextStyleSheet
* wxRichTextParagraphLayoutBox::GetStyleSheet() const
1705 return GetBuffer() ? GetBuffer()->GetStyleSheet() : (wxRichTextStyleSheet
*) NULL
;
1708 // Get the number of floating objects at this level
1709 int wxRichTextParagraphLayoutBox::GetFloatingObjectCount() const
1711 if (m_floatCollector
)
1712 return m_floatCollector
->GetFloatingObjectCount();
1717 // Get a list of floating objects
1718 bool wxRichTextParagraphLayoutBox::GetFloatingObjects(wxRichTextObjectList
& objects
) const
1720 if (m_floatCollector
)
1722 return m_floatCollector
->GetFloatingObjects(objects
);
1729 void wxRichTextParagraphLayoutBox::UpdateRanges()
1733 start
= GetRange().GetStart();
1735 CalculateRange(start
, end
);
1739 int wxRichTextParagraphLayoutBox::HitTest(wxDC
& dc
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
1742 return wxRICHTEXT_HITTEST_NONE
;
1744 int ret
= wxRICHTEXT_HITTEST_NONE
;
1745 if (m_floatCollector
&& (flags
& wxRICHTEXT_HITTEST_NO_FLOATING_OBJECTS
) == 0)
1746 ret
= m_floatCollector
->HitTest(dc
, pt
, textPosition
, obj
, flags
);
1748 if (ret
== wxRICHTEXT_HITTEST_NONE
)
1749 return wxRichTextCompositeObject::HitTest(dc
, pt
, textPosition
, obj
, contextObj
, flags
);
1757 /// Draw the floating objects
1758 void wxRichTextParagraphLayoutBox::DrawFloats(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
1760 if (m_floatCollector
)
1761 m_floatCollector
->Draw(dc
, range
, selection
, rect
, descent
, style
);
1764 void wxRichTextParagraphLayoutBox::MoveAnchoredObjectToParagraph(wxRichTextParagraph
* from
, wxRichTextParagraph
* to
, wxRichTextObject
* obj
)
1769 from
->RemoveChild(obj
);
1770 to
->AppendChild(obj
);
1774 bool wxRichTextParagraphLayoutBox::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
1779 wxRect
thisRect(GetPosition(), GetCachedSize());
1782 if (selection
.IsValid() && GetParentContainer() != this && selection
.WithinSelection(GetRange().GetStart(), GetParentContainer()))
1783 flags
|= wxRICHTEXT_DRAW_SELECTED
;
1785 // Don't draw guidelines if at top level
1786 int theseFlags
= flags
;
1788 theseFlags
&= ~wxRICHTEXT_DRAW_GUIDELINES
;
1789 DrawBoxAttributes(dc
, GetBuffer(), GetAttributes(), thisRect
, theseFlags
);
1791 DrawFloats(dc
, range
, selection
, rect
, descent
, style
);
1792 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1795 wxRichTextObject
* child
= node
->GetData();
1797 if (child
&& !child
->GetRange().IsOutside(range
))
1799 wxRect
childRect(child
->GetPosition(), child
->GetCachedSize());
1800 wxRichTextRange childRange
= range
;
1801 if (child
->IsTopLevel())
1803 childRange
= child
->GetOwnRange();
1806 if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) == 0) && childRect
.GetTop() > rect
.GetBottom())
1811 else if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) == 0) && childRect
.GetBottom() < rect
.GetTop())
1816 child
->Draw(dc
, childRange
, selection
, rect
, descent
, style
);
1819 node
= node
->GetNext();
1824 /// Lay the item out
1825 bool wxRichTextParagraphLayoutBox::Layout(wxDC
& dc
, const wxRect
& rect
, const wxRect
& parentRect
, int style
)
1827 SetPosition(rect
.GetPosition());
1832 wxRect availableSpace
;
1833 bool formatRect
= (style
& wxRICHTEXT_LAYOUT_SPECIFIED_RECT
) == wxRICHTEXT_LAYOUT_SPECIFIED_RECT
;
1835 // If only laying out a specific area, the passed rect has a different meaning:
1836 // the visible part of the buffer. This is used in wxRichTextCtrl::OnSize,
1837 // so that during a size, only the visible part will be relaid out, or
1838 // it would take too long causing flicker. As an approximation, we assume that
1839 // everything up to the start of the visible area is laid out correctly.
1842 wxRect
rect2(0, 0, rect
.width
, rect
.height
);
1843 availableSpace
= GetAvailableContentArea(dc
, rect2
);
1845 // Invalidate the part of the buffer from the first visible line
1846 // to the end. If other parts of the buffer are currently invalid,
1847 // then they too will be taken into account if they are above
1848 // the visible point.
1850 wxRichTextLine
* line
= GetLineAtYPosition(rect
.y
);
1852 startPos
= line
->GetAbsoluteRange().GetStart();
1854 Invalidate(wxRichTextRange(startPos
, GetOwnRange().GetEnd()));
1858 availableSpace
= GetAvailableContentArea(dc
, rect
);
1861 int leftMargin
, rightMargin
, topMargin
, bottomMargin
;
1862 wxRichTextObject::GetTotalMargin(dc
, GetBuffer(), GetAttributes(), leftMargin
, rightMargin
,
1863 topMargin
, bottomMargin
);
1868 // The maximum paragraph maximum width, so we can set the overall maximum width for this object
1869 int maxMaxWidth
= 0;
1871 // The maximum paragraph minimum width, so we can set the overall minimum width for this object
1872 int maxMinWidth
= 0;
1874 // If we have vertical alignment, we must recalculate everything.
1875 bool hasVerticalAlignment
= (GetAttributes().GetTextBoxAttr().HasVerticalAlignment() &&
1876 (GetAttributes().GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP
));
1878 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1880 bool layoutAll
= true;
1882 // Get invalid range, rounding to paragraph start/end.
1883 wxRichTextRange invalidRange
= GetInvalidRange(true);
1885 if (invalidRange
== wxRICHTEXT_NONE
&& !formatRect
)
1888 if (invalidRange
== wxRICHTEXT_ALL
|| hasVerticalAlignment
)
1890 else // If we know what range is affected, start laying out from that point on.
1891 if (invalidRange
.GetStart() >= GetOwnRange().GetStart())
1893 wxRichTextParagraph
* firstParagraph
= GetParagraphAtPosition(invalidRange
.GetStart());
1896 wxRichTextObjectList::compatibility_iterator firstNode
= m_children
.Find(firstParagraph
);
1897 wxRichTextObjectList::compatibility_iterator previousNode
;
1899 previousNode
= firstNode
->GetPrevious();
1904 wxRichTextParagraph
* previousParagraph
= wxDynamicCast(previousNode
->GetData(), wxRichTextParagraph
);
1905 availableSpace
.y
= previousParagraph
->GetPosition().y
+ previousParagraph
->GetCachedSize().y
;
1908 // Now we're going to start iterating from the first affected paragraph.
1916 // Gather information about only those floating objects that will not be formatted,
1917 // after which floats will be gathered per-paragraph during layout.
1918 UpdateFloatingObjects(availableSpace
, node
? node
->GetData() : (wxRichTextObject
*) NULL
);
1920 // A way to force speedy rest-of-buffer layout (the 'else' below)
1921 bool forceQuickLayout
= false;
1923 // First get the size of the paragraphs we won't be laying out
1924 wxRichTextObjectList::compatibility_iterator n
= m_children
.GetFirst();
1925 while (n
&& n
!= node
)
1927 wxRichTextParagraph
* child
= wxDynamicCast(n
->GetData(), wxRichTextParagraph
);
1930 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
1931 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
1932 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
1939 // Assume this box only contains paragraphs
1941 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
1942 // Unsure if this is needed
1943 // wxCHECK_MSG( child, false, wxT("Unknown object in layout") );
1945 if (child
&& child
->IsShown())
1947 // TODO: what if the child hasn't been laid out (e.g. involved in Undo) but still has 'old' lines
1948 if ( !forceQuickLayout
&&
1950 child
->GetLines().IsEmpty() ||
1951 !child
->GetRange().IsOutside(invalidRange
)) )
1953 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1954 // lays out the object again using the minimum size
1955 child
->LayoutToBestSize(dc
, GetBuffer(),
1956 GetAttributes(), child
->GetAttributes(), availableSpace
, rect
, style
&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT
);
1958 // Layout must set the cached size
1959 availableSpace
.y
+= child
->GetCachedSize().y
;
1960 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
1961 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
1962 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
1964 // If we're just formatting the visible part of the buffer,
1965 // and we're now past the bottom of the window, and we don't have any
1966 // floating objects (since they may cause wrapping to change for the rest of the
1967 // the buffer), start quick layout.
1968 if (!hasVerticalAlignment
&& formatRect
&& child
->GetPosition().y
> rect
.GetBottom() && GetFloatingObjectCount() == 0)
1969 forceQuickLayout
= true;
1973 // We're outside the immediately affected range, so now let's just
1974 // move everything up or down. This assumes that all the children have previously
1975 // been laid out and have wrapped line lists associated with them.
1976 // TODO: check all paragraphs before the affected range.
1978 int inc
= availableSpace
.y
- child
->GetPosition().y
;
1982 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
1985 if (child
->GetLines().GetCount() == 0)
1987 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1988 // lays out the object again using the minimum size
1989 child
->LayoutToBestSize(dc
, GetBuffer(),
1990 GetAttributes(), child
->GetAttributes(), availableSpace
, rect
, style
&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT
);
1992 //child->Layout(dc, availableChildRect, style);
1995 child
->Move(wxPoint(child
->GetPosition().x
, child
->GetPosition().y
+ inc
));
1997 availableSpace
.y
+= child
->GetCachedSize().y
;
1998 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
1999 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
2000 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
2003 node
= node
->GetNext();
2009 node
= node
->GetNext();
2012 node
= m_children
.GetLast();
2013 if (node
&& node
->GetData()->IsShown())
2015 wxRichTextObject
* child
= node
->GetData();
2016 maxHeight
= child
->GetPosition().y
- (GetPosition().y
+ topMargin
) + child
->GetCachedSize().y
;
2019 maxHeight
= 0; // topMargin + bottomMargin;
2021 if (GetAttributes().GetTextBoxAttr().GetSize().GetWidth().IsValid())
2023 wxRect r
= AdjustAvailableSpace(dc
, GetBuffer(), wxRichTextAttr() /* not used */, GetAttributes(), parentRect
, parentRect
);
2024 int w
= r
.GetWidth();
2026 // Convert external to content rect
2027 w
= w
- leftMargin
- rightMargin
;
2028 maxWidth
= wxMax(maxWidth
, w
);
2029 maxMaxWidth
= wxMax(maxMaxWidth
, w
);
2032 // TODO: (also in para layout) should set the
2033 // object's size to an absolute one if specified,
2034 // but if not specified, calculate it from content.
2036 // We need to add back the margins etc.
2038 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2039 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxWidth
, maxHeight
));
2040 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2041 SetCachedSize(marginRect
.GetSize());
2044 // The maximum size is the greatest of all maximum widths for all paragraphs.
2046 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2047 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxMaxWidth
, maxHeight
)); // Actually max height is a lie, we can't know it
2048 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2049 SetMaxSize(marginRect
.GetSize());
2052 // The minimum size is the greatest of all minimum widths for all paragraphs.
2054 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2055 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxMinWidth
, maxHeight
)); // Actually max height is a lie, we can't know it
2056 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2057 SetMinSize(marginRect
.GetSize());
2060 if (GetAttributes().GetTextBoxAttr().HasVerticalAlignment() &&
2061 (GetAttributes().GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP
))
2064 int leftOverSpace
= availableSpace
.height
- topMargin
- bottomMargin
- maxHeight
;
2065 if (leftOverSpace
> 0)
2067 if (GetAttributes().GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_CENTRE
)
2069 yOffset
= (leftOverSpace
/2);
2071 else if (GetAttributes().GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_BOTTOM
)
2073 yOffset
= leftOverSpace
;
2077 // Move all the children to vertically align the content
2078 // This doesn't take into account floating objects, unfortunately.
2081 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2084 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2086 child
->Move(wxPoint(child
->GetPosition().x
, child
->GetPosition().y
+ yOffset
));
2088 node
= node
->GetNext();
2093 m_invalidRange
= wxRICHTEXT_NONE
;
2098 /// Get/set the size for the given range.
2099 bool wxRichTextParagraphLayoutBox::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, int flags
, wxPoint position
, wxArrayInt
* WXUNUSED(partialExtents
)) const
2103 wxRichTextObjectList::compatibility_iterator startPara
= wxRichTextObjectList::compatibility_iterator();
2104 wxRichTextObjectList::compatibility_iterator endPara
= wxRichTextObjectList::compatibility_iterator();
2106 // First find the first paragraph whose starting position is within the range.
2107 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2110 // child is a paragraph
2111 wxRichTextObject
* child
= node
->GetData();
2112 const wxRichTextRange
& r
= child
->GetRange();
2114 if (r
.GetStart() <= range
.GetStart() && r
.GetEnd() >= range
.GetStart())
2120 node
= node
->GetNext();
2123 // Next find the last paragraph containing part of the range
2124 node
= m_children
.GetFirst();
2127 // child is a paragraph
2128 wxRichTextObject
* child
= node
->GetData();
2129 const wxRichTextRange
& r
= child
->GetRange();
2131 if (r
.GetStart() <= range
.GetEnd() && r
.GetEnd() >= range
.GetEnd())
2137 node
= node
->GetNext();
2140 if (!startPara
|| !endPara
)
2143 // Now we can add up the sizes
2144 for (node
= startPara
; node
; node
= node
->GetNext())
2146 // child is a paragraph
2147 wxRichTextObject
* child
= node
->GetData();
2148 const wxRichTextRange
& childRange
= child
->GetRange();
2149 wxRichTextRange rangeToFind
= range
;
2150 rangeToFind
.LimitTo(childRange
);
2152 if (child
->IsTopLevel())
2153 rangeToFind
= child
->GetOwnRange();
2157 int childDescent
= 0;
2158 child
->GetRangeSize(rangeToFind
, childSize
, childDescent
, dc
, flags
, position
);
2160 descent
= wxMax(childDescent
, descent
);
2162 sz
.x
= wxMax(sz
.x
, childSize
.x
);
2163 sz
.y
+= childSize
.y
;
2165 if (node
== endPara
)
2174 /// Get the paragraph at the given position
2175 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphAtPosition(long pos
, bool caretPosition
) const
2180 // First find the first paragraph whose starting position is within the range.
2181 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2184 // child is a paragraph
2185 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2186 // wxASSERT (child != NULL);
2190 // Return first child in buffer if position is -1
2194 if (child
->GetRange().Contains(pos
))
2198 node
= node
->GetNext();
2203 /// Get the line at the given position
2204 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineAtPosition(long pos
, bool caretPosition
) const
2209 // First find the first paragraph whose starting position is within the range.
2210 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2213 wxRichTextObject
* obj
= (wxRichTextObject
*) node
->GetData();
2214 if (obj
->GetRange().Contains(pos
))
2216 // child is a paragraph
2217 wxRichTextParagraph
* child
= wxDynamicCast(obj
, wxRichTextParagraph
);
2218 // wxASSERT (child != NULL);
2222 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2225 wxRichTextLine
* line
= node2
->GetData();
2227 wxRichTextRange range
= line
->GetAbsoluteRange();
2229 if (range
.Contains(pos
) ||
2231 // If the position is end-of-paragraph, then return the last line of
2232 // of the paragraph.
2233 ((range
.GetEnd() == child
->GetRange().GetEnd()-1) && (pos
== child
->GetRange().GetEnd())))
2236 node2
= node2
->GetNext();
2241 node
= node
->GetNext();
2244 int lineCount
= GetLineCount();
2246 return GetLineForVisibleLineNumber(lineCount
-1);
2251 /// Get the line at the given y pixel position, or the last line.
2252 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineAtYPosition(int y
) const
2254 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2257 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2258 // wxASSERT (child != NULL);
2262 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2265 wxRichTextLine
* line
= node2
->GetData();
2267 wxRect
rect(line
->GetRect());
2269 if (y
<= rect
.GetBottom())
2272 node2
= node2
->GetNext();
2276 node
= node
->GetNext();
2280 int lineCount
= GetLineCount();
2282 return GetLineForVisibleLineNumber(lineCount
-1);
2287 /// Get the number of visible lines
2288 int wxRichTextParagraphLayoutBox::GetLineCount() const
2292 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2295 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2296 // wxASSERT (child != NULL);
2299 count
+= child
->GetLines().GetCount();
2301 node
= node
->GetNext();
2307 /// Get the paragraph for a given line
2308 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphForLine(wxRichTextLine
* line
) const
2310 return GetParagraphAtPosition(line
->GetAbsoluteRange().GetStart());
2313 /// Get the line size at the given position
2314 wxSize
wxRichTextParagraphLayoutBox::GetLineSizeAtPosition(long pos
, bool caretPosition
) const
2316 wxRichTextLine
* line
= GetLineAtPosition(pos
, caretPosition
);
2319 return line
->GetSize();
2322 return wxSize(0, 0);
2326 /// Convenience function to add a paragraph of text
2327 wxRichTextRange
wxRichTextParagraphLayoutBox::AddParagraph(const wxString
& text
, wxRichTextAttr
* paraStyle
)
2329 // Don't use the base style, just the default style, and the base style will
2330 // be combined at display time.
2331 // Divide into paragraph and character styles.
2333 wxRichTextAttr defaultCharStyle
;
2334 wxRichTextAttr defaultParaStyle
;
2336 // If the default style is a named paragraph style, don't apply any character formatting
2337 // to the initial text string.
2338 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2340 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2342 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2345 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2347 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2348 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2350 wxRichTextParagraph
* para
= new wxRichTextParagraph(text
, this, pStyle
, cStyle
);
2356 return para
->GetRange();
2359 /// Adds multiple paragraphs, based on newlines.
2360 wxRichTextRange
wxRichTextParagraphLayoutBox::AddParagraphs(const wxString
& text
, wxRichTextAttr
* paraStyle
)
2362 // Don't use the base style, just the default style, and the base style will
2363 // be combined at display time.
2364 // Divide into paragraph and character styles.
2366 wxRichTextAttr defaultCharStyle
;
2367 wxRichTextAttr defaultParaStyle
;
2369 // If the default style is a named paragraph style, don't apply any character formatting
2370 // to the initial text string.
2371 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2373 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2375 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2378 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2380 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2381 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2383 wxRichTextParagraph
* firstPara
= NULL
;
2384 wxRichTextParagraph
* lastPara
= NULL
;
2386 wxRichTextRange
range(-1, -1);
2389 size_t len
= text
.length();
2391 wxRichTextParagraph
* para
= new wxRichTextParagraph(wxEmptyString
, this, pStyle
, cStyle
);
2400 wxChar ch
= text
[i
];
2401 if (ch
== wxT('\n') || ch
== wxT('\r'))
2405 wxRichTextPlainText
* plainText
= (wxRichTextPlainText
*) para
->GetChildren().GetFirst()->GetData();
2406 plainText
->SetText(line
);
2408 para
= new wxRichTextParagraph(wxEmptyString
, this, pStyle
, cStyle
);
2413 line
= wxEmptyString
;
2424 wxRichTextPlainText
* plainText
= (wxRichTextPlainText
*) para
->GetChildren().GetFirst()->GetData();
2425 plainText
->SetText(line
);
2430 return wxRichTextRange(firstPara
->GetRange().GetStart(), lastPara
->GetRange().GetEnd());
2433 /// Convenience function to add an image
2434 wxRichTextRange
wxRichTextParagraphLayoutBox::AddImage(const wxImage
& image
, wxRichTextAttr
* paraStyle
)
2436 // Don't use the base style, just the default style, and the base style will
2437 // be combined at display time.
2438 // Divide into paragraph and character styles.
2440 wxRichTextAttr defaultCharStyle
;
2441 wxRichTextAttr defaultParaStyle
;
2443 // If the default style is a named paragraph style, don't apply any character formatting
2444 // to the initial text string.
2445 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2447 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2449 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2452 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2454 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2455 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2457 wxRichTextParagraph
* para
= new wxRichTextParagraph(this, pStyle
);
2459 para
->AppendChild(new wxRichTextImage(image
, this, cStyle
));
2463 return para
->GetRange();
2467 /// Insert fragment into this box at the given position. If partialParagraph is true,
2468 /// it is assumed that the last (or only) paragraph is just a piece of data with no paragraph
2471 bool wxRichTextParagraphLayoutBox::InsertFragment(long position
, wxRichTextParagraphLayoutBox
& fragment
)
2473 // First, find the first paragraph whose starting position is within the range.
2474 wxRichTextParagraph
* para
= GetParagraphAtPosition(position
);
2477 wxRichTextAttr originalAttr
= para
->GetAttributes();
2479 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(para
);
2481 // Now split at this position, returning the object to insert the new
2482 // ones in front of.
2483 wxRichTextObject
* nextObject
= para
->SplitAt(position
);
2485 // Special case: partial paragraph, just one paragraph. Might be a small amount of
2486 // text, for example, so let's optimize.
2488 if (fragment
.GetPartialParagraph() && fragment
.GetChildren().GetCount() == 1)
2490 // Add the first para to this para...
2491 wxRichTextObjectList::compatibility_iterator firstParaNode
= fragment
.GetChildren().GetFirst();
2495 // Iterate through the fragment paragraph inserting the content into this paragraph.
2496 wxRichTextParagraph
* firstPara
= wxDynamicCast(firstParaNode
->GetData(), wxRichTextParagraph
);
2497 wxASSERT (firstPara
!= NULL
);
2499 wxRichTextObjectList::compatibility_iterator objectNode
= firstPara
->GetChildren().GetFirst();
2502 wxRichTextObject
* newObj
= objectNode
->GetData()->Clone();
2507 para
->AppendChild(newObj
);
2511 // Insert before nextObject
2512 para
->InsertChild(newObj
, nextObject
);
2515 objectNode
= objectNode
->GetNext();
2522 // Procedure for inserting a fragment consisting of a number of
2525 // 1. Remove and save the content that's after the insertion point, for adding
2526 // back once we've added the fragment.
2527 // 2. Add the content from the first fragment paragraph to the current
2529 // 3. Add remaining fragment paragraphs after the current paragraph.
2530 // 4. Add back the saved content from the first paragraph. If partialParagraph
2531 // is true, add it to the last paragraph added and not a new one.
2533 // 1. Remove and save objects after split point.
2534 wxList savedObjects
;
2536 para
->MoveToList(nextObject
, savedObjects
);
2538 // 2. Add the content from the 1st fragment paragraph.
2539 wxRichTextObjectList::compatibility_iterator firstParaNode
= fragment
.GetChildren().GetFirst();
2543 wxRichTextParagraph
* firstPara
= wxDynamicCast(firstParaNode
->GetData(), wxRichTextParagraph
);
2544 wxASSERT(firstPara
!= NULL
);
2546 if (!(fragment
.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
))
2547 para
->SetAttributes(firstPara
->GetAttributes());
2549 // Save empty paragraph attributes for appending later
2550 // These are character attributes deliberately set for a new paragraph. Without this,
2551 // we couldn't pass default attributes when appending a new paragraph.
2552 wxRichTextAttr emptyParagraphAttributes
;
2554 wxRichTextObjectList::compatibility_iterator objectNode
= firstPara
->GetChildren().GetFirst();
2556 if (objectNode
&& firstPara
->GetChildren().GetCount() == 1 && objectNode
->GetData()->IsEmpty())
2557 emptyParagraphAttributes
= objectNode
->GetData()->GetAttributes();
2561 wxRichTextObject
* newObj
= objectNode
->GetData()->Clone();
2564 para
->AppendChild(newObj
);
2566 objectNode
= objectNode
->GetNext();
2569 // 3. Add remaining fragment paragraphs after the current paragraph.
2570 wxRichTextObjectList::compatibility_iterator nextParagraphNode
= node
->GetNext();
2571 wxRichTextObject
* nextParagraph
= NULL
;
2572 if (nextParagraphNode
)
2573 nextParagraph
= nextParagraphNode
->GetData();
2575 wxRichTextObjectList::compatibility_iterator i
= fragment
.GetChildren().GetFirst()->GetNext();
2576 wxRichTextParagraph
* finalPara
= para
;
2578 bool needExtraPara
= (!i
|| !fragment
.GetPartialParagraph());
2580 // If there was only one paragraph, we need to insert a new one.
2583 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2584 wxASSERT( para
!= NULL
);
2586 finalPara
= (wxRichTextParagraph
*) para
->Clone();
2589 InsertChild(finalPara
, nextParagraph
);
2591 AppendChild(finalPara
);
2596 // If there was only one paragraph, or we have full paragraphs in our fragment,
2597 // we need to insert a new one.
2600 finalPara
= new wxRichTextParagraph
;
2603 InsertChild(finalPara
, nextParagraph
);
2605 AppendChild(finalPara
);
2608 // 4. Add back the remaining content.
2612 finalPara
->MoveFromList(savedObjects
);
2614 // Ensure there's at least one object
2615 if (finalPara
->GetChildCount() == 0)
2617 wxRichTextPlainText
* text
= new wxRichTextPlainText(wxEmptyString
);
2618 text
->SetAttributes(emptyParagraphAttributes
);
2620 finalPara
->AppendChild(text
);
2624 if ((fragment
.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
) && firstPara
)
2625 finalPara
->SetAttributes(firstPara
->GetAttributes());
2626 else if (finalPara
&& finalPara
!= para
)
2627 finalPara
->SetAttributes(originalAttr
);
2635 wxRichTextObjectList::compatibility_iterator i
= fragment
.GetChildren().GetFirst();
2638 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2639 wxASSERT( para
!= NULL
);
2641 AppendChild(para
->Clone());
2650 /// Make a copy of the fragment corresponding to the given range, putting it in 'fragment'.
2651 /// If there was an incomplete paragraph at the end, partialParagraph is set to true.
2652 bool wxRichTextParagraphLayoutBox::CopyFragment(const wxRichTextRange
& range
, wxRichTextParagraphLayoutBox
& fragment
)
2654 wxRichTextObjectList::compatibility_iterator i
= GetChildren().GetFirst();
2657 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2658 wxASSERT( para
!= NULL
);
2660 if (!para
->GetRange().IsOutside(range
))
2662 fragment
.AppendChild(para
->Clone());
2667 // Now top and tail the first and last paragraphs in our new fragment (which might be the same).
2668 if (!fragment
.IsEmpty())
2670 wxRichTextParagraph
* firstPara
= wxDynamicCast(fragment
.GetChildren().GetFirst()->GetData(), wxRichTextParagraph
);
2671 wxASSERT( firstPara
!= NULL
);
2673 wxRichTextParagraph
* lastPara
= wxDynamicCast(fragment
.GetChildren().GetLast()->GetData(), wxRichTextParagraph
);
2674 wxASSERT( lastPara
!= NULL
);
2676 if (!firstPara
|| !lastPara
)
2679 bool isFragment
= (range
.GetEnd() < lastPara
->GetRange().GetEnd());
2681 long firstPos
= firstPara
->GetRange().GetStart();
2683 // Adjust for renumbering from zero
2684 wxRichTextRange
topTailRange(range
.GetStart() - firstPos
, range
.GetEnd() - firstPos
);
2687 fragment
.CalculateRange(0, end
);
2689 // Chop off the start of the paragraph
2690 if (topTailRange
.GetStart() > 0)
2692 wxRichTextRange
r(0, topTailRange
.GetStart()-1);
2693 firstPara
->DeleteRange(r
);
2695 // Make sure the numbering is correct
2696 fragment
.CalculateRange(0, end
);
2698 // Now, we've deleted some positions, so adjust the range
2700 topTailRange
.SetStart(range
.GetLength());
2701 topTailRange
.SetEnd(fragment
.GetOwnRange().GetEnd());
2705 topTailRange
.SetStart(range
.GetLength());
2706 topTailRange
.SetEnd(fragment
.GetOwnRange().GetEnd());
2709 if (topTailRange
.GetStart() < lastPara
->GetRange().GetEnd())
2711 lastPara
->DeleteRange(topTailRange
);
2713 // Make sure the numbering is correct
2715 fragment
.CalculateRange(0, end
);
2717 // We only have part of a paragraph at the end
2718 fragment
.SetPartialParagraph(true);
2722 // We have a partial paragraph (don't save last new paragraph marker)
2723 // or complete paragraph
2724 fragment
.SetPartialParagraph(isFragment
);
2731 /// Given a position, get the number of the visible line (potentially many to a paragraph),
2732 /// starting from zero at the start of the buffer.
2733 long wxRichTextParagraphLayoutBox::GetVisibleLineNumber(long pos
, bool caretPosition
, bool startOfLine
) const
2740 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2743 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2744 // wxASSERT( child != NULL );
2748 if (child
->GetRange().Contains(pos
))
2750 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2753 wxRichTextLine
* line
= node2
->GetData();
2754 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
2756 if (lineRange
.Contains(pos
) || pos
== lineRange
.GetStart())
2758 // If the caret is displayed at the end of the previous wrapped line,
2759 // we want to return the line it's _displayed_ at (not the actual line
2760 // containing the position).
2761 if (lineRange
.GetStart() == pos
&& !startOfLine
&& child
->GetRange().GetStart() != pos
)
2762 return lineCount
- 1;
2769 node2
= node2
->GetNext();
2771 // If we didn't find it in the lines, it must be
2772 // the last position of the paragraph. So return the last line.
2776 lineCount
+= child
->GetLines().GetCount();
2779 node
= node
->GetNext();
2786 /// Given a line number, get the corresponding wxRichTextLine object.
2787 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineForVisibleLineNumber(long lineNumber
) const
2791 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2794 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2795 // wxASSERT(child != NULL);
2799 if (lineNumber
< (int) (child
->GetLines().GetCount() + lineCount
))
2801 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2804 wxRichTextLine
* line
= node2
->GetData();
2806 if (lineCount
== lineNumber
)
2811 node2
= node2
->GetNext();
2815 lineCount
+= child
->GetLines().GetCount();
2818 node
= node
->GetNext();
2825 /// Delete range from layout.
2826 bool wxRichTextParagraphLayoutBox::DeleteRange(const wxRichTextRange
& range
)
2828 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2830 wxRichTextParagraph
* firstPara
= NULL
;
2833 wxRichTextParagraph
* obj
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2834 // wxASSERT (obj != NULL);
2836 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
2840 // Delete the range in each paragraph
2842 if (!obj
->GetRange().IsOutside(range
))
2844 // Deletes the content of this object within the given range
2845 obj
->DeleteRange(range
);
2847 wxRichTextRange thisRange
= obj
->GetRange();
2848 wxRichTextAttr thisAttr
= obj
->GetAttributes();
2850 // If the whole paragraph is within the range to delete,
2851 // delete the whole thing.
2852 if (range
.GetStart() <= thisRange
.GetStart() && range
.GetEnd() >= thisRange
.GetEnd())
2854 // Delete the whole object
2855 RemoveChild(obj
, true);
2858 else if (!firstPara
)
2861 // If the range includes the paragraph end, we need to join this
2862 // and the next paragraph.
2863 if (range
.GetEnd() <= thisRange
.GetEnd())
2865 // We need to move the objects from the next paragraph
2866 // to this paragraph
2868 wxRichTextParagraph
* nextParagraph
= NULL
;
2869 if ((range
.GetEnd() < thisRange
.GetEnd()) && obj
)
2870 nextParagraph
= obj
;
2873 // We're ending at the end of the paragraph, so merge the _next_ paragraph.
2875 nextParagraph
= wxDynamicCast(next
->GetData(), wxRichTextParagraph
);
2878 bool applyFinalParagraphStyle
= firstPara
&& nextParagraph
&& nextParagraph
!= firstPara
;
2880 wxRichTextAttr nextParaAttr
;
2881 if (applyFinalParagraphStyle
)
2883 // Special case when deleting the end of a paragraph - use _this_ paragraph's style,
2884 // not the next one.
2885 if (range
.GetStart() == range
.GetEnd() && range
.GetStart() == thisRange
.GetEnd())
2886 nextParaAttr
= thisAttr
;
2888 nextParaAttr
= nextParagraph
->GetAttributes();
2891 if (firstPara
&& nextParagraph
&& firstPara
!= nextParagraph
)
2893 // Move the objects to the previous para
2894 wxRichTextObjectList::compatibility_iterator node1
= nextParagraph
->GetChildren().GetFirst();
2898 wxRichTextObject
* obj1
= node1
->GetData();
2900 firstPara
->AppendChild(obj1
);
2902 wxRichTextObjectList::compatibility_iterator next1
= node1
->GetNext();
2903 nextParagraph
->GetChildren().Erase(node1
);
2908 // Delete the paragraph
2909 RemoveChild(nextParagraph
, true);
2912 // Avoid empty paragraphs
2913 if (firstPara
&& firstPara
->GetChildren().GetCount() == 0)
2915 wxRichTextPlainText
* text
= new wxRichTextPlainText(wxEmptyString
);
2916 firstPara
->AppendChild(text
);
2919 if (applyFinalParagraphStyle
)
2920 firstPara
->SetAttributes(nextParaAttr
);
2933 /// Get any text in this object for the given range
2934 wxString
wxRichTextParagraphLayoutBox::GetTextForRange(const wxRichTextRange
& range
) const
2938 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2941 wxRichTextObject
* child
= node
->GetData();
2942 if (!child
->GetRange().IsOutside(range
))
2944 wxRichTextRange childRange
= range
;
2945 childRange
.LimitTo(child
->GetRange());
2947 wxString childText
= child
->GetTextForRange(childRange
);
2951 if ((childRange
.GetEnd() == child
->GetRange().GetEnd()) && node
->GetNext())
2956 node
= node
->GetNext();
2962 /// Get all the text
2963 wxString
wxRichTextParagraphLayoutBox::GetText() const
2965 return GetTextForRange(GetOwnRange());
2968 /// Get the paragraph by number
2969 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphAtLine(long paragraphNumber
) const
2971 if ((size_t) paragraphNumber
>= GetChildCount())
2974 return (wxRichTextParagraph
*) GetChild((size_t) paragraphNumber
);
2977 /// Get the length of the paragraph
2978 int wxRichTextParagraphLayoutBox::GetParagraphLength(long paragraphNumber
) const
2980 wxRichTextParagraph
* para
= GetParagraphAtLine(paragraphNumber
);
2982 return para
->GetRange().GetLength() - 1; // don't include newline
2987 /// Get the text of the paragraph
2988 wxString
wxRichTextParagraphLayoutBox::GetParagraphText(long paragraphNumber
) const
2990 wxRichTextParagraph
* para
= GetParagraphAtLine(paragraphNumber
);
2992 return para
->GetTextForRange(para
->GetRange());
2994 return wxEmptyString
;
2997 /// Convert zero-based line column and paragraph number to a position.
2998 long wxRichTextParagraphLayoutBox::XYToPosition(long x
, long y
) const
3000 wxRichTextParagraph
* para
= GetParagraphAtLine(y
);
3003 return para
->GetRange().GetStart() + x
;
3009 /// Convert zero-based position to line column and paragraph number
3010 bool wxRichTextParagraphLayoutBox::PositionToXY(long pos
, long* x
, long* y
) const
3012 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
);
3016 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3019 wxRichTextObject
* child
= node
->GetData();
3023 node
= node
->GetNext();
3027 *x
= pos
- para
->GetRange().GetStart();
3035 /// Get the leaf object in a paragraph at this position.
3036 /// Given a line number, get the corresponding wxRichTextLine object.
3037 wxRichTextObject
* wxRichTextParagraphLayoutBox::GetLeafObjectAtPosition(long position
) const
3039 wxRichTextParagraph
* para
= GetParagraphAtPosition(position
);
3042 wxRichTextObjectList::compatibility_iterator node
= para
->GetChildren().GetFirst();
3046 wxRichTextObject
* child
= node
->GetData();
3047 if (child
->GetRange().Contains(position
))
3050 node
= node
->GetNext();
3052 if (position
== para
->GetRange().GetEnd() && para
->GetChildCount() > 0)
3053 return para
->GetChildren().GetLast()->GetData();
3058 /// Set character or paragraph text attributes: apply character styles only to immediate text nodes
3059 bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange
& range
, const wxRichTextAttr
& style
, int flags
)
3061 bool characterStyle
= false;
3062 bool paragraphStyle
= false;
3064 if (style
.IsCharacterStyle())
3065 characterStyle
= true;
3066 if (style
.IsParagraphStyle())
3067 paragraphStyle
= true;
3069 wxRichTextBuffer
* buffer
= GetBuffer();
3071 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
3072 bool applyMinimal
= ((flags
& wxRICHTEXT_SETSTYLE_OPTIMIZE
) != 0);
3073 bool parasOnly
= ((flags
& wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY
) != 0);
3074 bool charactersOnly
= ((flags
& wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY
) != 0);
3075 bool resetExistingStyle
= ((flags
& wxRICHTEXT_SETSTYLE_RESET
) != 0);
3076 bool removeStyle
= ((flags
& wxRICHTEXT_SETSTYLE_REMOVE
) != 0);
3078 // Apply paragraph style first, if any
3079 wxRichTextAttr
wholeStyle(style
);
3081 if (!removeStyle
&& wholeStyle
.HasParagraphStyleName() && buffer
->GetStyleSheet())
3083 wxRichTextParagraphStyleDefinition
* def
= buffer
->GetStyleSheet()->FindParagraphStyle(wholeStyle
.GetParagraphStyleName());
3085 wxRichTextApplyStyle(wholeStyle
, def
->GetStyleMergedWithBase(buffer
->GetStyleSheet()));
3088 // Limit the attributes to be set to the content to only character attributes.
3089 wxRichTextAttr
characterAttributes(wholeStyle
);
3090 characterAttributes
.SetFlags(characterAttributes
.GetFlags() & (wxTEXT_ATTR_CHARACTER
));
3092 if (!removeStyle
&& characterAttributes
.HasCharacterStyleName() && buffer
->GetStyleSheet())
3094 wxRichTextCharacterStyleDefinition
* def
= buffer
->GetStyleSheet()->FindCharacterStyle(characterAttributes
.GetCharacterStyleName());
3096 wxRichTextApplyStyle(characterAttributes
, def
->GetStyleMergedWithBase(buffer
->GetStyleSheet()));
3099 // If we are associated with a control, make undoable; otherwise, apply immediately
3102 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3104 wxRichTextAction
* action
= NULL
;
3106 if (haveControl
&& withUndo
)
3108 action
= new wxRichTextAction(NULL
, _("Change Style"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
3109 action
->SetRange(range
);
3110 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3113 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3116 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3117 // wxASSERT (para != NULL);
3119 if (para
&& para
->GetChildCount() > 0)
3121 // Stop searching if we're beyond the range of interest
3122 if (para
->GetRange().GetStart() > range
.GetEnd())
3125 if (!para
->GetRange().IsOutside(range
))
3127 // We'll be using a copy of the paragraph to make style changes,
3128 // not updating the buffer directly.
3129 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3131 if (haveControl
&& withUndo
)
3133 newPara
= new wxRichTextParagraph(*para
);
3134 action
->GetNewParagraphs().AppendChild(newPara
);
3136 // Also store the old ones for Undo
3137 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3142 // If we're specifying paragraphs only, then we really mean character formatting
3143 // to be included in the paragraph style
3144 if ((paragraphStyle
|| parasOnly
) && !charactersOnly
)
3148 // Removes the given style from the paragraph
3149 wxRichTextRemoveStyle(newPara
->GetAttributes(), style
);
3151 else if (resetExistingStyle
)
3152 newPara
->GetAttributes() = wholeStyle
;
3157 // Only apply attributes that will make a difference to the combined
3158 // style as seen on the display
3159 wxRichTextAttr
combinedAttr(para
->GetCombinedAttributes(true));
3160 wxRichTextApplyStyle(newPara
->GetAttributes(), wholeStyle
, & combinedAttr
);
3163 wxRichTextApplyStyle(newPara
->GetAttributes(), wholeStyle
);
3167 // When applying paragraph styles dynamically, don't change the text objects' attributes
3168 // since they will computed as needed. Only apply the character styling if it's _only_
3169 // character styling. This policy is subject to change and might be put under user control.
3171 // Hm. we might well be applying a mix of paragraph and character styles, in which
3172 // case we _do_ want to apply character styles regardless of what para styles are set.
3173 // But if we're applying a paragraph style, which has some character attributes, but
3174 // we only want the paragraphs to hold this character style, then we _don't_ want to
3175 // apply the character style. So we need to be able to choose.
3177 if (!parasOnly
&& (characterStyle
|charactersOnly
) && range
.GetStart() != newPara
->GetRange().GetEnd())
3179 wxRichTextRange
childRange(range
);
3180 childRange
.LimitTo(newPara
->GetRange());
3182 // Find the starting position and if necessary split it so
3183 // we can start applying a different style.
3184 // TODO: check that the style actually changes or is different
3185 // from style outside of range
3186 wxRichTextObject
* firstObject
wxDUMMY_INITIALIZE(NULL
);
3187 wxRichTextObject
* lastObject
wxDUMMY_INITIALIZE(NULL
);
3189 if (childRange
.GetStart() == newPara
->GetRange().GetStart())
3190 firstObject
= newPara
->GetChildren().GetFirst()->GetData();
3192 firstObject
= newPara
->SplitAt(range
.GetStart());
3194 // Increment by 1 because we're apply the style one _after_ the split point
3195 long splitPoint
= childRange
.GetEnd();
3196 if (splitPoint
!= newPara
->GetRange().GetEnd())
3200 if (splitPoint
== newPara
->GetRange().GetEnd())
3201 lastObject
= newPara
->GetChildren().GetLast()->GetData();
3203 // lastObject is set as a side-effect of splitting. It's
3204 // returned as the object before the new object.
3205 (void) newPara
->SplitAt(splitPoint
, & lastObject
);
3207 wxASSERT(firstObject
!= NULL
);
3208 wxASSERT(lastObject
!= NULL
);
3210 if (!firstObject
|| !lastObject
)
3213 wxRichTextObjectList::compatibility_iterator firstNode
= newPara
->GetChildren().Find(firstObject
);
3214 wxRichTextObjectList::compatibility_iterator lastNode
= newPara
->GetChildren().Find(lastObject
);
3216 wxASSERT(firstNode
);
3219 wxRichTextObjectList::compatibility_iterator node2
= firstNode
;
3223 wxRichTextObject
* child
= node2
->GetData();
3227 // Removes the given style from the paragraph
3228 wxRichTextRemoveStyle(child
->GetAttributes(), style
);
3230 else if (resetExistingStyle
)
3231 child
->GetAttributes() = characterAttributes
;
3236 // Only apply attributes that will make a difference to the combined
3237 // style as seen on the display
3238 wxRichTextAttr
combinedAttr(newPara
->GetCombinedAttributes(child
->GetAttributes(), true));
3239 wxRichTextApplyStyle(child
->GetAttributes(), characterAttributes
, & combinedAttr
);
3242 wxRichTextApplyStyle(child
->GetAttributes(), characterAttributes
);
3245 if (node2
== lastNode
)
3248 node2
= node2
->GetNext();
3254 node
= node
->GetNext();
3257 // Do action, or delay it until end of batch.
3258 if (haveControl
&& withUndo
)
3259 buffer
->SubmitAction(action
);
3264 // Just change the attributes for this single object.
3265 void wxRichTextParagraphLayoutBox::SetStyle(wxRichTextObject
* obj
, const wxRichTextAttr
& textAttr
, int flags
)
3267 wxRichTextBuffer
* buffer
= GetBuffer();
3268 bool withUndo
= flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
;
3269 bool resetExistingStyle
= ((flags
& wxRICHTEXT_SETSTYLE_RESET
) != 0);
3270 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3272 wxRichTextAction
*action
= NULL
;
3273 wxRichTextAttr newAttr
= obj
->GetAttributes();
3274 if (resetExistingStyle
)
3277 newAttr
.Apply(textAttr
);
3279 if (haveControl
&& withUndo
)
3281 action
= new wxRichTextAction(NULL
, _("Change Object Style"), wxRICHTEXT_CHANGE_ATTRIBUTES
, buffer
, obj
->GetContainer(), buffer
->GetRichTextCtrl());
3282 action
->SetRange(obj
->GetRange().FromInternal());
3283 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3284 action
->MakeObject(obj
);
3286 action
->GetAttributes() = newAttr
;
3289 obj
->GetAttributes() = newAttr
;
3291 if (haveControl
&& withUndo
)
3292 buffer
->SubmitAction(action
);
3295 /// Get the text attributes for this position.
3296 bool wxRichTextParagraphLayoutBox::GetStyle(long position
, wxRichTextAttr
& style
)
3298 return DoGetStyle(position
, style
, true);
3301 bool wxRichTextParagraphLayoutBox::GetUncombinedStyle(long position
, wxRichTextAttr
& style
)
3303 return DoGetStyle(position
, style
, false);
3306 /// Implementation helper for GetStyle. If combineStyles is true, combine base, paragraph and
3307 /// context attributes.
3308 bool wxRichTextParagraphLayoutBox::DoGetStyle(long position
, wxRichTextAttr
& style
, bool combineStyles
)
3310 wxRichTextObject
* obj
wxDUMMY_INITIALIZE(NULL
);
3312 if (style
.IsParagraphStyle())
3314 obj
= GetParagraphAtPosition(position
);
3319 // Start with the base style
3320 style
= GetAttributes();
3322 // Apply the paragraph style
3323 wxRichTextApplyStyle(style
, obj
->GetAttributes());
3326 style
= obj
->GetAttributes();
3333 obj
= GetLeafObjectAtPosition(position
);
3338 wxRichTextParagraph
* para
= wxDynamicCast(obj
->GetParent(), wxRichTextParagraph
);
3339 style
= para
? para
->GetCombinedAttributes(obj
->GetAttributes()) : obj
->GetAttributes();
3342 style
= obj
->GetAttributes();
3350 static bool wxHasStyle(long flags
, long style
)
3352 return (flags
& style
) != 0;
3355 /// Combines 'style' with 'currentStyle' for the purpose of summarising the attributes of a range of
3357 bool wxRichTextParagraphLayoutBox::CollectStyle(wxRichTextAttr
& currentStyle
, const wxRichTextAttr
& style
, wxRichTextAttr
& clashingAttr
, wxRichTextAttr
& absentAttr
)
3359 currentStyle
.CollectCommonAttributes(style
, clashingAttr
, absentAttr
);
3364 /// Get the combined style for a range - if any attribute is different within the range,
3365 /// that attribute is not present within the flags.
3366 /// *** Note that this is not recursive, and so assumes that content inside a paragraph is not itself
3368 bool wxRichTextParagraphLayoutBox::GetStyleForRange(const wxRichTextRange
& range
, wxRichTextAttr
& style
)
3370 style
= wxRichTextAttr();
3372 wxRichTextAttr clashingAttr
;
3373 wxRichTextAttr absentAttrPara
, absentAttrChar
;
3375 wxRichTextObjectList::compatibility_iterator node
= GetChildren().GetFirst();
3378 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3379 if (para
&& !(para
->GetRange().GetStart() > range
.GetEnd() || para
->GetRange().GetEnd() < range
.GetStart()))
3381 if (para
->GetChildren().GetCount() == 0)
3383 wxRichTextAttr paraStyle
= para
->GetCombinedAttributes(true /* use box attributes */);
3385 CollectStyle(style
, paraStyle
, clashingAttr
, absentAttrPara
);
3389 wxRichTextRange
paraRange(para
->GetRange());
3390 paraRange
.LimitTo(range
);
3392 // First collect paragraph attributes only
3393 wxRichTextAttr paraStyle
= para
->GetCombinedAttributes();
3394 paraStyle
.SetFlags(paraStyle
.GetFlags() & wxTEXT_ATTR_PARAGRAPH
);
3395 CollectStyle(style
, paraStyle
, clashingAttr
, absentAttrPara
);
3397 wxRichTextObjectList::compatibility_iterator childNode
= para
->GetChildren().GetFirst();
3401 wxRichTextObject
* child
= childNode
->GetData();
3402 if (!(child
->GetRange().GetStart() > range
.GetEnd() || child
->GetRange().GetEnd() < range
.GetStart()))
3404 wxRichTextAttr childStyle
= para
->GetCombinedAttributes(child
->GetAttributes(), true /* include box attributes */);
3406 // Now collect character attributes only
3407 childStyle
.SetFlags(childStyle
.GetFlags() & wxTEXT_ATTR_CHARACTER
);
3409 CollectStyle(style
, childStyle
, clashingAttr
, absentAttrChar
);
3412 childNode
= childNode
->GetNext();
3416 node
= node
->GetNext();
3421 /// Set default style
3422 bool wxRichTextParagraphLayoutBox::SetDefaultStyle(const wxRichTextAttr
& style
)
3424 m_defaultAttributes
= style
;
3428 /// Test if this whole range has character attributes of the specified kind. If any
3429 /// of the attributes are different within the range, the test fails. You
3430 /// can use this to implement, for example, bold button updating. style must have
3431 /// flags indicating which attributes are of interest.
3432 bool wxRichTextParagraphLayoutBox::HasCharacterAttributes(const wxRichTextRange
& range
, const wxRichTextAttr
& style
) const
3435 int matchingCount
= 0;
3437 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3440 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3441 // wxASSERT (para != NULL);
3445 // Stop searching if we're beyond the range of interest
3446 if (para
->GetRange().GetStart() > range
.GetEnd())
3447 return foundCount
== matchingCount
&& foundCount
!= 0;
3449 if (!para
->GetRange().IsOutside(range
))
3451 wxRichTextObjectList::compatibility_iterator node2
= para
->GetChildren().GetFirst();
3455 wxRichTextObject
* child
= node2
->GetData();
3456 // Allow for empty string if no buffer
3457 wxRichTextRange childRange
= child
->GetRange();
3458 if (childRange
.GetLength() == 0 && GetRange().GetLength() == 1)
3459 childRange
.SetEnd(childRange
.GetEnd()+1);
3461 if (!childRange
.IsOutside(range
) && child
->IsKindOf(CLASSINFO(wxRichTextPlainText
)))
3464 wxRichTextAttr textAttr
= para
->GetCombinedAttributes(child
->GetAttributes());
3466 if (wxTextAttrEqPartial(textAttr
, style
))
3470 node2
= node2
->GetNext();
3475 node
= node
->GetNext();
3478 return foundCount
== matchingCount
&& foundCount
!= 0;
3481 /// Test if this whole range has paragraph attributes of the specified kind. If any
3482 /// of the attributes are different within the range, the test fails. You
3483 /// can use this to implement, for example, centering button updating. style must have
3484 /// flags indicating which attributes are of interest.
3485 bool wxRichTextParagraphLayoutBox::HasParagraphAttributes(const wxRichTextRange
& range
, const wxRichTextAttr
& style
) const
3488 int matchingCount
= 0;
3490 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3493 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3494 // wxASSERT (para != NULL);
3498 // Stop searching if we're beyond the range of interest
3499 if (para
->GetRange().GetStart() > range
.GetEnd())
3500 return foundCount
== matchingCount
&& foundCount
!= 0;
3502 if (!para
->GetRange().IsOutside(range
))
3504 wxRichTextAttr textAttr
= GetAttributes();
3505 // Apply the paragraph style
3506 wxRichTextApplyStyle(textAttr
, para
->GetAttributes());
3509 if (wxTextAttrEqPartial(textAttr
, style
))
3514 node
= node
->GetNext();
3516 return foundCount
== matchingCount
&& foundCount
!= 0;
3519 /// Set character or paragraph properties
3520 bool wxRichTextParagraphLayoutBox::SetProperties(const wxRichTextRange
& range
, const wxRichTextProperties
& properties
, int flags
)
3522 wxRichTextBuffer
* buffer
= GetBuffer();
3524 bool withUndo
= ((flags
& wxRICHTEXT_SETPROPERTIES_WITH_UNDO
) != 0);
3525 bool parasOnly
= ((flags
& wxRICHTEXT_SETPROPERTIES_PARAGRAPHS_ONLY
) != 0);
3526 bool charactersOnly
= ((flags
& wxRICHTEXT_SETPROPERTIES_CHARACTERS_ONLY
) != 0);
3527 bool resetExistingProperties
= ((flags
& wxRICHTEXT_SETPROPERTIES_RESET
) != 0);
3528 bool removeProperties
= ((flags
& wxRICHTEXT_SETPROPERTIES_REMOVE
) != 0);
3530 // If we are associated with a control, make undoable; otherwise, apply immediately
3533 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3535 wxRichTextAction
* action
= NULL
;
3537 if (haveControl
&& withUndo
)
3539 action
= new wxRichTextAction(NULL
, _("Change Properties"), wxRICHTEXT_CHANGE_PROPERTIES
, buffer
, this, buffer
->GetRichTextCtrl());
3540 action
->SetRange(range
);
3541 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3544 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3547 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3548 // wxASSERT (para != NULL);
3550 if (para
&& para
->GetChildCount() > 0)
3552 // Stop searching if we're beyond the range of interest
3553 if (para
->GetRange().GetStart() > range
.GetEnd())
3556 if (!para
->GetRange().IsOutside(range
))
3558 // We'll be using a copy of the paragraph to make style changes,
3559 // not updating the buffer directly.
3560 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3562 if (haveControl
&& withUndo
)
3564 newPara
= new wxRichTextParagraph(*para
);
3565 action
->GetNewParagraphs().AppendChild(newPara
);
3567 // Also store the old ones for Undo
3568 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3575 if (removeProperties
)
3577 // Removes the given style from the paragraph
3579 newPara
->GetProperties().RemoveProperties(properties
);
3581 else if (resetExistingProperties
)
3582 newPara
->GetProperties() = properties
;
3584 newPara
->GetProperties().MergeProperties(properties
);
3587 // When applying paragraph styles dynamically, don't change the text objects' attributes
3588 // since they will computed as needed. Only apply the character styling if it's _only_
3589 // character styling. This policy is subject to change and might be put under user control.
3591 // Hm. we might well be applying a mix of paragraph and character styles, in which
3592 // case we _do_ want to apply character styles regardless of what para styles are set.
3593 // But if we're applying a paragraph style, which has some character attributes, but
3594 // we only want the paragraphs to hold this character style, then we _don't_ want to
3595 // apply the character style. So we need to be able to choose.
3597 if (!parasOnly
&& charactersOnly
&& range
.GetStart() != newPara
->GetRange().GetEnd())
3599 wxRichTextRange
childRange(range
);
3600 childRange
.LimitTo(newPara
->GetRange());
3602 // Find the starting position and if necessary split it so
3603 // we can start applying different properties.
3604 // TODO: check that the properties actually change or are different
3605 // from properties outside of range
3606 wxRichTextObject
* firstObject
wxDUMMY_INITIALIZE(NULL
);
3607 wxRichTextObject
* lastObject
wxDUMMY_INITIALIZE(NULL
);
3609 if (childRange
.GetStart() == newPara
->GetRange().GetStart())
3610 firstObject
= newPara
->GetChildren().GetFirst()->GetData();
3612 firstObject
= newPara
->SplitAt(range
.GetStart());
3614 // Increment by 1 because we're apply the style one _after_ the split point
3615 long splitPoint
= childRange
.GetEnd();
3616 if (splitPoint
!= newPara
->GetRange().GetEnd())
3620 if (splitPoint
== newPara
->GetRange().GetEnd())
3621 lastObject
= newPara
->GetChildren().GetLast()->GetData();
3623 // lastObject is set as a side-effect of splitting. It's
3624 // returned as the object before the new object.
3625 (void) newPara
->SplitAt(splitPoint
, & lastObject
);
3627 wxASSERT(firstObject
!= NULL
);
3628 wxASSERT(lastObject
!= NULL
);
3630 if (!firstObject
|| !lastObject
)
3633 wxRichTextObjectList::compatibility_iterator firstNode
= newPara
->GetChildren().Find(firstObject
);
3634 wxRichTextObjectList::compatibility_iterator lastNode
= newPara
->GetChildren().Find(lastObject
);
3636 wxASSERT(firstNode
);
3639 wxRichTextObjectList::compatibility_iterator node2
= firstNode
;
3643 wxRichTextObject
* child
= node2
->GetData();
3645 if (removeProperties
)
3647 // Removes the given properties from the paragraph
3648 child
->GetProperties().RemoveProperties(properties
);
3650 else if (resetExistingProperties
)
3651 child
->GetProperties() = properties
;
3654 child
->GetProperties().MergeProperties(properties
);
3657 if (node2
== lastNode
)
3660 node2
= node2
->GetNext();
3666 node
= node
->GetNext();
3669 // Do action, or delay it until end of batch.
3670 if (haveControl
&& withUndo
)
3671 buffer
->SubmitAction(action
);
3676 void wxRichTextParagraphLayoutBox::Reset()
3680 wxRichTextBuffer
* buffer
= GetBuffer();
3681 if (buffer
&& buffer
->GetRichTextCtrl())
3683 wxRichTextEvent
event(wxEVT_COMMAND_RICHTEXT_BUFFER_RESET
, buffer
->GetRichTextCtrl()->GetId());
3684 event
.SetEventObject(buffer
->GetRichTextCtrl());
3685 event
.SetContainer(this);
3687 buffer
->SendEvent(event
, true);
3690 AddParagraph(wxEmptyString
);
3692 InvalidateHierarchy(wxRICHTEXT_ALL
);
3695 /// Invalidate the buffer. With no argument, invalidates whole buffer.
3696 void wxRichTextParagraphLayoutBox::Invalidate(const wxRichTextRange
& invalidRange
)
3698 wxRichTextCompositeObject::Invalidate(invalidRange
);
3700 DoInvalidate(invalidRange
);
3703 // Do the (in)validation for this object only
3704 void wxRichTextParagraphLayoutBox::DoInvalidate(const wxRichTextRange
& invalidRange
)
3706 if (invalidRange
== wxRICHTEXT_ALL
)
3708 m_invalidRange
= wxRICHTEXT_ALL
;
3710 // Already invalidating everything
3711 else if (m_invalidRange
== wxRICHTEXT_ALL
)
3716 if ((invalidRange
.GetStart() < m_invalidRange
.GetStart()) || m_invalidRange
.GetStart() == -1)
3717 m_invalidRange
.SetStart(invalidRange
.GetStart());
3718 if (invalidRange
.GetEnd() > m_invalidRange
.GetEnd())
3719 m_invalidRange
.SetEnd(invalidRange
.GetEnd());
3723 // Do the (in)validation both up and down the hierarchy
3724 void wxRichTextParagraphLayoutBox::InvalidateHierarchy(const wxRichTextRange
& invalidRange
)
3726 Invalidate(invalidRange
);
3728 if (invalidRange
!= wxRICHTEXT_NONE
)
3730 // Now go up the hierarchy
3731 wxRichTextObject
* thisObj
= this;
3732 wxRichTextObject
* p
= GetParent();
3735 wxRichTextParagraphLayoutBox
* l
= wxDynamicCast(p
, wxRichTextParagraphLayoutBox
);
3737 l
->DoInvalidate(thisObj
->GetRange());
3745 /// Get invalid range, rounding to entire paragraphs if argument is true.
3746 wxRichTextRange
wxRichTextParagraphLayoutBox::GetInvalidRange(bool wholeParagraphs
) const
3748 if (m_invalidRange
== wxRICHTEXT_ALL
|| m_invalidRange
== wxRICHTEXT_NONE
)
3749 return m_invalidRange
;
3751 wxRichTextRange range
= m_invalidRange
;
3753 if (wholeParagraphs
)
3755 wxRichTextParagraph
* para1
= GetParagraphAtPosition(range
.GetStart());
3757 range
.SetStart(para1
->GetRange().GetStart());
3758 // floating layout make all child should be relayout
3759 range
.SetEnd(GetOwnRange().GetEnd());
3764 /// Apply the style sheet to the buffer, for example if the styles have changed.
3765 bool wxRichTextParagraphLayoutBox::ApplyStyleSheet(wxRichTextStyleSheet
* styleSheet
)
3767 wxASSERT(styleSheet
!= NULL
);
3773 wxRichTextAttr
attr(GetBasicStyle());
3774 if (GetBasicStyle().HasParagraphStyleName())
3776 wxRichTextParagraphStyleDefinition
* paraDef
= styleSheet
->FindParagraphStyle(GetBasicStyle().GetParagraphStyleName());
3779 attr
.Apply(paraDef
->GetStyleMergedWithBase(styleSheet
));
3780 SetBasicStyle(attr
);
3785 if (GetBasicStyle().HasCharacterStyleName())
3787 wxRichTextCharacterStyleDefinition
* charDef
= styleSheet
->FindCharacterStyle(GetBasicStyle().GetCharacterStyleName());
3790 attr
.Apply(charDef
->GetStyleMergedWithBase(styleSheet
));
3791 SetBasicStyle(attr
);
3796 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3799 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3800 // wxASSERT (para != NULL);
3804 // Combine paragraph and list styles. If there is a list style in the original attributes,
3805 // the current indentation overrides anything else and is used to find the item indentation.
3806 // Also, for applying paragraph styles, consider having 2 modes: (1) we merge with what we have,
3807 // thereby taking into account all user changes, (2) reset the style completely (except for indentation/list
3808 // exception as above).
3809 // Problem: when changing from one list style to another, there's a danger that the level info will get lost.
3810 // So when changing a list style interactively, could retrieve level based on current style, then
3811 // set appropriate indent and apply new style.
3815 if (para
->GetAttributes().HasOutlineLevel())
3816 outline
= para
->GetAttributes().GetOutlineLevel();
3817 if (para
->GetAttributes().HasBulletNumber())
3818 num
= para
->GetAttributes().GetBulletNumber();
3820 if (!para
->GetAttributes().GetParagraphStyleName().IsEmpty() && !para
->GetAttributes().GetListStyleName().IsEmpty())
3822 int currentIndent
= para
->GetAttributes().GetLeftIndent();
3824 wxRichTextParagraphStyleDefinition
* paraDef
= styleSheet
->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
3825 wxRichTextListStyleDefinition
* listDef
= styleSheet
->FindListStyle(para
->GetAttributes().GetListStyleName());
3826 if (paraDef
&& !listDef
)
3828 para
->GetAttributes() = paraDef
->GetStyleMergedWithBase(styleSheet
);
3831 else if (listDef
&& !paraDef
)
3833 // Set overall style defined for the list style definition
3834 para
->GetAttributes() = listDef
->GetStyleMergedWithBase(styleSheet
);
3836 // Apply the style for this level
3837 wxRichTextApplyStyle(para
->GetAttributes(), * listDef
->GetLevelAttributes(listDef
->FindLevelForIndent(currentIndent
)));
3840 else if (listDef
&& paraDef
)
3842 // Combines overall list style, style for level, and paragraph style
3843 para
->GetAttributes() = listDef
->CombineWithParagraphStyle(currentIndent
, paraDef
->GetStyleMergedWithBase(styleSheet
));
3847 else if (para
->GetAttributes().GetParagraphStyleName().IsEmpty() && !para
->GetAttributes().GetListStyleName().IsEmpty())
3849 int currentIndent
= para
->GetAttributes().GetLeftIndent();
3851 wxRichTextListStyleDefinition
* listDef
= styleSheet
->FindListStyle(para
->GetAttributes().GetListStyleName());
3853 // Overall list definition style
3854 para
->GetAttributes() = listDef
->GetStyleMergedWithBase(styleSheet
);
3856 // Style for this level
3857 wxRichTextApplyStyle(para
->GetAttributes(), * listDef
->GetLevelAttributes(listDef
->FindLevelForIndent(currentIndent
)));
3861 else if (!para
->GetAttributes().GetParagraphStyleName().IsEmpty() && para
->GetAttributes().GetListStyleName().IsEmpty())
3863 wxRichTextParagraphStyleDefinition
* def
= styleSheet
->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
3866 para
->GetAttributes() = def
->GetStyleMergedWithBase(styleSheet
);
3872 para
->GetAttributes().SetOutlineLevel(outline
);
3874 para
->GetAttributes().SetBulletNumber(num
);
3877 node
= node
->GetNext();
3879 return foundCount
!= 0;
3883 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
3885 wxRichTextBuffer
* buffer
= GetBuffer();
3886 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
3888 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
3889 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
3890 bool specifyLevel
= ((flags
& wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL
) != 0);
3891 bool renumber
= ((flags
& wxRICHTEXT_SETSTYLE_RENUMBER
) != 0);
3893 // Current number, if numbering
3896 wxASSERT (!specifyLevel
|| (specifyLevel
&& (specifiedLevel
>= 0)));
3898 // If we are associated with a control, make undoable; otherwise, apply immediately
3901 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3903 wxRichTextAction
* action
= NULL
;
3905 if (haveControl
&& withUndo
)
3907 action
= new wxRichTextAction(NULL
, _("Change List Style"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
3908 action
->SetRange(range
);
3909 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3912 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3915 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3916 // wxASSERT (para != NULL);
3918 if (para
&& para
->GetChildCount() > 0)
3920 // Stop searching if we're beyond the range of interest
3921 if (para
->GetRange().GetStart() > range
.GetEnd())
3924 if (!para
->GetRange().IsOutside(range
))
3926 // We'll be using a copy of the paragraph to make style changes,
3927 // not updating the buffer directly.
3928 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3930 if (haveControl
&& withUndo
)
3932 newPara
= new wxRichTextParagraph(*para
);
3933 action
->GetNewParagraphs().AppendChild(newPara
);
3935 // Also store the old ones for Undo
3936 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3943 int thisIndent
= newPara
->GetAttributes().GetLeftIndent();
3944 int thisLevel
= specifyLevel
? specifiedLevel
: def
->FindLevelForIndent(thisIndent
);
3946 // How is numbering going to work?
3947 // If we are renumbering, or numbering for the first time, we need to keep
3948 // track of the number for each level. But we might be simply applying a different
3950 // In Word, applying a style to several paragraphs, even if at different levels,
3951 // reverts the level back to the same one. So we could do the same here.
3952 // Renumbering will need to be done when we promote/demote a paragraph.
3954 // Apply the overall list style, and item style for this level
3955 wxRichTextAttr
listStyle(def
->GetCombinedStyleForLevel(thisLevel
, styleSheet
));
3956 wxRichTextApplyStyle(newPara
->GetAttributes(), listStyle
);
3958 // Now we need to do numbering
3961 newPara
->GetAttributes().SetBulletNumber(n
);
3966 else if (!newPara
->GetAttributes().GetListStyleName().IsEmpty())
3968 // if def is NULL, remove list style, applying any associated paragraph style
3969 // to restore the attributes
3971 newPara
->GetAttributes().SetListStyleName(wxEmptyString
);
3972 newPara
->GetAttributes().SetLeftIndent(0, 0);
3973 newPara
->GetAttributes().SetBulletText(wxEmptyString
);
3975 // Eliminate the main list-related attributes
3976 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
);
3978 if (styleSheet
&& !newPara
->GetAttributes().GetParagraphStyleName().IsEmpty())
3980 wxRichTextParagraphStyleDefinition
* def
= styleSheet
->FindParagraphStyle(newPara
->GetAttributes().GetParagraphStyleName());
3983 newPara
->GetAttributes() = def
->GetStyleMergedWithBase(styleSheet
);
3990 node
= node
->GetNext();
3993 // Do action, or delay it until end of batch.
3994 if (haveControl
&& withUndo
)
3995 buffer
->SubmitAction(action
);
4000 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
)
4002 wxRichTextBuffer
* buffer
= GetBuffer();
4003 if (buffer
&& buffer
->GetStyleSheet())
4005 wxRichTextListStyleDefinition
* def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4007 return SetListStyle(range
, def
, flags
, startFrom
, specifiedLevel
);
4012 /// Clear list for given range
4013 bool wxRichTextParagraphLayoutBox::ClearListStyle(const wxRichTextRange
& range
, int flags
)
4015 return SetListStyle(range
, NULL
, flags
);
4018 /// Number/renumber any list elements in the given range
4019 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
4021 return DoNumberList(range
, range
, 0, def
, flags
, startFrom
, specifiedLevel
);
4024 /// Number/renumber any list elements in the given range. Also do promotion or demotion of items, if specified
4025 bool wxRichTextParagraphLayoutBox::DoNumberList(const wxRichTextRange
& range
, const wxRichTextRange
& promotionRange
, int promoteBy
,
4026 wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
4028 wxRichTextBuffer
* buffer
= GetBuffer();
4029 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
4031 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
4032 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
4034 bool specifyLevel
= ((flags
& wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL
) != 0);
4037 bool renumber
= ((flags
& wxRICHTEXT_SETSTYLE_RENUMBER
) != 0);
4039 // Max number of levels
4040 const int maxLevels
= 10;
4042 // The level we're looking at now
4043 int currentLevel
= -1;
4045 // The item number for each level
4046 int levels
[maxLevels
];
4049 // Reset all numbering
4050 for (i
= 0; i
< maxLevels
; i
++)
4052 if (startFrom
!= -1)
4053 levels
[i
] = startFrom
-1;
4054 else if (renumber
) // start again
4057 levels
[i
] = -1; // start from the number we found, if any
4061 wxASSERT(!specifyLevel
|| (specifyLevel
&& (specifiedLevel
>= 0)));
4064 // If we are associated with a control, make undoable; otherwise, apply immediately
4067 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
4069 wxRichTextAction
* action
= NULL
;
4071 if (haveControl
&& withUndo
)
4073 action
= new wxRichTextAction(NULL
, _("Renumber List"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
4074 action
->SetRange(range
);
4075 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
4078 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
4081 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
4082 // wxASSERT (para != NULL);
4084 if (para
&& para
->GetChildCount() > 0)
4086 // Stop searching if we're beyond the range of interest
4087 if (para
->GetRange().GetStart() > range
.GetEnd())
4090 if (!para
->GetRange().IsOutside(range
))
4092 // We'll be using a copy of the paragraph to make style changes,
4093 // not updating the buffer directly.
4094 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
4096 if (haveControl
&& withUndo
)
4098 newPara
= new wxRichTextParagraph(*para
);
4099 action
->GetNewParagraphs().AppendChild(newPara
);
4101 // Also store the old ones for Undo
4102 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
4107 wxRichTextListStyleDefinition
* defToUse
= def
;
4110 if (styleSheet
&& !newPara
->GetAttributes().GetListStyleName().IsEmpty())
4111 defToUse
= styleSheet
->FindListStyle(newPara
->GetAttributes().GetListStyleName());
4116 int thisIndent
= newPara
->GetAttributes().GetLeftIndent();
4117 int thisLevel
= defToUse
->FindLevelForIndent(thisIndent
);
4119 // If we've specified a level to apply to all, change the level.
4120 if (specifiedLevel
!= -1)
4121 thisLevel
= specifiedLevel
;
4123 // Do promotion if specified
4124 if ((promoteBy
!= 0) && !para
->GetRange().IsOutside(promotionRange
))
4126 thisLevel
= thisLevel
- promoteBy
;
4133 // Apply the overall list style, and item style for this level
4134 wxRichTextAttr
listStyle(defToUse
->GetCombinedStyleForLevel(thisLevel
, styleSheet
));
4135 wxRichTextApplyStyle(newPara
->GetAttributes(), listStyle
);
4137 // OK, we've (re)applied the style, now let's get the numbering right.
4139 if (currentLevel
== -1)
4140 currentLevel
= thisLevel
;
4142 // Same level as before, do nothing except increment level's number afterwards
4143 if (currentLevel
== thisLevel
)
4146 // A deeper level: start renumbering all levels after current level
4147 else if (thisLevel
> currentLevel
)
4149 for (i
= currentLevel
+1; i
<= thisLevel
; i
++)
4153 currentLevel
= thisLevel
;
4155 else if (thisLevel
< currentLevel
)
4157 currentLevel
= thisLevel
;
4160 // Use the current numbering if -1 and we have a bullet number already
4161 if (levels
[currentLevel
] == -1)
4163 if (newPara
->GetAttributes().HasBulletNumber())
4164 levels
[currentLevel
] = newPara
->GetAttributes().GetBulletNumber();
4166 levels
[currentLevel
] = 1;
4170 levels
[currentLevel
] ++;
4173 newPara
->GetAttributes().SetBulletNumber(levels
[currentLevel
]);
4175 // Create the bullet text if an outline list
4176 if (listStyle
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
)
4179 for (i
= 0; i
<= currentLevel
; i
++)
4181 if (!text
.IsEmpty())
4183 text
+= wxString::Format(wxT("%d"), levels
[i
]);
4185 newPara
->GetAttributes().SetBulletText(text
);
4191 node
= node
->GetNext();
4194 // Do action, or delay it until end of batch.
4195 if (haveControl
&& withUndo
)
4196 buffer
->SubmitAction(action
);
4201 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
)
4203 wxRichTextBuffer
* buffer
= GetBuffer();
4204 if (buffer
->GetStyleSheet())
4206 wxRichTextListStyleDefinition
* def
= NULL
;
4207 if (!defName
.IsEmpty())
4208 def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4209 return NumberList(range
, def
, flags
, startFrom
, specifiedLevel
);
4214 /// Promote the list items within the given range. promoteBy can be a positive or negative number, e.g. 1 or -1
4215 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy
, const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int specifiedLevel
)
4218 // One strategy is to first work out the range within which renumbering must occur. Then could pass these two ranges
4219 // to NumberList with a flag indicating promotion is required within one of the ranges.
4220 // Find first and last paragraphs in range. Then for first, calculate new indentation and look back until we find
4221 // a paragraph that either has no list style, or has one that is different or whose indentation is less.
4222 // We start renumbering from the para after that different para we found. We specify that the numbering of that
4223 // list position will start from 1.
4224 // Similarly, we look after the last para in the promote range for an indentation that is less (or no list style).
4225 // We can end the renumbering at this point.
4227 // For now, only renumber within the promotion range.
4229 return DoNumberList(range
, range
, promoteBy
, def
, flags
, 1, specifiedLevel
);
4232 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy
, const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int specifiedLevel
)
4234 wxRichTextBuffer
* buffer
= GetBuffer();
4235 if (buffer
->GetStyleSheet())
4237 wxRichTextListStyleDefinition
* def
= NULL
;
4238 if (!defName
.IsEmpty())
4239 def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4240 return PromoteList(promoteBy
, range
, def
, flags
, specifiedLevel
);
4245 /// Fills in the attributes for numbering a paragraph after previousParagraph. It also finds the
4246 /// position of the paragraph that it had to start looking from.
4247 bool wxRichTextParagraphLayoutBox::FindNextParagraphNumber(wxRichTextParagraph
* previousParagraph
, wxRichTextAttr
& attr
) const
4249 if (!previousParagraph
->GetAttributes().HasFlag(wxTEXT_ATTR_BULLET_STYLE
) || previousParagraph
->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
)
4252 wxRichTextBuffer
* buffer
= GetBuffer();
4253 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
4254 if (styleSheet
&& !previousParagraph
->GetAttributes().GetListStyleName().IsEmpty())
4256 wxRichTextListStyleDefinition
* def
= styleSheet
->FindListStyle(previousParagraph
->GetAttributes().GetListStyleName());
4259 // int thisIndent = previousParagraph->GetAttributes().GetLeftIndent();
4260 // int thisLevel = def->FindLevelForIndent(thisIndent);
4262 bool isOutline
= (previousParagraph
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
) != 0;
4264 attr
.SetFlags(previousParagraph
->GetAttributes().GetFlags() & (wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_BULLET_NUMBER
|wxTEXT_ATTR_BULLET_TEXT
|wxTEXT_ATTR_BULLET_NAME
));
4265 if (previousParagraph
->GetAttributes().HasBulletName())
4266 attr
.SetBulletName(previousParagraph
->GetAttributes().GetBulletName());
4267 attr
.SetBulletStyle(previousParagraph
->GetAttributes().GetBulletStyle());
4268 attr
.SetListStyleName(previousParagraph
->GetAttributes().GetListStyleName());
4270 int nextNumber
= previousParagraph
->GetAttributes().GetBulletNumber() + 1;
4271 attr
.SetBulletNumber(nextNumber
);
4275 wxString text
= previousParagraph
->GetAttributes().GetBulletText();
4276 if (!text
.IsEmpty())
4278 int pos
= text
.Find(wxT('.'), true);
4279 if (pos
!= wxNOT_FOUND
)
4281 text
= text
.Mid(0, text
.Length() - pos
- 1);
4284 text
= wxEmptyString
;
4285 if (!text
.IsEmpty())
4287 text
+= wxString::Format(wxT("%d"), nextNumber
);
4288 attr
.SetBulletText(text
);
4302 * wxRichTextParagraph
4303 * This object represents a single paragraph (or in a straight text editor, a line).
4306 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraph
, wxRichTextCompositeObject
)
4308 wxArrayInt
wxRichTextParagraph::sm_defaultTabs
;
4310 wxRichTextParagraph::wxRichTextParagraph(wxRichTextObject
* parent
, wxRichTextAttr
* style
):
4311 wxRichTextCompositeObject(parent
)
4314 SetAttributes(*style
);
4317 wxRichTextParagraph::wxRichTextParagraph(const wxString
& text
, wxRichTextObject
* parent
, wxRichTextAttr
* paraStyle
, wxRichTextAttr
* charStyle
):
4318 wxRichTextCompositeObject(parent
)
4321 SetAttributes(*paraStyle
);
4323 AppendChild(new wxRichTextPlainText(text
, this, charStyle
));
4326 wxRichTextParagraph::~wxRichTextParagraph()
4332 bool wxRichTextParagraph::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int WXUNUSED(descent
), int style
)
4337 // Currently we don't merge these attributes with the parent, but we
4338 // should consider whether we should (e.g. if we set a border colour
4339 // for all paragraphs). But generally box attributes are likely to be
4340 // different for different objects.
4341 wxRect paraRect
= GetRect();
4342 DrawBoxAttributes(dc
, GetBuffer(), GetAttributes(), paraRect
);
4344 wxRichTextAttr attr
= GetCombinedAttributes();
4346 // Draw the bullet, if any
4347 if (attr
.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE
)
4349 if (attr
.GetLeftSubIndent() != 0)
4351 int spaceBeforePara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingBefore());
4352 int leftIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftIndent());
4354 wxRichTextAttr
bulletAttr(GetCombinedAttributes());
4356 // Combine with the font of the first piece of content, if one is specified
4357 if (GetChildren().GetCount() > 0)
4359 wxRichTextObject
* firstObj
= (wxRichTextObject
*) GetChildren().GetFirst()->GetData();
4360 if (!firstObj
->IsFloatable() && firstObj
->GetAttributes().HasFont())
4362 wxRichTextApplyStyle(bulletAttr
, firstObj
->GetAttributes());
4366 // Get line height from first line, if any
4367 wxRichTextLine
* line
= m_cachedLines
.GetFirst() ? (wxRichTextLine
* ) m_cachedLines
.GetFirst()->GetData() : NULL
;
4370 int lineHeight
wxDUMMY_INITIALIZE(0);
4373 lineHeight
= line
->GetSize().y
;
4374 linePos
= line
->GetPosition() + GetPosition();
4379 if (bulletAttr
.HasFont() && GetBuffer())
4380 font
= GetBuffer()->GetFontTable().FindFont(bulletAttr
);
4382 font
= (*wxNORMAL_FONT
);
4384 wxCheckSetFont(dc
, font
);
4386 lineHeight
= dc
.GetCharHeight();
4387 linePos
= GetPosition();
4388 linePos
.y
+= spaceBeforePara
;
4391 wxRect
bulletRect(GetPosition().x
+ leftIndent
, linePos
.y
, linePos
.x
- (GetPosition().x
+ leftIndent
), lineHeight
);
4393 if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
)
4395 if (wxRichTextBuffer::GetRenderer())
4396 wxRichTextBuffer::GetRenderer()->DrawBitmapBullet(this, dc
, bulletAttr
, bulletRect
);
4398 else if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_STANDARD
)
4400 if (wxRichTextBuffer::GetRenderer())
4401 wxRichTextBuffer::GetRenderer()->DrawStandardBullet(this, dc
, bulletAttr
, bulletRect
);
4405 wxString bulletText
= GetBulletText();
4407 if (!bulletText
.empty() && wxRichTextBuffer::GetRenderer())
4408 wxRichTextBuffer::GetRenderer()->DrawTextBullet(this, dc
, bulletAttr
, bulletRect
, bulletText
);
4413 // Draw the range for each line, one object at a time.
4415 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
4418 wxRichTextLine
* line
= node
->GetData();
4419 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
4421 // Lines are specified relative to the paragraph
4423 wxPoint linePosition
= line
->GetPosition() + GetPosition();
4425 // Don't draw if off the screen
4426 if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) != 0) || ((linePosition
.y
+ line
->GetSize().y
) >= rect
.y
&& linePosition
.y
<= rect
.y
+ rect
.height
))
4428 wxPoint objectPosition
= linePosition
;
4429 int maxDescent
= line
->GetDescent();
4431 // Loop through objects until we get to the one within range
4432 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
4437 wxRichTextObject
* child
= node2
->GetData();
4439 if (!child
->IsFloating() && child
->GetRange().GetLength() > 0 && !child
->GetRange().IsOutside(lineRange
) && !lineRange
.IsOutside(range
))
4441 // Draw this part of the line at the correct position
4442 wxRichTextRange
objectRange(child
->GetRange());
4443 objectRange
.LimitTo(lineRange
);
4446 if (child
->IsTopLevel())
4448 objectSize
= child
->GetCachedSize();
4449 objectRange
= child
->GetOwnRange();
4453 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING && wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4454 if (i
< (int) line
->GetObjectSizes().GetCount())
4456 objectSize
.x
= line
->GetObjectSizes()[(size_t) i
];
4462 child
->GetRangeSize(objectRange
, objectSize
, descent
, dc
, wxRICHTEXT_UNFORMATTED
, objectPosition
);
4466 // Use the child object's width, but the whole line's height
4467 wxRect
childRect(objectPosition
, wxSize(objectSize
.x
, line
->GetSize().y
));
4468 child
->Draw(dc
, objectRange
, selection
, childRect
, maxDescent
, style
);
4470 objectPosition
.x
+= objectSize
.x
;
4473 else if (child
->GetRange().GetStart() > lineRange
.GetEnd())
4474 // Can break out of inner loop now since we've passed this line's range
4477 node2
= node2
->GetNext();
4481 node
= node
->GetNext();
4487 // Get the range width using partial extents calculated for the whole paragraph.
4488 static int wxRichTextGetRangeWidth(const wxRichTextParagraph
& para
, const wxRichTextRange
& range
, const wxArrayInt
& partialExtents
)
4490 wxASSERT(partialExtents
.GetCount() >= (size_t) range
.GetLength());
4492 if (partialExtents
.GetCount() < (size_t) range
.GetLength())
4495 int leftMostPos
= 0;
4496 if (range
.GetStart() - para
.GetRange().GetStart() > 0)
4497 leftMostPos
= partialExtents
[range
.GetStart() - para
.GetRange().GetStart() - 1];
4499 int rightMostPos
= partialExtents
[range
.GetEnd() - para
.GetRange().GetStart()];
4501 int w
= rightMostPos
- leftMostPos
;
4506 /// Lay the item out
4507 bool wxRichTextParagraph::Layout(wxDC
& dc
, const wxRect
& rect
, const wxRect
& parentRect
, int style
)
4509 // Deal with floating objects firstly before the normal layout
4510 wxRichTextBuffer
* buffer
= GetBuffer();
4512 wxRichTextFloatCollector
* collector
= GetContainer()->GetFloatCollector();
4513 wxASSERT(collector
);
4514 LayoutFloat(dc
, rect
, style
, collector
);
4516 wxRichTextAttr attr
= GetCombinedAttributes();
4520 // Increase the size of the paragraph due to spacing
4521 int spaceBeforePara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingBefore());
4522 int spaceAfterPara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingAfter());
4523 int leftIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftIndent());
4524 int leftSubIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftSubIndent());
4525 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
4527 int lineSpacing
= 0;
4529 // Let's assume line spacing of 10 is normal, 15 is 1.5, 20 is 2, etc.
4530 if (attr
.HasLineSpacing() && attr
.GetLineSpacing() > 0 && attr
.GetFont().IsOk())
4532 wxCheckSetFont(dc
, attr
.GetFont());
4533 lineSpacing
= (int) (double(dc
.GetCharHeight()) * (double(attr
.GetLineSpacing())/10.0 - 1.0));
4536 // Start position for each line relative to the paragraph
4537 int startPositionFirstLine
= leftIndent
;
4538 int startPositionSubsequentLines
= leftIndent
+ leftSubIndent
;
4540 // If we have a bullet in this paragraph, the start position for the first line's text
4541 // is actually leftIndent + leftSubIndent.
4542 if (attr
.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE
)
4543 startPositionFirstLine
= startPositionSubsequentLines
;
4545 long lastEndPos
= GetRange().GetStart()-1;
4546 long lastCompletedEndPos
= lastEndPos
;
4548 int currentWidth
= 0;
4549 SetPosition(rect
.GetPosition());
4551 wxPoint
currentPosition(0, spaceBeforePara
); // We will calculate lines relative to paragraph
4554 int maxHeight
= currentPosition
.y
;
4559 int lineDescent
= 0;
4561 wxRichTextObjectList::compatibility_iterator node
;
4563 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4565 wxArrayInt partialExtents
;
4568 int paraDescent
= 0;
4570 // This calculates the partial text extents
4571 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, rect
.GetPosition(), & partialExtents
);
4573 node
= m_children
.GetFirst();
4576 wxRichTextObject
* child
= node
->GetData();
4578 //child->SetCachedSize(wxDefaultSize);
4579 child
->Layout(dc
, rect
, style
);
4581 node
= node
->GetNext();
4588 // We may need to go back to a previous child, in which case create the new line,
4589 // find the child corresponding to the start position of the string, and
4592 wxRect availableRect
;
4594 node
= m_children
.GetFirst();
4597 wxRichTextObject
* child
= node
->GetData();
4599 // If floating, ignore. We already laid out floats.
4600 // Also ignore if empty object, except if we haven't got any
4602 if (child
->IsFloating() || !child
->IsShown() ||
4603 (child
->GetRange().GetLength() == 0 && maxHeight
> spaceBeforePara
)
4606 node
= node
->GetNext();
4610 // If this is e.g. a composite text box, it will need to be laid out itself.
4611 // But if just a text fragment or image, for example, this will
4612 // do nothing. NB: won't we need to set the position after layout?
4613 // since for example if position is dependent on vertical line size, we
4614 // can't tell the position until the size is determined. So possibly introduce
4615 // another layout phase.
4617 // We may only be looking at part of a child, if we searched back for wrapping
4618 // and found a suitable point some way into the child. So get the size for the fragment
4621 long nextBreakPos
= GetFirstLineBreakPosition(lastEndPos
+1);
4622 long lastPosToUse
= child
->GetRange().GetEnd();
4623 bool lineBreakInThisObject
= (nextBreakPos
> -1 && nextBreakPos
<= child
->GetRange().GetEnd());
4625 if (lineBreakInThisObject
)
4626 lastPosToUse
= nextBreakPos
;
4629 int childDescent
= 0;
4631 int startOffset
= (lineCount
== 0 ? startPositionFirstLine
: startPositionSubsequentLines
);
4632 availableRect
= wxRect(rect
.x
+ startOffset
, rect
.y
+ currentPosition
.y
,
4633 rect
.width
- startOffset
- rightIndent
, rect
.height
);
4635 if (child
->IsTopLevel())
4637 wxSize oldSize
= child
->GetCachedSize();
4639 child
->Invalidate(wxRICHTEXT_ALL
);
4640 child
->SetPosition(wxPoint(0, 0));
4642 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4643 // lays out the object again using the minimum size
4644 // The position will be determined by its location in its line,
4645 // and not by the child's actual position.
4646 child
->LayoutToBestSize(dc
, buffer
,
4647 GetAttributes(), child
->GetAttributes(), availableRect
, parentRect
, style
);
4649 if (oldSize
!= child
->GetCachedSize())
4651 partialExtents
.Clear();
4653 // Recalculate the partial text extents since the child object changed size
4654 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), & partialExtents
);
4658 // Problem: we need to layout composites here for which we need the available width,
4659 // but we can't get the available width without using the float collector which
4660 // needs to know the object height.
4662 if ((nextBreakPos
== -1) && (lastEndPos
== child
->GetRange().GetStart() - 1)) // i.e. we want to get the whole thing
4664 childSize
= child
->GetCachedSize();
4665 childDescent
= child
->GetDescent();
4669 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4670 // Get height only, then the width using the partial extents
4671 GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
);
4672 childSize
.x
= wxRichTextGetRangeWidth(*this, wxRichTextRange(lastEndPos
+1, lastPosToUse
), partialExtents
);
4674 GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, wxRICHTEXT_UNFORMATTED
, rect
.GetPosition());
4679 int loopIterations
= 0;
4681 // If there are nested objects that need to lay themselves out, we have to do this in a
4682 // loop because the height of the object may well depend on the available width.
4683 // And because of floating object positioning, the available width depends on the
4684 // height of the object and whether it will clash with the floating objects.
4685 // So, we see whether the available width changes due to the presence of floating images.
4686 // If it does, then we'll use the new restricted width to find the object height again.
4687 // If this causes another restriction in the available width, we'll try again, until
4688 // either we lose patience or the available width settles down.
4693 wxRect oldAvailableRect
= availableRect
;
4695 // Available width depends on the floating objects and the line height.
4696 // Note: the floating objects may be placed vertically along the two side of
4697 // buffer, so we may have different available line widths with different
4698 // [startY, endY]. So, we can't determine how wide the available
4699 // space is until we know the exact line height.
4700 lineDescent
= wxMax(childDescent
, maxDescent
);
4701 lineAscent
= wxMax(childSize
.y
-childDescent
, maxAscent
);
4702 lineHeight
= lineDescent
+ lineAscent
;
4703 wxRect floatAvailableRect
= collector
->GetAvailableRect(rect
.y
+ currentPosition
.y
, rect
.y
+ currentPosition
.y
+ lineHeight
);
4705 // Adjust availableRect to the space that is available when taking floating objects into account.
4707 if (floatAvailableRect
.x
+ startOffset
> availableRect
.x
)
4709 int newX
= floatAvailableRect
.x
+ startOffset
;
4710 int newW
= availableRect
.width
- (newX
- availableRect
.x
);
4711 availableRect
.x
= newX
;
4712 availableRect
.width
= newW
;
4715 if (floatAvailableRect
.width
< availableRect
.width
)
4716 availableRect
.width
= floatAvailableRect
.width
;
4718 currentPosition
.x
= availableRect
.x
- rect
.x
;
4720 if (child
->IsTopLevel() && loopIterations
<= 20)
4722 if (availableRect
!= oldAvailableRect
)
4724 wxSize oldSize
= child
->GetCachedSize();
4726 //child->SetCachedSize(wxDefaultSize);
4727 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4728 // lays out the object again using the minimum size
4729 child
->Invalidate(wxRICHTEXT_ALL
);
4730 child
->LayoutToBestSize(dc
, buffer
,
4731 GetAttributes(), child
->GetAttributes(), availableRect
, parentRect
, style
);
4732 childSize
= child
->GetCachedSize();
4733 childDescent
= child
->GetDescent();
4734 //child->SetPosition(availableRect.GetPosition());
4736 if (oldSize
!= child
->GetCachedSize())
4738 partialExtents
.Clear();
4740 // Recalculate the partial text extents since the child object changed size
4741 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), & partialExtents
);
4744 // Go around the loop finding the available rect for the given floating objects
4755 // 1) There was a line break BEFORE the natural break
4756 // 2) There was a line break AFTER the natural break
4757 // 3) It's the last line
4758 // 4) The child still fits (carry on) - 'else' clause
4760 if ((lineBreakInThisObject
&& (childSize
.x
+ currentWidth
<= availableRect
.width
))
4762 (childSize
.x
+ currentWidth
> availableRect
.width
)
4764 ((childSize
.x
+ currentWidth
<= availableRect
.width
) && !node
->GetNext())
4768 if (child
->IsTopLevel())
4770 // We can move it to the correct position at this point
4771 child
->Move(GetPosition() + wxPoint(currentWidth
, currentPosition
.y
));
4774 long wrapPosition
= 0;
4775 if ((childSize
.x
+ currentWidth
<= availableRect
.width
) && !node
->GetNext() && !lineBreakInThisObject
)
4776 wrapPosition
= child
->GetRange().GetEnd();
4779 // Find a place to wrap. This may walk back to previous children,
4780 // for example if a word spans several objects.
4781 // Note: one object must contains only one wxTextAtrr, so the line height will not
4782 // change inside one object. Thus, we can pass the remain line width to the
4783 // FindWrapPosition function.
4784 if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos
+1, child
->GetRange().GetEnd()), dc
, availableRect
.width
, wrapPosition
, & partialExtents
))
4786 // If the function failed, just cut it off at the end of this child.
4787 wrapPosition
= child
->GetRange().GetEnd();
4790 // FindWrapPosition can still return a value that will put us in an endless wrapping loop
4791 if (wrapPosition
<= lastCompletedEndPos
)
4792 wrapPosition
= wxMax(lastCompletedEndPos
+1,child
->GetRange().GetEnd());
4794 // Line end position shouldn't be the same as the end, or greater.
4795 if (wrapPosition
>= GetRange().GetEnd())
4796 wrapPosition
= GetRange().GetEnd()-1;
4798 // wxLogDebug(wxT("Split at %ld"), wrapPosition);
4800 // Let's find the actual size of the current line now
4802 wxRichTextRange
actualRange(lastCompletedEndPos
+1, wrapPosition
);
4804 /// Use previous descent, not the wrapping descent we just found, since this may be too big
4805 /// for the fragment we're about to add.
4806 childDescent
= maxDescent
;
4808 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4809 if (!child
->IsEmpty())
4811 // Get height only, then the width using the partial extents
4812 GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
);
4813 actualSize
.x
= wxRichTextGetRangeWidth(*this, actualRange
, partialExtents
);
4817 GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, wxRICHTEXT_UNFORMATTED
);
4819 currentWidth
= actualSize
.x
;
4820 maxDescent
= wxMax(childDescent
, maxDescent
);
4821 maxAscent
= wxMax(actualSize
.y
-childDescent
, maxAscent
);
4822 lineHeight
= maxDescent
+ maxAscent
;
4824 if (lineHeight
== 0 && buffer
)
4826 wxFont
font(buffer
->GetFontTable().FindFont(attr
));
4827 wxCheckSetFont(dc
, font
);
4828 lineHeight
= dc
.GetCharHeight();
4831 if (maxDescent
== 0)
4834 dc
.GetTextExtent(wxT("X"), & w
, &h
, & maxDescent
);
4838 wxRichTextLine
* line
= AllocateLine(lineCount
);
4840 // Set relative range so we won't have to change line ranges when paragraphs are moved
4841 line
->SetRange(wxRichTextRange(actualRange
.GetStart() - GetRange().GetStart(), actualRange
.GetEnd() - GetRange().GetStart()));
4842 line
->SetPosition(currentPosition
);
4843 line
->SetSize(wxSize(currentWidth
, lineHeight
));
4844 line
->SetDescent(maxDescent
);
4846 maxHeight
= currentPosition
.y
+ lineHeight
;
4848 // Now move down a line. TODO: add margins, spacing
4849 currentPosition
.y
+= lineHeight
;
4850 currentPosition
.y
+= lineSpacing
;
4853 maxWidth
= wxMax(maxWidth
, currentWidth
+startOffset
);
4858 // TODO: account for zero-length objects, such as fields
4859 // wxASSERT(wrapPosition > lastCompletedEndPos);
4861 lastEndPos
= wrapPosition
;
4862 lastCompletedEndPos
= lastEndPos
;
4866 if (wrapPosition
< GetRange().GetEnd()-1)
4868 // May need to set the node back to a previous one, due to searching back in wrapping
4869 wxRichTextObject
* childAfterWrapPosition
= FindObjectAtPosition(wrapPosition
+1);
4870 if (childAfterWrapPosition
)
4871 node
= m_children
.Find(childAfterWrapPosition
);
4873 node
= node
->GetNext();
4876 node
= node
->GetNext();
4878 // Apply paragraph styles such as alignment to the wrapped line
4879 ApplyParagraphStyle(line
, attr
, availableRect
, dc
);
4883 // We still fit, so don't add a line, and keep going
4884 currentWidth
+= childSize
.x
;
4885 maxDescent
= wxMax(childDescent
, maxDescent
);
4886 maxAscent
= wxMax(childSize
.y
-childDescent
, maxAscent
);
4887 lineHeight
= maxDescent
+ maxAscent
;
4889 maxWidth
= wxMax(maxWidth
, currentWidth
+startOffset
);
4890 lastEndPos
= child
->GetRange().GetEnd();
4892 node
= node
->GetNext();
4896 //wxASSERT(!(lastCompletedEndPos != -1 && lastCompletedEndPos < GetRange().GetEnd()-1));
4898 // Remove remaining unused line objects, if any
4899 ClearUnusedLines(lineCount
);
4901 // We need to add back the margins etc.
4903 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
4904 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxWidth
, currentPosition
.y
+ spaceAfterPara
));
4905 GetBoxRects(dc
, buffer
, GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
4906 SetCachedSize(marginRect
.GetSize());
4909 // The maximum size is the length of the paragraph stretched out into a line.
4910 // So if there were a single word, or an image, or a fixed-size text box, the object could be shrunk around
4911 // this size. TODO: take into account line breaks.
4913 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
4914 contentRect
= wxRect(wxPoint(0, 0), wxSize(paraSize
.x
+ wxMax(leftIndent
, leftIndent
+ leftSubIndent
) + rightIndent
, currentPosition
.y
+ spaceAfterPara
));
4915 GetBoxRects(dc
, buffer
, GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
4916 SetMaxSize(marginRect
.GetSize());
4919 // Find the greatest minimum size. Currently we only look at non-text objects,
4920 // which isn't ideal but it would be slow to find the maximum word width to
4921 // use as the minimum.
4924 node
= m_children
.GetFirst();
4927 wxRichTextObject
* child
= node
->GetData();
4929 // If floating, ignore. We already laid out floats.
4930 // Also ignore if empty object, except if we haven't got any
4932 if (!child
->IsFloating() && child
->GetRange().GetLength() != 0 && !child
->IsKindOf(CLASSINFO(wxRichTextPlainText
)))
4934 if (child
->GetCachedSize().x
> minWidth
)
4935 minWidth
= child
->GetMinSize().x
;
4937 node
= node
->GetNext();
4940 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
4941 contentRect
= wxRect(wxPoint(0, 0), wxSize(minWidth
, currentPosition
.y
+ spaceAfterPara
));
4942 GetBoxRects(dc
, buffer
, GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
4943 SetMinSize(marginRect
.GetSize());
4947 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4948 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
4949 // Use the text extents to calculate the size of each fragment in each line
4950 wxRichTextLineList::compatibility_iterator lineNode
= m_cachedLines
.GetFirst();
4953 wxRichTextLine
* line
= lineNode
->GetData();
4954 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
4956 // Loop through objects until we get to the one within range
4957 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
4961 wxRichTextObject
* child
= node2
->GetData();
4963 if (child
->GetRange().GetLength() > 0 && !child
->GetRange().IsOutside(lineRange
))
4965 wxRichTextRange rangeToUse
= lineRange
;
4966 rangeToUse
.LimitTo(child
->GetRange());
4968 // Find the size of the child from the text extents, and store in an array
4969 // for drawing later
4971 if (rangeToUse
.GetStart() > GetRange().GetStart())
4972 left
= partialExtents
[(rangeToUse
.GetStart()-1) - GetRange().GetStart()];
4973 int right
= partialExtents
[rangeToUse
.GetEnd() - GetRange().GetStart()];
4974 int sz
= right
- left
;
4975 line
->GetObjectSizes().Add(sz
);
4977 else if (child
->GetRange().GetStart() > lineRange
.GetEnd())
4978 // Can break out of inner loop now since we've passed this line's range
4981 node2
= node2
->GetNext();
4984 lineNode
= lineNode
->GetNext();
4992 /// Apply paragraph styles, such as centering, to wrapped lines
4993 /// TODO: take into account box attributes, possibly
4994 void wxRichTextParagraph::ApplyParagraphStyle(wxRichTextLine
* line
, const wxRichTextAttr
& attr
, const wxRect
& rect
, wxDC
& dc
)
4996 if (!attr
.HasAlignment())
4999 wxPoint pos
= line
->GetPosition();
5000 wxSize size
= line
->GetSize();
5002 // centering, right-justification
5003 if (attr
.HasAlignment() && GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_CENTRE
)
5005 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
5006 pos
.x
= (rect
.GetWidth() - rightIndent
- size
.x
)/2 + pos
.x
;
5007 line
->SetPosition(pos
);
5009 else if (attr
.HasAlignment() && GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_RIGHT
)
5011 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
5012 pos
.x
= pos
.x
+ rect
.GetWidth() - size
.x
- rightIndent
;
5013 line
->SetPosition(pos
);
5017 /// Insert text at the given position
5018 bool wxRichTextParagraph::InsertText(long pos
, const wxString
& text
)
5020 wxRichTextObject
* childToUse
= NULL
;
5021 wxRichTextObjectList::compatibility_iterator nodeToUse
= wxRichTextObjectList::compatibility_iterator();
5023 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5026 wxRichTextObject
* child
= node
->GetData();
5027 if (child
->GetRange().Contains(pos
) && child
->GetRange().GetLength() > 0)
5034 node
= node
->GetNext();
5039 wxRichTextPlainText
* textObject
= wxDynamicCast(childToUse
, wxRichTextPlainText
);
5042 int posInString
= pos
- textObject
->GetRange().GetStart();
5044 wxString newText
= textObject
->GetText().Mid(0, posInString
) +
5045 text
+ textObject
->GetText().Mid(posInString
);
5046 textObject
->SetText(newText
);
5048 int textLength
= text
.length();
5050 textObject
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart(),
5051 textObject
->GetRange().GetEnd() + textLength
));
5053 // Increment the end range of subsequent fragments in this paragraph.
5054 // We'll set the paragraph range itself at a higher level.
5056 wxRichTextObjectList::compatibility_iterator node
= nodeToUse
->GetNext();
5059 wxRichTextObject
* child
= node
->GetData();
5060 child
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart() + textLength
,
5061 textObject
->GetRange().GetEnd() + textLength
));
5063 node
= node
->GetNext();
5070 // TODO: if not a text object, insert at closest position, e.g. in front of it
5076 // Don't pass parent initially to suppress auto-setting of parent range.
5077 // We'll do that at a higher level.
5078 wxRichTextPlainText
* textObject
= new wxRichTextPlainText(text
, this);
5080 AppendChild(textObject
);
5087 void wxRichTextParagraph::Copy(const wxRichTextParagraph
& obj
)
5089 wxRichTextCompositeObject::Copy(obj
);
5092 /// Clear the cached lines
5093 void wxRichTextParagraph::ClearLines()
5095 WX_CLEAR_LIST(wxRichTextLineList
, m_cachedLines
);
5098 /// Get/set the object size for the given range. Returns false if the range
5099 /// is invalid for this object.
5100 bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
5102 if (!range
.IsWithin(GetRange()))
5105 if (flags
& wxRICHTEXT_UNFORMATTED
)
5107 // Just use unformatted data, assume no line breaks
5108 // TODO: take into account line breaks
5112 wxArrayInt childExtents
;
5119 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5123 wxRichTextObject
* child
= node
->GetData();
5124 if (!child
->GetRange().IsOutside(range
))
5126 // Floating objects have a zero size within the paragraph.
5127 if (child
->IsFloating())
5132 if (partialExtents
->GetCount() > 0)
5133 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5137 partialExtents
->Add(0 /* zero size */ + lastSize
);
5144 wxRichTextRange rangeToUse
= range
;
5145 rangeToUse
.LimitTo(child
->GetRange());
5147 if (child
->IsTopLevel())
5148 rangeToUse
= child
->GetOwnRange();
5150 int childDescent
= 0;
5152 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we're already cached the size,
5153 // but it's only going to be used after caching has taken place.
5154 if ((flags
& wxRICHTEXT_HEIGHT_ONLY
) && child
->GetCachedSize().y
!= 0)
5156 childDescent
= child
->GetDescent();
5157 childSize
= child
->GetCachedSize();
5159 sz
.y
= wxMax(sz
.y
, childSize
.y
);
5160 sz
.x
+= childSize
.x
;
5161 descent
= wxMax(descent
, childDescent
);
5163 else if (child
->IsTopLevel())
5165 childDescent
= child
->GetDescent();
5166 childSize
= child
->GetCachedSize();
5168 sz
.y
= wxMax(sz
.y
, childSize
.y
);
5169 sz
.x
+= childSize
.x
;
5170 descent
= wxMax(descent
, childDescent
);
5171 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange()))
5173 child
->SetCachedSize(childSize
);
5174 child
->SetDescent(childDescent
);
5180 if (partialExtents
->GetCount() > 0)
5181 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5185 partialExtents
->Add(childSize
.x
+ lastSize
);
5188 else if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
), p
))
5190 sz
.y
= wxMax(sz
.y
, childSize
.y
);
5191 sz
.x
+= childSize
.x
;
5192 descent
= wxMax(descent
, childDescent
);
5194 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange()))
5196 child
->SetCachedSize(childSize
);
5197 child
->SetDescent(childDescent
);
5203 if (partialExtents
->GetCount() > 0)
5204 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5209 for (i
= 0; i
< childExtents
.GetCount(); i
++)
5211 partialExtents
->Add(childExtents
[i
] + lastSize
);
5221 node
= node
->GetNext();
5227 // Use formatted data, with line breaks
5230 // We're going to loop through each line, and then for each line,
5231 // call GetRangeSize for the fragment that comprises that line.
5232 // Only we have to do that multiple times within the line, because
5233 // the line may be broken into pieces. For now ignore line break commands
5234 // (so we can assume that getting the unformatted size for a fragment
5235 // within a line is the actual size)
5237 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5240 wxRichTextLine
* line
= node
->GetData();
5241 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5242 if (!lineRange
.IsOutside(range
))
5246 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
5249 wxRichTextObject
* child
= node2
->GetData();
5251 if (!child
->IsFloating() && !child
->GetRange().IsOutside(lineRange
))
5253 wxRichTextRange rangeToUse
= lineRange
;
5254 rangeToUse
.LimitTo(child
->GetRange());
5255 if (child
->IsTopLevel())
5256 rangeToUse
= child
->GetOwnRange();
5259 int childDescent
= 0;
5260 if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
)))
5262 lineSize
.y
= wxMax(lineSize
.y
, childSize
.y
);
5263 lineSize
.x
+= childSize
.x
;
5265 descent
= wxMax(descent
, childDescent
);
5268 node2
= node2
->GetNext();
5271 // Increase size by a line (TODO: paragraph spacing)
5273 sz
.x
= wxMax(sz
.x
, lineSize
.x
);
5275 node
= node
->GetNext();
5282 /// Finds the absolute position and row height for the given character position
5283 bool wxRichTextParagraph::FindPosition(wxDC
& dc
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
5287 wxRichTextLine
* line
= ((wxRichTextParagraphLayoutBox
*)GetParent())->GetLineAtPosition(0);
5289 *height
= line
->GetSize().y
;
5291 *height
= dc
.GetCharHeight();
5293 // -1 means 'the start of the buffer'.
5296 pt
= pt
+ line
->GetPosition();
5301 // The final position in a paragraph is taken to mean the position
5302 // at the start of the next paragraph.
5303 if (index
== GetRange().GetEnd())
5305 wxRichTextParagraphLayoutBox
* parent
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
5306 wxASSERT( parent
!= NULL
);
5308 // Find the height at the next paragraph, if any
5309 wxRichTextLine
* line
= parent
->GetLineAtPosition(index
+ 1);
5312 *height
= line
->GetSize().y
;
5313 pt
= line
->GetAbsolutePosition();
5317 *height
= dc
.GetCharHeight();
5318 int indent
= ConvertTenthsMMToPixels(dc
, m_attributes
.GetLeftIndent());
5319 pt
= wxPoint(indent
, GetCachedSize().y
);
5325 if (index
< GetRange().GetStart() || index
> GetRange().GetEnd())
5328 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5331 wxRichTextLine
* line
= node
->GetData();
5332 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5333 if (index
>= lineRange
.GetStart() && index
<= lineRange
.GetEnd())
5335 // If this is the last point in the line, and we're forcing the
5336 // returned value to be the start of the next line, do the required
5338 if (index
== lineRange
.GetEnd() && forceLineStart
)
5340 if (node
->GetNext())
5342 wxRichTextLine
* nextLine
= node
->GetNext()->GetData();
5343 *height
= nextLine
->GetSize().y
;
5344 pt
= nextLine
->GetAbsolutePosition();
5349 pt
.y
= line
->GetPosition().y
+ GetPosition().y
;
5351 wxRichTextRange
r(lineRange
.GetStart(), index
);
5355 // We find the size of the line up to this point,
5356 // then we can add this size to the line start position and
5357 // paragraph start position to find the actual position.
5359 if (GetRangeSize(r
, rangeSize
, descent
, dc
, wxRICHTEXT_UNFORMATTED
, line
->GetPosition()+ GetPosition()))
5361 pt
.x
= line
->GetPosition().x
+ GetPosition().x
+ rangeSize
.x
;
5362 *height
= line
->GetSize().y
;
5369 node
= node
->GetNext();
5375 /// Hit-testing: returns a flag indicating hit test details, plus
5376 /// information about position
5377 int wxRichTextParagraph::HitTest(wxDC
& dc
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
5380 return wxRICHTEXT_HITTEST_NONE
;
5382 // If we're in the top-level container, then we can return
5383 // a suitable hit test code even if the point is outside the container area,
5384 // so that we can position the caret sensibly even if we don't
5385 // click on valid content. If we're not at the top-level, and the point
5386 // is not within this paragraph object, then we don't want to stop more
5387 // precise hit-testing from working prematurely, so return immediately.
5388 // NEW STRATEGY: use the parent boundary to test whether we're in the
5389 // right region, not the paragraph, since the paragraph may be positioned
5390 // some way in from where the user clicks.
5393 wxRichTextObject
* tempObj
, *tempContextObj
;
5394 if (GetParent() && GetParent()->wxRichTextObject::HitTest(dc
, pt
, tmpPos
, & tempObj
, & tempContextObj
, flags
) == wxRICHTEXT_HITTEST_NONE
)
5395 return wxRICHTEXT_HITTEST_NONE
;
5398 wxRichTextObjectList::compatibility_iterator objNode
= m_children
.GetFirst();
5401 wxRichTextObject
* child
= objNode
->GetData();
5402 if (child
->IsTopLevel() && ((flags
& wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS
) == 0))
5405 int hitTest
= child
->HitTest(dc
, pt
, textPosition
, obj
, contextObj
);
5406 if (hitTest
!= wxRICHTEXT_HITTEST_NONE
)
5411 objNode
= objNode
->GetNext();
5414 wxPoint paraPos
= GetPosition();
5416 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5419 wxRichTextLine
* line
= node
->GetData();
5420 wxPoint linePos
= paraPos
+ line
->GetPosition();
5421 wxSize lineSize
= line
->GetSize();
5422 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5424 if (pt
.y
<= linePos
.y
+ lineSize
.y
)
5426 if (pt
.x
< linePos
.x
)
5428 textPosition
= lineRange
.GetStart();
5429 *obj
= FindObjectAtPosition(textPosition
);
5430 *contextObj
= GetContainer();
5431 return wxRICHTEXT_HITTEST_BEFORE
|wxRICHTEXT_HITTEST_OUTSIDE
;
5433 else if (pt
.x
>= (linePos
.x
+ lineSize
.x
))
5435 textPosition
= lineRange
.GetEnd();
5436 *obj
= FindObjectAtPosition(textPosition
);
5437 *contextObj
= GetContainer();
5438 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
;
5442 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5443 wxArrayInt partialExtents
;
5448 // This calculates the partial text extents
5449 GetRangeSize(lineRange
, paraSize
, paraDescent
, dc
, wxRICHTEXT_UNFORMATTED
, linePos
, & partialExtents
);
5451 int lastX
= linePos
.x
;
5453 for (i
= 0; i
< partialExtents
.GetCount(); i
++)
5455 int nextX
= partialExtents
[i
] + linePos
.x
;
5457 if (pt
.x
>= lastX
&& pt
.x
<= nextX
)
5459 textPosition
= i
+ lineRange
.GetStart(); // minus 1?
5461 *obj
= FindObjectAtPosition(textPosition
);
5462 *contextObj
= GetContainer();
5464 // So now we know it's between i-1 and i.
5465 // Let's see if we can be more precise about
5466 // which side of the position it's on.
5468 int midPoint
= (nextX
+ lastX
)/2;
5469 if (pt
.x
>= midPoint
)
5470 return wxRICHTEXT_HITTEST_AFTER
;
5472 return wxRICHTEXT_HITTEST_BEFORE
;
5479 int lastX
= linePos
.x
;
5480 for (i
= lineRange
.GetStart(); i
<= lineRange
.GetEnd(); i
++)
5485 wxRichTextRange
rangeToUse(lineRange
.GetStart(), i
);
5487 GetRangeSize(rangeToUse
, childSize
, descent
, dc
, wxRICHTEXT_UNFORMATTED
, linePos
);
5489 int nextX
= childSize
.x
+ linePos
.x
;
5491 if (pt
.x
>= lastX
&& pt
.x
<= nextX
)
5495 *obj
= FindObjectAtPosition(textPosition
);
5496 *contextObj
= GetContainer();
5498 // So now we know it's between i-1 and i.
5499 // Let's see if we can be more precise about
5500 // which side of the position it's on.
5502 int midPoint
= (nextX
+ lastX
)/2;
5503 if (pt
.x
>= midPoint
)
5504 return wxRICHTEXT_HITTEST_AFTER
;
5506 return wxRICHTEXT_HITTEST_BEFORE
;
5517 node
= node
->GetNext();
5520 return wxRICHTEXT_HITTEST_NONE
;
5523 /// Split an object at this position if necessary, and return
5524 /// the previous object, or NULL if inserting at beginning.
5525 wxRichTextObject
* wxRichTextParagraph::SplitAt(long pos
, wxRichTextObject
** previousObject
)
5527 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5530 wxRichTextObject
* child
= node
->GetData();
5532 if (pos
== child
->GetRange().GetStart())
5536 if (node
->GetPrevious())
5537 *previousObject
= node
->GetPrevious()->GetData();
5539 *previousObject
= NULL
;
5545 if (child
->GetRange().Contains(pos
))
5547 // This should create a new object, transferring part of
5548 // the content to the old object and the rest to the new object.
5549 wxRichTextObject
* newObject
= child
->DoSplit(pos
);
5551 // If we couldn't split this object, just insert in front of it.
5554 // Maybe this is an empty string, try the next one
5559 // Insert the new object after 'child'
5560 if (node
->GetNext())
5561 m_children
.Insert(node
->GetNext(), newObject
);
5563 m_children
.Append(newObject
);
5564 newObject
->SetParent(this);
5567 *previousObject
= child
;
5573 node
= node
->GetNext();
5576 *previousObject
= NULL
;
5580 /// Move content to a list from obj on
5581 void wxRichTextParagraph::MoveToList(wxRichTextObject
* obj
, wxList
& list
)
5583 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(obj
);
5586 wxRichTextObject
* child
= node
->GetData();
5589 wxRichTextObjectList::compatibility_iterator oldNode
= node
;
5591 node
= node
->GetNext();
5593 m_children
.DeleteNode(oldNode
);
5597 /// Add content back from list
5598 void wxRichTextParagraph::MoveFromList(wxList
& list
)
5600 for (wxList::compatibility_iterator node
= list
.GetFirst(); node
; node
= node
->GetNext())
5602 AppendChild((wxRichTextObject
*) node
->GetData());
5607 void wxRichTextParagraph::CalculateRange(long start
, long& end
)
5609 wxRichTextCompositeObject::CalculateRange(start
, end
);
5611 // Add one for end of paragraph
5614 m_range
.SetRange(start
, end
);
5617 /// Find the object at the given position
5618 wxRichTextObject
* wxRichTextParagraph::FindObjectAtPosition(long position
)
5620 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5623 wxRichTextObject
* obj
= node
->GetData();
5624 if (obj
->GetRange().Contains(position
) ||
5625 obj
->GetRange().GetStart() == position
||
5626 obj
->GetRange().GetEnd() == position
)
5629 node
= node
->GetNext();
5634 /// Get the plain text searching from the start or end of the range.
5635 /// The resulting string may be shorter than the range given.
5636 bool wxRichTextParagraph::GetContiguousPlainText(wxString
& text
, const wxRichTextRange
& range
, bool fromStart
)
5638 text
= wxEmptyString
;
5642 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5645 wxRichTextObject
* obj
= node
->GetData();
5646 if (!obj
->GetRange().IsOutside(range
))
5648 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5651 text
+= textObj
->GetTextForRange(range
);
5659 node
= node
->GetNext();
5664 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetLast();
5667 wxRichTextObject
* obj
= node
->GetData();
5668 if (!obj
->GetRange().IsOutside(range
))
5670 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5673 text
= textObj
->GetTextForRange(range
) + text
;
5677 text
= wxT(" ") + text
;
5681 node
= node
->GetPrevious();
5688 /// Find a suitable wrap position.
5689 bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange
& range
, wxDC
& dc
, int availableSpace
, long& wrapPosition
, wxArrayInt
* partialExtents
)
5691 if (range
.GetLength() <= 0)
5694 // Find the first position where the line exceeds the available space.
5696 long breakPosition
= range
.GetEnd();
5698 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5699 if (partialExtents
&& partialExtents
->GetCount() >= (size_t) (GetRange().GetLength()-1)) // the final position in a paragraph is the newline
5703 if (range
.GetStart() > GetRange().GetStart())
5704 widthBefore
= (*partialExtents
)[range
.GetStart() - GetRange().GetStart() - 1];
5709 for (i
= (size_t) range
.GetStart(); i
<= (size_t) range
.GetEnd(); i
++)
5711 int widthFromStartOfThisRange
= (*partialExtents
)[i
- GetRange().GetStart()] - widthBefore
;
5713 if (widthFromStartOfThisRange
> availableSpace
)
5715 breakPosition
= i
-1;
5723 // Binary chop for speed
5724 long minPos
= range
.GetStart();
5725 long maxPos
= range
.GetEnd();
5728 if (minPos
== maxPos
)
5731 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, wxRICHTEXT_UNFORMATTED
);
5733 if (sz
.x
> availableSpace
)
5734 breakPosition
= minPos
- 1;
5737 else if ((maxPos
- minPos
) == 1)
5740 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, wxRICHTEXT_UNFORMATTED
);
5742 if (sz
.x
> availableSpace
)
5743 breakPosition
= minPos
- 1;
5746 GetRangeSize(wxRichTextRange(range
.GetStart(), maxPos
), sz
, descent
, dc
, wxRICHTEXT_UNFORMATTED
);
5747 if (sz
.x
> availableSpace
)
5748 breakPosition
= maxPos
-1;
5754 long nextPos
= minPos
+ ((maxPos
- minPos
) / 2);
5757 GetRangeSize(wxRichTextRange(range
.GetStart(), nextPos
), sz
, descent
, dc
, wxRICHTEXT_UNFORMATTED
);
5759 if (sz
.x
> availableSpace
)
5771 // Now we know the last position on the line.
5772 // Let's try to find a word break.
5775 if (GetContiguousPlainText(plainText
, wxRichTextRange(range
.GetStart(), breakPosition
), false))
5777 int newLinePos
= plainText
.Find(wxRichTextLineBreakChar
);
5778 if (newLinePos
!= wxNOT_FOUND
)
5780 breakPosition
= wxMax(0, range
.GetStart() + newLinePos
);
5784 int spacePos
= plainText
.Find(wxT(' '), true);
5785 int tabPos
= plainText
.Find(wxT('\t'), true);
5786 int pos
= wxMax(spacePos
, tabPos
);
5787 if (pos
!= wxNOT_FOUND
)
5789 int positionsFromEndOfString
= plainText
.length() - pos
- 1;
5790 breakPosition
= breakPosition
- positionsFromEndOfString
;
5795 wrapPosition
= breakPosition
;
5800 /// Get the bullet text for this paragraph.
5801 wxString
wxRichTextParagraph::GetBulletText()
5803 if (GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
||
5804 (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
))
5805 return wxEmptyString
;
5807 int number
= GetAttributes().GetBulletNumber();
5810 if ((GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ARABIC
) || (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
))
5812 text
.Printf(wxT("%d"), number
);
5814 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_UPPER
)
5816 // TODO: Unicode, and also check if number > 26
5817 text
.Printf(wxT("%c"), (wxChar
) (number
+64));
5819 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_LOWER
)
5821 // TODO: Unicode, and also check if number > 26
5822 text
.Printf(wxT("%c"), (wxChar
) (number
+96));
5824 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER
)
5826 text
= wxRichTextDecimalToRoman(number
);
5828 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER
)
5830 text
= wxRichTextDecimalToRoman(number
);
5833 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
)
5835 text
= GetAttributes().GetBulletText();
5838 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
)
5840 // The outline style relies on the text being computed statically,
5841 // since it depends on other levels points (e.g. 1.2.1.1). So normally the bullet text
5842 // should be stored in the attributes; if not, just use the number for this
5843 // level, as previously computed.
5844 if (!GetAttributes().GetBulletText().IsEmpty())
5845 text
= GetAttributes().GetBulletText();
5848 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PARENTHESES
)
5850 text
= wxT("(") + text
+ wxT(")");
5852 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_RIGHT_PARENTHESIS
)
5854 text
= text
+ wxT(")");
5857 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PERIOD
)
5865 /// Allocate or reuse a line object
5866 wxRichTextLine
* wxRichTextParagraph::AllocateLine(int pos
)
5868 if (pos
< (int) m_cachedLines
.GetCount())
5870 wxRichTextLine
* line
= m_cachedLines
.Item(pos
)->GetData();
5876 wxRichTextLine
* line
= new wxRichTextLine(this);
5877 m_cachedLines
.Append(line
);
5882 /// Clear remaining unused line objects, if any
5883 bool wxRichTextParagraph::ClearUnusedLines(int lineCount
)
5885 int cachedLineCount
= m_cachedLines
.GetCount();
5886 if ((int) cachedLineCount
> lineCount
)
5888 for (int i
= 0; i
< (int) (cachedLineCount
- lineCount
); i
++)
5890 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetLast();
5891 wxRichTextLine
* line
= node
->GetData();
5892 m_cachedLines
.Erase(node
);
5899 /// Get combined attributes of the base style, paragraph style and character style. We use this to dynamically
5900 /// retrieve the actual style.
5901 wxRichTextAttr
wxRichTextParagraph::GetCombinedAttributes(const wxRichTextAttr
& contentStyle
, bool includingBoxAttr
) const
5903 wxRichTextAttr attr
;
5904 wxRichTextParagraphLayoutBox
* buf
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
5907 attr
= buf
->GetBasicStyle();
5908 if (!includingBoxAttr
)
5910 attr
.GetTextBoxAttr().Reset();
5911 // The background colour will be painted by the container, and we don't
5912 // want to unnecessarily overwrite the background when we're drawing text
5913 // because this may erase the guideline (which appears just under the text
5914 // if there's no padding).
5915 attr
.SetFlags(attr
.GetFlags() & ~wxTEXT_ATTR_BACKGROUND_COLOUR
);
5917 wxRichTextApplyStyle(attr
, GetAttributes());
5920 attr
= GetAttributes();
5922 wxRichTextApplyStyle(attr
, contentStyle
);
5926 /// Get combined attributes of the base style and paragraph style.
5927 wxRichTextAttr
wxRichTextParagraph::GetCombinedAttributes(bool includingBoxAttr
) const
5929 wxRichTextAttr attr
;
5930 wxRichTextParagraphLayoutBox
* buf
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
5933 attr
= buf
->GetBasicStyle();
5934 if (!includingBoxAttr
)
5935 attr
.GetTextBoxAttr().Reset();
5936 wxRichTextApplyStyle(attr
, GetAttributes());
5939 attr
= GetAttributes();
5944 // Create default tabstop array
5945 void wxRichTextParagraph::InitDefaultTabs()
5947 // create a default tab list at 10 mm each.
5948 for (int i
= 0; i
< 20; ++i
)
5950 sm_defaultTabs
.Add(i
*100);
5954 // Clear default tabstop array
5955 void wxRichTextParagraph::ClearDefaultTabs()
5957 sm_defaultTabs
.Clear();
5960 void wxRichTextParagraph::LayoutFloat(wxDC
& dc
, const wxRect
& rect
, int style
, wxRichTextFloatCollector
* floatCollector
)
5962 wxRichTextObjectList::compatibility_iterator node
= GetChildren().GetFirst();
5965 wxRichTextObject
* anchored
= node
->GetData();
5966 if (anchored
&& anchored
->IsFloating() && !floatCollector
->HasFloat(anchored
))
5970 anchored
->GetRangeSize(anchored
->GetRange(), size
, descent
, dc
, style
);
5973 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().IsValid())
5975 offsetY
= anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetValue();
5976 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
5978 offsetY
= ConvertTenthsMMToPixels(dc
, offsetY
);
5982 int pos
= floatCollector
->GetFitPosition(anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode(), rect
.y
+ offsetY
, size
.y
);
5984 /* Update the offset */
5985 int newOffsetY
= pos
- rect
.y
;
5986 if (newOffsetY
!= offsetY
)
5988 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
5989 newOffsetY
= ConvertPixelsToTenthsMM(dc
, newOffsetY
);
5990 anchored
->GetAttributes().GetTextBoxAttr().GetTop().SetValue(newOffsetY
);
5993 if (anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT
)
5995 else if (anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT
)
5996 x
= rect
.x
+ rect
.width
- size
.x
;
5998 anchored
->SetPosition(wxPoint(x
, pos
));
5999 anchored
->SetCachedSize(size
);
6000 floatCollector
->CollectFloat(this, anchored
);
6003 node
= node
->GetNext();
6007 // Get the first position from pos that has a line break character.
6008 long wxRichTextParagraph::GetFirstLineBreakPosition(long pos
)
6010 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
6013 wxRichTextObject
* obj
= node
->GetData();
6014 if (pos
>= obj
->GetRange().GetStart() && pos
<= obj
->GetRange().GetEnd())
6016 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
6019 long breakPos
= textObj
->GetFirstLineBreakPosition(pos
);
6024 node
= node
->GetNext();
6031 * This object represents a line in a paragraph, and stores
6032 * offsets from the start of the paragraph representing the
6033 * start and end positions of the line.
6036 wxRichTextLine::wxRichTextLine(wxRichTextParagraph
* parent
)
6042 void wxRichTextLine::Init(wxRichTextParagraph
* parent
)
6045 m_range
.SetRange(-1, -1);
6046 m_pos
= wxPoint(0, 0);
6047 m_size
= wxSize(0, 0);
6049 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6050 m_objectSizes
.Clear();
6055 void wxRichTextLine::Copy(const wxRichTextLine
& obj
)
6057 m_range
= obj
.m_range
;
6058 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6059 m_objectSizes
= obj
.m_objectSizes
;
6063 /// Get the absolute object position
6064 wxPoint
wxRichTextLine::GetAbsolutePosition() const
6066 return m_parent
->GetPosition() + m_pos
;
6069 /// Get the absolute range
6070 wxRichTextRange
wxRichTextLine::GetAbsoluteRange() const
6072 wxRichTextRange
range(m_range
.GetStart() + m_parent
->GetRange().GetStart(), 0);
6073 range
.SetEnd(range
.GetStart() + m_range
.GetLength()-1);
6078 * wxRichTextPlainText
6079 * This object represents a single piece of text.
6082 IMPLEMENT_DYNAMIC_CLASS(wxRichTextPlainText
, wxRichTextObject
)
6084 wxRichTextPlainText::wxRichTextPlainText(const wxString
& text
, wxRichTextObject
* parent
, wxRichTextAttr
* style
):
6085 wxRichTextObject(parent
)
6088 SetAttributes(*style
);
6093 #define USE_KERNING_FIX 1
6095 // If insufficient tabs are defined, this is the tab width used
6096 #define WIDTH_FOR_DEFAULT_TABS 50
6099 bool wxRichTextPlainText::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int WXUNUSED(style
))
6101 wxRichTextParagraph
* para
= wxDynamicCast(GetParent(), wxRichTextParagraph
);
6102 wxASSERT (para
!= NULL
);
6104 wxRichTextAttr
textAttr(para
? para
->GetCombinedAttributes(GetAttributes(), false /* no box attributes */) : GetAttributes());
6106 // Let's make the assumption for now that for content in a paragraph, including
6107 // text, we never have a discontinuous selection. So we only deal with a
6109 wxRichTextRange selectionRange
;
6110 if (selection
.IsValid())
6112 wxRichTextRangeArray selectionRanges
= selection
.GetSelectionForObject(this);
6113 if (selectionRanges
.GetCount() > 0)
6114 selectionRange
= selectionRanges
[0];
6116 selectionRange
= wxRICHTEXT_NO_SELECTION
;
6119 selectionRange
= wxRICHTEXT_NO_SELECTION
;
6121 int offset
= GetRange().GetStart();
6123 // Replace line break characters with spaces
6124 wxString str
= m_text
;
6125 wxString toRemove
= wxRichTextLineBreakChar
;
6126 str
.Replace(toRemove
, wxT(" "));
6127 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS
))
6130 long len
= range
.GetLength();
6131 wxString stringChunk
= str
.Mid(range
.GetStart() - offset
, (size_t) len
);
6133 // Test for the optimized situations where all is selected, or none
6136 wxFont
textFont(GetBuffer()->GetFontTable().FindFont(textAttr
));
6137 wxCheckSetFont(dc
, textFont
);
6138 int charHeight
= dc
.GetCharHeight();
6141 if ( textFont
.IsOk() )
6143 if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
) )
6145 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6146 textFont
.SetPointSize( static_cast<int>(size
) );
6149 wxCheckSetFont(dc
, textFont
);
6151 else if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) )
6153 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6154 textFont
.SetPointSize( static_cast<int>(size
) );
6156 int sub_height
= static_cast<int>( static_cast<double>(charHeight
) / wxSCRIPT_MUL_FACTOR
);
6157 y
= rect
.y
+ (rect
.height
- sub_height
+ (descent
- m_descent
));
6158 wxCheckSetFont(dc
, textFont
);
6163 y
= rect
.y
+ (rect
.height
- charHeight
- (descent
- m_descent
));
6169 y
= rect
.y
+ (rect
.height
- charHeight
- (descent
- m_descent
));
6172 // TODO: new selection code
6174 // (a) All selected.
6175 if (selectionRange
.GetStart() <= range
.GetStart() && selectionRange
.GetEnd() >= range
.GetEnd())
6177 DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, true);
6179 // (b) None selected.
6180 else if (selectionRange
.GetEnd() < range
.GetStart() || selectionRange
.GetStart() > range
.GetEnd())
6182 // Draw all unselected
6183 DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, false);
6187 // (c) Part selected, part not
6188 // Let's draw unselected chunk, selected chunk, then unselected chunk.
6190 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6192 // 1. Initial unselected chunk, if any, up until start of selection.
6193 if (selectionRange
.GetStart() > range
.GetStart() && selectionRange
.GetStart() <= range
.GetEnd())
6195 int r1
= range
.GetStart();
6196 int s1
= selectionRange
.GetStart()-1;
6197 int fragmentLen
= s1
- r1
+ 1;
6198 if (fragmentLen
< 0)
6200 wxLogDebug(wxT("Mid(%d, %d"), (int)(r1
- offset
), (int)fragmentLen
);
6202 wxString stringFragment
= str
.Mid(r1
- offset
, fragmentLen
);
6204 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false);
6207 if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
)
6209 // Compensate for kerning difference
6210 wxString
stringFragment2(str
.Mid(r1
- offset
, fragmentLen
+1));
6211 wxString
stringFragment3(str
.Mid(r1
- offset
+ fragmentLen
, 1));
6213 wxCoord w1
, h1
, w2
, h2
, w3
, h3
;
6214 dc
.GetTextExtent(stringFragment
, & w1
, & h1
);
6215 dc
.GetTextExtent(stringFragment2
, & w2
, & h2
);
6216 dc
.GetTextExtent(stringFragment3
, & w3
, & h3
);
6218 int kerningDiff
= (w1
+ w3
) - w2
;
6219 x
= x
- kerningDiff
;
6224 // 2. Selected chunk, if any.
6225 if (selectionRange
.GetEnd() >= range
.GetStart())
6227 int s1
= wxMax(selectionRange
.GetStart(), range
.GetStart());
6228 int s2
= wxMin(selectionRange
.GetEnd(), range
.GetEnd());
6230 int fragmentLen
= s2
- s1
+ 1;
6231 if (fragmentLen
< 0)
6233 wxLogDebug(wxT("Mid(%d, %d"), (int)(s1
- offset
), (int)fragmentLen
);
6235 wxString stringFragment
= str
.Mid(s1
- offset
, fragmentLen
);
6237 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, true);
6240 if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
)
6242 // Compensate for kerning difference
6243 wxString
stringFragment2(str
.Mid(s1
- offset
, fragmentLen
+1));
6244 wxString
stringFragment3(str
.Mid(s1
- offset
+ fragmentLen
, 1));
6246 wxCoord w1
, h1
, w2
, h2
, w3
, h3
;
6247 dc
.GetTextExtent(stringFragment
, & w1
, & h1
);
6248 dc
.GetTextExtent(stringFragment2
, & w2
, & h2
);
6249 dc
.GetTextExtent(stringFragment3
, & w3
, & h3
);
6251 int kerningDiff
= (w1
+ w3
) - w2
;
6252 x
= x
- kerningDiff
;
6257 // 3. Remaining unselected chunk, if any
6258 if (selectionRange
.GetEnd() < range
.GetEnd())
6260 int s2
= wxMin(selectionRange
.GetEnd()+1, range
.GetEnd());
6261 int r2
= range
.GetEnd();
6263 int fragmentLen
= r2
- s2
+ 1;
6264 if (fragmentLen
< 0)
6266 wxLogDebug(wxT("Mid(%d, %d"), (int)(s2
- offset
), (int)fragmentLen
);
6268 wxString stringFragment
= str
.Mid(s2
- offset
, fragmentLen
);
6270 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false);
6277 bool wxRichTextPlainText::DrawTabbedString(wxDC
& dc
, const wxRichTextAttr
& attr
, const wxRect
& rect
,wxString
& str
, wxCoord
& x
, wxCoord
& y
, bool selected
)
6279 bool hasTabs
= (str
.Find(wxT('\t')) != wxNOT_FOUND
);
6281 wxArrayInt tabArray
;
6285 if (attr
.GetTabs().IsEmpty())
6286 tabArray
= wxRichTextParagraph::GetDefaultTabs();
6288 tabArray
= attr
.GetTabs();
6289 tabCount
= tabArray
.GetCount();
6291 for (int i
= 0; i
< tabCount
; ++i
)
6293 int pos
= tabArray
[i
];
6294 pos
= ConvertTenthsMMToPixels(dc
, pos
);
6301 int nextTabPos
= -1;
6307 wxColour
highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
));
6308 wxColour
highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
));
6310 wxCheckSetBrush(dc
, wxBrush(highlightColour
));
6311 wxCheckSetPen(dc
, wxPen(highlightColour
));
6312 dc
.SetTextForeground(highlightTextColour
);
6313 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6317 dc
.SetTextForeground(attr
.GetTextColour());
6319 if (attr
.HasFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
) && attr
.GetBackgroundColour().IsOk())
6321 dc
.SetBackgroundMode(wxBRUSHSTYLE_SOLID
);
6322 dc
.SetTextBackground(attr
.GetBackgroundColour());
6325 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6328 wxCoord x_orig
= GetParent()->GetPosition().x
;
6331 // the string has a tab
6332 // break up the string at the Tab
6333 wxString stringChunk
= str
.BeforeFirst(wxT('\t'));
6334 str
= str
.AfterFirst(wxT('\t'));
6335 dc
.GetTextExtent(stringChunk
, & w
, & h
);
6337 bool not_found
= true;
6338 for (int i
= 0; i
< tabCount
&& not_found
; ++i
)
6340 nextTabPos
= tabArray
.Item(i
) + x_orig
;
6342 // Find the next tab position.
6343 // Even if we're at the end of the tab array, we must still draw the chunk.
6345 if (nextTabPos
> tabPos
|| (i
== (tabCount
- 1)))
6347 if (nextTabPos
<= tabPos
)
6349 int defaultTabWidth
= ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
);
6350 nextTabPos
= tabPos
+ defaultTabWidth
;
6357 wxRect
selRect(x
, rect
.y
, w
, rect
.GetHeight());
6358 dc
.DrawRectangle(selRect
);
6360 dc
.DrawText(stringChunk
, x
, y
);
6362 if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
))
6364 wxPen oldPen
= dc
.GetPen();
6365 wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1));
6366 dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5));
6367 wxCheckSetPen(dc
, oldPen
);
6373 hasTabs
= (str
.Find(wxT('\t')) != wxNOT_FOUND
);
6378 dc
.GetTextExtent(str
, & w
, & h
);
6381 wxRect
selRect(x
, rect
.y
, w
, rect
.GetHeight());
6382 dc
.DrawRectangle(selRect
);
6384 dc
.DrawText(str
, x
, y
);
6386 if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
))
6388 wxPen oldPen
= dc
.GetPen();
6389 wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1));
6390 dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5));
6391 wxCheckSetPen(dc
, oldPen
);
6400 /// Lay the item out
6401 bool wxRichTextPlainText::Layout(wxDC
& dc
, const wxRect
& WXUNUSED(rect
), const wxRect
& WXUNUSED(parentRect
), int WXUNUSED(style
))
6403 // Only lay out if we haven't already cached the size
6405 GetRangeSize(GetRange(), m_size
, m_descent
, dc
, 0, wxPoint(0, 0));
6407 // Eventually we want to have a reasonable estimate of minimum size.
6408 m_minSize
= wxSize(0, 0);
6413 void wxRichTextPlainText::Copy(const wxRichTextPlainText
& obj
)
6415 wxRichTextObject::Copy(obj
);
6417 m_text
= obj
.m_text
;
6420 /// Get/set the object size for the given range. Returns false if the range
6421 /// is invalid for this object.
6422 bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, int WXUNUSED(flags
), wxPoint position
, wxArrayInt
* partialExtents
) const
6424 if (!range
.IsWithin(GetRange()))
6427 wxRichTextParagraph
* para
= wxDynamicCast(GetParent(), wxRichTextParagraph
);
6428 wxASSERT (para
!= NULL
);
6430 int relativeX
= position
.x
- GetParent()->GetPosition().x
;
6432 wxRichTextAttr
textAttr(para
? para
->GetCombinedAttributes(GetAttributes()) : GetAttributes());
6434 // Always assume unformatted text, since at this level we have no knowledge
6435 // of line breaks - and we don't need it, since we'll calculate size within
6436 // formatted text by doing it in chunks according to the line ranges
6438 bool bScript(false);
6439 wxFont
font(GetBuffer()->GetFontTable().FindFont(textAttr
));
6442 if ( textAttr
.HasTextEffects() && ( (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
)
6443 || (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) ) )
6445 wxFont textFont
= font
;
6446 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6447 textFont
.SetPointSize( static_cast<int>(size
) );
6448 wxCheckSetFont(dc
, textFont
);
6453 wxCheckSetFont(dc
, font
);
6457 bool haveDescent
= false;
6458 int startPos
= range
.GetStart() - GetRange().GetStart();
6459 long len
= range
.GetLength();
6461 wxString
str(m_text
);
6462 wxString toReplace
= wxRichTextLineBreakChar
;
6463 str
.Replace(toReplace
, wxT(" "));
6465 wxString stringChunk
= str
.Mid(startPos
, (size_t) len
);
6467 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS
))
6468 stringChunk
.MakeUpper();
6472 if (stringChunk
.Find(wxT('\t')) != wxNOT_FOUND
)
6474 // the string has a tab
6475 wxArrayInt tabArray
;
6476 if (textAttr
.GetTabs().IsEmpty())
6477 tabArray
= wxRichTextParagraph::GetDefaultTabs();
6479 tabArray
= textAttr
.GetTabs();
6481 int tabCount
= tabArray
.GetCount();
6483 for (int i
= 0; i
< tabCount
; ++i
)
6485 int pos
= tabArray
[i
];
6486 pos
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, pos
);
6490 int nextTabPos
= -1;
6492 while (stringChunk
.Find(wxT('\t')) >= 0)
6494 int absoluteWidth
= 0;
6496 // the string has a tab
6497 // break up the string at the Tab
6498 wxString stringFragment
= stringChunk
.BeforeFirst(wxT('\t'));
6499 stringChunk
= stringChunk
.AfterFirst(wxT('\t'));
6504 if (partialExtents
->GetCount() > 0)
6505 oldWidth
= (*partialExtents
)[partialExtents
->GetCount()-1];
6509 // Add these partial extents
6511 dc
.GetPartialTextExtents(stringFragment
, p
);
6513 for (j
= 0; j
< p
.GetCount(); j
++)
6514 partialExtents
->Add(oldWidth
+ p
[j
]);
6516 if (partialExtents
->GetCount() > 0)
6517 absoluteWidth
= (*partialExtents
)[(*partialExtents
).GetCount()-1] + relativeX
;
6519 absoluteWidth
= relativeX
;
6523 dc
.GetTextExtent(stringFragment
, & w
, & h
);
6525 absoluteWidth
= width
+ relativeX
;
6529 bool notFound
= true;
6530 for (int i
= 0; i
< tabCount
&& notFound
; ++i
)
6532 nextTabPos
= tabArray
.Item(i
);
6534 // Find the next tab position.
6535 // Even if we're at the end of the tab array, we must still process the chunk.
6537 if (nextTabPos
> absoluteWidth
|| (i
== (tabCount
- 1)))
6539 if (nextTabPos
<= absoluteWidth
)
6541 int defaultTabWidth
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
);
6542 nextTabPos
= absoluteWidth
+ defaultTabWidth
;
6546 width
= nextTabPos
- relativeX
;
6549 partialExtents
->Add(width
);
6555 if (!stringChunk
.IsEmpty())
6560 if (partialExtents
->GetCount() > 0)
6561 oldWidth
= (*partialExtents
)[partialExtents
->GetCount()-1];
6565 // Add these partial extents
6567 dc
.GetPartialTextExtents(stringChunk
, p
);
6569 for (j
= 0; j
< p
.GetCount(); j
++)
6570 partialExtents
->Add(oldWidth
+ p
[j
]);
6574 dc
.GetTextExtent(stringChunk
, & w
, & h
, & descent
);
6582 int charHeight
= dc
.GetCharHeight();
6583 if ((*partialExtents
).GetCount() > 0)
6584 w
= (*partialExtents
)[partialExtents
->GetCount()-1];
6587 size
= wxSize(w
, charHeight
);
6591 size
= wxSize(width
, dc
.GetCharHeight());
6595 dc
.GetTextExtent(wxT("X"), & w
, & h
, & descent
);
6603 /// Do a split, returning an object containing the second part, and setting
6604 /// the first part in 'this'.
6605 wxRichTextObject
* wxRichTextPlainText::DoSplit(long pos
)
6607 long index
= pos
- GetRange().GetStart();
6609 if (index
< 0 || index
>= (int) m_text
.length())
6612 wxString firstPart
= m_text
.Mid(0, index
);
6613 wxString secondPart
= m_text
.Mid(index
);
6617 wxRichTextPlainText
* newObject
= new wxRichTextPlainText(secondPart
);
6618 newObject
->SetAttributes(GetAttributes());
6620 newObject
->SetRange(wxRichTextRange(pos
, GetRange().GetEnd()));
6621 GetRange().SetEnd(pos
-1);
6627 void wxRichTextPlainText::CalculateRange(long start
, long& end
)
6629 end
= start
+ m_text
.length() - 1;
6630 m_range
.SetRange(start
, end
);
6634 bool wxRichTextPlainText::DeleteRange(const wxRichTextRange
& range
)
6636 wxRichTextRange r
= range
;
6638 r
.LimitTo(GetRange());
6640 if (r
.GetStart() == GetRange().GetStart() && r
.GetEnd() == GetRange().GetEnd())
6646 long startIndex
= r
.GetStart() - GetRange().GetStart();
6647 long len
= r
.GetLength();
6649 m_text
= m_text
.Mid(0, startIndex
) + m_text
.Mid(startIndex
+len
);
6653 /// Get text for the given range.
6654 wxString
wxRichTextPlainText::GetTextForRange(const wxRichTextRange
& range
) const
6656 wxRichTextRange r
= range
;
6658 r
.LimitTo(GetRange());
6660 long startIndex
= r
.GetStart() - GetRange().GetStart();
6661 long len
= r
.GetLength();
6663 return m_text
.Mid(startIndex
, len
);
6666 /// Returns true if this object can merge itself with the given one.
6667 bool wxRichTextPlainText::CanMerge(wxRichTextObject
* object
) const
6669 return object
->GetClassInfo() == CLASSINFO(wxRichTextPlainText
) &&
6670 (m_text
.empty() || wxTextAttrEq(GetAttributes(), object
->GetAttributes()));
6673 /// Returns true if this object merged itself with the given one.
6674 /// The calling code will then delete the given object.
6675 bool wxRichTextPlainText::Merge(wxRichTextObject
* object
)
6677 wxRichTextPlainText
* textObject
= wxDynamicCast(object
, wxRichTextPlainText
);
6678 wxASSERT( textObject
!= NULL
);
6682 m_text
+= textObject
->GetText();
6683 wxRichTextApplyStyle(m_attributes
, textObject
->GetAttributes());
6690 /// Dump to output stream for debugging
6691 void wxRichTextPlainText::Dump(wxTextOutputStream
& stream
)
6693 wxRichTextObject::Dump(stream
);
6694 stream
<< m_text
<< wxT("\n");
6697 /// Get the first position from pos that has a line break character.
6698 long wxRichTextPlainText::GetFirstLineBreakPosition(long pos
)
6701 int len
= m_text
.length();
6702 int startPos
= pos
- m_range
.GetStart();
6703 for (i
= startPos
; i
< len
; i
++)
6705 wxChar ch
= m_text
[i
];
6706 if (ch
== wxRichTextLineBreakChar
)
6708 return i
+ m_range
.GetStart();
6716 * This is a kind of box, used to represent the whole buffer
6719 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBuffer
, wxRichTextParagraphLayoutBox
)
6721 wxList
wxRichTextBuffer::sm_handlers
;
6722 wxRichTextRenderer
* wxRichTextBuffer::sm_renderer
= NULL
;
6723 int wxRichTextBuffer::sm_bulletRightMargin
= 20;
6724 float wxRichTextBuffer::sm_bulletProportion
= (float) 0.3;
6727 void wxRichTextBuffer::Init()
6729 m_commandProcessor
= new wxCommandProcessor
;
6730 m_styleSheet
= NULL
;
6732 m_batchedCommandDepth
= 0;
6733 m_batchedCommand
= NULL
;
6740 wxRichTextBuffer::~wxRichTextBuffer()
6742 delete m_commandProcessor
;
6743 delete m_batchedCommand
;
6746 ClearEventHandlers();
6749 void wxRichTextBuffer::ResetAndClearCommands()
6753 GetCommandProcessor()->ClearCommands();
6756 Invalidate(wxRICHTEXT_ALL
);
6759 void wxRichTextBuffer::Copy(const wxRichTextBuffer
& obj
)
6761 wxRichTextParagraphLayoutBox::Copy(obj
);
6763 m_styleSheet
= obj
.m_styleSheet
;
6764 m_modified
= obj
.m_modified
;
6765 m_batchedCommandDepth
= 0;
6766 if (m_batchedCommand
)
6767 delete m_batchedCommand
;
6768 m_batchedCommand
= NULL
;
6769 m_suppressUndo
= obj
.m_suppressUndo
;
6770 m_invalidRange
= obj
.m_invalidRange
;
6773 /// Push style sheet to top of stack
6774 bool wxRichTextBuffer::PushStyleSheet(wxRichTextStyleSheet
* styleSheet
)
6777 styleSheet
->InsertSheet(m_styleSheet
);
6779 SetStyleSheet(styleSheet
);
6784 /// Pop style sheet from top of stack
6785 wxRichTextStyleSheet
* wxRichTextBuffer::PopStyleSheet()
6789 wxRichTextStyleSheet
* oldSheet
= m_styleSheet
;
6790 m_styleSheet
= oldSheet
->GetNextSheet();
6799 /// Submit command to insert paragraphs
6800 bool wxRichTextBuffer::InsertParagraphsWithUndo(long pos
, const wxRichTextParagraphLayoutBox
& paragraphs
, wxRichTextCtrl
* ctrl
, int flags
)
6802 return ctrl
->GetFocusObject()->InsertParagraphsWithUndo(this, pos
, paragraphs
, ctrl
, flags
);
6805 /// Submit command to insert paragraphs
6806 bool wxRichTextParagraphLayoutBox::InsertParagraphsWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxRichTextParagraphLayoutBox
& paragraphs
, wxRichTextCtrl
* ctrl
, int WXUNUSED(flags
))
6808 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6810 action
->GetNewParagraphs() = paragraphs
;
6812 action
->SetPosition(pos
);
6814 wxRichTextRange range
= wxRichTextRange(pos
, pos
+ paragraphs
.GetOwnRange().GetEnd() - 1);
6815 if (!paragraphs
.GetPartialParagraph())
6816 range
.SetEnd(range
.GetEnd()+1);
6818 // Set the range we'll need to delete in Undo
6819 action
->SetRange(range
);
6821 buffer
->SubmitAction(action
);
6826 /// Submit command to insert the given text
6827 bool wxRichTextBuffer::InsertTextWithUndo(long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
, int flags
)
6829 return ctrl
->GetFocusObject()->InsertTextWithUndo(this, pos
, text
, ctrl
, flags
);
6832 /// Submit command to insert the given text
6833 bool wxRichTextParagraphLayoutBox::InsertTextWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
, int flags
)
6835 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6837 wxRichTextAttr
* p
= NULL
;
6838 wxRichTextAttr paraAttr
;
6839 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
6841 // Get appropriate paragraph style
6842 paraAttr
= GetStyleForNewParagraph(buffer
, pos
, false, false);
6843 if (!paraAttr
.IsDefault())
6847 action
->GetNewParagraphs().AddParagraphs(text
, p
);
6849 int length
= action
->GetNewParagraphs().GetOwnRange().GetLength();
6851 if (!text
.empty() && text
.Last() != wxT('\n'))
6853 // Don't count the newline when undoing
6855 action
->GetNewParagraphs().SetPartialParagraph(true);
6857 else if (!text
.empty() && text
.Last() == wxT('\n'))
6860 action
->SetPosition(pos
);
6862 // Set the range we'll need to delete in Undo
6863 action
->SetRange(wxRichTextRange(pos
, pos
+ length
- 1));
6865 buffer
->SubmitAction(action
);
6870 /// Submit command to insert the given text
6871 bool wxRichTextBuffer::InsertNewlineWithUndo(long pos
, wxRichTextCtrl
* ctrl
, int flags
)
6873 return ctrl
->GetFocusObject()->InsertNewlineWithUndo(this, pos
, ctrl
, flags
);
6876 /// Submit command to insert the given text
6877 bool wxRichTextParagraphLayoutBox::InsertNewlineWithUndo(wxRichTextBuffer
* buffer
, long pos
, wxRichTextCtrl
* ctrl
, int flags
)
6879 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6881 wxRichTextAttr
* p
= NULL
;
6882 wxRichTextAttr paraAttr
;
6883 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
6885 paraAttr
= GetStyleForNewParagraph(buffer
, pos
, false, true /* look for next paragraph style */);
6886 if (!paraAttr
.IsDefault())
6890 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
6892 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(wxEmptyString
, this, & attr
);
6893 action
->GetNewParagraphs().AppendChild(newPara
);
6894 action
->GetNewParagraphs().UpdateRanges();
6895 action
->GetNewParagraphs().SetPartialParagraph(false);
6896 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
, false);
6900 newPara
->SetAttributes(*p
);
6902 if (flags
& wxRICHTEXT_INSERT_INTERACTIVE
)
6904 if (para
&& para
->GetRange().GetEnd() == pos
)
6907 // Now see if we need to number the paragraph.
6908 if (newPara
->GetAttributes().HasBulletNumber())
6910 wxRichTextAttr numberingAttr
;
6911 if (FindNextParagraphNumber(para
, numberingAttr
))
6912 wxRichTextApplyStyle(newPara
->GetAttributes(), (const wxRichTextAttr
&) numberingAttr
);
6916 action
->SetPosition(pos
);
6918 // Use the default character style
6919 // Use the default character style
6920 if (!buffer
->GetDefaultStyle().IsDefault() && newPara
->GetChildren().GetFirst())
6922 // Check whether the default style merely reflects the paragraph/basic style,
6923 // in which case don't apply it.
6924 wxRichTextAttr
defaultStyle(buffer
->GetDefaultStyle());
6925 wxRichTextAttr toApply
;
6928 wxRichTextAttr combinedAttr
= para
->GetCombinedAttributes(true /* include box attributes */);
6929 wxRichTextAttr newAttr
;
6930 // This filters out attributes that are accounted for by the current
6931 // paragraph/basic style
6932 wxRichTextApplyStyle(toApply
, defaultStyle
, & combinedAttr
);
6935 toApply
= defaultStyle
;
6937 if (!toApply
.IsDefault())
6938 newPara
->GetChildren().GetFirst()->GetData()->SetAttributes(toApply
);
6941 // Set the range we'll need to delete in Undo
6942 action
->SetRange(wxRichTextRange(pos1
, pos1
));
6944 buffer
->SubmitAction(action
);
6949 /// Submit command to insert the given image
6950 bool wxRichTextBuffer::InsertImageWithUndo(long pos
, const wxRichTextImageBlock
& imageBlock
, wxRichTextCtrl
* ctrl
, int flags
,
6951 const wxRichTextAttr
& textAttr
)
6953 return ctrl
->GetFocusObject()->InsertImageWithUndo(this, pos
, imageBlock
, ctrl
, flags
, textAttr
);
6956 /// Submit command to insert the given image
6957 bool wxRichTextParagraphLayoutBox::InsertImageWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxRichTextImageBlock
& imageBlock
,
6958 wxRichTextCtrl
* ctrl
, int flags
,
6959 const wxRichTextAttr
& textAttr
)
6961 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6963 wxRichTextAttr
* p
= NULL
;
6964 wxRichTextAttr paraAttr
;
6965 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
6967 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
6968 if (!paraAttr
.IsDefault())
6972 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
6974 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
6976 newPara
->SetAttributes(*p
);
6978 wxRichTextImage
* imageObject
= new wxRichTextImage(imageBlock
, newPara
);
6979 newPara
->AppendChild(imageObject
);
6980 imageObject
->SetAttributes(textAttr
);
6981 action
->GetNewParagraphs().AppendChild(newPara
);
6982 action
->GetNewParagraphs().UpdateRanges();
6984 action
->GetNewParagraphs().SetPartialParagraph(true);
6986 action
->SetPosition(pos
);
6988 // Set the range we'll need to delete in Undo
6989 action
->SetRange(wxRichTextRange(pos
, pos
));
6991 buffer
->SubmitAction(action
);
6996 // Insert an object with no change of it
6997 wxRichTextObject
* wxRichTextBuffer::InsertObjectWithUndo(long pos
, wxRichTextObject
*object
, wxRichTextCtrl
* ctrl
, int flags
)
6999 return ctrl
->GetFocusObject()->InsertObjectWithUndo(this, pos
, object
, ctrl
, flags
);
7002 // Insert an object with no change of it
7003 wxRichTextObject
* wxRichTextParagraphLayoutBox::InsertObjectWithUndo(wxRichTextBuffer
* buffer
, long pos
, wxRichTextObject
*object
, wxRichTextCtrl
* ctrl
, int flags
)
7005 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Object"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7007 wxRichTextAttr
* p
= NULL
;
7008 wxRichTextAttr paraAttr
;
7009 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7011 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
7012 if (!paraAttr
.IsDefault())
7016 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7018 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7020 newPara
->SetAttributes(*p
);
7022 newPara
->AppendChild(object
);
7023 action
->GetNewParagraphs().AppendChild(newPara
);
7024 action
->GetNewParagraphs().UpdateRanges();
7026 action
->GetNewParagraphs().SetPartialParagraph(true);
7028 action
->SetPosition(pos
);
7030 // Set the range we'll need to delete in Undo
7031 action
->SetRange(wxRichTextRange(pos
, pos
));
7033 buffer
->SubmitAction(action
);
7035 wxRichTextObject
* obj
= GetLeafObjectAtPosition(pos
);
7039 /// Get the style that is appropriate for a new paragraph at this position.
7040 /// If the previous paragraph has a paragraph style name, look up the next-paragraph
7042 wxRichTextAttr
wxRichTextParagraphLayoutBox::GetStyleForNewParagraph(wxRichTextBuffer
* buffer
, long pos
, bool caretPosition
, bool lookUpNewParaStyle
) const
7044 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
, caretPosition
);
7047 wxRichTextAttr attr
;
7048 bool foundAttributes
= false;
7050 // Look for a matching paragraph style
7051 if (lookUpNewParaStyle
&& !para
->GetAttributes().GetParagraphStyleName().IsEmpty() && buffer
->GetStyleSheet())
7053 wxRichTextParagraphStyleDefinition
* paraDef
= buffer
->GetStyleSheet()->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
7056 // If we're not at the end of the paragraph, then we apply THIS style, and not the designated next style.
7057 if (para
->GetRange().GetEnd() == pos
&& !paraDef
->GetNextStyle().IsEmpty())
7059 wxRichTextParagraphStyleDefinition
* nextParaDef
= buffer
->GetStyleSheet()->FindParagraphStyle(paraDef
->GetNextStyle());
7062 foundAttributes
= true;
7063 attr
= nextParaDef
->GetStyleMergedWithBase(buffer
->GetStyleSheet());
7067 // If we didn't find the 'next style', use this style instead.
7068 if (!foundAttributes
)
7070 foundAttributes
= true;
7071 attr
= paraDef
->GetStyleMergedWithBase(buffer
->GetStyleSheet());
7076 // Also apply list style if present
7077 if (lookUpNewParaStyle
&& !para
->GetAttributes().GetListStyleName().IsEmpty() && buffer
->GetStyleSheet())
7079 wxRichTextListStyleDefinition
* listDef
= buffer
->GetStyleSheet()->FindListStyle(para
->GetAttributes().GetListStyleName());
7082 int thisIndent
= para
->GetAttributes().GetLeftIndent();
7083 int thisLevel
= para
->GetAttributes().HasOutlineLevel() ? para
->GetAttributes().GetOutlineLevel() : listDef
->FindLevelForIndent(thisIndent
);
7085 // Apply the overall list style, and item style for this level
7086 wxRichTextAttr
listStyle(listDef
->GetCombinedStyleForLevel(thisLevel
, buffer
->GetStyleSheet()));
7087 wxRichTextApplyStyle(attr
, listStyle
);
7088 attr
.SetOutlineLevel(thisLevel
);
7089 if (para
->GetAttributes().HasBulletNumber())
7090 attr
.SetBulletNumber(para
->GetAttributes().GetBulletNumber());
7094 if (!foundAttributes
)
7096 attr
= para
->GetAttributes();
7097 int flags
= attr
.GetFlags();
7099 // Eliminate character styles
7100 flags
&= ( (~ wxTEXT_ATTR_FONT
) |
7101 (~ wxTEXT_ATTR_TEXT_COLOUR
) |
7102 (~ wxTEXT_ATTR_BACKGROUND_COLOUR
) );
7103 attr
.SetFlags(flags
);
7109 return wxRichTextAttr();
7112 /// Submit command to delete this range
7113 bool wxRichTextBuffer::DeleteRangeWithUndo(const wxRichTextRange
& range
, wxRichTextCtrl
* ctrl
)
7115 return ctrl
->GetFocusObject()->DeleteRangeWithUndo(range
, ctrl
, this);
7118 /// Submit command to delete this range
7119 bool wxRichTextParagraphLayoutBox::DeleteRangeWithUndo(const wxRichTextRange
& range
, wxRichTextCtrl
* ctrl
, wxRichTextBuffer
* buffer
)
7121 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Delete"), wxRICHTEXT_DELETE
, buffer
, this, ctrl
);
7123 action
->SetPosition(ctrl
->GetCaretPosition());
7125 // Set the range to delete
7126 action
->SetRange(range
);
7128 // Copy the fragment that we'll need to restore in Undo
7129 CopyFragment(range
, action
->GetOldParagraphs());
7131 // See if we're deleting a paragraph marker, in which case we need to
7132 // make a note not to copy the attributes from the 2nd paragraph to the 1st.
7133 if (range
.GetStart() == range
.GetEnd())
7135 wxRichTextParagraph
* para
= GetParagraphAtPosition(range
.GetStart());
7136 if (para
&& para
->GetRange().GetEnd() == range
.GetEnd())
7138 wxRichTextParagraph
* nextPara
= GetParagraphAtPosition(range
.GetStart()+1);
7139 if (nextPara
&& nextPara
!= para
)
7141 action
->GetOldParagraphs().GetChildren().GetFirst()->GetData()->SetAttributes(nextPara
->GetAttributes());
7142 action
->GetOldParagraphs().GetAttributes().SetFlags(action
->GetOldParagraphs().GetAttributes().GetFlags() | wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
);
7147 buffer
->SubmitAction(action
);
7152 /// Collapse undo/redo commands
7153 bool wxRichTextBuffer::BeginBatchUndo(const wxString
& cmdName
)
7155 if (m_batchedCommandDepth
== 0)
7157 wxASSERT(m_batchedCommand
== NULL
);
7158 if (m_batchedCommand
)
7160 GetCommandProcessor()->Store(m_batchedCommand
);
7162 m_batchedCommand
= new wxRichTextCommand(cmdName
);
7165 m_batchedCommandDepth
++;
7170 /// Collapse undo/redo commands
7171 bool wxRichTextBuffer::EndBatchUndo()
7173 m_batchedCommandDepth
--;
7175 wxASSERT(m_batchedCommandDepth
>= 0);
7176 wxASSERT(m_batchedCommand
!= NULL
);
7178 if (m_batchedCommandDepth
== 0)
7180 GetCommandProcessor()->Store(m_batchedCommand
);
7181 m_batchedCommand
= NULL
;
7187 /// Submit immediately, or delay according to whether collapsing is on
7188 bool wxRichTextBuffer::SubmitAction(wxRichTextAction
* action
)
7190 if (BatchingUndo() && m_batchedCommand
&& !SuppressingUndo())
7192 wxRichTextCommand
* cmd
= new wxRichTextCommand(action
->GetName());
7193 cmd
->AddAction(action
);
7195 cmd
->GetActions().Clear();
7198 m_batchedCommand
->AddAction(action
);
7202 wxRichTextCommand
* cmd
= new wxRichTextCommand(action
->GetName());
7203 cmd
->AddAction(action
);
7205 // Only store it if we're not suppressing undo.
7206 return GetCommandProcessor()->Submit(cmd
, !SuppressingUndo());
7212 /// Begin suppressing undo/redo commands.
7213 bool wxRichTextBuffer::BeginSuppressUndo()
7220 /// End suppressing undo/redo commands.
7221 bool wxRichTextBuffer::EndSuppressUndo()
7228 /// Begin using a style
7229 bool wxRichTextBuffer::BeginStyle(const wxRichTextAttr
& style
)
7231 wxRichTextAttr
newStyle(GetDefaultStyle());
7233 // Save the old default style
7234 m_attributeStack
.Append((wxObject
*) new wxRichTextAttr(GetDefaultStyle()));
7236 wxRichTextApplyStyle(newStyle
, style
);
7237 newStyle
.SetFlags(style
.GetFlags()|newStyle
.GetFlags());
7239 SetDefaultStyle(newStyle
);
7245 bool wxRichTextBuffer::EndStyle()
7247 if (!m_attributeStack
.GetFirst())
7249 wxLogDebug(_("Too many EndStyle calls!"));
7253 wxList::compatibility_iterator node
= m_attributeStack
.GetLast();
7254 wxRichTextAttr
* attr
= (wxRichTextAttr
*)node
->GetData();
7255 m_attributeStack
.Erase(node
);
7257 SetDefaultStyle(*attr
);
7264 bool wxRichTextBuffer::EndAllStyles()
7266 while (m_attributeStack
.GetCount() != 0)
7271 /// Clear the style stack
7272 void wxRichTextBuffer::ClearStyleStack()
7274 for (wxList::compatibility_iterator node
= m_attributeStack
.GetFirst(); node
; node
= node
->GetNext())
7275 delete (wxRichTextAttr
*) node
->GetData();
7276 m_attributeStack
.Clear();
7279 /// Begin using bold
7280 bool wxRichTextBuffer::BeginBold()
7282 wxRichTextAttr attr
;
7283 attr
.SetFontWeight(wxFONTWEIGHT_BOLD
);
7285 return BeginStyle(attr
);
7288 /// Begin using italic
7289 bool wxRichTextBuffer::BeginItalic()
7291 wxRichTextAttr attr
;
7292 attr
.SetFontStyle(wxFONTSTYLE_ITALIC
);
7294 return BeginStyle(attr
);
7297 /// Begin using underline
7298 bool wxRichTextBuffer::BeginUnderline()
7300 wxRichTextAttr attr
;
7301 attr
.SetFontUnderlined(true);
7303 return BeginStyle(attr
);
7306 /// Begin using point size
7307 bool wxRichTextBuffer::BeginFontSize(int pointSize
)
7309 wxRichTextAttr attr
;
7310 attr
.SetFontSize(pointSize
);
7312 return BeginStyle(attr
);
7315 /// Begin using this font
7316 bool wxRichTextBuffer::BeginFont(const wxFont
& font
)
7318 wxRichTextAttr attr
;
7321 return BeginStyle(attr
);
7324 /// Begin using this colour
7325 bool wxRichTextBuffer::BeginTextColour(const wxColour
& colour
)
7327 wxRichTextAttr attr
;
7328 attr
.SetFlags(wxTEXT_ATTR_TEXT_COLOUR
);
7329 attr
.SetTextColour(colour
);
7331 return BeginStyle(attr
);
7334 /// Begin using alignment
7335 bool wxRichTextBuffer::BeginAlignment(wxTextAttrAlignment alignment
)
7337 wxRichTextAttr attr
;
7338 attr
.SetFlags(wxTEXT_ATTR_ALIGNMENT
);
7339 attr
.SetAlignment(alignment
);
7341 return BeginStyle(attr
);
7344 /// Begin left indent
7345 bool wxRichTextBuffer::BeginLeftIndent(int leftIndent
, int leftSubIndent
)
7347 wxRichTextAttr attr
;
7348 attr
.SetFlags(wxTEXT_ATTR_LEFT_INDENT
);
7349 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7351 return BeginStyle(attr
);
7354 /// Begin right indent
7355 bool wxRichTextBuffer::BeginRightIndent(int rightIndent
)
7357 wxRichTextAttr attr
;
7358 attr
.SetFlags(wxTEXT_ATTR_RIGHT_INDENT
);
7359 attr
.SetRightIndent(rightIndent
);
7361 return BeginStyle(attr
);
7364 /// Begin paragraph spacing
7365 bool wxRichTextBuffer::BeginParagraphSpacing(int before
, int after
)
7369 flags
|= wxTEXT_ATTR_PARA_SPACING_BEFORE
;
7371 flags
|= wxTEXT_ATTR_PARA_SPACING_AFTER
;
7373 wxRichTextAttr attr
;
7374 attr
.SetFlags(flags
);
7375 attr
.SetParagraphSpacingBefore(before
);
7376 attr
.SetParagraphSpacingAfter(after
);
7378 return BeginStyle(attr
);
7381 /// Begin line spacing
7382 bool wxRichTextBuffer::BeginLineSpacing(int lineSpacing
)
7384 wxRichTextAttr attr
;
7385 attr
.SetFlags(wxTEXT_ATTR_LINE_SPACING
);
7386 attr
.SetLineSpacing(lineSpacing
);
7388 return BeginStyle(attr
);
7391 /// Begin numbered bullet
7392 bool wxRichTextBuffer::BeginNumberedBullet(int bulletNumber
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7394 wxRichTextAttr attr
;
7395 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7396 attr
.SetBulletStyle(bulletStyle
);
7397 attr
.SetBulletNumber(bulletNumber
);
7398 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7400 return BeginStyle(attr
);
7403 /// Begin symbol bullet
7404 bool wxRichTextBuffer::BeginSymbolBullet(const wxString
& symbol
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7406 wxRichTextAttr attr
;
7407 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7408 attr
.SetBulletStyle(bulletStyle
);
7409 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7410 attr
.SetBulletText(symbol
);
7412 return BeginStyle(attr
);
7415 /// Begin standard bullet
7416 bool wxRichTextBuffer::BeginStandardBullet(const wxString
& bulletName
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7418 wxRichTextAttr attr
;
7419 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7420 attr
.SetBulletStyle(bulletStyle
);
7421 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7422 attr
.SetBulletName(bulletName
);
7424 return BeginStyle(attr
);
7427 /// Begin named character style
7428 bool wxRichTextBuffer::BeginCharacterStyle(const wxString
& characterStyle
)
7430 if (GetStyleSheet())
7432 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->FindCharacterStyle(characterStyle
);
7435 wxRichTextAttr attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7436 return BeginStyle(attr
);
7442 /// Begin named paragraph style
7443 bool wxRichTextBuffer::BeginParagraphStyle(const wxString
& paragraphStyle
)
7445 if (GetStyleSheet())
7447 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(paragraphStyle
);
7450 wxRichTextAttr attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7451 return BeginStyle(attr
);
7457 /// Begin named list style
7458 bool wxRichTextBuffer::BeginListStyle(const wxString
& listStyle
, int level
, int number
)
7460 if (GetStyleSheet())
7462 wxRichTextListStyleDefinition
* def
= GetStyleSheet()->FindListStyle(listStyle
);
7465 wxRichTextAttr
attr(def
->GetCombinedStyleForLevel(level
));
7467 attr
.SetBulletNumber(number
);
7469 return BeginStyle(attr
);
7476 bool wxRichTextBuffer::BeginURL(const wxString
& url
, const wxString
& characterStyle
)
7478 wxRichTextAttr attr
;
7480 if (!characterStyle
.IsEmpty() && GetStyleSheet())
7482 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->FindCharacterStyle(characterStyle
);
7485 attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7490 return BeginStyle(attr
);
7493 /// Adds a handler to the end
7494 void wxRichTextBuffer::AddHandler(wxRichTextFileHandler
*handler
)
7496 sm_handlers
.Append(handler
);
7499 /// Inserts a handler at the front
7500 void wxRichTextBuffer::InsertHandler(wxRichTextFileHandler
*handler
)
7502 sm_handlers
.Insert( handler
);
7505 /// Removes a handler
7506 bool wxRichTextBuffer::RemoveHandler(const wxString
& name
)
7508 wxRichTextFileHandler
*handler
= FindHandler(name
);
7511 sm_handlers
.DeleteObject(handler
);
7519 /// Finds a handler by filename or, if supplied, type
7520 wxRichTextFileHandler
*wxRichTextBuffer::FindHandlerFilenameOrType(const wxString
& filename
,
7521 wxRichTextFileType imageType
)
7523 if (imageType
!= wxRICHTEXT_TYPE_ANY
)
7524 return FindHandler(imageType
);
7525 else if (!filename
.IsEmpty())
7527 wxString path
, file
, ext
;
7528 wxFileName::SplitPath(filename
, & path
, & file
, & ext
);
7529 return FindHandler(ext
, imageType
);
7536 /// Finds a handler by name
7537 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(const wxString
& name
)
7539 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7542 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7543 if (handler
->GetName().Lower() == name
.Lower()) return handler
;
7545 node
= node
->GetNext();
7550 /// Finds a handler by extension and type
7551 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(const wxString
& extension
, wxRichTextFileType type
)
7553 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7556 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7557 if ( handler
->GetExtension().Lower() == extension
.Lower() &&
7558 (type
== wxRICHTEXT_TYPE_ANY
|| handler
->GetType() == type
) )
7560 node
= node
->GetNext();
7565 /// Finds a handler by type
7566 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(wxRichTextFileType type
)
7568 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7571 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7572 if (handler
->GetType() == type
) return handler
;
7573 node
= node
->GetNext();
7578 void wxRichTextBuffer::InitStandardHandlers()
7580 if (!FindHandler(wxRICHTEXT_TYPE_TEXT
))
7581 AddHandler(new wxRichTextPlainTextHandler
);
7584 void wxRichTextBuffer::CleanUpHandlers()
7586 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7589 wxRichTextFileHandler
* handler
= (wxRichTextFileHandler
*)node
->GetData();
7590 wxList::compatibility_iterator next
= node
->GetNext();
7595 sm_handlers
.Clear();
7598 wxString
wxRichTextBuffer::GetExtWildcard(bool combine
, bool save
, wxArrayInt
* types
)
7605 wxList::compatibility_iterator node
= GetHandlers().GetFirst();
7609 wxRichTextFileHandler
* handler
= (wxRichTextFileHandler
*) node
->GetData();
7610 if (handler
->IsVisible() && ((save
&& handler
->CanSave()) || (!save
&& handler
->CanLoad())))
7615 wildcard
+= wxT(";");
7616 wildcard
+= wxT("*.") + handler
->GetExtension();
7621 wildcard
+= wxT("|");
7622 wildcard
+= handler
->GetName();
7623 wildcard
+= wxT(" ");
7624 wildcard
+= _("files");
7625 wildcard
+= wxT(" (*.");
7626 wildcard
+= handler
->GetExtension();
7627 wildcard
+= wxT(")|*.");
7628 wildcard
+= handler
->GetExtension();
7630 types
->Add(handler
->GetType());
7635 node
= node
->GetNext();
7639 wildcard
= wxT("(") + wildcard
+ wxT(")|") + wildcard
;
7644 bool wxRichTextBuffer::LoadFile(const wxString
& filename
, wxRichTextFileType type
)
7646 wxRichTextFileHandler
* handler
= FindHandlerFilenameOrType(filename
, type
);
7649 SetDefaultStyle(wxRichTextAttr());
7650 handler
->SetFlags(GetHandlerFlags());
7651 bool success
= handler
->LoadFile(this, filename
);
7652 Invalidate(wxRICHTEXT_ALL
);
7660 bool wxRichTextBuffer::SaveFile(const wxString
& filename
, wxRichTextFileType type
)
7662 wxRichTextFileHandler
* handler
= FindHandlerFilenameOrType(filename
, type
);
7665 handler
->SetFlags(GetHandlerFlags());
7666 return handler
->SaveFile(this, filename
);
7672 /// Load from a stream
7673 bool wxRichTextBuffer::LoadFile(wxInputStream
& stream
, wxRichTextFileType type
)
7675 wxRichTextFileHandler
* handler
= FindHandler(type
);
7678 SetDefaultStyle(wxRichTextAttr());
7679 handler
->SetFlags(GetHandlerFlags());
7680 bool success
= handler
->LoadFile(this, stream
);
7681 Invalidate(wxRICHTEXT_ALL
);
7688 /// Save to a stream
7689 bool wxRichTextBuffer::SaveFile(wxOutputStream
& stream
, wxRichTextFileType type
)
7691 wxRichTextFileHandler
* handler
= FindHandler(type
);
7694 handler
->SetFlags(GetHandlerFlags());
7695 return handler
->SaveFile(this, stream
);
7701 /// Copy the range to the clipboard
7702 bool wxRichTextBuffer::CopyToClipboard(const wxRichTextRange
& range
)
7704 bool success
= false;
7705 wxRichTextParagraphLayoutBox
* container
= this;
7706 if (GetRichTextCtrl())
7707 container
= GetRichTextCtrl()->GetFocusObject();
7709 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
7711 if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open())
7713 wxTheClipboard
->Clear();
7715 // Add composite object
7717 wxDataObjectComposite
* compositeObject
= new wxDataObjectComposite();
7720 wxString text
= container
->GetTextForRange(range
);
7723 text
= wxTextFile::Translate(text
, wxTextFileType_Dos
);
7726 compositeObject
->Add(new wxTextDataObject(text
), false /* not preferred */);
7729 // Add rich text buffer data object. This needs the XML handler to be present.
7731 if (FindHandler(wxRICHTEXT_TYPE_XML
))
7733 wxRichTextBuffer
* richTextBuf
= new wxRichTextBuffer
;
7734 container
->CopyFragment(range
, *richTextBuf
);
7736 compositeObject
->Add(new wxRichTextBufferDataObject(richTextBuf
), true /* preferred */);
7739 if (wxTheClipboard
->SetData(compositeObject
))
7742 wxTheClipboard
->Close();
7751 /// Paste the clipboard content to the buffer
7752 bool wxRichTextBuffer::PasteFromClipboard(long position
)
7754 bool success
= false;
7755 wxRichTextParagraphLayoutBox
* container
= this;
7756 if (GetRichTextCtrl())
7757 container
= GetRichTextCtrl()->GetFocusObject();
7759 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
7760 if (CanPasteFromClipboard())
7762 if (wxTheClipboard
->Open())
7764 if (wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())))
7766 wxRichTextBufferDataObject data
;
7767 wxTheClipboard
->GetData(data
);
7768 wxRichTextBuffer
* richTextBuffer
= data
.GetRichTextBuffer();
7771 container
->InsertParagraphsWithUndo(this, position
+1, *richTextBuffer
, GetRichTextCtrl(), 0);
7772 if (GetRichTextCtrl())
7773 GetRichTextCtrl()->ShowPosition(position
+ richTextBuffer
->GetOwnRange().GetEnd());
7774 delete richTextBuffer
;
7777 else if (wxTheClipboard
->IsSupported(wxDF_TEXT
)
7779 || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
)
7783 wxTextDataObject data
;
7784 wxTheClipboard
->GetData(data
);
7785 wxString
text(data
.GetText());
7788 text2
.Alloc(text
.Length()+1);
7790 for (i
= 0; i
< text
.Length(); i
++)
7792 wxChar ch
= text
[i
];
7793 if (ch
!= wxT('\r'))
7797 wxString text2
= text
;
7799 container
->InsertTextWithUndo(this, position
+1, text2
, GetRichTextCtrl(), wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
);
7801 if (GetRichTextCtrl())
7802 GetRichTextCtrl()->ShowPosition(position
+ text2
.Length());
7806 else if (wxTheClipboard
->IsSupported(wxDF_BITMAP
))
7808 wxBitmapDataObject data
;
7809 wxTheClipboard
->GetData(data
);
7810 wxBitmap
bitmap(data
.GetBitmap());
7811 wxImage
image(bitmap
.ConvertToImage());
7813 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, this, container
, GetRichTextCtrl(), false);
7815 action
->GetNewParagraphs().AddImage(image
);
7817 if (action
->GetNewParagraphs().GetChildCount() == 1)
7818 action
->GetNewParagraphs().SetPartialParagraph(true);
7820 action
->SetPosition(position
+1);
7822 // Set the range we'll need to delete in Undo
7823 action
->SetRange(wxRichTextRange(position
+1, position
+1));
7825 SubmitAction(action
);
7829 wxTheClipboard
->Close();
7833 wxUnusedVar(position
);
7838 /// Can we paste from the clipboard?
7839 bool wxRichTextBuffer::CanPasteFromClipboard() const
7841 bool canPaste
= false;
7842 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
7843 if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open())
7845 if (wxTheClipboard
->IsSupported(wxDF_TEXT
)
7847 || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
)
7849 || wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())) ||
7850 wxTheClipboard
->IsSupported(wxDF_BITMAP
))
7854 wxTheClipboard
->Close();
7860 /// Dumps contents of buffer for debugging purposes
7861 void wxRichTextBuffer::Dump()
7865 wxStringOutputStream
stream(& text
);
7866 wxTextOutputStream
textStream(stream
);
7873 /// Add an event handler
7874 bool wxRichTextBuffer::AddEventHandler(wxEvtHandler
* handler
)
7876 m_eventHandlers
.Append(handler
);
7880 /// Remove an event handler
7881 bool wxRichTextBuffer::RemoveEventHandler(wxEvtHandler
* handler
, bool deleteHandler
)
7883 wxList::compatibility_iterator node
= m_eventHandlers
.Find(handler
);
7886 m_eventHandlers
.Erase(node
);
7896 /// Clear event handlers
7897 void wxRichTextBuffer::ClearEventHandlers()
7899 m_eventHandlers
.Clear();
7902 /// Send event to event handlers. If sendToAll is true, will send to all event handlers,
7903 /// otherwise will stop at the first successful one.
7904 bool wxRichTextBuffer::SendEvent(wxEvent
& event
, bool sendToAll
)
7906 bool success
= false;
7907 for (wxList::compatibility_iterator node
= m_eventHandlers
.GetFirst(); node
; node
= node
->GetNext())
7909 wxEvtHandler
* handler
= (wxEvtHandler
*) node
->GetData();
7910 if (handler
->ProcessEvent(event
))
7920 /// Set style sheet and notify of the change
7921 bool wxRichTextBuffer::SetStyleSheetAndNotify(wxRichTextStyleSheet
* sheet
)
7923 wxRichTextStyleSheet
* oldSheet
= GetStyleSheet();
7925 wxWindowID winid
= wxID_ANY
;
7926 if (GetRichTextCtrl())
7927 winid
= GetRichTextCtrl()->GetId();
7929 wxRichTextEvent
event(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACING
, winid
);
7930 event
.SetEventObject(GetRichTextCtrl());
7931 event
.SetContainer(GetRichTextCtrl()->GetFocusObject());
7932 event
.SetOldStyleSheet(oldSheet
);
7933 event
.SetNewStyleSheet(sheet
);
7936 if (SendEvent(event
) && !event
.IsAllowed())
7938 if (sheet
!= oldSheet
)
7944 if (oldSheet
&& oldSheet
!= sheet
)
7947 SetStyleSheet(sheet
);
7949 event
.SetEventType(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACED
);
7950 event
.SetOldStyleSheet(NULL
);
7953 return SendEvent(event
);
7956 /// Set renderer, deleting old one
7957 void wxRichTextBuffer::SetRenderer(wxRichTextRenderer
* renderer
)
7961 sm_renderer
= renderer
;
7964 /// Hit-testing: returns a flag indicating hit test details, plus
7965 /// information about position
7966 int wxRichTextBuffer::HitTest(wxDC
& dc
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
7968 int ret
= wxRichTextParagraphLayoutBox::HitTest(dc
, pt
, textPosition
, obj
, contextObj
, flags
);
7969 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
7975 textPosition
= m_ownRange
.GetEnd()-1;
7978 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
;
7982 bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxRichTextAttr
& bulletAttr
, const wxRect
& rect
)
7984 if (bulletAttr
.GetTextColour().IsOk())
7986 wxCheckSetPen(dc
, wxPen(bulletAttr
.GetTextColour()));
7987 wxCheckSetBrush(dc
, wxBrush(bulletAttr
.GetTextColour()));
7991 wxCheckSetPen(dc
, *wxBLACK_PEN
);
7992 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
7996 if (bulletAttr
.HasFont())
7998 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(bulletAttr
);
8001 font
= (*wxNORMAL_FONT
);
8003 wxCheckSetFont(dc
, font
);
8005 int charHeight
= dc
.GetCharHeight();
8007 int bulletWidth
= (int) (((float) charHeight
) * wxRichTextBuffer::GetBulletProportion());
8008 int bulletHeight
= bulletWidth
;
8012 // Calculate the top position of the character (as opposed to the whole line height)
8013 int y
= rect
.y
+ (rect
.height
- charHeight
);
8015 // Calculate where the bullet should be positioned
8016 y
= y
+ (charHeight
+1)/2 - (bulletHeight
+1)/2;
8018 // The margin between a bullet and text.
8019 int margin
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer::GetBulletRightMargin());
8021 if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
)
8022 x
= rect
.x
+ rect
.width
- bulletWidth
- margin
;
8023 else if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
)
8024 x
= x
+ (rect
.width
)/2 - bulletWidth
/2;
8026 if (bulletAttr
.GetBulletName() == wxT("standard/square"))
8028 dc
.DrawRectangle(x
, y
, bulletWidth
, bulletHeight
);
8030 else if (bulletAttr
.GetBulletName() == wxT("standard/diamond"))
8033 pts
[0].x
= x
; pts
[0].y
= y
+ bulletHeight
/2;
8034 pts
[1].x
= x
+ bulletWidth
/2; pts
[1].y
= y
;
8035 pts
[2].x
= x
+ bulletWidth
; pts
[2].y
= y
+ bulletHeight
/2;
8036 pts
[3].x
= x
+ bulletWidth
/2; pts
[3].y
= y
+ bulletHeight
;
8038 dc
.DrawPolygon(4, pts
);
8040 else if (bulletAttr
.GetBulletName() == wxT("standard/triangle"))
8043 pts
[0].x
= x
; pts
[0].y
= y
;
8044 pts
[1].x
= x
+ bulletWidth
; pts
[1].y
= y
+ bulletHeight
/2;
8045 pts
[2].x
= x
; pts
[2].y
= y
+ bulletHeight
;
8047 dc
.DrawPolygon(3, pts
);
8049 else if (bulletAttr
.GetBulletName() == wxT("standard/circle-outline"))
8051 wxCheckSetBrush(dc
, *wxTRANSPARENT_BRUSH
);
8052 dc
.DrawEllipse(x
, y
, bulletWidth
, bulletHeight
);
8054 else // "standard/circle", and catch-all
8056 dc
.DrawEllipse(x
, y
, bulletWidth
, bulletHeight
);
8062 bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxRichTextAttr
& attr
, const wxRect
& rect
, const wxString
& text
)
8067 if ((attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
) && !attr
.GetBulletFont().IsEmpty() && attr
.HasFont())
8069 wxRichTextAttr fontAttr
;
8070 fontAttr
.SetFontSize(attr
.GetFontSize());
8071 fontAttr
.SetFontStyle(attr
.GetFontStyle());
8072 fontAttr
.SetFontWeight(attr
.GetFontWeight());
8073 fontAttr
.SetFontUnderlined(attr
.GetFontUnderlined());
8074 fontAttr
.SetFontFaceName(attr
.GetBulletFont());
8075 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(fontAttr
);
8077 else if (attr
.HasFont())
8078 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(attr
);
8080 font
= (*wxNORMAL_FONT
);
8082 wxCheckSetFont(dc
, font
);
8084 if (attr
.GetTextColour().IsOk())
8085 dc
.SetTextForeground(attr
.GetTextColour());
8087 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
8089 int charHeight
= dc
.GetCharHeight();
8091 dc
.GetTextExtent(text
, & tw
, & th
);
8095 // Calculate the top position of the character (as opposed to the whole line height)
8096 int y
= rect
.y
+ (rect
.height
- charHeight
);
8098 // The margin between a bullet and text.
8099 int margin
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer::GetBulletRightMargin());
8101 if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
)
8102 x
= (rect
.x
+ rect
.width
) - tw
- margin
;
8103 else if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
)
8104 x
= x
+ (rect
.width
)/2 - tw
/2;
8106 dc
.DrawText(text
, x
, y
);
8114 bool wxRichTextStdRenderer::DrawBitmapBullet(wxRichTextParagraph
* WXUNUSED(paragraph
), wxDC
& WXUNUSED(dc
), const wxRichTextAttr
& WXUNUSED(attr
), const wxRect
& WXUNUSED(rect
))
8116 // Currently unimplemented. The intention is to store bitmaps by name in a media store associated
8117 // with the buffer. The store will allow retrieval from memory, disk or other means.
8121 /// Enumerate the standard bullet names currently supported
8122 bool wxRichTextStdRenderer::EnumerateStandardBulletNames(wxArrayString
& bulletNames
)
8124 bulletNames
.Add(wxTRANSLATE("standard/circle"));
8125 bulletNames
.Add(wxTRANSLATE("standard/circle-outline"));
8126 bulletNames
.Add(wxTRANSLATE("standard/square"));
8127 bulletNames
.Add(wxTRANSLATE("standard/diamond"));
8128 bulletNames
.Add(wxTRANSLATE("standard/triangle"));
8137 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBox
, wxRichTextParagraphLayoutBox
)
8139 wxRichTextBox::wxRichTextBox(wxRichTextObject
* parent
):
8140 wxRichTextParagraphLayoutBox(parent
)
8145 bool wxRichTextBox::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8150 // TODO: if the active object in the control, draw an indication.
8151 // We need to add the concept of active object, and not just focus object,
8152 // so we can apply commands (properties, delete, ...) to objects such as text boxes and images.
8153 // Ultimately we would like to be able to interactively resize an active object
8154 // using drag handles.
8155 return wxRichTextParagraphLayoutBox::Draw(dc
, range
, selection
, rect
, descent
, style
);
8159 void wxRichTextBox::Copy(const wxRichTextBox
& obj
)
8161 wxRichTextParagraphLayoutBox::Copy(obj
);
8164 // Edit properties via a GUI
8165 bool wxRichTextBox::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8167 wxRichTextObjectPropertiesDialog
boxDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Box Properties"));
8168 boxDlg
.SetAttributes(GetAttributes());
8170 if (boxDlg
.ShowModal() == wxID_OK
)
8172 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
8173 // indeterminate in the object.
8174 boxDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
8181 IMPLEMENT_DYNAMIC_CLASS(wxRichTextCell
, wxRichTextBox
)
8183 wxRichTextCell::wxRichTextCell(wxRichTextObject
* parent
):
8184 wxRichTextBox(parent
)
8189 bool wxRichTextCell::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8191 return wxRichTextBox::Draw(dc
, range
, selection
, rect
, descent
, style
);
8195 void wxRichTextCell::Copy(const wxRichTextCell
& obj
)
8197 wxRichTextBox::Copy(obj
);
8200 // Edit properties via a GUI
8201 bool wxRichTextCell::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8203 // We need to gather common attributes for all selected cells.
8205 wxRichTextTable
* table
= wxDynamicCast(GetParent(), wxRichTextTable
);
8206 bool multipleCells
= false;
8207 wxRichTextAttr attr
;
8209 if (table
&& buffer
&& buffer
->GetRichTextCtrl() && buffer
->GetRichTextCtrl()->GetSelection().IsValid() &&
8210 buffer
->GetRichTextCtrl()->GetSelection().GetContainer() == GetParent())
8212 wxRichTextAttr clashingAttr
, absentAttr
;
8213 const wxRichTextSelection
& sel
= buffer
->GetRichTextCtrl()->GetSelection();
8215 int selectedCellCount
= 0;
8216 for (i
= 0; i
< sel
.GetCount(); i
++)
8218 const wxRichTextRange
& range
= sel
[i
];
8219 wxRichTextCell
* cell
= table
->GetCell(range
.GetStart());
8222 wxRichTextAttr cellStyle
= cell
->GetAttributes();
8224 CollectStyle(attr
, cellStyle
, clashingAttr
, absentAttr
);
8226 selectedCellCount
++;
8229 multipleCells
= selectedCellCount
> 1;
8233 attr
= GetAttributes();
8238 caption
= _("Multiple Cell Properties");
8240 caption
= _("Cell Properties");
8242 wxRichTextObjectPropertiesDialog
cellDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, caption
);
8243 cellDlg
.SetAttributes(attr
);
8245 wxRichTextSizePage
* sizePage
= wxDynamicCast(cellDlg
.FindPage(CLASSINFO(wxRichTextSizePage
)), wxRichTextSizePage
);
8248 // We don't want position and floating controls for a cell.
8249 sizePage
->ShowPositionControls(false);
8250 sizePage
->ShowFloatingControls(false);
8253 if (cellDlg
.ShowModal() == wxID_OK
)
8257 const wxRichTextSelection
& sel
= buffer
->GetRichTextCtrl()->GetSelection();
8258 // Apply the style; we interpret indeterminate attributes as 'don't touch this attribute'
8259 // since it may represent clashing attributes across multiple objects.
8260 table
->SetCellStyle(sel
, attr
);
8263 // For a single object, indeterminate attributes set by the user should be reflected in the
8264 // actual object style, so pass the wxRICHTEXT_SETSTYLE_RESET flag to assign
8265 // the style directly instead of applying (which ignores indeterminate attributes,
8266 // leaving them as they were).
8267 cellDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
8274 WX_DEFINE_OBJARRAY(wxRichTextObjectPtrArrayArray
)
8276 IMPLEMENT_DYNAMIC_CLASS(wxRichTextTable
, wxRichTextBox
)
8278 wxRichTextTable::wxRichTextTable(wxRichTextObject
* parent
): wxRichTextBox(parent
)
8284 // Draws the object.
8285 bool wxRichTextTable::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8287 return wxRichTextBox::Draw(dc
, range
, selection
, rect
, descent
, style
);
8290 WX_DECLARE_OBJARRAY(wxRect
, wxRichTextRectArray
);
8291 WX_DEFINE_OBJARRAY(wxRichTextRectArray
);
8293 // Lays the object out. rect is the space available for layout. Often it will
8294 // be the specified overall space for this object, if trying to constrain
8295 // layout to a particular size, or it could be the total space available in the
8296 // parent. rect is the overall size, so we must subtract margins and padding.
8297 // to get the actual available space.
8298 bool wxRichTextTable::Layout(wxDC
& dc
, const wxRect
& rect
, const wxRect
& WXUNUSED(parentRect
), int style
)
8300 SetPosition(rect
.GetPosition());
8302 // TODO: the meaty bit. Calculate sizes of all cells and rows. Try to use
8303 // minimum size if within alloted size, then divide up remaining size
8304 // between rows/cols.
8307 wxRichTextBuffer
* buffer
= GetBuffer();
8308 if (buffer
) scale
= buffer
->GetScale();
8310 wxRect availableSpace
= GetAvailableContentArea(dc
, rect
);
8311 wxTextAttrDimensionConverter
converter(dc
, scale
, availableSpace
.GetSize());
8313 // If we have no fixed table size, and assuming we're not pushed for
8314 // space, then we don't have to try to stretch the table to fit the contents.
8315 bool stretchToFitTableWidth
= false;
8317 int tableWidth
= rect
.width
;
8318 if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
8320 tableWidth
= converter
.GetPixels(GetAttributes().GetTextBoxAttr().GetWidth());
8322 // Fixed table width, so we do want to stretch columns out if necessary.
8323 stretchToFitTableWidth
= true;
8325 // Shouldn't be able to exceed the size passed to this function
8326 tableWidth
= wxMin(rect
.width
, tableWidth
);
8329 // Get internal padding
8330 int paddingLeft
= 0, paddingTop
= 0;
8331 if (GetAttributes().GetTextBoxAttr().GetPadding().GetLeft().IsValid())
8332 paddingLeft
= converter
.GetPixels(GetAttributes().GetTextBoxAttr().GetPadding().GetLeft());
8333 if (GetAttributes().GetTextBoxAttr().GetPadding().GetTop().IsValid())
8334 paddingTop
= converter
.GetPixels(GetAttributes().GetTextBoxAttr().GetPadding().GetTop());
8336 // Assume that left and top padding are also used for inter-cell padding.
8337 int paddingX
= paddingLeft
;
8338 int paddingY
= paddingTop
;
8340 int totalLeftMargin
= 0, totalRightMargin
= 0, totalTopMargin
= 0, totalBottomMargin
= 0;
8341 GetTotalMargin(dc
, buffer
, GetAttributes(), totalLeftMargin
, totalRightMargin
, totalTopMargin
, totalBottomMargin
);
8343 // Internal table width - the area for content
8344 int internalTableWidth
= tableWidth
- totalLeftMargin
- totalRightMargin
;
8346 int rowCount
= m_cells
.GetCount();
8347 if (m_colCount
== 0 || rowCount
== 0)
8349 wxRect
overallRect(rect
.x
, rect
.y
, totalLeftMargin
+ totalRightMargin
, totalTopMargin
+ totalBottomMargin
);
8350 SetCachedSize(overallRect
.GetSize());
8352 // Zero content size
8353 SetMinSize(overallRect
.GetSize());
8354 SetMaxSize(GetMinSize());
8358 // The final calculated widths
8359 wxArrayInt colWidths
;
8360 colWidths
.Add(0, m_colCount
);
8362 wxArrayInt absoluteColWidths
;
8363 absoluteColWidths
.Add(0, m_colCount
);
8364 // wxArrayInt absoluteColWidthsSpanning(m_colCount);
8365 wxArrayInt percentageColWidths
;
8366 percentageColWidths
.Add(0, m_colCount
);
8367 // wxArrayInt percentageColWidthsSpanning(m_colCount);
8368 // These are only relevant when the first column contains spanning information.
8369 // wxArrayInt columnSpans(m_colCount); // Each contains 1 for non-spanning cell, > 1 for spanning cell.
8370 wxArrayInt maxColWidths
;
8371 maxColWidths
.Add(0, m_colCount
);
8372 wxArrayInt minColWidths
;
8373 minColWidths
.Add(0, m_colCount
);
8375 wxSize
tableSize(tableWidth
, 0);
8379 for (i
= 0; i
< m_colCount
; i
++)
8381 absoluteColWidths
[i
] = 0;
8382 // absoluteColWidthsSpanning[i] = 0;
8383 percentageColWidths
[i
] = -1;
8384 // percentageColWidthsSpanning[i] = -1;
8386 maxColWidths
[i
] = 0;
8387 minColWidths
[i
] = 0;
8388 // columnSpans[i] = 1;
8391 // (0) Determine which cells are visible according to spans
8393 // __________________
8398 // |------------------|
8399 // |__________________| 4
8401 // To calculate cell visibility:
8402 // First find all spanning cells. Build an array of span records with start x, y and end x, y.
8403 // Then for each cell, test whether we're within one of those cells, and unless we're at the start of
8404 // that cell, hide the cell.
8406 // We can also use this array to match the size of spanning cells to the grid. Or just do
8407 // this when we iterate through all cells.
8409 // 0.1: add spanning cells to an array
8410 wxRichTextRectArray rectArray
;
8411 for (j
= 0; j
< m_rowCount
; j
++)
8413 for (i
= 0; i
< m_colCount
; i
++)
8415 wxRichTextBox
* cell
= GetCell(j
, i
);
8416 int colSpan
= 1, rowSpan
= 1;
8417 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8418 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8419 if (cell
->GetProperties().HasProperty(wxT("rowspan")))
8420 rowSpan
= cell
->GetProperties().GetPropertyLong(wxT("rowspan"));
8421 if (colSpan
> 1 || rowSpan
> 1)
8423 rectArray
.Add(wxRect(i
, j
, colSpan
, rowSpan
));
8427 // 0.2: find which cells are subsumed by a spanning cell
8428 for (j
= 0; j
< m_rowCount
; j
++)
8430 for (i
= 0; i
< m_colCount
; i
++)
8432 wxRichTextBox
* cell
= GetCell(j
, i
);
8433 if (rectArray
.GetCount() == 0)
8439 int colSpan
= 1, rowSpan
= 1;
8440 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8441 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8442 if (cell
->GetProperties().HasProperty(wxT("rowspan")))
8443 rowSpan
= cell
->GetProperties().GetPropertyLong(wxT("rowspan"));
8444 if (colSpan
> 1 || rowSpan
> 1)
8446 // Assume all spanning cells are shown
8452 for (k
= 0; k
< (int) rectArray
.GetCount(); k
++)
8454 if (rectArray
[k
].Contains(wxPoint(i
, j
)))
8466 // TODO: find the first spanned cell in each row that spans the most columns and doesn't
8467 // overlap with a spanned cell starting at a previous column position.
8468 // This means we need to keep an array of rects so we can check. However
8469 // it does also mean that some spans simply may not be taken into account
8470 // where there are different spans happening on different rows. In these cases,
8471 // they will simply be as wide as their constituent columns.
8473 // (1) Do an initial layout for all cells to get minimum and maximum size, and get
8474 // the absolute or percentage width of each column.
8476 for (j
= 0; j
< m_rowCount
; j
++)
8478 // First get the overall margins so we can calculate percentage widths based on
8479 // the available content space for all cells on the row
8481 int overallRowContentMargin
= 0;
8482 int visibleCellCount
= 0;
8484 for (i
= 0; i
< m_colCount
; i
++)
8486 wxRichTextBox
* cell
= GetCell(j
, i
);
8487 if (cell
->IsShown())
8489 int cellTotalLeftMargin
= 0, cellTotalRightMargin
= 0, cellTotalTopMargin
= 0, cellTotalBottomMargin
= 0;
8490 GetTotalMargin(dc
, buffer
, cell
->GetAttributes(), cellTotalLeftMargin
, cellTotalRightMargin
, cellTotalTopMargin
, cellTotalBottomMargin
);
8492 overallRowContentMargin
+= (cellTotalLeftMargin
+ cellTotalRightMargin
);
8493 visibleCellCount
++;
8497 // Add in inter-cell padding
8498 overallRowContentMargin
+= ((visibleCellCount
-1) * paddingX
);
8500 int rowContentWidth
= internalTableWidth
- overallRowContentMargin
;
8501 wxSize
rowTableSize(rowContentWidth
, 0);
8502 wxTextAttrDimensionConverter
converter(dc
, scale
, rowTableSize
);
8504 for (i
= 0; i
< m_colCount
; i
++)
8506 wxRichTextBox
* cell
= GetCell(j
, i
);
8507 if (cell
->IsShown())
8510 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8511 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8513 // Lay out cell to find min/max widths
8514 cell
->Invalidate(wxRICHTEXT_ALL
);
8515 cell
->Layout(dc
, availableSpace
, availableSpace
, style
);
8519 int absoluteCellWidth
= -1;
8520 int percentageCellWidth
= -1;
8522 // I think we need to calculate percentages from the internal table size,
8523 // minus the padding between cells which we'll need to calculate from the
8524 // (number of VISIBLE cells - 1)*paddingX. Then percentages that add up to 100%
8525 // will add up to 100%. In CSS, the width specifies the cell's content rect width,
8526 // so if we want to conform to that we'll need to add in the overall cell margins.
8527 // However, this will make it difficult to specify percentages that add up to
8528 // 100% and still fit within the table width.
8529 // Let's say two cells have 50% width. They have 10 pixels of overall margin each.
8530 // The table content rect is 500 pixels and the inter-cell padding is 20 pixels.
8531 // If we're using internal content size for the width, we would calculate the
8532 // the overall cell width for n cells as:
8533 // (500 - 20*(n-1) - overallCellMargin1 - overallCellMargin2 - ...) * percentage / 100
8534 // + thisOverallCellMargin
8535 // = 500 - 20 - 10 - 10) * 0.5 + 10 = 240 pixels overall cell width.
8536 // Adding this back, we get 240 + 240 + 20 = 500 pixels.
8538 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
8540 int w
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetWidth());
8541 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
8543 percentageCellWidth
= w
;
8547 absoluteCellWidth
= w
;
8549 // Override absolute width with minimum width if necessary
8550 if (cell
->GetMinSize().x
> 0 && absoluteCellWidth
!=1 && cell
->GetMinSize().x
> absoluteCellWidth
)
8551 absoluteCellWidth
= cell
->GetMinSize().x
;
8554 if (absoluteCellWidth
!= -1)
8556 if (absoluteCellWidth
> absoluteColWidths
[i
])
8557 absoluteColWidths
[i
] = absoluteCellWidth
;
8560 if (percentageCellWidth
!= -1)
8562 if (percentageCellWidth
> percentageColWidths
[i
])
8563 percentageColWidths
[i
] = percentageCellWidth
;
8566 if (colSpan
== 1 && cell
->GetMinSize().x
&& cell
->GetMinSize().x
> minColWidths
[i
])
8567 minColWidths
[i
] = cell
->GetMinSize().x
;
8568 if (colSpan
== 1 && cell
->GetMaxSize().x
&& cell
->GetMaxSize().x
> maxColWidths
[i
])
8569 maxColWidths
[i
] = cell
->GetMaxSize().x
;
8575 // (2) Allocate initial column widths from minimum widths, absolute values and proportions
8576 // TODO: simply merge this into (1).
8577 for (i
= 0; i
< m_colCount
; i
++)
8579 if (absoluteColWidths
[i
] > 0)
8581 colWidths
[i
] = absoluteColWidths
[i
];
8583 else if (percentageColWidths
[i
] > 0)
8585 colWidths
[i
] = percentageColWidths
[i
];
8587 // This is rubbish - we calculated the absolute widths from percentages, so
8588 // we can't do it again here.
8589 //colWidths[i] = (int) (double(percentageColWidths[i]) * double(tableWidth) / 100.0 + 0.5);
8593 // (3) Process absolute or proportional widths of spanning columns,
8594 // now that we know what our fixed column widths are going to be.
8595 // Spanned cells will try to adjust columns so the span will fit.
8596 // Even existing fixed column widths can be expanded if necessary.
8597 // Actually, currently fixed columns widths aren't adjusted; instead,
8598 // the algorithm favours earlier rows and adjusts unspecified column widths
8599 // the first time only. After that, we can't know whether the column has been
8600 // specified explicitly or not. (We could make a note if necessary.)
8601 for (j
= 0; j
< m_rowCount
; j
++)
8603 // First get the overall margins so we can calculate percentage widths based on
8604 // the available content space for all cells on the row
8606 int overallRowContentMargin
= 0;
8607 int visibleCellCount
= 0;
8609 for (i
= 0; i
< m_colCount
; i
++)
8611 wxRichTextBox
* cell
= GetCell(j
, i
);
8612 if (cell
->IsShown())
8614 int cellTotalLeftMargin
= 0, cellTotalRightMargin
= 0, cellTotalTopMargin
= 0, cellTotalBottomMargin
= 0;
8615 GetTotalMargin(dc
, buffer
, cell
->GetAttributes(), cellTotalLeftMargin
, cellTotalRightMargin
, cellTotalTopMargin
, cellTotalBottomMargin
);
8617 overallRowContentMargin
+= (cellTotalLeftMargin
+ cellTotalRightMargin
);
8618 visibleCellCount
++;
8622 // Add in inter-cell padding
8623 overallRowContentMargin
+= ((visibleCellCount
-1) * paddingX
);
8625 int rowContentWidth
= internalTableWidth
- overallRowContentMargin
;
8626 wxSize
rowTableSize(rowContentWidth
, 0);
8627 wxTextAttrDimensionConverter
converter(dc
, scale
, rowTableSize
);
8629 for (i
= 0; i
< m_colCount
; i
++)
8631 wxRichTextBox
* cell
= GetCell(j
, i
);
8632 if (cell
->IsShown())
8635 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8636 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8640 int spans
= wxMin(colSpan
, m_colCount
- i
);
8644 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
8646 cellWidth
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetWidth());
8647 // Override absolute width with minimum width if necessary
8648 if (cell
->GetMinSize().x
> 0 && cellWidth
!=1 && cell
->GetMinSize().x
> cellWidth
)
8649 cellWidth
= cell
->GetMinSize().x
;
8653 // Do we want to do this? It's the only chance we get to
8654 // use the cell's min/max sizes, so we need to work out
8655 // how we're going to balance the unspecified spanning cell
8656 // width with the possibility more-constrained constituent cell widths.
8657 // Say there's a tiny bitmap giving it a max width of 10 pixels. We
8658 // don't want to constraint all the spanned columns to fit into this cell.
8659 // OK, let's say that if any of the constituent columns don't fit,
8660 // then we simply stop constraining the columns; instead, we'll just fit the spanning
8661 // cells to the columns later.
8662 cellWidth
= cell
->GetMinSize().x
;
8663 if (cell
->GetMaxSize().x
> cellWidth
)
8664 cellWidth
= cell
->GetMaxSize().x
;
8667 // Subtract the padding between cells
8668 int spanningWidth
= cellWidth
;
8669 spanningWidth
-= paddingX
* (spans
-1);
8671 if (spanningWidth
> 0)
8673 // Now share the spanning width between columns within that span
8674 // TODO: take into account min widths of columns within the span
8675 int spanningWidthLeft
= spanningWidth
;
8676 int stretchColCount
= 0;
8677 for (k
= i
; k
< (i
+spans
); k
++)
8679 if (colWidths
[k
] > 0) // absolute or proportional width has been specified
8680 spanningWidthLeft
-= colWidths
[k
];
8684 // Now divide what's left between the remaining columns
8686 if (stretchColCount
> 0)
8687 colShare
= spanningWidthLeft
/ stretchColCount
;
8688 int colShareRemainder
= spanningWidthLeft
- (colShare
* stretchColCount
);
8690 // If fixed-width columns are currently too big, then we'll later
8691 // stretch the spanned cell to fit.
8693 if (spanningWidthLeft
> 0)
8695 for (k
= i
; k
< (i
+spans
); k
++)
8697 if (colWidths
[k
] <= 0) // absolute or proportional width has not been specified
8699 int newWidth
= colShare
;
8700 if (k
== (i
+spans
-1))
8701 newWidth
+= colShareRemainder
; // ensure all pixels are filled
8702 colWidths
[k
] = newWidth
;
8713 // (4) Next, share any remaining space out between columns that have not yet been calculated.
8714 // TODO: take into account min widths of columns within the span
8715 int tableWidthMinusPadding
= internalTableWidth
- (m_colCount
-1)*paddingX
;
8716 int widthLeft
= tableWidthMinusPadding
;
8717 int stretchColCount
= 0;
8718 for (i
= 0; i
< m_colCount
; i
++)
8720 // TODO: we need to take into account min widths.
8721 // Subtract min width from width left, then
8722 // add the colShare to the min width
8723 if (colWidths
[i
] > 0) // absolute or proportional width has been specified
8724 widthLeft
-= colWidths
[i
];
8727 if (minColWidths
[i
] > 0)
8728 widthLeft
-= minColWidths
[i
];
8734 // Now divide what's left between the remaining columns
8736 if (stretchColCount
> 0)
8737 colShare
= widthLeft
/ stretchColCount
;
8738 int colShareRemainder
= widthLeft
- (colShare
* stretchColCount
);
8740 // Check we don't have enough space, in which case shrink all columns, overriding
8741 // any absolute/proportional widths
8742 // TODO: actually we would like to divide up the shrinkage according to size.
8743 // How do we calculate the proportions that will achieve this?
8744 // Could first choose an arbitrary value for stretching cells, and then calculate
8745 // factors to multiply each width by.
8746 // TODO: want to record this fact and pass to an iteration that tries e.g. min widths
8747 if (widthLeft
< 0 || (stretchToFitTableWidth
&& (stretchColCount
== 0)))
8749 colShare
= tableWidthMinusPadding
/ m_colCount
;
8750 colShareRemainder
= tableWidthMinusPadding
- (colShare
* m_colCount
);
8751 for (i
= 0; i
< m_colCount
; i
++)
8754 minColWidths
[i
] = 0;
8758 // We have to adjust the columns if either we need to shrink the
8759 // table to fit the parent/table width, or we explicitly set the
8760 // table width and need to stretch out the table.
8761 if (widthLeft
< 0 || stretchToFitTableWidth
)
8763 for (i
= 0; i
< m_colCount
; i
++)
8765 if (colWidths
[i
] <= 0) // absolute or proportional width has not been specified
8767 if (minColWidths
[i
] > 0)
8768 colWidths
[i
] = minColWidths
[i
] + colShare
;
8770 colWidths
[i
] = colShare
;
8771 if (i
== (m_colCount
-1))
8772 colWidths
[i
] += colShareRemainder
; // ensure all pixels are filled
8777 // TODO: if spanned cells have no specified or max width, make them the
8778 // as big as the columns they span. Do this for all spanned cells in all
8779 // rows, of course. Size any spanned cells left over at the end - even if they
8780 // have width > 0, make sure they're limited to the appropriate column edge.
8784 Sort out confusion between content width
8785 and overall width later. For now, assume we specify overall width.
8787 So, now we've laid out the table to fit into the given space
8788 and have used specified widths and minimum widths.
8790 Now we need to consider how we will try to take maximum width into account.
8794 // (??) TODO: take max width into account
8796 // (6) Lay out all cells again with the current values
8799 int y
= availableSpace
.y
;
8800 for (j
= 0; j
< m_rowCount
; j
++)
8802 int x
= availableSpace
.x
; // TODO: take into account centering etc.
8803 int maxCellHeight
= 0;
8804 int maxSpecifiedCellHeight
= 0;
8806 wxArrayInt actualWidths
;
8807 actualWidths
.Add(0, m_colCount
);
8809 wxTextAttrDimensionConverter
converter(dc
, scale
);
8810 for (i
= 0; i
< m_colCount
; i
++)
8812 wxRichTextCell
* cell
= GetCell(j
, i
);
8813 if (cell
->IsShown())
8815 // Get max specified cell height
8816 // Don't handle percentages for height
8817 if (cell
->GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && cell
->GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() != wxTEXT_ATTR_UNITS_PERCENTAGE
)
8819 int h
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetHeight());
8820 if (h
> maxSpecifiedCellHeight
)
8821 maxSpecifiedCellHeight
= h
;
8824 if (colWidths
[i
] > 0) // absolute or proportional width has been specified
8827 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8828 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8830 wxRect availableCellSpace
;
8832 // TODO: take into acount spans
8835 // Calculate the size of this spanning cell from its constituent columns
8837 int spans
= wxMin(colSpan
, m_colCount
- i
);
8838 for (k
= i
; k
< spans
; k
++)
8844 availableCellSpace
= wxRect(x
, y
, xx
, -1);
8847 availableCellSpace
= wxRect(x
, y
, colWidths
[i
], -1);
8849 // Store actual width so we can force cell to be the appropriate width on the final loop
8850 actualWidths
[i
] = availableCellSpace
.GetWidth();
8853 cell
->Invalidate(wxRICHTEXT_ALL
);
8854 cell
->Layout(dc
, availableCellSpace
, availableSpace
, style
);
8856 // TODO: use GetCachedSize().x to compute 'natural' size
8858 x
+= (availableCellSpace
.GetWidth() + paddingX
);
8859 if (cell
->GetCachedSize().y
> maxCellHeight
)
8860 maxCellHeight
= cell
->GetCachedSize().y
;
8865 maxCellHeight
= wxMax(maxCellHeight
, maxSpecifiedCellHeight
);
8867 for (i
= 0; i
< m_colCount
; i
++)
8869 wxRichTextCell
* cell
= GetCell(j
, i
);
8870 if (cell
->IsShown())
8872 wxRect availableCellSpace
= wxRect(cell
->GetPosition(), wxSize(actualWidths
[i
], maxCellHeight
));
8873 // Lay out cell with new height
8874 cell
->Invalidate(wxRICHTEXT_ALL
);
8875 cell
->Layout(dc
, availableCellSpace
, availableSpace
, style
);
8877 // Make sure the cell size really is the appropriate size,
8878 // not the calculated box size
8879 cell
->SetCachedSize(wxSize(actualWidths
[i
], maxCellHeight
));
8881 maxRight
= wxMax(maxRight
, cell
->GetPosition().x
+ cell
->GetCachedSize().x
);
8886 if (j
< (m_rowCount
-1))
8890 // We need to add back the margins etc.
8892 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
8893 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxRight
- availableSpace
.x
, y
- availableSpace
.y
));
8894 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
8895 SetCachedSize(marginRect
.GetSize());
8898 // TODO: calculate max size
8900 SetMaxSize(GetCachedSize());
8903 // TODO: calculate min size
8905 SetMinSize(GetCachedSize());
8908 // TODO: currently we use either a fixed table width or the parent's size.
8909 // We also want to be able to calculate the table width from its content,
8910 // whether using fixed column widths or cell content min/max width.
8911 // Probably need a boolean flag to say whether we need to stretch cells
8912 // to fit the table width, or to simply use min/max cell widths. The
8913 // trouble with this is that if cell widths are not specified, they
8914 // will be tiny; we could use arbitrary defaults but this seems unsatisfactory.
8915 // Anyway, ignoring that problem, we probably need to factor layout into a function
8916 // that can can calculate the maximum unconstrained layout in case table size is
8917 // not specified. Then LayoutToBestSize() can choose to use either parent size to
8918 // constrain Layout(), or the previously-calculated max size to constraint layout.
8923 // Finds the absolute position and row height for the given character position
8924 bool wxRichTextTable::FindPosition(wxDC
& dc
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
8926 wxRichTextCell
* child
= GetCell(index
+1);
8929 // Find the position at the start of the child cell, since the table doesn't
8930 // have any caret position of its own.
8931 return child
->FindPosition(dc
, -1, pt
, height
, forceLineStart
);
8937 // Get the cell at the given character position (in the range of the table).
8938 wxRichTextCell
* wxRichTextTable::GetCell(long pos
) const
8940 int row
= 0, col
= 0;
8941 if (GetCellRowColumnPosition(pos
, row
, col
))
8943 return GetCell(row
, col
);
8949 // Get the row/column for a given character position
8950 bool wxRichTextTable::GetCellRowColumnPosition(long pos
, int& row
, int& col
) const
8952 if (m_colCount
== 0 || m_rowCount
== 0)
8955 row
= (int) (pos
/ m_colCount
);
8956 col
= pos
- (row
* m_colCount
);
8958 wxASSERT(row
< m_rowCount
&& col
< m_colCount
);
8960 if (row
< m_rowCount
&& col
< m_colCount
)
8966 // Calculate range, taking row/cell ordering into account instead of relying
8967 // on list ordering.
8968 void wxRichTextTable::CalculateRange(long start
, long& end
)
8970 long current
= start
;
8971 long lastEnd
= current
;
8980 for (i
= 0; i
< m_rowCount
; i
++)
8982 for (j
= 0; j
< m_colCount
; j
++)
8984 wxRichTextCell
* child
= GetCell(i
, j
);
8989 child
->CalculateRange(current
, childEnd
);
8992 current
= childEnd
+ 1;
8997 // A top-level object always has a range of size 1,
8998 // because its children don't count at this level.
9000 m_range
.SetRange(start
, start
);
9002 // An object with no children has zero length
9003 if (m_children
.GetCount() == 0)
9005 m_ownRange
.SetRange(0, lastEnd
);
9008 // Gets the range size.
9009 bool wxRichTextTable::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
9011 return wxRichTextBox::GetRangeSize(range
, size
, descent
, dc
, flags
, position
, partialExtents
);
9014 // Deletes content in the given range.
9015 bool wxRichTextTable::DeleteRange(const wxRichTextRange
& WXUNUSED(range
))
9017 // TODO: implement deletion of cells
9021 // Gets any text in this object for the given range.
9022 wxString
wxRichTextTable::GetTextForRange(const wxRichTextRange
& range
) const
9024 return wxRichTextBox::GetTextForRange(range
);
9027 // Copies this object.
9028 void wxRichTextTable::Copy(const wxRichTextTable
& obj
)
9030 wxRichTextBox::Copy(obj
);
9034 m_rowCount
= obj
.m_rowCount
;
9035 m_colCount
= obj
.m_colCount
;
9037 m_cells
.Add(wxRichTextObjectPtrArray(), m_rowCount
);
9040 for (i
= 0; i
< m_rowCount
; i
++)
9042 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
9043 for (j
= 0; j
< m_colCount
; j
++)
9045 wxRichTextCell
* cell
= wxDynamicCast(obj
.GetCell(i
, j
)->Clone(), wxRichTextCell
);
9053 void wxRichTextTable::ClearTable()
9059 bool wxRichTextTable::CreateTable(int rows
, int cols
)
9066 m_cells
.Add(wxRichTextObjectPtrArray(), rows
);
9069 for (i
= 0; i
< rows
; i
++)
9071 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
9072 for (j
= 0; j
< cols
; j
++)
9074 wxRichTextCell
* cell
= new wxRichTextCell
;
9076 cell
->AddParagraph(wxEmptyString
);
9085 wxRichTextCell
* wxRichTextTable::GetCell(int row
, int col
) const
9087 wxASSERT(row
< m_rowCount
);
9088 wxASSERT(col
< m_colCount
);
9090 if (row
< m_rowCount
&& col
< m_colCount
)
9092 wxRichTextObjectPtrArray
& colArray
= m_cells
[row
];
9093 wxRichTextObject
* obj
= colArray
[col
];
9094 return wxDynamicCast(obj
, wxRichTextCell
);
9100 // Returns a selection object specifying the selections between start and end character positions.
9101 // For example, a table would deduce what cells (of range length 1) are selected when dragging across the table.
9102 wxRichTextSelection
wxRichTextTable::GetSelection(long start
, long end
) const
9104 wxRichTextSelection selection
;
9105 selection
.SetContainer((wxRichTextTable
*) this);
9114 wxASSERT( start
>= 0 && end
< (m_colCount
* m_rowCount
));
9116 if (end
>= (m_colCount
* m_rowCount
))
9119 // We need to find the rectangle of cells that is described by the rectangle
9120 // with start, end as the diagonal. Make sure we don't add cells that are
9121 // not currenty visible because they are overlapped by spanning cells.
9123 --------------------------
9124 | 0 | 1 | 2 | 3 | 4 |
9125 --------------------------
9126 | 5 | 6 | 7 | 8 | 9 |
9127 --------------------------
9128 | 10 | 11 | 12 | 13 | 14 |
9129 --------------------------
9130 | 15 | 16 | 17 | 18 | 19 |
9131 --------------------------
9133 Let's say we select 6 -> 18.
9135 Left and right edge cols of rectangle are 1 and 3 inclusive. Find least/greatest to find
9136 which is left and which is right.
9138 Top and bottom edge rows are 1 and 3 inclusive. Again, find least/greatest to find top and bottom.
9140 Now go through rows from 1 to 3 and only add cells that are (a) within above column range
9146 int leftCol
= start
- m_colCount
* int(start
/m_colCount
);
9147 int rightCol
= end
- m_colCount
* int(end
/m_colCount
);
9149 int topRow
= int(start
/m_colCount
);
9150 int bottomRow
= int(end
/m_colCount
);
9152 if (leftCol
> rightCol
)
9159 if (topRow
> bottomRow
)
9161 int tmp
= bottomRow
;
9167 for (i
= topRow
; i
<= bottomRow
; i
++)
9169 for (j
= leftCol
; j
<= rightCol
; j
++)
9171 wxRichTextCell
* cell
= GetCell(i
, j
);
9172 if (cell
&& cell
->IsShown())
9173 selection
.Add(cell
->GetRange());
9180 // Sets the attributes for the cells specified by the selection.
9181 bool wxRichTextTable::SetCellStyle(const wxRichTextSelection
& selection
, const wxRichTextAttr
& style
, int flags
)
9183 if (selection
.GetContainer() != this)
9186 wxRichTextBuffer
* buffer
= GetBuffer();
9187 bool haveControl
= (buffer
&& buffer
->GetRichTextCtrl() != NULL
);
9188 bool withUndo
= haveControl
&& ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
9191 buffer
->BeginBatchUndo(_("Set Cell Style"));
9193 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
9196 wxRichTextCell
* cell
= wxDynamicCast(node
->GetData(), wxRichTextCell
);
9197 if (cell
&& selection
.WithinSelection(cell
->GetRange().GetStart()))
9198 SetStyle(cell
, style
, flags
);
9199 node
= node
->GetNext();
9202 // Do action, or delay it until end of batch.
9204 buffer
->EndBatchUndo();
9209 bool wxRichTextTable::DeleteRows(int startRow
, int noRows
)
9211 wxASSERT((startRow
+ noRows
) < m_rowCount
);
9212 if ((startRow
+ noRows
) >= m_rowCount
)
9216 for (i
= startRow
; i
< (startRow
+noRows
); i
++)
9218 wxRichTextObjectPtrArray
& colArray
= m_cells
[startRow
];
9219 for (j
= 0; j
< (int) colArray
.GetCount(); j
++)
9221 wxRichTextObject
* cell
= colArray
[j
];
9222 RemoveChild(cell
, true);
9225 // Keep deleting at the same position, since we move all
9227 m_cells
.RemoveAt(startRow
);
9230 m_rowCount
= m_rowCount
- noRows
;
9235 bool wxRichTextTable::DeleteColumns(int startCol
, int noCols
)
9237 wxASSERT((startCol
+ noCols
) < m_colCount
);
9238 if ((startCol
+ noCols
) >= m_colCount
)
9241 bool deleteRows
= (noCols
== m_colCount
);
9244 for (i
= 0; i
< m_rowCount
; i
++)
9246 wxRichTextObjectPtrArray
& colArray
= m_cells
[deleteRows
? 0 : i
];
9247 for (j
= startCol
; j
< (startCol
+noCols
); j
++)
9249 wxRichTextObject
* cell
= colArray
[j
];
9250 RemoveChild(cell
, true);
9254 m_cells
.RemoveAt(0);
9259 m_colCount
= m_colCount
- noCols
;
9264 bool wxRichTextTable::AddRows(int startRow
, int noRows
, const wxRichTextAttr
& attr
)
9266 wxASSERT(startRow
<= m_rowCount
);
9267 if (startRow
> m_rowCount
)
9271 for (i
= 0; i
< noRows
; i
++)
9274 if (startRow
== m_rowCount
)
9276 m_cells
.Add(wxRichTextObjectPtrArray());
9277 idx
= m_cells
.GetCount() - 1;
9281 m_cells
.Insert(wxRichTextObjectPtrArray(), startRow
+i
);
9285 wxRichTextObjectPtrArray
& colArray
= m_cells
[idx
];
9286 for (j
= 0; j
< m_colCount
; j
++)
9288 wxRichTextCell
* cell
= new wxRichTextCell
;
9289 cell
->GetAttributes() = attr
;
9296 m_rowCount
= m_rowCount
+ noRows
;
9300 bool wxRichTextTable::AddColumns(int startCol
, int noCols
, const wxRichTextAttr
& attr
)
9302 wxASSERT(startCol
<= m_colCount
);
9303 if (startCol
> m_colCount
)
9307 for (i
= 0; i
< m_rowCount
; i
++)
9309 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
9310 for (j
= 0; j
< noCols
; j
++)
9312 wxRichTextCell
* cell
= new wxRichTextCell
;
9313 cell
->GetAttributes() = attr
;
9317 if (startCol
== m_colCount
)
9320 colArray
.Insert(cell
, startCol
+j
);
9324 m_colCount
= m_colCount
+ noCols
;
9329 // Edit properties via a GUI
9330 bool wxRichTextTable::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
9332 wxRichTextObjectPropertiesDialog
boxDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Table Properties"));
9333 boxDlg
.SetAttributes(GetAttributes());
9335 if (boxDlg
.ShowModal() == wxID_OK
)
9337 boxDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
9345 * Module to initialise and clean up handlers
9348 class wxRichTextModule
: public wxModule
9350 DECLARE_DYNAMIC_CLASS(wxRichTextModule
)
9352 wxRichTextModule() {}
9355 wxRichTextBuffer::SetRenderer(new wxRichTextStdRenderer
);
9356 wxRichTextBuffer::InitStandardHandlers();
9357 wxRichTextParagraph::InitDefaultTabs();
9362 wxRichTextBuffer::CleanUpHandlers();
9363 wxRichTextDecimalToRoman(-1);
9364 wxRichTextParagraph::ClearDefaultTabs();
9365 wxRichTextCtrl::ClearAvailableFontNames();
9366 wxRichTextBuffer::SetRenderer(NULL
);
9370 IMPLEMENT_DYNAMIC_CLASS(wxRichTextModule
, wxModule
)
9373 // If the richtext lib is dynamically loaded after the app has already started
9374 // (such as from wxPython) then the built-in module system will not init this
9375 // module. Provide this function to do it manually.
9376 void wxRichTextModuleInit()
9378 wxModule
* module = new wxRichTextModule
;
9380 wxModule::RegisterModule(module);
9385 * Commands for undo/redo
9389 wxRichTextCommand::wxRichTextCommand(const wxString
& name
, wxRichTextCommandId id
, wxRichTextBuffer
* buffer
,
9390 wxRichTextParagraphLayoutBox
* container
, wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
): wxCommand(true, name
)
9392 /* wxRichTextAction* action = */ new wxRichTextAction(this, name
, id
, buffer
, container
, ctrl
, ignoreFirstTime
);
9395 wxRichTextCommand::wxRichTextCommand(const wxString
& name
): wxCommand(true, name
)
9399 wxRichTextCommand::~wxRichTextCommand()
9404 void wxRichTextCommand::AddAction(wxRichTextAction
* action
)
9406 if (!m_actions
.Member(action
))
9407 m_actions
.Append(action
);
9410 bool wxRichTextCommand::Do()
9412 for (wxList::compatibility_iterator node
= m_actions
.GetFirst(); node
; node
= node
->GetNext())
9414 wxRichTextAction
* action
= (wxRichTextAction
*) node
->GetData();
9421 bool wxRichTextCommand::Undo()
9423 for (wxList::compatibility_iterator node
= m_actions
.GetLast(); node
; node
= node
->GetPrevious())
9425 wxRichTextAction
* action
= (wxRichTextAction
*) node
->GetData();
9432 void wxRichTextCommand::ClearActions()
9434 WX_CLEAR_LIST(wxList
, m_actions
);
9442 wxRichTextAction::wxRichTextAction(wxRichTextCommand
* cmd
, const wxString
& name
, wxRichTextCommandId id
,
9443 wxRichTextBuffer
* buffer
, wxRichTextParagraphLayoutBox
* container
,
9444 wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
)
9448 m_containerAddress
.Create(buffer
, container
);
9449 m_ignoreThis
= ignoreFirstTime
;
9454 m_newParagraphs
.SetDefaultStyle(buffer
->GetDefaultStyle());
9455 m_newParagraphs
.SetBasicStyle(buffer
->GetBasicStyle());
9457 cmd
->AddAction(this);
9460 wxRichTextAction::~wxRichTextAction()
9466 // Returns the container that this action refers to, using the container address and top-level buffer.
9467 wxRichTextParagraphLayoutBox
* wxRichTextAction::GetContainer() const
9469 wxRichTextParagraphLayoutBox
* container
= wxDynamicCast(GetContainerAddress().GetObject(m_buffer
), wxRichTextParagraphLayoutBox
);
9474 void wxRichTextAction::CalculateRefreshOptimizations(wxArrayInt
& optimizationLineCharPositions
, wxArrayInt
& optimizationLineYPositions
)
9476 // Store a list of line start character and y positions so we can figure out which area
9477 // we need to refresh
9479 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9480 wxRichTextParagraphLayoutBox
* container
= GetContainer();
9481 wxASSERT(container
!= NULL
);
9485 // NOTE: we're assuming that the buffer is laid out correctly at this point.
9486 // If we had several actions, which only invalidate and leave layout until the
9487 // paint handler is called, then this might not be true. So we may need to switch
9488 // optimisation on only when we're simply adding text and not simultaneously
9489 // deleting a selection, for example. Or, we make sure the buffer is laid out correctly
9490 // first, but of course this means we'll be doing it twice.
9491 if (!m_buffer
->IsDirty() && m_ctrl
) // can only do optimisation if the buffer is already laid out correctly
9493 wxSize clientSize
= m_ctrl
->GetClientSize();
9494 wxPoint firstVisiblePt
= m_ctrl
->GetFirstVisiblePoint();
9495 int lastY
= firstVisiblePt
.y
+ clientSize
.y
;
9497 wxRichTextParagraph
* para
= container
->GetParagraphAtPosition(GetRange().GetStart());
9498 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(para
);
9501 wxRichTextParagraph
* child
= (wxRichTextParagraph
*) node
->GetData();
9502 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
9505 wxRichTextLine
* line
= node2
->GetData();
9506 wxPoint pt
= line
->GetAbsolutePosition();
9507 wxRichTextRange range
= line
->GetAbsoluteRange();
9511 node2
= wxRichTextLineList::compatibility_iterator();
9512 node
= wxRichTextObjectList::compatibility_iterator();
9514 else if (range
.GetStart() > GetPosition() && pt
.y
>= firstVisiblePt
.y
)
9516 optimizationLineCharPositions
.Add(range
.GetStart());
9517 optimizationLineYPositions
.Add(pt
.y
);
9521 node2
= node2
->GetNext();
9525 node
= node
->GetNext();
9531 bool wxRichTextAction::Do()
9533 m_buffer
->Modify(true);
9535 wxRichTextParagraphLayoutBox
* container
= GetContainer();
9536 wxASSERT(container
!= NULL
);
9542 case wxRICHTEXT_INSERT
:
9544 // Store a list of line start character and y positions so we can figure out which area
9545 // we need to refresh
9546 wxArrayInt optimizationLineCharPositions
;
9547 wxArrayInt optimizationLineYPositions
;
9549 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9550 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
9553 container
->InsertFragment(GetRange().GetStart(), m_newParagraphs
);
9554 container
->UpdateRanges();
9556 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9557 // Layout() would stop prematurely at the top level.
9558 container
->InvalidateHierarchy(wxRichTextRange(wxMax(0, GetRange().GetStart()-1), GetRange().GetEnd()));
9560 long newCaretPosition
= GetPosition() + m_newParagraphs
.GetOwnRange().GetLength();
9562 // Character position to caret position
9563 newCaretPosition
--;
9565 // Don't take into account the last newline
9566 if (m_newParagraphs
.GetPartialParagraph())
9567 newCaretPosition
--;
9569 if (m_newParagraphs
.GetChildren().GetCount() > 1)
9571 wxRichTextObject
* p
= (wxRichTextObject
*) m_newParagraphs
.GetChildren().GetLast()->GetData();
9572 if (p
->GetRange().GetLength() == 1)
9573 newCaretPosition
--;
9576 newCaretPosition
= wxMin(newCaretPosition
, (container
->GetOwnRange().GetEnd()-1));
9578 UpdateAppearance(newCaretPosition
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */);
9580 wxRichTextEvent
cmdEvent(
9581 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED
,
9582 m_ctrl
? m_ctrl
->GetId() : -1);
9583 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9584 cmdEvent
.SetRange(GetRange());
9585 cmdEvent
.SetPosition(GetRange().GetStart());
9586 cmdEvent
.SetContainer(container
);
9588 m_buffer
->SendEvent(cmdEvent
);
9592 case wxRICHTEXT_DELETE
:
9594 wxArrayInt optimizationLineCharPositions
;
9595 wxArrayInt optimizationLineYPositions
;
9597 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9598 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
9601 container
->DeleteRange(GetRange());
9602 container
->UpdateRanges();
9603 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9604 // Layout() would stop prematurely at the top level.
9605 container
->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
9607 long caretPos
= GetRange().GetStart()-1;
9608 if (caretPos
>= container
->GetOwnRange().GetEnd())
9611 UpdateAppearance(caretPos
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */);
9613 wxRichTextEvent
cmdEvent(
9614 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED
,
9615 m_ctrl
? m_ctrl
->GetId() : -1);
9616 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9617 cmdEvent
.SetRange(GetRange());
9618 cmdEvent
.SetPosition(GetRange().GetStart());
9619 cmdEvent
.SetContainer(container
);
9621 m_buffer
->SendEvent(cmdEvent
);
9625 case wxRICHTEXT_CHANGE_STYLE
:
9626 case wxRICHTEXT_CHANGE_PROPERTIES
:
9628 ApplyParagraphs(GetNewParagraphs());
9630 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9631 // Layout() would stop prematurely at the top level.
9632 container
->InvalidateHierarchy(GetRange());
9634 UpdateAppearance(GetPosition());
9636 wxRichTextEvent
cmdEvent(
9637 m_cmdId
== wxRICHTEXT_CHANGE_STYLE
? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
: wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED
,
9638 m_ctrl
? m_ctrl
->GetId() : -1);
9639 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9640 cmdEvent
.SetRange(GetRange());
9641 cmdEvent
.SetPosition(GetRange().GetStart());
9642 cmdEvent
.SetContainer(container
);
9644 m_buffer
->SendEvent(cmdEvent
);
9648 case wxRICHTEXT_CHANGE_ATTRIBUTES
:
9650 wxRichTextObject
* obj
= m_objectAddress
.GetObject(m_buffer
); // container->GetChildAtPosition(GetRange().GetStart());
9653 wxRichTextAttr oldAttr
= obj
->GetAttributes();
9654 obj
->GetAttributes() = m_attributes
;
9655 m_attributes
= oldAttr
;
9658 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9659 // Layout() would stop prematurely at the top level.
9660 container
->InvalidateHierarchy(GetRange());
9662 UpdateAppearance(GetPosition());
9664 wxRichTextEvent
cmdEvent(
9665 wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
,
9666 m_ctrl
? m_ctrl
->GetId() : -1);
9667 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9668 cmdEvent
.SetRange(GetRange());
9669 cmdEvent
.SetPosition(GetRange().GetStart());
9670 cmdEvent
.SetContainer(container
);
9672 m_buffer
->SendEvent(cmdEvent
);
9676 case wxRICHTEXT_CHANGE_OBJECT
:
9678 wxRichTextObject
* obj
= m_objectAddress
.GetObject(m_buffer
);
9679 // wxRichTextObject* obj = container->GetChildAtPosition(GetRange().GetStart());
9680 if (obj
&& m_object
)
9682 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(obj
);
9685 wxRichTextObject
* obj
= node
->GetData();
9686 node
->SetData(m_object
);
9691 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9692 // Layout() would stop prematurely at the top level.
9693 container
->InvalidateHierarchy(GetRange());
9695 UpdateAppearance(GetPosition());
9697 // TODO: send new kind of modification event
9708 bool wxRichTextAction::Undo()
9710 m_buffer
->Modify(true);
9712 wxRichTextParagraphLayoutBox
* container
= GetContainer();
9713 wxASSERT(container
!= NULL
);
9719 case wxRICHTEXT_INSERT
:
9721 wxArrayInt optimizationLineCharPositions
;
9722 wxArrayInt optimizationLineYPositions
;
9724 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9725 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
9728 container
->DeleteRange(GetRange());
9729 container
->UpdateRanges();
9730 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9731 // Layout() would stop prematurely at the top level.
9732 container
->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
9734 long newCaretPosition
= GetPosition() - 1;
9736 UpdateAppearance(newCaretPosition
, true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */);
9738 wxRichTextEvent
cmdEvent(
9739 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED
,
9740 m_ctrl
? m_ctrl
->GetId() : -1);
9741 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9742 cmdEvent
.SetRange(GetRange());
9743 cmdEvent
.SetPosition(GetRange().GetStart());
9744 cmdEvent
.SetContainer(container
);
9746 m_buffer
->SendEvent(cmdEvent
);
9750 case wxRICHTEXT_DELETE
:
9752 wxArrayInt optimizationLineCharPositions
;
9753 wxArrayInt optimizationLineYPositions
;
9755 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9756 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
9759 container
->InsertFragment(GetRange().GetStart(), m_oldParagraphs
);
9760 container
->UpdateRanges();
9761 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9762 // Layout() would stop prematurely at the top level.
9763 container
->InvalidateHierarchy(GetRange());
9765 UpdateAppearance(GetPosition(), true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */);
9767 wxRichTextEvent
cmdEvent(
9768 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED
,
9769 m_ctrl
? m_ctrl
->GetId() : -1);
9770 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9771 cmdEvent
.SetRange(GetRange());
9772 cmdEvent
.SetPosition(GetRange().GetStart());
9773 cmdEvent
.SetContainer(container
);
9775 m_buffer
->SendEvent(cmdEvent
);
9779 case wxRICHTEXT_CHANGE_STYLE
:
9780 case wxRICHTEXT_CHANGE_PROPERTIES
:
9782 ApplyParagraphs(GetOldParagraphs());
9783 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9784 // Layout() would stop prematurely at the top level.
9785 container
->InvalidateHierarchy(GetRange());
9787 UpdateAppearance(GetPosition());
9789 wxRichTextEvent
cmdEvent(
9790 m_cmdId
== wxRICHTEXT_CHANGE_STYLE
? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
: wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED
,
9791 m_ctrl
? m_ctrl
->GetId() : -1);
9792 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9793 cmdEvent
.SetRange(GetRange());
9794 cmdEvent
.SetPosition(GetRange().GetStart());
9795 cmdEvent
.SetContainer(container
);
9797 m_buffer
->SendEvent(cmdEvent
);
9801 case wxRICHTEXT_CHANGE_ATTRIBUTES
:
9802 case wxRICHTEXT_CHANGE_OBJECT
:
9813 /// Update the control appearance
9814 void wxRichTextAction::UpdateAppearance(long caretPosition
, bool sendUpdateEvent
, wxArrayInt
* optimizationLineCharPositions
, wxArrayInt
* optimizationLineYPositions
, bool isDoCmd
)
9816 wxRichTextParagraphLayoutBox
* container
= GetContainer();
9817 wxASSERT(container
!= NULL
);
9823 m_ctrl
->SetFocusObject(container
);
9824 m_ctrl
->SetCaretPosition(caretPosition
);
9826 if (!m_ctrl
->IsFrozen())
9828 wxRect containerRect
= container
->GetRect();
9830 m_ctrl
->LayoutContent();
9832 // Refresh everything if there were floating objects or the container changed size
9833 // (we can't yet optimize in these cases, since more complex interaction with other content occurs)
9834 if (container
->GetFloatingObjectCount() > 0 || (container
->GetParent() && containerRect
!= container
->GetRect()))
9836 m_ctrl
->Refresh(false);
9840 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9841 // Find refresh rectangle if we are in a position to optimise refresh
9842 if ((m_cmdId
== wxRICHTEXT_INSERT
|| m_cmdId
== wxRICHTEXT_DELETE
) && optimizationLineCharPositions
)
9846 wxSize clientSize
= m_ctrl
->GetClientSize();
9847 wxPoint firstVisiblePt
= m_ctrl
->GetFirstVisiblePoint();
9849 // Start/end positions
9851 int lastY
= firstVisiblePt
.y
+ clientSize
.y
;
9853 bool foundEnd
= false;
9855 // position offset - how many characters were inserted
9856 int positionOffset
= GetRange().GetLength();
9858 // Determine whether this is Do or Undo, and adjust positionOffset accordingly
9859 if ((m_cmdId
== wxRICHTEXT_DELETE
&& isDoCmd
) || (m_cmdId
== wxRICHTEXT_INSERT
&& !isDoCmd
))
9860 positionOffset
= - positionOffset
;
9862 // find the first line which is being drawn at the same position as it was
9863 // before. Since we're talking about a simple insertion, we can assume
9864 // that the rest of the window does not need to be redrawn.
9866 wxRichTextParagraph
* para
= container
->GetParagraphAtPosition(GetPosition());
9867 // Since we support floating layout, we should redraw the whole para instead of just
9868 // the first line touching the invalid range.
9871 firstY
= para
->GetPosition().y
;
9874 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(para
);
9877 wxRichTextParagraph
* child
= (wxRichTextParagraph
*) node
->GetData();
9878 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
9881 wxRichTextLine
* line
= node2
->GetData();
9882 wxPoint pt
= line
->GetAbsolutePosition();
9883 wxRichTextRange range
= line
->GetAbsoluteRange();
9885 // we want to find the first line that is in the same position
9886 // as before. This will mean we're at the end of the changed text.
9888 if (pt
.y
> lastY
) // going past the end of the window, no more info
9890 node2
= wxRichTextLineList::compatibility_iterator();
9891 node
= wxRichTextObjectList::compatibility_iterator();
9893 // Detect last line in the buffer
9894 else if (!node2
->GetNext() && para
->GetRange().Contains(container
->GetOwnRange().GetEnd()))
9896 // If deleting text, make sure we refresh below as well as above
9897 if (positionOffset
>= 0)
9900 lastY
= pt
.y
+ line
->GetSize().y
;
9903 node2
= wxRichTextLineList::compatibility_iterator();
9904 node
= wxRichTextObjectList::compatibility_iterator();
9910 // search for this line being at the same position as before
9911 for (i
= 0; i
< optimizationLineCharPositions
->GetCount(); i
++)
9913 if (((*optimizationLineCharPositions
)[i
] + positionOffset
== range
.GetStart()) &&
9914 ((*optimizationLineYPositions
)[i
] == pt
.y
))
9916 // Stop, we're now the same as we were
9921 node2
= wxRichTextLineList::compatibility_iterator();
9922 node
= wxRichTextObjectList::compatibility_iterator();
9930 node2
= node2
->GetNext();
9934 node
= node
->GetNext();
9937 firstY
= wxMax(firstVisiblePt
.y
, firstY
);
9939 lastY
= firstVisiblePt
.y
+ clientSize
.y
;
9941 // Convert to device coordinates
9942 wxRect
rect(m_ctrl
->GetPhysicalPoint(wxPoint(firstVisiblePt
.x
, firstY
)), wxSize(clientSize
.x
, lastY
- firstY
));
9943 m_ctrl
->RefreshRect(rect
);
9947 m_ctrl
->Refresh(false);
9949 m_ctrl
->PositionCaret();
9951 // This causes styles to persist when doing programmatic
9952 // content creation except when Freeze/Thaw is used, so
9953 // disable this and check for the consequences.
9954 // m_ctrl->SetDefaultStyleToCursorStyle();
9956 if (sendUpdateEvent
)
9957 wxTextCtrl::SendTextUpdatedEvent(m_ctrl
);
9962 /// Replace the buffer paragraphs with the new ones.
9963 void wxRichTextAction::ApplyParagraphs(const wxRichTextParagraphLayoutBox
& fragment
)
9965 wxRichTextParagraphLayoutBox
* container
= GetContainer();
9966 wxASSERT(container
!= NULL
);
9970 wxRichTextObjectList::compatibility_iterator node
= fragment
.GetChildren().GetFirst();
9973 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
9974 wxASSERT (para
!= NULL
);
9976 // We'll replace the existing paragraph by finding the paragraph at this position,
9977 // delete its node data, and setting a copy as the new node data.
9978 // TODO: make more efficient by simply swapping old and new paragraph objects.
9980 wxRichTextParagraph
* existingPara
= container
->GetParagraphAtPosition(para
->GetRange().GetStart());
9983 wxRichTextObjectList::compatibility_iterator bufferParaNode
= container
->GetChildren().Find(existingPara
);
9986 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(*para
);
9987 newPara
->SetParent(container
);
9989 bufferParaNode
->SetData(newPara
);
9991 delete existingPara
;
9995 node
= node
->GetNext();
10002 * This stores beginning and end positions for a range of data.
10005 WX_DEFINE_OBJARRAY(wxRichTextRangeArray
);
10007 /// Limit this range to be within 'range'
10008 bool wxRichTextRange::LimitTo(const wxRichTextRange
& range
)
10010 if (m_start
< range
.m_start
)
10011 m_start
= range
.m_start
;
10013 if (m_end
> range
.m_end
)
10014 m_end
= range
.m_end
;
10020 * wxRichTextImage implementation
10021 * This object represents an image.
10024 IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage
, wxRichTextObject
)
10026 wxRichTextImage::wxRichTextImage(const wxImage
& image
, wxRichTextObject
* parent
, wxRichTextAttr
* charStyle
):
10027 wxRichTextObject(parent
)
10029 m_imageBlock
.MakeImageBlockDefaultQuality(image
, wxBITMAP_TYPE_PNG
);
10031 SetAttributes(*charStyle
);
10034 wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock
& imageBlock
, wxRichTextObject
* parent
, wxRichTextAttr
* charStyle
):
10035 wxRichTextObject(parent
)
10037 m_imageBlock
= imageBlock
;
10039 SetAttributes(*charStyle
);
10042 /// Create a cached image at the required size
10043 bool wxRichTextImage::LoadImageCache(wxDC
& dc
, bool resetCache
)
10045 if (resetCache
|| !m_imageCache
.IsOk() /* || m_imageCache.GetWidth() != size.x || m_imageCache.GetHeight() != size.y */)
10047 if (!m_imageBlock
.IsOk())
10051 m_imageBlock
.Load(image
);
10055 int width
= image
.GetWidth();
10056 int height
= image
.GetHeight();
10058 if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetWidth().GetValue() > 0)
10060 if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
10061 width
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetWidth().GetValue());
10063 width
= GetAttributes().GetTextBoxAttr().GetWidth().GetValue();
10065 if (GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetHeight().GetValue() > 0)
10067 if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
10068 height
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetHeight().GetValue());
10070 height
= GetAttributes().GetTextBoxAttr().GetHeight().GetValue();
10073 if (image
.GetWidth() == width
&& image
.GetHeight() == height
)
10074 m_imageCache
= wxBitmap(image
);
10077 // If the original width and height is small, e.g. 400 or below,
10078 // scale up and then down to improve image quality. This can make
10079 // a big difference, with not much performance hit.
10080 int upscaleThreshold
= 400;
10082 if (image
.GetWidth() <= upscaleThreshold
|| image
.GetHeight() <= upscaleThreshold
)
10084 img
= image
.Scale(image
.GetWidth()*2, image
.GetHeight()*2);
10085 img
.Rescale(width
, height
, wxIMAGE_QUALITY_HIGH
);
10088 img
= image
.Scale(width
, height
, wxIMAGE_QUALITY_HIGH
);
10089 m_imageCache
= wxBitmap(img
);
10093 return m_imageCache
.IsOk();
10097 bool wxRichTextImage::Draw(wxDC
& dc
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int WXUNUSED(descent
), int WXUNUSED(style
))
10102 // Don't need cached size AFAIK
10103 // wxSize size = GetCachedSize();
10104 if (!LoadImageCache(dc
))
10107 DrawBoxAttributes(dc
, GetBuffer(), GetAttributes(), wxRect(rect
.GetPosition(), GetCachedSize()));
10110 int y
= rect
.y
+ (rect
.height
- m_imageCache
.GetHeight());
10112 dc
.DrawBitmap(m_imageCache
, rect
.x
, y
, true);
10115 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
10116 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
10117 marginRect
= rect
; // outer rectangle, will calculate contentRect
10118 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
10120 dc
.DrawBitmap(m_imageCache
, contentRect
.x
, contentRect
.y
, true);
10122 if (selection
.WithinSelection(range
.GetStart(), this))
10124 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
10125 wxCheckSetPen(dc
, *wxBLACK_PEN
);
10126 dc
.SetLogicalFunction(wxINVERT
);
10127 dc
.DrawRectangle(contentRect
);
10128 dc
.SetLogicalFunction(wxCOPY
);
10134 /// Lay the item out
10135 bool wxRichTextImage::Layout(wxDC
& dc
, const wxRect
& rect
, const wxRect
& WXUNUSED(parentRect
), int WXUNUSED(style
))
10137 if (!LoadImageCache(dc
))
10140 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
10141 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
10142 contentRect
= wxRect(wxPoint(0,0), imageSize
);
10143 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
10145 wxSize overallSize
= marginRect
.GetSize();
10147 SetCachedSize(overallSize
);
10148 SetMaxSize(overallSize
);
10149 SetMinSize(overallSize
);
10150 SetPosition(rect
.GetPosition());
10155 /// Get/set the object size for the given range. Returns false if the range
10156 /// is invalid for this object.
10157 bool wxRichTextImage::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& WXUNUSED(descent
), wxDC
& dc
, int WXUNUSED(flags
), wxPoint
WXUNUSED(position
), wxArrayInt
* partialExtents
) const
10159 if (!range
.IsWithin(GetRange()))
10162 if (!((wxRichTextImage
*)this)->LoadImageCache(dc
))
10164 size
.x
= 0; size
.y
= 0;
10165 if (partialExtents
)
10166 partialExtents
->Add(0);
10170 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
10171 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
10172 contentRect
= wxRect(wxPoint(0,0), imageSize
);
10173 GetBoxRects(dc
, GetBuffer(), GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
10175 wxSize overallSize
= marginRect
.GetSize();
10177 if (partialExtents
)
10178 partialExtents
->Add(overallSize
.x
);
10180 size
= overallSize
;
10185 // Get the 'natural' size for an object. For an image, it would be the
10187 wxTextAttrSize
wxRichTextImage::GetNaturalSize() const
10189 wxTextAttrSize size
;
10190 if (GetImageCache().IsOk())
10192 size
.SetWidth(GetImageCache().GetWidth(), wxTEXT_ATTR_UNITS_PIXELS
);
10193 size
.SetHeight(GetImageCache().GetHeight(), wxTEXT_ATTR_UNITS_PIXELS
);
10200 void wxRichTextImage::Copy(const wxRichTextImage
& obj
)
10202 wxRichTextObject::Copy(obj
);
10204 m_imageBlock
= obj
.m_imageBlock
;
10207 /// Edit properties via a GUI
10208 bool wxRichTextImage::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
10210 wxRichTextObjectPropertiesDialog
imageDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Picture Properties"));
10211 imageDlg
.SetAttributes(GetAttributes());
10213 if (imageDlg
.ShowModal() == wxID_OK
)
10215 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
10216 // indeterminate in the object.
10217 imageDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
10229 /// Compare two attribute objects
10230 bool wxTextAttrEq(const wxRichTextAttr
& attr1
, const wxRichTextAttr
& attr2
)
10232 return (attr1
== attr2
);
10235 // Partial equality test taking flags into account
10236 bool wxTextAttrEqPartial(const wxRichTextAttr
& attr1
, const wxRichTextAttr
& attr2
)
10238 return attr1
.EqPartial(attr2
);
10242 bool wxRichTextTabsEq(const wxArrayInt
& tabs1
, const wxArrayInt
& tabs2
)
10244 if (tabs1
.GetCount() != tabs2
.GetCount())
10248 for (i
= 0; i
< tabs1
.GetCount(); i
++)
10250 if (tabs1
[i
] != tabs2
[i
])
10256 bool wxRichTextApplyStyle(wxRichTextAttr
& destStyle
, const wxRichTextAttr
& style
, wxRichTextAttr
* compareWith
)
10258 return destStyle
.Apply(style
, compareWith
);
10261 // Remove attributes
10262 bool wxRichTextRemoveStyle(wxRichTextAttr
& destStyle
, const wxRichTextAttr
& style
)
10264 return destStyle
.RemoveStyle(style
);
10267 /// Combine two bitlists, specifying the bits of interest with separate flags.
10268 bool wxRichTextCombineBitlists(int& valueA
, int valueB
, int& flagsA
, int flagsB
)
10270 return wxRichTextAttr::CombineBitlists(valueA
, valueB
, flagsA
, flagsB
);
10273 /// Compare two bitlists
10274 bool wxRichTextBitlistsEqPartial(int valueA
, int valueB
, int flags
)
10276 return wxRichTextAttr::BitlistsEqPartial(valueA
, valueB
, flags
);
10279 /// Split into paragraph and character styles
10280 bool wxRichTextSplitParaCharStyles(const wxRichTextAttr
& style
, wxRichTextAttr
& parStyle
, wxRichTextAttr
& charStyle
)
10282 return wxRichTextAttr::SplitParaCharStyles(style
, parStyle
, charStyle
);
10285 /// Convert a decimal to Roman numerals
10286 wxString
wxRichTextDecimalToRoman(long n
)
10288 static wxArrayInt decimalNumbers
;
10289 static wxArrayString romanNumbers
;
10294 decimalNumbers
.Clear();
10295 romanNumbers
.Clear();
10296 return wxEmptyString
;
10299 if (decimalNumbers
.GetCount() == 0)
10301 #define wxRichTextAddDecRom(n, r) decimalNumbers.Add(n); romanNumbers.Add(r);
10303 wxRichTextAddDecRom(1000, wxT("M"));
10304 wxRichTextAddDecRom(900, wxT("CM"));
10305 wxRichTextAddDecRom(500, wxT("D"));
10306 wxRichTextAddDecRom(400, wxT("CD"));
10307 wxRichTextAddDecRom(100, wxT("C"));
10308 wxRichTextAddDecRom(90, wxT("XC"));
10309 wxRichTextAddDecRom(50, wxT("L"));
10310 wxRichTextAddDecRom(40, wxT("XL"));
10311 wxRichTextAddDecRom(10, wxT("X"));
10312 wxRichTextAddDecRom(9, wxT("IX"));
10313 wxRichTextAddDecRom(5, wxT("V"));
10314 wxRichTextAddDecRom(4, wxT("IV"));
10315 wxRichTextAddDecRom(1, wxT("I"));
10321 while (n
> 0 && i
< 13)
10323 if (n
>= decimalNumbers
[i
])
10325 n
-= decimalNumbers
[i
];
10326 roman
+= romanNumbers
[i
];
10333 if (roman
.IsEmpty())
10339 * wxRichTextFileHandler
10340 * Base class for file handlers
10343 IMPLEMENT_CLASS(wxRichTextFileHandler
, wxObject
)
10345 #if wxUSE_FFILE && wxUSE_STREAMS
10346 bool wxRichTextFileHandler::LoadFile(wxRichTextBuffer
*buffer
, const wxString
& filename
)
10348 wxFFileInputStream
stream(filename
);
10350 return LoadFile(buffer
, stream
);
10355 bool wxRichTextFileHandler::SaveFile(wxRichTextBuffer
*buffer
, const wxString
& filename
)
10357 wxFFileOutputStream
stream(filename
);
10359 return SaveFile(buffer
, stream
);
10363 #endif // wxUSE_FFILE && wxUSE_STREAMS
10365 /// Can we handle this filename (if using files)? By default, checks the extension.
10366 bool wxRichTextFileHandler::CanHandle(const wxString
& filename
) const
10368 wxString path
, file
, ext
;
10369 wxFileName::SplitPath(filename
, & path
, & file
, & ext
);
10371 return (ext
.Lower() == GetExtension());
10375 * wxRichTextTextHandler
10376 * Plain text handler
10379 IMPLEMENT_CLASS(wxRichTextPlainTextHandler
, wxRichTextFileHandler
)
10382 bool wxRichTextPlainTextHandler::DoLoadFile(wxRichTextBuffer
*buffer
, wxInputStream
& stream
)
10384 if (!stream
.IsOk())
10390 while (!stream
.Eof())
10392 int ch
= stream
.GetC();
10396 if (ch
== 10 && lastCh
!= 13)
10399 if (ch
> 0 && ch
!= 10)
10406 buffer
->ResetAndClearCommands();
10408 buffer
->AddParagraphs(str
);
10409 buffer
->UpdateRanges();
10414 bool wxRichTextPlainTextHandler::DoSaveFile(wxRichTextBuffer
*buffer
, wxOutputStream
& stream
)
10416 if (!stream
.IsOk())
10419 wxString text
= buffer
->GetText();
10421 wxString newLine
= wxRichTextLineBreakChar
;
10422 text
.Replace(newLine
, wxT("\n"));
10424 wxCharBuffer buf
= text
.ToAscii();
10426 stream
.Write((const char*) buf
, text
.length());
10429 #endif // wxUSE_STREAMS
10432 * Stores information about an image, in binary in-memory form
10435 wxRichTextImageBlock::wxRichTextImageBlock()
10440 wxRichTextImageBlock::wxRichTextImageBlock(const wxRichTextImageBlock
& block
):wxObject()
10446 wxRichTextImageBlock::~wxRichTextImageBlock()
10451 void wxRichTextImageBlock::Init()
10455 m_imageType
= wxBITMAP_TYPE_INVALID
;
10458 void wxRichTextImageBlock::Clear()
10462 m_imageType
= wxBITMAP_TYPE_INVALID
;
10466 // Load the original image into a memory block.
10467 // If the image is not a JPEG, we must convert it into a JPEG
10468 // to conserve space.
10469 // If it's not a JPEG we can make use of 'image', already scaled, so we don't have to
10470 // load the image a 2nd time.
10472 bool wxRichTextImageBlock::MakeImageBlock(const wxString
& filename
, wxBitmapType imageType
,
10473 wxImage
& image
, bool convertToJPEG
)
10475 m_imageType
= imageType
;
10477 wxString
filenameToRead(filename
);
10478 bool removeFile
= false;
10480 if (imageType
== wxBITMAP_TYPE_INVALID
)
10481 return false; // Could not determine image type
10483 if ((imageType
!= wxBITMAP_TYPE_JPEG
) && convertToJPEG
)
10485 wxString tempFile
=
10486 wxFileName::CreateTempFileName(_("image"));
10488 wxASSERT(!tempFile
.IsEmpty());
10490 image
.SaveFile(tempFile
, wxBITMAP_TYPE_JPEG
);
10491 filenameToRead
= tempFile
;
10494 m_imageType
= wxBITMAP_TYPE_JPEG
;
10497 if (!file
.Open(filenameToRead
))
10500 m_dataSize
= (size_t) file
.Length();
10505 m_data
= ReadBlock(filenameToRead
, m_dataSize
);
10508 wxRemoveFile(filenameToRead
);
10510 return (m_data
!= NULL
);
10513 // Make an image block from the wxImage in the given
10515 bool wxRichTextImageBlock::MakeImageBlock(wxImage
& image
, wxBitmapType imageType
, int quality
)
10517 image
.SetOption(wxT("quality"), quality
);
10519 if (imageType
== wxBITMAP_TYPE_INVALID
)
10520 return false; // Could not determine image type
10522 return DoMakeImageBlock(image
, imageType
);
10525 // Uses a const wxImage for efficiency, but can't set quality (only relevant for JPEG)
10526 bool wxRichTextImageBlock::MakeImageBlockDefaultQuality(const wxImage
& image
, wxBitmapType imageType
)
10528 if (imageType
== wxBITMAP_TYPE_INVALID
)
10529 return false; // Could not determine image type
10531 return DoMakeImageBlock(image
, imageType
);
10534 // Makes the image block
10535 bool wxRichTextImageBlock::DoMakeImageBlock(const wxImage
& image
, wxBitmapType imageType
)
10537 wxMemoryOutputStream memStream
;
10538 if (!image
.SaveFile(memStream
, imageType
))
10543 unsigned char* block
= new unsigned char[memStream
.GetSize()];
10551 m_imageType
= imageType
;
10552 m_dataSize
= memStream
.GetSize();
10554 memStream
.CopyTo(m_data
, m_dataSize
);
10556 return (m_data
!= NULL
);
10560 bool wxRichTextImageBlock::Write(const wxString
& filename
)
10562 return WriteBlock(filename
, m_data
, m_dataSize
);
10565 void wxRichTextImageBlock::Copy(const wxRichTextImageBlock
& block
)
10567 m_imageType
= block
.m_imageType
;
10569 m_dataSize
= block
.m_dataSize
;
10570 if (m_dataSize
== 0)
10573 m_data
= new unsigned char[m_dataSize
];
10575 for (i
= 0; i
< m_dataSize
; i
++)
10576 m_data
[i
] = block
.m_data
[i
];
10580 void wxRichTextImageBlock::operator=(const wxRichTextImageBlock
& block
)
10585 // Load a wxImage from the block
10586 bool wxRichTextImageBlock::Load(wxImage
& image
)
10591 // Read in the image.
10593 wxMemoryInputStream
mstream(m_data
, m_dataSize
);
10594 bool success
= image
.LoadFile(mstream
, GetImageType());
10596 wxString tempFile
= wxFileName::CreateTempFileName(_("image"));
10597 wxASSERT(!tempFile
.IsEmpty());
10599 if (!WriteBlock(tempFile
, m_data
, m_dataSize
))
10603 success
= image
.LoadFile(tempFile
, GetImageType());
10604 wxRemoveFile(tempFile
);
10610 // Write data in hex to a stream
10611 bool wxRichTextImageBlock::WriteHex(wxOutputStream
& stream
)
10613 if (m_dataSize
== 0)
10616 int bufSize
= 100000;
10617 if (int(2*m_dataSize
) < bufSize
)
10618 bufSize
= 2*m_dataSize
;
10619 char* buf
= new char[bufSize
+1];
10621 int left
= m_dataSize
;
10626 if (left
*2 > bufSize
)
10628 n
= bufSize
; left
-= (bufSize
/2);
10632 n
= left
*2; left
= 0;
10636 for (i
= 0; i
< (n
/2); i
++)
10638 wxDecToHex(m_data
[j
], b
, b
+1);
10643 stream
.Write((const char*) buf
, n
);
10649 // Read data in hex from a stream
10650 bool wxRichTextImageBlock::ReadHex(wxInputStream
& stream
, int length
, wxBitmapType imageType
)
10652 int dataSize
= length
/2;
10657 // create a null terminated temporary string:
10661 m_data
= new unsigned char[dataSize
];
10663 for (i
= 0; i
< dataSize
; i
++)
10665 str
[0] = (char)stream
.GetC();
10666 str
[1] = (char)stream
.GetC();
10668 m_data
[i
] = (unsigned char)wxHexToDec(str
);
10671 m_dataSize
= dataSize
;
10672 m_imageType
= imageType
;
10677 // Allocate and read from stream as a block of memory
10678 unsigned char* wxRichTextImageBlock::ReadBlock(wxInputStream
& stream
, size_t size
)
10680 unsigned char* block
= new unsigned char[size
];
10684 stream
.Read(block
, size
);
10689 unsigned char* wxRichTextImageBlock::ReadBlock(const wxString
& filename
, size_t size
)
10691 wxFileInputStream
stream(filename
);
10692 if (!stream
.IsOk())
10695 return ReadBlock(stream
, size
);
10698 // Write memory block to stream
10699 bool wxRichTextImageBlock::WriteBlock(wxOutputStream
& stream
, unsigned char* block
, size_t size
)
10701 stream
.Write((void*) block
, size
);
10702 return stream
.IsOk();
10706 // Write memory block to file
10707 bool wxRichTextImageBlock::WriteBlock(const wxString
& filename
, unsigned char* block
, size_t size
)
10709 wxFileOutputStream
outStream(filename
);
10710 if (!outStream
.IsOk())
10713 return WriteBlock(outStream
, block
, size
);
10716 // Gets the extension for the block's type
10717 wxString
wxRichTextImageBlock::GetExtension() const
10719 wxImageHandler
* handler
= wxImage::FindHandler(GetImageType());
10721 return handler
->GetExtension();
10723 return wxEmptyString
;
10729 * The data object for a wxRichTextBuffer
10732 const wxChar
*wxRichTextBufferDataObject::ms_richTextBufferFormatId
= wxT("wxShape");
10734 wxRichTextBufferDataObject::wxRichTextBufferDataObject(wxRichTextBuffer
* richTextBuffer
)
10736 m_richTextBuffer
= richTextBuffer
;
10738 // this string should uniquely identify our format, but is otherwise
10740 m_formatRichTextBuffer
.SetId(GetRichTextBufferFormatId());
10742 SetFormat(m_formatRichTextBuffer
);
10745 wxRichTextBufferDataObject::~wxRichTextBufferDataObject()
10747 delete m_richTextBuffer
;
10750 // after a call to this function, the richTextBuffer is owned by the caller and it
10751 // is responsible for deleting it!
10752 wxRichTextBuffer
* wxRichTextBufferDataObject::GetRichTextBuffer()
10754 wxRichTextBuffer
* richTextBuffer
= m_richTextBuffer
;
10755 m_richTextBuffer
= NULL
;
10757 return richTextBuffer
;
10760 wxDataFormat
wxRichTextBufferDataObject::GetPreferredFormat(Direction
WXUNUSED(dir
)) const
10762 return m_formatRichTextBuffer
;
10765 size_t wxRichTextBufferDataObject::GetDataSize() const
10767 if (!m_richTextBuffer
)
10773 wxStringOutputStream
stream(& bufXML
);
10774 if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
))
10776 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
10782 wxCharBuffer buffer
= bufXML
.mb_str(wxConvUTF8
);
10783 return strlen(buffer
) + 1;
10785 return bufXML
.Length()+1;
10789 bool wxRichTextBufferDataObject::GetDataHere(void *pBuf
) const
10791 if (!pBuf
|| !m_richTextBuffer
)
10797 wxStringOutputStream
stream(& bufXML
);
10798 if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
))
10800 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
10806 wxCharBuffer buffer
= bufXML
.mb_str(wxConvUTF8
);
10807 size_t len
= strlen(buffer
);
10808 memcpy((char*) pBuf
, (const char*) buffer
, len
);
10809 ((char*) pBuf
)[len
] = 0;
10811 size_t len
= bufXML
.Length();
10812 memcpy((char*) pBuf
, (const char*) bufXML
.c_str(), len
);
10813 ((char*) pBuf
)[len
] = 0;
10819 bool wxRichTextBufferDataObject::SetData(size_t WXUNUSED(len
), const void *buf
)
10821 wxDELETE(m_richTextBuffer
);
10823 wxString
bufXML((const char*) buf
, wxConvUTF8
);
10825 m_richTextBuffer
= new wxRichTextBuffer
;
10827 wxStringInputStream
stream(bufXML
);
10828 if (!m_richTextBuffer
->LoadFile(stream
, wxRICHTEXT_TYPE_XML
))
10830 wxLogError(wxT("Could not read the buffer from an XML stream.\nYou may have forgotten to add the XML file handler."));
10832 wxDELETE(m_richTextBuffer
);
10844 * wxRichTextFontTable
10845 * Manages quick access to a pool of fonts for rendering rich text
10848 WX_DECLARE_STRING_HASH_MAP_WITH_DECL(wxFont
, wxRichTextFontTableHashMap
, class WXDLLIMPEXP_RICHTEXT
);
10850 class wxRichTextFontTableData
: public wxObjectRefData
10853 wxRichTextFontTableData() {}
10855 wxFont
FindFont(const wxRichTextAttr
& fontSpec
);
10857 wxRichTextFontTableHashMap m_hashMap
;
10860 wxFont
wxRichTextFontTableData::FindFont(const wxRichTextAttr
& fontSpec
)
10862 wxString
facename(fontSpec
.GetFontFaceName());
10863 wxString
spec(wxString::Format(wxT("%d-%d-%d-%d-%s-%d"), fontSpec
.GetFontSize(), fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), (int) fontSpec
.GetFontUnderlined(), facename
.c_str(), (int) fontSpec
.GetFontEncoding()));
10864 wxRichTextFontTableHashMap::iterator entry
= m_hashMap
.find(spec
);
10866 if ( entry
== m_hashMap
.end() )
10868 wxFont
font(fontSpec
.GetFontSize(), wxDEFAULT
, fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), fontSpec
.GetFontUnderlined(), facename
.c_str());
10869 m_hashMap
[spec
] = font
;
10874 return entry
->second
;
10878 IMPLEMENT_DYNAMIC_CLASS(wxRichTextFontTable
, wxObject
)
10880 wxRichTextFontTable::wxRichTextFontTable()
10882 m_refData
= new wxRichTextFontTableData
;
10885 wxRichTextFontTable::wxRichTextFontTable(const wxRichTextFontTable
& table
)
10891 wxRichTextFontTable::~wxRichTextFontTable()
10896 bool wxRichTextFontTable::operator == (const wxRichTextFontTable
& table
) const
10898 return (m_refData
== table
.m_refData
);
10901 void wxRichTextFontTable::operator= (const wxRichTextFontTable
& table
)
10906 wxFont
wxRichTextFontTable::FindFont(const wxRichTextAttr
& fontSpec
)
10908 wxRichTextFontTableData
* data
= (wxRichTextFontTableData
*) m_refData
;
10910 return data
->FindFont(fontSpec
);
10915 void wxRichTextFontTable::Clear()
10917 wxRichTextFontTableData
* data
= (wxRichTextFontTableData
*) m_refData
;
10919 data
->m_hashMap
.clear();
10925 void wxTextBoxAttr::Reset()
10928 m_floatMode
= wxTEXT_BOX_ATTR_FLOAT_NONE
;
10929 m_clearMode
= wxTEXT_BOX_ATTR_CLEAR_NONE
;
10930 m_collapseMode
= wxTEXT_BOX_ATTR_COLLAPSE_NONE
;
10931 m_verticalAlignment
= wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_NONE
;
10932 m_boxStyleName
= wxEmptyString
;
10936 m_position
.Reset();
10947 bool wxTextBoxAttr::operator== (const wxTextBoxAttr
& attr
) const
10950 m_flags
== attr
.m_flags
&&
10951 m_floatMode
== attr
.m_floatMode
&&
10952 m_clearMode
== attr
.m_clearMode
&&
10953 m_collapseMode
== attr
.m_collapseMode
&&
10954 m_verticalAlignment
== attr
.m_verticalAlignment
&&
10956 m_margins
== attr
.m_margins
&&
10957 m_padding
== attr
.m_padding
&&
10958 m_position
== attr
.m_position
&&
10960 m_size
== attr
.m_size
&&
10961 m_minSize
== attr
.m_minSize
&&
10962 m_maxSize
== attr
.m_maxSize
&&
10964 m_border
== attr
.m_border
&&
10965 m_outline
== attr
.m_outline
&&
10967 m_boxStyleName
== attr
.m_boxStyleName
10971 // Partial equality test
10972 bool wxTextBoxAttr::EqPartial(const wxTextBoxAttr
& attr
) const
10974 if (attr
.HasFloatMode() && HasFloatMode() && (GetFloatMode() != attr
.GetFloatMode()))
10977 if (attr
.HasClearMode() && HasClearMode() && (GetClearMode() != attr
.GetClearMode()))
10980 if (attr
.HasCollapseBorders() && HasCollapseBorders() && (attr
.GetCollapseBorders() != GetCollapseBorders()))
10983 if (attr
.HasVerticalAlignment() && HasVerticalAlignment() && (attr
.GetVerticalAlignment() != GetVerticalAlignment()))
10986 if (attr
.HasBoxStyleName() && HasBoxStyleName() && (attr
.GetBoxStyleName() != GetBoxStyleName()))
10991 if (!m_position
.EqPartial(attr
.m_position
))
10996 if (!m_size
.EqPartial(attr
.m_size
))
10998 if (!m_minSize
.EqPartial(attr
.m_minSize
))
11000 if (!m_maxSize
.EqPartial(attr
.m_maxSize
))
11005 if (!m_margins
.EqPartial(attr
.m_margins
))
11010 if (!m_padding
.EqPartial(attr
.m_padding
))
11015 if (!GetBorder().EqPartial(attr
.GetBorder()))
11020 if (!GetOutline().EqPartial(attr
.GetOutline()))
11026 // Merges the given attributes. If compareWith
11027 // is non-NULL, then it will be used to mask out those attributes that are the same in style
11028 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
11029 bool wxTextBoxAttr::Apply(const wxTextBoxAttr
& attr
, const wxTextBoxAttr
* compareWith
)
11031 if (attr
.HasFloatMode())
11033 if (!(compareWith
&& compareWith
->HasFloatMode() && compareWith
->GetFloatMode() == attr
.GetFloatMode()))
11034 SetFloatMode(attr
.GetFloatMode());
11037 if (attr
.HasClearMode())
11039 if (!(compareWith
&& compareWith
->HasClearMode() && compareWith
->GetClearMode() == attr
.GetClearMode()))
11040 SetClearMode(attr
.GetClearMode());
11043 if (attr
.HasCollapseBorders())
11045 if (!(compareWith
&& compareWith
->HasCollapseBorders() && compareWith
->GetCollapseBorders() == attr
.GetCollapseBorders()))
11046 SetCollapseBorders(attr
.GetCollapseBorders());
11049 if (attr
.HasVerticalAlignment())
11051 if (!(compareWith
&& compareWith
->HasVerticalAlignment() && compareWith
->GetVerticalAlignment() == attr
.GetVerticalAlignment()))
11052 SetVerticalAlignment(attr
.GetVerticalAlignment());
11055 if (attr
.HasBoxStyleName())
11057 if (!(compareWith
&& compareWith
->HasBoxStyleName() && compareWith
->GetBoxStyleName() == attr
.GetBoxStyleName()))
11058 SetBoxStyleName(attr
.GetBoxStyleName());
11061 m_margins
.Apply(attr
.m_margins
, compareWith
? (& attr
.m_margins
) : (const wxTextAttrDimensions
*) NULL
);
11062 m_padding
.Apply(attr
.m_padding
, compareWith
? (& attr
.m_padding
) : (const wxTextAttrDimensions
*) NULL
);
11063 m_position
.Apply(attr
.m_position
, compareWith
? (& attr
.m_position
) : (const wxTextAttrDimensions
*) NULL
);
11065 m_size
.Apply(attr
.m_size
, compareWith
? (& attr
.m_size
) : (const wxTextAttrSize
*) NULL
);
11066 m_minSize
.Apply(attr
.m_minSize
, compareWith
? (& attr
.m_minSize
) : (const wxTextAttrSize
*) NULL
);
11067 m_maxSize
.Apply(attr
.m_maxSize
, compareWith
? (& attr
.m_maxSize
) : (const wxTextAttrSize
*) NULL
);
11069 m_border
.Apply(attr
.m_border
, compareWith
? (& attr
.m_border
) : (const wxTextAttrBorders
*) NULL
);
11070 m_outline
.Apply(attr
.m_outline
, compareWith
? (& attr
.m_outline
) : (const wxTextAttrBorders
*) NULL
);
11075 // Remove specified attributes from this object
11076 bool wxTextBoxAttr::RemoveStyle(const wxTextBoxAttr
& attr
)
11078 if (attr
.HasFloatMode())
11079 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT
);
11081 if (attr
.HasClearMode())
11082 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR
);
11084 if (attr
.HasCollapseBorders())
11085 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
11087 if (attr
.HasVerticalAlignment())
11088 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
11090 if (attr
.HasBoxStyleName())
11092 SetBoxStyleName(wxEmptyString
);
11093 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
11096 m_margins
.RemoveStyle(attr
.m_margins
);
11097 m_padding
.RemoveStyle(attr
.m_padding
);
11098 m_position
.RemoveStyle(attr
.m_position
);
11100 m_size
.RemoveStyle(attr
.m_size
);
11101 m_minSize
.RemoveStyle(attr
.m_minSize
);
11102 m_maxSize
.RemoveStyle(attr
.m_maxSize
);
11104 m_border
.RemoveStyle(attr
.m_border
);
11105 m_outline
.RemoveStyle(attr
.m_outline
);
11110 // Collects the attributes that are common to a range of content, building up a note of
11111 // which attributes are absent in some objects and which clash in some objects.
11112 void wxTextBoxAttr::CollectCommonAttributes(const wxTextBoxAttr
& attr
, wxTextBoxAttr
& clashingAttr
, wxTextBoxAttr
& absentAttr
)
11114 if (attr
.HasFloatMode())
11116 if (!clashingAttr
.HasFloatMode() && !absentAttr
.HasFloatMode())
11118 if (HasFloatMode())
11120 if (GetFloatMode() != attr
.GetFloatMode())
11122 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_FLOAT
);
11123 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT
);
11127 SetFloatMode(attr
.GetFloatMode());
11131 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_FLOAT
);
11133 if (attr
.HasClearMode())
11135 if (!clashingAttr
.HasClearMode() && !absentAttr
.HasClearMode())
11137 if (HasClearMode())
11139 if (GetClearMode() != attr
.GetClearMode())
11141 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_CLEAR
);
11142 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR
);
11146 SetClearMode(attr
.GetClearMode());
11150 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_CLEAR
);
11152 if (attr
.HasCollapseBorders())
11154 if (!clashingAttr
.HasCollapseBorders() && !absentAttr
.HasCollapseBorders())
11156 if (HasCollapseBorders())
11158 if (GetCollapseBorders() != attr
.GetCollapseBorders())
11160 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
11161 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
11165 SetCollapseBorders(attr
.GetCollapseBorders());
11169 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
11171 if (attr
.HasVerticalAlignment())
11173 if (!clashingAttr
.HasVerticalAlignment() && !absentAttr
.HasVerticalAlignment())
11175 if (HasVerticalAlignment())
11177 if (GetVerticalAlignment() != attr
.GetVerticalAlignment())
11179 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
11180 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
11184 SetVerticalAlignment(attr
.GetVerticalAlignment());
11188 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
11190 if (attr
.HasBoxStyleName())
11192 if (!clashingAttr
.HasBoxStyleName() && !absentAttr
.HasBoxStyleName())
11194 if (HasBoxStyleName())
11196 if (GetBoxStyleName() != attr
.GetBoxStyleName())
11198 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
11199 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
11203 SetBoxStyleName(attr
.GetBoxStyleName());
11207 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
11209 m_margins
.CollectCommonAttributes(attr
.m_margins
, clashingAttr
.m_margins
, absentAttr
.m_margins
);
11210 m_padding
.CollectCommonAttributes(attr
.m_padding
, clashingAttr
.m_padding
, absentAttr
.m_padding
);
11211 m_position
.CollectCommonAttributes(attr
.m_position
, clashingAttr
.m_position
, absentAttr
.m_position
);
11213 m_size
.CollectCommonAttributes(attr
.m_size
, clashingAttr
.m_size
, absentAttr
.m_size
);
11214 m_minSize
.CollectCommonAttributes(attr
.m_minSize
, clashingAttr
.m_minSize
, absentAttr
.m_minSize
);
11215 m_maxSize
.CollectCommonAttributes(attr
.m_maxSize
, clashingAttr
.m_maxSize
, absentAttr
.m_maxSize
);
11217 m_border
.CollectCommonAttributes(attr
.m_border
, clashingAttr
.m_border
, absentAttr
.m_border
);
11218 m_outline
.CollectCommonAttributes(attr
.m_outline
, clashingAttr
.m_outline
, absentAttr
.m_outline
);
11221 bool wxTextBoxAttr::IsDefault() const
11223 return GetFlags() == 0 && !m_border
.IsValid() && !m_outline
.IsValid() &&
11224 !m_size
.IsValid() && !m_minSize
.IsValid() && !m_maxSize
.IsValid() &&
11225 !m_position
.IsValid() && !m_padding
.IsValid() && !m_margins
.IsValid();
11230 void wxRichTextAttr::Copy(const wxRichTextAttr
& attr
)
11232 wxTextAttr::Copy(attr
);
11234 m_textBoxAttr
= attr
.m_textBoxAttr
;
11237 bool wxRichTextAttr::operator==(const wxRichTextAttr
& attr
) const
11239 if (!(wxTextAttr::operator==(attr
)))
11242 return (m_textBoxAttr
== attr
.m_textBoxAttr
);
11245 // Partial equality test taking comparison object into account
11246 bool wxRichTextAttr::EqPartial(const wxRichTextAttr
& attr
) const
11248 if (!(wxTextAttr::EqPartial(attr
)))
11251 return m_textBoxAttr
.EqPartial(attr
.m_textBoxAttr
);
11254 // Merges the given attributes. If compareWith
11255 // is non-NULL, then it will be used to mask out those attributes that are the same in style
11256 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
11257 bool wxRichTextAttr::Apply(const wxRichTextAttr
& style
, const wxRichTextAttr
* compareWith
)
11259 wxTextAttr::Apply(style
, compareWith
);
11261 return m_textBoxAttr
.Apply(style
.m_textBoxAttr
, compareWith
? (& compareWith
->m_textBoxAttr
) : (const wxTextBoxAttr
*) NULL
);
11264 // Remove specified attributes from this object
11265 bool wxRichTextAttr::RemoveStyle(const wxRichTextAttr
& attr
)
11267 wxTextAttr::RemoveStyle(*this, attr
);
11269 return m_textBoxAttr
.RemoveStyle(attr
.m_textBoxAttr
);
11272 // Collects the attributes that are common to a range of content, building up a note of
11273 // which attributes are absent in some objects and which clash in some objects.
11274 void wxRichTextAttr::CollectCommonAttributes(const wxRichTextAttr
& attr
, wxRichTextAttr
& clashingAttr
, wxRichTextAttr
& absentAttr
)
11276 wxTextAttrCollectCommonAttributes(*this, attr
, clashingAttr
, absentAttr
);
11278 m_textBoxAttr
.CollectCommonAttributes(attr
.m_textBoxAttr
, clashingAttr
.m_textBoxAttr
, absentAttr
.m_textBoxAttr
);
11281 // Partial equality test
11282 bool wxTextAttrBorder::EqPartial(const wxTextAttrBorder
& border
) const
11284 if (border
.HasStyle() && !HasStyle() && (border
.GetStyle() != GetStyle()))
11287 if (border
.HasColour() && !HasColour() && (border
.GetColourLong() != GetColourLong()))
11290 if (border
.HasWidth() && !HasWidth() && !(border
.GetWidth() == GetWidth()))
11296 // Apply border to 'this', but not if the same as compareWith
11297 bool wxTextAttrBorder::Apply(const wxTextAttrBorder
& border
, const wxTextAttrBorder
* compareWith
)
11299 if (border
.HasStyle())
11301 if (!(compareWith
&& (border
.GetStyle() == compareWith
->GetStyle())))
11302 SetStyle(border
.GetStyle());
11304 if (border
.HasColour())
11306 if (!(compareWith
&& (border
.GetColourLong() == compareWith
->GetColourLong())))
11307 SetColour(border
.GetColourLong());
11309 if (border
.HasWidth())
11311 if (!(compareWith
&& (border
.GetWidth() == compareWith
->GetWidth())))
11312 SetWidth(border
.GetWidth());
11318 // Remove specified attributes from this object
11319 bool wxTextAttrBorder::RemoveStyle(const wxTextAttrBorder
& attr
)
11321 if (attr
.HasStyle() && HasStyle())
11322 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_STYLE
);
11323 if (attr
.HasColour() && HasColour())
11324 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_COLOUR
);
11325 if (attr
.HasWidth() && HasWidth())
11326 m_borderWidth
.Reset();
11331 // Collects the attributes that are common to a range of content, building up a note of
11332 // which attributes are absent in some objects and which clash in some objects.
11333 void wxTextAttrBorder::CollectCommonAttributes(const wxTextAttrBorder
& attr
, wxTextAttrBorder
& clashingAttr
, wxTextAttrBorder
& absentAttr
)
11335 if (attr
.HasStyle())
11337 if (!clashingAttr
.HasStyle() && !absentAttr
.HasStyle())
11341 if (GetStyle() != attr
.GetStyle())
11343 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
11344 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
11348 SetStyle(attr
.GetStyle());
11352 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
11354 if (attr
.HasColour())
11356 if (!clashingAttr
.HasColour() && !absentAttr
.HasColour())
11360 if (GetColour() != attr
.GetColour())
11362 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
11363 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
11367 SetColour(attr
.GetColourLong());
11371 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
11373 m_borderWidth
.CollectCommonAttributes(attr
.m_borderWidth
, clashingAttr
.m_borderWidth
, absentAttr
.m_borderWidth
);
11376 // Partial equality test
11377 bool wxTextAttrBorders::EqPartial(const wxTextAttrBorders
& borders
) const
11379 return m_left
.EqPartial(borders
.m_left
) && m_right
.EqPartial(borders
.m_right
) &&
11380 m_top
.EqPartial(borders
.m_top
) && m_bottom
.EqPartial(borders
.m_bottom
);
11383 // Apply border to 'this', but not if the same as compareWith
11384 bool wxTextAttrBorders::Apply(const wxTextAttrBorders
& borders
, const wxTextAttrBorders
* compareWith
)
11386 m_left
.Apply(borders
.m_left
, compareWith
? (& compareWith
->m_left
) : (const wxTextAttrBorder
*) NULL
);
11387 m_right
.Apply(borders
.m_right
, compareWith
? (& compareWith
->m_right
) : (const wxTextAttrBorder
*) NULL
);
11388 m_top
.Apply(borders
.m_top
, compareWith
? (& compareWith
->m_top
) : (const wxTextAttrBorder
*) NULL
);
11389 m_bottom
.Apply(borders
.m_bottom
, compareWith
? (& compareWith
->m_bottom
) : (const wxTextAttrBorder
*) NULL
);
11393 // Remove specified attributes from this object
11394 bool wxTextAttrBorders::RemoveStyle(const wxTextAttrBorders
& attr
)
11396 m_left
.RemoveStyle(attr
.m_left
);
11397 m_right
.RemoveStyle(attr
.m_right
);
11398 m_top
.RemoveStyle(attr
.m_top
);
11399 m_bottom
.RemoveStyle(attr
.m_bottom
);
11403 // Collects the attributes that are common to a range of content, building up a note of
11404 // which attributes are absent in some objects and which clash in some objects.
11405 void wxTextAttrBorders::CollectCommonAttributes(const wxTextAttrBorders
& attr
, wxTextAttrBorders
& clashingAttr
, wxTextAttrBorders
& absentAttr
)
11407 m_left
.CollectCommonAttributes(attr
.m_left
, clashingAttr
.m_left
, absentAttr
.m_left
);
11408 m_right
.CollectCommonAttributes(attr
.m_right
, clashingAttr
.m_right
, absentAttr
.m_right
);
11409 m_top
.CollectCommonAttributes(attr
.m_top
, clashingAttr
.m_top
, absentAttr
.m_top
);
11410 m_bottom
.CollectCommonAttributes(attr
.m_bottom
, clashingAttr
.m_bottom
, absentAttr
.m_bottom
);
11413 // Set style of all borders
11414 void wxTextAttrBorders::SetStyle(int style
)
11416 m_left
.SetStyle(style
);
11417 m_right
.SetStyle(style
);
11418 m_top
.SetStyle(style
);
11419 m_bottom
.SetStyle(style
);
11422 // Set colour of all borders
11423 void wxTextAttrBorders::SetColour(unsigned long colour
)
11425 m_left
.SetColour(colour
);
11426 m_right
.SetColour(colour
);
11427 m_top
.SetColour(colour
);
11428 m_bottom
.SetColour(colour
);
11431 void wxTextAttrBorders::SetColour(const wxColour
& colour
)
11433 m_left
.SetColour(colour
);
11434 m_right
.SetColour(colour
);
11435 m_top
.SetColour(colour
);
11436 m_bottom
.SetColour(colour
);
11439 // Set width of all borders
11440 void wxTextAttrBorders::SetWidth(const wxTextAttrDimension
& width
)
11442 m_left
.SetWidth(width
);
11443 m_right
.SetWidth(width
);
11444 m_top
.SetWidth(width
);
11445 m_bottom
.SetWidth(width
);
11448 // Partial equality test
11449 bool wxTextAttrDimension::EqPartial(const wxTextAttrDimension
& dim
) const
11451 if (dim
.IsValid() && IsValid() && !((*this) == dim
))
11457 bool wxTextAttrDimension::Apply(const wxTextAttrDimension
& dim
, const wxTextAttrDimension
* compareWith
)
11461 if (!(compareWith
&& dim
== (*compareWith
)))
11468 // Collects the attributes that are common to a range of content, building up a note of
11469 // which attributes are absent in some objects and which clash in some objects.
11470 void wxTextAttrDimension::CollectCommonAttributes(const wxTextAttrDimension
& attr
, wxTextAttrDimension
& clashingAttr
, wxTextAttrDimension
& absentAttr
)
11472 if (attr
.IsValid())
11474 if (!clashingAttr
.IsValid() && !absentAttr
.IsValid())
11478 if (!((*this) == attr
))
11480 clashingAttr
.SetValid(true);
11489 absentAttr
.SetValid(true);
11492 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(wxDC
& dc
, double scale
, const wxSize
& parentSize
)
11494 m_ppi
= dc
.GetPPI().x
; m_scale
= scale
; m_parentSize
= parentSize
;
11497 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(int ppi
, double scale
, const wxSize
& parentSize
)
11499 m_ppi
= ppi
; m_scale
= scale
; m_parentSize
= parentSize
;
11502 int wxTextAttrDimensionConverter::ConvertTenthsMMToPixels(int units
) const
11504 return wxRichTextObject::ConvertTenthsMMToPixels(m_ppi
, units
, m_scale
);
11507 int wxTextAttrDimensionConverter::ConvertPixelsToTenthsMM(int pixels
) const
11509 return wxRichTextObject::ConvertPixelsToTenthsMM(m_ppi
, pixels
, m_scale
);
11512 int wxTextAttrDimensionConverter::GetPixels(const wxTextAttrDimension
& dim
, int direction
) const
11514 if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
11515 return ConvertTenthsMMToPixels(dim
.GetValue());
11516 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
11517 return dim
.GetValue();
11518 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
11520 wxASSERT(m_parentSize
!= wxDefaultSize
);
11521 if (direction
== wxHORIZONTAL
)
11522 return (int) (double(m_parentSize
.x
) * double(dim
.GetValue()) / 100.0);
11524 return (int) (double(m_parentSize
.y
) * double(dim
.GetValue()) / 100.0);
11533 int wxTextAttrDimensionConverter::GetTenthsMM(const wxTextAttrDimension
& dim
) const
11535 if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
11536 return dim
.GetValue();
11537 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
11538 return ConvertPixelsToTenthsMM(dim
.GetValue());
11546 // Partial equality test
11547 bool wxTextAttrDimensions::EqPartial(const wxTextAttrDimensions
& dims
) const
11549 if (!m_left
.EqPartial(dims
.m_left
))
11552 if (!m_right
.EqPartial(dims
.m_right
))
11555 if (!m_top
.EqPartial(dims
.m_top
))
11558 if (!m_bottom
.EqPartial(dims
.m_bottom
))
11564 // Apply border to 'this', but not if the same as compareWith
11565 bool wxTextAttrDimensions::Apply(const wxTextAttrDimensions
& dims
, const wxTextAttrDimensions
* compareWith
)
11567 m_left
.Apply(dims
.m_left
, compareWith
? (& compareWith
->m_left
) : (const wxTextAttrDimension
*) NULL
);
11568 m_right
.Apply(dims
.m_right
, compareWith
? (& compareWith
->m_right
): (const wxTextAttrDimension
*) NULL
);
11569 m_top
.Apply(dims
.m_top
, compareWith
? (& compareWith
->m_top
): (const wxTextAttrDimension
*) NULL
);
11570 m_bottom
.Apply(dims
.m_bottom
, compareWith
? (& compareWith
->m_bottom
): (const wxTextAttrDimension
*) NULL
);
11575 // Remove specified attributes from this object
11576 bool wxTextAttrDimensions::RemoveStyle(const wxTextAttrDimensions
& attr
)
11578 if (attr
.m_left
.IsValid())
11580 if (attr
.m_right
.IsValid())
11582 if (attr
.m_top
.IsValid())
11584 if (attr
.m_bottom
.IsValid())
11590 // Collects the attributes that are common to a range of content, building up a note of
11591 // which attributes are absent in some objects and which clash in some objects.
11592 void wxTextAttrDimensions::CollectCommonAttributes(const wxTextAttrDimensions
& attr
, wxTextAttrDimensions
& clashingAttr
, wxTextAttrDimensions
& absentAttr
)
11594 m_left
.CollectCommonAttributes(attr
.m_left
, clashingAttr
.m_left
, absentAttr
.m_left
);
11595 m_right
.CollectCommonAttributes(attr
.m_right
, clashingAttr
.m_right
, absentAttr
.m_right
);
11596 m_top
.CollectCommonAttributes(attr
.m_top
, clashingAttr
.m_top
, absentAttr
.m_top
);
11597 m_bottom
.CollectCommonAttributes(attr
.m_bottom
, clashingAttr
.m_bottom
, absentAttr
.m_bottom
);
11600 // Partial equality test
11601 bool wxTextAttrSize::EqPartial(const wxTextAttrSize
& size
) const
11603 if (!m_width
.EqPartial(size
.m_width
))
11606 if (!m_height
.EqPartial(size
.m_height
))
11612 // Apply border to 'this', but not if the same as compareWith
11613 bool wxTextAttrSize::Apply(const wxTextAttrSize
& size
, const wxTextAttrSize
* compareWith
)
11615 m_width
.Apply(size
.m_width
, compareWith
? (& compareWith
->m_width
) : (const wxTextAttrDimension
*) NULL
);
11616 m_height
.Apply(size
.m_height
, compareWith
? (& compareWith
->m_height
): (const wxTextAttrDimension
*) NULL
);
11621 // Remove specified attributes from this object
11622 bool wxTextAttrSize::RemoveStyle(const wxTextAttrSize
& attr
)
11624 if (attr
.m_width
.IsValid())
11626 if (attr
.m_height
.IsValid())
11632 // Collects the attributes that are common to a range of content, building up a note of
11633 // which attributes are absent in some objects and which clash in some objects.
11634 void wxTextAttrSize::CollectCommonAttributes(const wxTextAttrSize
& attr
, wxTextAttrSize
& clashingAttr
, wxTextAttrSize
& absentAttr
)
11636 m_width
.CollectCommonAttributes(attr
.m_width
, clashingAttr
.m_width
, absentAttr
.m_width
);
11637 m_height
.CollectCommonAttributes(attr
.m_height
, clashingAttr
.m_height
, absentAttr
.m_height
);
11640 // Collects the attributes that are common to a range of content, building up a note of
11641 // which attributes are absent in some objects and which clash in some objects.
11642 void wxTextAttrCollectCommonAttributes(wxTextAttr
& currentStyle
, const wxTextAttr
& attr
, wxTextAttr
& clashingAttr
, wxTextAttr
& absentAttr
)
11644 absentAttr
.SetFlags(absentAttr
.GetFlags() | (~attr
.GetFlags() & wxTEXT_ATTR_ALL
));
11645 absentAttr
.SetTextEffectFlags(absentAttr
.GetTextEffectFlags() | (~attr
.GetTextEffectFlags() & 0xFFFF));
11647 long forbiddenFlags
= clashingAttr
.GetFlags()|absentAttr
.GetFlags();
11649 if (attr
.HasFont())
11651 if (attr
.HasFontSize() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_SIZE
))
11653 if (currentStyle
.HasFontSize())
11655 if (currentStyle
.GetFontSize() != attr
.GetFontSize())
11657 // Clash of attr - mark as such
11658 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_SIZE
);
11659 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_SIZE
);
11663 currentStyle
.SetFontSize(attr
.GetFontSize());
11666 if (attr
.HasFontItalic() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_ITALIC
))
11668 if (currentStyle
.HasFontItalic())
11670 if (currentStyle
.GetFontStyle() != attr
.GetFontStyle())
11672 // Clash of attr - mark as such
11673 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_ITALIC
);
11674 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC
);
11678 currentStyle
.SetFontStyle(attr
.GetFontStyle());
11681 if (attr
.HasFontFamily() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_FAMILY
))
11683 if (currentStyle
.HasFontFamily())
11685 if (currentStyle
.GetFontFamily() != attr
.GetFontFamily())
11687 // Clash of attr - mark as such
11688 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FAMILY
);
11689 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY
);
11693 currentStyle
.SetFontFamily(attr
.GetFontFamily());
11696 if (attr
.HasFontWeight() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_WEIGHT
))
11698 if (currentStyle
.HasFontWeight())
11700 if (currentStyle
.GetFontWeight() != attr
.GetFontWeight())
11702 // Clash of attr - mark as such
11703 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_WEIGHT
);
11704 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT
);
11708 currentStyle
.SetFontWeight(attr
.GetFontWeight());
11711 if (attr
.HasFontFaceName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_FACE
))
11713 if (currentStyle
.HasFontFaceName())
11715 wxString
faceName1(currentStyle
.GetFontFaceName());
11716 wxString
faceName2(attr
.GetFontFaceName());
11718 if (faceName1
!= faceName2
)
11720 // Clash of attr - mark as such
11721 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FACE
);
11722 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FACE
);
11726 currentStyle
.SetFontFaceName(attr
.GetFontFaceName());
11729 if (attr
.HasFontUnderlined() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_UNDERLINE
))
11731 if (currentStyle
.HasFontUnderlined())
11733 if (currentStyle
.GetFontUnderlined() != attr
.GetFontUnderlined())
11735 // Clash of attr - mark as such
11736 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
11737 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
11741 currentStyle
.SetFontUnderlined(attr
.GetFontUnderlined());
11745 if (attr
.HasTextColour() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_TEXT_COLOUR
))
11747 if (currentStyle
.HasTextColour())
11749 if (currentStyle
.GetTextColour() != attr
.GetTextColour())
11751 // Clash of attr - mark as such
11752 clashingAttr
.AddFlag(wxTEXT_ATTR_TEXT_COLOUR
);
11753 currentStyle
.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR
);
11757 currentStyle
.SetTextColour(attr
.GetTextColour());
11760 if (attr
.HasBackgroundColour() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BACKGROUND_COLOUR
))
11762 if (currentStyle
.HasBackgroundColour())
11764 if (currentStyle
.GetBackgroundColour() != attr
.GetBackgroundColour())
11766 // Clash of attr - mark as such
11767 clashingAttr
.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
11768 currentStyle
.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
11772 currentStyle
.SetBackgroundColour(attr
.GetBackgroundColour());
11775 if (attr
.HasAlignment() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_ALIGNMENT
))
11777 if (currentStyle
.HasAlignment())
11779 if (currentStyle
.GetAlignment() != attr
.GetAlignment())
11781 // Clash of attr - mark as such
11782 clashingAttr
.AddFlag(wxTEXT_ATTR_ALIGNMENT
);
11783 currentStyle
.RemoveFlag(wxTEXT_ATTR_ALIGNMENT
);
11787 currentStyle
.SetAlignment(attr
.GetAlignment());
11790 if (attr
.HasTabs() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_TABS
))
11792 if (currentStyle
.HasTabs())
11794 if (!wxRichTextTabsEq(currentStyle
.GetTabs(), attr
.GetTabs()))
11796 // Clash of attr - mark as such
11797 clashingAttr
.AddFlag(wxTEXT_ATTR_TABS
);
11798 currentStyle
.RemoveFlag(wxTEXT_ATTR_TABS
);
11802 currentStyle
.SetTabs(attr
.GetTabs());
11805 if (attr
.HasLeftIndent() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LEFT_INDENT
))
11807 if (currentStyle
.HasLeftIndent())
11809 if (currentStyle
.GetLeftIndent() != attr
.GetLeftIndent() || currentStyle
.GetLeftSubIndent() != attr
.GetLeftSubIndent())
11811 // Clash of attr - mark as such
11812 clashingAttr
.AddFlag(wxTEXT_ATTR_LEFT_INDENT
);
11813 currentStyle
.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT
);
11817 currentStyle
.SetLeftIndent(attr
.GetLeftIndent(), attr
.GetLeftSubIndent());
11820 if (attr
.HasRightIndent() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_RIGHT_INDENT
))
11822 if (currentStyle
.HasRightIndent())
11824 if (currentStyle
.GetRightIndent() != attr
.GetRightIndent())
11826 // Clash of attr - mark as such
11827 clashingAttr
.AddFlag(wxTEXT_ATTR_RIGHT_INDENT
);
11828 currentStyle
.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT
);
11832 currentStyle
.SetRightIndent(attr
.GetRightIndent());
11835 if (attr
.HasParagraphSpacingAfter() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARA_SPACING_AFTER
))
11837 if (currentStyle
.HasParagraphSpacingAfter())
11839 if (currentStyle
.GetParagraphSpacingAfter() != attr
.GetParagraphSpacingAfter())
11841 // Clash of attr - mark as such
11842 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
11843 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
11847 currentStyle
.SetParagraphSpacingAfter(attr
.GetParagraphSpacingAfter());
11850 if (attr
.HasParagraphSpacingBefore() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARA_SPACING_BEFORE
))
11852 if (currentStyle
.HasParagraphSpacingBefore())
11854 if (currentStyle
.GetParagraphSpacingBefore() != attr
.GetParagraphSpacingBefore())
11856 // Clash of attr - mark as such
11857 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
11858 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
11862 currentStyle
.SetParagraphSpacingBefore(attr
.GetParagraphSpacingBefore());
11865 if (attr
.HasLineSpacing() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LINE_SPACING
))
11867 if (currentStyle
.HasLineSpacing())
11869 if (currentStyle
.GetLineSpacing() != attr
.GetLineSpacing())
11871 // Clash of attr - mark as such
11872 clashingAttr
.AddFlag(wxTEXT_ATTR_LINE_SPACING
);
11873 currentStyle
.RemoveFlag(wxTEXT_ATTR_LINE_SPACING
);
11877 currentStyle
.SetLineSpacing(attr
.GetLineSpacing());
11880 if (attr
.HasCharacterStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_CHARACTER_STYLE_NAME
))
11882 if (currentStyle
.HasCharacterStyleName())
11884 if (currentStyle
.GetCharacterStyleName() != attr
.GetCharacterStyleName())
11886 // Clash of attr - mark as such
11887 clashingAttr
.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
11888 currentStyle
.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
11892 currentStyle
.SetCharacterStyleName(attr
.GetCharacterStyleName());
11895 if (attr
.HasParagraphStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
))
11897 if (currentStyle
.HasParagraphStyleName())
11899 if (currentStyle
.GetParagraphStyleName() != attr
.GetParagraphStyleName())
11901 // Clash of attr - mark as such
11902 clashingAttr
.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
11903 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
11907 currentStyle
.SetParagraphStyleName(attr
.GetParagraphStyleName());
11910 if (attr
.HasListStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LIST_STYLE_NAME
))
11912 if (currentStyle
.HasListStyleName())
11914 if (currentStyle
.GetListStyleName() != attr
.GetListStyleName())
11916 // Clash of attr - mark as such
11917 clashingAttr
.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
11918 currentStyle
.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
11922 currentStyle
.SetListStyleName(attr
.GetListStyleName());
11925 if (attr
.HasBulletStyle() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_STYLE
))
11927 if (currentStyle
.HasBulletStyle())
11929 if (currentStyle
.GetBulletStyle() != attr
.GetBulletStyle())
11931 // Clash of attr - mark as such
11932 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_STYLE
);
11933 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE
);
11937 currentStyle
.SetBulletStyle(attr
.GetBulletStyle());
11940 if (attr
.HasBulletNumber() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_NUMBER
))
11942 if (currentStyle
.HasBulletNumber())
11944 if (currentStyle
.GetBulletNumber() != attr
.GetBulletNumber())
11946 // Clash of attr - mark as such
11947 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NUMBER
);
11948 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER
);
11952 currentStyle
.SetBulletNumber(attr
.GetBulletNumber());
11955 if (attr
.HasBulletText() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_TEXT
))
11957 if (currentStyle
.HasBulletText())
11959 if (currentStyle
.GetBulletText() != attr
.GetBulletText())
11961 // Clash of attr - mark as such
11962 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_TEXT
);
11963 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT
);
11968 currentStyle
.SetBulletText(attr
.GetBulletText());
11969 currentStyle
.SetBulletFont(attr
.GetBulletFont());
11973 if (attr
.HasBulletName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_NAME
))
11975 if (currentStyle
.HasBulletName())
11977 if (currentStyle
.GetBulletName() != attr
.GetBulletName())
11979 // Clash of attr - mark as such
11980 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NAME
);
11981 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NAME
);
11986 currentStyle
.SetBulletName(attr
.GetBulletName());
11990 if (attr
.HasURL() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_URL
))
11992 if (currentStyle
.HasURL())
11994 if (currentStyle
.GetURL() != attr
.GetURL())
11996 // Clash of attr - mark as such
11997 clashingAttr
.AddFlag(wxTEXT_ATTR_URL
);
11998 currentStyle
.RemoveFlag(wxTEXT_ATTR_URL
);
12003 currentStyle
.SetURL(attr
.GetURL());
12007 if (attr
.HasTextEffects() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_EFFECTS
))
12009 if (currentStyle
.HasTextEffects())
12011 // We need to find the bits in the new attr that are different:
12012 // just look at those bits that are specified by the new attr.
12014 // We need to remove the bits and flags that are not common between current attr
12015 // and new attr. In so doing we need to take account of the styles absent from one or more of the
12016 // previous styles.
12018 int currentRelevantTextEffects
= currentStyle
.GetTextEffects() & attr
.GetTextEffectFlags();
12019 int newRelevantTextEffects
= attr
.GetTextEffects() & attr
.GetTextEffectFlags();
12021 if (currentRelevantTextEffects
!= newRelevantTextEffects
)
12023 // Find the text effects that were different, using XOR
12024 int differentEffects
= currentRelevantTextEffects
^ newRelevantTextEffects
;
12026 // Clash of attr - mark as such
12027 clashingAttr
.SetTextEffectFlags(clashingAttr
.GetTextEffectFlags() | differentEffects
);
12028 currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~differentEffects
);
12033 currentStyle
.SetTextEffects(attr
.GetTextEffects());
12034 currentStyle
.SetTextEffectFlags(attr
.GetTextEffectFlags());
12037 // Mask out the flags and values that cannot be common because they were absent in one or more objecrs
12038 // that we've looked at so far
12039 currentStyle
.SetTextEffects(currentStyle
.GetTextEffects() & ~absentAttr
.GetTextEffectFlags());
12040 currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~absentAttr
.GetTextEffectFlags());
12042 if (currentStyle
.GetTextEffectFlags() == 0)
12043 currentStyle
.RemoveFlag(wxTEXT_ATTR_EFFECTS
);
12046 if (attr
.HasOutlineLevel() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_OUTLINE_LEVEL
))
12048 if (currentStyle
.HasOutlineLevel())
12050 if (currentStyle
.GetOutlineLevel() != attr
.GetOutlineLevel())
12052 // Clash of attr - mark as such
12053 clashingAttr
.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
12054 currentStyle
.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
12058 currentStyle
.SetOutlineLevel(attr
.GetOutlineLevel());
12062 WX_DEFINE_OBJARRAY(wxRichTextVariantArray
);
12064 IMPLEMENT_DYNAMIC_CLASS(wxRichTextProperties
, wxObject
)
12066 bool wxRichTextProperties::operator==(const wxRichTextProperties
& props
) const
12068 if (m_properties
.GetCount() != props
.GetCount())
12072 for (i
= 0; i
< m_properties
.GetCount(); i
++)
12074 const wxVariant
& var1
= m_properties
[i
];
12075 int idx
= props
.Find(var1
.GetName());
12078 const wxVariant
& var2
= props
.m_properties
[idx
];
12079 if (!(var1
== var2
))
12086 wxArrayString
wxRichTextProperties::GetPropertyNames() const
12090 for (i
= 0; i
< m_properties
.GetCount(); i
++)
12092 arr
.Add(m_properties
[i
].GetName());
12097 int wxRichTextProperties::Find(const wxString
& name
) const
12100 for (i
= 0; i
< m_properties
.GetCount(); i
++)
12102 if (m_properties
[i
].GetName() == name
)
12108 bool wxRichTextProperties::Remove(const wxString
& name
)
12110 int idx
= Find(name
);
12113 m_properties
.RemoveAt(idx
);
12120 wxVariant
* wxRichTextProperties::FindOrCreateProperty(const wxString
& name
)
12122 int idx
= Find(name
);
12123 if (idx
== wxNOT_FOUND
)
12124 SetProperty(name
, wxString());
12126 if (idx
!= wxNOT_FOUND
)
12128 return & (*this)[idx
];
12134 const wxVariant
& wxRichTextProperties::GetProperty(const wxString
& name
) const
12136 static const wxVariant nullVariant
;
12137 int idx
= Find(name
);
12139 return m_properties
[idx
];
12141 return nullVariant
;
12144 wxString
wxRichTextProperties::GetPropertyString(const wxString
& name
) const
12146 return GetProperty(name
).GetString();
12149 long wxRichTextProperties::GetPropertyLong(const wxString
& name
) const
12151 return GetProperty(name
).GetLong();
12154 bool wxRichTextProperties::GetPropertyBool(const wxString
& name
) const
12156 return GetProperty(name
).GetBool();
12159 double wxRichTextProperties::GetPropertyDouble(const wxString
& name
) const
12161 return GetProperty(name
).GetDouble();
12164 void wxRichTextProperties::SetProperty(const wxVariant
& variant
)
12166 wxASSERT(!variant
.GetName().IsEmpty());
12168 int idx
= Find(variant
.GetName());
12171 m_properties
.Add(variant
);
12173 m_properties
[idx
] = variant
;
12176 void wxRichTextProperties::SetProperty(const wxString
& name
, const wxVariant
& variant
)
12178 int idx
= Find(name
);
12179 wxVariant
var(variant
);
12183 m_properties
.Add(var
);
12185 m_properties
[idx
] = var
;
12188 void wxRichTextProperties::SetProperty(const wxString
& name
, const wxString
& value
)
12190 SetProperty(name
, wxVariant(value
, name
));
12193 void wxRichTextProperties::SetProperty(const wxString
& name
, long value
)
12195 SetProperty(name
, wxVariant(value
, name
));
12198 void wxRichTextProperties::SetProperty(const wxString
& name
, double value
)
12200 SetProperty(name
, wxVariant(value
, name
));
12203 void wxRichTextProperties::SetProperty(const wxString
& name
, bool value
)
12205 SetProperty(name
, wxVariant(value
, name
));
12208 void wxRichTextProperties::RemoveProperties(const wxRichTextProperties
& properties
)
12211 for (i
= 0; i
< properties
.GetCount(); i
++)
12213 wxString name
= properties
.GetProperties()[i
].GetName();
12214 if (HasProperty(name
))
12219 void wxRichTextProperties::MergeProperties(const wxRichTextProperties
& properties
)
12222 for (i
= 0; i
< properties
.GetCount(); i
++)
12224 SetProperty(properties
.GetProperties()[i
]);
12228 wxRichTextObject
* wxRichTextObjectAddress::GetObject(wxRichTextParagraphLayoutBox
* topLevelContainer
) const
12230 if (m_address
.GetCount() == 0)
12231 return topLevelContainer
;
12233 wxRichTextCompositeObject
* p
= topLevelContainer
;
12235 while (p
&& i
< m_address
.GetCount())
12237 int pos
= m_address
[i
];
12238 wxASSERT(pos
>= 0 && pos
< (int) p
->GetChildren().GetCount());
12239 if (pos
< 0 || pos
>= (int) p
->GetChildren().GetCount())
12242 wxRichTextObject
* p1
= p
->GetChild(pos
);
12243 if (i
== (m_address
.GetCount()-1))
12246 p
= wxDynamicCast(p1
, wxRichTextCompositeObject
);
12252 bool wxRichTextObjectAddress::Create(wxRichTextParagraphLayoutBox
* topLevelContainer
, wxRichTextObject
* obj
)
12256 if (topLevelContainer
== obj
)
12259 wxRichTextObject
* o
= obj
;
12262 wxRichTextCompositeObject
* p
= wxDynamicCast(o
->GetParent(), wxRichTextCompositeObject
);
12266 int pos
= p
->GetChildren().IndexOf(o
);
12270 m_address
.Insert(pos
, 0);
12272 if (p
== topLevelContainer
)
12281 bool wxRichTextSelection::operator==(const wxRichTextSelection
& sel
) const
12283 if (m_container
!= sel
.m_container
)
12285 if (m_ranges
.GetCount() != sel
.m_ranges
.GetCount())
12288 for (i
= 0; i
< m_ranges
.GetCount(); i
++)
12289 if (!(m_ranges
[i
] == sel
.m_ranges
[i
]))
12294 // Get the selections appropriate to the specified object, if any; returns wxRICHTEXT_NO_SELECTION if none
12295 // or none at the level of the object's container.
12296 wxRichTextRangeArray
wxRichTextSelection::GetSelectionForObject(wxRichTextObject
* obj
) const
12300 wxRichTextParagraphLayoutBox
* container
= obj
->GetParentContainer();
12302 if (container
== m_container
)
12305 container
= obj
->GetContainer();
12308 if (container
->GetParent())
12310 // If we found that our object's container is within the range of
12311 // a selection higher up, then assume the whole original object
12312 // is also selected.
12313 wxRichTextParagraphLayoutBox
* parentContainer
= container
->GetParentContainer();
12314 if (parentContainer
== m_container
)
12316 if (WithinSelection(container
->GetRange().GetStart(), m_ranges
))
12318 wxRichTextRangeArray ranges
;
12319 ranges
.Add(obj
->GetRange());
12324 container
= parentContainer
;
12333 return wxRichTextRangeArray();
12336 // Is the given position within the selection?
12337 bool wxRichTextSelection::WithinSelection(long pos
, wxRichTextObject
* obj
) const
12343 wxRichTextRangeArray selectionRanges
= GetSelectionForObject(obj
);
12344 return WithinSelection(pos
, selectionRanges
);
12348 // Is the given position within the selection range?
12349 bool wxRichTextSelection::WithinSelection(long pos
, const wxRichTextRangeArray
& ranges
)
12352 for (i
= 0; i
< ranges
.GetCount(); i
++)
12354 const wxRichTextRange
& range
= ranges
[i
];
12355 if (pos
>= range
.GetStart() && pos
<= range
.GetEnd())
12361 // Is the given range completely within the selection range?
12362 bool wxRichTextSelection::WithinSelection(const wxRichTextRange
& range
, const wxRichTextRangeArray
& ranges
)
12365 for (i
= 0; i
< ranges
.GetCount(); i
++)
12367 const wxRichTextRange
& eachRange
= ranges
[i
];
12368 if (range
.IsWithin(eachRange
))