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
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
);
110 // HitTest the floats
111 int HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, int flags
);
113 // Get floating object count
114 int GetFloatingObjectCount() const { return m_left
.GetCount() + m_right
.GetCount(); }
116 // Get floating objects
117 bool GetFloatingObjects(wxRichTextObjectList
& objects
) const;
120 bool DeleteFloat(wxRichTextObject
* obj
);
122 // Do we have this float already?
123 bool HasFloat(wxRichTextObject
* obj
);
125 bool HasFloats() const { return m_left
.GetCount() >0 || m_right
.GetCount() > 0; }
127 static int SearchAdjacentRect(const wxRichTextFloatRectMapArray
& array
, int point
);
129 static int GetWidthFromFloatRect(const wxRichTextFloatRectMapArray
& array
, int index
, int startY
, int endY
);
131 static void FreeFloatRectMapArray(wxRichTextFloatRectMapArray
& array
);
133 static void DrawFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
);
135 static int HitTestFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, int flags
);
138 wxRichTextFloatRectMapArray m_left
;
139 wxRichTextFloatRectMapArray m_right
;
141 wxRect m_availableRect
;
142 wxRichTextParagraph
* m_para
;
146 bool wxRichTextFloatCollector::DeleteFloat(wxRichTextObject
* obj
)
149 for (i
= 0; i
< m_left
.GetCount(); i
++)
151 if (m_left
[i
]->anchor
== obj
)
157 for (i
= 0; i
< m_right
.GetCount(); i
++)
159 if (m_right
[i
]->anchor
== obj
)
168 // Do we have this float already?
169 bool wxRichTextFloatCollector::HasFloat(wxRichTextObject
* obj
)
172 for (i
= 0; i
< m_left
.GetCount(); i
++)
174 if (m_left
[i
]->anchor
== obj
)
179 for (i
= 0; i
< m_right
.GetCount(); i
++)
181 if (m_right
[i
]->anchor
== obj
)
189 // Get floating objects
190 bool wxRichTextFloatCollector::GetFloatingObjects(wxRichTextObjectList
& objects
) const
193 for (i
= 0; i
< m_left
.GetCount(); i
++)
194 objects
.Append(m_left
[i
]->anchor
);
195 for (i
= 0; i
< m_right
.GetCount(); i
++)
196 objects
.Append(m_right
[i
]->anchor
);
202 * Binary search helper function
203 * The argument point is the Y coordinate, and this fuction
204 * always return the floating rect that contain this coordinate
205 * or under this coordinate.
207 int wxRichTextFloatCollector::SearchAdjacentRect(const wxRichTextFloatRectMapArray
& array
, int point
)
209 int end
= array
.GetCount() - 1;
222 int mid
= (start
+ end
) / 2;
223 if (array
[mid
]->startY
<= point
&& array
[mid
]->endY
>= point
)
225 else if (array
[mid
]->startY
> point
)
230 else if (array
[mid
]->endY
< point
)
240 int wxRichTextFloatCollector::GetWidthFromFloatRect(const wxRichTextFloatRectMapArray
& array
, int index
, int startY
, int endY
)
243 int len
= array
.GetCount();
245 wxASSERT(index
>= 0 && index
< len
);
247 if (array
[index
]->startY
< startY
&& array
[index
]->endY
> startY
)
248 ret
= ret
< array
[index
]->width
? array
[index
]->width
: ret
;
249 while (index
< len
&& array
[index
]->startY
<= endY
)
251 ret
= ret
< array
[index
]->width
? array
[index
]->width
: ret
;
258 wxRichTextFloatCollector::wxRichTextFloatCollector(const wxRect
& rect
) : m_left(wxRichTextFloatRectMapCmp
), m_right(wxRichTextFloatRectMapCmp
)
260 m_availableRect
= rect
;
264 void wxRichTextFloatCollector::FreeFloatRectMapArray(wxRichTextFloatRectMapArray
& array
)
266 int len
= array
.GetCount();
267 for (int i
= 0; i
< len
; i
++)
271 wxRichTextFloatCollector::~wxRichTextFloatCollector()
273 FreeFloatRectMapArray(m_left
);
274 FreeFloatRectMapArray(m_right
);
277 int wxRichTextFloatCollector::GetFitPosition(const wxRichTextFloatRectMapArray
& array
, int start
, int height
) const
279 if (array
.GetCount() == 0)
282 int i
= SearchAdjacentRect(array
, start
);
284 while (i
< (int) array
.GetCount())
286 if (array
[i
]->startY
- last
>= height
)
288 last
= array
[i
]->endY
;
295 int wxRichTextFloatCollector::GetFitPosition(int direction
, int start
, int height
) const
297 if (direction
== wxTEXT_BOX_ATTR_FLOAT_LEFT
)
298 return GetFitPosition(m_left
, start
, height
);
299 else if (direction
== wxTEXT_BOX_ATTR_FLOAT_RIGHT
)
300 return GetFitPosition(m_right
, start
, height
);
303 wxASSERT("Never should be here");
308 // Adds a floating image to the float collector.
309 // The actual positioning is done by wxRichTextParagraph::LayoutFloat.
310 void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph
* para
, wxRichTextObject
* floating
)
312 int direction
= floating
->GetFloatDirection();
314 wxPoint pos
= floating
->GetPosition();
315 wxSize size
= floating
->GetCachedSize();
316 wxRichTextFloatRectMap
*map
= new wxRichTextFloatRectMap(pos
.y
, pos
.y
+ size
.y
, size
.x
, floating
);
319 case wxTEXT_BOX_ATTR_FLOAT_NONE
:
322 case wxTEXT_BOX_ATTR_FLOAT_LEFT
:
323 // Just a not-enough simple assertion
324 wxASSERT (m_left
.Index(map
) == wxNOT_FOUND
);
327 case wxTEXT_BOX_ATTR_FLOAT_RIGHT
:
328 wxASSERT (m_right
.Index(map
) == wxNOT_FOUND
);
333 wxASSERT("Unrecognised float attribute.");
339 void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph
* para
)
341 wxRichTextObjectList::compatibility_iterator node
= para
->GetChildren().GetFirst();
344 wxRichTextObject
* floating
= node
->GetData();
346 if (floating
->IsFloating())
348 CollectFloat(para
, floating
);
351 node
= node
->GetNext();
357 wxRichTextParagraph
* wxRichTextFloatCollector::LastParagraph()
362 wxRect
wxRichTextFloatCollector::GetAvailableRect(int startY
, int endY
)
364 int widthLeft
= 0, widthRight
= 0;
365 if (m_left
.GetCount() != 0)
367 int i
= SearchAdjacentRect(m_left
, startY
);
368 if (i
< (int) m_left
.GetCount())
369 widthLeft
= GetWidthFromFloatRect(m_left
, i
, startY
, endY
);
371 if (m_right
.GetCount() != 0)
373 int j
= SearchAdjacentRect(m_right
, startY
);
374 if (j
< (int) m_right
.GetCount())
375 widthRight
= GetWidthFromFloatRect(m_right
, j
, startY
, endY
);
378 // TODO: actually we want to use the actual image positions to find the
379 // available remaining space, since the image might not be right up against
380 // the left or right edge of the container.
381 return wxRect(widthLeft
+ m_availableRect
.x
, 0, m_availableRect
.width
- widthLeft
- widthRight
, 0);
384 int wxRichTextFloatCollector::GetLastRectBottom()
387 int len
= m_left
.GetCount();
389 ret
= ret
> m_left
[len
-1]->endY
? ret
: m_left
[len
-1]->endY
;
391 len
= m_right
.GetCount();
393 ret
= ret
> m_right
[len
-1]->endY
? ret
: m_right
[len
-1]->endY
;
399 void wxRichTextFloatCollector::DrawFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& WXUNUSED(range
), const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
402 int end
= rect
.y
+ rect
.height
;
404 i
= SearchAdjacentRect(array
, start
);
405 if (i
< 0 || i
>= (int) array
.GetCount())
407 j
= SearchAdjacentRect(array
, end
);
408 if (j
< 0 || j
>= (int) array
.GetCount())
409 j
= array
.GetCount() - 1;
412 wxRichTextObject
* obj
= array
[i
]->anchor
;
413 wxRichTextRange r
= obj
->GetRange();
414 obj
->Draw(dc
, context
, r
, selection
, wxRect(obj
->GetPosition(), obj
->GetCachedSize()), descent
, style
);
419 void wxRichTextFloatCollector::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
421 if (m_left
.GetCount() > 0)
422 DrawFloat(m_left
, dc
, context
, range
, selection
, rect
, descent
, style
);
423 if (m_right
.GetCount() > 0)
424 DrawFloat(m_right
, dc
, context
, range
, selection
, rect
, descent
, style
);
427 int wxRichTextFloatCollector::HitTestFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& WXUNUSED(dc
), wxRichTextDrawingContext
& WXUNUSED(context
), const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, int WXUNUSED(flags
))
430 if (array
.GetCount() == 0)
431 return wxRICHTEXT_HITTEST_NONE
;
432 i
= SearchAdjacentRect(array
, pt
.y
);
433 if (i
< 0 || i
>= (int) array
.GetCount())
434 return wxRICHTEXT_HITTEST_NONE
;
435 if (!array
[i
]->anchor
->IsShown())
436 return wxRICHTEXT_HITTEST_NONE
;
438 wxPoint point
= array
[i
]->anchor
->GetPosition();
439 wxSize size
= array
[i
]->anchor
->GetCachedSize();
440 if (point
.x
<= pt
.x
&& point
.x
+ size
.x
>= pt
.x
441 && point
.y
<= pt
.y
&& point
.y
+ size
.y
>= pt
.y
)
443 textPosition
= array
[i
]->anchor
->GetRange().GetStart();
444 * obj
= array
[i
]->anchor
;
445 if (pt
.x
> (pt
.x
+ pt
.x
+ size
.x
) / 2)
446 return wxRICHTEXT_HITTEST_BEFORE
;
448 return wxRICHTEXT_HITTEST_AFTER
;
451 return wxRICHTEXT_HITTEST_NONE
;
454 int wxRichTextFloatCollector::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, int flags
)
456 int ret
= HitTestFloat(m_left
, dc
, context
, pt
, textPosition
, obj
, flags
);
457 if (ret
== wxRICHTEXT_HITTEST_NONE
)
459 ret
= HitTestFloat(m_right
, dc
, context
, pt
, textPosition
, obj
, flags
);
464 // Helpers for efficiency
465 inline void wxCheckSetFont(wxDC
& dc
, const wxFont
& font
)
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
, wxRichTextDrawingContext
& context
, const wxRect
& outerRect
) const
603 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
604 marginRect
= outerRect
;
605 wxRichTextAttr
attr(GetAttributes());
606 context
.ApplyVirtualAttributes(attr
, (wxRichTextObject
*) this);
607 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
611 // Invalidate the buffer. With no argument, invalidates whole buffer.
612 void wxRichTextObject::Invalidate(const wxRichTextRange
& invalidRange
)
614 if (invalidRange
!= wxRICHTEXT_NONE
)
616 // If this is a floating object, size may not be recalculated
617 // after floats have been collected in an early stage of Layout.
618 // So avoid resetting the cache for floating objects during layout.
620 SetCachedSize(wxDefaultSize
);
621 SetMaxSize(wxDefaultSize
);
622 SetMinSize(wxDefaultSize
);
626 // Convert units in tenths of a millimetre to device units
627 int wxRichTextObject::ConvertTenthsMMToPixels(wxDC
& dc
, int units
) const
632 scale
= GetBuffer()->GetScale();
633 int p
= ConvertTenthsMMToPixels(dc
.GetPPI().x
, units
, scale
);
638 // Convert units in tenths of a millimetre to device units
639 int wxRichTextObject::ConvertTenthsMMToPixels(int ppi
, int units
, double scale
)
641 // There are ppi pixels in 254.1 "1/10 mm"
643 double pixels
= ((double) units
* (double)ppi
) / 254.1;
647 // If the result is very small, make it at least one pixel in size.
648 if (pixels
== 0 && units
> 0)
654 // Convert units in pixels to tenths of a millimetre
655 int wxRichTextObject::ConvertPixelsToTenthsMM(wxDC
& dc
, int pixels
) const
660 scale
= GetBuffer()->GetScale();
662 return ConvertPixelsToTenthsMM(dc
.GetPPI().x
, p
, scale
);
665 int wxRichTextObject::ConvertPixelsToTenthsMM(int ppi
, int pixels
, double scale
)
667 // There are ppi pixels in 254.1 "1/10 mm"
669 double p
= double(pixels
);
674 int units
= int( p
* 254.1 / (double) ppi
);
678 // Draw the borders and background for the given rectangle and attributes.
679 // Width and height are taken to be the outer margin size, not the content.
680 bool wxRichTextObject::DrawBoxAttributes(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& attr
, const wxRect
& boxRect
, int flags
)
682 // Assume boxRect is the area around the content
683 wxRect marginRect
= boxRect
;
684 wxRect contentRect
, borderRect
, paddingRect
, outlineRect
;
686 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
688 // Margin is transparent. Draw background from margin.
689 if (attr
.HasBackgroundColour() || (flags
& wxRICHTEXT_DRAW_SELECTED
))
692 if (flags
& wxRICHTEXT_DRAW_SELECTED
)
694 // TODO: get selection colour from control?
695 colour
= wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
);
698 colour
= attr
.GetBackgroundColour();
701 wxBrush
brush(colour
);
705 dc
.DrawRectangle(borderRect
);
708 if (flags
& wxRICHTEXT_DRAW_GUIDELINES
)
710 wxRichTextAttr editBorderAttr
= attr
;
711 // TODO: make guideline colour configurable
712 editBorderAttr
.GetTextBoxAttr().GetBorder().SetColour(*wxLIGHT_GREY
);
713 editBorderAttr
.GetTextBoxAttr().GetBorder().SetWidth(1, wxTEXT_ATTR_UNITS_PIXELS
);
714 editBorderAttr
.GetTextBoxAttr().GetBorder().SetStyle(wxTEXT_BOX_ATTR_BORDER_SOLID
);
716 DrawBorder(dc
, buffer
, editBorderAttr
.GetTextBoxAttr().GetBorder(), borderRect
, flags
);
719 if (attr
.GetTextBoxAttr().GetBorder().IsValid())
720 DrawBorder(dc
, buffer
, attr
.GetTextBoxAttr().GetBorder(), borderRect
);
722 if (attr
.GetTextBoxAttr().GetOutline().IsValid())
723 DrawBorder(dc
, buffer
, attr
.GetTextBoxAttr().GetOutline(), outlineRect
);
729 bool wxRichTextObject::DrawBorder(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxTextAttrBorders
& attr
, const wxRect
& rect
, int WXUNUSED(flags
))
731 int borderLeft
= 0, borderRight
= 0, borderTop
= 0, borderBottom
= 0;
732 wxTextAttrDimensionConverter
converter(dc
, buffer
? buffer
->GetScale() : 1.0);
734 if (attr
.GetLeft().IsValid() && attr
.GetLeft().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
736 borderLeft
= converter
.GetPixels(attr
.GetLeft().GetWidth());
737 wxColour
col(attr
.GetLeft().GetColour());
739 // If pen width is > 1, resorts to a solid rectangle.
742 int penStyle
= wxSOLID
;
743 if (attr
.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
745 else if (attr
.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
746 penStyle
= wxLONG_DASH
;
747 wxPen
pen(col
, 1, penStyle
);
749 dc
.DrawLine(rect
.x
, rect
.y
, rect
.x
, rect
.y
+ rect
.height
);
752 else if (borderLeft
> 1)
758 dc
.DrawRectangle(rect
.x
, rect
.y
, borderLeft
, rect
.height
);
762 if (attr
.GetRight().IsValid() && attr
.GetRight().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
764 borderRight
= converter
.GetPixels(attr
.GetRight().GetWidth());
766 wxColour
col(attr
.GetRight().GetColour());
768 // If pen width is > 1, resorts to a solid rectangle.
769 if (borderRight
== 1)
771 int penStyle
= wxSOLID
;
772 if (attr
.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
774 else if (attr
.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
775 penStyle
= wxLONG_DASH
;
776 wxPen
pen(col
, 1, penStyle
);
778 dc
.DrawLine(rect
.x
+ rect
.width
, rect
.y
, rect
.x
+ rect
.width
, rect
.y
+ rect
.height
+ 1);
781 else if (borderRight
> 1)
787 dc
.DrawRectangle(rect
.x
+ rect
.width
- borderRight
, rect
.y
, borderRight
, rect
.height
);
791 if (attr
.GetTop().IsValid() && attr
.GetTop().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
793 borderTop
= converter
.GetPixels(attr
.GetTop().GetWidth());
795 wxColour
col(attr
.GetTop().GetColour());
797 // If pen width is > 1, resorts to a solid rectangle.
800 int penStyle
= wxSOLID
;
801 if (attr
.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
803 else if (attr
.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
804 penStyle
= wxLONG_DASH
;
805 wxPen
pen(col
, 1, penStyle
);
807 dc
.DrawLine(rect
.x
, rect
.y
, rect
.x
+ rect
.width
, rect
.y
);
810 else if (borderTop
> 1)
816 dc
.DrawRectangle(rect
.x
, rect
.y
, rect
.width
, borderTop
);
820 if (attr
.GetBottom().IsValid() && attr
.GetBottom().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
822 borderBottom
= converter
.GetPixels(attr
.GetBottom().GetWidth());
823 wxColour
col(attr
.GetTop().GetColour());
825 // If pen width is > 1, resorts to a solid rectangle.
826 if (borderBottom
== 1)
828 int penStyle
= wxSOLID
;
829 if (attr
.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
831 else if (attr
.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
832 penStyle
= wxLONG_DASH
;
833 wxPen
pen(col
, 1, penStyle
);
835 dc
.DrawLine(rect
.x
, rect
.y
+ rect
.height
, rect
.x
+ rect
.width
, rect
.y
+ rect
.height
);
838 else if (borderBottom
> 1)
844 dc
.DrawRectangle(rect
.x
, rect
.y
+ rect
.height
- borderBottom
, rect
.width
, borderBottom
);
851 // Get the various rectangles of the box model in pixels. You can either specify contentRect (inner)
852 // or marginRect (outer), and the other must be the default rectangle (no width or height).
853 // Note that the outline doesn't affect the position of the rectangle, it's drawn in whatever space
856 // | Margin | Border | Padding | CONTENT | Padding | Border | Margin |
858 bool wxRichTextObject::GetBoxRects(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& attr
, wxRect
& marginRect
, wxRect
& borderRect
, wxRect
& contentRect
, wxRect
& paddingRect
, wxRect
& outlineRect
)
860 int borderLeft
= 0, borderRight
= 0, borderTop
= 0, borderBottom
= 0;
861 int outlineLeft
= 0, outlineRight
= 0, outlineTop
= 0, outlineBottom
= 0;
862 int paddingLeft
= 0, paddingRight
= 0, paddingTop
= 0, paddingBottom
= 0;
863 int marginLeft
= 0, marginRight
= 0, marginTop
= 0, marginBottom
= 0;
865 wxTextAttrDimensionConverter
converter(dc
, buffer
? buffer
->GetScale() : 1.0);
867 if (attr
.GetTextBoxAttr().GetMargins().GetLeft().IsValid())
868 marginLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetLeft());
869 if (attr
.GetTextBoxAttr().GetMargins().GetRight().IsValid())
870 marginRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetRight());
871 if (attr
.GetTextBoxAttr().GetMargins().GetTop().IsValid())
872 marginTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetTop());
873 if (attr
.GetTextBoxAttr().GetMargins().GetBottom().IsValid())
874 marginBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetBottom());
876 if (attr
.GetTextBoxAttr().GetBorder().GetLeft().GetWidth().IsValid())
877 borderLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetLeft().GetWidth());
878 if (attr
.GetTextBoxAttr().GetBorder().GetRight().GetWidth().IsValid())
879 borderRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetRight().GetWidth());
880 if (attr
.GetTextBoxAttr().GetBorder().GetTop().GetWidth().IsValid())
881 borderTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetTop().GetWidth());
882 if (attr
.GetTextBoxAttr().GetBorder().GetBottom().GetWidth().IsValid())
883 borderBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetBottom().GetWidth());
885 if (attr
.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
886 paddingLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetLeft());
887 if (attr
.GetTextBoxAttr().GetPadding().GetRight().IsValid())
888 paddingRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetRight());
889 if (attr
.GetTextBoxAttr().GetPadding().GetTop().IsValid())
890 paddingTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetTop());
891 if (attr
.GetTextBoxAttr().GetPadding().GetBottom().IsValid())
892 paddingBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetBottom());
894 if (attr
.GetTextBoxAttr().GetOutline().GetLeft().GetWidth().IsValid())
895 outlineLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetLeft().GetWidth());
896 if (attr
.GetTextBoxAttr().GetOutline().GetRight().GetWidth().IsValid())
897 outlineRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetRight().GetWidth());
898 if (attr
.GetTextBoxAttr().GetOutline().GetTop().GetWidth().IsValid())
899 outlineTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetTop().GetWidth());
900 if (attr
.GetTextBoxAttr().GetOutline().GetBottom().GetWidth().IsValid())
901 outlineBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetBottom().GetWidth());
903 int leftTotal
= marginLeft
+ borderLeft
+ paddingLeft
;
904 int rightTotal
= marginRight
+ borderRight
+ paddingRight
;
905 int topTotal
= marginTop
+ borderTop
+ paddingTop
;
906 int bottomTotal
= marginBottom
+ borderBottom
+ paddingBottom
;
908 if (marginRect
!= wxRect())
910 contentRect
.x
= marginRect
.x
+ leftTotal
;
911 contentRect
.y
= marginRect
.y
+ topTotal
;
912 contentRect
.width
= marginRect
.width
- (leftTotal
+ rightTotal
);
913 contentRect
.height
= marginRect
.height
- (topTotal
+ bottomTotal
);
917 marginRect
.x
= contentRect
.x
- leftTotal
;
918 marginRect
.y
= contentRect
.y
- topTotal
;
919 marginRect
.width
= contentRect
.width
+ (leftTotal
+ rightTotal
);
920 marginRect
.height
= contentRect
.height
+ (topTotal
+ bottomTotal
);
923 borderRect
.x
= marginRect
.x
+ marginLeft
;
924 borderRect
.y
= marginRect
.y
+ marginTop
;
925 borderRect
.width
= marginRect
.width
- (marginLeft
+ marginRight
);
926 borderRect
.height
= marginRect
.height
- (marginTop
+ marginBottom
);
928 paddingRect
.x
= marginRect
.x
+ marginLeft
+ borderLeft
;
929 paddingRect
.y
= marginRect
.y
+ marginTop
+ borderTop
;
930 paddingRect
.width
= marginRect
.width
- (marginLeft
+ marginRight
+ borderLeft
+ borderRight
);
931 paddingRect
.height
= marginRect
.height
- (marginTop
+ marginBottom
+ borderTop
+ borderBottom
);
933 // The outline is outside the margin and doesn't influence the overall box position or content size.
934 outlineRect
.x
= marginRect
.x
- outlineLeft
;
935 outlineRect
.y
= marginRect
.y
- outlineTop
;
936 outlineRect
.width
= marginRect
.width
+ (outlineLeft
+ outlineRight
);
937 outlineRect
.height
= marginRect
.height
+ (outlineTop
+ outlineBottom
);
942 // Get the total margin for the object in pixels, taking into account margin, padding and border size
943 bool wxRichTextObject::GetTotalMargin(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& attr
, int& leftMargin
, int& rightMargin
,
944 int& topMargin
, int& bottomMargin
)
946 // Assume boxRect is the area around the content
947 wxRect contentRect
, marginRect
, borderRect
, paddingRect
, outlineRect
;
948 marginRect
= wxRect(0, 0, 1000, 1000);
950 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
952 leftMargin
= contentRect
.GetLeft() - marginRect
.GetLeft();
953 rightMargin
= marginRect
.GetRight() - contentRect
.GetRight();
954 topMargin
= contentRect
.GetTop() - marginRect
.GetTop();
955 bottomMargin
= marginRect
.GetBottom() - contentRect
.GetBottom();
960 // Returns the rectangle which the child has available to it given restrictions specified in the
961 // child attribute, e.g. 50% width of the parent, 400 pixels, x position 20% of the parent, etc.
962 // availableContainerSpace might be a parent that the cell has to compute its width relative to.
963 // E.g. a cell that's 50% of its parent.
964 wxRect
wxRichTextObject::AdjustAvailableSpace(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& WXUNUSED(parentAttr
), const wxRichTextAttr
& childAttr
, const wxRect
& availableParentSpace
, const wxRect
& availableContainerSpace
)
966 wxRect rect
= availableParentSpace
;
969 scale
= buffer
->GetScale();
971 wxTextAttrDimensionConverter
converter(dc
, scale
, availableContainerSpace
.GetSize());
973 if (childAttr
.GetTextBoxAttr().GetWidth().IsValid())
974 rect
.width
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetWidth());
976 if (childAttr
.GetTextBoxAttr().GetHeight().IsValid())
977 rect
.height
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetHeight());
979 // Can specify either left or right for the position (we're assuming we can't
980 // set the left and right edges to effectively set the size. Would we want to do that?)
981 if (childAttr
.GetTextBoxAttr().GetPosition().GetLeft().IsValid())
983 rect
.x
= rect
.x
+ converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetLeft());
985 else if (childAttr
.GetTextBoxAttr().GetPosition().GetRight().IsValid())
987 int x
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetRight());
988 if (childAttr
.GetTextBoxAttr().GetPosition().GetRight().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE
)
989 rect
.x
= availableContainerSpace
.x
+ availableContainerSpace
.width
- rect
.width
;
994 if (childAttr
.GetTextBoxAttr().GetPosition().GetTop().IsValid())
996 rect
.y
= rect
.y
+ converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetTop());
998 else if (childAttr
.GetTextBoxAttr().GetPosition().GetBottom().IsValid())
1000 int y
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetBottom());
1001 if (childAttr
.GetTextBoxAttr().GetPosition().GetBottom().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE
)
1002 rect
.y
= availableContainerSpace
.y
+ availableContainerSpace
.height
- rect
.height
;
1007 if (rect
.GetWidth() > availableParentSpace
.GetWidth())
1008 rect
.SetWidth(availableParentSpace
.GetWidth());
1013 // Dump to output stream for debugging
1014 void wxRichTextObject::Dump(wxTextOutputStream
& stream
)
1016 stream
<< GetClassInfo()->GetClassName() << wxT("\n");
1017 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");
1018 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");
1021 // Gets the containing buffer
1022 wxRichTextBuffer
* wxRichTextObject::GetBuffer() const
1024 const wxRichTextObject
* obj
= this;
1025 while (obj
&& !obj
->IsKindOf(CLASSINFO(wxRichTextBuffer
)))
1026 obj
= obj
->GetParent();
1027 return wxDynamicCast(obj
, wxRichTextBuffer
);
1030 // Get the absolute object position, by traversing up the child/parent hierarchy
1031 wxPoint
wxRichTextObject::GetAbsolutePosition() const
1033 wxPoint pt
= GetPosition();
1035 wxRichTextObject
* p
= GetParent();
1038 pt
= pt
+ p
->GetPosition();
1045 // Hit-testing: returns a flag indicating hit test details, plus
1046 // information about position
1047 int wxRichTextObject::HitTest(wxDC
& WXUNUSED(dc
), wxRichTextDrawingContext
& WXUNUSED(context
), const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int WXUNUSED(flags
))
1050 return wxRICHTEXT_HITTEST_NONE
;
1052 wxRect rect
= GetRect();
1053 if (pt
.x
>= rect
.x
&& pt
.x
< rect
.x
+ rect
.width
&&
1054 pt
.y
>= rect
.y
&& pt
.y
< rect
.y
+ rect
.height
)
1057 *contextObj
= GetParentContainer();
1058 textPosition
= GetRange().GetStart();
1059 return wxRICHTEXT_HITTEST_ON
;
1062 return wxRICHTEXT_HITTEST_NONE
;
1065 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1066 // lays out the object again using the maximum ('best') size
1067 bool wxRichTextObject::LayoutToBestSize(wxDC
& dc
, wxRichTextDrawingContext
& context
, wxRichTextBuffer
* buffer
,
1068 const wxRichTextAttr
& parentAttr
, const wxRichTextAttr
& attr
,
1069 const wxRect
& availableParentSpace
, const wxRect
& availableContainerSpace
,
1072 wxRect availableChildRect
= AdjustAvailableSpace(dc
, buffer
, parentAttr
, attr
, availableParentSpace
, availableContainerSpace
);
1073 wxRect originalAvailableRect
= availableChildRect
;
1074 Layout(dc
, context
, availableChildRect
, availableContainerSpace
, style
);
1076 wxSize maxSize
= GetMaxSize();
1078 // Don't ignore if maxSize.x is zero, since we need to redo the paragraph's lines
1080 if (!attr
.GetTextBoxAttr().GetWidth().IsValid() && maxSize
.x
< availableChildRect
.width
)
1082 // Redo the layout with a fixed, minimum size this time.
1083 Invalidate(wxRICHTEXT_ALL
);
1084 wxRichTextAttr
newAttr(attr
);
1085 newAttr
.GetTextBoxAttr().GetWidth().SetValue(maxSize
.x
, wxTEXT_ATTR_UNITS_PIXELS
);
1086 newAttr
.GetTextBoxAttr().GetWidth().SetPosition(wxTEXT_BOX_ATTR_POSITION_ABSOLUTE
);
1088 availableChildRect
= AdjustAvailableSpace(dc
, buffer
, parentAttr
, newAttr
, availableParentSpace
, availableContainerSpace
);
1090 // If a paragraph, align the whole paragraph.
1091 // Problem with this: if we're limited by a floating object, a line may be centered
1092 // w.r.t. the smaller resulting box rather than the actual available width.
1093 if (attr
.HasAlignment() && !GetContainer()->GetFloatCollector()->HasFloats()) // FIXME: aligning whole paragraph not compatible with floating objects
1095 // centering, right-justification
1096 if (attr
.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE
)
1098 availableChildRect
.x
= (originalAvailableRect
.GetWidth() - availableChildRect
.GetWidth())/2 + availableChildRect
.x
;
1100 else if (attr
.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT
)
1102 availableChildRect
.x
= availableChildRect
.x
+ originalAvailableRect
.GetWidth() - availableChildRect
.GetWidth();
1106 Layout(dc
, context
, availableChildRect
, availableContainerSpace
, style
);
1120 // Move the object recursively, by adding the offset from old to new
1121 void wxRichTextObject::Move(const wxPoint
& pt
)
1128 * wxRichTextCompositeObject
1129 * This is the base for drawable objects.
1132 IMPLEMENT_CLASS(wxRichTextCompositeObject
, wxRichTextObject
)
1134 wxRichTextCompositeObject::wxRichTextCompositeObject(wxRichTextObject
* parent
):
1135 wxRichTextObject(parent
)
1139 wxRichTextCompositeObject::~wxRichTextCompositeObject()
1144 /// Get the nth child
1145 wxRichTextObject
* wxRichTextCompositeObject::GetChild(size_t n
) const
1147 wxASSERT ( n
< m_children
.GetCount() );
1149 return m_children
.Item(n
)->GetData();
1152 /// Append a child, returning the position
1153 size_t wxRichTextCompositeObject::AppendChild(wxRichTextObject
* child
)
1155 m_children
.Append(child
);
1156 child
->SetParent(this);
1157 return m_children
.GetCount() - 1;
1160 /// Insert the child in front of the given object, or at the beginning
1161 bool wxRichTextCompositeObject::InsertChild(wxRichTextObject
* child
, wxRichTextObject
* inFrontOf
)
1165 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(inFrontOf
);
1166 m_children
.Insert(node
, child
);
1169 m_children
.Insert(child
);
1170 child
->SetParent(this);
1175 /// Delete the child
1176 bool wxRichTextCompositeObject::RemoveChild(wxRichTextObject
* child
, bool deleteChild
)
1178 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(child
);
1181 wxRichTextObject
* obj
= node
->GetData();
1182 m_children
.Erase(node
);
1191 /// Delete all children
1192 bool wxRichTextCompositeObject::DeleteChildren()
1194 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1197 wxRichTextObjectList::compatibility_iterator oldNode
= node
;
1199 wxRichTextObject
* child
= node
->GetData();
1200 child
->Dereference(); // Only delete if reference count is zero
1202 node
= node
->GetNext();
1203 m_children
.Erase(oldNode
);
1209 /// Get the child count
1210 size_t wxRichTextCompositeObject::GetChildCount() const
1212 return m_children
.GetCount();
1216 void wxRichTextCompositeObject::Copy(const wxRichTextCompositeObject
& obj
)
1218 wxRichTextObject::Copy(obj
);
1222 wxRichTextObjectList::compatibility_iterator node
= obj
.m_children
.GetFirst();
1225 wxRichTextObject
* child
= node
->GetData();
1226 wxRichTextObject
* newChild
= child
->Clone();
1227 newChild
->SetParent(this);
1228 m_children
.Append(newChild
);
1230 node
= node
->GetNext();
1234 /// Hit-testing: returns a flag indicating hit test details, plus
1235 /// information about position
1236 int wxRichTextCompositeObject::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
1239 return wxRICHTEXT_HITTEST_NONE
;
1241 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1244 wxRichTextObject
* child
= node
->GetData();
1246 if (child
->IsShown() && child
->IsTopLevel() && (flags
& wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS
))
1248 // Just check if we hit the overall object
1249 int ret
= child
->wxRichTextObject::HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
1250 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
1253 else if (child
->IsShown())
1255 int ret
= child
->HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
1256 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
1260 node
= node
->GetNext();
1263 return wxRICHTEXT_HITTEST_NONE
;
1266 /// Finds the absolute position and row height for the given character position
1267 bool wxRichTextCompositeObject::FindPosition(wxDC
& dc
, wxRichTextDrawingContext
& context
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
1269 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1272 wxRichTextObject
* child
= node
->GetData();
1274 // Don't recurse if the child is a top-level object,
1275 // such as a text box, because the character position will no longer
1276 // apply. By definition, a top-level object has its own range of
1277 // character positions.
1278 if (!child
->IsTopLevel() && child
->FindPosition(dc
, context
, index
, pt
, height
, forceLineStart
))
1281 node
= node
->GetNext();
1288 void wxRichTextCompositeObject::CalculateRange(long start
, long& end
)
1290 long current
= start
;
1291 long lastEnd
= current
;
1299 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1302 wxRichTextObject
* child
= node
->GetData();
1305 child
->CalculateRange(current
, childEnd
);
1308 current
= childEnd
+ 1;
1310 node
= node
->GetNext();
1315 // A top-level object always has a range of size 1,
1316 // because its children don't count at this level.
1318 m_range
.SetRange(start
, start
);
1320 // An object with no children has zero length
1321 if (m_children
.GetCount() == 0)
1323 m_ownRange
.SetRange(0, lastEnd
);
1329 // An object with no children has zero length
1330 if (m_children
.GetCount() == 0)
1333 m_range
.SetRange(start
, end
);
1337 /// Delete range from layout.
1338 bool wxRichTextCompositeObject::DeleteRange(const wxRichTextRange
& range
)
1340 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1344 wxRichTextObject
* obj
= (wxRichTextObject
*) node
->GetData();
1345 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
1347 // Delete the range in each paragraph
1349 // When a chunk has been deleted, internally the content does not
1350 // now match the ranges.
1351 // However, so long as deletion is not done on the same object twice this is OK.
1352 // If you may delete content from the same object twice, recalculate
1353 // the ranges inbetween DeleteRange calls by calling CalculateRanges, and
1354 // adjust the range you're deleting accordingly.
1356 if (!obj
->GetRange().IsOutside(range
))
1358 // No need to delete within a top-level object; just removing this object will do fine
1359 if (!obj
->IsTopLevel())
1360 obj
->DeleteRange(range
);
1362 // Delete an empty object, or paragraph within this range.
1363 if (obj
->IsEmpty() ||
1364 (range
.GetStart() <= obj
->GetRange().GetStart() && range
.GetEnd() >= obj
->GetRange().GetEnd()))
1366 // An empty paragraph has length 1, so won't be deleted unless the
1367 // whole range is deleted.
1368 RemoveChild(obj
, true);
1378 /// Get any text in this object for the given range
1379 wxString
wxRichTextCompositeObject::GetTextForRange(const wxRichTextRange
& range
) const
1382 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1385 wxRichTextObject
* child
= node
->GetData();
1386 wxRichTextRange childRange
= range
;
1387 if (!child
->GetRange().IsOutside(range
))
1389 childRange
.LimitTo(child
->GetRange());
1391 wxString childText
= child
->GetTextForRange(childRange
);
1395 node
= node
->GetNext();
1401 /// Get the child object at the given character position
1402 wxRichTextObject
* wxRichTextCompositeObject::GetChildAtPosition(long pos
) const
1404 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1407 wxRichTextObject
* child
= node
->GetData();
1408 if (child
->GetRange().GetStart() == pos
)
1410 node
= node
->GetNext();
1415 /// Recursively merge all pieces that can be merged.
1416 bool wxRichTextCompositeObject::Defragment(const wxRichTextRange
& range
)
1418 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1421 wxRichTextObject
* child
= node
->GetData();
1422 if (range
== wxRICHTEXT_ALL
|| !child
->GetRange().IsOutside(range
))
1424 wxRichTextCompositeObject
* composite
= wxDynamicCast(child
, wxRichTextCompositeObject
);
1426 composite
->Defragment();
1428 if (node
->GetNext())
1430 wxRichTextObject
* nextChild
= node
->GetNext()->GetData();
1431 if (child
->CanMerge(nextChild
) && child
->Merge(nextChild
))
1433 nextChild
->Dereference();
1434 m_children
.Erase(node
->GetNext());
1436 // Don't set node -- we'll see if we can merge again with the next
1440 node
= node
->GetNext();
1443 node
= node
->GetNext();
1446 node
= node
->GetNext();
1449 // Delete any remaining empty objects, but leave at least one empty object per composite object.
1450 if (GetChildCount() > 1)
1452 node
= m_children
.GetFirst();
1455 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
1456 wxRichTextObject
* child
= node
->GetData();
1457 if (range
== wxRICHTEXT_ALL
|| !child
->GetRange().IsOutside(range
))
1459 if (child
->IsEmpty())
1461 child
->Dereference();
1462 m_children
.Erase(node
);
1467 node
= node
->GetNext();
1474 /// Dump to output stream for debugging
1475 void wxRichTextCompositeObject::Dump(wxTextOutputStream
& stream
)
1477 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1480 wxRichTextObject
* child
= node
->GetData();
1481 child
->Dump(stream
);
1482 node
= node
->GetNext();
1486 /// Get/set the object size for the given range. Returns false if the range
1487 /// is invalid for this object.
1488 bool wxRichTextCompositeObject::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
1490 if (!range
.IsWithin(GetRange()))
1495 wxArrayInt childExtents
;
1502 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1505 wxRichTextObject
* child
= node
->GetData();
1506 if (!child
->GetRange().IsOutside(range
))
1508 // Floating objects have a zero size within the paragraph.
1509 if (child
->IsFloating())
1514 if (partialExtents
->GetCount() > 0)
1515 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
1519 partialExtents
->Add(0 /* zero size */ + lastSize
);
1526 wxRichTextRange rangeToUse
= range
;
1527 rangeToUse
.LimitTo(child
->GetRange());
1528 if (child
->IsTopLevel())
1529 rangeToUse
= child
->GetOwnRange();
1531 int childDescent
= 0;
1533 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we're already cached the size,
1534 // but it's only going to be used after caching has taken place.
1535 if ((flags
& wxRICHTEXT_HEIGHT_ONLY
) && child
->GetCachedSize().y
!= 0)
1537 childDescent
= child
->GetDescent();
1538 childSize
= child
->GetCachedSize();
1540 sz
.y
= wxMax(sz
.y
, childSize
.y
);
1541 sz
.x
+= childSize
.x
;
1542 descent
= wxMax(descent
, childDescent
);
1544 else if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, context
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
), p
))
1546 sz
.y
= wxMax(sz
.y
, childSize
.y
);
1547 sz
.x
+= childSize
.x
;
1548 descent
= wxMax(descent
, childDescent
);
1550 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange() || child
->IsTopLevel()))
1552 child
->SetCachedSize(childSize
);
1553 child
->SetDescent(childDescent
);
1559 if (partialExtents
->GetCount() > 0)
1560 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
1565 for (i
= 0; i
< childExtents
.GetCount(); i
++)
1567 partialExtents
->Add(childExtents
[i
] + lastSize
);
1577 node
= node
->GetNext();
1583 // Invalidate the buffer. With no argument, invalidates whole buffer.
1584 void wxRichTextCompositeObject::Invalidate(const wxRichTextRange
& invalidRange
)
1586 wxRichTextObject::Invalidate(invalidRange
);
1588 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1591 wxRichTextObject
* child
= node
->GetData();
1592 if (invalidRange
!= wxRICHTEXT_ALL
&& invalidRange
!= wxRICHTEXT_NONE
&& child
->GetRange().IsOutside(invalidRange
))
1596 else if (child
->IsTopLevel())
1598 if (invalidRange
== wxRICHTEXT_NONE
)
1599 child
->Invalidate(wxRICHTEXT_NONE
);
1601 child
->Invalidate(wxRICHTEXT_ALL
); // All children must be invalidated if within parent range
1604 child
->Invalidate(invalidRange
);
1605 node
= node
->GetNext();
1609 // Move the object recursively, by adding the offset from old to new
1610 void wxRichTextCompositeObject::Move(const wxPoint
& pt
)
1612 wxPoint oldPos
= GetPosition();
1614 wxPoint offset
= pt
- oldPos
;
1616 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1619 wxRichTextObject
* child
= node
->GetData();
1620 wxPoint childPos
= child
->GetPosition() + offset
;
1621 child
->Move(childPos
);
1622 node
= node
->GetNext();
1628 * wxRichTextParagraphLayoutBox
1629 * This box knows how to lay out paragraphs.
1632 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraphLayoutBox
, wxRichTextCompositeObject
)
1634 wxRichTextParagraphLayoutBox::wxRichTextParagraphLayoutBox(wxRichTextObject
* parent
):
1635 wxRichTextCompositeObject(parent
)
1640 wxRichTextParagraphLayoutBox::~wxRichTextParagraphLayoutBox()
1642 if (m_floatCollector
)
1644 delete m_floatCollector
;
1645 m_floatCollector
= NULL
;
1649 /// Initialize the object.
1650 void wxRichTextParagraphLayoutBox::Init()
1654 // For now, assume is the only box and has no initial size.
1655 m_range
= wxRichTextRange(0, -1);
1656 m_ownRange
= wxRichTextRange(0, -1);
1658 m_invalidRange
= wxRICHTEXT_ALL
;
1661 m_partialParagraph
= false;
1662 m_floatCollector
= NULL
;
1665 void wxRichTextParagraphLayoutBox::Clear()
1669 if (m_floatCollector
)
1670 delete m_floatCollector
;
1671 m_floatCollector
= NULL
;
1672 m_partialParagraph
= false;
1676 void wxRichTextParagraphLayoutBox::Copy(const wxRichTextParagraphLayoutBox
& obj
)
1680 wxRichTextCompositeObject::Copy(obj
);
1682 m_partialParagraph
= obj
.m_partialParagraph
;
1683 m_defaultAttributes
= obj
.m_defaultAttributes
;
1686 // Gather information about floating objects; only gather floats for those paragraphs that
1687 // will not be formatted again due to optimization, after which floats will be gathered per-paragraph
1689 bool wxRichTextParagraphLayoutBox::UpdateFloatingObjects(const wxRect
& availableRect
, wxRichTextObject
* untilObj
)
1691 if (m_floatCollector
!= NULL
)
1692 delete m_floatCollector
;
1693 m_floatCollector
= new wxRichTextFloatCollector(availableRect
);
1694 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1695 // Only gather floats up to the point we'll start formatting paragraphs.
1696 while (untilObj
&& node
&& node
->GetData() != untilObj
)
1698 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
1699 wxASSERT (child
!= NULL
);
1701 m_floatCollector
->CollectFloat(child
);
1702 node
= node
->GetNext();
1708 // Returns the style sheet associated with the overall buffer.
1709 wxRichTextStyleSheet
* wxRichTextParagraphLayoutBox::GetStyleSheet() const
1711 return GetBuffer() ? GetBuffer()->GetStyleSheet() : (wxRichTextStyleSheet
*) NULL
;
1714 // Get the number of floating objects at this level
1715 int wxRichTextParagraphLayoutBox::GetFloatingObjectCount() const
1717 if (m_floatCollector
)
1718 return m_floatCollector
->GetFloatingObjectCount();
1723 // Get a list of floating objects
1724 bool wxRichTextParagraphLayoutBox::GetFloatingObjects(wxRichTextObjectList
& objects
) const
1726 if (m_floatCollector
)
1728 return m_floatCollector
->GetFloatingObjects(objects
);
1735 void wxRichTextParagraphLayoutBox::UpdateRanges()
1739 start
= GetRange().GetStart();
1741 CalculateRange(start
, end
);
1745 int wxRichTextParagraphLayoutBox::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
1748 return wxRICHTEXT_HITTEST_NONE
;
1750 int ret
= wxRICHTEXT_HITTEST_NONE
;
1751 if (m_floatCollector
&& (flags
& wxRICHTEXT_HITTEST_NO_FLOATING_OBJECTS
) == 0)
1752 ret
= m_floatCollector
->HitTest(dc
, context
, pt
, textPosition
, obj
, flags
);
1754 if (ret
== wxRICHTEXT_HITTEST_NONE
)
1755 return wxRichTextCompositeObject::HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
1763 /// Draw the floating objects
1764 void wxRichTextParagraphLayoutBox::DrawFloats(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
1766 if (m_floatCollector
)
1767 m_floatCollector
->Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
1770 void wxRichTextParagraphLayoutBox::MoveAnchoredObjectToParagraph(wxRichTextParagraph
* from
, wxRichTextParagraph
* to
, wxRichTextObject
* obj
)
1775 from
->RemoveChild(obj
);
1776 to
->AppendChild(obj
);
1780 bool wxRichTextParagraphLayoutBox::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
1785 wxRect
thisRect(GetPosition(), GetCachedSize());
1787 wxRichTextAttr
attr(GetAttributes());
1788 context
.ApplyVirtualAttributes(attr
, this);
1791 if (selection
.IsValid() && GetParentContainer() != this && selection
.WithinSelection(GetRange().GetStart(), GetParentContainer()))
1792 flags
|= wxRICHTEXT_DRAW_SELECTED
;
1794 // Don't draw guidelines if at top level
1795 int theseFlags
= flags
;
1797 theseFlags
&= ~wxRICHTEXT_DRAW_GUIDELINES
;
1798 DrawBoxAttributes(dc
, GetBuffer(), attr
, thisRect
, theseFlags
);
1800 DrawFloats(dc
, context
, range
, selection
, rect
, descent
, style
);
1801 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1804 wxRichTextObject
* child
= node
->GetData();
1806 if (child
&& !child
->GetRange().IsOutside(range
))
1808 wxRect
childRect(child
->GetPosition(), child
->GetCachedSize());
1809 wxRichTextRange childRange
= range
;
1810 if (child
->IsTopLevel())
1812 childRange
= child
->GetOwnRange();
1815 if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) == 0) && childRect
.GetTop() > rect
.GetBottom())
1820 else if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) == 0) && childRect
.GetBottom() < rect
.GetTop())
1825 child
->Draw(dc
, context
, childRange
, selection
, rect
, descent
, style
);
1828 node
= node
->GetNext();
1833 /// Lay the item out
1834 bool wxRichTextParagraphLayoutBox::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
)
1836 SetPosition(rect
.GetPosition());
1841 wxRect availableSpace
;
1842 bool formatRect
= (style
& wxRICHTEXT_LAYOUT_SPECIFIED_RECT
) == wxRICHTEXT_LAYOUT_SPECIFIED_RECT
;
1844 wxRichTextAttr
attr(GetAttributes());
1845 context
.ApplyVirtualAttributes(attr
, this);
1847 // If only laying out a specific area, the passed rect has a different meaning:
1848 // the visible part of the buffer. This is used in wxRichTextCtrl::OnSize,
1849 // so that during a size, only the visible part will be relaid out, or
1850 // it would take too long causing flicker. As an approximation, we assume that
1851 // everything up to the start of the visible area is laid out correctly.
1854 wxRect
rect2(0, 0, rect
.width
, rect
.height
);
1855 availableSpace
= GetAvailableContentArea(dc
, context
, rect2
);
1857 // Invalidate the part of the buffer from the first visible line
1858 // to the end. If other parts of the buffer are currently invalid,
1859 // then they too will be taken into account if they are above
1860 // the visible point.
1862 wxRichTextLine
* line
= GetLineAtYPosition(rect
.y
);
1864 startPos
= line
->GetAbsoluteRange().GetStart();
1866 Invalidate(wxRichTextRange(startPos
, GetOwnRange().GetEnd()));
1870 availableSpace
= GetAvailableContentArea(dc
, context
, rect
);
1873 int leftMargin
, rightMargin
, topMargin
, bottomMargin
;
1874 wxRichTextObject::GetTotalMargin(dc
, GetBuffer(), attr
, leftMargin
, rightMargin
,
1875 topMargin
, bottomMargin
);
1880 // The maximum paragraph maximum width, so we can set the overall maximum width for this object
1881 int maxMaxWidth
= 0;
1883 // The maximum paragraph minimum width, so we can set the overall minimum width for this object
1884 int maxMinWidth
= 0;
1886 // If we have vertical alignment, we must recalculate everything.
1887 bool hasVerticalAlignment
= (attr
.GetTextBoxAttr().HasVerticalAlignment() &&
1888 (attr
.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP
));
1890 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1892 bool layoutAll
= true;
1894 // Get invalid range, rounding to paragraph start/end.
1895 wxRichTextRange invalidRange
= GetInvalidRange(true);
1897 if (invalidRange
== wxRICHTEXT_NONE
&& !formatRect
)
1900 if (invalidRange
== wxRICHTEXT_ALL
|| hasVerticalAlignment
)
1902 else // If we know what range is affected, start laying out from that point on.
1903 if (invalidRange
.GetStart() >= GetOwnRange().GetStart())
1905 wxRichTextParagraph
* firstParagraph
= GetParagraphAtPosition(invalidRange
.GetStart());
1908 wxRichTextObjectList::compatibility_iterator firstNode
= m_children
.Find(firstParagraph
);
1909 wxRichTextObjectList::compatibility_iterator previousNode
;
1911 previousNode
= firstNode
->GetPrevious();
1916 wxRichTextParagraph
* previousParagraph
= wxDynamicCast(previousNode
->GetData(), wxRichTextParagraph
);
1917 availableSpace
.y
= previousParagraph
->GetPosition().y
+ previousParagraph
->GetCachedSize().y
;
1920 // Now we're going to start iterating from the first affected paragraph.
1928 // Gather information about only those floating objects that will not be formatted,
1929 // after which floats will be gathered per-paragraph during layout.
1930 UpdateFloatingObjects(availableSpace
, node
? node
->GetData() : (wxRichTextObject
*) NULL
);
1932 // A way to force speedy rest-of-buffer layout (the 'else' below)
1933 bool forceQuickLayout
= false;
1935 // First get the size of the paragraphs we won't be laying out
1936 wxRichTextObjectList::compatibility_iterator n
= m_children
.GetFirst();
1937 while (n
&& n
!= node
)
1939 wxRichTextParagraph
* child
= wxDynamicCast(n
->GetData(), wxRichTextParagraph
);
1942 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
1943 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
1944 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
1951 // Assume this box only contains paragraphs
1953 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
1954 // Unsure if this is needed
1955 // wxCHECK_MSG( child, false, wxT("Unknown object in layout") );
1957 if (child
&& child
->IsShown())
1959 // TODO: what if the child hasn't been laid out (e.g. involved in Undo) but still has 'old' lines
1960 if ( !forceQuickLayout
&&
1962 child
->GetLines().IsEmpty() ||
1963 !child
->GetRange().IsOutside(invalidRange
)) )
1965 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1966 // lays out the object again using the minimum size
1967 child
->LayoutToBestSize(dc
, context
, GetBuffer(),
1968 attr
, child
->GetAttributes(), availableSpace
, rect
, style
&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT
);
1970 // Layout must set the cached size
1971 availableSpace
.y
+= child
->GetCachedSize().y
;
1972 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
1973 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
1974 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
1976 // If we're just formatting the visible part of the buffer,
1977 // and we're now past the bottom of the window, and we don't have any
1978 // floating objects (since they may cause wrapping to change for the rest of the
1979 // the buffer), start quick layout.
1980 if (!hasVerticalAlignment
&& formatRect
&& child
->GetPosition().y
> rect
.GetBottom() && GetFloatingObjectCount() == 0)
1981 forceQuickLayout
= true;
1985 // We're outside the immediately affected range, so now let's just
1986 // move everything up or down. This assumes that all the children have previously
1987 // been laid out and have wrapped line lists associated with them.
1988 // TODO: check all paragraphs before the affected range.
1990 int inc
= availableSpace
.y
- child
->GetPosition().y
;
1994 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
1997 if (child
->GetLines().GetCount() == 0)
1999 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
2000 // lays out the object again using the minimum size
2001 child
->LayoutToBestSize(dc
, context
, GetBuffer(),
2002 attr
, child
->GetAttributes(), availableSpace
, rect
, style
&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT
);
2004 //child->Layout(dc, availableChildRect, style);
2007 child
->Move(wxPoint(child
->GetPosition().x
, child
->GetPosition().y
+ inc
));
2009 availableSpace
.y
+= child
->GetCachedSize().y
;
2010 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
2011 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
2012 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
2015 node
= node
->GetNext();
2021 node
= node
->GetNext();
2024 node
= m_children
.GetLast();
2025 if (node
&& node
->GetData()->IsShown())
2027 wxRichTextObject
* child
= node
->GetData();
2028 maxHeight
= child
->GetPosition().y
- (GetPosition().y
+ topMargin
) + child
->GetCachedSize().y
;
2031 maxHeight
= 0; // topMargin + bottomMargin;
2033 // Check the bottom edge of any floating object
2034 if (GetFloatCollector() && GetFloatCollector()->HasFloats())
2036 int bottom
= GetFloatCollector()->GetLastRectBottom();
2037 if (bottom
> maxHeight
)
2041 if (attr
.GetTextBoxAttr().GetSize().GetWidth().IsValid())
2043 wxRect r
= AdjustAvailableSpace(dc
, GetBuffer(), wxRichTextAttr() /* not used */, attr
, parentRect
, parentRect
);
2044 int w
= r
.GetWidth();
2046 // Convert external to content rect
2047 w
= w
- leftMargin
- rightMargin
;
2048 maxWidth
= wxMax(maxWidth
, w
);
2049 maxMaxWidth
= wxMax(maxMaxWidth
, w
);
2052 // TODO: (also in para layout) should set the
2053 // object's size to an absolute one if specified,
2054 // but if not specified, calculate it from content.
2056 // We need to add back the margins etc.
2058 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2059 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxWidth
, maxHeight
));
2060 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2061 SetCachedSize(marginRect
.GetSize());
2064 // The maximum size is the greatest of all maximum widths for all paragraphs.
2066 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2067 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxMaxWidth
, maxHeight
)); // Actually max height is a lie, we can't know it
2068 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2069 SetMaxSize(marginRect
.GetSize());
2072 // The minimum size is the greatest of all minimum widths for all paragraphs.
2074 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2075 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxMinWidth
, maxHeight
)); // Actually max height is a lie, we can't know it
2076 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2077 SetMinSize(marginRect
.GetSize());
2080 if (attr
.GetTextBoxAttr().HasVerticalAlignment() &&
2081 (attr
.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP
))
2084 int leftOverSpace
= availableSpace
.height
- topMargin
- bottomMargin
- maxHeight
;
2085 if (leftOverSpace
> 0)
2087 if (attr
.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_CENTRE
)
2089 yOffset
= (leftOverSpace
/2);
2091 else if (attr
.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_BOTTOM
)
2093 yOffset
= leftOverSpace
;
2097 // Move all the children to vertically align the content
2098 // This doesn't take into account floating objects, unfortunately.
2101 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2104 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2106 child
->Move(wxPoint(child
->GetPosition().x
, child
->GetPosition().y
+ yOffset
));
2108 node
= node
->GetNext();
2113 m_invalidRange
= wxRICHTEXT_NONE
;
2118 /// Get/set the size for the given range.
2119 bool wxRichTextParagraphLayoutBox::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* WXUNUSED(partialExtents
)) const
2123 wxRichTextObjectList::compatibility_iterator startPara
= wxRichTextObjectList::compatibility_iterator();
2124 wxRichTextObjectList::compatibility_iterator endPara
= wxRichTextObjectList::compatibility_iterator();
2126 // First find the first paragraph whose starting position is within the range.
2127 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2130 // child is a paragraph
2131 wxRichTextObject
* child
= node
->GetData();
2132 const wxRichTextRange
& r
= child
->GetRange();
2134 if (r
.GetStart() <= range
.GetStart() && r
.GetEnd() >= range
.GetStart())
2140 node
= node
->GetNext();
2143 // Next find the last paragraph containing part of the range
2144 node
= m_children
.GetFirst();
2147 // child is a paragraph
2148 wxRichTextObject
* child
= node
->GetData();
2149 const wxRichTextRange
& r
= child
->GetRange();
2151 if (r
.GetStart() <= range
.GetEnd() && r
.GetEnd() >= range
.GetEnd())
2157 node
= node
->GetNext();
2160 if (!startPara
|| !endPara
)
2163 // Now we can add up the sizes
2164 for (node
= startPara
; node
; node
= node
->GetNext())
2166 // child is a paragraph
2167 wxRichTextObject
* child
= node
->GetData();
2168 const wxRichTextRange
& childRange
= child
->GetRange();
2169 wxRichTextRange rangeToFind
= range
;
2170 rangeToFind
.LimitTo(childRange
);
2172 if (child
->IsTopLevel())
2173 rangeToFind
= child
->GetOwnRange();
2177 int childDescent
= 0;
2178 child
->GetRangeSize(rangeToFind
, childSize
, childDescent
, dc
, context
, flags
, position
);
2180 descent
= wxMax(childDescent
, descent
);
2182 sz
.x
= wxMax(sz
.x
, childSize
.x
);
2183 sz
.y
+= childSize
.y
;
2185 if (node
== endPara
)
2194 /// Get the paragraph at the given position
2195 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphAtPosition(long pos
, bool caretPosition
) const
2200 // First find the first paragraph whose starting position is within the range.
2201 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2204 // child is a paragraph
2205 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2206 // wxASSERT (child != NULL);
2210 // Return first child in buffer if position is -1
2214 if (child
->GetRange().Contains(pos
))
2218 node
= node
->GetNext();
2223 /// Get the line at the given position
2224 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineAtPosition(long pos
, bool caretPosition
) const
2229 // First find the first paragraph whose starting position is within the range.
2230 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2233 wxRichTextObject
* obj
= (wxRichTextObject
*) node
->GetData();
2234 if (obj
->GetRange().Contains(pos
))
2236 // child is a paragraph
2237 wxRichTextParagraph
* child
= wxDynamicCast(obj
, wxRichTextParagraph
);
2238 // wxASSERT (child != NULL);
2242 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2245 wxRichTextLine
* line
= node2
->GetData();
2247 wxRichTextRange range
= line
->GetAbsoluteRange();
2249 if (range
.Contains(pos
) ||
2251 // If the position is end-of-paragraph, then return the last line of
2252 // of the paragraph.
2253 ((range
.GetEnd() == child
->GetRange().GetEnd()-1) && (pos
== child
->GetRange().GetEnd())))
2256 node2
= node2
->GetNext();
2261 node
= node
->GetNext();
2264 int lineCount
= GetLineCount();
2266 return GetLineForVisibleLineNumber(lineCount
-1);
2271 /// Get the line at the given y pixel position, or the last line.
2272 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineAtYPosition(int y
) const
2274 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2277 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2278 // wxASSERT (child != NULL);
2282 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2285 wxRichTextLine
* line
= node2
->GetData();
2287 wxRect
rect(line
->GetRect());
2289 if (y
<= rect
.GetBottom())
2292 node2
= node2
->GetNext();
2296 node
= node
->GetNext();
2300 int lineCount
= GetLineCount();
2302 return GetLineForVisibleLineNumber(lineCount
-1);
2307 /// Get the number of visible lines
2308 int wxRichTextParagraphLayoutBox::GetLineCount() const
2312 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2315 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2316 // wxASSERT (child != NULL);
2319 count
+= child
->GetLines().GetCount();
2321 node
= node
->GetNext();
2327 /// Get the paragraph for a given line
2328 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphForLine(wxRichTextLine
* line
) const
2330 return GetParagraphAtPosition(line
->GetAbsoluteRange().GetStart());
2333 /// Get the line size at the given position
2334 wxSize
wxRichTextParagraphLayoutBox::GetLineSizeAtPosition(long pos
, bool caretPosition
) const
2336 wxRichTextLine
* line
= GetLineAtPosition(pos
, caretPosition
);
2339 return line
->GetSize();
2342 return wxSize(0, 0);
2346 /// Convenience function to add a paragraph of text
2347 wxRichTextRange
wxRichTextParagraphLayoutBox::AddParagraph(const wxString
& text
, wxRichTextAttr
* paraStyle
)
2349 // Don't use the base style, just the default style, and the base style will
2350 // be combined at display time.
2351 // Divide into paragraph and character styles.
2353 wxRichTextAttr defaultCharStyle
;
2354 wxRichTextAttr defaultParaStyle
;
2356 // If the default style is a named paragraph style, don't apply any character formatting
2357 // to the initial text string.
2358 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2360 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2362 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2365 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2367 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2368 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2370 wxRichTextParagraph
* para
= new wxRichTextParagraph(text
, this, pStyle
, cStyle
);
2376 return para
->GetRange();
2379 /// Adds multiple paragraphs, based on newlines.
2380 wxRichTextRange
wxRichTextParagraphLayoutBox::AddParagraphs(const wxString
& text
, wxRichTextAttr
* paraStyle
)
2382 // Don't use the base style, just the default style, and the base style will
2383 // be combined at display time.
2384 // Divide into paragraph and character styles.
2386 wxRichTextAttr defaultCharStyle
;
2387 wxRichTextAttr defaultParaStyle
;
2389 // If the default style is a named paragraph style, don't apply any character formatting
2390 // to the initial text string.
2391 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2393 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2395 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2398 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2400 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2401 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2403 wxRichTextParagraph
* firstPara
= NULL
;
2404 wxRichTextParagraph
* lastPara
= NULL
;
2406 wxRichTextRange
range(-1, -1);
2409 size_t len
= text
.length();
2411 wxRichTextParagraph
* para
= new wxRichTextParagraph(wxEmptyString
, this, pStyle
, cStyle
);
2420 wxChar ch
= text
[i
];
2421 if (ch
== wxT('\n') || ch
== wxT('\r'))
2425 wxRichTextPlainText
* plainText
= (wxRichTextPlainText
*) para
->GetChildren().GetFirst()->GetData();
2426 plainText
->SetText(line
);
2428 para
= new wxRichTextParagraph(wxEmptyString
, this, pStyle
, cStyle
);
2433 line
= wxEmptyString
;
2444 wxRichTextPlainText
* plainText
= (wxRichTextPlainText
*) para
->GetChildren().GetFirst()->GetData();
2445 plainText
->SetText(line
);
2450 return wxRichTextRange(firstPara
->GetRange().GetStart(), lastPara
->GetRange().GetEnd());
2453 /// Convenience function to add an image
2454 wxRichTextRange
wxRichTextParagraphLayoutBox::AddImage(const wxImage
& image
, wxRichTextAttr
* paraStyle
)
2456 // Don't use the base style, just the default style, and the base style will
2457 // be combined at display time.
2458 // Divide into paragraph and character styles.
2460 wxRichTextAttr defaultCharStyle
;
2461 wxRichTextAttr defaultParaStyle
;
2463 // If the default style is a named paragraph style, don't apply any character formatting
2464 // to the initial text string.
2465 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2467 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2469 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2472 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2474 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2475 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2477 wxRichTextParagraph
* para
= new wxRichTextParagraph(this, pStyle
);
2479 para
->AppendChild(new wxRichTextImage(image
, this, cStyle
));
2483 return para
->GetRange();
2487 /// Insert fragment into this box at the given position. If partialParagraph is true,
2488 /// it is assumed that the last (or only) paragraph is just a piece of data with no paragraph
2491 bool wxRichTextParagraphLayoutBox::InsertFragment(long position
, wxRichTextParagraphLayoutBox
& fragment
)
2493 // First, find the first paragraph whose starting position is within the range.
2494 wxRichTextParagraph
* para
= GetParagraphAtPosition(position
);
2497 wxRichTextAttr originalAttr
= para
->GetAttributes();
2499 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(para
);
2501 // Now split at this position, returning the object to insert the new
2502 // ones in front of.
2503 wxRichTextObject
* nextObject
= para
->SplitAt(position
);
2505 // Special case: partial paragraph, just one paragraph. Might be a small amount of
2506 // text, for example, so let's optimize.
2508 if (fragment
.GetPartialParagraph() && fragment
.GetChildren().GetCount() == 1)
2510 // Add the first para to this para...
2511 wxRichTextObjectList::compatibility_iterator firstParaNode
= fragment
.GetChildren().GetFirst();
2515 // Iterate through the fragment paragraph inserting the content into this paragraph.
2516 wxRichTextParagraph
* firstPara
= wxDynamicCast(firstParaNode
->GetData(), wxRichTextParagraph
);
2517 wxASSERT (firstPara
!= NULL
);
2519 wxRichTextObjectList::compatibility_iterator objectNode
= firstPara
->GetChildren().GetFirst();
2522 wxRichTextObject
* newObj
= objectNode
->GetData()->Clone();
2527 para
->AppendChild(newObj
);
2531 // Insert before nextObject
2532 para
->InsertChild(newObj
, nextObject
);
2535 objectNode
= objectNode
->GetNext();
2542 // Procedure for inserting a fragment consisting of a number of
2545 // 1. Remove and save the content that's after the insertion point, for adding
2546 // back once we've added the fragment.
2547 // 2. Add the content from the first fragment paragraph to the current
2549 // 3. Add remaining fragment paragraphs after the current paragraph.
2550 // 4. Add back the saved content from the first paragraph. If partialParagraph
2551 // is true, add it to the last paragraph added and not a new one.
2553 // 1. Remove and save objects after split point.
2554 wxList savedObjects
;
2556 para
->MoveToList(nextObject
, savedObjects
);
2558 // 2. Add the content from the 1st fragment paragraph.
2559 wxRichTextObjectList::compatibility_iterator firstParaNode
= fragment
.GetChildren().GetFirst();
2563 wxRichTextParagraph
* firstPara
= wxDynamicCast(firstParaNode
->GetData(), wxRichTextParagraph
);
2564 wxASSERT(firstPara
!= NULL
);
2566 if (!(fragment
.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
))
2567 para
->SetAttributes(firstPara
->GetAttributes());
2569 // Save empty paragraph attributes for appending later
2570 // These are character attributes deliberately set for a new paragraph. Without this,
2571 // we couldn't pass default attributes when appending a new paragraph.
2572 wxRichTextAttr emptyParagraphAttributes
;
2574 wxRichTextObjectList::compatibility_iterator objectNode
= firstPara
->GetChildren().GetFirst();
2576 if (objectNode
&& firstPara
->GetChildren().GetCount() == 1 && objectNode
->GetData()->IsEmpty())
2577 emptyParagraphAttributes
= objectNode
->GetData()->GetAttributes();
2581 wxRichTextObject
* newObj
= objectNode
->GetData()->Clone();
2584 para
->AppendChild(newObj
);
2586 objectNode
= objectNode
->GetNext();
2589 // 3. Add remaining fragment paragraphs after the current paragraph.
2590 wxRichTextObjectList::compatibility_iterator nextParagraphNode
= node
->GetNext();
2591 wxRichTextObject
* nextParagraph
= NULL
;
2592 if (nextParagraphNode
)
2593 nextParagraph
= nextParagraphNode
->GetData();
2595 wxRichTextObjectList::compatibility_iterator i
= fragment
.GetChildren().GetFirst()->GetNext();
2596 wxRichTextParagraph
* finalPara
= para
;
2598 bool needExtraPara
= (!i
|| !fragment
.GetPartialParagraph());
2600 // If there was only one paragraph, we need to insert a new one.
2603 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2604 wxASSERT( para
!= NULL
);
2606 finalPara
= (wxRichTextParagraph
*) para
->Clone();
2609 InsertChild(finalPara
, nextParagraph
);
2611 AppendChild(finalPara
);
2616 // If there was only one paragraph, or we have full paragraphs in our fragment,
2617 // we need to insert a new one.
2620 finalPara
= new wxRichTextParagraph
;
2623 InsertChild(finalPara
, nextParagraph
);
2625 AppendChild(finalPara
);
2628 // 4. Add back the remaining content.
2632 finalPara
->MoveFromList(savedObjects
);
2634 // Ensure there's at least one object
2635 if (finalPara
->GetChildCount() == 0)
2637 wxRichTextPlainText
* text
= new wxRichTextPlainText(wxEmptyString
);
2638 text
->SetAttributes(emptyParagraphAttributes
);
2640 finalPara
->AppendChild(text
);
2644 if ((fragment
.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
) && firstPara
)
2645 finalPara
->SetAttributes(firstPara
->GetAttributes());
2646 else if (finalPara
&& finalPara
!= para
)
2647 finalPara
->SetAttributes(originalAttr
);
2655 wxRichTextObjectList::compatibility_iterator i
= fragment
.GetChildren().GetFirst();
2658 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2659 wxASSERT( para
!= NULL
);
2661 AppendChild(para
->Clone());
2670 /// Make a copy of the fragment corresponding to the given range, putting it in 'fragment'.
2671 /// If there was an incomplete paragraph at the end, partialParagraph is set to true.
2672 bool wxRichTextParagraphLayoutBox::CopyFragment(const wxRichTextRange
& range
, wxRichTextParagraphLayoutBox
& fragment
)
2674 wxRichTextObjectList::compatibility_iterator i
= GetChildren().GetFirst();
2677 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2678 wxASSERT( para
!= NULL
);
2680 if (!para
->GetRange().IsOutside(range
))
2682 fragment
.AppendChild(para
->Clone());
2687 // Now top and tail the first and last paragraphs in our new fragment (which might be the same).
2688 if (!fragment
.IsEmpty())
2690 wxRichTextParagraph
* firstPara
= wxDynamicCast(fragment
.GetChildren().GetFirst()->GetData(), wxRichTextParagraph
);
2691 wxASSERT( firstPara
!= NULL
);
2693 wxRichTextParagraph
* lastPara
= wxDynamicCast(fragment
.GetChildren().GetLast()->GetData(), wxRichTextParagraph
);
2694 wxASSERT( lastPara
!= NULL
);
2696 if (!firstPara
|| !lastPara
)
2699 bool isFragment
= (range
.GetEnd() < lastPara
->GetRange().GetEnd());
2701 long firstPos
= firstPara
->GetRange().GetStart();
2703 // Adjust for renumbering from zero
2704 wxRichTextRange
topTailRange(range
.GetStart() - firstPos
, range
.GetEnd() - firstPos
);
2707 fragment
.CalculateRange(0, end
);
2709 // Chop off the start of the paragraph
2710 if (topTailRange
.GetStart() > 0)
2712 wxRichTextRange
r(0, topTailRange
.GetStart()-1);
2713 firstPara
->DeleteRange(r
);
2715 // Make sure the numbering is correct
2716 fragment
.CalculateRange(0, end
);
2718 // Now, we've deleted some positions, so adjust the range
2720 topTailRange
.SetStart(range
.GetLength());
2721 topTailRange
.SetEnd(fragment
.GetOwnRange().GetEnd());
2725 topTailRange
.SetStart(range
.GetLength());
2726 topTailRange
.SetEnd(fragment
.GetOwnRange().GetEnd());
2729 if (topTailRange
.GetStart() < lastPara
->GetRange().GetEnd())
2731 lastPara
->DeleteRange(topTailRange
);
2733 // Make sure the numbering is correct
2735 fragment
.CalculateRange(0, end
);
2737 // We only have part of a paragraph at the end
2738 fragment
.SetPartialParagraph(true);
2742 // We have a partial paragraph (don't save last new paragraph marker)
2743 // or complete paragraph
2744 fragment
.SetPartialParagraph(isFragment
);
2751 /// Given a position, get the number of the visible line (potentially many to a paragraph),
2752 /// starting from zero at the start of the buffer.
2753 long wxRichTextParagraphLayoutBox::GetVisibleLineNumber(long pos
, bool caretPosition
, bool startOfLine
) const
2760 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2763 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2764 // wxASSERT( child != NULL );
2768 if (child
->GetRange().Contains(pos
))
2770 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2773 wxRichTextLine
* line
= node2
->GetData();
2774 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
2776 if (lineRange
.Contains(pos
) || pos
== lineRange
.GetStart())
2778 // If the caret is displayed at the end of the previous wrapped line,
2779 // we want to return the line it's _displayed_ at (not the actual line
2780 // containing the position).
2781 if (lineRange
.GetStart() == pos
&& !startOfLine
&& child
->GetRange().GetStart() != pos
)
2782 return lineCount
- 1;
2789 node2
= node2
->GetNext();
2791 // If we didn't find it in the lines, it must be
2792 // the last position of the paragraph. So return the last line.
2796 lineCount
+= child
->GetLines().GetCount();
2799 node
= node
->GetNext();
2806 /// Given a line number, get the corresponding wxRichTextLine object.
2807 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineForVisibleLineNumber(long lineNumber
) const
2811 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2814 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2815 // wxASSERT(child != NULL);
2819 if (lineNumber
< (int) (child
->GetLines().GetCount() + lineCount
))
2821 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2824 wxRichTextLine
* line
= node2
->GetData();
2826 if (lineCount
== lineNumber
)
2831 node2
= node2
->GetNext();
2835 lineCount
+= child
->GetLines().GetCount();
2838 node
= node
->GetNext();
2845 /// Delete range from layout.
2846 bool wxRichTextParagraphLayoutBox::DeleteRange(const wxRichTextRange
& range
)
2848 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2850 wxRichTextParagraph
* firstPara
= NULL
;
2853 wxRichTextParagraph
* obj
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2854 // wxASSERT (obj != NULL);
2856 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
2860 // Delete the range in each paragraph
2862 if (!obj
->GetRange().IsOutside(range
))
2864 // Deletes the content of this object within the given range
2865 obj
->DeleteRange(range
);
2867 wxRichTextRange thisRange
= obj
->GetRange();
2868 wxRichTextAttr thisAttr
= obj
->GetAttributes();
2870 // If the whole paragraph is within the range to delete,
2871 // delete the whole thing.
2872 if (range
.GetStart() <= thisRange
.GetStart() && range
.GetEnd() >= thisRange
.GetEnd())
2874 // Delete the whole object
2875 RemoveChild(obj
, true);
2878 else if (!firstPara
)
2881 // If the range includes the paragraph end, we need to join this
2882 // and the next paragraph.
2883 if (range
.GetEnd() <= thisRange
.GetEnd())
2885 // We need to move the objects from the next paragraph
2886 // to this paragraph
2888 wxRichTextParagraph
* nextParagraph
= NULL
;
2889 if ((range
.GetEnd() < thisRange
.GetEnd()) && obj
)
2890 nextParagraph
= obj
;
2893 // We're ending at the end of the paragraph, so merge the _next_ paragraph.
2895 nextParagraph
= wxDynamicCast(next
->GetData(), wxRichTextParagraph
);
2898 bool applyFinalParagraphStyle
= firstPara
&& nextParagraph
&& nextParagraph
!= firstPara
;
2900 wxRichTextAttr nextParaAttr
;
2901 if (applyFinalParagraphStyle
)
2903 // Special case when deleting the end of a paragraph - use _this_ paragraph's style,
2904 // not the next one.
2905 if (range
.GetStart() == range
.GetEnd() && range
.GetStart() == thisRange
.GetEnd())
2906 nextParaAttr
= thisAttr
;
2908 nextParaAttr
= nextParagraph
->GetAttributes();
2911 if (firstPara
&& nextParagraph
&& firstPara
!= nextParagraph
)
2913 // Move the objects to the previous para
2914 wxRichTextObjectList::compatibility_iterator node1
= nextParagraph
->GetChildren().GetFirst();
2918 wxRichTextObject
* obj1
= node1
->GetData();
2920 firstPara
->AppendChild(obj1
);
2922 wxRichTextObjectList::compatibility_iterator next1
= node1
->GetNext();
2923 nextParagraph
->GetChildren().Erase(node1
);
2928 // Delete the paragraph
2929 RemoveChild(nextParagraph
, true);
2932 // Avoid empty paragraphs
2933 if (firstPara
&& firstPara
->GetChildren().GetCount() == 0)
2935 wxRichTextPlainText
* text
= new wxRichTextPlainText(wxEmptyString
);
2936 firstPara
->AppendChild(text
);
2939 if (applyFinalParagraphStyle
)
2940 firstPara
->SetAttributes(nextParaAttr
);
2953 /// Get any text in this object for the given range
2954 wxString
wxRichTextParagraphLayoutBox::GetTextForRange(const wxRichTextRange
& range
) const
2958 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2961 wxRichTextObject
* child
= node
->GetData();
2962 if (!child
->GetRange().IsOutside(range
))
2964 wxRichTextRange childRange
= range
;
2965 childRange
.LimitTo(child
->GetRange());
2967 wxString childText
= child
->GetTextForRange(childRange
);
2971 if ((childRange
.GetEnd() == child
->GetRange().GetEnd()) && node
->GetNext())
2976 node
= node
->GetNext();
2982 /// Get all the text
2983 wxString
wxRichTextParagraphLayoutBox::GetText() const
2985 return GetTextForRange(GetOwnRange());
2988 /// Get the paragraph by number
2989 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphAtLine(long paragraphNumber
) const
2991 if ((size_t) paragraphNumber
>= GetChildCount())
2994 return (wxRichTextParagraph
*) GetChild((size_t) paragraphNumber
);
2997 /// Get the length of the paragraph
2998 int wxRichTextParagraphLayoutBox::GetParagraphLength(long paragraphNumber
) const
3000 wxRichTextParagraph
* para
= GetParagraphAtLine(paragraphNumber
);
3002 return para
->GetRange().GetLength() - 1; // don't include newline
3007 /// Get the text of the paragraph
3008 wxString
wxRichTextParagraphLayoutBox::GetParagraphText(long paragraphNumber
) const
3010 wxRichTextParagraph
* para
= GetParagraphAtLine(paragraphNumber
);
3012 return para
->GetTextForRange(para
->GetRange());
3014 return wxEmptyString
;
3017 /// Convert zero-based line column and paragraph number to a position.
3018 long wxRichTextParagraphLayoutBox::XYToPosition(long x
, long y
) const
3020 wxRichTextParagraph
* para
= GetParagraphAtLine(y
);
3023 return para
->GetRange().GetStart() + x
;
3029 /// Convert zero-based position to line column and paragraph number
3030 bool wxRichTextParagraphLayoutBox::PositionToXY(long pos
, long* x
, long* y
) const
3032 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
);
3036 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3039 wxRichTextObject
* child
= node
->GetData();
3043 node
= node
->GetNext();
3047 *x
= pos
- para
->GetRange().GetStart();
3055 /// Get the leaf object in a paragraph at this position.
3056 /// Given a line number, get the corresponding wxRichTextLine object.
3057 wxRichTextObject
* wxRichTextParagraphLayoutBox::GetLeafObjectAtPosition(long position
) const
3059 wxRichTextParagraph
* para
= GetParagraphAtPosition(position
);
3062 wxRichTextObjectList::compatibility_iterator node
= para
->GetChildren().GetFirst();
3066 wxRichTextObject
* child
= node
->GetData();
3067 if (child
->GetRange().Contains(position
))
3070 node
= node
->GetNext();
3072 if (position
== para
->GetRange().GetEnd() && para
->GetChildCount() > 0)
3073 return para
->GetChildren().GetLast()->GetData();
3078 /// Set character or paragraph text attributes: apply character styles only to immediate text nodes
3079 bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange
& range
, const wxRichTextAttr
& style
, int flags
)
3081 bool characterStyle
= false;
3082 bool paragraphStyle
= false;
3084 if (style
.IsCharacterStyle())
3085 characterStyle
= true;
3086 if (style
.IsParagraphStyle())
3087 paragraphStyle
= true;
3089 wxRichTextBuffer
* buffer
= GetBuffer();
3091 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
3092 bool applyMinimal
= ((flags
& wxRICHTEXT_SETSTYLE_OPTIMIZE
) != 0);
3093 bool parasOnly
= ((flags
& wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY
) != 0);
3094 bool charactersOnly
= ((flags
& wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY
) != 0);
3095 bool resetExistingStyle
= ((flags
& wxRICHTEXT_SETSTYLE_RESET
) != 0);
3096 bool removeStyle
= ((flags
& wxRICHTEXT_SETSTYLE_REMOVE
) != 0);
3098 // Apply paragraph style first, if any
3099 wxRichTextAttr
wholeStyle(style
);
3101 if (!removeStyle
&& wholeStyle
.HasParagraphStyleName() && buffer
->GetStyleSheet())
3103 wxRichTextParagraphStyleDefinition
* def
= buffer
->GetStyleSheet()->FindParagraphStyle(wholeStyle
.GetParagraphStyleName());
3105 wxRichTextApplyStyle(wholeStyle
, def
->GetStyleMergedWithBase(buffer
->GetStyleSheet()));
3108 // Limit the attributes to be set to the content to only character attributes.
3109 wxRichTextAttr
characterAttributes(wholeStyle
);
3110 characterAttributes
.SetFlags(characterAttributes
.GetFlags() & (wxTEXT_ATTR_CHARACTER
));
3112 if (!removeStyle
&& characterAttributes
.HasCharacterStyleName() && buffer
->GetStyleSheet())
3114 wxRichTextCharacterStyleDefinition
* def
= buffer
->GetStyleSheet()->FindCharacterStyle(characterAttributes
.GetCharacterStyleName());
3116 wxRichTextApplyStyle(characterAttributes
, def
->GetStyleMergedWithBase(buffer
->GetStyleSheet()));
3119 // If we are associated with a control, make undoable; otherwise, apply immediately
3122 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3124 wxRichTextAction
* action
= NULL
;
3126 if (haveControl
&& withUndo
)
3128 action
= new wxRichTextAction(NULL
, _("Change Style"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
3129 action
->SetRange(range
);
3130 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3133 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3136 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3137 // wxASSERT (para != NULL);
3139 if (para
&& para
->GetChildCount() > 0)
3141 // Stop searching if we're beyond the range of interest
3142 if (para
->GetRange().GetStart() > range
.GetEnd())
3145 if (!para
->GetRange().IsOutside(range
))
3147 // We'll be using a copy of the paragraph to make style changes,
3148 // not updating the buffer directly.
3149 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3151 if (haveControl
&& withUndo
)
3153 newPara
= new wxRichTextParagraph(*para
);
3154 action
->GetNewParagraphs().AppendChild(newPara
);
3156 // Also store the old ones for Undo
3157 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3162 // If we're specifying paragraphs only, then we really mean character formatting
3163 // to be included in the paragraph style
3164 if ((paragraphStyle
|| parasOnly
) && !charactersOnly
)
3168 // Removes the given style from the paragraph
3169 wxRichTextRemoveStyle(newPara
->GetAttributes(), style
);
3171 else if (resetExistingStyle
)
3172 newPara
->GetAttributes() = wholeStyle
;
3177 // Only apply attributes that will make a difference to the combined
3178 // style as seen on the display
3179 wxRichTextAttr
combinedAttr(para
->GetCombinedAttributes(true));
3180 wxRichTextApplyStyle(newPara
->GetAttributes(), wholeStyle
, & combinedAttr
);
3183 wxRichTextApplyStyle(newPara
->GetAttributes(), wholeStyle
);
3187 // When applying paragraph styles dynamically, don't change the text objects' attributes
3188 // since they will computed as needed. Only apply the character styling if it's _only_
3189 // character styling. This policy is subject to change and might be put under user control.
3191 // Hm. we might well be applying a mix of paragraph and character styles, in which
3192 // case we _do_ want to apply character styles regardless of what para styles are set.
3193 // But if we're applying a paragraph style, which has some character attributes, but
3194 // we only want the paragraphs to hold this character style, then we _don't_ want to
3195 // apply the character style. So we need to be able to choose.
3197 if (!parasOnly
&& (characterStyle
|charactersOnly
) && range
.GetStart() != newPara
->GetRange().GetEnd())
3199 wxRichTextRange
childRange(range
);
3200 childRange
.LimitTo(newPara
->GetRange());
3202 // Find the starting position and if necessary split it so
3203 // we can start applying a different style.
3204 // TODO: check that the style actually changes or is different
3205 // from style outside of range
3206 wxRichTextObject
* firstObject
wxDUMMY_INITIALIZE(NULL
);
3207 wxRichTextObject
* lastObject
wxDUMMY_INITIALIZE(NULL
);
3209 if (childRange
.GetStart() == newPara
->GetRange().GetStart())
3210 firstObject
= newPara
->GetChildren().GetFirst()->GetData();
3212 firstObject
= newPara
->SplitAt(range
.GetStart());
3214 // Increment by 1 because we're apply the style one _after_ the split point
3215 long splitPoint
= childRange
.GetEnd();
3216 if (splitPoint
!= newPara
->GetRange().GetEnd())
3220 if (splitPoint
== newPara
->GetRange().GetEnd())
3221 lastObject
= newPara
->GetChildren().GetLast()->GetData();
3223 // lastObject is set as a side-effect of splitting. It's
3224 // returned as the object before the new object.
3225 (void) newPara
->SplitAt(splitPoint
, & lastObject
);
3227 wxASSERT(firstObject
!= NULL
);
3228 wxASSERT(lastObject
!= NULL
);
3230 if (!firstObject
|| !lastObject
)
3233 wxRichTextObjectList::compatibility_iterator firstNode
= newPara
->GetChildren().Find(firstObject
);
3234 wxRichTextObjectList::compatibility_iterator lastNode
= newPara
->GetChildren().Find(lastObject
);
3236 wxASSERT(firstNode
);
3239 wxRichTextObjectList::compatibility_iterator node2
= firstNode
;
3243 wxRichTextObject
* child
= node2
->GetData();
3247 // Removes the given style from the paragraph
3248 wxRichTextRemoveStyle(child
->GetAttributes(), style
);
3250 else if (resetExistingStyle
)
3251 child
->GetAttributes() = characterAttributes
;
3256 // Only apply attributes that will make a difference to the combined
3257 // style as seen on the display
3258 wxRichTextAttr
combinedAttr(newPara
->GetCombinedAttributes(child
->GetAttributes(), true));
3259 wxRichTextApplyStyle(child
->GetAttributes(), characterAttributes
, & combinedAttr
);
3262 wxRichTextApplyStyle(child
->GetAttributes(), characterAttributes
);
3265 if (node2
== lastNode
)
3268 node2
= node2
->GetNext();
3274 node
= node
->GetNext();
3277 // Do action, or delay it until end of batch.
3278 if (haveControl
&& withUndo
)
3279 buffer
->SubmitAction(action
);
3284 // Just change the attributes for this single object.
3285 void wxRichTextParagraphLayoutBox::SetStyle(wxRichTextObject
* obj
, const wxRichTextAttr
& textAttr
, int flags
)
3287 wxRichTextBuffer
* buffer
= GetBuffer();
3288 bool withUndo
= flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
;
3289 bool resetExistingStyle
= ((flags
& wxRICHTEXT_SETSTYLE_RESET
) != 0);
3290 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3292 wxRichTextAction
*action
= NULL
;
3293 wxRichTextAttr newAttr
= obj
->GetAttributes();
3294 if (resetExistingStyle
)
3297 newAttr
.Apply(textAttr
);
3299 if (haveControl
&& withUndo
)
3301 action
= new wxRichTextAction(NULL
, _("Change Object Style"), wxRICHTEXT_CHANGE_ATTRIBUTES
, buffer
, obj
->GetContainer(), buffer
->GetRichTextCtrl());
3302 action
->SetRange(obj
->GetRange().FromInternal());
3303 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3304 action
->MakeObject(obj
);
3306 action
->GetAttributes() = newAttr
;
3309 obj
->GetAttributes() = newAttr
;
3311 if (haveControl
&& withUndo
)
3312 buffer
->SubmitAction(action
);
3315 /// Get the text attributes for this position.
3316 bool wxRichTextParagraphLayoutBox::GetStyle(long position
, wxRichTextAttr
& style
)
3318 return DoGetStyle(position
, style
, true);
3321 bool wxRichTextParagraphLayoutBox::GetUncombinedStyle(long position
, wxRichTextAttr
& style
)
3323 return DoGetStyle(position
, style
, false);
3326 /// Implementation helper for GetStyle. If combineStyles is true, combine base, paragraph and
3327 /// context attributes.
3328 bool wxRichTextParagraphLayoutBox::DoGetStyle(long position
, wxRichTextAttr
& style
, bool combineStyles
)
3330 wxRichTextObject
* obj
wxDUMMY_INITIALIZE(NULL
);
3332 if (style
.IsParagraphStyle())
3334 obj
= GetParagraphAtPosition(position
);
3339 // Start with the base style
3340 style
= GetAttributes();
3342 // Apply the paragraph style
3343 wxRichTextApplyStyle(style
, obj
->GetAttributes());
3346 style
= obj
->GetAttributes();
3353 obj
= GetLeafObjectAtPosition(position
);
3358 wxRichTextParagraph
* para
= wxDynamicCast(obj
->GetParent(), wxRichTextParagraph
);
3359 style
= para
? para
->GetCombinedAttributes(obj
->GetAttributes()) : obj
->GetAttributes();
3362 style
= obj
->GetAttributes();
3370 static bool wxHasStyle(long flags
, long style
)
3372 return (flags
& style
) != 0;
3375 /// Combines 'style' with 'currentStyle' for the purpose of summarising the attributes of a range of
3377 bool wxRichTextParagraphLayoutBox::CollectStyle(wxRichTextAttr
& currentStyle
, const wxRichTextAttr
& style
, wxRichTextAttr
& clashingAttr
, wxRichTextAttr
& absentAttr
)
3379 currentStyle
.CollectCommonAttributes(style
, clashingAttr
, absentAttr
);
3384 /// Get the combined style for a range - if any attribute is different within the range,
3385 /// that attribute is not present within the flags.
3386 /// *** Note that this is not recursive, and so assumes that content inside a paragraph is not itself
3388 bool wxRichTextParagraphLayoutBox::GetStyleForRange(const wxRichTextRange
& range
, wxRichTextAttr
& style
)
3390 style
= wxRichTextAttr();
3392 wxRichTextAttr clashingAttr
;
3393 wxRichTextAttr absentAttrPara
, absentAttrChar
;
3395 wxRichTextObjectList::compatibility_iterator node
= GetChildren().GetFirst();
3398 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3399 if (para
&& !(para
->GetRange().GetStart() > range
.GetEnd() || para
->GetRange().GetEnd() < range
.GetStart()))
3401 if (para
->GetChildren().GetCount() == 0)
3403 wxRichTextAttr paraStyle
= para
->GetCombinedAttributes(true /* use box attributes */);
3405 CollectStyle(style
, paraStyle
, clashingAttr
, absentAttrPara
);
3409 wxRichTextRange
paraRange(para
->GetRange());
3410 paraRange
.LimitTo(range
);
3412 // First collect paragraph attributes only
3413 wxRichTextAttr paraStyle
= para
->GetCombinedAttributes();
3414 paraStyle
.SetFlags(paraStyle
.GetFlags() & wxTEXT_ATTR_PARAGRAPH
);
3415 CollectStyle(style
, paraStyle
, clashingAttr
, absentAttrPara
);
3417 wxRichTextObjectList::compatibility_iterator childNode
= para
->GetChildren().GetFirst();
3421 wxRichTextObject
* child
= childNode
->GetData();
3422 if (!(child
->GetRange().GetStart() > range
.GetEnd() || child
->GetRange().GetEnd() < range
.GetStart()))
3424 wxRichTextAttr childStyle
= para
->GetCombinedAttributes(child
->GetAttributes(), true /* include box attributes */);
3426 // Now collect character attributes only
3427 childStyle
.SetFlags(childStyle
.GetFlags() & wxTEXT_ATTR_CHARACTER
);
3429 CollectStyle(style
, childStyle
, clashingAttr
, absentAttrChar
);
3432 childNode
= childNode
->GetNext();
3436 node
= node
->GetNext();
3441 /// Set default style
3442 bool wxRichTextParagraphLayoutBox::SetDefaultStyle(const wxRichTextAttr
& style
)
3444 m_defaultAttributes
= style
;
3448 /// Test if this whole range has character attributes of the specified kind. If any
3449 /// of the attributes are different within the range, the test fails. You
3450 /// can use this to implement, for example, bold button updating. style must have
3451 /// flags indicating which attributes are of interest.
3452 bool wxRichTextParagraphLayoutBox::HasCharacterAttributes(const wxRichTextRange
& range
, const wxRichTextAttr
& style
) const
3455 int matchingCount
= 0;
3457 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3460 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3461 // wxASSERT (para != NULL);
3465 // Stop searching if we're beyond the range of interest
3466 if (para
->GetRange().GetStart() > range
.GetEnd())
3467 return foundCount
== matchingCount
&& foundCount
!= 0;
3469 if (!para
->GetRange().IsOutside(range
))
3471 wxRichTextObjectList::compatibility_iterator node2
= para
->GetChildren().GetFirst();
3475 wxRichTextObject
* child
= node2
->GetData();
3476 // Allow for empty string if no buffer
3477 wxRichTextRange childRange
= child
->GetRange();
3478 if (childRange
.GetLength() == 0 && GetRange().GetLength() == 1)
3479 childRange
.SetEnd(childRange
.GetEnd()+1);
3481 if (!childRange
.IsOutside(range
) && child
->IsKindOf(CLASSINFO(wxRichTextPlainText
)))
3484 wxRichTextAttr textAttr
= para
->GetCombinedAttributes(child
->GetAttributes());
3486 if (wxTextAttrEqPartial(textAttr
, style
))
3490 node2
= node2
->GetNext();
3495 node
= node
->GetNext();
3498 return foundCount
== matchingCount
&& foundCount
!= 0;
3501 /// Test if this whole range has paragraph attributes of the specified kind. If any
3502 /// of the attributes are different within the range, the test fails. You
3503 /// can use this to implement, for example, centering button updating. style must have
3504 /// flags indicating which attributes are of interest.
3505 bool wxRichTextParagraphLayoutBox::HasParagraphAttributes(const wxRichTextRange
& range
, const wxRichTextAttr
& style
) const
3508 int matchingCount
= 0;
3510 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3513 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3514 // wxASSERT (para != NULL);
3518 // Stop searching if we're beyond the range of interest
3519 if (para
->GetRange().GetStart() > range
.GetEnd())
3520 return foundCount
== matchingCount
&& foundCount
!= 0;
3522 if (!para
->GetRange().IsOutside(range
))
3524 wxRichTextAttr textAttr
= GetAttributes();
3525 // Apply the paragraph style
3526 wxRichTextApplyStyle(textAttr
, para
->GetAttributes());
3529 if (wxTextAttrEqPartial(textAttr
, style
))
3534 node
= node
->GetNext();
3536 return foundCount
== matchingCount
&& foundCount
!= 0;
3539 void wxRichTextParagraphLayoutBox::PrepareContent(wxRichTextParagraphLayoutBox
& container
)
3541 wxRichTextBuffer
* buffer
= GetBuffer();
3542 if (buffer
&& buffer
->GetRichTextCtrl())
3543 buffer
->GetRichTextCtrl()->PrepareContent(container
);
3547 /// Set character or paragraph properties
3548 bool wxRichTextParagraphLayoutBox::SetProperties(const wxRichTextRange
& range
, const wxRichTextProperties
& properties
, int flags
)
3550 wxRichTextBuffer
* buffer
= GetBuffer();
3552 bool withUndo
= ((flags
& wxRICHTEXT_SETPROPERTIES_WITH_UNDO
) != 0);
3553 bool parasOnly
= ((flags
& wxRICHTEXT_SETPROPERTIES_PARAGRAPHS_ONLY
) != 0);
3554 bool charactersOnly
= ((flags
& wxRICHTEXT_SETPROPERTIES_CHARACTERS_ONLY
) != 0);
3555 bool resetExistingProperties
= ((flags
& wxRICHTEXT_SETPROPERTIES_RESET
) != 0);
3556 bool removeProperties
= ((flags
& wxRICHTEXT_SETPROPERTIES_REMOVE
) != 0);
3558 // If we are associated with a control, make undoable; otherwise, apply immediately
3561 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3563 wxRichTextAction
* action
= NULL
;
3565 if (haveControl
&& withUndo
)
3567 action
= new wxRichTextAction(NULL
, _("Change Properties"), wxRICHTEXT_CHANGE_PROPERTIES
, buffer
, this, buffer
->GetRichTextCtrl());
3568 action
->SetRange(range
);
3569 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3572 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3575 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3576 // wxASSERT (para != NULL);
3578 if (para
&& para
->GetChildCount() > 0)
3580 // Stop searching if we're beyond the range of interest
3581 if (para
->GetRange().GetStart() > range
.GetEnd())
3584 if (!para
->GetRange().IsOutside(range
))
3586 // We'll be using a copy of the paragraph to make style changes,
3587 // not updating the buffer directly.
3588 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3590 if (haveControl
&& withUndo
)
3592 newPara
= new wxRichTextParagraph(*para
);
3593 action
->GetNewParagraphs().AppendChild(newPara
);
3595 // Also store the old ones for Undo
3596 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3603 if (removeProperties
)
3605 // Removes the given style from the paragraph
3607 newPara
->GetProperties().RemoveProperties(properties
);
3609 else if (resetExistingProperties
)
3610 newPara
->GetProperties() = properties
;
3612 newPara
->GetProperties().MergeProperties(properties
);
3615 // When applying paragraph styles dynamically, don't change the text objects' attributes
3616 // since they will computed as needed. Only apply the character styling if it's _only_
3617 // character styling. This policy is subject to change and might be put under user control.
3619 // Hm. we might well be applying a mix of paragraph and character styles, in which
3620 // case we _do_ want to apply character styles regardless of what para styles are set.
3621 // But if we're applying a paragraph style, which has some character attributes, but
3622 // we only want the paragraphs to hold this character style, then we _don't_ want to
3623 // apply the character style. So we need to be able to choose.
3625 if (!parasOnly
&& charactersOnly
&& range
.GetStart() != newPara
->GetRange().GetEnd())
3627 wxRichTextRange
childRange(range
);
3628 childRange
.LimitTo(newPara
->GetRange());
3630 // Find the starting position and if necessary split it so
3631 // we can start applying different properties.
3632 // TODO: check that the properties actually change or are different
3633 // from properties outside of range
3634 wxRichTextObject
* firstObject
wxDUMMY_INITIALIZE(NULL
);
3635 wxRichTextObject
* lastObject
wxDUMMY_INITIALIZE(NULL
);
3637 if (childRange
.GetStart() == newPara
->GetRange().GetStart())
3638 firstObject
= newPara
->GetChildren().GetFirst()->GetData();
3640 firstObject
= newPara
->SplitAt(range
.GetStart());
3642 // Increment by 1 because we're apply the style one _after_ the split point
3643 long splitPoint
= childRange
.GetEnd();
3644 if (splitPoint
!= newPara
->GetRange().GetEnd())
3648 if (splitPoint
== newPara
->GetRange().GetEnd())
3649 lastObject
= newPara
->GetChildren().GetLast()->GetData();
3651 // lastObject is set as a side-effect of splitting. It's
3652 // returned as the object before the new object.
3653 (void) newPara
->SplitAt(splitPoint
, & lastObject
);
3655 wxASSERT(firstObject
!= NULL
);
3656 wxASSERT(lastObject
!= NULL
);
3658 if (!firstObject
|| !lastObject
)
3661 wxRichTextObjectList::compatibility_iterator firstNode
= newPara
->GetChildren().Find(firstObject
);
3662 wxRichTextObjectList::compatibility_iterator lastNode
= newPara
->GetChildren().Find(lastObject
);
3664 wxASSERT(firstNode
);
3667 wxRichTextObjectList::compatibility_iterator node2
= firstNode
;
3671 wxRichTextObject
* child
= node2
->GetData();
3673 if (removeProperties
)
3675 // Removes the given properties from the paragraph
3676 child
->GetProperties().RemoveProperties(properties
);
3678 else if (resetExistingProperties
)
3679 child
->GetProperties() = properties
;
3682 child
->GetProperties().MergeProperties(properties
);
3685 if (node2
== lastNode
)
3688 node2
= node2
->GetNext();
3694 node
= node
->GetNext();
3697 // Do action, or delay it until end of batch.
3698 if (haveControl
&& withUndo
)
3699 buffer
->SubmitAction(action
);
3704 void wxRichTextParagraphLayoutBox::Reset()
3708 wxRichTextBuffer
* buffer
= GetBuffer();
3709 if (buffer
&& buffer
->GetRichTextCtrl())
3711 wxRichTextEvent
event(wxEVT_COMMAND_RICHTEXT_BUFFER_RESET
, buffer
->GetRichTextCtrl()->GetId());
3712 event
.SetEventObject(buffer
->GetRichTextCtrl());
3713 event
.SetContainer(this);
3715 buffer
->SendEvent(event
, true);
3718 AddParagraph(wxEmptyString
);
3720 PrepareContent(*this);
3722 InvalidateHierarchy(wxRICHTEXT_ALL
);
3725 /// Invalidate the buffer. With no argument, invalidates whole buffer.
3726 void wxRichTextParagraphLayoutBox::Invalidate(const wxRichTextRange
& invalidRange
)
3728 wxRichTextCompositeObject::Invalidate(invalidRange
);
3730 DoInvalidate(invalidRange
);
3733 // Do the (in)validation for this object only
3734 void wxRichTextParagraphLayoutBox::DoInvalidate(const wxRichTextRange
& invalidRange
)
3736 if (invalidRange
== wxRICHTEXT_ALL
)
3738 m_invalidRange
= wxRICHTEXT_ALL
;
3740 // Already invalidating everything
3741 else if (m_invalidRange
== wxRICHTEXT_ALL
)
3746 if ((invalidRange
.GetStart() < m_invalidRange
.GetStart()) || m_invalidRange
.GetStart() == -1)
3747 m_invalidRange
.SetStart(invalidRange
.GetStart());
3748 if (invalidRange
.GetEnd() > m_invalidRange
.GetEnd())
3749 m_invalidRange
.SetEnd(invalidRange
.GetEnd());
3753 // Do the (in)validation both up and down the hierarchy
3754 void wxRichTextParagraphLayoutBox::InvalidateHierarchy(const wxRichTextRange
& invalidRange
)
3756 Invalidate(invalidRange
);
3758 if (invalidRange
!= wxRICHTEXT_NONE
)
3760 // Now go up the hierarchy
3761 wxRichTextObject
* thisObj
= this;
3762 wxRichTextObject
* p
= GetParent();
3765 wxRichTextParagraphLayoutBox
* l
= wxDynamicCast(p
, wxRichTextParagraphLayoutBox
);
3767 l
->DoInvalidate(thisObj
->GetRange());
3775 /// Get invalid range, rounding to entire paragraphs if argument is true.
3776 wxRichTextRange
wxRichTextParagraphLayoutBox::GetInvalidRange(bool wholeParagraphs
) const
3778 if (m_invalidRange
== wxRICHTEXT_ALL
|| m_invalidRange
== wxRICHTEXT_NONE
)
3779 return m_invalidRange
;
3781 wxRichTextRange range
= m_invalidRange
;
3783 if (wholeParagraphs
)
3785 wxRichTextParagraph
* para1
= GetParagraphAtPosition(range
.GetStart());
3787 range
.SetStart(para1
->GetRange().GetStart());
3788 // floating layout make all child should be relayout
3789 range
.SetEnd(GetOwnRange().GetEnd());
3794 /// Apply the style sheet to the buffer, for example if the styles have changed.
3795 bool wxRichTextParagraphLayoutBox::ApplyStyleSheet(wxRichTextStyleSheet
* styleSheet
)
3797 wxASSERT(styleSheet
!= NULL
);
3803 wxRichTextAttr
attr(GetBasicStyle());
3804 if (GetBasicStyle().HasParagraphStyleName())
3806 wxRichTextParagraphStyleDefinition
* paraDef
= styleSheet
->FindParagraphStyle(GetBasicStyle().GetParagraphStyleName());
3809 attr
.Apply(paraDef
->GetStyleMergedWithBase(styleSheet
));
3810 SetBasicStyle(attr
);
3815 if (GetBasicStyle().HasCharacterStyleName())
3817 wxRichTextCharacterStyleDefinition
* charDef
= styleSheet
->FindCharacterStyle(GetBasicStyle().GetCharacterStyleName());
3820 attr
.Apply(charDef
->GetStyleMergedWithBase(styleSheet
));
3821 SetBasicStyle(attr
);
3826 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3829 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3830 // wxASSERT (para != NULL);
3834 // Combine paragraph and list styles. If there is a list style in the original attributes,
3835 // the current indentation overrides anything else and is used to find the item indentation.
3836 // Also, for applying paragraph styles, consider having 2 modes: (1) we merge with what we have,
3837 // thereby taking into account all user changes, (2) reset the style completely (except for indentation/list
3838 // exception as above).
3839 // Problem: when changing from one list style to another, there's a danger that the level info will get lost.
3840 // So when changing a list style interactively, could retrieve level based on current style, then
3841 // set appropriate indent and apply new style.
3845 if (para
->GetAttributes().HasOutlineLevel())
3846 outline
= para
->GetAttributes().GetOutlineLevel();
3847 if (para
->GetAttributes().HasBulletNumber())
3848 num
= para
->GetAttributes().GetBulletNumber();
3850 if (!para
->GetAttributes().GetParagraphStyleName().IsEmpty() && !para
->GetAttributes().GetListStyleName().IsEmpty())
3852 int currentIndent
= para
->GetAttributes().GetLeftIndent();
3854 wxRichTextParagraphStyleDefinition
* paraDef
= styleSheet
->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
3855 wxRichTextListStyleDefinition
* listDef
= styleSheet
->FindListStyle(para
->GetAttributes().GetListStyleName());
3856 if (paraDef
&& !listDef
)
3858 para
->GetAttributes() = paraDef
->GetStyleMergedWithBase(styleSheet
);
3861 else if (listDef
&& !paraDef
)
3863 // Set overall style defined for the list style definition
3864 para
->GetAttributes() = listDef
->GetStyleMergedWithBase(styleSheet
);
3866 // Apply the style for this level
3867 wxRichTextApplyStyle(para
->GetAttributes(), * listDef
->GetLevelAttributes(listDef
->FindLevelForIndent(currentIndent
)));
3870 else if (listDef
&& paraDef
)
3872 // Combines overall list style, style for level, and paragraph style
3873 para
->GetAttributes() = listDef
->CombineWithParagraphStyle(currentIndent
, paraDef
->GetStyleMergedWithBase(styleSheet
));
3877 else if (para
->GetAttributes().GetParagraphStyleName().IsEmpty() && !para
->GetAttributes().GetListStyleName().IsEmpty())
3879 int currentIndent
= para
->GetAttributes().GetLeftIndent();
3881 wxRichTextListStyleDefinition
* listDef
= styleSheet
->FindListStyle(para
->GetAttributes().GetListStyleName());
3883 // Overall list definition style
3884 para
->GetAttributes() = listDef
->GetStyleMergedWithBase(styleSheet
);
3886 // Style for this level
3887 wxRichTextApplyStyle(para
->GetAttributes(), * listDef
->GetLevelAttributes(listDef
->FindLevelForIndent(currentIndent
)));
3891 else if (!para
->GetAttributes().GetParagraphStyleName().IsEmpty() && para
->GetAttributes().GetListStyleName().IsEmpty())
3893 wxRichTextParagraphStyleDefinition
* def
= styleSheet
->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
3896 para
->GetAttributes() = def
->GetStyleMergedWithBase(styleSheet
);
3902 para
->GetAttributes().SetOutlineLevel(outline
);
3904 para
->GetAttributes().SetBulletNumber(num
);
3907 node
= node
->GetNext();
3909 return foundCount
!= 0;
3913 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
3915 wxRichTextBuffer
* buffer
= GetBuffer();
3916 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
3918 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
3919 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
3920 bool specifyLevel
= ((flags
& wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL
) != 0);
3921 bool renumber
= ((flags
& wxRICHTEXT_SETSTYLE_RENUMBER
) != 0);
3923 // Current number, if numbering
3926 wxASSERT (!specifyLevel
|| (specifyLevel
&& (specifiedLevel
>= 0)));
3928 // If we are associated with a control, make undoable; otherwise, apply immediately
3931 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3933 wxRichTextAction
* action
= NULL
;
3935 if (haveControl
&& withUndo
)
3937 action
= new wxRichTextAction(NULL
, _("Change List Style"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
3938 action
->SetRange(range
);
3939 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3942 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3945 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3946 // wxASSERT (para != NULL);
3948 if (para
&& para
->GetChildCount() > 0)
3950 // Stop searching if we're beyond the range of interest
3951 if (para
->GetRange().GetStart() > range
.GetEnd())
3954 if (!para
->GetRange().IsOutside(range
))
3956 // We'll be using a copy of the paragraph to make style changes,
3957 // not updating the buffer directly.
3958 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3960 if (haveControl
&& withUndo
)
3962 newPara
= new wxRichTextParagraph(*para
);
3963 action
->GetNewParagraphs().AppendChild(newPara
);
3965 // Also store the old ones for Undo
3966 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3973 int thisIndent
= newPara
->GetAttributes().GetLeftIndent();
3974 int thisLevel
= specifyLevel
? specifiedLevel
: def
->FindLevelForIndent(thisIndent
);
3976 // How is numbering going to work?
3977 // If we are renumbering, or numbering for the first time, we need to keep
3978 // track of the number for each level. But we might be simply applying a different
3980 // In Word, applying a style to several paragraphs, even if at different levels,
3981 // reverts the level back to the same one. So we could do the same here.
3982 // Renumbering will need to be done when we promote/demote a paragraph.
3984 // Apply the overall list style, and item style for this level
3985 wxRichTextAttr
listStyle(def
->GetCombinedStyleForLevel(thisLevel
, styleSheet
));
3986 wxRichTextApplyStyle(newPara
->GetAttributes(), listStyle
);
3988 // Now we need to do numbering
3991 newPara
->GetAttributes().SetBulletNumber(n
);
3996 else if (!newPara
->GetAttributes().GetListStyleName().IsEmpty())
3998 // if def is NULL, remove list style, applying any associated paragraph style
3999 // to restore the attributes
4001 newPara
->GetAttributes().SetListStyleName(wxEmptyString
);
4002 newPara
->GetAttributes().SetLeftIndent(0, 0);
4003 newPara
->GetAttributes().SetBulletText(wxEmptyString
);
4005 // Eliminate the main list-related attributes
4006 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
);
4008 if (styleSheet
&& !newPara
->GetAttributes().GetParagraphStyleName().IsEmpty())
4010 wxRichTextParagraphStyleDefinition
* def
= styleSheet
->FindParagraphStyle(newPara
->GetAttributes().GetParagraphStyleName());
4013 newPara
->GetAttributes() = def
->GetStyleMergedWithBase(styleSheet
);
4020 node
= node
->GetNext();
4023 // Do action, or delay it until end of batch.
4024 if (haveControl
&& withUndo
)
4025 buffer
->SubmitAction(action
);
4030 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
)
4032 wxRichTextBuffer
* buffer
= GetBuffer();
4033 if (buffer
&& buffer
->GetStyleSheet())
4035 wxRichTextListStyleDefinition
* def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4037 return SetListStyle(range
, def
, flags
, startFrom
, specifiedLevel
);
4042 /// Clear list for given range
4043 bool wxRichTextParagraphLayoutBox::ClearListStyle(const wxRichTextRange
& range
, int flags
)
4045 return SetListStyle(range
, NULL
, flags
);
4048 /// Number/renumber any list elements in the given range
4049 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
4051 return DoNumberList(range
, range
, 0, def
, flags
, startFrom
, specifiedLevel
);
4054 /// Number/renumber any list elements in the given range. Also do promotion or demotion of items, if specified
4055 bool wxRichTextParagraphLayoutBox::DoNumberList(const wxRichTextRange
& range
, const wxRichTextRange
& promotionRange
, int promoteBy
,
4056 wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
4058 wxRichTextBuffer
* buffer
= GetBuffer();
4059 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
4061 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
4062 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
4064 bool specifyLevel
= ((flags
& wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL
) != 0);
4067 bool renumber
= ((flags
& wxRICHTEXT_SETSTYLE_RENUMBER
) != 0);
4069 // Max number of levels
4070 const int maxLevels
= 10;
4072 // The level we're looking at now
4073 int currentLevel
= -1;
4075 // The item number for each level
4076 int levels
[maxLevels
];
4079 // Reset all numbering
4080 for (i
= 0; i
< maxLevels
; i
++)
4082 if (startFrom
!= -1)
4083 levels
[i
] = startFrom
-1;
4084 else if (renumber
) // start again
4087 levels
[i
] = -1; // start from the number we found, if any
4091 wxASSERT(!specifyLevel
|| (specifyLevel
&& (specifiedLevel
>= 0)));
4094 // If we are associated with a control, make undoable; otherwise, apply immediately
4097 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
4099 wxRichTextAction
* action
= NULL
;
4101 if (haveControl
&& withUndo
)
4103 action
= new wxRichTextAction(NULL
, _("Renumber List"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
4104 action
->SetRange(range
);
4105 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
4108 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
4111 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
4112 // wxASSERT (para != NULL);
4114 if (para
&& para
->GetChildCount() > 0)
4116 // Stop searching if we're beyond the range of interest
4117 if (para
->GetRange().GetStart() > range
.GetEnd())
4120 if (!para
->GetRange().IsOutside(range
))
4122 // We'll be using a copy of the paragraph to make style changes,
4123 // not updating the buffer directly.
4124 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
4126 if (haveControl
&& withUndo
)
4128 newPara
= new wxRichTextParagraph(*para
);
4129 action
->GetNewParagraphs().AppendChild(newPara
);
4131 // Also store the old ones for Undo
4132 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
4137 wxRichTextListStyleDefinition
* defToUse
= def
;
4140 if (styleSheet
&& !newPara
->GetAttributes().GetListStyleName().IsEmpty())
4141 defToUse
= styleSheet
->FindListStyle(newPara
->GetAttributes().GetListStyleName());
4146 int thisIndent
= newPara
->GetAttributes().GetLeftIndent();
4147 int thisLevel
= defToUse
->FindLevelForIndent(thisIndent
);
4149 // If we've specified a level to apply to all, change the level.
4150 if (specifiedLevel
!= -1)
4151 thisLevel
= specifiedLevel
;
4153 // Do promotion if specified
4154 if ((promoteBy
!= 0) && !para
->GetRange().IsOutside(promotionRange
))
4156 thisLevel
= thisLevel
- promoteBy
;
4163 // Apply the overall list style, and item style for this level
4164 wxRichTextAttr
listStyle(defToUse
->GetCombinedStyleForLevel(thisLevel
, styleSheet
));
4165 wxRichTextApplyStyle(newPara
->GetAttributes(), listStyle
);
4167 // OK, we've (re)applied the style, now let's get the numbering right.
4169 if (currentLevel
== -1)
4170 currentLevel
= thisLevel
;
4172 // Same level as before, do nothing except increment level's number afterwards
4173 if (currentLevel
== thisLevel
)
4176 // A deeper level: start renumbering all levels after current level
4177 else if (thisLevel
> currentLevel
)
4179 for (i
= currentLevel
+1; i
<= thisLevel
; i
++)
4183 currentLevel
= thisLevel
;
4185 else if (thisLevel
< currentLevel
)
4187 currentLevel
= thisLevel
;
4190 // Use the current numbering if -1 and we have a bullet number already
4191 if (levels
[currentLevel
] == -1)
4193 if (newPara
->GetAttributes().HasBulletNumber())
4194 levels
[currentLevel
] = newPara
->GetAttributes().GetBulletNumber();
4196 levels
[currentLevel
] = 1;
4200 levels
[currentLevel
] ++;
4203 newPara
->GetAttributes().SetBulletNumber(levels
[currentLevel
]);
4205 // Create the bullet text if an outline list
4206 if (listStyle
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
)
4209 for (i
= 0; i
<= currentLevel
; i
++)
4211 if (!text
.IsEmpty())
4213 text
+= wxString::Format(wxT("%d"), levels
[i
]);
4215 newPara
->GetAttributes().SetBulletText(text
);
4221 node
= node
->GetNext();
4224 // Do action, or delay it until end of batch.
4225 if (haveControl
&& withUndo
)
4226 buffer
->SubmitAction(action
);
4231 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
)
4233 wxRichTextBuffer
* buffer
= GetBuffer();
4234 if (buffer
->GetStyleSheet())
4236 wxRichTextListStyleDefinition
* def
= NULL
;
4237 if (!defName
.IsEmpty())
4238 def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4239 return NumberList(range
, def
, flags
, startFrom
, specifiedLevel
);
4244 /// Promote the list items within the given range. promoteBy can be a positive or negative number, e.g. 1 or -1
4245 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy
, const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int specifiedLevel
)
4248 // One strategy is to first work out the range within which renumbering must occur. Then could pass these two ranges
4249 // to NumberList with a flag indicating promotion is required within one of the ranges.
4250 // Find first and last paragraphs in range. Then for first, calculate new indentation and look back until we find
4251 // a paragraph that either has no list style, or has one that is different or whose indentation is less.
4252 // We start renumbering from the para after that different para we found. We specify that the numbering of that
4253 // list position will start from 1.
4254 // Similarly, we look after the last para in the promote range for an indentation that is less (or no list style).
4255 // We can end the renumbering at this point.
4257 // For now, only renumber within the promotion range.
4259 return DoNumberList(range
, range
, promoteBy
, def
, flags
, 1, specifiedLevel
);
4262 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy
, const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int specifiedLevel
)
4264 wxRichTextBuffer
* buffer
= GetBuffer();
4265 if (buffer
->GetStyleSheet())
4267 wxRichTextListStyleDefinition
* def
= NULL
;
4268 if (!defName
.IsEmpty())
4269 def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4270 return PromoteList(promoteBy
, range
, def
, flags
, specifiedLevel
);
4275 /// Fills in the attributes for numbering a paragraph after previousParagraph. It also finds the
4276 /// position of the paragraph that it had to start looking from.
4277 bool wxRichTextParagraphLayoutBox::FindNextParagraphNumber(wxRichTextParagraph
* previousParagraph
, wxRichTextAttr
& attr
) const
4279 if (!previousParagraph
->GetAttributes().HasFlag(wxTEXT_ATTR_BULLET_STYLE
) || previousParagraph
->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
)
4282 wxRichTextBuffer
* buffer
= GetBuffer();
4283 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
4284 if (styleSheet
&& !previousParagraph
->GetAttributes().GetListStyleName().IsEmpty())
4286 wxRichTextListStyleDefinition
* def
= styleSheet
->FindListStyle(previousParagraph
->GetAttributes().GetListStyleName());
4289 // int thisIndent = previousParagraph->GetAttributes().GetLeftIndent();
4290 // int thisLevel = def->FindLevelForIndent(thisIndent);
4292 bool isOutline
= (previousParagraph
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
) != 0;
4294 attr
.SetFlags(previousParagraph
->GetAttributes().GetFlags() & (wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_BULLET_NUMBER
|wxTEXT_ATTR_BULLET_TEXT
|wxTEXT_ATTR_BULLET_NAME
));
4295 if (previousParagraph
->GetAttributes().HasBulletName())
4296 attr
.SetBulletName(previousParagraph
->GetAttributes().GetBulletName());
4297 attr
.SetBulletStyle(previousParagraph
->GetAttributes().GetBulletStyle());
4298 attr
.SetListStyleName(previousParagraph
->GetAttributes().GetListStyleName());
4300 int nextNumber
= previousParagraph
->GetAttributes().GetBulletNumber() + 1;
4301 attr
.SetBulletNumber(nextNumber
);
4305 wxString text
= previousParagraph
->GetAttributes().GetBulletText();
4306 if (!text
.IsEmpty())
4308 int pos
= text
.Find(wxT('.'), true);
4309 if (pos
!= wxNOT_FOUND
)
4311 text
= text
.Mid(0, text
.Length() - pos
- 1);
4314 text
= wxEmptyString
;
4315 if (!text
.IsEmpty())
4317 text
+= wxString::Format(wxT("%d"), nextNumber
);
4318 attr
.SetBulletText(text
);
4332 * wxRichTextParagraph
4333 * This object represents a single paragraph (or in a straight text editor, a line).
4336 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraph
, wxRichTextCompositeObject
)
4338 wxArrayInt
wxRichTextParagraph::sm_defaultTabs
;
4340 wxRichTextParagraph::wxRichTextParagraph(wxRichTextObject
* parent
, wxRichTextAttr
* style
):
4341 wxRichTextCompositeObject(parent
)
4344 SetAttributes(*style
);
4347 wxRichTextParagraph::wxRichTextParagraph(const wxString
& text
, wxRichTextObject
* parent
, wxRichTextAttr
* paraStyle
, wxRichTextAttr
* charStyle
):
4348 wxRichTextCompositeObject(parent
)
4351 SetAttributes(*paraStyle
);
4353 AppendChild(new wxRichTextPlainText(text
, this, charStyle
));
4356 wxRichTextParagraph::~wxRichTextParagraph()
4362 bool wxRichTextParagraph::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int WXUNUSED(descent
), int style
)
4367 // Currently we don't merge these attributes with the parent, but we
4368 // should consider whether we should (e.g. if we set a border colour
4369 // for all paragraphs). But generally box attributes are likely to be
4370 // different for different objects.
4371 wxRect paraRect
= GetRect();
4372 wxRichTextAttr attr
= GetCombinedAttributes();
4373 context
.ApplyVirtualAttributes(attr
, this);
4375 DrawBoxAttributes(dc
, GetBuffer(), attr
, paraRect
);
4377 // Draw the bullet, if any
4378 if (attr
.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE
)
4380 if (attr
.GetLeftSubIndent() != 0)
4382 int spaceBeforePara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingBefore());
4383 int leftIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftIndent());
4385 wxRichTextAttr
bulletAttr(attr
);
4387 // Combine with the font of the first piece of content, if one is specified
4388 if (GetChildren().GetCount() > 0)
4390 wxRichTextObject
* firstObj
= (wxRichTextObject
*) GetChildren().GetFirst()->GetData();
4391 if (!firstObj
->IsFloatable() && firstObj
->GetAttributes().HasFont())
4393 wxRichTextApplyStyle(bulletAttr
, firstObj
->GetAttributes());
4397 // Get line height from first line, if any
4398 wxRichTextLine
* line
= m_cachedLines
.GetFirst() ? (wxRichTextLine
* ) m_cachedLines
.GetFirst()->GetData() : NULL
;
4401 int lineHeight
wxDUMMY_INITIALIZE(0);
4404 lineHeight
= line
->GetSize().y
;
4405 linePos
= line
->GetPosition() + GetPosition();
4410 if (bulletAttr
.HasFont() && GetBuffer())
4411 font
= GetBuffer()->GetFontTable().FindFont(bulletAttr
);
4413 font
= (*wxNORMAL_FONT
);
4415 wxCheckSetFont(dc
, font
);
4417 lineHeight
= dc
.GetCharHeight();
4418 linePos
= GetPosition();
4419 linePos
.y
+= spaceBeforePara
;
4422 wxRect
bulletRect(GetPosition().x
+ leftIndent
, linePos
.y
, linePos
.x
- (GetPosition().x
+ leftIndent
), lineHeight
);
4424 if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
)
4426 if (wxRichTextBuffer::GetRenderer())
4427 wxRichTextBuffer::GetRenderer()->DrawBitmapBullet(this, dc
, bulletAttr
, bulletRect
);
4429 else if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_STANDARD
)
4431 if (wxRichTextBuffer::GetRenderer())
4432 wxRichTextBuffer::GetRenderer()->DrawStandardBullet(this, dc
, bulletAttr
, bulletRect
);
4436 wxString bulletText
= GetBulletText();
4438 if (!bulletText
.empty() && wxRichTextBuffer::GetRenderer())
4439 wxRichTextBuffer::GetRenderer()->DrawTextBullet(this, dc
, bulletAttr
, bulletRect
, bulletText
);
4444 // Draw the range for each line, one object at a time.
4446 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
4449 wxRichTextLine
* line
= node
->GetData();
4450 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
4452 // Lines are specified relative to the paragraph
4454 wxPoint linePosition
= line
->GetPosition() + GetPosition();
4456 // Don't draw if off the screen
4457 if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) != 0) || ((linePosition
.y
+ line
->GetSize().y
) >= rect
.y
&& linePosition
.y
<= rect
.y
+ rect
.height
))
4459 wxPoint objectPosition
= linePosition
;
4460 int maxDescent
= line
->GetDescent();
4462 // Loop through objects until we get to the one within range
4463 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
4468 wxRichTextObject
* child
= node2
->GetData();
4470 if (!child
->IsFloating() && child
->GetRange().GetLength() > 0 && !child
->GetRange().IsOutside(lineRange
) && !lineRange
.IsOutside(range
))
4472 // Draw this part of the line at the correct position
4473 wxRichTextRange
objectRange(child
->GetRange());
4474 objectRange
.LimitTo(lineRange
);
4477 if (child
->IsTopLevel())
4479 objectSize
= child
->GetCachedSize();
4480 objectRange
= child
->GetOwnRange();
4484 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING && wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4485 if (i
< (int) line
->GetObjectSizes().GetCount())
4487 objectSize
.x
= line
->GetObjectSizes()[(size_t) i
];
4493 child
->GetRangeSize(objectRange
, objectSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, objectPosition
);
4497 // Use the child object's width, but the whole line's height
4498 wxRect
childRect(objectPosition
, wxSize(objectSize
.x
, line
->GetSize().y
));
4499 child
->Draw(dc
, context
, objectRange
, selection
, childRect
, maxDescent
, style
);
4501 objectPosition
.x
+= objectSize
.x
;
4504 else if (child
->GetRange().GetStart() > lineRange
.GetEnd())
4505 // Can break out of inner loop now since we've passed this line's range
4508 node2
= node2
->GetNext();
4512 node
= node
->GetNext();
4518 // Get the range width using partial extents calculated for the whole paragraph.
4519 static int wxRichTextGetRangeWidth(const wxRichTextParagraph
& para
, const wxRichTextRange
& range
, const wxArrayInt
& partialExtents
)
4521 wxASSERT(partialExtents
.GetCount() >= (size_t) range
.GetLength());
4523 if (partialExtents
.GetCount() < (size_t) range
.GetLength())
4526 int leftMostPos
= 0;
4527 if (range
.GetStart() - para
.GetRange().GetStart() > 0)
4528 leftMostPos
= partialExtents
[range
.GetStart() - para
.GetRange().GetStart() - 1];
4530 int rightMostPos
= partialExtents
[range
.GetEnd() - para
.GetRange().GetStart()];
4532 int w
= rightMostPos
- leftMostPos
;
4537 /// Lay the item out
4538 bool wxRichTextParagraph::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
)
4540 // Deal with floating objects firstly before the normal layout
4541 wxRichTextBuffer
* buffer
= GetBuffer();
4543 wxRichTextFloatCollector
* collector
= GetContainer()->GetFloatCollector();
4544 wxASSERT(collector
);
4545 LayoutFloat(dc
, context
, rect
, style
, collector
);
4547 wxRichTextAttr attr
= GetCombinedAttributes();
4548 context
.ApplyVirtualAttributes(attr
, this);
4552 // Increase the size of the paragraph due to spacing
4553 int spaceBeforePara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingBefore());
4554 int spaceAfterPara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingAfter());
4555 int leftIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftIndent());
4556 int leftSubIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftSubIndent());
4557 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
4559 int lineSpacing
= 0;
4561 // Let's assume line spacing of 10 is normal, 15 is 1.5, 20 is 2, etc.
4562 if (attr
.HasLineSpacing() && attr
.GetLineSpacing() > 0 && attr
.GetFont().IsOk())
4564 wxCheckSetFont(dc
, attr
.GetFont());
4565 lineSpacing
= (int) (double(dc
.GetCharHeight()) * (double(attr
.GetLineSpacing())/10.0 - 1.0));
4568 // Start position for each line relative to the paragraph
4569 int startPositionFirstLine
= leftIndent
;
4570 int startPositionSubsequentLines
= leftIndent
+ leftSubIndent
;
4572 // If we have a bullet in this paragraph, the start position for the first line's text
4573 // is actually leftIndent + leftSubIndent.
4574 if (attr
.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE
)
4575 startPositionFirstLine
= startPositionSubsequentLines
;
4577 long lastEndPos
= GetRange().GetStart()-1;
4578 long lastCompletedEndPos
= lastEndPos
;
4580 int currentWidth
= 0;
4581 SetPosition(rect
.GetPosition());
4583 wxPoint
currentPosition(0, spaceBeforePara
); // We will calculate lines relative to paragraph
4586 int maxHeight
= currentPosition
.y
;
4591 int lineDescent
= 0;
4593 wxRichTextObjectList::compatibility_iterator node
;
4595 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4597 wxArrayInt partialExtents
;
4600 int paraDescent
= 0;
4602 // This calculates the partial text extents
4603 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, rect
.GetPosition(), & partialExtents
);
4605 node
= m_children
.GetFirst();
4608 wxRichTextObject
* child
= node
->GetData();
4610 //child->SetCachedSize(wxDefaultSize);
4611 child
->Layout(dc
, context
, rect
, style
);
4613 node
= node
->GetNext();
4620 // We may need to go back to a previous child, in which case create the new line,
4621 // find the child corresponding to the start position of the string, and
4624 wxRect availableRect
;
4626 node
= m_children
.GetFirst();
4629 wxRichTextObject
* child
= node
->GetData();
4631 // If floating, ignore. We already laid out floats.
4632 // Also ignore if empty object, except if we haven't got any
4634 if (child
->IsFloating() || !child
->IsShown() ||
4635 (child
->GetRange().GetLength() == 0 && maxHeight
> spaceBeforePara
)
4638 node
= node
->GetNext();
4642 // If this is e.g. a composite text box, it will need to be laid out itself.
4643 // But if just a text fragment or image, for example, this will
4644 // do nothing. NB: won't we need to set the position after layout?
4645 // since for example if position is dependent on vertical line size, we
4646 // can't tell the position until the size is determined. So possibly introduce
4647 // another layout phase.
4649 // We may only be looking at part of a child, if we searched back for wrapping
4650 // and found a suitable point some way into the child. So get the size for the fragment
4653 long nextBreakPos
= GetFirstLineBreakPosition(lastEndPos
+1);
4654 long lastPosToUse
= child
->GetRange().GetEnd();
4655 bool lineBreakInThisObject
= (nextBreakPos
> -1 && nextBreakPos
<= child
->GetRange().GetEnd());
4657 if (lineBreakInThisObject
)
4658 lastPosToUse
= nextBreakPos
;
4661 int childDescent
= 0;
4663 int startOffset
= (lineCount
== 0 ? startPositionFirstLine
: startPositionSubsequentLines
);
4664 availableRect
= wxRect(rect
.x
+ startOffset
, rect
.y
+ currentPosition
.y
,
4665 rect
.width
- startOffset
- rightIndent
, rect
.height
);
4667 if (child
->IsTopLevel())
4669 wxSize oldSize
= child
->GetCachedSize();
4671 child
->Invalidate(wxRICHTEXT_ALL
);
4672 child
->SetPosition(wxPoint(0, 0));
4674 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4675 // lays out the object again using the minimum size
4676 // The position will be determined by its location in its line,
4677 // and not by the child's actual position.
4678 child
->LayoutToBestSize(dc
, context
, buffer
,
4679 attr
, child
->GetAttributes(), availableRect
, parentRect
, style
);
4681 if (oldSize
!= child
->GetCachedSize())
4683 partialExtents
.Clear();
4685 // Recalculate the partial text extents since the child object changed size
4686 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), & partialExtents
);
4690 // Problem: we need to layout composites here for which we need the available width,
4691 // but we can't get the available width without using the float collector which
4692 // needs to know the object height.
4694 if ((nextBreakPos
== -1) && (lastEndPos
== child
->GetRange().GetStart() - 1)) // i.e. we want to get the whole thing
4696 childSize
= child
->GetCachedSize();
4697 childDescent
= child
->GetDescent();
4701 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4702 // Get height only, then the width using the partial extents
4703 GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
);
4704 childSize
.x
= wxRichTextGetRangeWidth(*this, wxRichTextRange(lastEndPos
+1, lastPosToUse
), partialExtents
);
4706 GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, rect
.GetPosition());
4711 int loopIterations
= 0;
4713 // If there are nested objects that need to lay themselves out, we have to do this in a
4714 // loop because the height of the object may well depend on the available width.
4715 // And because of floating object positioning, the available width depends on the
4716 // height of the object and whether it will clash with the floating objects.
4717 // So, we see whether the available width changes due to the presence of floating images.
4718 // If it does, then we'll use the new restricted width to find the object height again.
4719 // If this causes another restriction in the available width, we'll try again, until
4720 // either we lose patience or the available width settles down.
4725 wxRect oldAvailableRect
= availableRect
;
4727 // Available width depends on the floating objects and the line height.
4728 // Note: the floating objects may be placed vertically along the two side of
4729 // buffer, so we may have different available line widths with different
4730 // [startY, endY]. So, we can't determine how wide the available
4731 // space is until we know the exact line height.
4732 lineDescent
= wxMax(childDescent
, maxDescent
);
4733 lineAscent
= wxMax(childSize
.y
-childDescent
, maxAscent
);
4734 lineHeight
= lineDescent
+ lineAscent
;
4735 wxRect floatAvailableRect
= collector
->GetAvailableRect(rect
.y
+ currentPosition
.y
, rect
.y
+ currentPosition
.y
+ lineHeight
);
4737 // Adjust availableRect to the space that is available when taking floating objects into account.
4739 if (floatAvailableRect
.x
+ startOffset
> availableRect
.x
)
4741 int newX
= floatAvailableRect
.x
+ startOffset
;
4742 int newW
= availableRect
.width
- (newX
- availableRect
.x
);
4743 availableRect
.x
= newX
;
4744 availableRect
.width
= newW
;
4747 if (floatAvailableRect
.width
< availableRect
.width
)
4748 availableRect
.width
= floatAvailableRect
.width
;
4750 currentPosition
.x
= availableRect
.x
- rect
.x
;
4752 if (child
->IsTopLevel() && loopIterations
<= 20)
4754 if (availableRect
!= oldAvailableRect
)
4756 wxSize oldSize
= child
->GetCachedSize();
4758 //child->SetCachedSize(wxDefaultSize);
4759 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4760 // lays out the object again using the minimum size
4761 child
->Invalidate(wxRICHTEXT_ALL
);
4762 child
->LayoutToBestSize(dc
, context
, buffer
,
4763 attr
, child
->GetAttributes(), availableRect
, parentRect
, style
);
4764 childSize
= child
->GetCachedSize();
4765 childDescent
= child
->GetDescent();
4766 //child->SetPosition(availableRect.GetPosition());
4768 if (oldSize
!= child
->GetCachedSize())
4770 partialExtents
.Clear();
4772 // Recalculate the partial text extents since the child object changed size
4773 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), & partialExtents
);
4776 // Go around the loop finding the available rect for the given floating objects
4787 // 1) There was a line break BEFORE the natural break
4788 // 2) There was a line break AFTER the natural break
4789 // 3) It's the last line
4790 // 4) The child still fits (carry on) - 'else' clause
4792 if ((lineBreakInThisObject
&& (childSize
.x
+ currentWidth
<= availableRect
.width
))
4794 (childSize
.x
+ currentWidth
> availableRect
.width
)
4796 ((childSize
.x
+ currentWidth
<= availableRect
.width
) && !node
->GetNext())
4800 if (child
->IsTopLevel())
4802 // We can move it to the correct position at this point
4803 child
->Move(GetPosition() + wxPoint(currentWidth
, currentPosition
.y
));
4806 long wrapPosition
= 0;
4807 if ((childSize
.x
+ currentWidth
<= availableRect
.width
) && !node
->GetNext() && !lineBreakInThisObject
)
4808 wrapPosition
= child
->GetRange().GetEnd();
4811 // Find a place to wrap. This may walk back to previous children,
4812 // for example if a word spans several objects.
4813 // Note: one object must contains only one wxTextAtrr, so the line height will not
4814 // change inside one object. Thus, we can pass the remain line width to the
4815 // FindWrapPosition function.
4816 if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos
+1, child
->GetRange().GetEnd()), dc
, context
, availableRect
.width
, wrapPosition
, & partialExtents
))
4818 // If the function failed, just cut it off at the end of this child.
4819 wrapPosition
= child
->GetRange().GetEnd();
4822 // FindWrapPosition can still return a value that will put us in an endless wrapping loop
4823 if (wrapPosition
<= lastCompletedEndPos
)
4824 wrapPosition
= wxMax(lastCompletedEndPos
+1,child
->GetRange().GetEnd());
4826 // Line end position shouldn't be the same as the end, or greater.
4827 if (wrapPosition
>= GetRange().GetEnd())
4828 wrapPosition
= GetRange().GetEnd()-1;
4830 // wxLogDebug(wxT("Split at %ld"), wrapPosition);
4832 // Let's find the actual size of the current line now
4834 wxRichTextRange
actualRange(lastCompletedEndPos
+1, wrapPosition
);
4836 /// Use previous descent, not the wrapping descent we just found, since this may be too big
4837 /// for the fragment we're about to add.
4838 childDescent
= maxDescent
;
4840 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4841 if (!child
->IsEmpty())
4843 // Get height only, then the width using the partial extents
4844 GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
);
4845 actualSize
.x
= wxRichTextGetRangeWidth(*this, actualRange
, partialExtents
);
4849 GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
4851 currentWidth
= actualSize
.x
;
4852 maxDescent
= wxMax(childDescent
, maxDescent
);
4853 maxAscent
= wxMax(actualSize
.y
-childDescent
, maxAscent
);
4854 lineHeight
= maxDescent
+ maxAscent
;
4856 if (lineHeight
== 0 && buffer
)
4858 wxFont
font(buffer
->GetFontTable().FindFont(attr
));
4859 wxCheckSetFont(dc
, font
);
4860 lineHeight
= dc
.GetCharHeight();
4863 if (maxDescent
== 0)
4866 dc
.GetTextExtent(wxT("X"), & w
, &h
, & maxDescent
);
4870 wxRichTextLine
* line
= AllocateLine(lineCount
);
4872 // Set relative range so we won't have to change line ranges when paragraphs are moved
4873 line
->SetRange(wxRichTextRange(actualRange
.GetStart() - GetRange().GetStart(), actualRange
.GetEnd() - GetRange().GetStart()));
4874 line
->SetPosition(currentPosition
);
4875 line
->SetSize(wxSize(currentWidth
, lineHeight
));
4876 line
->SetDescent(maxDescent
);
4878 maxHeight
= currentPosition
.y
+ lineHeight
;
4880 // Now move down a line. TODO: add margins, spacing
4881 currentPosition
.y
+= lineHeight
;
4882 currentPosition
.y
+= lineSpacing
;
4885 maxWidth
= wxMax(maxWidth
, currentWidth
+startOffset
);
4890 // TODO: account for zero-length objects, such as fields
4891 // wxASSERT(wrapPosition > lastCompletedEndPos);
4893 lastEndPos
= wrapPosition
;
4894 lastCompletedEndPos
= lastEndPos
;
4898 if (wrapPosition
< GetRange().GetEnd()-1)
4900 // May need to set the node back to a previous one, due to searching back in wrapping
4901 wxRichTextObject
* childAfterWrapPosition
= FindObjectAtPosition(wrapPosition
+1);
4902 if (childAfterWrapPosition
)
4903 node
= m_children
.Find(childAfterWrapPosition
);
4905 node
= node
->GetNext();
4908 node
= node
->GetNext();
4910 // Apply paragraph styles such as alignment to the wrapped line
4911 ApplyParagraphStyle(line
, attr
, availableRect
, dc
);
4915 // We still fit, so don't add a line, and keep going
4916 currentWidth
+= childSize
.x
;
4917 maxDescent
= wxMax(childDescent
, maxDescent
);
4918 maxAscent
= wxMax(childSize
.y
-childDescent
, maxAscent
);
4919 lineHeight
= maxDescent
+ maxAscent
;
4921 maxWidth
= wxMax(maxWidth
, currentWidth
+startOffset
);
4922 lastEndPos
= child
->GetRange().GetEnd();
4924 node
= node
->GetNext();
4928 //wxASSERT(!(lastCompletedEndPos != -1 && lastCompletedEndPos < GetRange().GetEnd()-1));
4930 // Remove remaining unused line objects, if any
4931 ClearUnusedLines(lineCount
);
4933 // We need to add back the margins etc.
4935 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
4936 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxWidth
, currentPosition
.y
+ spaceAfterPara
));
4937 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
4938 SetCachedSize(marginRect
.GetSize());
4941 // The maximum size is the length of the paragraph stretched out into a line.
4942 // So if there were a single word, or an image, or a fixed-size text box, the object could be shrunk around
4943 // this size. TODO: take into account line breaks.
4945 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
4946 contentRect
= wxRect(wxPoint(0, 0), wxSize(paraSize
.x
+ wxMax(leftIndent
, leftIndent
+ leftSubIndent
) + rightIndent
, currentPosition
.y
+ spaceAfterPara
));
4947 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
4948 SetMaxSize(marginRect
.GetSize());
4951 // Find the greatest minimum size. Currently we only look at non-text objects,
4952 // which isn't ideal but it would be slow to find the maximum word width to
4953 // use as the minimum.
4956 node
= m_children
.GetFirst();
4959 wxRichTextObject
* child
= node
->GetData();
4961 // If floating, ignore. We already laid out floats.
4962 // Also ignore if empty object, except if we haven't got any
4964 if (!child
->IsFloating() && child
->GetRange().GetLength() != 0 && !child
->IsKindOf(CLASSINFO(wxRichTextPlainText
)))
4966 if (child
->GetCachedSize().x
> minWidth
)
4967 minWidth
= child
->GetMinSize().x
;
4969 node
= node
->GetNext();
4972 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
4973 contentRect
= wxRect(wxPoint(0, 0), wxSize(minWidth
, currentPosition
.y
+ spaceAfterPara
));
4974 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
4975 SetMinSize(marginRect
.GetSize());
4979 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4980 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
4981 // Use the text extents to calculate the size of each fragment in each line
4982 wxRichTextLineList::compatibility_iterator lineNode
= m_cachedLines
.GetFirst();
4985 wxRichTextLine
* line
= lineNode
->GetData();
4986 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
4988 // Loop through objects until we get to the one within range
4989 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
4993 wxRichTextObject
* child
= node2
->GetData();
4995 if (child
->GetRange().GetLength() > 0 && !child
->GetRange().IsOutside(lineRange
))
4997 wxRichTextRange rangeToUse
= lineRange
;
4998 rangeToUse
.LimitTo(child
->GetRange());
5000 // Find the size of the child from the text extents, and store in an array
5001 // for drawing later
5003 if (rangeToUse
.GetStart() > GetRange().GetStart())
5004 left
= partialExtents
[(rangeToUse
.GetStart()-1) - GetRange().GetStart()];
5005 int right
= partialExtents
[rangeToUse
.GetEnd() - GetRange().GetStart()];
5006 int sz
= right
- left
;
5007 line
->GetObjectSizes().Add(sz
);
5009 else if (child
->GetRange().GetStart() > lineRange
.GetEnd())
5010 // Can break out of inner loop now since we've passed this line's range
5013 node2
= node2
->GetNext();
5016 lineNode
= lineNode
->GetNext();
5024 /// Apply paragraph styles, such as centering, to wrapped lines
5025 /// TODO: take into account box attributes, possibly
5026 void wxRichTextParagraph::ApplyParagraphStyle(wxRichTextLine
* line
, const wxRichTextAttr
& attr
, const wxRect
& rect
, wxDC
& dc
)
5028 if (!attr
.HasAlignment())
5031 wxPoint pos
= line
->GetPosition();
5032 wxSize size
= line
->GetSize();
5034 // centering, right-justification
5035 if (attr
.HasAlignment() && attr
.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE
)
5037 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
5038 pos
.x
= (rect
.GetWidth() - rightIndent
- size
.x
)/2 + pos
.x
;
5039 line
->SetPosition(pos
);
5041 else if (attr
.HasAlignment() && attr
.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT
)
5043 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
5044 pos
.x
= pos
.x
+ rect
.GetWidth() - size
.x
- rightIndent
;
5045 line
->SetPosition(pos
);
5049 /// Insert text at the given position
5050 bool wxRichTextParagraph::InsertText(long pos
, const wxString
& text
)
5052 wxRichTextObject
* childToUse
= NULL
;
5053 wxRichTextObjectList::compatibility_iterator nodeToUse
= wxRichTextObjectList::compatibility_iterator();
5055 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5058 wxRichTextObject
* child
= node
->GetData();
5059 if (child
->GetRange().Contains(pos
) && child
->GetRange().GetLength() > 0)
5066 node
= node
->GetNext();
5071 wxRichTextPlainText
* textObject
= wxDynamicCast(childToUse
, wxRichTextPlainText
);
5074 int posInString
= pos
- textObject
->GetRange().GetStart();
5076 wxString newText
= textObject
->GetText().Mid(0, posInString
) +
5077 text
+ textObject
->GetText().Mid(posInString
);
5078 textObject
->SetText(newText
);
5080 int textLength
= text
.length();
5082 textObject
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart(),
5083 textObject
->GetRange().GetEnd() + textLength
));
5085 // Increment the end range of subsequent fragments in this paragraph.
5086 // We'll set the paragraph range itself at a higher level.
5088 wxRichTextObjectList::compatibility_iterator node
= nodeToUse
->GetNext();
5091 wxRichTextObject
* child
= node
->GetData();
5092 child
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart() + textLength
,
5093 textObject
->GetRange().GetEnd() + textLength
));
5095 node
= node
->GetNext();
5102 // TODO: if not a text object, insert at closest position, e.g. in front of it
5108 // Don't pass parent initially to suppress auto-setting of parent range.
5109 // We'll do that at a higher level.
5110 wxRichTextPlainText
* textObject
= new wxRichTextPlainText(text
, this);
5112 AppendChild(textObject
);
5119 void wxRichTextParagraph::Copy(const wxRichTextParagraph
& obj
)
5121 wxRichTextCompositeObject::Copy(obj
);
5124 /// Clear the cached lines
5125 void wxRichTextParagraph::ClearLines()
5127 WX_CLEAR_LIST(wxRichTextLineList
, m_cachedLines
);
5130 /// Get/set the object size for the given range. Returns false if the range
5131 /// is invalid for this object.
5132 bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
5134 if (!range
.IsWithin(GetRange()))
5137 if (flags
& wxRICHTEXT_UNFORMATTED
)
5139 // Just use unformatted data, assume no line breaks
5140 // TODO: take into account line breaks
5144 wxArrayInt childExtents
;
5151 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5155 wxRichTextObject
* child
= node
->GetData();
5156 if (!child
->GetRange().IsOutside(range
))
5158 // Floating objects have a zero size within the paragraph.
5159 if (child
->IsFloating())
5164 if (partialExtents
->GetCount() > 0)
5165 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5169 partialExtents
->Add(0 /* zero size */ + lastSize
);
5176 wxRichTextRange rangeToUse
= range
;
5177 rangeToUse
.LimitTo(child
->GetRange());
5179 if (child
->IsTopLevel())
5180 rangeToUse
= child
->GetOwnRange();
5182 int childDescent
= 0;
5184 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we're already cached the size,
5185 // but it's only going to be used after caching has taken place.
5186 if ((flags
& wxRICHTEXT_HEIGHT_ONLY
) && child
->GetCachedSize().y
!= 0)
5188 childDescent
= child
->GetDescent();
5189 childSize
= child
->GetCachedSize();
5191 sz
.y
= wxMax(sz
.y
, childSize
.y
);
5192 sz
.x
+= childSize
.x
;
5193 descent
= wxMax(descent
, childDescent
);
5195 else if (child
->IsTopLevel())
5197 childDescent
= child
->GetDescent();
5198 childSize
= child
->GetCachedSize();
5200 sz
.y
= wxMax(sz
.y
, childSize
.y
);
5201 sz
.x
+= childSize
.x
;
5202 descent
= wxMax(descent
, childDescent
);
5203 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange()))
5205 child
->SetCachedSize(childSize
);
5206 child
->SetDescent(childDescent
);
5212 if (partialExtents
->GetCount() > 0)
5213 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5217 partialExtents
->Add(childSize
.x
+ lastSize
);
5220 else if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, context
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
), p
))
5222 sz
.y
= wxMax(sz
.y
, childSize
.y
);
5223 sz
.x
+= childSize
.x
;
5224 descent
= wxMax(descent
, childDescent
);
5226 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange()))
5228 child
->SetCachedSize(childSize
);
5229 child
->SetDescent(childDescent
);
5235 if (partialExtents
->GetCount() > 0)
5236 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5241 for (i
= 0; i
< childExtents
.GetCount(); i
++)
5243 partialExtents
->Add(childExtents
[i
] + lastSize
);
5253 node
= node
->GetNext();
5259 // Use formatted data, with line breaks
5262 // We're going to loop through each line, and then for each line,
5263 // call GetRangeSize for the fragment that comprises that line.
5264 // Only we have to do that multiple times within the line, because
5265 // the line may be broken into pieces. For now ignore line break commands
5266 // (so we can assume that getting the unformatted size for a fragment
5267 // within a line is the actual size)
5269 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5272 wxRichTextLine
* line
= node
->GetData();
5273 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5274 if (!lineRange
.IsOutside(range
))
5278 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
5281 wxRichTextObject
* child
= node2
->GetData();
5283 if (!child
->IsFloating() && !child
->GetRange().IsOutside(lineRange
))
5285 wxRichTextRange rangeToUse
= lineRange
;
5286 rangeToUse
.LimitTo(child
->GetRange());
5287 if (child
->IsTopLevel())
5288 rangeToUse
= child
->GetOwnRange();
5291 int childDescent
= 0;
5292 if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, context
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
)))
5294 lineSize
.y
= wxMax(lineSize
.y
, childSize
.y
);
5295 lineSize
.x
+= childSize
.x
;
5297 descent
= wxMax(descent
, childDescent
);
5300 node2
= node2
->GetNext();
5303 // Increase size by a line (TODO: paragraph spacing)
5305 sz
.x
= wxMax(sz
.x
, lineSize
.x
);
5307 node
= node
->GetNext();
5314 /// Finds the absolute position and row height for the given character position
5315 bool wxRichTextParagraph::FindPosition(wxDC
& dc
, wxRichTextDrawingContext
& context
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
5319 wxRichTextLine
* line
= ((wxRichTextParagraphLayoutBox
*)GetParent())->GetLineAtPosition(0);
5321 *height
= line
->GetSize().y
;
5323 *height
= dc
.GetCharHeight();
5325 // -1 means 'the start of the buffer'.
5328 pt
= pt
+ line
->GetPosition();
5333 // The final position in a paragraph is taken to mean the position
5334 // at the start of the next paragraph.
5335 if (index
== GetRange().GetEnd())
5337 wxRichTextParagraphLayoutBox
* parent
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
5338 wxASSERT( parent
!= NULL
);
5340 // Find the height at the next paragraph, if any
5341 wxRichTextLine
* line
= parent
->GetLineAtPosition(index
+ 1);
5344 *height
= line
->GetSize().y
;
5345 pt
= line
->GetAbsolutePosition();
5349 *height
= dc
.GetCharHeight();
5350 int indent
= ConvertTenthsMMToPixels(dc
, m_attributes
.GetLeftIndent());
5351 pt
= wxPoint(indent
, GetCachedSize().y
);
5357 if (index
< GetRange().GetStart() || index
> GetRange().GetEnd())
5360 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5363 wxRichTextLine
* line
= node
->GetData();
5364 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5365 if (index
>= lineRange
.GetStart() && index
<= lineRange
.GetEnd())
5367 // If this is the last point in the line, and we're forcing the
5368 // returned value to be the start of the next line, do the required
5370 if (index
== lineRange
.GetEnd() && forceLineStart
)
5372 if (node
->GetNext())
5374 wxRichTextLine
* nextLine
= node
->GetNext()->GetData();
5375 *height
= nextLine
->GetSize().y
;
5376 pt
= nextLine
->GetAbsolutePosition();
5381 pt
.y
= line
->GetPosition().y
+ GetPosition().y
;
5383 wxRichTextRange
r(lineRange
.GetStart(), index
);
5387 // We find the size of the line up to this point,
5388 // then we can add this size to the line start position and
5389 // paragraph start position to find the actual position.
5391 if (GetRangeSize(r
, rangeSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, line
->GetPosition()+ GetPosition()))
5393 pt
.x
= line
->GetPosition().x
+ GetPosition().x
+ rangeSize
.x
;
5394 *height
= line
->GetSize().y
;
5401 node
= node
->GetNext();
5407 /// Hit-testing: returns a flag indicating hit test details, plus
5408 /// information about position
5409 int wxRichTextParagraph::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
5412 return wxRICHTEXT_HITTEST_NONE
;
5414 // If we're in the top-level container, then we can return
5415 // a suitable hit test code even if the point is outside the container area,
5416 // so that we can position the caret sensibly even if we don't
5417 // click on valid content. If we're not at the top-level, and the point
5418 // is not within this paragraph object, then we don't want to stop more
5419 // precise hit-testing from working prematurely, so return immediately.
5420 // NEW STRATEGY: use the parent boundary to test whether we're in the
5421 // right region, not the paragraph, since the paragraph may be positioned
5422 // some way in from where the user clicks.
5425 wxRichTextObject
* tempObj
, *tempContextObj
;
5426 if (GetParent() && GetParent()->wxRichTextObject::HitTest(dc
, context
, pt
, tmpPos
, & tempObj
, & tempContextObj
, flags
) == wxRICHTEXT_HITTEST_NONE
)
5427 return wxRICHTEXT_HITTEST_NONE
;
5430 wxRichTextObjectList::compatibility_iterator objNode
= m_children
.GetFirst();
5433 wxRichTextObject
* child
= objNode
->GetData();
5434 if (child
->IsTopLevel() && ((flags
& wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS
) == 0))
5437 int hitTest
= child
->HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
);
5438 if (hitTest
!= wxRICHTEXT_HITTEST_NONE
)
5443 objNode
= objNode
->GetNext();
5446 wxPoint paraPos
= GetPosition();
5448 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5451 wxRichTextLine
* line
= node
->GetData();
5452 wxPoint linePos
= paraPos
+ line
->GetPosition();
5453 wxSize lineSize
= line
->GetSize();
5454 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5456 if (pt
.y
<= linePos
.y
+ lineSize
.y
)
5458 if (pt
.x
< linePos
.x
)
5460 textPosition
= lineRange
.GetStart();
5461 *obj
= FindObjectAtPosition(textPosition
);
5462 *contextObj
= GetContainer();
5463 return wxRICHTEXT_HITTEST_BEFORE
|wxRICHTEXT_HITTEST_OUTSIDE
;
5465 else if (pt
.x
>= (linePos
.x
+ lineSize
.x
))
5467 textPosition
= lineRange
.GetEnd();
5468 *obj
= FindObjectAtPosition(textPosition
);
5469 *contextObj
= GetContainer();
5470 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
;
5474 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5475 wxArrayInt partialExtents
;
5480 // This calculates the partial text extents
5481 GetRangeSize(lineRange
, paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, linePos
, & partialExtents
);
5483 int lastX
= linePos
.x
;
5485 for (i
= 0; i
< partialExtents
.GetCount(); i
++)
5487 int nextX
= partialExtents
[i
] + linePos
.x
;
5489 if (pt
.x
>= lastX
&& pt
.x
<= nextX
)
5491 textPosition
= i
+ lineRange
.GetStart(); // minus 1?
5493 *obj
= FindObjectAtPosition(textPosition
);
5494 *contextObj
= GetContainer();
5496 // So now we know it's between i-1 and i.
5497 // Let's see if we can be more precise about
5498 // which side of the position it's on.
5500 int midPoint
= (nextX
+ lastX
)/2;
5501 if (pt
.x
>= midPoint
)
5502 return wxRICHTEXT_HITTEST_AFTER
;
5504 return wxRICHTEXT_HITTEST_BEFORE
;
5511 int lastX
= linePos
.x
;
5512 for (i
= lineRange
.GetStart(); i
<= lineRange
.GetEnd(); i
++)
5517 wxRichTextRange
rangeToUse(lineRange
.GetStart(), i
);
5519 GetRangeSize(rangeToUse
, childSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, linePos
);
5521 int nextX
= childSize
.x
+ linePos
.x
;
5523 if (pt
.x
>= lastX
&& pt
.x
<= nextX
)
5527 *obj
= FindObjectAtPosition(textPosition
);
5528 *contextObj
= GetContainer();
5530 // So now we know it's between i-1 and i.
5531 // Let's see if we can be more precise about
5532 // which side of the position it's on.
5534 int midPoint
= (nextX
+ lastX
)/2;
5535 if (pt
.x
>= midPoint
)
5536 return wxRICHTEXT_HITTEST_AFTER
;
5538 return wxRICHTEXT_HITTEST_BEFORE
;
5549 node
= node
->GetNext();
5552 return wxRICHTEXT_HITTEST_NONE
;
5555 /// Split an object at this position if necessary, and return
5556 /// the previous object, or NULL if inserting at beginning.
5557 wxRichTextObject
* wxRichTextParagraph::SplitAt(long pos
, wxRichTextObject
** previousObject
)
5559 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5562 wxRichTextObject
* child
= node
->GetData();
5564 if (pos
== child
->GetRange().GetStart())
5568 if (node
->GetPrevious())
5569 *previousObject
= node
->GetPrevious()->GetData();
5571 *previousObject
= NULL
;
5577 if (child
->GetRange().Contains(pos
))
5579 // This should create a new object, transferring part of
5580 // the content to the old object and the rest to the new object.
5581 wxRichTextObject
* newObject
= child
->DoSplit(pos
);
5583 // If we couldn't split this object, just insert in front of it.
5586 // Maybe this is an empty string, try the next one
5591 // Insert the new object after 'child'
5592 if (node
->GetNext())
5593 m_children
.Insert(node
->GetNext(), newObject
);
5595 m_children
.Append(newObject
);
5596 newObject
->SetParent(this);
5599 *previousObject
= child
;
5605 node
= node
->GetNext();
5608 *previousObject
= NULL
;
5612 /// Move content to a list from obj on
5613 void wxRichTextParagraph::MoveToList(wxRichTextObject
* obj
, wxList
& list
)
5615 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(obj
);
5618 wxRichTextObject
* child
= node
->GetData();
5621 wxRichTextObjectList::compatibility_iterator oldNode
= node
;
5623 node
= node
->GetNext();
5625 m_children
.DeleteNode(oldNode
);
5629 /// Add content back from list
5630 void wxRichTextParagraph::MoveFromList(wxList
& list
)
5632 for (wxList::compatibility_iterator node
= list
.GetFirst(); node
; node
= node
->GetNext())
5634 AppendChild((wxRichTextObject
*) node
->GetData());
5639 void wxRichTextParagraph::CalculateRange(long start
, long& end
)
5641 wxRichTextCompositeObject::CalculateRange(start
, end
);
5643 // Add one for end of paragraph
5646 m_range
.SetRange(start
, end
);
5649 /// Find the object at the given position
5650 wxRichTextObject
* wxRichTextParagraph::FindObjectAtPosition(long position
)
5652 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5655 wxRichTextObject
* obj
= node
->GetData();
5656 if (obj
->GetRange().Contains(position
) ||
5657 obj
->GetRange().GetStart() == position
||
5658 obj
->GetRange().GetEnd() == position
)
5661 node
= node
->GetNext();
5666 /// Get the plain text searching from the start or end of the range.
5667 /// The resulting string may be shorter than the range given.
5668 bool wxRichTextParagraph::GetContiguousPlainText(wxString
& text
, const wxRichTextRange
& range
, bool fromStart
)
5670 text
= wxEmptyString
;
5674 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5677 wxRichTextObject
* obj
= node
->GetData();
5678 if (!obj
->GetRange().IsOutside(range
))
5680 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5683 text
+= textObj
->GetTextForRange(range
);
5691 node
= node
->GetNext();
5696 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetLast();
5699 wxRichTextObject
* obj
= node
->GetData();
5700 if (!obj
->GetRange().IsOutside(range
))
5702 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5705 text
= textObj
->GetTextForRange(range
) + text
;
5709 text
= wxT(" ") + text
;
5713 node
= node
->GetPrevious();
5720 /// Find a suitable wrap position.
5721 bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange
& range
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int availableSpace
, long& wrapPosition
, wxArrayInt
* partialExtents
)
5723 if (range
.GetLength() <= 0)
5726 // Find the first position where the line exceeds the available space.
5728 long breakPosition
= range
.GetEnd();
5730 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5731 if (partialExtents
&& partialExtents
->GetCount() >= (size_t) (GetRange().GetLength()-1)) // the final position in a paragraph is the newline
5735 if (range
.GetStart() > GetRange().GetStart())
5736 widthBefore
= (*partialExtents
)[range
.GetStart() - GetRange().GetStart() - 1];
5741 for (i
= (size_t) range
.GetStart(); i
<= (size_t) range
.GetEnd(); i
++)
5743 int widthFromStartOfThisRange
= (*partialExtents
)[i
- GetRange().GetStart()] - widthBefore
;
5745 if (widthFromStartOfThisRange
> availableSpace
)
5747 breakPosition
= i
-1;
5755 // Binary chop for speed
5756 long minPos
= range
.GetStart();
5757 long maxPos
= range
.GetEnd();
5760 if (minPos
== maxPos
)
5763 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
5765 if (sz
.x
> availableSpace
)
5766 breakPosition
= minPos
- 1;
5769 else if ((maxPos
- minPos
) == 1)
5772 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
5774 if (sz
.x
> availableSpace
)
5775 breakPosition
= minPos
- 1;
5778 GetRangeSize(wxRichTextRange(range
.GetStart(), maxPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
5779 if (sz
.x
> availableSpace
)
5780 breakPosition
= maxPos
-1;
5786 long nextPos
= minPos
+ ((maxPos
- minPos
) / 2);
5789 GetRangeSize(wxRichTextRange(range
.GetStart(), nextPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
5791 if (sz
.x
> availableSpace
)
5803 // Now we know the last position on the line.
5804 // Let's try to find a word break.
5807 if (GetContiguousPlainText(plainText
, wxRichTextRange(range
.GetStart(), breakPosition
), false))
5809 int newLinePos
= plainText
.Find(wxRichTextLineBreakChar
);
5810 if (newLinePos
!= wxNOT_FOUND
)
5812 breakPosition
= wxMax(0, range
.GetStart() + newLinePos
);
5816 int spacePos
= plainText
.Find(wxT(' '), true);
5817 int tabPos
= plainText
.Find(wxT('\t'), true);
5818 int pos
= wxMax(spacePos
, tabPos
);
5819 if (pos
!= wxNOT_FOUND
)
5821 int positionsFromEndOfString
= plainText
.length() - pos
- 1;
5822 breakPosition
= breakPosition
- positionsFromEndOfString
;
5827 wrapPosition
= breakPosition
;
5832 /// Get the bullet text for this paragraph.
5833 wxString
wxRichTextParagraph::GetBulletText()
5835 if (GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
||
5836 (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
))
5837 return wxEmptyString
;
5839 int number
= GetAttributes().GetBulletNumber();
5842 if ((GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ARABIC
) || (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
))
5844 text
.Printf(wxT("%d"), number
);
5846 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_UPPER
)
5848 // TODO: Unicode, and also check if number > 26
5849 text
.Printf(wxT("%c"), (wxChar
) (number
+64));
5851 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_LOWER
)
5853 // TODO: Unicode, and also check if number > 26
5854 text
.Printf(wxT("%c"), (wxChar
) (number
+96));
5856 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER
)
5858 text
= wxRichTextDecimalToRoman(number
);
5860 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER
)
5862 text
= wxRichTextDecimalToRoman(number
);
5865 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
)
5867 text
= GetAttributes().GetBulletText();
5870 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
)
5872 // The outline style relies on the text being computed statically,
5873 // since it depends on other levels points (e.g. 1.2.1.1). So normally the bullet text
5874 // should be stored in the attributes; if not, just use the number for this
5875 // level, as previously computed.
5876 if (!GetAttributes().GetBulletText().IsEmpty())
5877 text
= GetAttributes().GetBulletText();
5880 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PARENTHESES
)
5882 text
= wxT("(") + text
+ wxT(")");
5884 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_RIGHT_PARENTHESIS
)
5886 text
= text
+ wxT(")");
5889 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PERIOD
)
5897 /// Allocate or reuse a line object
5898 wxRichTextLine
* wxRichTextParagraph::AllocateLine(int pos
)
5900 if (pos
< (int) m_cachedLines
.GetCount())
5902 wxRichTextLine
* line
= m_cachedLines
.Item(pos
)->GetData();
5908 wxRichTextLine
* line
= new wxRichTextLine(this);
5909 m_cachedLines
.Append(line
);
5914 /// Clear remaining unused line objects, if any
5915 bool wxRichTextParagraph::ClearUnusedLines(int lineCount
)
5917 int cachedLineCount
= m_cachedLines
.GetCount();
5918 if ((int) cachedLineCount
> lineCount
)
5920 for (int i
= 0; i
< (int) (cachedLineCount
- lineCount
); i
++)
5922 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetLast();
5923 wxRichTextLine
* line
= node
->GetData();
5924 m_cachedLines
.Erase(node
);
5931 /// Get combined attributes of the base style, paragraph style and character style. We use this to dynamically
5932 /// retrieve the actual style.
5933 wxRichTextAttr
wxRichTextParagraph::GetCombinedAttributes(const wxRichTextAttr
& contentStyle
, bool includingBoxAttr
) const
5935 wxRichTextAttr attr
;
5936 wxRichTextParagraphLayoutBox
* buf
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
5939 attr
= buf
->GetBasicStyle();
5940 if (!includingBoxAttr
)
5942 attr
.GetTextBoxAttr().Reset();
5943 // The background colour will be painted by the container, and we don't
5944 // want to unnecessarily overwrite the background when we're drawing text
5945 // because this may erase the guideline (which appears just under the text
5946 // if there's no padding).
5947 attr
.SetFlags(attr
.GetFlags() & ~wxTEXT_ATTR_BACKGROUND_COLOUR
);
5949 wxRichTextApplyStyle(attr
, GetAttributes());
5952 attr
= GetAttributes();
5954 wxRichTextApplyStyle(attr
, contentStyle
);
5958 /// Get combined attributes of the base style and paragraph style.
5959 wxRichTextAttr
wxRichTextParagraph::GetCombinedAttributes(bool includingBoxAttr
) const
5961 wxRichTextAttr attr
;
5962 wxRichTextParagraphLayoutBox
* buf
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
5965 attr
= buf
->GetBasicStyle();
5966 if (!includingBoxAttr
)
5967 attr
.GetTextBoxAttr().Reset();
5968 wxRichTextApplyStyle(attr
, GetAttributes());
5971 attr
= GetAttributes();
5976 // Create default tabstop array
5977 void wxRichTextParagraph::InitDefaultTabs()
5979 // create a default tab list at 10 mm each.
5980 for (int i
= 0; i
< 20; ++i
)
5982 sm_defaultTabs
.Add(i
*100);
5986 // Clear default tabstop array
5987 void wxRichTextParagraph::ClearDefaultTabs()
5989 sm_defaultTabs
.Clear();
5992 void wxRichTextParagraph::LayoutFloat(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, int style
, wxRichTextFloatCollector
* floatCollector
)
5994 wxRichTextObjectList::compatibility_iterator node
= GetChildren().GetFirst();
5997 wxRichTextObject
* anchored
= node
->GetData();
5998 if (anchored
&& anchored
->IsFloating() && !floatCollector
->HasFloat(anchored
))
6002 anchored
->GetRangeSize(anchored
->GetRange(), size
, descent
, dc
, context
, style
);
6005 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().IsValid())
6007 offsetY
= anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetValue();
6008 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
6010 offsetY
= ConvertTenthsMMToPixels(dc
, offsetY
);
6014 int pos
= floatCollector
->GetFitPosition(anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode(), rect
.y
+ offsetY
, size
.y
);
6016 /* Update the offset */
6017 int newOffsetY
= pos
- rect
.y
;
6018 if (newOffsetY
!= offsetY
)
6020 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
6021 newOffsetY
= ConvertPixelsToTenthsMM(dc
, newOffsetY
);
6022 anchored
->GetAttributes().GetTextBoxAttr().GetTop().SetValue(newOffsetY
);
6025 if (anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT
)
6027 else if (anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT
)
6028 x
= rect
.x
+ rect
.width
- size
.x
;
6030 anchored
->SetPosition(wxPoint(x
, pos
));
6031 anchored
->SetCachedSize(size
);
6032 floatCollector
->CollectFloat(this, anchored
);
6035 node
= node
->GetNext();
6039 // Get the first position from pos that has a line break character.
6040 long wxRichTextParagraph::GetFirstLineBreakPosition(long pos
)
6042 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
6045 wxRichTextObject
* obj
= node
->GetData();
6046 if (pos
>= obj
->GetRange().GetStart() && pos
<= obj
->GetRange().GetEnd())
6048 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
6051 long breakPos
= textObj
->GetFirstLineBreakPosition(pos
);
6056 node
= node
->GetNext();
6063 * This object represents a line in a paragraph, and stores
6064 * offsets from the start of the paragraph representing the
6065 * start and end positions of the line.
6068 wxRichTextLine::wxRichTextLine(wxRichTextParagraph
* parent
)
6074 void wxRichTextLine::Init(wxRichTextParagraph
* parent
)
6077 m_range
.SetRange(-1, -1);
6078 m_pos
= wxPoint(0, 0);
6079 m_size
= wxSize(0, 0);
6081 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6082 m_objectSizes
.Clear();
6087 void wxRichTextLine::Copy(const wxRichTextLine
& obj
)
6089 m_range
= obj
.m_range
;
6090 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6091 m_objectSizes
= obj
.m_objectSizes
;
6095 /// Get the absolute object position
6096 wxPoint
wxRichTextLine::GetAbsolutePosition() const
6098 return m_parent
->GetPosition() + m_pos
;
6101 /// Get the absolute range
6102 wxRichTextRange
wxRichTextLine::GetAbsoluteRange() const
6104 wxRichTextRange
range(m_range
.GetStart() + m_parent
->GetRange().GetStart(), 0);
6105 range
.SetEnd(range
.GetStart() + m_range
.GetLength()-1);
6110 * wxRichTextPlainText
6111 * This object represents a single piece of text.
6114 IMPLEMENT_DYNAMIC_CLASS(wxRichTextPlainText
, wxRichTextObject
)
6116 wxRichTextPlainText::wxRichTextPlainText(const wxString
& text
, wxRichTextObject
* parent
, wxRichTextAttr
* style
):
6117 wxRichTextObject(parent
)
6120 SetAttributes(*style
);
6125 #define USE_KERNING_FIX 1
6127 // If insufficient tabs are defined, this is the tab width used
6128 #define WIDTH_FOR_DEFAULT_TABS 50
6131 bool wxRichTextPlainText::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int WXUNUSED(style
))
6133 wxRichTextParagraph
* para
= wxDynamicCast(GetParent(), wxRichTextParagraph
);
6134 wxASSERT (para
!= NULL
);
6136 wxRichTextAttr
textAttr(para
? para
->GetCombinedAttributes(GetAttributes(), false /* no box attributes */) : GetAttributes());
6137 context
.ApplyVirtualAttributes(textAttr
, this);
6139 // Let's make the assumption for now that for content in a paragraph, including
6140 // text, we never have a discontinuous selection. So we only deal with a
6142 wxRichTextRange selectionRange
;
6143 if (selection
.IsValid())
6145 wxRichTextRangeArray selectionRanges
= selection
.GetSelectionForObject(this);
6146 if (selectionRanges
.GetCount() > 0)
6147 selectionRange
= selectionRanges
[0];
6149 selectionRange
= wxRICHTEXT_NO_SELECTION
;
6152 selectionRange
= wxRICHTEXT_NO_SELECTION
;
6154 int offset
= GetRange().GetStart();
6156 // Replace line break characters with spaces
6157 wxString str
= m_text
;
6158 wxString toRemove
= wxRichTextLineBreakChar
;
6159 str
.Replace(toRemove
, wxT(" "));
6160 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS
))
6163 long len
= range
.GetLength();
6164 wxString stringChunk
= str
.Mid(range
.GetStart() - offset
, (size_t) len
);
6166 // Test for the optimized situations where all is selected, or none
6169 wxFont
textFont(GetBuffer()->GetFontTable().FindFont(textAttr
));
6170 wxCheckSetFont(dc
, textFont
);
6171 int charHeight
= dc
.GetCharHeight();
6174 if ( textFont
.IsOk() )
6176 if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
) )
6178 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6179 textFont
.SetPointSize( static_cast<int>(size
) );
6182 wxCheckSetFont(dc
, textFont
);
6184 else if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) )
6186 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6187 textFont
.SetPointSize( static_cast<int>(size
) );
6189 int sub_height
= static_cast<int>( static_cast<double>(charHeight
) / wxSCRIPT_MUL_FACTOR
);
6190 y
= rect
.y
+ (rect
.height
- sub_height
+ (descent
- m_descent
));
6191 wxCheckSetFont(dc
, textFont
);
6196 y
= rect
.y
+ (rect
.height
- charHeight
- (descent
- m_descent
));
6202 y
= rect
.y
+ (rect
.height
- charHeight
- (descent
- m_descent
));
6205 // TODO: new selection code
6207 // (a) All selected.
6208 if (selectionRange
.GetStart() <= range
.GetStart() && selectionRange
.GetEnd() >= range
.GetEnd())
6210 DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, true);
6212 // (b) None selected.
6213 else if (selectionRange
.GetEnd() < range
.GetStart() || selectionRange
.GetStart() > range
.GetEnd())
6215 // Draw all unselected
6216 DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, false);
6220 // (c) Part selected, part not
6221 // Let's draw unselected chunk, selected chunk, then unselected chunk.
6223 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6225 // 1. Initial unselected chunk, if any, up until start of selection.
6226 if (selectionRange
.GetStart() > range
.GetStart() && selectionRange
.GetStart() <= range
.GetEnd())
6228 int r1
= range
.GetStart();
6229 int s1
= selectionRange
.GetStart()-1;
6230 int fragmentLen
= s1
- r1
+ 1;
6231 if (fragmentLen
< 0)
6233 wxLogDebug(wxT("Mid(%d, %d"), (int)(r1
- offset
), (int)fragmentLen
);
6235 wxString stringFragment
= str
.Mid(r1
- offset
, fragmentLen
);
6237 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false);
6240 if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
)
6242 // Compensate for kerning difference
6243 wxString
stringFragment2(str
.Mid(r1
- offset
, fragmentLen
+1));
6244 wxString
stringFragment3(str
.Mid(r1
- 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 // 2. Selected chunk, if any.
6258 if (selectionRange
.GetEnd() >= range
.GetStart())
6260 int s1
= wxMax(selectionRange
.GetStart(), range
.GetStart());
6261 int s2
= wxMin(selectionRange
.GetEnd(), range
.GetEnd());
6263 int fragmentLen
= s2
- s1
+ 1;
6264 if (fragmentLen
< 0)
6266 wxLogDebug(wxT("Mid(%d, %d"), (int)(s1
- offset
), (int)fragmentLen
);
6268 wxString stringFragment
= str
.Mid(s1
- offset
, fragmentLen
);
6270 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, true);
6273 if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
)
6275 // Compensate for kerning difference
6276 wxString
stringFragment2(str
.Mid(s1
- offset
, fragmentLen
+1));
6277 wxString
stringFragment3(str
.Mid(s1
- offset
+ fragmentLen
, 1));
6279 wxCoord w1
, h1
, w2
, h2
, w3
, h3
;
6280 dc
.GetTextExtent(stringFragment
, & w1
, & h1
);
6281 dc
.GetTextExtent(stringFragment2
, & w2
, & h2
);
6282 dc
.GetTextExtent(stringFragment3
, & w3
, & h3
);
6284 int kerningDiff
= (w1
+ w3
) - w2
;
6285 x
= x
- kerningDiff
;
6290 // 3. Remaining unselected chunk, if any
6291 if (selectionRange
.GetEnd() < range
.GetEnd())
6293 int s2
= wxMin(selectionRange
.GetEnd()+1, range
.GetEnd());
6294 int r2
= range
.GetEnd();
6296 int fragmentLen
= r2
- s2
+ 1;
6297 if (fragmentLen
< 0)
6299 wxLogDebug(wxT("Mid(%d, %d"), (int)(s2
- offset
), (int)fragmentLen
);
6301 wxString stringFragment
= str
.Mid(s2
- offset
, fragmentLen
);
6303 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false);
6310 bool wxRichTextPlainText::DrawTabbedString(wxDC
& dc
, const wxRichTextAttr
& attr
, const wxRect
& rect
,wxString
& str
, wxCoord
& x
, wxCoord
& y
, bool selected
)
6312 bool hasTabs
= (str
.Find(wxT('\t')) != wxNOT_FOUND
);
6314 wxArrayInt tabArray
;
6318 if (attr
.GetTabs().IsEmpty())
6319 tabArray
= wxRichTextParagraph::GetDefaultTabs();
6321 tabArray
= attr
.GetTabs();
6322 tabCount
= tabArray
.GetCount();
6324 for (int i
= 0; i
< tabCount
; ++i
)
6326 int pos
= tabArray
[i
];
6327 pos
= ConvertTenthsMMToPixels(dc
, pos
);
6334 int nextTabPos
= -1;
6340 wxColour
highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
));
6341 wxColour
highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
));
6343 wxCheckSetBrush(dc
, wxBrush(highlightColour
));
6344 wxCheckSetPen(dc
, wxPen(highlightColour
));
6345 dc
.SetTextForeground(highlightTextColour
);
6346 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6350 dc
.SetTextForeground(attr
.GetTextColour());
6352 if (attr
.HasFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
) && attr
.GetBackgroundColour().IsOk())
6354 dc
.SetBackgroundMode(wxBRUSHSTYLE_SOLID
);
6355 dc
.SetTextBackground(attr
.GetBackgroundColour());
6358 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6361 wxCoord x_orig
= GetParent()->GetPosition().x
;
6364 // the string has a tab
6365 // break up the string at the Tab
6366 wxString stringChunk
= str
.BeforeFirst(wxT('\t'));
6367 str
= str
.AfterFirst(wxT('\t'));
6368 dc
.GetTextExtent(stringChunk
, & w
, & h
);
6370 bool not_found
= true;
6371 for (int i
= 0; i
< tabCount
&& not_found
; ++i
)
6373 nextTabPos
= tabArray
.Item(i
) + x_orig
;
6375 // Find the next tab position.
6376 // Even if we're at the end of the tab array, we must still draw the chunk.
6378 if (nextTabPos
> tabPos
|| (i
== (tabCount
- 1)))
6380 if (nextTabPos
<= tabPos
)
6382 int defaultTabWidth
= ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
);
6383 nextTabPos
= tabPos
+ defaultTabWidth
;
6390 wxRect
selRect(x
, rect
.y
, w
, rect
.GetHeight());
6391 dc
.DrawRectangle(selRect
);
6393 dc
.DrawText(stringChunk
, x
, y
);
6395 if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
))
6397 wxPen oldPen
= dc
.GetPen();
6398 wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1));
6399 dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5));
6400 wxCheckSetPen(dc
, oldPen
);
6406 hasTabs
= (str
.Find(wxT('\t')) != wxNOT_FOUND
);
6411 dc
.GetTextExtent(str
, & w
, & h
);
6414 wxRect
selRect(x
, rect
.y
, w
, rect
.GetHeight());
6415 dc
.DrawRectangle(selRect
);
6417 dc
.DrawText(str
, x
, y
);
6419 if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
))
6421 wxPen oldPen
= dc
.GetPen();
6422 wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1));
6423 dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5));
6424 wxCheckSetPen(dc
, oldPen
);
6433 /// Lay the item out
6434 bool wxRichTextPlainText::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& WXUNUSED(rect
), const wxRect
& WXUNUSED(parentRect
), int WXUNUSED(style
))
6436 // Only lay out if we haven't already cached the size
6438 GetRangeSize(GetRange(), m_size
, m_descent
, dc
, context
, 0, wxPoint(0, 0));
6440 // Eventually we want to have a reasonable estimate of minimum size.
6441 m_minSize
= wxSize(0, 0);
6446 void wxRichTextPlainText::Copy(const wxRichTextPlainText
& obj
)
6448 wxRichTextObject::Copy(obj
);
6450 m_text
= obj
.m_text
;
6453 /// Get/set the object size for the given range. Returns false if the range
6454 /// is invalid for this object.
6455 bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int WXUNUSED(flags
), wxPoint position
, wxArrayInt
* partialExtents
) const
6457 if (!range
.IsWithin(GetRange()))
6460 wxRichTextParagraph
* para
= wxDynamicCast(GetParent(), wxRichTextParagraph
);
6461 wxASSERT (para
!= NULL
);
6463 int relativeX
= position
.x
- GetParent()->GetPosition().x
;
6465 wxRichTextAttr
textAttr(para
? para
->GetCombinedAttributes(GetAttributes()) : GetAttributes());
6466 context
.ApplyVirtualAttributes(textAttr
, (wxRichTextObject
*) this);
6468 // Always assume unformatted text, since at this level we have no knowledge
6469 // of line breaks - and we don't need it, since we'll calculate size within
6470 // formatted text by doing it in chunks according to the line ranges
6472 bool bScript(false);
6473 wxFont
font(GetBuffer()->GetFontTable().FindFont(textAttr
));
6476 if ( textAttr
.HasTextEffects() && ( (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
)
6477 || (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) ) )
6479 wxFont textFont
= font
;
6480 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6481 textFont
.SetPointSize( static_cast<int>(size
) );
6482 wxCheckSetFont(dc
, textFont
);
6487 wxCheckSetFont(dc
, font
);
6491 bool haveDescent
= false;
6492 int startPos
= range
.GetStart() - GetRange().GetStart();
6493 long len
= range
.GetLength();
6495 wxString
str(m_text
);
6496 wxString toReplace
= wxRichTextLineBreakChar
;
6497 str
.Replace(toReplace
, wxT(" "));
6499 wxString stringChunk
= str
.Mid(startPos
, (size_t) len
);
6501 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS
))
6502 stringChunk
.MakeUpper();
6506 if (stringChunk
.Find(wxT('\t')) != wxNOT_FOUND
)
6508 // the string has a tab
6509 wxArrayInt tabArray
;
6510 if (textAttr
.GetTabs().IsEmpty())
6511 tabArray
= wxRichTextParagraph::GetDefaultTabs();
6513 tabArray
= textAttr
.GetTabs();
6515 int tabCount
= tabArray
.GetCount();
6517 for (int i
= 0; i
< tabCount
; ++i
)
6519 int pos
= tabArray
[i
];
6520 pos
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, pos
);
6524 int nextTabPos
= -1;
6526 while (stringChunk
.Find(wxT('\t')) >= 0)
6528 int absoluteWidth
= 0;
6530 // the string has a tab
6531 // break up the string at the Tab
6532 wxString stringFragment
= stringChunk
.BeforeFirst(wxT('\t'));
6533 stringChunk
= stringChunk
.AfterFirst(wxT('\t'));
6538 if (partialExtents
->GetCount() > 0)
6539 oldWidth
= (*partialExtents
)[partialExtents
->GetCount()-1];
6543 // Add these partial extents
6545 dc
.GetPartialTextExtents(stringFragment
, p
);
6547 for (j
= 0; j
< p
.GetCount(); j
++)
6548 partialExtents
->Add(oldWidth
+ p
[j
]);
6550 if (partialExtents
->GetCount() > 0)
6551 absoluteWidth
= (*partialExtents
)[(*partialExtents
).GetCount()-1] + relativeX
;
6553 absoluteWidth
= relativeX
;
6557 dc
.GetTextExtent(stringFragment
, & w
, & h
);
6559 absoluteWidth
= width
+ relativeX
;
6563 bool notFound
= true;
6564 for (int i
= 0; i
< tabCount
&& notFound
; ++i
)
6566 nextTabPos
= tabArray
.Item(i
);
6568 // Find the next tab position.
6569 // Even if we're at the end of the tab array, we must still process the chunk.
6571 if (nextTabPos
> absoluteWidth
|| (i
== (tabCount
- 1)))
6573 if (nextTabPos
<= absoluteWidth
)
6575 int defaultTabWidth
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
);
6576 nextTabPos
= absoluteWidth
+ defaultTabWidth
;
6580 width
= nextTabPos
- relativeX
;
6583 partialExtents
->Add(width
);
6589 if (!stringChunk
.IsEmpty())
6594 if (partialExtents
->GetCount() > 0)
6595 oldWidth
= (*partialExtents
)[partialExtents
->GetCount()-1];
6599 // Add these partial extents
6601 dc
.GetPartialTextExtents(stringChunk
, p
);
6603 for (j
= 0; j
< p
.GetCount(); j
++)
6604 partialExtents
->Add(oldWidth
+ p
[j
]);
6608 dc
.GetTextExtent(stringChunk
, & w
, & h
, & descent
);
6616 int charHeight
= dc
.GetCharHeight();
6617 if ((*partialExtents
).GetCount() > 0)
6618 w
= (*partialExtents
)[partialExtents
->GetCount()-1];
6621 size
= wxSize(w
, charHeight
);
6625 size
= wxSize(width
, dc
.GetCharHeight());
6629 dc
.GetTextExtent(wxT("X"), & w
, & h
, & descent
);
6637 /// Do a split, returning an object containing the second part, and setting
6638 /// the first part in 'this'.
6639 wxRichTextObject
* wxRichTextPlainText::DoSplit(long pos
)
6641 long index
= pos
- GetRange().GetStart();
6643 if (index
< 0 || index
>= (int) m_text
.length())
6646 wxString firstPart
= m_text
.Mid(0, index
);
6647 wxString secondPart
= m_text
.Mid(index
);
6651 wxRichTextPlainText
* newObject
= new wxRichTextPlainText(secondPart
);
6652 newObject
->SetAttributes(GetAttributes());
6653 newObject
->SetProperties(GetProperties());
6655 newObject
->SetRange(wxRichTextRange(pos
, GetRange().GetEnd()));
6656 GetRange().SetEnd(pos
-1);
6662 void wxRichTextPlainText::CalculateRange(long start
, long& end
)
6664 end
= start
+ m_text
.length() - 1;
6665 m_range
.SetRange(start
, end
);
6669 bool wxRichTextPlainText::DeleteRange(const wxRichTextRange
& range
)
6671 wxRichTextRange r
= range
;
6673 r
.LimitTo(GetRange());
6675 if (r
.GetStart() == GetRange().GetStart() && r
.GetEnd() == GetRange().GetEnd())
6681 long startIndex
= r
.GetStart() - GetRange().GetStart();
6682 long len
= r
.GetLength();
6684 m_text
= m_text
.Mid(0, startIndex
) + m_text
.Mid(startIndex
+len
);
6688 /// Get text for the given range.
6689 wxString
wxRichTextPlainText::GetTextForRange(const wxRichTextRange
& range
) const
6691 wxRichTextRange r
= range
;
6693 r
.LimitTo(GetRange());
6695 long startIndex
= r
.GetStart() - GetRange().GetStart();
6696 long len
= r
.GetLength();
6698 return m_text
.Mid(startIndex
, len
);
6701 /// Returns true if this object can merge itself with the given one.
6702 bool wxRichTextPlainText::CanMerge(wxRichTextObject
* object
) const
6704 return object
->GetClassInfo() == CLASSINFO(wxRichTextPlainText
) &&
6705 (m_text
.empty() || (wxTextAttrEq(GetAttributes(), object
->GetAttributes()) && m_properties
== object
->GetProperties()));
6708 /// Returns true if this object merged itself with the given one.
6709 /// The calling code will then delete the given object.
6710 bool wxRichTextPlainText::Merge(wxRichTextObject
* object
)
6712 wxRichTextPlainText
* textObject
= wxDynamicCast(object
, wxRichTextPlainText
);
6713 wxASSERT( textObject
!= NULL
);
6717 m_text
+= textObject
->GetText();
6718 wxRichTextApplyStyle(m_attributes
, textObject
->GetAttributes());
6725 /// Dump to output stream for debugging
6726 void wxRichTextPlainText::Dump(wxTextOutputStream
& stream
)
6728 wxRichTextObject::Dump(stream
);
6729 stream
<< m_text
<< wxT("\n");
6732 /// Get the first position from pos that has a line break character.
6733 long wxRichTextPlainText::GetFirstLineBreakPosition(long pos
)
6736 int len
= m_text
.length();
6737 int startPos
= pos
- m_range
.GetStart();
6738 for (i
= startPos
; i
< len
; i
++)
6740 wxChar ch
= m_text
[i
];
6741 if (ch
== wxRichTextLineBreakChar
)
6743 return i
+ m_range
.GetStart();
6751 * This is a kind of box, used to represent the whole buffer
6754 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBuffer
, wxRichTextParagraphLayoutBox
)
6756 wxList
wxRichTextBuffer::sm_handlers
;
6757 wxList
wxRichTextBuffer::sm_drawingHandlers
;
6758 wxRichTextRenderer
* wxRichTextBuffer::sm_renderer
= NULL
;
6759 int wxRichTextBuffer::sm_bulletRightMargin
= 20;
6760 float wxRichTextBuffer::sm_bulletProportion
= (float) 0.3;
6763 void wxRichTextBuffer::Init()
6765 m_commandProcessor
= new wxCommandProcessor
;
6766 m_styleSheet
= NULL
;
6768 m_batchedCommandDepth
= 0;
6769 m_batchedCommand
= NULL
;
6776 wxRichTextBuffer::~wxRichTextBuffer()
6778 delete m_commandProcessor
;
6779 delete m_batchedCommand
;
6782 ClearEventHandlers();
6785 void wxRichTextBuffer::ResetAndClearCommands()
6789 GetCommandProcessor()->ClearCommands();
6792 Invalidate(wxRICHTEXT_ALL
);
6795 void wxRichTextBuffer::Copy(const wxRichTextBuffer
& obj
)
6797 wxRichTextParagraphLayoutBox::Copy(obj
);
6799 m_styleSheet
= obj
.m_styleSheet
;
6800 m_modified
= obj
.m_modified
;
6801 m_batchedCommandDepth
= 0;
6802 if (m_batchedCommand
)
6803 delete m_batchedCommand
;
6804 m_batchedCommand
= NULL
;
6805 m_suppressUndo
= obj
.m_suppressUndo
;
6806 m_invalidRange
= obj
.m_invalidRange
;
6809 /// Push style sheet to top of stack
6810 bool wxRichTextBuffer::PushStyleSheet(wxRichTextStyleSheet
* styleSheet
)
6813 styleSheet
->InsertSheet(m_styleSheet
);
6815 SetStyleSheet(styleSheet
);
6820 /// Pop style sheet from top of stack
6821 wxRichTextStyleSheet
* wxRichTextBuffer::PopStyleSheet()
6825 wxRichTextStyleSheet
* oldSheet
= m_styleSheet
;
6826 m_styleSheet
= oldSheet
->GetNextSheet();
6835 /// Submit command to insert paragraphs
6836 bool wxRichTextBuffer::InsertParagraphsWithUndo(long pos
, const wxRichTextParagraphLayoutBox
& paragraphs
, wxRichTextCtrl
* ctrl
, int flags
)
6838 return ctrl
->GetFocusObject()->InsertParagraphsWithUndo(this, pos
, paragraphs
, ctrl
, flags
);
6841 /// Submit command to insert paragraphs
6842 bool wxRichTextParagraphLayoutBox::InsertParagraphsWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxRichTextParagraphLayoutBox
& paragraphs
, wxRichTextCtrl
* ctrl
, int WXUNUSED(flags
))
6844 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6846 action
->GetNewParagraphs() = paragraphs
;
6848 action
->SetPosition(pos
);
6850 wxRichTextRange range
= wxRichTextRange(pos
, pos
+ paragraphs
.GetOwnRange().GetEnd() - 1);
6851 if (!paragraphs
.GetPartialParagraph())
6852 range
.SetEnd(range
.GetEnd()+1);
6854 // Set the range we'll need to delete in Undo
6855 action
->SetRange(range
);
6857 buffer
->SubmitAction(action
);
6862 /// Submit command to insert the given text
6863 bool wxRichTextBuffer::InsertTextWithUndo(long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
, int flags
)
6865 return ctrl
->GetFocusObject()->InsertTextWithUndo(this, pos
, text
, ctrl
, flags
);
6868 /// Submit command to insert the given text
6869 bool wxRichTextParagraphLayoutBox::InsertTextWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
, int flags
)
6871 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6873 wxRichTextAttr
* p
= NULL
;
6874 wxRichTextAttr paraAttr
;
6875 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
6877 // Get appropriate paragraph style
6878 paraAttr
= GetStyleForNewParagraph(buffer
, pos
, false, false);
6879 if (!paraAttr
.IsDefault())
6883 action
->GetNewParagraphs().AddParagraphs(text
, p
);
6885 int length
= action
->GetNewParagraphs().GetOwnRange().GetLength();
6887 if (!text
.empty() && text
.Last() != wxT('\n'))
6889 // Don't count the newline when undoing
6891 action
->GetNewParagraphs().SetPartialParagraph(true);
6893 else if (!text
.empty() && text
.Last() == wxT('\n'))
6896 action
->SetPosition(pos
);
6898 // Set the range we'll need to delete in Undo
6899 action
->SetRange(wxRichTextRange(pos
, pos
+ length
- 1));
6901 buffer
->SubmitAction(action
);
6906 /// Submit command to insert the given text
6907 bool wxRichTextBuffer::InsertNewlineWithUndo(long pos
, wxRichTextCtrl
* ctrl
, int flags
)
6909 return ctrl
->GetFocusObject()->InsertNewlineWithUndo(this, pos
, ctrl
, flags
);
6912 /// Submit command to insert the given text
6913 bool wxRichTextParagraphLayoutBox::InsertNewlineWithUndo(wxRichTextBuffer
* buffer
, long pos
, wxRichTextCtrl
* ctrl
, int flags
)
6915 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6917 wxRichTextAttr
* p
= NULL
;
6918 wxRichTextAttr paraAttr
;
6919 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
6921 paraAttr
= GetStyleForNewParagraph(buffer
, pos
, false, true /* look for next paragraph style */);
6922 if (!paraAttr
.IsDefault())
6926 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
6928 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(wxEmptyString
, this, & attr
);
6929 action
->GetNewParagraphs().AppendChild(newPara
);
6930 action
->GetNewParagraphs().UpdateRanges();
6931 action
->GetNewParagraphs().SetPartialParagraph(false);
6932 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
, false);
6936 newPara
->SetAttributes(*p
);
6938 if (flags
& wxRICHTEXT_INSERT_INTERACTIVE
)
6940 if (para
&& para
->GetRange().GetEnd() == pos
)
6943 // Now see if we need to number the paragraph.
6944 if (newPara
->GetAttributes().HasBulletNumber())
6946 wxRichTextAttr numberingAttr
;
6947 if (FindNextParagraphNumber(para
, numberingAttr
))
6948 wxRichTextApplyStyle(newPara
->GetAttributes(), (const wxRichTextAttr
&) numberingAttr
);
6952 action
->SetPosition(pos
);
6954 // Use the default character style
6955 // Use the default character style
6956 if (!buffer
->GetDefaultStyle().IsDefault() && newPara
->GetChildren().GetFirst())
6958 // Check whether the default style merely reflects the paragraph/basic style,
6959 // in which case don't apply it.
6960 wxRichTextAttr
defaultStyle(buffer
->GetDefaultStyle());
6961 wxRichTextAttr toApply
;
6964 wxRichTextAttr combinedAttr
= para
->GetCombinedAttributes(true /* include box attributes */);
6965 wxRichTextAttr newAttr
;
6966 // This filters out attributes that are accounted for by the current
6967 // paragraph/basic style
6968 wxRichTextApplyStyle(toApply
, defaultStyle
, & combinedAttr
);
6971 toApply
= defaultStyle
;
6973 if (!toApply
.IsDefault())
6974 newPara
->GetChildren().GetFirst()->GetData()->SetAttributes(toApply
);
6977 // Set the range we'll need to delete in Undo
6978 action
->SetRange(wxRichTextRange(pos1
, pos1
));
6980 buffer
->SubmitAction(action
);
6985 /// Submit command to insert the given image
6986 bool wxRichTextBuffer::InsertImageWithUndo(long pos
, const wxRichTextImageBlock
& imageBlock
, wxRichTextCtrl
* ctrl
, int flags
,
6987 const wxRichTextAttr
& textAttr
)
6989 return ctrl
->GetFocusObject()->InsertImageWithUndo(this, pos
, imageBlock
, ctrl
, flags
, textAttr
);
6992 /// Submit command to insert the given image
6993 bool wxRichTextParagraphLayoutBox::InsertImageWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxRichTextImageBlock
& imageBlock
,
6994 wxRichTextCtrl
* ctrl
, int flags
,
6995 const wxRichTextAttr
& textAttr
)
6997 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6999 wxRichTextAttr
* p
= NULL
;
7000 wxRichTextAttr paraAttr
;
7001 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7003 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
7004 if (!paraAttr
.IsDefault())
7008 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7010 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7012 newPara
->SetAttributes(*p
);
7014 wxRichTextImage
* imageObject
= new wxRichTextImage(imageBlock
, newPara
);
7015 newPara
->AppendChild(imageObject
);
7016 imageObject
->SetAttributes(textAttr
);
7017 action
->GetNewParagraphs().AppendChild(newPara
);
7018 action
->GetNewParagraphs().UpdateRanges();
7020 action
->GetNewParagraphs().SetPartialParagraph(true);
7022 action
->SetPosition(pos
);
7024 // Set the range we'll need to delete in Undo
7025 action
->SetRange(wxRichTextRange(pos
, pos
));
7027 buffer
->SubmitAction(action
);
7032 // Insert an object with no change of it
7033 wxRichTextObject
* wxRichTextBuffer::InsertObjectWithUndo(long pos
, wxRichTextObject
*object
, wxRichTextCtrl
* ctrl
, int flags
)
7035 return ctrl
->GetFocusObject()->InsertObjectWithUndo(this, pos
, object
, ctrl
, flags
);
7038 // Insert an object with no change of it
7039 wxRichTextObject
* wxRichTextParagraphLayoutBox::InsertObjectWithUndo(wxRichTextBuffer
* buffer
, long pos
, wxRichTextObject
*object
, wxRichTextCtrl
* ctrl
, int flags
)
7041 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Object"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7043 wxRichTextAttr
* p
= NULL
;
7044 wxRichTextAttr paraAttr
;
7045 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7047 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
7048 if (!paraAttr
.IsDefault())
7052 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7054 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7056 newPara
->SetAttributes(*p
);
7058 newPara
->AppendChild(object
);
7059 action
->GetNewParagraphs().AppendChild(newPara
);
7060 action
->GetNewParagraphs().UpdateRanges();
7062 action
->GetNewParagraphs().SetPartialParagraph(true);
7064 action
->SetPosition(pos
);
7066 // Set the range we'll need to delete in Undo
7067 action
->SetRange(wxRichTextRange(pos
, pos
));
7069 buffer
->SubmitAction(action
);
7071 wxRichTextObject
* obj
= GetLeafObjectAtPosition(pos
);
7075 /// Get the style that is appropriate for a new paragraph at this position.
7076 /// If the previous paragraph has a paragraph style name, look up the next-paragraph
7078 wxRichTextAttr
wxRichTextParagraphLayoutBox::GetStyleForNewParagraph(wxRichTextBuffer
* buffer
, long pos
, bool caretPosition
, bool lookUpNewParaStyle
) const
7080 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
, caretPosition
);
7083 wxRichTextAttr attr
;
7084 bool foundAttributes
= false;
7086 // Look for a matching paragraph style
7087 if (lookUpNewParaStyle
&& !para
->GetAttributes().GetParagraphStyleName().IsEmpty() && buffer
->GetStyleSheet())
7089 wxRichTextParagraphStyleDefinition
* paraDef
= buffer
->GetStyleSheet()->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
7092 // If we're not at the end of the paragraph, then we apply THIS style, and not the designated next style.
7093 if (para
->GetRange().GetEnd() == pos
&& !paraDef
->GetNextStyle().IsEmpty())
7095 wxRichTextParagraphStyleDefinition
* nextParaDef
= buffer
->GetStyleSheet()->FindParagraphStyle(paraDef
->GetNextStyle());
7098 foundAttributes
= true;
7099 attr
= nextParaDef
->GetStyleMergedWithBase(buffer
->GetStyleSheet());
7103 // If we didn't find the 'next style', use this style instead.
7104 if (!foundAttributes
)
7106 foundAttributes
= true;
7107 attr
= paraDef
->GetStyleMergedWithBase(buffer
->GetStyleSheet());
7112 // Also apply list style if present
7113 if (lookUpNewParaStyle
&& !para
->GetAttributes().GetListStyleName().IsEmpty() && buffer
->GetStyleSheet())
7115 wxRichTextListStyleDefinition
* listDef
= buffer
->GetStyleSheet()->FindListStyle(para
->GetAttributes().GetListStyleName());
7118 int thisIndent
= para
->GetAttributes().GetLeftIndent();
7119 int thisLevel
= para
->GetAttributes().HasOutlineLevel() ? para
->GetAttributes().GetOutlineLevel() : listDef
->FindLevelForIndent(thisIndent
);
7121 // Apply the overall list style, and item style for this level
7122 wxRichTextAttr
listStyle(listDef
->GetCombinedStyleForLevel(thisLevel
, buffer
->GetStyleSheet()));
7123 wxRichTextApplyStyle(attr
, listStyle
);
7124 attr
.SetOutlineLevel(thisLevel
);
7125 if (para
->GetAttributes().HasBulletNumber())
7126 attr
.SetBulletNumber(para
->GetAttributes().GetBulletNumber());
7130 if (!foundAttributes
)
7132 attr
= para
->GetAttributes();
7133 int flags
= attr
.GetFlags();
7135 // Eliminate character styles
7136 flags
&= ( (~ wxTEXT_ATTR_FONT
) |
7137 (~ wxTEXT_ATTR_TEXT_COLOUR
) |
7138 (~ wxTEXT_ATTR_BACKGROUND_COLOUR
) );
7139 attr
.SetFlags(flags
);
7145 return wxRichTextAttr();
7148 /// Submit command to delete this range
7149 bool wxRichTextBuffer::DeleteRangeWithUndo(const wxRichTextRange
& range
, wxRichTextCtrl
* ctrl
)
7151 return ctrl
->GetFocusObject()->DeleteRangeWithUndo(range
, ctrl
, this);
7154 /// Submit command to delete this range
7155 bool wxRichTextParagraphLayoutBox::DeleteRangeWithUndo(const wxRichTextRange
& range
, wxRichTextCtrl
* ctrl
, wxRichTextBuffer
* buffer
)
7157 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Delete"), wxRICHTEXT_DELETE
, buffer
, this, ctrl
);
7159 action
->SetPosition(ctrl
->GetCaretPosition());
7161 // Set the range to delete
7162 action
->SetRange(range
);
7164 // Copy the fragment that we'll need to restore in Undo
7165 CopyFragment(range
, action
->GetOldParagraphs());
7167 // See if we're deleting a paragraph marker, in which case we need to
7168 // make a note not to copy the attributes from the 2nd paragraph to the 1st.
7169 if (range
.GetStart() == range
.GetEnd())
7171 wxRichTextParagraph
* para
= GetParagraphAtPosition(range
.GetStart());
7172 if (para
&& para
->GetRange().GetEnd() == range
.GetEnd())
7174 wxRichTextParagraph
* nextPara
= GetParagraphAtPosition(range
.GetStart()+1);
7175 if (nextPara
&& nextPara
!= para
)
7177 action
->GetOldParagraphs().GetChildren().GetFirst()->GetData()->SetAttributes(nextPara
->GetAttributes());
7178 action
->GetOldParagraphs().GetAttributes().SetFlags(action
->GetOldParagraphs().GetAttributes().GetFlags() | wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
);
7183 buffer
->SubmitAction(action
);
7188 /// Collapse undo/redo commands
7189 bool wxRichTextBuffer::BeginBatchUndo(const wxString
& cmdName
)
7191 if (m_batchedCommandDepth
== 0)
7193 wxASSERT(m_batchedCommand
== NULL
);
7194 if (m_batchedCommand
)
7196 GetCommandProcessor()->Store(m_batchedCommand
);
7198 m_batchedCommand
= new wxRichTextCommand(cmdName
);
7201 m_batchedCommandDepth
++;
7206 /// Collapse undo/redo commands
7207 bool wxRichTextBuffer::EndBatchUndo()
7209 m_batchedCommandDepth
--;
7211 wxASSERT(m_batchedCommandDepth
>= 0);
7212 wxASSERT(m_batchedCommand
!= NULL
);
7214 if (m_batchedCommandDepth
== 0)
7216 GetCommandProcessor()->Store(m_batchedCommand
);
7217 m_batchedCommand
= NULL
;
7223 /// Submit immediately, or delay according to whether collapsing is on
7224 bool wxRichTextBuffer::SubmitAction(wxRichTextAction
* action
)
7226 if (action
&& !action
->GetNewParagraphs().IsEmpty())
7227 PrepareContent(action
->GetNewParagraphs());
7229 if (BatchingUndo() && m_batchedCommand
&& !SuppressingUndo())
7231 wxRichTextCommand
* cmd
= new wxRichTextCommand(action
->GetName());
7232 cmd
->AddAction(action
);
7234 cmd
->GetActions().Clear();
7237 m_batchedCommand
->AddAction(action
);
7241 wxRichTextCommand
* cmd
= new wxRichTextCommand(action
->GetName());
7242 cmd
->AddAction(action
);
7244 // Only store it if we're not suppressing undo.
7245 return GetCommandProcessor()->Submit(cmd
, !SuppressingUndo());
7251 /// Begin suppressing undo/redo commands.
7252 bool wxRichTextBuffer::BeginSuppressUndo()
7259 /// End suppressing undo/redo commands.
7260 bool wxRichTextBuffer::EndSuppressUndo()
7267 /// Begin using a style
7268 bool wxRichTextBuffer::BeginStyle(const wxRichTextAttr
& style
)
7270 wxRichTextAttr
newStyle(GetDefaultStyle());
7272 // Save the old default style
7273 m_attributeStack
.Append((wxObject
*) new wxRichTextAttr(GetDefaultStyle()));
7275 wxRichTextApplyStyle(newStyle
, style
);
7276 newStyle
.SetFlags(style
.GetFlags()|newStyle
.GetFlags());
7278 SetDefaultStyle(newStyle
);
7284 bool wxRichTextBuffer::EndStyle()
7286 if (!m_attributeStack
.GetFirst())
7288 wxLogDebug(_("Too many EndStyle calls!"));
7292 wxList::compatibility_iterator node
= m_attributeStack
.GetLast();
7293 wxRichTextAttr
* attr
= (wxRichTextAttr
*)node
->GetData();
7294 m_attributeStack
.Erase(node
);
7296 SetDefaultStyle(*attr
);
7303 bool wxRichTextBuffer::EndAllStyles()
7305 while (m_attributeStack
.GetCount() != 0)
7310 /// Clear the style stack
7311 void wxRichTextBuffer::ClearStyleStack()
7313 for (wxList::compatibility_iterator node
= m_attributeStack
.GetFirst(); node
; node
= node
->GetNext())
7314 delete (wxRichTextAttr
*) node
->GetData();
7315 m_attributeStack
.Clear();
7318 /// Begin using bold
7319 bool wxRichTextBuffer::BeginBold()
7321 wxRichTextAttr attr
;
7322 attr
.SetFontWeight(wxFONTWEIGHT_BOLD
);
7324 return BeginStyle(attr
);
7327 /// Begin using italic
7328 bool wxRichTextBuffer::BeginItalic()
7330 wxRichTextAttr attr
;
7331 attr
.SetFontStyle(wxFONTSTYLE_ITALIC
);
7333 return BeginStyle(attr
);
7336 /// Begin using underline
7337 bool wxRichTextBuffer::BeginUnderline()
7339 wxRichTextAttr attr
;
7340 attr
.SetFontUnderlined(true);
7342 return BeginStyle(attr
);
7345 /// Begin using point size
7346 bool wxRichTextBuffer::BeginFontSize(int pointSize
)
7348 wxRichTextAttr attr
;
7349 attr
.SetFontSize(pointSize
);
7351 return BeginStyle(attr
);
7354 /// Begin using this font
7355 bool wxRichTextBuffer::BeginFont(const wxFont
& font
)
7357 wxRichTextAttr attr
;
7360 return BeginStyle(attr
);
7363 /// Begin using this colour
7364 bool wxRichTextBuffer::BeginTextColour(const wxColour
& colour
)
7366 wxRichTextAttr attr
;
7367 attr
.SetFlags(wxTEXT_ATTR_TEXT_COLOUR
);
7368 attr
.SetTextColour(colour
);
7370 return BeginStyle(attr
);
7373 /// Begin using alignment
7374 bool wxRichTextBuffer::BeginAlignment(wxTextAttrAlignment alignment
)
7376 wxRichTextAttr attr
;
7377 attr
.SetFlags(wxTEXT_ATTR_ALIGNMENT
);
7378 attr
.SetAlignment(alignment
);
7380 return BeginStyle(attr
);
7383 /// Begin left indent
7384 bool wxRichTextBuffer::BeginLeftIndent(int leftIndent
, int leftSubIndent
)
7386 wxRichTextAttr attr
;
7387 attr
.SetFlags(wxTEXT_ATTR_LEFT_INDENT
);
7388 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7390 return BeginStyle(attr
);
7393 /// Begin right indent
7394 bool wxRichTextBuffer::BeginRightIndent(int rightIndent
)
7396 wxRichTextAttr attr
;
7397 attr
.SetFlags(wxTEXT_ATTR_RIGHT_INDENT
);
7398 attr
.SetRightIndent(rightIndent
);
7400 return BeginStyle(attr
);
7403 /// Begin paragraph spacing
7404 bool wxRichTextBuffer::BeginParagraphSpacing(int before
, int after
)
7408 flags
|= wxTEXT_ATTR_PARA_SPACING_BEFORE
;
7410 flags
|= wxTEXT_ATTR_PARA_SPACING_AFTER
;
7412 wxRichTextAttr attr
;
7413 attr
.SetFlags(flags
);
7414 attr
.SetParagraphSpacingBefore(before
);
7415 attr
.SetParagraphSpacingAfter(after
);
7417 return BeginStyle(attr
);
7420 /// Begin line spacing
7421 bool wxRichTextBuffer::BeginLineSpacing(int lineSpacing
)
7423 wxRichTextAttr attr
;
7424 attr
.SetFlags(wxTEXT_ATTR_LINE_SPACING
);
7425 attr
.SetLineSpacing(lineSpacing
);
7427 return BeginStyle(attr
);
7430 /// Begin numbered bullet
7431 bool wxRichTextBuffer::BeginNumberedBullet(int bulletNumber
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7433 wxRichTextAttr attr
;
7434 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7435 attr
.SetBulletStyle(bulletStyle
);
7436 attr
.SetBulletNumber(bulletNumber
);
7437 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7439 return BeginStyle(attr
);
7442 /// Begin symbol bullet
7443 bool wxRichTextBuffer::BeginSymbolBullet(const wxString
& symbol
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7445 wxRichTextAttr attr
;
7446 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7447 attr
.SetBulletStyle(bulletStyle
);
7448 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7449 attr
.SetBulletText(symbol
);
7451 return BeginStyle(attr
);
7454 /// Begin standard bullet
7455 bool wxRichTextBuffer::BeginStandardBullet(const wxString
& bulletName
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7457 wxRichTextAttr attr
;
7458 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7459 attr
.SetBulletStyle(bulletStyle
);
7460 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7461 attr
.SetBulletName(bulletName
);
7463 return BeginStyle(attr
);
7466 /// Begin named character style
7467 bool wxRichTextBuffer::BeginCharacterStyle(const wxString
& characterStyle
)
7469 if (GetStyleSheet())
7471 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->FindCharacterStyle(characterStyle
);
7474 wxRichTextAttr attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7475 return BeginStyle(attr
);
7481 /// Begin named paragraph style
7482 bool wxRichTextBuffer::BeginParagraphStyle(const wxString
& paragraphStyle
)
7484 if (GetStyleSheet())
7486 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(paragraphStyle
);
7489 wxRichTextAttr attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7490 return BeginStyle(attr
);
7496 /// Begin named list style
7497 bool wxRichTextBuffer::BeginListStyle(const wxString
& listStyle
, int level
, int number
)
7499 if (GetStyleSheet())
7501 wxRichTextListStyleDefinition
* def
= GetStyleSheet()->FindListStyle(listStyle
);
7504 wxRichTextAttr
attr(def
->GetCombinedStyleForLevel(level
));
7506 attr
.SetBulletNumber(number
);
7508 return BeginStyle(attr
);
7515 bool wxRichTextBuffer::BeginURL(const wxString
& url
, const wxString
& characterStyle
)
7517 wxRichTextAttr attr
;
7519 if (!characterStyle
.IsEmpty() && GetStyleSheet())
7521 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->FindCharacterStyle(characterStyle
);
7524 attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7529 return BeginStyle(attr
);
7532 /// Adds a handler to the end
7533 void wxRichTextBuffer::AddHandler(wxRichTextFileHandler
*handler
)
7535 sm_handlers
.Append(handler
);
7538 /// Inserts a handler at the front
7539 void wxRichTextBuffer::InsertHandler(wxRichTextFileHandler
*handler
)
7541 sm_handlers
.Insert( handler
);
7544 /// Removes a handler
7545 bool wxRichTextBuffer::RemoveHandler(const wxString
& name
)
7547 wxRichTextFileHandler
*handler
= FindHandler(name
);
7550 sm_handlers
.DeleteObject(handler
);
7558 /// Finds a handler by filename or, if supplied, type
7559 wxRichTextFileHandler
*wxRichTextBuffer::FindHandlerFilenameOrType(const wxString
& filename
,
7560 wxRichTextFileType imageType
)
7562 if (imageType
!= wxRICHTEXT_TYPE_ANY
)
7563 return FindHandler(imageType
);
7564 else if (!filename
.IsEmpty())
7566 wxString path
, file
, ext
;
7567 wxFileName::SplitPath(filename
, & path
, & file
, & ext
);
7568 return FindHandler(ext
, imageType
);
7575 /// Finds a handler by name
7576 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(const wxString
& name
)
7578 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7581 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7582 if (handler
->GetName().Lower() == name
.Lower()) return handler
;
7584 node
= node
->GetNext();
7589 /// Finds a handler by extension and type
7590 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(const wxString
& extension
, wxRichTextFileType type
)
7592 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7595 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7596 if ( handler
->GetExtension().Lower() == extension
.Lower() &&
7597 (type
== wxRICHTEXT_TYPE_ANY
|| handler
->GetType() == type
) )
7599 node
= node
->GetNext();
7604 /// Finds a handler by type
7605 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(wxRichTextFileType type
)
7607 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7610 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7611 if (handler
->GetType() == type
) return handler
;
7612 node
= node
->GetNext();
7617 void wxRichTextBuffer::InitStandardHandlers()
7619 if (!FindHandler(wxRICHTEXT_TYPE_TEXT
))
7620 AddHandler(new wxRichTextPlainTextHandler
);
7623 void wxRichTextBuffer::CleanUpHandlers()
7625 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7628 wxRichTextFileHandler
* handler
= (wxRichTextFileHandler
*)node
->GetData();
7629 wxList::compatibility_iterator next
= node
->GetNext();
7634 sm_handlers
.Clear();
7637 wxString
wxRichTextBuffer::GetExtWildcard(bool combine
, bool save
, wxArrayInt
* types
)
7644 wxList::compatibility_iterator node
= GetHandlers().GetFirst();
7648 wxRichTextFileHandler
* handler
= (wxRichTextFileHandler
*) node
->GetData();
7649 if (handler
->IsVisible() && ((save
&& handler
->CanSave()) || (!save
&& handler
->CanLoad())))
7654 wildcard
+= wxT(";");
7655 wildcard
+= wxT("*.") + handler
->GetExtension();
7660 wildcard
+= wxT("|");
7661 wildcard
+= handler
->GetName();
7662 wildcard
+= wxT(" ");
7663 wildcard
+= _("files");
7664 wildcard
+= wxT(" (*.");
7665 wildcard
+= handler
->GetExtension();
7666 wildcard
+= wxT(")|*.");
7667 wildcard
+= handler
->GetExtension();
7669 types
->Add(handler
->GetType());
7674 node
= node
->GetNext();
7678 wildcard
= wxT("(") + wildcard
+ wxT(")|") + wildcard
;
7683 bool wxRichTextBuffer::LoadFile(const wxString
& filename
, wxRichTextFileType type
)
7685 wxRichTextFileHandler
* handler
= FindHandlerFilenameOrType(filename
, type
);
7688 SetDefaultStyle(wxRichTextAttr());
7689 handler
->SetFlags(GetHandlerFlags());
7690 bool success
= handler
->LoadFile(this, filename
);
7691 Invalidate(wxRICHTEXT_ALL
);
7699 bool wxRichTextBuffer::SaveFile(const wxString
& filename
, wxRichTextFileType type
)
7701 wxRichTextFileHandler
* handler
= FindHandlerFilenameOrType(filename
, type
);
7704 handler
->SetFlags(GetHandlerFlags());
7705 return handler
->SaveFile(this, filename
);
7711 /// Load from a stream
7712 bool wxRichTextBuffer::LoadFile(wxInputStream
& stream
, wxRichTextFileType type
)
7714 wxRichTextFileHandler
* handler
= FindHandler(type
);
7717 SetDefaultStyle(wxRichTextAttr());
7718 handler
->SetFlags(GetHandlerFlags());
7719 bool success
= handler
->LoadFile(this, stream
);
7720 Invalidate(wxRICHTEXT_ALL
);
7727 /// Save to a stream
7728 bool wxRichTextBuffer::SaveFile(wxOutputStream
& stream
, wxRichTextFileType type
)
7730 wxRichTextFileHandler
* handler
= FindHandler(type
);
7733 handler
->SetFlags(GetHandlerFlags());
7734 return handler
->SaveFile(this, stream
);
7740 /// Copy the range to the clipboard
7741 bool wxRichTextBuffer::CopyToClipboard(const wxRichTextRange
& range
)
7743 bool success
= false;
7744 wxRichTextParagraphLayoutBox
* container
= this;
7745 if (GetRichTextCtrl())
7746 container
= GetRichTextCtrl()->GetFocusObject();
7748 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
7750 if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open())
7752 wxTheClipboard
->Clear();
7754 // Add composite object
7756 wxDataObjectComposite
* compositeObject
= new wxDataObjectComposite();
7759 wxString text
= container
->GetTextForRange(range
);
7762 text
= wxTextFile::Translate(text
, wxTextFileType_Dos
);
7765 compositeObject
->Add(new wxTextDataObject(text
), false /* not preferred */);
7768 // Add rich text buffer data object. This needs the XML handler to be present.
7770 if (FindHandler(wxRICHTEXT_TYPE_XML
))
7772 wxRichTextBuffer
* richTextBuf
= new wxRichTextBuffer
;
7773 container
->CopyFragment(range
, *richTextBuf
);
7775 compositeObject
->Add(new wxRichTextBufferDataObject(richTextBuf
), true /* preferred */);
7778 if (wxTheClipboard
->SetData(compositeObject
))
7781 wxTheClipboard
->Close();
7790 /// Paste the clipboard content to the buffer
7791 bool wxRichTextBuffer::PasteFromClipboard(long position
)
7793 bool success
= false;
7794 wxRichTextParagraphLayoutBox
* container
= this;
7795 if (GetRichTextCtrl())
7796 container
= GetRichTextCtrl()->GetFocusObject();
7798 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
7799 if (CanPasteFromClipboard())
7801 if (wxTheClipboard
->Open())
7803 if (wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())))
7805 wxRichTextBufferDataObject data
;
7806 wxTheClipboard
->GetData(data
);
7807 wxRichTextBuffer
* richTextBuffer
= data
.GetRichTextBuffer();
7810 container
->InsertParagraphsWithUndo(this, position
+1, *richTextBuffer
, GetRichTextCtrl(), 0);
7811 if (GetRichTextCtrl())
7812 GetRichTextCtrl()->ShowPosition(position
+ richTextBuffer
->GetOwnRange().GetEnd());
7813 delete richTextBuffer
;
7816 else if (wxTheClipboard
->IsSupported(wxDF_TEXT
)
7818 || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
)
7822 wxTextDataObject data
;
7823 wxTheClipboard
->GetData(data
);
7824 wxString
text(data
.GetText());
7827 text2
.Alloc(text
.Length()+1);
7829 for (i
= 0; i
< text
.Length(); i
++)
7831 wxChar ch
= text
[i
];
7832 if (ch
!= wxT('\r'))
7836 wxString text2
= text
;
7838 container
->InsertTextWithUndo(this, position
+1, text2
, GetRichTextCtrl(), wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
);
7840 if (GetRichTextCtrl())
7841 GetRichTextCtrl()->ShowPosition(position
+ text2
.Length());
7845 else if (wxTheClipboard
->IsSupported(wxDF_BITMAP
))
7847 wxBitmapDataObject data
;
7848 wxTheClipboard
->GetData(data
);
7849 wxBitmap
bitmap(data
.GetBitmap());
7850 wxImage
image(bitmap
.ConvertToImage());
7852 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, this, container
, GetRichTextCtrl(), false);
7854 action
->GetNewParagraphs().AddImage(image
);
7856 if (action
->GetNewParagraphs().GetChildCount() == 1)
7857 action
->GetNewParagraphs().SetPartialParagraph(true);
7859 action
->SetPosition(position
+1);
7861 // Set the range we'll need to delete in Undo
7862 action
->SetRange(wxRichTextRange(position
+1, position
+1));
7864 SubmitAction(action
);
7868 wxTheClipboard
->Close();
7872 wxUnusedVar(position
);
7877 /// Can we paste from the clipboard?
7878 bool wxRichTextBuffer::CanPasteFromClipboard() const
7880 bool canPaste
= false;
7881 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
7882 if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open())
7884 if (wxTheClipboard
->IsSupported(wxDF_TEXT
)
7886 || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
)
7888 || wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())) ||
7889 wxTheClipboard
->IsSupported(wxDF_BITMAP
))
7893 wxTheClipboard
->Close();
7899 /// Dumps contents of buffer for debugging purposes
7900 void wxRichTextBuffer::Dump()
7904 wxStringOutputStream
stream(& text
);
7905 wxTextOutputStream
textStream(stream
);
7912 /// Add an event handler
7913 bool wxRichTextBuffer::AddEventHandler(wxEvtHandler
* handler
)
7915 m_eventHandlers
.Append(handler
);
7919 /// Remove an event handler
7920 bool wxRichTextBuffer::RemoveEventHandler(wxEvtHandler
* handler
, bool deleteHandler
)
7922 wxList::compatibility_iterator node
= m_eventHandlers
.Find(handler
);
7925 m_eventHandlers
.Erase(node
);
7935 /// Clear event handlers
7936 void wxRichTextBuffer::ClearEventHandlers()
7938 m_eventHandlers
.Clear();
7941 /// Send event to event handlers. If sendToAll is true, will send to all event handlers,
7942 /// otherwise will stop at the first successful one.
7943 bool wxRichTextBuffer::SendEvent(wxEvent
& event
, bool sendToAll
)
7945 bool success
= false;
7946 for (wxList::compatibility_iterator node
= m_eventHandlers
.GetFirst(); node
; node
= node
->GetNext())
7948 wxEvtHandler
* handler
= (wxEvtHandler
*) node
->GetData();
7949 if (handler
->ProcessEvent(event
))
7959 /// Set style sheet and notify of the change
7960 bool wxRichTextBuffer::SetStyleSheetAndNotify(wxRichTextStyleSheet
* sheet
)
7962 wxRichTextStyleSheet
* oldSheet
= GetStyleSheet();
7964 wxWindowID winid
= wxID_ANY
;
7965 if (GetRichTextCtrl())
7966 winid
= GetRichTextCtrl()->GetId();
7968 wxRichTextEvent
event(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACING
, winid
);
7969 event
.SetEventObject(GetRichTextCtrl());
7970 event
.SetContainer(GetRichTextCtrl()->GetFocusObject());
7971 event
.SetOldStyleSheet(oldSheet
);
7972 event
.SetNewStyleSheet(sheet
);
7975 if (SendEvent(event
) && !event
.IsAllowed())
7977 if (sheet
!= oldSheet
)
7983 if (oldSheet
&& oldSheet
!= sheet
)
7986 SetStyleSheet(sheet
);
7988 event
.SetEventType(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACED
);
7989 event
.SetOldStyleSheet(NULL
);
7992 return SendEvent(event
);
7995 /// Set renderer, deleting old one
7996 void wxRichTextBuffer::SetRenderer(wxRichTextRenderer
* renderer
)
8000 sm_renderer
= renderer
;
8003 /// Hit-testing: returns a flag indicating hit test details, plus
8004 /// information about position
8005 int wxRichTextBuffer::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
8007 int ret
= wxRichTextParagraphLayoutBox::HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
8008 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
8014 textPosition
= m_ownRange
.GetEnd()-1;
8017 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
;
8021 bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxRichTextAttr
& bulletAttr
, const wxRect
& rect
)
8023 if (bulletAttr
.GetTextColour().IsOk())
8025 wxCheckSetPen(dc
, wxPen(bulletAttr
.GetTextColour()));
8026 wxCheckSetBrush(dc
, wxBrush(bulletAttr
.GetTextColour()));
8030 wxCheckSetPen(dc
, *wxBLACK_PEN
);
8031 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
8035 if (bulletAttr
.HasFont())
8037 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(bulletAttr
);
8040 font
= (*wxNORMAL_FONT
);
8042 wxCheckSetFont(dc
, font
);
8044 int charHeight
= dc
.GetCharHeight();
8046 int bulletWidth
= (int) (((float) charHeight
) * wxRichTextBuffer::GetBulletProportion());
8047 int bulletHeight
= bulletWidth
;
8051 // Calculate the top position of the character (as opposed to the whole line height)
8052 int y
= rect
.y
+ (rect
.height
- charHeight
);
8054 // Calculate where the bullet should be positioned
8055 y
= y
+ (charHeight
+1)/2 - (bulletHeight
+1)/2;
8057 // The margin between a bullet and text.
8058 int margin
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer::GetBulletRightMargin());
8060 if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
)
8061 x
= rect
.x
+ rect
.width
- bulletWidth
- margin
;
8062 else if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
)
8063 x
= x
+ (rect
.width
)/2 - bulletWidth
/2;
8065 if (bulletAttr
.GetBulletName() == wxT("standard/square"))
8067 dc
.DrawRectangle(x
, y
, bulletWidth
, bulletHeight
);
8069 else if (bulletAttr
.GetBulletName() == wxT("standard/diamond"))
8072 pts
[0].x
= x
; pts
[0].y
= y
+ bulletHeight
/2;
8073 pts
[1].x
= x
+ bulletWidth
/2; pts
[1].y
= y
;
8074 pts
[2].x
= x
+ bulletWidth
; pts
[2].y
= y
+ bulletHeight
/2;
8075 pts
[3].x
= x
+ bulletWidth
/2; pts
[3].y
= y
+ bulletHeight
;
8077 dc
.DrawPolygon(4, pts
);
8079 else if (bulletAttr
.GetBulletName() == wxT("standard/triangle"))
8082 pts
[0].x
= x
; pts
[0].y
= y
;
8083 pts
[1].x
= x
+ bulletWidth
; pts
[1].y
= y
+ bulletHeight
/2;
8084 pts
[2].x
= x
; pts
[2].y
= y
+ bulletHeight
;
8086 dc
.DrawPolygon(3, pts
);
8088 else if (bulletAttr
.GetBulletName() == wxT("standard/circle-outline"))
8090 wxCheckSetBrush(dc
, *wxTRANSPARENT_BRUSH
);
8091 dc
.DrawEllipse(x
, y
, bulletWidth
, bulletHeight
);
8093 else // "standard/circle", and catch-all
8095 dc
.DrawEllipse(x
, y
, bulletWidth
, bulletHeight
);
8101 bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxRichTextAttr
& attr
, const wxRect
& rect
, const wxString
& text
)
8106 if ((attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
) && !attr
.GetBulletFont().IsEmpty() && attr
.HasFont())
8108 wxRichTextAttr fontAttr
;
8109 fontAttr
.SetFontSize(attr
.GetFontSize());
8110 fontAttr
.SetFontStyle(attr
.GetFontStyle());
8111 fontAttr
.SetFontWeight(attr
.GetFontWeight());
8112 fontAttr
.SetFontUnderlined(attr
.GetFontUnderlined());
8113 fontAttr
.SetFontFaceName(attr
.GetBulletFont());
8114 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(fontAttr
);
8116 else if (attr
.HasFont())
8117 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(attr
);
8119 font
= (*wxNORMAL_FONT
);
8121 wxCheckSetFont(dc
, font
);
8123 if (attr
.GetTextColour().IsOk())
8124 dc
.SetTextForeground(attr
.GetTextColour());
8126 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
8128 int charHeight
= dc
.GetCharHeight();
8130 dc
.GetTextExtent(text
, & tw
, & th
);
8134 // Calculate the top position of the character (as opposed to the whole line height)
8135 int y
= rect
.y
+ (rect
.height
- charHeight
);
8137 // The margin between a bullet and text.
8138 int margin
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer::GetBulletRightMargin());
8140 if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
)
8141 x
= (rect
.x
+ rect
.width
) - tw
- margin
;
8142 else if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
)
8143 x
= x
+ (rect
.width
)/2 - tw
/2;
8145 dc
.DrawText(text
, x
, y
);
8153 bool wxRichTextStdRenderer::DrawBitmapBullet(wxRichTextParagraph
* WXUNUSED(paragraph
), wxDC
& WXUNUSED(dc
), const wxRichTextAttr
& WXUNUSED(attr
), const wxRect
& WXUNUSED(rect
))
8155 // Currently unimplemented. The intention is to store bitmaps by name in a media store associated
8156 // with the buffer. The store will allow retrieval from memory, disk or other means.
8160 /// Enumerate the standard bullet names currently supported
8161 bool wxRichTextStdRenderer::EnumerateStandardBulletNames(wxArrayString
& bulletNames
)
8163 bulletNames
.Add(wxTRANSLATE("standard/circle"));
8164 bulletNames
.Add(wxTRANSLATE("standard/circle-outline"));
8165 bulletNames
.Add(wxTRANSLATE("standard/square"));
8166 bulletNames
.Add(wxTRANSLATE("standard/diamond"));
8167 bulletNames
.Add(wxTRANSLATE("standard/triangle"));
8176 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBox
, wxRichTextParagraphLayoutBox
)
8178 wxRichTextBox::wxRichTextBox(wxRichTextObject
* parent
):
8179 wxRichTextParagraphLayoutBox(parent
)
8184 bool wxRichTextBox::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8189 // TODO: if the active object in the control, draw an indication.
8190 // We need to add the concept of active object, and not just focus object,
8191 // so we can apply commands (properties, delete, ...) to objects such as text boxes and images.
8192 // Ultimately we would like to be able to interactively resize an active object
8193 // using drag handles.
8194 return wxRichTextParagraphLayoutBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8198 void wxRichTextBox::Copy(const wxRichTextBox
& obj
)
8200 wxRichTextParagraphLayoutBox::Copy(obj
);
8203 // Edit properties via a GUI
8204 bool wxRichTextBox::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8206 wxRichTextObjectPropertiesDialog
boxDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Box Properties"));
8207 boxDlg
.SetAttributes(GetAttributes());
8209 if (boxDlg
.ShowModal() == wxID_OK
)
8211 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
8212 // indeterminate in the object.
8213 boxDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
8220 IMPLEMENT_DYNAMIC_CLASS(wxRichTextCell
, wxRichTextBox
)
8222 wxRichTextCell::wxRichTextCell(wxRichTextObject
* parent
):
8223 wxRichTextBox(parent
)
8228 bool wxRichTextCell::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8230 return wxRichTextBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8234 void wxRichTextCell::Copy(const wxRichTextCell
& obj
)
8236 wxRichTextBox::Copy(obj
);
8239 // Edit properties via a GUI
8240 bool wxRichTextCell::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8242 // We need to gather common attributes for all selected cells.
8244 wxRichTextTable
* table
= wxDynamicCast(GetParent(), wxRichTextTable
);
8245 bool multipleCells
= false;
8246 wxRichTextAttr attr
;
8248 if (table
&& buffer
&& buffer
->GetRichTextCtrl() && buffer
->GetRichTextCtrl()->GetSelection().IsValid() &&
8249 buffer
->GetRichTextCtrl()->GetSelection().GetContainer() == GetParent())
8251 wxRichTextAttr clashingAttr
, absentAttr
;
8252 const wxRichTextSelection
& sel
= buffer
->GetRichTextCtrl()->GetSelection();
8254 int selectedCellCount
= 0;
8255 for (i
= 0; i
< sel
.GetCount(); i
++)
8257 const wxRichTextRange
& range
= sel
[i
];
8258 wxRichTextCell
* cell
= table
->GetCell(range
.GetStart());
8261 wxRichTextAttr cellStyle
= cell
->GetAttributes();
8263 CollectStyle(attr
, cellStyle
, clashingAttr
, absentAttr
);
8265 selectedCellCount
++;
8268 multipleCells
= selectedCellCount
> 1;
8272 attr
= GetAttributes();
8277 caption
= _("Multiple Cell Properties");
8279 caption
= _("Cell Properties");
8281 wxRichTextObjectPropertiesDialog
cellDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, caption
);
8282 cellDlg
.SetAttributes(attr
);
8284 wxRichTextSizePage
* sizePage
= wxDynamicCast(cellDlg
.FindPage(CLASSINFO(wxRichTextSizePage
)), wxRichTextSizePage
);
8287 // We don't want position and floating controls for a cell.
8288 sizePage
->ShowPositionControls(false);
8289 sizePage
->ShowFloatingControls(false);
8292 if (cellDlg
.ShowModal() == wxID_OK
)
8296 const wxRichTextSelection
& sel
= buffer
->GetRichTextCtrl()->GetSelection();
8297 // Apply the style; we interpret indeterminate attributes as 'don't touch this attribute'
8298 // since it may represent clashing attributes across multiple objects.
8299 table
->SetCellStyle(sel
, attr
);
8302 // For a single object, indeterminate attributes set by the user should be reflected in the
8303 // actual object style, so pass the wxRICHTEXT_SETSTYLE_RESET flag to assign
8304 // the style directly instead of applying (which ignores indeterminate attributes,
8305 // leaving them as they were).
8306 cellDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
8313 WX_DEFINE_OBJARRAY(wxRichTextObjectPtrArrayArray
)
8315 IMPLEMENT_DYNAMIC_CLASS(wxRichTextTable
, wxRichTextBox
)
8317 wxRichTextTable::wxRichTextTable(wxRichTextObject
* parent
): wxRichTextBox(parent
)
8323 // Draws the object.
8324 bool wxRichTextTable::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8326 return wxRichTextBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8329 WX_DECLARE_OBJARRAY(wxRect
, wxRichTextRectArray
);
8330 WX_DEFINE_OBJARRAY(wxRichTextRectArray
);
8332 // Lays the object out. rect is the space available for layout. Often it will
8333 // be the specified overall space for this object, if trying to constrain
8334 // layout to a particular size, or it could be the total space available in the
8335 // parent. rect is the overall size, so we must subtract margins and padding.
8336 // to get the actual available space.
8337 bool wxRichTextTable::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& WXUNUSED(parentRect
), int style
)
8339 SetPosition(rect
.GetPosition());
8341 // TODO: the meaty bit. Calculate sizes of all cells and rows. Try to use
8342 // minimum size if within alloted size, then divide up remaining size
8343 // between rows/cols.
8346 wxRichTextBuffer
* buffer
= GetBuffer();
8347 if (buffer
) scale
= buffer
->GetScale();
8349 wxRect availableSpace
= GetAvailableContentArea(dc
, context
, rect
);
8350 wxTextAttrDimensionConverter
converter(dc
, scale
, availableSpace
.GetSize());
8352 wxRichTextAttr
attr(GetAttributes());
8353 context
.ApplyVirtualAttributes(attr
, this);
8355 // If we have no fixed table size, and assuming we're not pushed for
8356 // space, then we don't have to try to stretch the table to fit the contents.
8357 bool stretchToFitTableWidth
= false;
8359 int tableWidth
= rect
.width
;
8360 if (attr
.GetTextBoxAttr().GetWidth().IsValid())
8362 tableWidth
= converter
.GetPixels(attr
.GetTextBoxAttr().GetWidth());
8364 // Fixed table width, so we do want to stretch columns out if necessary.
8365 stretchToFitTableWidth
= true;
8367 // Shouldn't be able to exceed the size passed to this function
8368 tableWidth
= wxMin(rect
.width
, tableWidth
);
8371 // Get internal padding
8372 int paddingLeft
= 0, paddingTop
= 0;
8373 if (attr
.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
8374 paddingLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetLeft());
8375 if (attr
.GetTextBoxAttr().GetPadding().GetTop().IsValid())
8376 paddingTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetTop());
8378 // Assume that left and top padding are also used for inter-cell padding.
8379 int paddingX
= paddingLeft
;
8380 int paddingY
= paddingTop
;
8382 int totalLeftMargin
= 0, totalRightMargin
= 0, totalTopMargin
= 0, totalBottomMargin
= 0;
8383 GetTotalMargin(dc
, buffer
, attr
, totalLeftMargin
, totalRightMargin
, totalTopMargin
, totalBottomMargin
);
8385 // Internal table width - the area for content
8386 int internalTableWidth
= tableWidth
- totalLeftMargin
- totalRightMargin
;
8388 int rowCount
= m_cells
.GetCount();
8389 if (m_colCount
== 0 || rowCount
== 0)
8391 wxRect
overallRect(rect
.x
, rect
.y
, totalLeftMargin
+ totalRightMargin
, totalTopMargin
+ totalBottomMargin
);
8392 SetCachedSize(overallRect
.GetSize());
8394 // Zero content size
8395 SetMinSize(overallRect
.GetSize());
8396 SetMaxSize(GetMinSize());
8400 // The final calculated widths
8401 wxArrayInt colWidths
;
8402 colWidths
.Add(0, m_colCount
);
8404 wxArrayInt absoluteColWidths
;
8405 absoluteColWidths
.Add(0, m_colCount
);
8406 // wxArrayInt absoluteColWidthsSpanning(m_colCount);
8407 wxArrayInt percentageColWidths
;
8408 percentageColWidths
.Add(0, m_colCount
);
8409 // wxArrayInt percentageColWidthsSpanning(m_colCount);
8410 // These are only relevant when the first column contains spanning information.
8411 // wxArrayInt columnSpans(m_colCount); // Each contains 1 for non-spanning cell, > 1 for spanning cell.
8412 wxArrayInt maxColWidths
;
8413 maxColWidths
.Add(0, m_colCount
);
8414 wxArrayInt minColWidths
;
8415 minColWidths
.Add(0, m_colCount
);
8417 wxSize
tableSize(tableWidth
, 0);
8421 for (i
= 0; i
< m_colCount
; i
++)
8423 absoluteColWidths
[i
] = 0;
8424 // absoluteColWidthsSpanning[i] = 0;
8425 percentageColWidths
[i
] = -1;
8426 // percentageColWidthsSpanning[i] = -1;
8428 maxColWidths
[i
] = 0;
8429 minColWidths
[i
] = 0;
8430 // columnSpans[i] = 1;
8433 // (0) Determine which cells are visible according to spans
8435 // __________________
8440 // |------------------|
8441 // |__________________| 4
8443 // To calculate cell visibility:
8444 // First find all spanning cells. Build an array of span records with start x, y and end x, y.
8445 // Then for each cell, test whether we're within one of those cells, and unless we're at the start of
8446 // that cell, hide the cell.
8448 // We can also use this array to match the size of spanning cells to the grid. Or just do
8449 // this when we iterate through all cells.
8451 // 0.1: add spanning cells to an array
8452 wxRichTextRectArray rectArray
;
8453 for (j
= 0; j
< m_rowCount
; j
++)
8455 for (i
= 0; i
< m_colCount
; i
++)
8457 wxRichTextBox
* cell
= GetCell(j
, i
);
8458 int colSpan
= 1, rowSpan
= 1;
8459 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8460 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8461 if (cell
->GetProperties().HasProperty(wxT("rowspan")))
8462 rowSpan
= cell
->GetProperties().GetPropertyLong(wxT("rowspan"));
8463 if (colSpan
> 1 || rowSpan
> 1)
8465 rectArray
.Add(wxRect(i
, j
, colSpan
, rowSpan
));
8469 // 0.2: find which cells are subsumed by a spanning cell
8470 for (j
= 0; j
< m_rowCount
; j
++)
8472 for (i
= 0; i
< m_colCount
; i
++)
8474 wxRichTextBox
* cell
= GetCell(j
, i
);
8475 if (rectArray
.GetCount() == 0)
8481 int colSpan
= 1, rowSpan
= 1;
8482 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8483 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8484 if (cell
->GetProperties().HasProperty(wxT("rowspan")))
8485 rowSpan
= cell
->GetProperties().GetPropertyLong(wxT("rowspan"));
8486 if (colSpan
> 1 || rowSpan
> 1)
8488 // Assume all spanning cells are shown
8494 for (k
= 0; k
< (int) rectArray
.GetCount(); k
++)
8496 if (rectArray
[k
].Contains(wxPoint(i
, j
)))
8508 // TODO: find the first spanned cell in each row that spans the most columns and doesn't
8509 // overlap with a spanned cell starting at a previous column position.
8510 // This means we need to keep an array of rects so we can check. However
8511 // it does also mean that some spans simply may not be taken into account
8512 // where there are different spans happening on different rows. In these cases,
8513 // they will simply be as wide as their constituent columns.
8515 // (1) Do an initial layout for all cells to get minimum and maximum size, and get
8516 // the absolute or percentage width of each column.
8518 for (j
= 0; j
< m_rowCount
; j
++)
8520 // First get the overall margins so we can calculate percentage widths based on
8521 // the available content space for all cells on the row
8523 int overallRowContentMargin
= 0;
8524 int visibleCellCount
= 0;
8526 for (i
= 0; i
< m_colCount
; i
++)
8528 wxRichTextBox
* cell
= GetCell(j
, i
);
8529 if (cell
->IsShown())
8531 int cellTotalLeftMargin
= 0, cellTotalRightMargin
= 0, cellTotalTopMargin
= 0, cellTotalBottomMargin
= 0;
8532 GetTotalMargin(dc
, buffer
, cell
->GetAttributes(), cellTotalLeftMargin
, cellTotalRightMargin
, cellTotalTopMargin
, cellTotalBottomMargin
);
8534 overallRowContentMargin
+= (cellTotalLeftMargin
+ cellTotalRightMargin
);
8535 visibleCellCount
++;
8539 // Add in inter-cell padding
8540 overallRowContentMargin
+= ((visibleCellCount
-1) * paddingX
);
8542 int rowContentWidth
= internalTableWidth
- overallRowContentMargin
;
8543 wxSize
rowTableSize(rowContentWidth
, 0);
8544 wxTextAttrDimensionConverter
converter(dc
, scale
, rowTableSize
);
8546 for (i
= 0; i
< m_colCount
; i
++)
8548 wxRichTextBox
* cell
= GetCell(j
, i
);
8549 if (cell
->IsShown())
8552 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8553 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8555 // Lay out cell to find min/max widths
8556 cell
->Invalidate(wxRICHTEXT_ALL
);
8557 cell
->Layout(dc
, context
, availableSpace
, availableSpace
, style
);
8561 int absoluteCellWidth
= -1;
8562 int percentageCellWidth
= -1;
8564 // I think we need to calculate percentages from the internal table size,
8565 // minus the padding between cells which we'll need to calculate from the
8566 // (number of VISIBLE cells - 1)*paddingX. Then percentages that add up to 100%
8567 // will add up to 100%. In CSS, the width specifies the cell's content rect width,
8568 // so if we want to conform to that we'll need to add in the overall cell margins.
8569 // However, this will make it difficult to specify percentages that add up to
8570 // 100% and still fit within the table width.
8571 // Let's say two cells have 50% width. They have 10 pixels of overall margin each.
8572 // The table content rect is 500 pixels and the inter-cell padding is 20 pixels.
8573 // If we're using internal content size for the width, we would calculate the
8574 // the overall cell width for n cells as:
8575 // (500 - 20*(n-1) - overallCellMargin1 - overallCellMargin2 - ...) * percentage / 100
8576 // + thisOverallCellMargin
8577 // = 500 - 20 - 10 - 10) * 0.5 + 10 = 240 pixels overall cell width.
8578 // Adding this back, we get 240 + 240 + 20 = 500 pixels.
8580 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
8582 int w
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetWidth());
8583 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
8585 percentageCellWidth
= w
;
8589 absoluteCellWidth
= w
;
8591 // Override absolute width with minimum width if necessary
8592 if (cell
->GetMinSize().x
> 0 && absoluteCellWidth
!=1 && cell
->GetMinSize().x
> absoluteCellWidth
)
8593 absoluteCellWidth
= cell
->GetMinSize().x
;
8596 if (absoluteCellWidth
!= -1)
8598 if (absoluteCellWidth
> absoluteColWidths
[i
])
8599 absoluteColWidths
[i
] = absoluteCellWidth
;
8602 if (percentageCellWidth
!= -1)
8604 if (percentageCellWidth
> percentageColWidths
[i
])
8605 percentageColWidths
[i
] = percentageCellWidth
;
8608 if (colSpan
== 1 && cell
->GetMinSize().x
&& cell
->GetMinSize().x
> minColWidths
[i
])
8609 minColWidths
[i
] = cell
->GetMinSize().x
;
8610 if (colSpan
== 1 && cell
->GetMaxSize().x
&& cell
->GetMaxSize().x
> maxColWidths
[i
])
8611 maxColWidths
[i
] = cell
->GetMaxSize().x
;
8617 // (2) Allocate initial column widths from minimum widths, absolute values and proportions
8618 // TODO: simply merge this into (1).
8619 for (i
= 0; i
< m_colCount
; i
++)
8621 if (absoluteColWidths
[i
] > 0)
8623 colWidths
[i
] = absoluteColWidths
[i
];
8625 else if (percentageColWidths
[i
] > 0)
8627 colWidths
[i
] = percentageColWidths
[i
];
8629 // This is rubbish - we calculated the absolute widths from percentages, so
8630 // we can't do it again here.
8631 //colWidths[i] = (int) (double(percentageColWidths[i]) * double(tableWidth) / 100.0 + 0.5);
8635 // (3) Process absolute or proportional widths of spanning columns,
8636 // now that we know what our fixed column widths are going to be.
8637 // Spanned cells will try to adjust columns so the span will fit.
8638 // Even existing fixed column widths can be expanded if necessary.
8639 // Actually, currently fixed columns widths aren't adjusted; instead,
8640 // the algorithm favours earlier rows and adjusts unspecified column widths
8641 // the first time only. After that, we can't know whether the column has been
8642 // specified explicitly or not. (We could make a note if necessary.)
8643 for (j
= 0; j
< m_rowCount
; j
++)
8645 // First get the overall margins so we can calculate percentage widths based on
8646 // the available content space for all cells on the row
8648 int overallRowContentMargin
= 0;
8649 int visibleCellCount
= 0;
8651 for (i
= 0; i
< m_colCount
; i
++)
8653 wxRichTextBox
* cell
= GetCell(j
, i
);
8654 if (cell
->IsShown())
8656 int cellTotalLeftMargin
= 0, cellTotalRightMargin
= 0, cellTotalTopMargin
= 0, cellTotalBottomMargin
= 0;
8657 GetTotalMargin(dc
, buffer
, cell
->GetAttributes(), cellTotalLeftMargin
, cellTotalRightMargin
, cellTotalTopMargin
, cellTotalBottomMargin
);
8659 overallRowContentMargin
+= (cellTotalLeftMargin
+ cellTotalRightMargin
);
8660 visibleCellCount
++;
8664 // Add in inter-cell padding
8665 overallRowContentMargin
+= ((visibleCellCount
-1) * paddingX
);
8667 int rowContentWidth
= internalTableWidth
- overallRowContentMargin
;
8668 wxSize
rowTableSize(rowContentWidth
, 0);
8669 wxTextAttrDimensionConverter
converter(dc
, scale
, rowTableSize
);
8671 for (i
= 0; i
< m_colCount
; i
++)
8673 wxRichTextBox
* cell
= GetCell(j
, i
);
8674 if (cell
->IsShown())
8677 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8678 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8682 int spans
= wxMin(colSpan
, m_colCount
- i
);
8686 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
8688 cellWidth
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetWidth());
8689 // Override absolute width with minimum width if necessary
8690 if (cell
->GetMinSize().x
> 0 && cellWidth
!=1 && cell
->GetMinSize().x
> cellWidth
)
8691 cellWidth
= cell
->GetMinSize().x
;
8695 // Do we want to do this? It's the only chance we get to
8696 // use the cell's min/max sizes, so we need to work out
8697 // how we're going to balance the unspecified spanning cell
8698 // width with the possibility more-constrained constituent cell widths.
8699 // Say there's a tiny bitmap giving it a max width of 10 pixels. We
8700 // don't want to constraint all the spanned columns to fit into this cell.
8701 // OK, let's say that if any of the constituent columns don't fit,
8702 // then we simply stop constraining the columns; instead, we'll just fit the spanning
8703 // cells to the columns later.
8704 cellWidth
= cell
->GetMinSize().x
;
8705 if (cell
->GetMaxSize().x
> cellWidth
)
8706 cellWidth
= cell
->GetMaxSize().x
;
8709 // Subtract the padding between cells
8710 int spanningWidth
= cellWidth
;
8711 spanningWidth
-= paddingX
* (spans
-1);
8713 if (spanningWidth
> 0)
8715 // Now share the spanning width between columns within that span
8716 // TODO: take into account min widths of columns within the span
8717 int spanningWidthLeft
= spanningWidth
;
8718 int stretchColCount
= 0;
8719 for (k
= i
; k
< (i
+spans
); k
++)
8721 if (colWidths
[k
] > 0) // absolute or proportional width has been specified
8722 spanningWidthLeft
-= colWidths
[k
];
8726 // Now divide what's left between the remaining columns
8728 if (stretchColCount
> 0)
8729 colShare
= spanningWidthLeft
/ stretchColCount
;
8730 int colShareRemainder
= spanningWidthLeft
- (colShare
* stretchColCount
);
8732 // If fixed-width columns are currently too big, then we'll later
8733 // stretch the spanned cell to fit.
8735 if (spanningWidthLeft
> 0)
8737 for (k
= i
; k
< (i
+spans
); k
++)
8739 if (colWidths
[k
] <= 0) // absolute or proportional width has not been specified
8741 int newWidth
= colShare
;
8742 if (k
== (i
+spans
-1))
8743 newWidth
+= colShareRemainder
; // ensure all pixels are filled
8744 colWidths
[k
] = newWidth
;
8755 // (4) Next, share any remaining space out between columns that have not yet been calculated.
8756 // TODO: take into account min widths of columns within the span
8757 int tableWidthMinusPadding
= internalTableWidth
- (m_colCount
-1)*paddingX
;
8758 int widthLeft
= tableWidthMinusPadding
;
8759 int stretchColCount
= 0;
8760 for (i
= 0; i
< m_colCount
; i
++)
8762 // TODO: we need to take into account min widths.
8763 // Subtract min width from width left, then
8764 // add the colShare to the min width
8765 if (colWidths
[i
] > 0) // absolute or proportional width has been specified
8766 widthLeft
-= colWidths
[i
];
8769 if (minColWidths
[i
] > 0)
8770 widthLeft
-= minColWidths
[i
];
8776 // Now divide what's left between the remaining columns
8778 if (stretchColCount
> 0)
8779 colShare
= widthLeft
/ stretchColCount
;
8780 int colShareRemainder
= widthLeft
- (colShare
* stretchColCount
);
8782 // Check we don't have enough space, in which case shrink all columns, overriding
8783 // any absolute/proportional widths
8784 // TODO: actually we would like to divide up the shrinkage according to size.
8785 // How do we calculate the proportions that will achieve this?
8786 // Could first choose an arbitrary value for stretching cells, and then calculate
8787 // factors to multiply each width by.
8788 // TODO: want to record this fact and pass to an iteration that tries e.g. min widths
8789 if (widthLeft
< 0 || (stretchToFitTableWidth
&& (stretchColCount
== 0)))
8791 colShare
= tableWidthMinusPadding
/ m_colCount
;
8792 colShareRemainder
= tableWidthMinusPadding
- (colShare
* m_colCount
);
8793 for (i
= 0; i
< m_colCount
; i
++)
8796 minColWidths
[i
] = 0;
8800 // We have to adjust the columns if either we need to shrink the
8801 // table to fit the parent/table width, or we explicitly set the
8802 // table width and need to stretch out the table.
8803 if (widthLeft
< 0 || stretchToFitTableWidth
)
8805 for (i
= 0; i
< m_colCount
; i
++)
8807 if (colWidths
[i
] <= 0) // absolute or proportional width has not been specified
8809 if (minColWidths
[i
] > 0)
8810 colWidths
[i
] = minColWidths
[i
] + colShare
;
8812 colWidths
[i
] = colShare
;
8813 if (i
== (m_colCount
-1))
8814 colWidths
[i
] += colShareRemainder
; // ensure all pixels are filled
8819 // TODO: if spanned cells have no specified or max width, make them the
8820 // as big as the columns they span. Do this for all spanned cells in all
8821 // rows, of course. Size any spanned cells left over at the end - even if they
8822 // have width > 0, make sure they're limited to the appropriate column edge.
8826 Sort out confusion between content width
8827 and overall width later. For now, assume we specify overall width.
8829 So, now we've laid out the table to fit into the given space
8830 and have used specified widths and minimum widths.
8832 Now we need to consider how we will try to take maximum width into account.
8836 // (??) TODO: take max width into account
8838 // (6) Lay out all cells again with the current values
8841 int y
= availableSpace
.y
;
8842 for (j
= 0; j
< m_rowCount
; j
++)
8844 int x
= availableSpace
.x
; // TODO: take into account centering etc.
8845 int maxCellHeight
= 0;
8846 int maxSpecifiedCellHeight
= 0;
8848 wxArrayInt actualWidths
;
8849 actualWidths
.Add(0, m_colCount
);
8851 wxTextAttrDimensionConverter
converter(dc
, scale
);
8852 for (i
= 0; i
< m_colCount
; i
++)
8854 wxRichTextCell
* cell
= GetCell(j
, i
);
8855 if (cell
->IsShown())
8857 // Get max specified cell height
8858 // Don't handle percentages for height
8859 if (cell
->GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && cell
->GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() != wxTEXT_ATTR_UNITS_PERCENTAGE
)
8861 int h
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetHeight());
8862 if (h
> maxSpecifiedCellHeight
)
8863 maxSpecifiedCellHeight
= h
;
8866 if (colWidths
[i
] > 0) // absolute or proportional width has been specified
8869 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8870 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8872 wxRect availableCellSpace
;
8874 // TODO: take into acount spans
8877 // Calculate the size of this spanning cell from its constituent columns
8879 int spans
= wxMin(colSpan
, m_colCount
- i
);
8880 for (k
= i
; k
< spans
; k
++)
8886 availableCellSpace
= wxRect(x
, y
, xx
, -1);
8889 availableCellSpace
= wxRect(x
, y
, colWidths
[i
], -1);
8891 // Store actual width so we can force cell to be the appropriate width on the final loop
8892 actualWidths
[i
] = availableCellSpace
.GetWidth();
8895 cell
->Invalidate(wxRICHTEXT_ALL
);
8896 cell
->Layout(dc
, context
, availableCellSpace
, availableSpace
, style
);
8898 // TODO: use GetCachedSize().x to compute 'natural' size
8900 x
+= (availableCellSpace
.GetWidth() + paddingX
);
8901 if (cell
->GetCachedSize().y
> maxCellHeight
)
8902 maxCellHeight
= cell
->GetCachedSize().y
;
8907 maxCellHeight
= wxMax(maxCellHeight
, maxSpecifiedCellHeight
);
8909 for (i
= 0; i
< m_colCount
; i
++)
8911 wxRichTextCell
* cell
= GetCell(j
, i
);
8912 if (cell
->IsShown())
8914 wxRect availableCellSpace
= wxRect(cell
->GetPosition(), wxSize(actualWidths
[i
], maxCellHeight
));
8915 // Lay out cell with new height
8916 cell
->Invalidate(wxRICHTEXT_ALL
);
8917 cell
->Layout(dc
, context
, availableCellSpace
, availableSpace
, style
);
8919 // Make sure the cell size really is the appropriate size,
8920 // not the calculated box size
8921 cell
->SetCachedSize(wxSize(actualWidths
[i
], maxCellHeight
));
8923 maxRight
= wxMax(maxRight
, cell
->GetPosition().x
+ cell
->GetCachedSize().x
);
8928 if (j
< (m_rowCount
-1))
8932 // We need to add back the margins etc.
8934 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
8935 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxRight
- availableSpace
.x
, y
- availableSpace
.y
));
8936 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
8937 SetCachedSize(marginRect
.GetSize());
8940 // TODO: calculate max size
8942 SetMaxSize(GetCachedSize());
8945 // TODO: calculate min size
8947 SetMinSize(GetCachedSize());
8950 // TODO: currently we use either a fixed table width or the parent's size.
8951 // We also want to be able to calculate the table width from its content,
8952 // whether using fixed column widths or cell content min/max width.
8953 // Probably need a boolean flag to say whether we need to stretch cells
8954 // to fit the table width, or to simply use min/max cell widths. The
8955 // trouble with this is that if cell widths are not specified, they
8956 // will be tiny; we could use arbitrary defaults but this seems unsatisfactory.
8957 // Anyway, ignoring that problem, we probably need to factor layout into a function
8958 // that can can calculate the maximum unconstrained layout in case table size is
8959 // not specified. Then LayoutToBestSize() can choose to use either parent size to
8960 // constrain Layout(), or the previously-calculated max size to constraint layout.
8965 // Finds the absolute position and row height for the given character position
8966 bool wxRichTextTable::FindPosition(wxDC
& dc
, wxRichTextDrawingContext
& context
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
8968 wxRichTextCell
* child
= GetCell(index
+1);
8971 // Find the position at the start of the child cell, since the table doesn't
8972 // have any caret position of its own.
8973 return child
->FindPosition(dc
, context
, -1, pt
, height
, forceLineStart
);
8979 // Get the cell at the given character position (in the range of the table).
8980 wxRichTextCell
* wxRichTextTable::GetCell(long pos
) const
8982 int row
= 0, col
= 0;
8983 if (GetCellRowColumnPosition(pos
, row
, col
))
8985 return GetCell(row
, col
);
8991 // Get the row/column for a given character position
8992 bool wxRichTextTable::GetCellRowColumnPosition(long pos
, int& row
, int& col
) const
8994 if (m_colCount
== 0 || m_rowCount
== 0)
8997 row
= (int) (pos
/ m_colCount
);
8998 col
= pos
- (row
* m_colCount
);
9000 wxASSERT(row
< m_rowCount
&& col
< m_colCount
);
9002 if (row
< m_rowCount
&& col
< m_colCount
)
9008 // Calculate range, taking row/cell ordering into account instead of relying
9009 // on list ordering.
9010 void wxRichTextTable::CalculateRange(long start
, long& end
)
9012 long current
= start
;
9013 long lastEnd
= current
;
9022 for (i
= 0; i
< m_rowCount
; i
++)
9024 for (j
= 0; j
< m_colCount
; j
++)
9026 wxRichTextCell
* child
= GetCell(i
, j
);
9031 child
->CalculateRange(current
, childEnd
);
9034 current
= childEnd
+ 1;
9039 // A top-level object always has a range of size 1,
9040 // because its children don't count at this level.
9042 m_range
.SetRange(start
, start
);
9044 // An object with no children has zero length
9045 if (m_children
.GetCount() == 0)
9047 m_ownRange
.SetRange(0, lastEnd
);
9050 // Gets the range size.
9051 bool wxRichTextTable::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
9053 return wxRichTextBox::GetRangeSize(range
, size
, descent
, dc
, context
, flags
, position
, partialExtents
);
9056 // Deletes content in the given range.
9057 bool wxRichTextTable::DeleteRange(const wxRichTextRange
& WXUNUSED(range
))
9059 // TODO: implement deletion of cells
9063 // Gets any text in this object for the given range.
9064 wxString
wxRichTextTable::GetTextForRange(const wxRichTextRange
& range
) const
9066 return wxRichTextBox::GetTextForRange(range
);
9069 // Copies this object.
9070 void wxRichTextTable::Copy(const wxRichTextTable
& obj
)
9072 wxRichTextBox::Copy(obj
);
9076 m_rowCount
= obj
.m_rowCount
;
9077 m_colCount
= obj
.m_colCount
;
9079 m_cells
.Add(wxRichTextObjectPtrArray(), m_rowCount
);
9082 for (i
= 0; i
< m_rowCount
; i
++)
9084 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
9085 for (j
= 0; j
< m_colCount
; j
++)
9087 wxRichTextCell
* cell
= wxDynamicCast(obj
.GetCell(i
, j
)->Clone(), wxRichTextCell
);
9095 void wxRichTextTable::ClearTable()
9101 bool wxRichTextTable::CreateTable(int rows
, int cols
)
9108 m_cells
.Add(wxRichTextObjectPtrArray(), rows
);
9111 for (i
= 0; i
< rows
; i
++)
9113 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
9114 for (j
= 0; j
< cols
; j
++)
9116 wxRichTextCell
* cell
= new wxRichTextCell
;
9118 cell
->AddParagraph(wxEmptyString
);
9127 wxRichTextCell
* wxRichTextTable::GetCell(int row
, int col
) const
9129 wxASSERT(row
< m_rowCount
);
9130 wxASSERT(col
< m_colCount
);
9132 if (row
< m_rowCount
&& col
< m_colCount
)
9134 wxRichTextObjectPtrArray
& colArray
= m_cells
[row
];
9135 wxRichTextObject
* obj
= colArray
[col
];
9136 return wxDynamicCast(obj
, wxRichTextCell
);
9142 // Returns a selection object specifying the selections between start and end character positions.
9143 // For example, a table would deduce what cells (of range length 1) are selected when dragging across the table.
9144 wxRichTextSelection
wxRichTextTable::GetSelection(long start
, long end
) const
9146 wxRichTextSelection selection
;
9147 selection
.SetContainer((wxRichTextTable
*) this);
9156 wxASSERT( start
>= 0 && end
< (m_colCount
* m_rowCount
));
9158 if (end
>= (m_colCount
* m_rowCount
))
9161 // We need to find the rectangle of cells that is described by the rectangle
9162 // with start, end as the diagonal. Make sure we don't add cells that are
9163 // not currenty visible because they are overlapped by spanning cells.
9165 --------------------------
9166 | 0 | 1 | 2 | 3 | 4 |
9167 --------------------------
9168 | 5 | 6 | 7 | 8 | 9 |
9169 --------------------------
9170 | 10 | 11 | 12 | 13 | 14 |
9171 --------------------------
9172 | 15 | 16 | 17 | 18 | 19 |
9173 --------------------------
9175 Let's say we select 6 -> 18.
9177 Left and right edge cols of rectangle are 1 and 3 inclusive. Find least/greatest to find
9178 which is left and which is right.
9180 Top and bottom edge rows are 1 and 3 inclusive. Again, find least/greatest to find top and bottom.
9182 Now go through rows from 1 to 3 and only add cells that are (a) within above column range
9188 int leftCol
= start
- m_colCount
* int(start
/m_colCount
);
9189 int rightCol
= end
- m_colCount
* int(end
/m_colCount
);
9191 int topRow
= int(start
/m_colCount
);
9192 int bottomRow
= int(end
/m_colCount
);
9194 if (leftCol
> rightCol
)
9201 if (topRow
> bottomRow
)
9203 int tmp
= bottomRow
;
9209 for (i
= topRow
; i
<= bottomRow
; i
++)
9211 for (j
= leftCol
; j
<= rightCol
; j
++)
9213 wxRichTextCell
* cell
= GetCell(i
, j
);
9214 if (cell
&& cell
->IsShown())
9215 selection
.Add(cell
->GetRange());
9222 // Sets the attributes for the cells specified by the selection.
9223 bool wxRichTextTable::SetCellStyle(const wxRichTextSelection
& selection
, const wxRichTextAttr
& style
, int flags
)
9225 if (selection
.GetContainer() != this)
9228 wxRichTextBuffer
* buffer
= GetBuffer();
9229 bool haveControl
= (buffer
&& buffer
->GetRichTextCtrl() != NULL
);
9230 bool withUndo
= haveControl
&& ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
9233 buffer
->BeginBatchUndo(_("Set Cell Style"));
9235 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
9238 wxRichTextCell
* cell
= wxDynamicCast(node
->GetData(), wxRichTextCell
);
9239 if (cell
&& selection
.WithinSelection(cell
->GetRange().GetStart()))
9240 SetStyle(cell
, style
, flags
);
9241 node
= node
->GetNext();
9244 // Do action, or delay it until end of batch.
9246 buffer
->EndBatchUndo();
9251 bool wxRichTextTable::DeleteRows(int startRow
, int noRows
)
9253 wxASSERT((startRow
+ noRows
) < m_rowCount
);
9254 if ((startRow
+ noRows
) >= m_rowCount
)
9258 for (i
= startRow
; i
< (startRow
+noRows
); i
++)
9260 wxRichTextObjectPtrArray
& colArray
= m_cells
[startRow
];
9261 for (j
= 0; j
< (int) colArray
.GetCount(); j
++)
9263 wxRichTextObject
* cell
= colArray
[j
];
9264 RemoveChild(cell
, true);
9267 // Keep deleting at the same position, since we move all
9269 m_cells
.RemoveAt(startRow
);
9272 m_rowCount
= m_rowCount
- noRows
;
9277 bool wxRichTextTable::DeleteColumns(int startCol
, int noCols
)
9279 wxASSERT((startCol
+ noCols
) < m_colCount
);
9280 if ((startCol
+ noCols
) >= m_colCount
)
9283 bool deleteRows
= (noCols
== m_colCount
);
9286 for (i
= 0; i
< m_rowCount
; i
++)
9288 wxRichTextObjectPtrArray
& colArray
= m_cells
[deleteRows
? 0 : i
];
9289 for (j
= startCol
; j
< (startCol
+noCols
); j
++)
9291 wxRichTextObject
* cell
= colArray
[j
];
9292 RemoveChild(cell
, true);
9296 m_cells
.RemoveAt(0);
9301 m_colCount
= m_colCount
- noCols
;
9306 bool wxRichTextTable::AddRows(int startRow
, int noRows
, const wxRichTextAttr
& attr
)
9308 wxASSERT(startRow
<= m_rowCount
);
9309 if (startRow
> m_rowCount
)
9313 for (i
= 0; i
< noRows
; i
++)
9316 if (startRow
== m_rowCount
)
9318 m_cells
.Add(wxRichTextObjectPtrArray());
9319 idx
= m_cells
.GetCount() - 1;
9323 m_cells
.Insert(wxRichTextObjectPtrArray(), startRow
+i
);
9327 wxRichTextObjectPtrArray
& colArray
= m_cells
[idx
];
9328 for (j
= 0; j
< m_colCount
; j
++)
9330 wxRichTextCell
* cell
= new wxRichTextCell
;
9331 cell
->GetAttributes() = attr
;
9338 m_rowCount
= m_rowCount
+ noRows
;
9342 bool wxRichTextTable::AddColumns(int startCol
, int noCols
, const wxRichTextAttr
& attr
)
9344 wxASSERT(startCol
<= m_colCount
);
9345 if (startCol
> m_colCount
)
9349 for (i
= 0; i
< m_rowCount
; i
++)
9351 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
9352 for (j
= 0; j
< noCols
; j
++)
9354 wxRichTextCell
* cell
= new wxRichTextCell
;
9355 cell
->GetAttributes() = attr
;
9359 if (startCol
== m_colCount
)
9362 colArray
.Insert(cell
, startCol
+j
);
9366 m_colCount
= m_colCount
+ noCols
;
9371 // Edit properties via a GUI
9372 bool wxRichTextTable::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
9374 wxRichTextObjectPropertiesDialog
boxDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Table Properties"));
9375 boxDlg
.SetAttributes(GetAttributes());
9377 if (boxDlg
.ShowModal() == wxID_OK
)
9379 boxDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
9387 * Module to initialise and clean up handlers
9390 class wxRichTextModule
: public wxModule
9392 DECLARE_DYNAMIC_CLASS(wxRichTextModule
)
9394 wxRichTextModule() {}
9397 wxRichTextBuffer::SetRenderer(new wxRichTextStdRenderer
);
9398 wxRichTextBuffer::InitStandardHandlers();
9399 wxRichTextParagraph::InitDefaultTabs();
9404 wxRichTextBuffer::CleanUpHandlers();
9405 wxRichTextBuffer::CleanUpDrawingHandlers();
9406 wxRichTextDecimalToRoman(-1);
9407 wxRichTextParagraph::ClearDefaultTabs();
9408 wxRichTextCtrl::ClearAvailableFontNames();
9409 wxRichTextBuffer::SetRenderer(NULL
);
9413 IMPLEMENT_DYNAMIC_CLASS(wxRichTextModule
, wxModule
)
9416 // If the richtext lib is dynamically loaded after the app has already started
9417 // (such as from wxPython) then the built-in module system will not init this
9418 // module. Provide this function to do it manually.
9419 void wxRichTextModuleInit()
9421 wxModule
* module = new wxRichTextModule
;
9423 wxModule::RegisterModule(module);
9428 * Commands for undo/redo
9432 wxRichTextCommand::wxRichTextCommand(const wxString
& name
, wxRichTextCommandId id
, wxRichTextBuffer
* buffer
,
9433 wxRichTextParagraphLayoutBox
* container
, wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
): wxCommand(true, name
)
9435 /* wxRichTextAction* action = */ new wxRichTextAction(this, name
, id
, buffer
, container
, ctrl
, ignoreFirstTime
);
9438 wxRichTextCommand::wxRichTextCommand(const wxString
& name
): wxCommand(true, name
)
9442 wxRichTextCommand::~wxRichTextCommand()
9447 void wxRichTextCommand::AddAction(wxRichTextAction
* action
)
9449 if (!m_actions
.Member(action
))
9450 m_actions
.Append(action
);
9453 bool wxRichTextCommand::Do()
9455 for (wxList::compatibility_iterator node
= m_actions
.GetFirst(); node
; node
= node
->GetNext())
9457 wxRichTextAction
* action
= (wxRichTextAction
*) node
->GetData();
9464 bool wxRichTextCommand::Undo()
9466 for (wxList::compatibility_iterator node
= m_actions
.GetLast(); node
; node
= node
->GetPrevious())
9468 wxRichTextAction
* action
= (wxRichTextAction
*) node
->GetData();
9475 void wxRichTextCommand::ClearActions()
9477 WX_CLEAR_LIST(wxList
, m_actions
);
9485 wxRichTextAction::wxRichTextAction(wxRichTextCommand
* cmd
, const wxString
& name
, wxRichTextCommandId id
,
9486 wxRichTextBuffer
* buffer
, wxRichTextParagraphLayoutBox
* container
,
9487 wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
)
9491 m_containerAddress
.Create(buffer
, container
);
9492 m_ignoreThis
= ignoreFirstTime
;
9497 m_newParagraphs
.SetDefaultStyle(buffer
->GetDefaultStyle());
9498 m_newParagraphs
.SetBasicStyle(buffer
->GetBasicStyle());
9500 cmd
->AddAction(this);
9503 wxRichTextAction::~wxRichTextAction()
9509 // Returns the container that this action refers to, using the container address and top-level buffer.
9510 wxRichTextParagraphLayoutBox
* wxRichTextAction::GetContainer() const
9512 wxRichTextParagraphLayoutBox
* container
= wxDynamicCast(GetContainerAddress().GetObject(m_buffer
), wxRichTextParagraphLayoutBox
);
9517 void wxRichTextAction::CalculateRefreshOptimizations(wxArrayInt
& optimizationLineCharPositions
, wxArrayInt
& optimizationLineYPositions
)
9519 // Store a list of line start character and y positions so we can figure out which area
9520 // we need to refresh
9522 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9523 wxRichTextParagraphLayoutBox
* container
= GetContainer();
9524 wxASSERT(container
!= NULL
);
9528 // NOTE: we're assuming that the buffer is laid out correctly at this point.
9529 // If we had several actions, which only invalidate and leave layout until the
9530 // paint handler is called, then this might not be true. So we may need to switch
9531 // optimisation on only when we're simply adding text and not simultaneously
9532 // deleting a selection, for example. Or, we make sure the buffer is laid out correctly
9533 // first, but of course this means we'll be doing it twice.
9534 if (!m_buffer
->IsDirty() && m_ctrl
) // can only do optimisation if the buffer is already laid out correctly
9536 wxSize clientSize
= m_ctrl
->GetClientSize();
9537 wxPoint firstVisiblePt
= m_ctrl
->GetFirstVisiblePoint();
9538 int lastY
= firstVisiblePt
.y
+ clientSize
.y
;
9540 wxRichTextParagraph
* para
= container
->GetParagraphAtPosition(GetRange().GetStart());
9541 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(para
);
9544 wxRichTextParagraph
* child
= (wxRichTextParagraph
*) node
->GetData();
9545 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
9548 wxRichTextLine
* line
= node2
->GetData();
9549 wxPoint pt
= line
->GetAbsolutePosition();
9550 wxRichTextRange range
= line
->GetAbsoluteRange();
9554 node2
= wxRichTextLineList::compatibility_iterator();
9555 node
= wxRichTextObjectList::compatibility_iterator();
9557 else if (range
.GetStart() > GetPosition() && pt
.y
>= firstVisiblePt
.y
)
9559 optimizationLineCharPositions
.Add(range
.GetStart());
9560 optimizationLineYPositions
.Add(pt
.y
);
9564 node2
= node2
->GetNext();
9568 node
= node
->GetNext();
9574 bool wxRichTextAction::Do()
9576 m_buffer
->Modify(true);
9578 wxRichTextParagraphLayoutBox
* container
= GetContainer();
9579 wxASSERT(container
!= NULL
);
9585 case wxRICHTEXT_INSERT
:
9587 // Store a list of line start character and y positions so we can figure out which area
9588 // we need to refresh
9589 wxArrayInt optimizationLineCharPositions
;
9590 wxArrayInt optimizationLineYPositions
;
9592 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9593 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
9596 container
->InsertFragment(GetRange().GetStart(), m_newParagraphs
);
9597 container
->UpdateRanges();
9599 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9600 // Layout() would stop prematurely at the top level.
9601 container
->InvalidateHierarchy(wxRichTextRange(wxMax(0, GetRange().GetStart()-1), GetRange().GetEnd()));
9603 long newCaretPosition
= GetPosition() + m_newParagraphs
.GetOwnRange().GetLength();
9605 // Character position to caret position
9606 newCaretPosition
--;
9608 // Don't take into account the last newline
9609 if (m_newParagraphs
.GetPartialParagraph())
9610 newCaretPosition
--;
9612 if (m_newParagraphs
.GetChildren().GetCount() > 1)
9614 wxRichTextObject
* p
= (wxRichTextObject
*) m_newParagraphs
.GetChildren().GetLast()->GetData();
9615 if (p
->GetRange().GetLength() == 1)
9616 newCaretPosition
--;
9619 newCaretPosition
= wxMin(newCaretPosition
, (container
->GetOwnRange().GetEnd()-1));
9621 UpdateAppearance(newCaretPosition
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */);
9623 wxRichTextEvent
cmdEvent(
9624 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED
,
9625 m_ctrl
? m_ctrl
->GetId() : -1);
9626 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9627 cmdEvent
.SetRange(GetRange());
9628 cmdEvent
.SetPosition(GetRange().GetStart());
9629 cmdEvent
.SetContainer(container
);
9631 m_buffer
->SendEvent(cmdEvent
);
9635 case wxRICHTEXT_DELETE
:
9637 wxArrayInt optimizationLineCharPositions
;
9638 wxArrayInt optimizationLineYPositions
;
9640 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9641 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
9644 container
->DeleteRange(GetRange());
9645 container
->UpdateRanges();
9646 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9647 // Layout() would stop prematurely at the top level.
9648 container
->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
9650 long caretPos
= GetRange().GetStart()-1;
9651 if (caretPos
>= container
->GetOwnRange().GetEnd())
9654 UpdateAppearance(caretPos
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */);
9656 wxRichTextEvent
cmdEvent(
9657 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED
,
9658 m_ctrl
? m_ctrl
->GetId() : -1);
9659 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9660 cmdEvent
.SetRange(GetRange());
9661 cmdEvent
.SetPosition(GetRange().GetStart());
9662 cmdEvent
.SetContainer(container
);
9664 m_buffer
->SendEvent(cmdEvent
);
9668 case wxRICHTEXT_CHANGE_STYLE
:
9669 case wxRICHTEXT_CHANGE_PROPERTIES
:
9671 ApplyParagraphs(GetNewParagraphs());
9673 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9674 // Layout() would stop prematurely at the top level.
9675 container
->InvalidateHierarchy(GetRange());
9677 UpdateAppearance(GetPosition());
9679 wxRichTextEvent
cmdEvent(
9680 m_cmdId
== wxRICHTEXT_CHANGE_STYLE
? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
: wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED
,
9681 m_ctrl
? m_ctrl
->GetId() : -1);
9682 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9683 cmdEvent
.SetRange(GetRange());
9684 cmdEvent
.SetPosition(GetRange().GetStart());
9685 cmdEvent
.SetContainer(container
);
9687 m_buffer
->SendEvent(cmdEvent
);
9691 case wxRICHTEXT_CHANGE_ATTRIBUTES
:
9693 wxRichTextObject
* obj
= m_objectAddress
.GetObject(m_buffer
); // container->GetChildAtPosition(GetRange().GetStart());
9696 wxRichTextAttr oldAttr
= obj
->GetAttributes();
9697 obj
->GetAttributes() = m_attributes
;
9698 m_attributes
= oldAttr
;
9701 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9702 // Layout() would stop prematurely at the top level.
9703 container
->InvalidateHierarchy(GetRange());
9705 UpdateAppearance(GetPosition());
9707 wxRichTextEvent
cmdEvent(
9708 wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
,
9709 m_ctrl
? m_ctrl
->GetId() : -1);
9710 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9711 cmdEvent
.SetRange(GetRange());
9712 cmdEvent
.SetPosition(GetRange().GetStart());
9713 cmdEvent
.SetContainer(container
);
9715 m_buffer
->SendEvent(cmdEvent
);
9719 case wxRICHTEXT_CHANGE_OBJECT
:
9721 wxRichTextObject
* obj
= m_objectAddress
.GetObject(m_buffer
);
9722 // wxRichTextObject* obj = container->GetChildAtPosition(GetRange().GetStart());
9723 if (obj
&& m_object
)
9725 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(obj
);
9728 wxRichTextObject
* obj
= node
->GetData();
9729 node
->SetData(m_object
);
9734 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9735 // Layout() would stop prematurely at the top level.
9736 container
->InvalidateHierarchy(GetRange());
9738 UpdateAppearance(GetPosition());
9740 // TODO: send new kind of modification event
9751 bool wxRichTextAction::Undo()
9753 m_buffer
->Modify(true);
9755 wxRichTextParagraphLayoutBox
* container
= GetContainer();
9756 wxASSERT(container
!= NULL
);
9762 case wxRICHTEXT_INSERT
:
9764 wxArrayInt optimizationLineCharPositions
;
9765 wxArrayInt optimizationLineYPositions
;
9767 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9768 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
9771 container
->DeleteRange(GetRange());
9772 container
->UpdateRanges();
9773 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9774 // Layout() would stop prematurely at the top level.
9775 container
->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
9777 long newCaretPosition
= GetPosition() - 1;
9779 UpdateAppearance(newCaretPosition
, true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */);
9781 wxRichTextEvent
cmdEvent(
9782 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED
,
9783 m_ctrl
? m_ctrl
->GetId() : -1);
9784 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9785 cmdEvent
.SetRange(GetRange());
9786 cmdEvent
.SetPosition(GetRange().GetStart());
9787 cmdEvent
.SetContainer(container
);
9789 m_buffer
->SendEvent(cmdEvent
);
9793 case wxRICHTEXT_DELETE
:
9795 wxArrayInt optimizationLineCharPositions
;
9796 wxArrayInt optimizationLineYPositions
;
9798 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9799 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
9802 container
->InsertFragment(GetRange().GetStart(), m_oldParagraphs
);
9803 container
->UpdateRanges();
9804 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9805 // Layout() would stop prematurely at the top level.
9806 container
->InvalidateHierarchy(GetRange());
9808 UpdateAppearance(GetPosition(), true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */);
9810 wxRichTextEvent
cmdEvent(
9811 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED
,
9812 m_ctrl
? m_ctrl
->GetId() : -1);
9813 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9814 cmdEvent
.SetRange(GetRange());
9815 cmdEvent
.SetPosition(GetRange().GetStart());
9816 cmdEvent
.SetContainer(container
);
9818 m_buffer
->SendEvent(cmdEvent
);
9822 case wxRICHTEXT_CHANGE_STYLE
:
9823 case wxRICHTEXT_CHANGE_PROPERTIES
:
9825 ApplyParagraphs(GetOldParagraphs());
9826 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9827 // Layout() would stop prematurely at the top level.
9828 container
->InvalidateHierarchy(GetRange());
9830 UpdateAppearance(GetPosition());
9832 wxRichTextEvent
cmdEvent(
9833 m_cmdId
== wxRICHTEXT_CHANGE_STYLE
? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
: wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED
,
9834 m_ctrl
? m_ctrl
->GetId() : -1);
9835 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9836 cmdEvent
.SetRange(GetRange());
9837 cmdEvent
.SetPosition(GetRange().GetStart());
9838 cmdEvent
.SetContainer(container
);
9840 m_buffer
->SendEvent(cmdEvent
);
9844 case wxRICHTEXT_CHANGE_ATTRIBUTES
:
9845 case wxRICHTEXT_CHANGE_OBJECT
:
9856 /// Update the control appearance
9857 void wxRichTextAction::UpdateAppearance(long caretPosition
, bool sendUpdateEvent
, wxArrayInt
* optimizationLineCharPositions
, wxArrayInt
* optimizationLineYPositions
, bool isDoCmd
)
9859 wxRichTextParagraphLayoutBox
* container
= GetContainer();
9860 wxASSERT(container
!= NULL
);
9866 m_ctrl
->SetFocusObject(container
);
9867 m_ctrl
->SetCaretPosition(caretPosition
);
9869 if (!m_ctrl
->IsFrozen())
9871 wxRect containerRect
= container
->GetRect();
9873 m_ctrl
->LayoutContent();
9875 // Refresh everything if there were floating objects or the container changed size
9876 // (we can't yet optimize in these cases, since more complex interaction with other content occurs)
9877 if (container
->GetFloatingObjectCount() > 0 || (container
->GetParent() && containerRect
!= container
->GetRect()))
9879 m_ctrl
->Refresh(false);
9883 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9884 // Find refresh rectangle if we are in a position to optimise refresh
9885 if ((m_cmdId
== wxRICHTEXT_INSERT
|| m_cmdId
== wxRICHTEXT_DELETE
) && optimizationLineCharPositions
)
9889 wxSize clientSize
= m_ctrl
->GetClientSize();
9890 wxPoint firstVisiblePt
= m_ctrl
->GetFirstVisiblePoint();
9892 // Start/end positions
9894 int lastY
= firstVisiblePt
.y
+ clientSize
.y
;
9896 bool foundEnd
= false;
9898 // position offset - how many characters were inserted
9899 int positionOffset
= GetRange().GetLength();
9901 // Determine whether this is Do or Undo, and adjust positionOffset accordingly
9902 if ((m_cmdId
== wxRICHTEXT_DELETE
&& isDoCmd
) || (m_cmdId
== wxRICHTEXT_INSERT
&& !isDoCmd
))
9903 positionOffset
= - positionOffset
;
9905 // find the first line which is being drawn at the same position as it was
9906 // before. Since we're talking about a simple insertion, we can assume
9907 // that the rest of the window does not need to be redrawn.
9909 wxRichTextParagraph
* para
= container
->GetParagraphAtPosition(GetPosition());
9910 // Since we support floating layout, we should redraw the whole para instead of just
9911 // the first line touching the invalid range.
9914 firstY
= para
->GetPosition().y
;
9917 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(para
);
9920 wxRichTextParagraph
* child
= (wxRichTextParagraph
*) node
->GetData();
9921 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
9924 wxRichTextLine
* line
= node2
->GetData();
9925 wxPoint pt
= line
->GetAbsolutePosition();
9926 wxRichTextRange range
= line
->GetAbsoluteRange();
9928 // we want to find the first line that is in the same position
9929 // as before. This will mean we're at the end of the changed text.
9931 if (pt
.y
> lastY
) // going past the end of the window, no more info
9933 node2
= wxRichTextLineList::compatibility_iterator();
9934 node
= wxRichTextObjectList::compatibility_iterator();
9936 // Detect last line in the buffer
9937 else if (!node2
->GetNext() && para
->GetRange().Contains(container
->GetOwnRange().GetEnd()))
9939 // If deleting text, make sure we refresh below as well as above
9940 if (positionOffset
>= 0)
9943 lastY
= pt
.y
+ line
->GetSize().y
;
9946 node2
= wxRichTextLineList::compatibility_iterator();
9947 node
= wxRichTextObjectList::compatibility_iterator();
9953 // search for this line being at the same position as before
9954 for (i
= 0; i
< optimizationLineCharPositions
->GetCount(); i
++)
9956 if (((*optimizationLineCharPositions
)[i
] + positionOffset
== range
.GetStart()) &&
9957 ((*optimizationLineYPositions
)[i
] == pt
.y
))
9959 // Stop, we're now the same as we were
9964 node2
= wxRichTextLineList::compatibility_iterator();
9965 node
= wxRichTextObjectList::compatibility_iterator();
9973 node2
= node2
->GetNext();
9977 node
= node
->GetNext();
9980 firstY
= wxMax(firstVisiblePt
.y
, firstY
);
9982 lastY
= firstVisiblePt
.y
+ clientSize
.y
;
9984 // Convert to device coordinates
9985 wxRect
rect(m_ctrl
->GetPhysicalPoint(wxPoint(firstVisiblePt
.x
, firstY
)), wxSize(clientSize
.x
, lastY
- firstY
));
9986 m_ctrl
->RefreshRect(rect
);
9990 m_ctrl
->Refresh(false);
9992 m_ctrl
->PositionCaret();
9994 // This causes styles to persist when doing programmatic
9995 // content creation except when Freeze/Thaw is used, so
9996 // disable this and check for the consequences.
9997 // m_ctrl->SetDefaultStyleToCursorStyle();
9999 if (sendUpdateEvent
)
10000 wxTextCtrl::SendTextUpdatedEvent(m_ctrl
);
10005 /// Replace the buffer paragraphs with the new ones.
10006 void wxRichTextAction::ApplyParagraphs(const wxRichTextParagraphLayoutBox
& fragment
)
10008 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10009 wxASSERT(container
!= NULL
);
10013 wxRichTextObjectList::compatibility_iterator node
= fragment
.GetChildren().GetFirst();
10016 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
10017 wxASSERT (para
!= NULL
);
10019 // We'll replace the existing paragraph by finding the paragraph at this position,
10020 // delete its node data, and setting a copy as the new node data.
10021 // TODO: make more efficient by simply swapping old and new paragraph objects.
10023 wxRichTextParagraph
* existingPara
= container
->GetParagraphAtPosition(para
->GetRange().GetStart());
10026 wxRichTextObjectList::compatibility_iterator bufferParaNode
= container
->GetChildren().Find(existingPara
);
10027 if (bufferParaNode
)
10029 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(*para
);
10030 newPara
->SetParent(container
);
10032 bufferParaNode
->SetData(newPara
);
10034 delete existingPara
;
10038 node
= node
->GetNext();
10045 * This stores beginning and end positions for a range of data.
10048 WX_DEFINE_OBJARRAY(wxRichTextRangeArray
);
10050 /// Limit this range to be within 'range'
10051 bool wxRichTextRange::LimitTo(const wxRichTextRange
& range
)
10053 if (m_start
< range
.m_start
)
10054 m_start
= range
.m_start
;
10056 if (m_end
> range
.m_end
)
10057 m_end
= range
.m_end
;
10063 * wxRichTextImage implementation
10064 * This object represents an image.
10067 IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage
, wxRichTextObject
)
10069 wxRichTextImage::wxRichTextImage(const wxImage
& image
, wxRichTextObject
* parent
, wxRichTextAttr
* charStyle
):
10070 wxRichTextObject(parent
)
10073 m_imageBlock
.MakeImageBlockDefaultQuality(image
, wxBITMAP_TYPE_PNG
);
10075 SetAttributes(*charStyle
);
10078 wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock
& imageBlock
, wxRichTextObject
* parent
, wxRichTextAttr
* charStyle
):
10079 wxRichTextObject(parent
)
10082 m_imageBlock
= imageBlock
;
10084 SetAttributes(*charStyle
);
10087 void wxRichTextImage::Init()
10089 m_originalImageSize
= wxSize(-1, -1);
10092 /// Create a cached image at the required size
10093 bool wxRichTextImage::LoadImageCache(wxDC
& dc
, bool resetCache
)
10095 if (!m_imageBlock
.IsOk())
10098 // If we have an original image size, use that to compute the cached bitmap size
10099 // instead of loading the image each time. This way we can avoid loading
10100 // the image so long as the new cached bitmap size hasn't changed.
10103 if (resetCache
|| m_originalImageSize
== wxSize(-1, -1))
10105 m_imageCache
= wxNullBitmap
;
10107 m_imageBlock
.Load(image
);
10111 m_originalImageSize
= wxSize(image
.GetWidth(), image
.GetHeight());
10114 int width
= m_originalImageSize
.GetWidth();
10115 int height
= m_originalImageSize
.GetHeight();
10117 int parentWidth
= 0;
10118 int parentHeight
= 0;
10121 int maxHeight
= -1;
10123 wxRichTextBuffer
* buffer
= GetBuffer();
10127 if (buffer
->GetRichTextCtrl())
10129 // Subtract borders
10130 sz
= buffer
->GetRichTextCtrl()->GetClientSize();
10132 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
10133 marginRect
= wxRect(0, 0, sz
.x
, sz
.y
);
10134 buffer
->GetBoxRects(dc
, buffer
, buffer
->GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
10136 sz
= contentRect
.GetSize();
10138 // Start with a maximum width of the control size, even if not specified by the content,
10139 // to minimize the amount of picture overlapping the right-hand side
10143 sz
= buffer
->GetCachedSize();
10144 parentWidth
= sz
.GetWidth();
10145 parentHeight
= sz
.GetHeight();
10148 if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetWidth().GetValue() > 0)
10150 if (parentWidth
> 0 && GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
10151 width
= (int) ((GetAttributes().GetTextBoxAttr().GetWidth().GetValue() * parentWidth
)/100.0);
10152 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
10153 width
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetWidth().GetValue());
10154 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
10155 width
= GetAttributes().GetTextBoxAttr().GetWidth().GetValue();
10158 // Limit to max width
10160 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() > 0)
10164 if (parentWidth
> 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
10165 mw
= (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() * parentWidth
)/100.0);
10166 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
10167 mw
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue());
10168 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
10169 mw
= GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue();
10171 // If we already have a smaller max width due to the constraints of the control size,
10172 // don't use the larger max width.
10173 if (mw
!= -1 && ((maxWidth
== -1) || (mw
< maxWidth
)))
10177 if (maxWidth
> 0 && width
> maxWidth
)
10180 // Preserve the aspect ratio
10181 if (width
!= m_originalImageSize
.GetWidth())
10182 height
= (int) (float(m_originalImageSize
.GetHeight()) * (float(width
)/float(m_originalImageSize
.GetWidth())));
10184 if (GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetHeight().GetValue() > 0)
10186 if (parentHeight
> 0 && GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
10187 height
= (int) ((GetAttributes().GetTextBoxAttr().GetHeight().GetValue() * parentHeight
)/100.0);
10188 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
10189 height
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetHeight().GetValue());
10190 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
10191 height
= GetAttributes().GetTextBoxAttr().GetHeight().GetValue();
10193 // Preserve the aspect ratio
10194 if (height
!= m_originalImageSize
.GetHeight())
10195 width
= (int) (float(m_originalImageSize
.GetWidth()) * (float(height
)/float(m_originalImageSize
.GetHeight())));
10198 // Limit to max height
10200 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() > 0)
10202 if (parentHeight
> 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
10203 maxHeight
= (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() * parentHeight
)/100.0);
10204 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
10205 maxHeight
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue());
10206 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
10207 maxHeight
= GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue();
10210 if (maxHeight
> 0 && height
> maxHeight
)
10212 height
= maxHeight
;
10214 // Preserve the aspect ratio
10215 if (height
!= m_originalImageSize
.GetHeight())
10216 width
= (int) (float(m_originalImageSize
.GetWidth()) * (float(height
)/float(m_originalImageSize
.GetHeight())));
10219 if (m_imageCache
.IsOk() && m_imageCache
.GetWidth() == width
&& m_imageCache
.GetHeight() == height
)
10221 // Do nothing, we didn't need to change the image cache
10227 m_imageBlock
.Load(image
);
10232 if (image
.GetWidth() == width
&& image
.GetHeight() == height
)
10233 m_imageCache
= wxBitmap(image
);
10236 // If the original width and height is small, e.g. 400 or below,
10237 // scale up and then down to improve image quality. This can make
10238 // a big difference, with not much performance hit.
10239 int upscaleThreshold
= 400;
10241 if (image
.GetWidth() <= upscaleThreshold
|| image
.GetHeight() <= upscaleThreshold
)
10243 img
= image
.Scale(image
.GetWidth()*2, image
.GetHeight()*2);
10244 img
.Rescale(width
, height
, wxIMAGE_QUALITY_HIGH
);
10247 img
= image
.Scale(width
, height
, wxIMAGE_QUALITY_HIGH
);
10248 m_imageCache
= wxBitmap(img
);
10252 return m_imageCache
.IsOk();
10256 bool wxRichTextImage::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int WXUNUSED(descent
), int WXUNUSED(style
))
10261 // Don't need cached size AFAIK
10262 // wxSize size = GetCachedSize();
10263 if (!LoadImageCache(dc
))
10266 wxRichTextAttr
attr(GetAttributes());
10267 context
.ApplyVirtualAttributes(attr
, this);
10269 DrawBoxAttributes(dc
, GetBuffer(), attr
, wxRect(rect
.GetPosition(), GetCachedSize()));
10272 int y
= rect
.y
+ (rect
.height
- m_imageCache
.GetHeight());
10274 dc
.DrawBitmap(m_imageCache
, rect
.x
, y
, true);
10277 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
10278 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
10279 marginRect
= rect
; // outer rectangle, will calculate contentRect
10280 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
10282 dc
.DrawBitmap(m_imageCache
, contentRect
.x
, contentRect
.y
, true);
10284 if (selection
.WithinSelection(range
.GetStart(), this))
10286 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
10287 wxCheckSetPen(dc
, *wxBLACK_PEN
);
10288 dc
.SetLogicalFunction(wxINVERT
);
10289 dc
.DrawRectangle(contentRect
);
10290 dc
.SetLogicalFunction(wxCOPY
);
10296 /// Lay the item out
10297 bool wxRichTextImage::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& WXUNUSED(parentRect
), int WXUNUSED(style
))
10299 if (!LoadImageCache(dc
))
10302 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
10303 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
10304 contentRect
= wxRect(wxPoint(0,0), imageSize
);
10306 wxRichTextAttr
attr(GetAttributes());
10307 context
.ApplyVirtualAttributes(attr
, this);
10309 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
10311 wxSize overallSize
= marginRect
.GetSize();
10313 SetCachedSize(overallSize
);
10314 SetMaxSize(overallSize
);
10315 SetMinSize(overallSize
);
10316 SetPosition(rect
.GetPosition());
10321 /// Get/set the object size for the given range. Returns false if the range
10322 /// is invalid for this object.
10323 bool wxRichTextImage::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& WXUNUSED(descent
), wxDC
& dc
, wxRichTextDrawingContext
& context
, int WXUNUSED(flags
), wxPoint
WXUNUSED(position
), wxArrayInt
* partialExtents
) const
10325 if (!range
.IsWithin(GetRange()))
10328 if (!((wxRichTextImage
*)this)->LoadImageCache(dc
))
10330 size
.x
= 0; size
.y
= 0;
10331 if (partialExtents
)
10332 partialExtents
->Add(0);
10336 wxRichTextAttr
attr(GetAttributes());
10337 context
.ApplyVirtualAttributes(attr
, (wxRichTextObject
*) this);
10339 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
10340 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
10341 contentRect
= wxRect(wxPoint(0,0), imageSize
);
10342 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
10344 wxSize overallSize
= marginRect
.GetSize();
10346 if (partialExtents
)
10347 partialExtents
->Add(overallSize
.x
);
10349 size
= overallSize
;
10354 // Get the 'natural' size for an object. For an image, it would be the
10356 wxTextAttrSize
wxRichTextImage::GetNaturalSize() const
10358 wxTextAttrSize size
;
10359 if (GetImageCache().IsOk())
10361 size
.SetWidth(GetImageCache().GetWidth(), wxTEXT_ATTR_UNITS_PIXELS
);
10362 size
.SetHeight(GetImageCache().GetHeight(), wxTEXT_ATTR_UNITS_PIXELS
);
10369 void wxRichTextImage::Copy(const wxRichTextImage
& obj
)
10371 wxRichTextObject::Copy(obj
);
10373 m_imageBlock
= obj
.m_imageBlock
;
10374 m_originalImageSize
= obj
.m_originalImageSize
;
10377 /// Edit properties via a GUI
10378 bool wxRichTextImage::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
10380 wxRichTextObjectPropertiesDialog
imageDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Picture Properties"));
10381 imageDlg
.SetAttributes(GetAttributes());
10383 if (imageDlg
.ShowModal() == wxID_OK
)
10385 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
10386 // indeterminate in the object.
10387 imageDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
10399 /// Compare two attribute objects
10400 bool wxTextAttrEq(const wxRichTextAttr
& attr1
, const wxRichTextAttr
& attr2
)
10402 return (attr1
== attr2
);
10405 // Partial equality test taking flags into account
10406 bool wxTextAttrEqPartial(const wxRichTextAttr
& attr1
, const wxRichTextAttr
& attr2
)
10408 return attr1
.EqPartial(attr2
);
10412 bool wxRichTextTabsEq(const wxArrayInt
& tabs1
, const wxArrayInt
& tabs2
)
10414 if (tabs1
.GetCount() != tabs2
.GetCount())
10418 for (i
= 0; i
< tabs1
.GetCount(); i
++)
10420 if (tabs1
[i
] != tabs2
[i
])
10426 bool wxRichTextApplyStyle(wxRichTextAttr
& destStyle
, const wxRichTextAttr
& style
, wxRichTextAttr
* compareWith
)
10428 return destStyle
.Apply(style
, compareWith
);
10431 // Remove attributes
10432 bool wxRichTextRemoveStyle(wxRichTextAttr
& destStyle
, const wxRichTextAttr
& style
)
10434 return destStyle
.RemoveStyle(style
);
10437 /// Combine two bitlists, specifying the bits of interest with separate flags.
10438 bool wxRichTextCombineBitlists(int& valueA
, int valueB
, int& flagsA
, int flagsB
)
10440 return wxRichTextAttr::CombineBitlists(valueA
, valueB
, flagsA
, flagsB
);
10443 /// Compare two bitlists
10444 bool wxRichTextBitlistsEqPartial(int valueA
, int valueB
, int flags
)
10446 return wxRichTextAttr::BitlistsEqPartial(valueA
, valueB
, flags
);
10449 /// Split into paragraph and character styles
10450 bool wxRichTextSplitParaCharStyles(const wxRichTextAttr
& style
, wxRichTextAttr
& parStyle
, wxRichTextAttr
& charStyle
)
10452 return wxRichTextAttr::SplitParaCharStyles(style
, parStyle
, charStyle
);
10455 /// Convert a decimal to Roman numerals
10456 wxString
wxRichTextDecimalToRoman(long n
)
10458 static wxArrayInt decimalNumbers
;
10459 static wxArrayString romanNumbers
;
10464 decimalNumbers
.Clear();
10465 romanNumbers
.Clear();
10466 return wxEmptyString
;
10469 if (decimalNumbers
.GetCount() == 0)
10471 #define wxRichTextAddDecRom(n, r) decimalNumbers.Add(n); romanNumbers.Add(r);
10473 wxRichTextAddDecRom(1000, wxT("M"));
10474 wxRichTextAddDecRom(900, wxT("CM"));
10475 wxRichTextAddDecRom(500, wxT("D"));
10476 wxRichTextAddDecRom(400, wxT("CD"));
10477 wxRichTextAddDecRom(100, wxT("C"));
10478 wxRichTextAddDecRom(90, wxT("XC"));
10479 wxRichTextAddDecRom(50, wxT("L"));
10480 wxRichTextAddDecRom(40, wxT("XL"));
10481 wxRichTextAddDecRom(10, wxT("X"));
10482 wxRichTextAddDecRom(9, wxT("IX"));
10483 wxRichTextAddDecRom(5, wxT("V"));
10484 wxRichTextAddDecRom(4, wxT("IV"));
10485 wxRichTextAddDecRom(1, wxT("I"));
10491 while (n
> 0 && i
< 13)
10493 if (n
>= decimalNumbers
[i
])
10495 n
-= decimalNumbers
[i
];
10496 roman
+= romanNumbers
[i
];
10503 if (roman
.IsEmpty())
10509 * wxRichTextFileHandler
10510 * Base class for file handlers
10513 IMPLEMENT_CLASS(wxRichTextFileHandler
, wxObject
)
10515 #if wxUSE_FFILE && wxUSE_STREAMS
10516 bool wxRichTextFileHandler::LoadFile(wxRichTextBuffer
*buffer
, const wxString
& filename
)
10518 wxFFileInputStream
stream(filename
);
10520 return LoadFile(buffer
, stream
);
10525 bool wxRichTextFileHandler::SaveFile(wxRichTextBuffer
*buffer
, const wxString
& filename
)
10527 wxFFileOutputStream
stream(filename
);
10529 return SaveFile(buffer
, stream
);
10533 #endif // wxUSE_FFILE && wxUSE_STREAMS
10535 /// Can we handle this filename (if using files)? By default, checks the extension.
10536 bool wxRichTextFileHandler::CanHandle(const wxString
& filename
) const
10538 wxString path
, file
, ext
;
10539 wxFileName::SplitPath(filename
, & path
, & file
, & ext
);
10541 return (ext
.Lower() == GetExtension());
10545 * wxRichTextTextHandler
10546 * Plain text handler
10549 IMPLEMENT_CLASS(wxRichTextPlainTextHandler
, wxRichTextFileHandler
)
10552 bool wxRichTextPlainTextHandler::DoLoadFile(wxRichTextBuffer
*buffer
, wxInputStream
& stream
)
10554 if (!stream
.IsOk())
10560 while (!stream
.Eof())
10562 int ch
= stream
.GetC();
10566 if (ch
== 10 && lastCh
!= 13)
10569 if (ch
> 0 && ch
!= 10)
10576 buffer
->ResetAndClearCommands();
10578 buffer
->AddParagraphs(str
);
10579 buffer
->UpdateRanges();
10584 bool wxRichTextPlainTextHandler::DoSaveFile(wxRichTextBuffer
*buffer
, wxOutputStream
& stream
)
10586 if (!stream
.IsOk())
10589 wxString text
= buffer
->GetText();
10591 wxString newLine
= wxRichTextLineBreakChar
;
10592 text
.Replace(newLine
, wxT("\n"));
10594 wxCharBuffer buf
= text
.ToAscii();
10596 stream
.Write((const char*) buf
, text
.length());
10599 #endif // wxUSE_STREAMS
10602 * Stores information about an image, in binary in-memory form
10605 wxRichTextImageBlock::wxRichTextImageBlock()
10610 wxRichTextImageBlock::wxRichTextImageBlock(const wxRichTextImageBlock
& block
):wxObject()
10616 wxRichTextImageBlock::~wxRichTextImageBlock()
10621 void wxRichTextImageBlock::Init()
10625 m_imageType
= wxBITMAP_TYPE_INVALID
;
10628 void wxRichTextImageBlock::Clear()
10632 m_imageType
= wxBITMAP_TYPE_INVALID
;
10636 // Load the original image into a memory block.
10637 // If the image is not a JPEG, we must convert it into a JPEG
10638 // to conserve space.
10639 // If it's not a JPEG we can make use of 'image', already scaled, so we don't have to
10640 // load the image a 2nd time.
10642 bool wxRichTextImageBlock::MakeImageBlock(const wxString
& filename
, wxBitmapType imageType
,
10643 wxImage
& image
, bool convertToJPEG
)
10645 m_imageType
= imageType
;
10647 wxString
filenameToRead(filename
);
10648 bool removeFile
= false;
10650 if (imageType
== wxBITMAP_TYPE_INVALID
)
10651 return false; // Could not determine image type
10653 if ((imageType
!= wxBITMAP_TYPE_JPEG
) && convertToJPEG
)
10655 wxString tempFile
=
10656 wxFileName::CreateTempFileName(_("image"));
10658 wxASSERT(!tempFile
.IsEmpty());
10660 image
.SaveFile(tempFile
, wxBITMAP_TYPE_JPEG
);
10661 filenameToRead
= tempFile
;
10664 m_imageType
= wxBITMAP_TYPE_JPEG
;
10667 if (!file
.Open(filenameToRead
))
10670 m_dataSize
= (size_t) file
.Length();
10675 m_data
= ReadBlock(filenameToRead
, m_dataSize
);
10678 wxRemoveFile(filenameToRead
);
10680 return (m_data
!= NULL
);
10683 // Make an image block from the wxImage in the given
10685 bool wxRichTextImageBlock::MakeImageBlock(wxImage
& image
, wxBitmapType imageType
, int quality
)
10687 image
.SetOption(wxT("quality"), quality
);
10689 if (imageType
== wxBITMAP_TYPE_INVALID
)
10690 return false; // Could not determine image type
10692 return DoMakeImageBlock(image
, imageType
);
10695 // Uses a const wxImage for efficiency, but can't set quality (only relevant for JPEG)
10696 bool wxRichTextImageBlock::MakeImageBlockDefaultQuality(const wxImage
& image
, wxBitmapType imageType
)
10698 if (imageType
== wxBITMAP_TYPE_INVALID
)
10699 return false; // Could not determine image type
10701 return DoMakeImageBlock(image
, imageType
);
10704 // Makes the image block
10705 bool wxRichTextImageBlock::DoMakeImageBlock(const wxImage
& image
, wxBitmapType imageType
)
10707 wxMemoryOutputStream memStream
;
10708 if (!image
.SaveFile(memStream
, imageType
))
10713 unsigned char* block
= new unsigned char[memStream
.GetSize()];
10721 m_imageType
= imageType
;
10722 m_dataSize
= memStream
.GetSize();
10724 memStream
.CopyTo(m_data
, m_dataSize
);
10726 return (m_data
!= NULL
);
10730 bool wxRichTextImageBlock::Write(const wxString
& filename
)
10732 return WriteBlock(filename
, m_data
, m_dataSize
);
10735 void wxRichTextImageBlock::Copy(const wxRichTextImageBlock
& block
)
10737 m_imageType
= block
.m_imageType
;
10739 m_dataSize
= block
.m_dataSize
;
10740 if (m_dataSize
== 0)
10743 m_data
= new unsigned char[m_dataSize
];
10745 for (i
= 0; i
< m_dataSize
; i
++)
10746 m_data
[i
] = block
.m_data
[i
];
10750 void wxRichTextImageBlock::operator=(const wxRichTextImageBlock
& block
)
10755 // Load a wxImage from the block
10756 bool wxRichTextImageBlock::Load(wxImage
& image
)
10761 // Read in the image.
10763 wxMemoryInputStream
mstream(m_data
, m_dataSize
);
10764 bool success
= image
.LoadFile(mstream
, GetImageType());
10766 wxString tempFile
= wxFileName::CreateTempFileName(_("image"));
10767 wxASSERT(!tempFile
.IsEmpty());
10769 if (!WriteBlock(tempFile
, m_data
, m_dataSize
))
10773 success
= image
.LoadFile(tempFile
, GetImageType());
10774 wxRemoveFile(tempFile
);
10780 // Write data in hex to a stream
10781 bool wxRichTextImageBlock::WriteHex(wxOutputStream
& stream
)
10783 if (m_dataSize
== 0)
10786 int bufSize
= 100000;
10787 if (int(2*m_dataSize
) < bufSize
)
10788 bufSize
= 2*m_dataSize
;
10789 char* buf
= new char[bufSize
+1];
10791 int left
= m_dataSize
;
10796 if (left
*2 > bufSize
)
10798 n
= bufSize
; left
-= (bufSize
/2);
10802 n
= left
*2; left
= 0;
10806 for (i
= 0; i
< (n
/2); i
++)
10808 wxDecToHex(m_data
[j
], b
, b
+1);
10813 stream
.Write((const char*) buf
, n
);
10819 // Read data in hex from a stream
10820 bool wxRichTextImageBlock::ReadHex(wxInputStream
& stream
, int length
, wxBitmapType imageType
)
10822 int dataSize
= length
/2;
10827 // create a null terminated temporary string:
10831 m_data
= new unsigned char[dataSize
];
10833 for (i
= 0; i
< dataSize
; i
++)
10835 str
[0] = (char)stream
.GetC();
10836 str
[1] = (char)stream
.GetC();
10838 m_data
[i
] = (unsigned char)wxHexToDec(str
);
10841 m_dataSize
= dataSize
;
10842 m_imageType
= imageType
;
10847 // Allocate and read from stream as a block of memory
10848 unsigned char* wxRichTextImageBlock::ReadBlock(wxInputStream
& stream
, size_t size
)
10850 unsigned char* block
= new unsigned char[size
];
10854 stream
.Read(block
, size
);
10859 unsigned char* wxRichTextImageBlock::ReadBlock(const wxString
& filename
, size_t size
)
10861 wxFileInputStream
stream(filename
);
10862 if (!stream
.IsOk())
10865 return ReadBlock(stream
, size
);
10868 // Write memory block to stream
10869 bool wxRichTextImageBlock::WriteBlock(wxOutputStream
& stream
, unsigned char* block
, size_t size
)
10871 stream
.Write((void*) block
, size
);
10872 return stream
.IsOk();
10876 // Write memory block to file
10877 bool wxRichTextImageBlock::WriteBlock(const wxString
& filename
, unsigned char* block
, size_t size
)
10879 wxFileOutputStream
outStream(filename
);
10880 if (!outStream
.IsOk())
10883 return WriteBlock(outStream
, block
, size
);
10886 // Gets the extension for the block's type
10887 wxString
wxRichTextImageBlock::GetExtension() const
10889 wxImageHandler
* handler
= wxImage::FindHandler(GetImageType());
10891 return handler
->GetExtension();
10893 return wxEmptyString
;
10899 * The data object for a wxRichTextBuffer
10902 const wxChar
*wxRichTextBufferDataObject::ms_richTextBufferFormatId
= wxT("wxShape");
10904 wxRichTextBufferDataObject::wxRichTextBufferDataObject(wxRichTextBuffer
* richTextBuffer
)
10906 m_richTextBuffer
= richTextBuffer
;
10908 // this string should uniquely identify our format, but is otherwise
10910 m_formatRichTextBuffer
.SetId(GetRichTextBufferFormatId());
10912 SetFormat(m_formatRichTextBuffer
);
10915 wxRichTextBufferDataObject::~wxRichTextBufferDataObject()
10917 delete m_richTextBuffer
;
10920 // after a call to this function, the richTextBuffer is owned by the caller and it
10921 // is responsible for deleting it!
10922 wxRichTextBuffer
* wxRichTextBufferDataObject::GetRichTextBuffer()
10924 wxRichTextBuffer
* richTextBuffer
= m_richTextBuffer
;
10925 m_richTextBuffer
= NULL
;
10927 return richTextBuffer
;
10930 wxDataFormat
wxRichTextBufferDataObject::GetPreferredFormat(Direction
WXUNUSED(dir
)) const
10932 return m_formatRichTextBuffer
;
10935 size_t wxRichTextBufferDataObject::GetDataSize() const
10937 if (!m_richTextBuffer
)
10943 wxStringOutputStream
stream(& bufXML
);
10944 if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
))
10946 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
10952 wxCharBuffer buffer
= bufXML
.mb_str(wxConvUTF8
);
10953 return strlen(buffer
) + 1;
10955 return bufXML
.Length()+1;
10959 bool wxRichTextBufferDataObject::GetDataHere(void *pBuf
) const
10961 if (!pBuf
|| !m_richTextBuffer
)
10967 wxStringOutputStream
stream(& bufXML
);
10968 if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
))
10970 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
10976 wxCharBuffer buffer
= bufXML
.mb_str(wxConvUTF8
);
10977 size_t len
= strlen(buffer
);
10978 memcpy((char*) pBuf
, (const char*) buffer
, len
);
10979 ((char*) pBuf
)[len
] = 0;
10981 size_t len
= bufXML
.Length();
10982 memcpy((char*) pBuf
, (const char*) bufXML
.c_str(), len
);
10983 ((char*) pBuf
)[len
] = 0;
10989 bool wxRichTextBufferDataObject::SetData(size_t WXUNUSED(len
), const void *buf
)
10991 wxDELETE(m_richTextBuffer
);
10993 wxString
bufXML((const char*) buf
, wxConvUTF8
);
10995 m_richTextBuffer
= new wxRichTextBuffer
;
10997 wxStringInputStream
stream(bufXML
);
10998 if (!m_richTextBuffer
->LoadFile(stream
, wxRICHTEXT_TYPE_XML
))
11000 wxLogError(wxT("Could not read the buffer from an XML stream.\nYou may have forgotten to add the XML file handler."));
11002 wxDELETE(m_richTextBuffer
);
11014 * wxRichTextFontTable
11015 * Manages quick access to a pool of fonts for rendering rich text
11018 WX_DECLARE_STRING_HASH_MAP_WITH_DECL(wxFont
, wxRichTextFontTableHashMap
, class WXDLLIMPEXP_RICHTEXT
);
11020 class wxRichTextFontTableData
: public wxObjectRefData
11023 wxRichTextFontTableData() {}
11025 wxFont
FindFont(const wxRichTextAttr
& fontSpec
);
11027 wxRichTextFontTableHashMap m_hashMap
;
11030 wxFont
wxRichTextFontTableData::FindFont(const wxRichTextAttr
& fontSpec
)
11032 wxString
facename(fontSpec
.GetFontFaceName());
11033 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()));
11034 wxRichTextFontTableHashMap::iterator entry
= m_hashMap
.find(spec
);
11036 if ( entry
== m_hashMap
.end() )
11038 wxFont
font(fontSpec
.GetFontSize(), wxDEFAULT
, fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), fontSpec
.GetFontUnderlined(), facename
.c_str());
11039 m_hashMap
[spec
] = font
;
11044 return entry
->second
;
11048 IMPLEMENT_DYNAMIC_CLASS(wxRichTextFontTable
, wxObject
)
11050 wxRichTextFontTable::wxRichTextFontTable()
11052 m_refData
= new wxRichTextFontTableData
;
11055 wxRichTextFontTable::wxRichTextFontTable(const wxRichTextFontTable
& table
)
11061 wxRichTextFontTable::~wxRichTextFontTable()
11066 bool wxRichTextFontTable::operator == (const wxRichTextFontTable
& table
) const
11068 return (m_refData
== table
.m_refData
);
11071 void wxRichTextFontTable::operator= (const wxRichTextFontTable
& table
)
11076 wxFont
wxRichTextFontTable::FindFont(const wxRichTextAttr
& fontSpec
)
11078 wxRichTextFontTableData
* data
= (wxRichTextFontTableData
*) m_refData
;
11080 return data
->FindFont(fontSpec
);
11085 void wxRichTextFontTable::Clear()
11087 wxRichTextFontTableData
* data
= (wxRichTextFontTableData
*) m_refData
;
11089 data
->m_hashMap
.clear();
11095 void wxTextBoxAttr::Reset()
11098 m_floatMode
= wxTEXT_BOX_ATTR_FLOAT_NONE
;
11099 m_clearMode
= wxTEXT_BOX_ATTR_CLEAR_NONE
;
11100 m_collapseMode
= wxTEXT_BOX_ATTR_COLLAPSE_NONE
;
11101 m_verticalAlignment
= wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_NONE
;
11102 m_boxStyleName
= wxEmptyString
;
11106 m_position
.Reset();
11117 bool wxTextBoxAttr::operator== (const wxTextBoxAttr
& attr
) const
11120 m_flags
== attr
.m_flags
&&
11121 m_floatMode
== attr
.m_floatMode
&&
11122 m_clearMode
== attr
.m_clearMode
&&
11123 m_collapseMode
== attr
.m_collapseMode
&&
11124 m_verticalAlignment
== attr
.m_verticalAlignment
&&
11126 m_margins
== attr
.m_margins
&&
11127 m_padding
== attr
.m_padding
&&
11128 m_position
== attr
.m_position
&&
11130 m_size
== attr
.m_size
&&
11131 m_minSize
== attr
.m_minSize
&&
11132 m_maxSize
== attr
.m_maxSize
&&
11134 m_border
== attr
.m_border
&&
11135 m_outline
== attr
.m_outline
&&
11137 m_boxStyleName
== attr
.m_boxStyleName
11141 // Partial equality test
11142 bool wxTextBoxAttr::EqPartial(const wxTextBoxAttr
& attr
) const
11144 if (attr
.HasFloatMode() && HasFloatMode() && (GetFloatMode() != attr
.GetFloatMode()))
11147 if (attr
.HasClearMode() && HasClearMode() && (GetClearMode() != attr
.GetClearMode()))
11150 if (attr
.HasCollapseBorders() && HasCollapseBorders() && (attr
.GetCollapseBorders() != GetCollapseBorders()))
11153 if (attr
.HasVerticalAlignment() && HasVerticalAlignment() && (attr
.GetVerticalAlignment() != GetVerticalAlignment()))
11156 if (attr
.HasBoxStyleName() && HasBoxStyleName() && (attr
.GetBoxStyleName() != GetBoxStyleName()))
11161 if (!m_position
.EqPartial(attr
.m_position
))
11166 if (!m_size
.EqPartial(attr
.m_size
))
11168 if (!m_minSize
.EqPartial(attr
.m_minSize
))
11170 if (!m_maxSize
.EqPartial(attr
.m_maxSize
))
11175 if (!m_margins
.EqPartial(attr
.m_margins
))
11180 if (!m_padding
.EqPartial(attr
.m_padding
))
11185 if (!GetBorder().EqPartial(attr
.GetBorder()))
11190 if (!GetOutline().EqPartial(attr
.GetOutline()))
11196 // Merges the given attributes. If compareWith
11197 // is non-NULL, then it will be used to mask out those attributes that are the same in style
11198 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
11199 bool wxTextBoxAttr::Apply(const wxTextBoxAttr
& attr
, const wxTextBoxAttr
* compareWith
)
11201 if (attr
.HasFloatMode())
11203 if (!(compareWith
&& compareWith
->HasFloatMode() && compareWith
->GetFloatMode() == attr
.GetFloatMode()))
11204 SetFloatMode(attr
.GetFloatMode());
11207 if (attr
.HasClearMode())
11209 if (!(compareWith
&& compareWith
->HasClearMode() && compareWith
->GetClearMode() == attr
.GetClearMode()))
11210 SetClearMode(attr
.GetClearMode());
11213 if (attr
.HasCollapseBorders())
11215 if (!(compareWith
&& compareWith
->HasCollapseBorders() && compareWith
->GetCollapseBorders() == attr
.GetCollapseBorders()))
11216 SetCollapseBorders(attr
.GetCollapseBorders());
11219 if (attr
.HasVerticalAlignment())
11221 if (!(compareWith
&& compareWith
->HasVerticalAlignment() && compareWith
->GetVerticalAlignment() == attr
.GetVerticalAlignment()))
11222 SetVerticalAlignment(attr
.GetVerticalAlignment());
11225 if (attr
.HasBoxStyleName())
11227 if (!(compareWith
&& compareWith
->HasBoxStyleName() && compareWith
->GetBoxStyleName() == attr
.GetBoxStyleName()))
11228 SetBoxStyleName(attr
.GetBoxStyleName());
11231 m_margins
.Apply(attr
.m_margins
, compareWith
? (& attr
.m_margins
) : (const wxTextAttrDimensions
*) NULL
);
11232 m_padding
.Apply(attr
.m_padding
, compareWith
? (& attr
.m_padding
) : (const wxTextAttrDimensions
*) NULL
);
11233 m_position
.Apply(attr
.m_position
, compareWith
? (& attr
.m_position
) : (const wxTextAttrDimensions
*) NULL
);
11235 m_size
.Apply(attr
.m_size
, compareWith
? (& attr
.m_size
) : (const wxTextAttrSize
*) NULL
);
11236 m_minSize
.Apply(attr
.m_minSize
, compareWith
? (& attr
.m_minSize
) : (const wxTextAttrSize
*) NULL
);
11237 m_maxSize
.Apply(attr
.m_maxSize
, compareWith
? (& attr
.m_maxSize
) : (const wxTextAttrSize
*) NULL
);
11239 m_border
.Apply(attr
.m_border
, compareWith
? (& attr
.m_border
) : (const wxTextAttrBorders
*) NULL
);
11240 m_outline
.Apply(attr
.m_outline
, compareWith
? (& attr
.m_outline
) : (const wxTextAttrBorders
*) NULL
);
11245 // Remove specified attributes from this object
11246 bool wxTextBoxAttr::RemoveStyle(const wxTextBoxAttr
& attr
)
11248 if (attr
.HasFloatMode())
11249 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT
);
11251 if (attr
.HasClearMode())
11252 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR
);
11254 if (attr
.HasCollapseBorders())
11255 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
11257 if (attr
.HasVerticalAlignment())
11258 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
11260 if (attr
.HasBoxStyleName())
11262 SetBoxStyleName(wxEmptyString
);
11263 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
11266 m_margins
.RemoveStyle(attr
.m_margins
);
11267 m_padding
.RemoveStyle(attr
.m_padding
);
11268 m_position
.RemoveStyle(attr
.m_position
);
11270 m_size
.RemoveStyle(attr
.m_size
);
11271 m_minSize
.RemoveStyle(attr
.m_minSize
);
11272 m_maxSize
.RemoveStyle(attr
.m_maxSize
);
11274 m_border
.RemoveStyle(attr
.m_border
);
11275 m_outline
.RemoveStyle(attr
.m_outline
);
11280 // Collects the attributes that are common to a range of content, building up a note of
11281 // which attributes are absent in some objects and which clash in some objects.
11282 void wxTextBoxAttr::CollectCommonAttributes(const wxTextBoxAttr
& attr
, wxTextBoxAttr
& clashingAttr
, wxTextBoxAttr
& absentAttr
)
11284 if (attr
.HasFloatMode())
11286 if (!clashingAttr
.HasFloatMode() && !absentAttr
.HasFloatMode())
11288 if (HasFloatMode())
11290 if (GetFloatMode() != attr
.GetFloatMode())
11292 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_FLOAT
);
11293 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT
);
11297 SetFloatMode(attr
.GetFloatMode());
11301 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_FLOAT
);
11303 if (attr
.HasClearMode())
11305 if (!clashingAttr
.HasClearMode() && !absentAttr
.HasClearMode())
11307 if (HasClearMode())
11309 if (GetClearMode() != attr
.GetClearMode())
11311 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_CLEAR
);
11312 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR
);
11316 SetClearMode(attr
.GetClearMode());
11320 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_CLEAR
);
11322 if (attr
.HasCollapseBorders())
11324 if (!clashingAttr
.HasCollapseBorders() && !absentAttr
.HasCollapseBorders())
11326 if (HasCollapseBorders())
11328 if (GetCollapseBorders() != attr
.GetCollapseBorders())
11330 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
11331 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
11335 SetCollapseBorders(attr
.GetCollapseBorders());
11339 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
11341 if (attr
.HasVerticalAlignment())
11343 if (!clashingAttr
.HasVerticalAlignment() && !absentAttr
.HasVerticalAlignment())
11345 if (HasVerticalAlignment())
11347 if (GetVerticalAlignment() != attr
.GetVerticalAlignment())
11349 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
11350 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
11354 SetVerticalAlignment(attr
.GetVerticalAlignment());
11358 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
11360 if (attr
.HasBoxStyleName())
11362 if (!clashingAttr
.HasBoxStyleName() && !absentAttr
.HasBoxStyleName())
11364 if (HasBoxStyleName())
11366 if (GetBoxStyleName() != attr
.GetBoxStyleName())
11368 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
11369 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
11373 SetBoxStyleName(attr
.GetBoxStyleName());
11377 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
11379 m_margins
.CollectCommonAttributes(attr
.m_margins
, clashingAttr
.m_margins
, absentAttr
.m_margins
);
11380 m_padding
.CollectCommonAttributes(attr
.m_padding
, clashingAttr
.m_padding
, absentAttr
.m_padding
);
11381 m_position
.CollectCommonAttributes(attr
.m_position
, clashingAttr
.m_position
, absentAttr
.m_position
);
11383 m_size
.CollectCommonAttributes(attr
.m_size
, clashingAttr
.m_size
, absentAttr
.m_size
);
11384 m_minSize
.CollectCommonAttributes(attr
.m_minSize
, clashingAttr
.m_minSize
, absentAttr
.m_minSize
);
11385 m_maxSize
.CollectCommonAttributes(attr
.m_maxSize
, clashingAttr
.m_maxSize
, absentAttr
.m_maxSize
);
11387 m_border
.CollectCommonAttributes(attr
.m_border
, clashingAttr
.m_border
, absentAttr
.m_border
);
11388 m_outline
.CollectCommonAttributes(attr
.m_outline
, clashingAttr
.m_outline
, absentAttr
.m_outline
);
11391 bool wxTextBoxAttr::IsDefault() const
11393 return GetFlags() == 0 && !m_border
.IsValid() && !m_outline
.IsValid() &&
11394 !m_size
.IsValid() && !m_minSize
.IsValid() && !m_maxSize
.IsValid() &&
11395 !m_position
.IsValid() && !m_padding
.IsValid() && !m_margins
.IsValid();
11400 void wxRichTextAttr::Copy(const wxRichTextAttr
& attr
)
11402 wxTextAttr::Copy(attr
);
11404 m_textBoxAttr
= attr
.m_textBoxAttr
;
11407 bool wxRichTextAttr::operator==(const wxRichTextAttr
& attr
) const
11409 if (!(wxTextAttr::operator==(attr
)))
11412 return (m_textBoxAttr
== attr
.m_textBoxAttr
);
11415 // Partial equality test taking comparison object into account
11416 bool wxRichTextAttr::EqPartial(const wxRichTextAttr
& attr
) const
11418 if (!(wxTextAttr::EqPartial(attr
)))
11421 return m_textBoxAttr
.EqPartial(attr
.m_textBoxAttr
);
11424 // Merges the given attributes. If compareWith
11425 // is non-NULL, then it will be used to mask out those attributes that are the same in style
11426 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
11427 bool wxRichTextAttr::Apply(const wxRichTextAttr
& style
, const wxRichTextAttr
* compareWith
)
11429 wxTextAttr::Apply(style
, compareWith
);
11431 return m_textBoxAttr
.Apply(style
.m_textBoxAttr
, compareWith
? (& compareWith
->m_textBoxAttr
) : (const wxTextBoxAttr
*) NULL
);
11434 // Remove specified attributes from this object
11435 bool wxRichTextAttr::RemoveStyle(const wxRichTextAttr
& attr
)
11437 wxTextAttr::RemoveStyle(*this, attr
);
11439 return m_textBoxAttr
.RemoveStyle(attr
.m_textBoxAttr
);
11442 // Collects the attributes that are common to a range of content, building up a note of
11443 // which attributes are absent in some objects and which clash in some objects.
11444 void wxRichTextAttr::CollectCommonAttributes(const wxRichTextAttr
& attr
, wxRichTextAttr
& clashingAttr
, wxRichTextAttr
& absentAttr
)
11446 wxTextAttrCollectCommonAttributes(*this, attr
, clashingAttr
, absentAttr
);
11448 m_textBoxAttr
.CollectCommonAttributes(attr
.m_textBoxAttr
, clashingAttr
.m_textBoxAttr
, absentAttr
.m_textBoxAttr
);
11451 // Partial equality test
11452 bool wxTextAttrBorder::EqPartial(const wxTextAttrBorder
& border
) const
11454 if (border
.HasStyle() && !HasStyle() && (border
.GetStyle() != GetStyle()))
11457 if (border
.HasColour() && !HasColour() && (border
.GetColourLong() != GetColourLong()))
11460 if (border
.HasWidth() && !HasWidth() && !(border
.GetWidth() == GetWidth()))
11466 // Apply border to 'this', but not if the same as compareWith
11467 bool wxTextAttrBorder::Apply(const wxTextAttrBorder
& border
, const wxTextAttrBorder
* compareWith
)
11469 if (border
.HasStyle())
11471 if (!(compareWith
&& (border
.GetStyle() == compareWith
->GetStyle())))
11472 SetStyle(border
.GetStyle());
11474 if (border
.HasColour())
11476 if (!(compareWith
&& (border
.GetColourLong() == compareWith
->GetColourLong())))
11477 SetColour(border
.GetColourLong());
11479 if (border
.HasWidth())
11481 if (!(compareWith
&& (border
.GetWidth() == compareWith
->GetWidth())))
11482 SetWidth(border
.GetWidth());
11488 // Remove specified attributes from this object
11489 bool wxTextAttrBorder::RemoveStyle(const wxTextAttrBorder
& attr
)
11491 if (attr
.HasStyle() && HasStyle())
11492 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_STYLE
);
11493 if (attr
.HasColour() && HasColour())
11494 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_COLOUR
);
11495 if (attr
.HasWidth() && HasWidth())
11496 m_borderWidth
.Reset();
11501 // Collects the attributes that are common to a range of content, building up a note of
11502 // which attributes are absent in some objects and which clash in some objects.
11503 void wxTextAttrBorder::CollectCommonAttributes(const wxTextAttrBorder
& attr
, wxTextAttrBorder
& clashingAttr
, wxTextAttrBorder
& absentAttr
)
11505 if (attr
.HasStyle())
11507 if (!clashingAttr
.HasStyle() && !absentAttr
.HasStyle())
11511 if (GetStyle() != attr
.GetStyle())
11513 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
11514 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
11518 SetStyle(attr
.GetStyle());
11522 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
11524 if (attr
.HasColour())
11526 if (!clashingAttr
.HasColour() && !absentAttr
.HasColour())
11530 if (GetColour() != attr
.GetColour())
11532 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
11533 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
11537 SetColour(attr
.GetColourLong());
11541 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
11543 m_borderWidth
.CollectCommonAttributes(attr
.m_borderWidth
, clashingAttr
.m_borderWidth
, absentAttr
.m_borderWidth
);
11546 // Partial equality test
11547 bool wxTextAttrBorders::EqPartial(const wxTextAttrBorders
& borders
) const
11549 return m_left
.EqPartial(borders
.m_left
) && m_right
.EqPartial(borders
.m_right
) &&
11550 m_top
.EqPartial(borders
.m_top
) && m_bottom
.EqPartial(borders
.m_bottom
);
11553 // Apply border to 'this', but not if the same as compareWith
11554 bool wxTextAttrBorders::Apply(const wxTextAttrBorders
& borders
, const wxTextAttrBorders
* compareWith
)
11556 m_left
.Apply(borders
.m_left
, compareWith
? (& compareWith
->m_left
) : (const wxTextAttrBorder
*) NULL
);
11557 m_right
.Apply(borders
.m_right
, compareWith
? (& compareWith
->m_right
) : (const wxTextAttrBorder
*) NULL
);
11558 m_top
.Apply(borders
.m_top
, compareWith
? (& compareWith
->m_top
) : (const wxTextAttrBorder
*) NULL
);
11559 m_bottom
.Apply(borders
.m_bottom
, compareWith
? (& compareWith
->m_bottom
) : (const wxTextAttrBorder
*) NULL
);
11563 // Remove specified attributes from this object
11564 bool wxTextAttrBorders::RemoveStyle(const wxTextAttrBorders
& attr
)
11566 m_left
.RemoveStyle(attr
.m_left
);
11567 m_right
.RemoveStyle(attr
.m_right
);
11568 m_top
.RemoveStyle(attr
.m_top
);
11569 m_bottom
.RemoveStyle(attr
.m_bottom
);
11573 // Collects the attributes that are common to a range of content, building up a note of
11574 // which attributes are absent in some objects and which clash in some objects.
11575 void wxTextAttrBorders::CollectCommonAttributes(const wxTextAttrBorders
& attr
, wxTextAttrBorders
& clashingAttr
, wxTextAttrBorders
& absentAttr
)
11577 m_left
.CollectCommonAttributes(attr
.m_left
, clashingAttr
.m_left
, absentAttr
.m_left
);
11578 m_right
.CollectCommonAttributes(attr
.m_right
, clashingAttr
.m_right
, absentAttr
.m_right
);
11579 m_top
.CollectCommonAttributes(attr
.m_top
, clashingAttr
.m_top
, absentAttr
.m_top
);
11580 m_bottom
.CollectCommonAttributes(attr
.m_bottom
, clashingAttr
.m_bottom
, absentAttr
.m_bottom
);
11583 // Set style of all borders
11584 void wxTextAttrBorders::SetStyle(int style
)
11586 m_left
.SetStyle(style
);
11587 m_right
.SetStyle(style
);
11588 m_top
.SetStyle(style
);
11589 m_bottom
.SetStyle(style
);
11592 // Set colour of all borders
11593 void wxTextAttrBorders::SetColour(unsigned long colour
)
11595 m_left
.SetColour(colour
);
11596 m_right
.SetColour(colour
);
11597 m_top
.SetColour(colour
);
11598 m_bottom
.SetColour(colour
);
11601 void wxTextAttrBorders::SetColour(const wxColour
& colour
)
11603 m_left
.SetColour(colour
);
11604 m_right
.SetColour(colour
);
11605 m_top
.SetColour(colour
);
11606 m_bottom
.SetColour(colour
);
11609 // Set width of all borders
11610 void wxTextAttrBorders::SetWidth(const wxTextAttrDimension
& width
)
11612 m_left
.SetWidth(width
);
11613 m_right
.SetWidth(width
);
11614 m_top
.SetWidth(width
);
11615 m_bottom
.SetWidth(width
);
11618 // Partial equality test
11619 bool wxTextAttrDimension::EqPartial(const wxTextAttrDimension
& dim
) const
11621 if (dim
.IsValid() && IsValid() && !((*this) == dim
))
11627 bool wxTextAttrDimension::Apply(const wxTextAttrDimension
& dim
, const wxTextAttrDimension
* compareWith
)
11631 if (!(compareWith
&& dim
== (*compareWith
)))
11638 // Collects the attributes that are common to a range of content, building up a note of
11639 // which attributes are absent in some objects and which clash in some objects.
11640 void wxTextAttrDimension::CollectCommonAttributes(const wxTextAttrDimension
& attr
, wxTextAttrDimension
& clashingAttr
, wxTextAttrDimension
& absentAttr
)
11642 if (attr
.IsValid())
11644 if (!clashingAttr
.IsValid() && !absentAttr
.IsValid())
11648 if (!((*this) == attr
))
11650 clashingAttr
.SetValid(true);
11659 absentAttr
.SetValid(true);
11662 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(wxDC
& dc
, double scale
, const wxSize
& parentSize
)
11664 m_ppi
= dc
.GetPPI().x
; m_scale
= scale
; m_parentSize
= parentSize
;
11667 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(int ppi
, double scale
, const wxSize
& parentSize
)
11669 m_ppi
= ppi
; m_scale
= scale
; m_parentSize
= parentSize
;
11672 int wxTextAttrDimensionConverter::ConvertTenthsMMToPixels(int units
) const
11674 return wxRichTextObject::ConvertTenthsMMToPixels(m_ppi
, units
, m_scale
);
11677 int wxTextAttrDimensionConverter::ConvertPixelsToTenthsMM(int pixels
) const
11679 return wxRichTextObject::ConvertPixelsToTenthsMM(m_ppi
, pixels
, m_scale
);
11682 int wxTextAttrDimensionConverter::GetPixels(const wxTextAttrDimension
& dim
, int direction
) const
11684 if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
11685 return ConvertTenthsMMToPixels(dim
.GetValue());
11686 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
11687 return dim
.GetValue();
11688 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
11690 wxASSERT(m_parentSize
!= wxDefaultSize
);
11691 if (direction
== wxHORIZONTAL
)
11692 return (int) (double(m_parentSize
.x
) * double(dim
.GetValue()) / 100.0);
11694 return (int) (double(m_parentSize
.y
) * double(dim
.GetValue()) / 100.0);
11703 int wxTextAttrDimensionConverter::GetTenthsMM(const wxTextAttrDimension
& dim
) const
11705 if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
11706 return dim
.GetValue();
11707 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
11708 return ConvertPixelsToTenthsMM(dim
.GetValue());
11716 // Partial equality test
11717 bool wxTextAttrDimensions::EqPartial(const wxTextAttrDimensions
& dims
) const
11719 if (!m_left
.EqPartial(dims
.m_left
))
11722 if (!m_right
.EqPartial(dims
.m_right
))
11725 if (!m_top
.EqPartial(dims
.m_top
))
11728 if (!m_bottom
.EqPartial(dims
.m_bottom
))
11734 // Apply border to 'this', but not if the same as compareWith
11735 bool wxTextAttrDimensions::Apply(const wxTextAttrDimensions
& dims
, const wxTextAttrDimensions
* compareWith
)
11737 m_left
.Apply(dims
.m_left
, compareWith
? (& compareWith
->m_left
) : (const wxTextAttrDimension
*) NULL
);
11738 m_right
.Apply(dims
.m_right
, compareWith
? (& compareWith
->m_right
): (const wxTextAttrDimension
*) NULL
);
11739 m_top
.Apply(dims
.m_top
, compareWith
? (& compareWith
->m_top
): (const wxTextAttrDimension
*) NULL
);
11740 m_bottom
.Apply(dims
.m_bottom
, compareWith
? (& compareWith
->m_bottom
): (const wxTextAttrDimension
*) NULL
);
11745 // Remove specified attributes from this object
11746 bool wxTextAttrDimensions::RemoveStyle(const wxTextAttrDimensions
& attr
)
11748 if (attr
.m_left
.IsValid())
11750 if (attr
.m_right
.IsValid())
11752 if (attr
.m_top
.IsValid())
11754 if (attr
.m_bottom
.IsValid())
11760 // Collects the attributes that are common to a range of content, building up a note of
11761 // which attributes are absent in some objects and which clash in some objects.
11762 void wxTextAttrDimensions::CollectCommonAttributes(const wxTextAttrDimensions
& attr
, wxTextAttrDimensions
& clashingAttr
, wxTextAttrDimensions
& absentAttr
)
11764 m_left
.CollectCommonAttributes(attr
.m_left
, clashingAttr
.m_left
, absentAttr
.m_left
);
11765 m_right
.CollectCommonAttributes(attr
.m_right
, clashingAttr
.m_right
, absentAttr
.m_right
);
11766 m_top
.CollectCommonAttributes(attr
.m_top
, clashingAttr
.m_top
, absentAttr
.m_top
);
11767 m_bottom
.CollectCommonAttributes(attr
.m_bottom
, clashingAttr
.m_bottom
, absentAttr
.m_bottom
);
11770 // Partial equality test
11771 bool wxTextAttrSize::EqPartial(const wxTextAttrSize
& size
) const
11773 if (!m_width
.EqPartial(size
.m_width
))
11776 if (!m_height
.EqPartial(size
.m_height
))
11782 // Apply border to 'this', but not if the same as compareWith
11783 bool wxTextAttrSize::Apply(const wxTextAttrSize
& size
, const wxTextAttrSize
* compareWith
)
11785 m_width
.Apply(size
.m_width
, compareWith
? (& compareWith
->m_width
) : (const wxTextAttrDimension
*) NULL
);
11786 m_height
.Apply(size
.m_height
, compareWith
? (& compareWith
->m_height
): (const wxTextAttrDimension
*) NULL
);
11791 // Remove specified attributes from this object
11792 bool wxTextAttrSize::RemoveStyle(const wxTextAttrSize
& attr
)
11794 if (attr
.m_width
.IsValid())
11796 if (attr
.m_height
.IsValid())
11802 // Collects the attributes that are common to a range of content, building up a note of
11803 // which attributes are absent in some objects and which clash in some objects.
11804 void wxTextAttrSize::CollectCommonAttributes(const wxTextAttrSize
& attr
, wxTextAttrSize
& clashingAttr
, wxTextAttrSize
& absentAttr
)
11806 m_width
.CollectCommonAttributes(attr
.m_width
, clashingAttr
.m_width
, absentAttr
.m_width
);
11807 m_height
.CollectCommonAttributes(attr
.m_height
, clashingAttr
.m_height
, absentAttr
.m_height
);
11810 // Collects the attributes that are common to a range of content, building up a note of
11811 // which attributes are absent in some objects and which clash in some objects.
11812 void wxTextAttrCollectCommonAttributes(wxTextAttr
& currentStyle
, const wxTextAttr
& attr
, wxTextAttr
& clashingAttr
, wxTextAttr
& absentAttr
)
11814 absentAttr
.SetFlags(absentAttr
.GetFlags() | (~attr
.GetFlags() & wxTEXT_ATTR_ALL
));
11815 absentAttr
.SetTextEffectFlags(absentAttr
.GetTextEffectFlags() | (~attr
.GetTextEffectFlags() & 0xFFFF));
11817 long forbiddenFlags
= clashingAttr
.GetFlags()|absentAttr
.GetFlags();
11819 if (attr
.HasFont())
11821 if (attr
.HasFontSize() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_SIZE
))
11823 if (currentStyle
.HasFontSize())
11825 if (currentStyle
.GetFontSize() != attr
.GetFontSize())
11827 // Clash of attr - mark as such
11828 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_SIZE
);
11829 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_SIZE
);
11833 currentStyle
.SetFontSize(attr
.GetFontSize());
11836 if (attr
.HasFontItalic() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_ITALIC
))
11838 if (currentStyle
.HasFontItalic())
11840 if (currentStyle
.GetFontStyle() != attr
.GetFontStyle())
11842 // Clash of attr - mark as such
11843 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_ITALIC
);
11844 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC
);
11848 currentStyle
.SetFontStyle(attr
.GetFontStyle());
11851 if (attr
.HasFontFamily() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_FAMILY
))
11853 if (currentStyle
.HasFontFamily())
11855 if (currentStyle
.GetFontFamily() != attr
.GetFontFamily())
11857 // Clash of attr - mark as such
11858 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FAMILY
);
11859 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY
);
11863 currentStyle
.SetFontFamily(attr
.GetFontFamily());
11866 if (attr
.HasFontWeight() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_WEIGHT
))
11868 if (currentStyle
.HasFontWeight())
11870 if (currentStyle
.GetFontWeight() != attr
.GetFontWeight())
11872 // Clash of attr - mark as such
11873 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_WEIGHT
);
11874 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT
);
11878 currentStyle
.SetFontWeight(attr
.GetFontWeight());
11881 if (attr
.HasFontFaceName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_FACE
))
11883 if (currentStyle
.HasFontFaceName())
11885 wxString
faceName1(currentStyle
.GetFontFaceName());
11886 wxString
faceName2(attr
.GetFontFaceName());
11888 if (faceName1
!= faceName2
)
11890 // Clash of attr - mark as such
11891 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FACE
);
11892 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FACE
);
11896 currentStyle
.SetFontFaceName(attr
.GetFontFaceName());
11899 if (attr
.HasFontUnderlined() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_UNDERLINE
))
11901 if (currentStyle
.HasFontUnderlined())
11903 if (currentStyle
.GetFontUnderlined() != attr
.GetFontUnderlined())
11905 // Clash of attr - mark as such
11906 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
11907 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
11911 currentStyle
.SetFontUnderlined(attr
.GetFontUnderlined());
11915 if (attr
.HasTextColour() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_TEXT_COLOUR
))
11917 if (currentStyle
.HasTextColour())
11919 if (currentStyle
.GetTextColour() != attr
.GetTextColour())
11921 // Clash of attr - mark as such
11922 clashingAttr
.AddFlag(wxTEXT_ATTR_TEXT_COLOUR
);
11923 currentStyle
.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR
);
11927 currentStyle
.SetTextColour(attr
.GetTextColour());
11930 if (attr
.HasBackgroundColour() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BACKGROUND_COLOUR
))
11932 if (currentStyle
.HasBackgroundColour())
11934 if (currentStyle
.GetBackgroundColour() != attr
.GetBackgroundColour())
11936 // Clash of attr - mark as such
11937 clashingAttr
.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
11938 currentStyle
.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
11942 currentStyle
.SetBackgroundColour(attr
.GetBackgroundColour());
11945 if (attr
.HasAlignment() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_ALIGNMENT
))
11947 if (currentStyle
.HasAlignment())
11949 if (currentStyle
.GetAlignment() != attr
.GetAlignment())
11951 // Clash of attr - mark as such
11952 clashingAttr
.AddFlag(wxTEXT_ATTR_ALIGNMENT
);
11953 currentStyle
.RemoveFlag(wxTEXT_ATTR_ALIGNMENT
);
11957 currentStyle
.SetAlignment(attr
.GetAlignment());
11960 if (attr
.HasTabs() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_TABS
))
11962 if (currentStyle
.HasTabs())
11964 if (!wxRichTextTabsEq(currentStyle
.GetTabs(), attr
.GetTabs()))
11966 // Clash of attr - mark as such
11967 clashingAttr
.AddFlag(wxTEXT_ATTR_TABS
);
11968 currentStyle
.RemoveFlag(wxTEXT_ATTR_TABS
);
11972 currentStyle
.SetTabs(attr
.GetTabs());
11975 if (attr
.HasLeftIndent() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LEFT_INDENT
))
11977 if (currentStyle
.HasLeftIndent())
11979 if (currentStyle
.GetLeftIndent() != attr
.GetLeftIndent() || currentStyle
.GetLeftSubIndent() != attr
.GetLeftSubIndent())
11981 // Clash of attr - mark as such
11982 clashingAttr
.AddFlag(wxTEXT_ATTR_LEFT_INDENT
);
11983 currentStyle
.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT
);
11987 currentStyle
.SetLeftIndent(attr
.GetLeftIndent(), attr
.GetLeftSubIndent());
11990 if (attr
.HasRightIndent() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_RIGHT_INDENT
))
11992 if (currentStyle
.HasRightIndent())
11994 if (currentStyle
.GetRightIndent() != attr
.GetRightIndent())
11996 // Clash of attr - mark as such
11997 clashingAttr
.AddFlag(wxTEXT_ATTR_RIGHT_INDENT
);
11998 currentStyle
.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT
);
12002 currentStyle
.SetRightIndent(attr
.GetRightIndent());
12005 if (attr
.HasParagraphSpacingAfter() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARA_SPACING_AFTER
))
12007 if (currentStyle
.HasParagraphSpacingAfter())
12009 if (currentStyle
.GetParagraphSpacingAfter() != attr
.GetParagraphSpacingAfter())
12011 // Clash of attr - mark as such
12012 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
12013 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
12017 currentStyle
.SetParagraphSpacingAfter(attr
.GetParagraphSpacingAfter());
12020 if (attr
.HasParagraphSpacingBefore() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARA_SPACING_BEFORE
))
12022 if (currentStyle
.HasParagraphSpacingBefore())
12024 if (currentStyle
.GetParagraphSpacingBefore() != attr
.GetParagraphSpacingBefore())
12026 // Clash of attr - mark as such
12027 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
12028 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
12032 currentStyle
.SetParagraphSpacingBefore(attr
.GetParagraphSpacingBefore());
12035 if (attr
.HasLineSpacing() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LINE_SPACING
))
12037 if (currentStyle
.HasLineSpacing())
12039 if (currentStyle
.GetLineSpacing() != attr
.GetLineSpacing())
12041 // Clash of attr - mark as such
12042 clashingAttr
.AddFlag(wxTEXT_ATTR_LINE_SPACING
);
12043 currentStyle
.RemoveFlag(wxTEXT_ATTR_LINE_SPACING
);
12047 currentStyle
.SetLineSpacing(attr
.GetLineSpacing());
12050 if (attr
.HasCharacterStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_CHARACTER_STYLE_NAME
))
12052 if (currentStyle
.HasCharacterStyleName())
12054 if (currentStyle
.GetCharacterStyleName() != attr
.GetCharacterStyleName())
12056 // Clash of attr - mark as such
12057 clashingAttr
.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
12058 currentStyle
.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
12062 currentStyle
.SetCharacterStyleName(attr
.GetCharacterStyleName());
12065 if (attr
.HasParagraphStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
))
12067 if (currentStyle
.HasParagraphStyleName())
12069 if (currentStyle
.GetParagraphStyleName() != attr
.GetParagraphStyleName())
12071 // Clash of attr - mark as such
12072 clashingAttr
.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
12073 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
12077 currentStyle
.SetParagraphStyleName(attr
.GetParagraphStyleName());
12080 if (attr
.HasListStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LIST_STYLE_NAME
))
12082 if (currentStyle
.HasListStyleName())
12084 if (currentStyle
.GetListStyleName() != attr
.GetListStyleName())
12086 // Clash of attr - mark as such
12087 clashingAttr
.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
12088 currentStyle
.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
12092 currentStyle
.SetListStyleName(attr
.GetListStyleName());
12095 if (attr
.HasBulletStyle() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_STYLE
))
12097 if (currentStyle
.HasBulletStyle())
12099 if (currentStyle
.GetBulletStyle() != attr
.GetBulletStyle())
12101 // Clash of attr - mark as such
12102 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_STYLE
);
12103 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE
);
12107 currentStyle
.SetBulletStyle(attr
.GetBulletStyle());
12110 if (attr
.HasBulletNumber() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_NUMBER
))
12112 if (currentStyle
.HasBulletNumber())
12114 if (currentStyle
.GetBulletNumber() != attr
.GetBulletNumber())
12116 // Clash of attr - mark as such
12117 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NUMBER
);
12118 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER
);
12122 currentStyle
.SetBulletNumber(attr
.GetBulletNumber());
12125 if (attr
.HasBulletText() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_TEXT
))
12127 if (currentStyle
.HasBulletText())
12129 if (currentStyle
.GetBulletText() != attr
.GetBulletText())
12131 // Clash of attr - mark as such
12132 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_TEXT
);
12133 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT
);
12138 currentStyle
.SetBulletText(attr
.GetBulletText());
12139 currentStyle
.SetBulletFont(attr
.GetBulletFont());
12143 if (attr
.HasBulletName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_NAME
))
12145 if (currentStyle
.HasBulletName())
12147 if (currentStyle
.GetBulletName() != attr
.GetBulletName())
12149 // Clash of attr - mark as such
12150 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NAME
);
12151 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NAME
);
12156 currentStyle
.SetBulletName(attr
.GetBulletName());
12160 if (attr
.HasURL() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_URL
))
12162 if (currentStyle
.HasURL())
12164 if (currentStyle
.GetURL() != attr
.GetURL())
12166 // Clash of attr - mark as such
12167 clashingAttr
.AddFlag(wxTEXT_ATTR_URL
);
12168 currentStyle
.RemoveFlag(wxTEXT_ATTR_URL
);
12173 currentStyle
.SetURL(attr
.GetURL());
12177 if (attr
.HasTextEffects() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_EFFECTS
))
12179 if (currentStyle
.HasTextEffects())
12181 // We need to find the bits in the new attr that are different:
12182 // just look at those bits that are specified by the new attr.
12184 // We need to remove the bits and flags that are not common between current attr
12185 // and new attr. In so doing we need to take account of the styles absent from one or more of the
12186 // previous styles.
12188 int currentRelevantTextEffects
= currentStyle
.GetTextEffects() & attr
.GetTextEffectFlags();
12189 int newRelevantTextEffects
= attr
.GetTextEffects() & attr
.GetTextEffectFlags();
12191 if (currentRelevantTextEffects
!= newRelevantTextEffects
)
12193 // Find the text effects that were different, using XOR
12194 int differentEffects
= currentRelevantTextEffects
^ newRelevantTextEffects
;
12196 // Clash of attr - mark as such
12197 clashingAttr
.SetTextEffectFlags(clashingAttr
.GetTextEffectFlags() | differentEffects
);
12198 currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~differentEffects
);
12203 currentStyle
.SetTextEffects(attr
.GetTextEffects());
12204 currentStyle
.SetTextEffectFlags(attr
.GetTextEffectFlags());
12207 // Mask out the flags and values that cannot be common because they were absent in one or more objecrs
12208 // that we've looked at so far
12209 currentStyle
.SetTextEffects(currentStyle
.GetTextEffects() & ~absentAttr
.GetTextEffectFlags());
12210 currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~absentAttr
.GetTextEffectFlags());
12212 if (currentStyle
.GetTextEffectFlags() == 0)
12213 currentStyle
.RemoveFlag(wxTEXT_ATTR_EFFECTS
);
12216 if (attr
.HasOutlineLevel() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_OUTLINE_LEVEL
))
12218 if (currentStyle
.HasOutlineLevel())
12220 if (currentStyle
.GetOutlineLevel() != attr
.GetOutlineLevel())
12222 // Clash of attr - mark as such
12223 clashingAttr
.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
12224 currentStyle
.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
12228 currentStyle
.SetOutlineLevel(attr
.GetOutlineLevel());
12232 WX_DEFINE_OBJARRAY(wxRichTextVariantArray
);
12234 IMPLEMENT_DYNAMIC_CLASS(wxRichTextProperties
, wxObject
)
12236 bool wxRichTextProperties::operator==(const wxRichTextProperties
& props
) const
12238 if (m_properties
.GetCount() != props
.GetCount())
12242 for (i
= 0; i
< m_properties
.GetCount(); i
++)
12244 const wxVariant
& var1
= m_properties
[i
];
12245 int idx
= props
.Find(var1
.GetName());
12248 const wxVariant
& var2
= props
.m_properties
[idx
];
12249 if (!(var1
== var2
))
12256 wxArrayString
wxRichTextProperties::GetPropertyNames() const
12260 for (i
= 0; i
< m_properties
.GetCount(); i
++)
12262 arr
.Add(m_properties
[i
].GetName());
12267 int wxRichTextProperties::Find(const wxString
& name
) const
12270 for (i
= 0; i
< m_properties
.GetCount(); i
++)
12272 if (m_properties
[i
].GetName() == name
)
12278 bool wxRichTextProperties::Remove(const wxString
& name
)
12280 int idx
= Find(name
);
12283 m_properties
.RemoveAt(idx
);
12290 wxVariant
* wxRichTextProperties::FindOrCreateProperty(const wxString
& name
)
12292 int idx
= Find(name
);
12293 if (idx
== wxNOT_FOUND
)
12294 SetProperty(name
, wxString());
12296 if (idx
!= wxNOT_FOUND
)
12298 return & (*this)[idx
];
12304 const wxVariant
& wxRichTextProperties::GetProperty(const wxString
& name
) const
12306 static const wxVariant nullVariant
;
12307 int idx
= Find(name
);
12309 return m_properties
[idx
];
12311 return nullVariant
;
12314 wxString
wxRichTextProperties::GetPropertyString(const wxString
& name
) const
12316 return GetProperty(name
).GetString();
12319 long wxRichTextProperties::GetPropertyLong(const wxString
& name
) const
12321 return GetProperty(name
).GetLong();
12324 bool wxRichTextProperties::GetPropertyBool(const wxString
& name
) const
12326 return GetProperty(name
).GetBool();
12329 double wxRichTextProperties::GetPropertyDouble(const wxString
& name
) const
12331 return GetProperty(name
).GetDouble();
12334 void wxRichTextProperties::SetProperty(const wxVariant
& variant
)
12336 wxASSERT(!variant
.GetName().IsEmpty());
12338 int idx
= Find(variant
.GetName());
12341 m_properties
.Add(variant
);
12343 m_properties
[idx
] = variant
;
12346 void wxRichTextProperties::SetProperty(const wxString
& name
, const wxVariant
& variant
)
12348 int idx
= Find(name
);
12349 wxVariant
var(variant
);
12353 m_properties
.Add(var
);
12355 m_properties
[idx
] = var
;
12358 void wxRichTextProperties::SetProperty(const wxString
& name
, const wxString
& value
)
12360 SetProperty(name
, wxVariant(value
, name
));
12363 void wxRichTextProperties::SetProperty(const wxString
& name
, long value
)
12365 SetProperty(name
, wxVariant(value
, name
));
12368 void wxRichTextProperties::SetProperty(const wxString
& name
, double value
)
12370 SetProperty(name
, wxVariant(value
, name
));
12373 void wxRichTextProperties::SetProperty(const wxString
& name
, bool value
)
12375 SetProperty(name
, wxVariant(value
, name
));
12378 void wxRichTextProperties::RemoveProperties(const wxRichTextProperties
& properties
)
12381 for (i
= 0; i
< properties
.GetCount(); i
++)
12383 wxString name
= properties
.GetProperties()[i
].GetName();
12384 if (HasProperty(name
))
12389 void wxRichTextProperties::MergeProperties(const wxRichTextProperties
& properties
)
12392 for (i
= 0; i
< properties
.GetCount(); i
++)
12394 SetProperty(properties
.GetProperties()[i
]);
12398 wxRichTextObject
* wxRichTextObjectAddress::GetObject(wxRichTextParagraphLayoutBox
* topLevelContainer
) const
12400 if (m_address
.GetCount() == 0)
12401 return topLevelContainer
;
12403 wxRichTextCompositeObject
* p
= topLevelContainer
;
12405 while (p
&& i
< m_address
.GetCount())
12407 int pos
= m_address
[i
];
12408 wxASSERT(pos
>= 0 && pos
< (int) p
->GetChildren().GetCount());
12409 if (pos
< 0 || pos
>= (int) p
->GetChildren().GetCount())
12412 wxRichTextObject
* p1
= p
->GetChild(pos
);
12413 if (i
== (m_address
.GetCount()-1))
12416 p
= wxDynamicCast(p1
, wxRichTextCompositeObject
);
12422 bool wxRichTextObjectAddress::Create(wxRichTextParagraphLayoutBox
* topLevelContainer
, wxRichTextObject
* obj
)
12426 if (topLevelContainer
== obj
)
12429 wxRichTextObject
* o
= obj
;
12432 wxRichTextCompositeObject
* p
= wxDynamicCast(o
->GetParent(), wxRichTextCompositeObject
);
12436 int pos
= p
->GetChildren().IndexOf(o
);
12440 m_address
.Insert(pos
, 0);
12442 if (p
== topLevelContainer
)
12451 bool wxRichTextSelection::operator==(const wxRichTextSelection
& sel
) const
12453 if (m_container
!= sel
.m_container
)
12455 if (m_ranges
.GetCount() != sel
.m_ranges
.GetCount())
12458 for (i
= 0; i
< m_ranges
.GetCount(); i
++)
12459 if (!(m_ranges
[i
] == sel
.m_ranges
[i
]))
12464 // Get the selections appropriate to the specified object, if any; returns wxRICHTEXT_NO_SELECTION if none
12465 // or none at the level of the object's container.
12466 wxRichTextRangeArray
wxRichTextSelection::GetSelectionForObject(wxRichTextObject
* obj
) const
12470 wxRichTextParagraphLayoutBox
* container
= obj
->GetParentContainer();
12472 if (container
== m_container
)
12475 container
= obj
->GetContainer();
12478 if (container
->GetParent())
12480 // If we found that our object's container is within the range of
12481 // a selection higher up, then assume the whole original object
12482 // is also selected.
12483 wxRichTextParagraphLayoutBox
* parentContainer
= container
->GetParentContainer();
12484 if (parentContainer
== m_container
)
12486 if (WithinSelection(container
->GetRange().GetStart(), m_ranges
))
12488 wxRichTextRangeArray ranges
;
12489 ranges
.Add(obj
->GetRange());
12494 container
= parentContainer
;
12503 return wxRichTextRangeArray();
12506 // Is the given position within the selection?
12507 bool wxRichTextSelection::WithinSelection(long pos
, wxRichTextObject
* obj
) const
12513 wxRichTextRangeArray selectionRanges
= GetSelectionForObject(obj
);
12514 return WithinSelection(pos
, selectionRanges
);
12518 // Is the given position within the selection range?
12519 bool wxRichTextSelection::WithinSelection(long pos
, const wxRichTextRangeArray
& ranges
)
12522 for (i
= 0; i
< ranges
.GetCount(); i
++)
12524 const wxRichTextRange
& range
= ranges
[i
];
12525 if (pos
>= range
.GetStart() && pos
<= range
.GetEnd())
12531 // Is the given range completely within the selection range?
12532 bool wxRichTextSelection::WithinSelection(const wxRichTextRange
& range
, const wxRichTextRangeArray
& ranges
)
12535 for (i
= 0; i
< ranges
.GetCount(); i
++)
12537 const wxRichTextRange
& eachRange
= ranges
[i
];
12538 if (range
.IsWithin(eachRange
))
12544 IMPLEMENT_CLASS(wxRichTextDrawingHandler
, wxObject
)
12545 IMPLEMENT_CLASS(wxRichTextDrawingContext
, wxObject
)
12547 bool wxRichTextDrawingContext::HasVirtualAttributes(wxRichTextObject
* obj
) const
12549 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
12552 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
12553 if (handler
->HasVirtualAttributes(obj
))
12556 node
= node
->GetNext();
12561 wxRichTextAttr
wxRichTextDrawingContext::GetVirtualAttributes(wxRichTextObject
* obj
) const
12563 wxRichTextAttr attr
;
12564 // We apply all handlers, so we can may combine several different attributes
12565 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
12568 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
12569 if (handler
->HasVirtualAttributes(obj
))
12571 bool success
= handler
->GetVirtualAttributes(attr
, obj
);
12573 wxUnusedVar(success
);
12576 node
= node
->GetNext();
12581 bool wxRichTextDrawingContext::ApplyVirtualAttributes(wxRichTextAttr
& attr
, wxRichTextObject
* obj
) const
12583 if (HasVirtualAttributes(obj
))
12585 wxRichTextAttr
a(GetVirtualAttributes(obj
));
12593 /// Adds a handler to the end
12594 void wxRichTextBuffer::AddDrawingHandler(wxRichTextDrawingHandler
*handler
)
12596 sm_drawingHandlers
.Append(handler
);
12599 /// Inserts a handler at the front
12600 void wxRichTextBuffer::InsertDrawingHandler(wxRichTextDrawingHandler
*handler
)
12602 sm_drawingHandlers
.Insert( handler
);
12605 /// Removes a handler
12606 bool wxRichTextBuffer::RemoveDrawingHandler(const wxString
& name
)
12608 wxRichTextDrawingHandler
*handler
= FindDrawingHandler(name
);
12611 sm_drawingHandlers
.DeleteObject(handler
);
12619 wxRichTextDrawingHandler
* wxRichTextBuffer::FindDrawingHandler(const wxString
& name
)
12621 wxList::compatibility_iterator node
= sm_drawingHandlers
.GetFirst();
12624 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
12625 if (handler
->GetName().Lower() == name
.Lower()) return handler
;
12627 node
= node
->GetNext();
12632 void wxRichTextBuffer::CleanUpDrawingHandlers()
12634 wxList::compatibility_iterator node
= sm_drawingHandlers
.GetFirst();
12637 wxRichTextDrawingHandler
* handler
= (wxRichTextDrawingHandler
*)node
->GetData();
12638 wxList::compatibility_iterator next
= node
->GetNext();
12643 sm_drawingHandlers
.Clear();