1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/richtext/richtextbuffer.cpp
3 // Purpose: Buffer for wxRichTextCtrl
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // For compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
21 #include "wx/richtext/richtextbuffer.h"
27 #include "wx/dataobj.h"
28 #include "wx/module.h"
31 #include "wx/settings.h"
32 #include "wx/filename.h"
33 #include "wx/clipbrd.h"
34 #include "wx/wfstream.h"
35 #include "wx/mstream.h"
36 #include "wx/sstream.h"
37 #include "wx/textfile.h"
38 #include "wx/hashmap.h"
39 #include "wx/dynarray.h"
41 #include "wx/richtext/richtextctrl.h"
42 #include "wx/richtext/richtextstyles.h"
43 #include "wx/richtext/richtextimagedlg.h"
44 #include "wx/richtext/richtextsizepage.h"
45 #include "wx/richtext/richtextxml.h"
47 #include "wx/listimpl.cpp"
48 #include "wx/arrimpl.cpp"
50 WX_DEFINE_LIST(wxRichTextObjectList
)
51 WX_DEFINE_LIST(wxRichTextLineList
)
53 // Switch off if the platform doesn't like it for some reason
54 #define wxRICHTEXT_USE_OPTIMIZED_DRAWING 1
56 // Use GetPartialTextExtents for platforms that support it natively
57 #define wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS 1
59 const wxChar wxRichTextLineBreakChar
= (wxChar
) 29;
61 // Helper classes for floating layout
62 struct wxRichTextFloatRectMap
64 wxRichTextFloatRectMap(int sY
, int eY
, int w
, wxRichTextObject
* obj
)
74 wxRichTextObject
* anchor
;
77 WX_DEFINE_SORTED_ARRAY(wxRichTextFloatRectMap
*, wxRichTextFloatRectMapArray
);
79 int wxRichTextFloatRectMapCmp(wxRichTextFloatRectMap
* r1
, wxRichTextFloatRectMap
* r2
)
81 return r1
->startY
- r2
->startY
;
84 class wxRichTextFloatCollector
87 wxRichTextFloatCollector(const wxRect
& availableRect
);
88 ~wxRichTextFloatCollector();
90 // Collect the floating objects info in the given paragraph
91 void CollectFloat(wxRichTextParagraph
* para
);
92 void CollectFloat(wxRichTextParagraph
* para
, wxRichTextObject
* floating
);
94 // Return the last paragraph we collected
95 wxRichTextParagraph
* LastParagraph();
97 // Given the start y position and the height of the line,
98 // find out how wide the line can be
99 wxRect
GetAvailableRect(int startY
, int endY
);
101 // Given a floating box, find its fit position
102 int GetFitPosition(int direction
, int start
, int height
) const;
103 int GetFitPosition(const wxRichTextFloatRectMapArray
& array
, int start
, int height
) const;
105 // Find the last y position
106 int GetLastRectBottom();
108 // Draw the floats inside a rect
109 void Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
);
111 // HitTest the floats
112 int HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, int flags
);
114 // Get floating object count
115 int GetFloatingObjectCount() const { return m_left
.GetCount() + m_right
.GetCount(); }
117 // Get floating objects
118 bool GetFloatingObjects(wxRichTextObjectList
& objects
) const;
121 bool DeleteFloat(wxRichTextObject
* obj
);
123 // Do we have this float already?
124 bool HasFloat(wxRichTextObject
* obj
);
126 bool HasFloats() const { return m_left
.GetCount() >0 || m_right
.GetCount() > 0; }
128 static int SearchAdjacentRect(const wxRichTextFloatRectMapArray
& array
, int point
);
130 static int GetWidthFromFloatRect(const wxRichTextFloatRectMapArray
& array
, int index
, int startY
, int endY
);
132 static void FreeFloatRectMapArray(wxRichTextFloatRectMapArray
& array
);
134 static void DrawFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
);
136 static int HitTestFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, int flags
);
139 wxRichTextFloatRectMapArray m_left
;
140 wxRichTextFloatRectMapArray m_right
;
142 wxRect m_availableRect
;
143 wxRichTextParagraph
* m_para
;
147 bool wxRichTextFloatCollector::DeleteFloat(wxRichTextObject
* obj
)
150 for (i
= 0; i
< m_left
.GetCount(); i
++)
152 if (m_left
[i
]->anchor
== obj
)
158 for (i
= 0; i
< m_right
.GetCount(); i
++)
160 if (m_right
[i
]->anchor
== obj
)
169 // Do we have this float already?
170 bool wxRichTextFloatCollector::HasFloat(wxRichTextObject
* obj
)
173 for (i
= 0; i
< m_left
.GetCount(); i
++)
175 if (m_left
[i
]->anchor
== obj
)
180 for (i
= 0; i
< m_right
.GetCount(); i
++)
182 if (m_right
[i
]->anchor
== obj
)
190 // Get floating objects
191 bool wxRichTextFloatCollector::GetFloatingObjects(wxRichTextObjectList
& objects
) const
194 for (i
= 0; i
< m_left
.GetCount(); i
++)
195 objects
.Append(m_left
[i
]->anchor
);
196 for (i
= 0; i
< m_right
.GetCount(); i
++)
197 objects
.Append(m_right
[i
]->anchor
);
203 * Binary search helper function
204 * The argument point is the Y coordinate, and this fuction
205 * always return the floating rect that contain this coordinate
206 * or under this coordinate.
208 int wxRichTextFloatCollector::SearchAdjacentRect(const wxRichTextFloatRectMapArray
& array
, int point
)
210 int end
= array
.GetCount() - 1;
223 int mid
= (start
+ end
) / 2;
224 if (array
[mid
]->startY
<= point
&& array
[mid
]->endY
>= point
)
226 else if (array
[mid
]->startY
> point
)
231 else if (array
[mid
]->endY
< point
)
241 int wxRichTextFloatCollector::GetWidthFromFloatRect(const wxRichTextFloatRectMapArray
& array
, int index
, int startY
, int endY
)
244 int len
= array
.GetCount();
246 wxASSERT(index
>= 0 && index
< len
);
248 if (array
[index
]->startY
< startY
&& array
[index
]->endY
> startY
)
249 ret
= ret
< array
[index
]->width
? array
[index
]->width
: ret
;
250 while (index
< len
&& array
[index
]->startY
<= endY
)
252 ret
= ret
< array
[index
]->width
? array
[index
]->width
: ret
;
259 wxRichTextFloatCollector::wxRichTextFloatCollector(const wxRect
& rect
) : m_left(wxRichTextFloatRectMapCmp
), m_right(wxRichTextFloatRectMapCmp
)
261 m_availableRect
= rect
;
265 void wxRichTextFloatCollector::FreeFloatRectMapArray(wxRichTextFloatRectMapArray
& array
)
267 int len
= array
.GetCount();
268 for (int i
= 0; i
< len
; i
++)
272 wxRichTextFloatCollector::~wxRichTextFloatCollector()
274 FreeFloatRectMapArray(m_left
);
275 FreeFloatRectMapArray(m_right
);
278 int wxRichTextFloatCollector::GetFitPosition(const wxRichTextFloatRectMapArray
& array
, int start
, int height
) const
280 if (array
.GetCount() == 0)
283 int i
= SearchAdjacentRect(array
, start
);
285 while (i
< (int) array
.GetCount())
287 if (array
[i
]->startY
- last
>= height
)
289 last
= array
[i
]->endY
;
296 int wxRichTextFloatCollector::GetFitPosition(int direction
, int start
, int height
) const
298 if (direction
== wxTEXT_BOX_ATTR_FLOAT_LEFT
)
299 return GetFitPosition(m_left
, start
, height
);
300 else if (direction
== wxTEXT_BOX_ATTR_FLOAT_RIGHT
)
301 return GetFitPosition(m_right
, start
, height
);
304 wxASSERT("Never should be here");
309 // Adds a floating image to the float collector.
310 // The actual positioning is done by wxRichTextParagraph::LayoutFloat.
311 void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph
* para
, wxRichTextObject
* floating
)
313 int direction
= floating
->GetFloatDirection();
315 wxPoint pos
= floating
->GetPosition();
316 wxSize size
= floating
->GetCachedSize();
317 wxRichTextFloatRectMap
*map
= new wxRichTextFloatRectMap(pos
.y
, pos
.y
+ size
.y
, size
.x
, floating
);
320 case wxTEXT_BOX_ATTR_FLOAT_NONE
:
323 case wxTEXT_BOX_ATTR_FLOAT_LEFT
:
324 // Just a not-enough simple assertion
325 wxASSERT (m_left
.Index(map
) == wxNOT_FOUND
);
328 case wxTEXT_BOX_ATTR_FLOAT_RIGHT
:
329 wxASSERT (m_right
.Index(map
) == wxNOT_FOUND
);
334 wxASSERT("Unrecognised float attribute.");
340 void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph
* para
)
342 wxRichTextObjectList::compatibility_iterator node
= para
->GetChildren().GetFirst();
345 wxRichTextObject
* floating
= node
->GetData();
347 if (floating
->IsFloating())
349 CollectFloat(para
, floating
);
352 node
= node
->GetNext();
358 wxRichTextParagraph
* wxRichTextFloatCollector::LastParagraph()
363 wxRect
wxRichTextFloatCollector::GetAvailableRect(int startY
, int endY
)
365 int widthLeft
= 0, widthRight
= 0;
366 if (m_left
.GetCount() != 0)
368 int i
= SearchAdjacentRect(m_left
, startY
);
369 if (i
< (int) m_left
.GetCount())
370 widthLeft
= GetWidthFromFloatRect(m_left
, i
, startY
, endY
);
372 if (m_right
.GetCount() != 0)
374 int j
= SearchAdjacentRect(m_right
, startY
);
375 if (j
< (int) m_right
.GetCount())
376 widthRight
= GetWidthFromFloatRect(m_right
, j
, startY
, endY
);
379 // TODO: actually we want to use the actual image positions to find the
380 // available remaining space, since the image might not be right up against
381 // the left or right edge of the container.
382 return wxRect(widthLeft
+ m_availableRect
.x
, 0, m_availableRect
.width
- widthLeft
- widthRight
, 0);
385 int wxRichTextFloatCollector::GetLastRectBottom()
388 int len
= m_left
.GetCount();
390 ret
= ret
> m_left
[len
-1]->endY
? ret
: m_left
[len
-1]->endY
;
392 len
= m_right
.GetCount();
394 ret
= ret
> m_right
[len
-1]->endY
? ret
: m_right
[len
-1]->endY
;
400 void wxRichTextFloatCollector::DrawFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& WXUNUSED(range
), const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
403 int end
= rect
.y
+ rect
.height
;
405 i
= SearchAdjacentRect(array
, start
);
406 if (i
< 0 || i
>= (int) array
.GetCount())
408 j
= SearchAdjacentRect(array
, end
);
409 if (j
< 0 || j
>= (int) array
.GetCount())
410 j
= array
.GetCount() - 1;
413 wxRichTextObject
* obj
= array
[i
]->anchor
;
414 wxRichTextRange r
= obj
->GetRange();
415 obj
->Draw(dc
, context
, r
, selection
, wxRect(obj
->GetPosition(), obj
->GetCachedSize()), descent
, style
);
420 void wxRichTextFloatCollector::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
422 if (m_left
.GetCount() > 0)
423 DrawFloat(m_left
, dc
, context
, range
, selection
, rect
, descent
, style
);
424 if (m_right
.GetCount() > 0)
425 DrawFloat(m_right
, dc
, context
, range
, selection
, rect
, descent
, style
);
428 int wxRichTextFloatCollector::HitTestFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& WXUNUSED(dc
), wxRichTextDrawingContext
& WXUNUSED(context
), const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, int WXUNUSED(flags
))
431 if (array
.GetCount() == 0)
432 return wxRICHTEXT_HITTEST_NONE
;
433 i
= SearchAdjacentRect(array
, pt
.y
);
434 if (i
< 0 || i
>= (int) array
.GetCount())
435 return wxRICHTEXT_HITTEST_NONE
;
436 if (!array
[i
]->anchor
->IsShown())
437 return wxRICHTEXT_HITTEST_NONE
;
439 wxPoint point
= array
[i
]->anchor
->GetPosition();
440 wxSize size
= array
[i
]->anchor
->GetCachedSize();
441 if (point
.x
<= pt
.x
&& point
.x
+ size
.x
>= pt
.x
442 && point
.y
<= pt
.y
&& point
.y
+ size
.y
>= pt
.y
)
444 textPosition
= array
[i
]->anchor
->GetRange().GetStart();
445 * obj
= array
[i
]->anchor
;
446 if (pt
.x
> (pt
.x
+ pt
.x
+ size
.x
) / 2)
447 return wxRICHTEXT_HITTEST_BEFORE
;
449 return wxRICHTEXT_HITTEST_AFTER
;
452 return wxRICHTEXT_HITTEST_NONE
;
455 int wxRichTextFloatCollector::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, int flags
)
457 int ret
= HitTestFloat(m_left
, dc
, context
, pt
, textPosition
, obj
, flags
);
458 if (ret
== wxRICHTEXT_HITTEST_NONE
)
460 ret
= HitTestFloat(m_right
, dc
, context
, pt
, textPosition
, obj
, flags
);
465 // Helpers for efficiency
466 inline void wxCheckSetFont(wxDC
& dc
, const wxFont
& font
)
468 // JACS: did I do this some time ago when testing? Should we re-enable it?
470 const wxFont
& font1
= dc
.GetFont();
471 if (font1
.IsOk() && font
.IsOk())
473 if (font1
.GetPointSize() == font
.GetPointSize() &&
474 font1
.GetFamily() == font
.GetFamily() &&
475 font1
.GetStyle() == font
.GetStyle() &&
476 font1
.GetWeight() == font
.GetWeight() &&
477 font1
.GetUnderlined() == font
.GetUnderlined() &&
478 font1
.GetFamily() == font
.GetFamily() &&
479 font1
.GetFaceName() == font
.GetFaceName())
486 inline void wxCheckSetPen(wxDC
& dc
, const wxPen
& pen
)
488 const wxPen
& pen1
= dc
.GetPen();
489 if (pen1
.IsOk() && pen
.IsOk())
491 if (pen1
.GetWidth() == pen
.GetWidth() &&
492 pen1
.GetStyle() == pen
.GetStyle() &&
493 pen1
.GetColour() == pen
.GetColour())
499 inline void wxCheckSetBrush(wxDC
& dc
, const wxBrush
& brush
)
501 const wxBrush
& brush1
= dc
.GetBrush();
502 if (brush1
.IsOk() && brush
.IsOk())
504 if (brush1
.GetStyle() == brush
.GetStyle() &&
505 brush1
.GetColour() == brush
.GetColour())
513 * This is the base for drawable objects.
516 IMPLEMENT_CLASS(wxRichTextObject
, wxObject
)
518 wxRichTextObject::wxRichTextObject(wxRichTextObject
* parent
)
526 wxRichTextObject::~wxRichTextObject()
530 void wxRichTextObject::Dereference()
538 void wxRichTextObject::Copy(const wxRichTextObject
& obj
)
541 m_maxSize
= obj
.m_maxSize
;
542 m_minSize
= obj
.m_minSize
;
544 m_range
= obj
.m_range
;
545 m_ownRange
= obj
.m_ownRange
;
546 m_attributes
= obj
.m_attributes
;
547 m_properties
= obj
.m_properties
;
548 m_descent
= obj
.m_descent
;
552 // Get/set the top-level container of this object.
553 wxRichTextParagraphLayoutBox
* wxRichTextObject::GetContainer() const
555 const wxRichTextObject
* p
= this;
560 return wxDynamicCast(p
, wxRichTextParagraphLayoutBox
);
567 void wxRichTextObject::SetMargins(int margin
)
569 SetMargins(margin
, margin
, margin
, margin
);
572 void wxRichTextObject::SetMargins(int leftMargin
, int rightMargin
, int topMargin
, int bottomMargin
)
574 GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().SetValue(leftMargin
, wxTEXT_ATTR_UNITS_PIXELS
);
575 GetAttributes().GetTextBoxAttr().GetMargins().GetRight().SetValue(rightMargin
, wxTEXT_ATTR_UNITS_PIXELS
);
576 GetAttributes().GetTextBoxAttr().GetMargins().GetTop().SetValue(topMargin
, wxTEXT_ATTR_UNITS_PIXELS
);
577 GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().SetValue(bottomMargin
, wxTEXT_ATTR_UNITS_PIXELS
);
580 int wxRichTextObject::GetLeftMargin() const
582 return GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().GetValue();
585 int wxRichTextObject::GetRightMargin() const
587 return GetAttributes().GetTextBoxAttr().GetMargins().GetRight().GetValue();
590 int wxRichTextObject::GetTopMargin() const
592 return GetAttributes().GetTextBoxAttr().GetMargins().GetTop().GetValue();
595 int wxRichTextObject::GetBottomMargin() const
597 return GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().GetValue();
600 // Calculate the available content space in the given rectangle, given the
601 // margins, border and padding specified in the object's attributes.
602 wxRect
wxRichTextObject::GetAvailableContentArea(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& outerRect
) const
604 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
605 marginRect
= outerRect
;
606 wxRichTextAttr
attr(GetAttributes());
607 context
.ApplyVirtualAttributes(attr
, (wxRichTextObject
*) this);
608 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
612 // Invalidate the buffer. With no argument, invalidates whole buffer.
613 void wxRichTextObject::Invalidate(const wxRichTextRange
& invalidRange
)
615 if (invalidRange
!= wxRICHTEXT_NONE
)
617 // If this is a floating object, size may not be recalculated
618 // after floats have been collected in an early stage of Layout.
619 // So avoid resetting the cache for floating objects during layout.
621 SetCachedSize(wxDefaultSize
);
622 SetMaxSize(wxDefaultSize
);
623 SetMinSize(wxDefaultSize
);
627 // Convert units in tenths of a millimetre to device units
628 int wxRichTextObject::ConvertTenthsMMToPixels(wxDC
& dc
, int units
) const
633 scale
= GetBuffer()->GetScale();
634 int p
= ConvertTenthsMMToPixels(dc
.GetPPI().x
, units
, scale
);
639 // Convert units in tenths of a millimetre to device units
640 int wxRichTextObject::ConvertTenthsMMToPixels(int ppi
, int units
, double scale
)
642 // There are ppi pixels in 254.1 "1/10 mm"
644 double pixels
= ((double) units
* (double)ppi
) / 254.1;
648 // If the result is very small, make it at least one pixel in size.
649 if (pixels
== 0 && units
> 0)
655 // Convert units in pixels to tenths of a millimetre
656 int wxRichTextObject::ConvertPixelsToTenthsMM(wxDC
& dc
, int pixels
) const
661 scale
= GetBuffer()->GetScale();
663 return ConvertPixelsToTenthsMM(dc
.GetPPI().x
, p
, scale
);
666 int wxRichTextObject::ConvertPixelsToTenthsMM(int ppi
, int pixels
, double scale
)
668 // There are ppi pixels in 254.1 "1/10 mm"
670 double p
= double(pixels
);
675 int units
= int( p
* 254.1 / (double) ppi
);
679 // Draw the borders and background for the given rectangle and attributes.
680 // Width and height are taken to be the outer margin size, not the content.
681 bool wxRichTextObject::DrawBoxAttributes(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& attr
, const wxRect
& boxRect
, int flags
)
683 // Assume boxRect is the area around the content
684 wxRect marginRect
= boxRect
;
685 wxRect contentRect
, borderRect
, paddingRect
, outlineRect
;
687 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
689 // Margin is transparent. Draw background from margin.
690 if (attr
.HasBackgroundColour() || (flags
& wxRICHTEXT_DRAW_SELECTED
))
693 if (flags
& wxRICHTEXT_DRAW_SELECTED
)
695 // TODO: get selection colour from control?
696 colour
= wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
);
699 colour
= attr
.GetBackgroundColour();
702 wxBrush
brush(colour
);
706 dc
.DrawRectangle(borderRect
);
709 if (flags
& wxRICHTEXT_DRAW_GUIDELINES
)
711 wxRichTextAttr editBorderAttr
= attr
;
712 // TODO: make guideline colour configurable
713 editBorderAttr
.GetTextBoxAttr().GetBorder().SetColour(*wxLIGHT_GREY
);
714 editBorderAttr
.GetTextBoxAttr().GetBorder().SetWidth(1, wxTEXT_ATTR_UNITS_PIXELS
);
715 editBorderAttr
.GetTextBoxAttr().GetBorder().SetStyle(wxTEXT_BOX_ATTR_BORDER_SOLID
);
717 DrawBorder(dc
, buffer
, editBorderAttr
.GetTextBoxAttr().GetBorder(), borderRect
, flags
);
720 if (attr
.GetTextBoxAttr().GetBorder().IsValid())
721 DrawBorder(dc
, buffer
, attr
.GetTextBoxAttr().GetBorder(), borderRect
);
723 if (attr
.GetTextBoxAttr().GetOutline().IsValid())
724 DrawBorder(dc
, buffer
, attr
.GetTextBoxAttr().GetOutline(), outlineRect
);
730 bool wxRichTextObject::DrawBorder(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxTextAttrBorders
& attr
, const wxRect
& rect
, int WXUNUSED(flags
))
732 int borderLeft
= 0, borderRight
= 0, borderTop
= 0, borderBottom
= 0;
733 wxTextAttrDimensionConverter
converter(dc
, buffer
? buffer
->GetScale() : 1.0);
735 if (attr
.GetLeft().IsValid() && attr
.GetLeft().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
737 borderLeft
= converter
.GetPixels(attr
.GetLeft().GetWidth());
738 wxColour
col(attr
.GetLeft().GetColour());
740 // If pen width is > 1, resorts to a solid rectangle.
743 int penStyle
= wxSOLID
;
744 if (attr
.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
746 else if (attr
.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
747 penStyle
= wxLONG_DASH
;
748 wxPen
pen(col
, 1, penStyle
);
750 dc
.DrawLine(rect
.x
, rect
.y
, rect
.x
, rect
.y
+ rect
.height
);
753 else if (borderLeft
> 1)
759 dc
.DrawRectangle(rect
.x
, rect
.y
, borderLeft
, rect
.height
);
763 if (attr
.GetRight().IsValid() && attr
.GetRight().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
765 borderRight
= converter
.GetPixels(attr
.GetRight().GetWidth());
767 wxColour
col(attr
.GetRight().GetColour());
769 // If pen width is > 1, resorts to a solid rectangle.
770 if (borderRight
== 1)
772 int penStyle
= wxSOLID
;
773 if (attr
.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
775 else if (attr
.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
776 penStyle
= wxLONG_DASH
;
777 wxPen
pen(col
, 1, penStyle
);
779 dc
.DrawLine(rect
.x
+ rect
.width
, rect
.y
, rect
.x
+ rect
.width
, rect
.y
+ rect
.height
+ 1);
782 else if (borderRight
> 1)
788 dc
.DrawRectangle(rect
.x
+ rect
.width
- borderRight
, rect
.y
, borderRight
, rect
.height
);
792 if (attr
.GetTop().IsValid() && attr
.GetTop().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
794 borderTop
= converter
.GetPixels(attr
.GetTop().GetWidth());
796 wxColour
col(attr
.GetTop().GetColour());
798 // If pen width is > 1, resorts to a solid rectangle.
801 int penStyle
= wxSOLID
;
802 if (attr
.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
804 else if (attr
.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
805 penStyle
= wxLONG_DASH
;
806 wxPen
pen(col
, 1, penStyle
);
808 dc
.DrawLine(rect
.x
, rect
.y
, rect
.x
+ rect
.width
, rect
.y
);
811 else if (borderTop
> 1)
817 dc
.DrawRectangle(rect
.x
, rect
.y
, rect
.width
, borderTop
);
821 if (attr
.GetBottom().IsValid() && attr
.GetBottom().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
823 borderBottom
= converter
.GetPixels(attr
.GetBottom().GetWidth());
824 wxColour
col(attr
.GetTop().GetColour());
826 // If pen width is > 1, resorts to a solid rectangle.
827 if (borderBottom
== 1)
829 int penStyle
= wxSOLID
;
830 if (attr
.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
832 else if (attr
.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
833 penStyle
= wxLONG_DASH
;
834 wxPen
pen(col
, 1, penStyle
);
836 dc
.DrawLine(rect
.x
, rect
.y
+ rect
.height
, rect
.x
+ rect
.width
, rect
.y
+ rect
.height
);
839 else if (borderBottom
> 1)
845 dc
.DrawRectangle(rect
.x
, rect
.y
+ rect
.height
- borderBottom
, rect
.width
, borderBottom
);
852 // Get the various rectangles of the box model in pixels. You can either specify contentRect (inner)
853 // or marginRect (outer), and the other must be the default rectangle (no width or height).
854 // Note that the outline doesn't affect the position of the rectangle, it's drawn in whatever space
857 // | Margin | Border | Padding | CONTENT | Padding | Border | Margin |
859 bool wxRichTextObject::GetBoxRects(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& attr
, wxRect
& marginRect
, wxRect
& borderRect
, wxRect
& contentRect
, wxRect
& paddingRect
, wxRect
& outlineRect
)
861 int borderLeft
= 0, borderRight
= 0, borderTop
= 0, borderBottom
= 0;
862 int outlineLeft
= 0, outlineRight
= 0, outlineTop
= 0, outlineBottom
= 0;
863 int paddingLeft
= 0, paddingRight
= 0, paddingTop
= 0, paddingBottom
= 0;
864 int marginLeft
= 0, marginRight
= 0, marginTop
= 0, marginBottom
= 0;
866 wxTextAttrDimensionConverter
converter(dc
, buffer
? buffer
->GetScale() : 1.0);
868 if (attr
.GetTextBoxAttr().GetMargins().GetLeft().IsValid())
869 marginLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetLeft());
870 if (attr
.GetTextBoxAttr().GetMargins().GetRight().IsValid())
871 marginRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetRight());
872 if (attr
.GetTextBoxAttr().GetMargins().GetTop().IsValid())
873 marginTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetTop());
874 if (attr
.GetTextBoxAttr().GetMargins().GetBottom().IsValid())
875 marginBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetBottom());
877 if (attr
.GetTextBoxAttr().GetBorder().GetLeft().GetWidth().IsValid())
878 borderLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetLeft().GetWidth());
879 if (attr
.GetTextBoxAttr().GetBorder().GetRight().GetWidth().IsValid())
880 borderRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetRight().GetWidth());
881 if (attr
.GetTextBoxAttr().GetBorder().GetTop().GetWidth().IsValid())
882 borderTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetTop().GetWidth());
883 if (attr
.GetTextBoxAttr().GetBorder().GetBottom().GetWidth().IsValid())
884 borderBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetBottom().GetWidth());
886 if (attr
.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
887 paddingLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetLeft());
888 if (attr
.GetTextBoxAttr().GetPadding().GetRight().IsValid())
889 paddingRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetRight());
890 if (attr
.GetTextBoxAttr().GetPadding().GetTop().IsValid())
891 paddingTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetTop());
892 if (attr
.GetTextBoxAttr().GetPadding().GetBottom().IsValid())
893 paddingBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetBottom());
895 if (attr
.GetTextBoxAttr().GetOutline().GetLeft().GetWidth().IsValid())
896 outlineLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetLeft().GetWidth());
897 if (attr
.GetTextBoxAttr().GetOutline().GetRight().GetWidth().IsValid())
898 outlineRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetRight().GetWidth());
899 if (attr
.GetTextBoxAttr().GetOutline().GetTop().GetWidth().IsValid())
900 outlineTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetTop().GetWidth());
901 if (attr
.GetTextBoxAttr().GetOutline().GetBottom().GetWidth().IsValid())
902 outlineBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetBottom().GetWidth());
904 int leftTotal
= marginLeft
+ borderLeft
+ paddingLeft
;
905 int rightTotal
= marginRight
+ borderRight
+ paddingRight
;
906 int topTotal
= marginTop
+ borderTop
+ paddingTop
;
907 int bottomTotal
= marginBottom
+ borderBottom
+ paddingBottom
;
909 if (marginRect
!= wxRect())
911 contentRect
.x
= marginRect
.x
+ leftTotal
;
912 contentRect
.y
= marginRect
.y
+ topTotal
;
913 contentRect
.width
= marginRect
.width
- (leftTotal
+ rightTotal
);
914 contentRect
.height
= marginRect
.height
- (topTotal
+ bottomTotal
);
918 marginRect
.x
= contentRect
.x
- leftTotal
;
919 marginRect
.y
= contentRect
.y
- topTotal
;
920 marginRect
.width
= contentRect
.width
+ (leftTotal
+ rightTotal
);
921 marginRect
.height
= contentRect
.height
+ (topTotal
+ bottomTotal
);
924 borderRect
.x
= marginRect
.x
+ marginLeft
;
925 borderRect
.y
= marginRect
.y
+ marginTop
;
926 borderRect
.width
= marginRect
.width
- (marginLeft
+ marginRight
);
927 borderRect
.height
= marginRect
.height
- (marginTop
+ marginBottom
);
929 paddingRect
.x
= marginRect
.x
+ marginLeft
+ borderLeft
;
930 paddingRect
.y
= marginRect
.y
+ marginTop
+ borderTop
;
931 paddingRect
.width
= marginRect
.width
- (marginLeft
+ marginRight
+ borderLeft
+ borderRight
);
932 paddingRect
.height
= marginRect
.height
- (marginTop
+ marginBottom
+ borderTop
+ borderBottom
);
934 // The outline is outside the margin and doesn't influence the overall box position or content size.
935 outlineRect
.x
= marginRect
.x
- outlineLeft
;
936 outlineRect
.y
= marginRect
.y
- outlineTop
;
937 outlineRect
.width
= marginRect
.width
+ (outlineLeft
+ outlineRight
);
938 outlineRect
.height
= marginRect
.height
+ (outlineTop
+ outlineBottom
);
943 // Get the total margin for the object in pixels, taking into account margin, padding and border size
944 bool wxRichTextObject::GetTotalMargin(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& attr
, int& leftMargin
, int& rightMargin
,
945 int& topMargin
, int& bottomMargin
)
947 // Assume boxRect is the area around the content
948 wxRect contentRect
, marginRect
, borderRect
, paddingRect
, outlineRect
;
949 marginRect
= wxRect(0, 0, 1000, 1000);
951 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
953 leftMargin
= contentRect
.GetLeft() - marginRect
.GetLeft();
954 rightMargin
= marginRect
.GetRight() - contentRect
.GetRight();
955 topMargin
= contentRect
.GetTop() - marginRect
.GetTop();
956 bottomMargin
= marginRect
.GetBottom() - contentRect
.GetBottom();
961 // Returns the rectangle which the child has available to it given restrictions specified in the
962 // child attribute, e.g. 50% width of the parent, 400 pixels, x position 20% of the parent, etc.
963 // availableContainerSpace might be a parent that the cell has to compute its width relative to.
964 // E.g. a cell that's 50% of its parent.
965 wxRect
wxRichTextObject::AdjustAvailableSpace(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& WXUNUSED(parentAttr
), const wxRichTextAttr
& childAttr
, const wxRect
& availableParentSpace
, const wxRect
& availableContainerSpace
)
967 wxRect rect
= availableParentSpace
;
970 scale
= buffer
->GetScale();
972 wxTextAttrDimensionConverter
converter(dc
, scale
, availableContainerSpace
.GetSize());
974 if (childAttr
.GetTextBoxAttr().GetWidth().IsValid())
975 rect
.width
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetWidth());
977 if (childAttr
.GetTextBoxAttr().GetHeight().IsValid())
978 rect
.height
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetHeight());
980 // Can specify either left or right for the position (we're assuming we can't
981 // set the left and right edges to effectively set the size. Would we want to do that?)
982 if (childAttr
.GetTextBoxAttr().GetPosition().GetLeft().IsValid())
984 rect
.x
= rect
.x
+ converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetLeft());
986 else if (childAttr
.GetTextBoxAttr().GetPosition().GetRight().IsValid())
988 int x
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetRight());
989 if (childAttr
.GetTextBoxAttr().GetPosition().GetRight().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE
)
990 rect
.x
= availableContainerSpace
.x
+ availableContainerSpace
.width
- rect
.width
;
995 if (childAttr
.GetTextBoxAttr().GetPosition().GetTop().IsValid())
997 rect
.y
= rect
.y
+ converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetTop());
999 else if (childAttr
.GetTextBoxAttr().GetPosition().GetBottom().IsValid())
1001 int y
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetBottom());
1002 if (childAttr
.GetTextBoxAttr().GetPosition().GetBottom().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE
)
1003 rect
.y
= availableContainerSpace
.y
+ availableContainerSpace
.height
- rect
.height
;
1008 if (rect
.GetWidth() > availableParentSpace
.GetWidth())
1009 rect
.SetWidth(availableParentSpace
.GetWidth());
1014 // Dump to output stream for debugging
1015 void wxRichTextObject::Dump(wxTextOutputStream
& stream
)
1017 stream
<< GetClassInfo()->GetClassName() << wxT("\n");
1018 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");
1019 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");
1022 // Gets the containing buffer
1023 wxRichTextBuffer
* wxRichTextObject::GetBuffer() const
1025 const wxRichTextObject
* obj
= this;
1026 while (obj
&& !obj
->IsKindOf(CLASSINFO(wxRichTextBuffer
)))
1027 obj
= obj
->GetParent();
1028 return wxDynamicCast(obj
, wxRichTextBuffer
);
1031 // Get the absolute object position, by traversing up the child/parent hierarchy
1032 wxPoint
wxRichTextObject::GetAbsolutePosition() const
1034 wxPoint pt
= GetPosition();
1036 wxRichTextObject
* p
= GetParent();
1039 pt
= pt
+ p
->GetPosition();
1046 // Hit-testing: returns a flag indicating hit test details, plus
1047 // information about position
1048 int wxRichTextObject::HitTest(wxDC
& WXUNUSED(dc
), wxRichTextDrawingContext
& WXUNUSED(context
), const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int WXUNUSED(flags
))
1051 return wxRICHTEXT_HITTEST_NONE
;
1053 wxRect rect
= GetRect();
1054 if (pt
.x
>= rect
.x
&& pt
.x
< rect
.x
+ rect
.width
&&
1055 pt
.y
>= rect
.y
&& pt
.y
< rect
.y
+ rect
.height
)
1058 *contextObj
= GetParentContainer();
1059 textPosition
= GetRange().GetStart();
1060 return wxRICHTEXT_HITTEST_ON
;
1063 return wxRICHTEXT_HITTEST_NONE
;
1066 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1067 // lays out the object again using the maximum ('best') size
1068 bool wxRichTextObject::LayoutToBestSize(wxDC
& dc
, wxRichTextDrawingContext
& context
, wxRichTextBuffer
* buffer
,
1069 const wxRichTextAttr
& parentAttr
, const wxRichTextAttr
& attr
,
1070 const wxRect
& availableParentSpace
, const wxRect
& availableContainerSpace
,
1073 wxRect availableChildRect
= AdjustAvailableSpace(dc
, buffer
, parentAttr
, attr
, availableParentSpace
, availableContainerSpace
);
1074 wxRect originalAvailableRect
= availableChildRect
;
1075 Layout(dc
, context
, availableChildRect
, availableContainerSpace
, style
);
1077 wxSize maxSize
= GetMaxSize();
1079 // Don't ignore if maxSize.x is zero, since we need to redo the paragraph's lines
1081 if (!attr
.GetTextBoxAttr().GetWidth().IsValid() && maxSize
.x
< availableChildRect
.width
)
1083 // Redo the layout with a fixed, minimum size this time.
1084 Invalidate(wxRICHTEXT_ALL
);
1085 wxRichTextAttr
newAttr(attr
);
1086 newAttr
.GetTextBoxAttr().GetWidth().SetValue(maxSize
.x
, wxTEXT_ATTR_UNITS_PIXELS
);
1087 newAttr
.GetTextBoxAttr().GetWidth().SetPosition(wxTEXT_BOX_ATTR_POSITION_ABSOLUTE
);
1089 availableChildRect
= AdjustAvailableSpace(dc
, buffer
, parentAttr
, newAttr
, availableParentSpace
, availableContainerSpace
);
1091 // If a paragraph, align the whole paragraph.
1092 // Problem with this: if we're limited by a floating object, a line may be centered
1093 // w.r.t. the smaller resulting box rather than the actual available width.
1094 if (attr
.HasAlignment() && !GetContainer()->GetFloatCollector()->HasFloats()) // FIXME: aligning whole paragraph not compatible with floating objects
1096 // centering, right-justification
1097 if (attr
.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE
)
1099 availableChildRect
.x
= (originalAvailableRect
.GetWidth() - availableChildRect
.GetWidth())/2 + availableChildRect
.x
;
1101 else if (attr
.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT
)
1103 availableChildRect
.x
= availableChildRect
.x
+ originalAvailableRect
.GetWidth() - availableChildRect
.GetWidth();
1107 Layout(dc
, context
, availableChildRect
, availableContainerSpace
, style
);
1121 // Move the object recursively, by adding the offset from old to new
1122 void wxRichTextObject::Move(const wxPoint
& pt
)
1129 * wxRichTextCompositeObject
1130 * This is the base for drawable objects.
1133 IMPLEMENT_CLASS(wxRichTextCompositeObject
, wxRichTextObject
)
1135 wxRichTextCompositeObject::wxRichTextCompositeObject(wxRichTextObject
* parent
):
1136 wxRichTextObject(parent
)
1140 wxRichTextCompositeObject::~wxRichTextCompositeObject()
1145 /// Get the nth child
1146 wxRichTextObject
* wxRichTextCompositeObject::GetChild(size_t n
) const
1148 wxASSERT ( n
< m_children
.GetCount() );
1150 return m_children
.Item(n
)->GetData();
1153 /// Append a child, returning the position
1154 size_t wxRichTextCompositeObject::AppendChild(wxRichTextObject
* child
)
1156 m_children
.Append(child
);
1157 child
->SetParent(this);
1158 return m_children
.GetCount() - 1;
1161 /// Insert the child in front of the given object, or at the beginning
1162 bool wxRichTextCompositeObject::InsertChild(wxRichTextObject
* child
, wxRichTextObject
* inFrontOf
)
1166 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(inFrontOf
);
1167 m_children
.Insert(node
, child
);
1170 m_children
.Insert(child
);
1171 child
->SetParent(this);
1176 /// Delete the child
1177 bool wxRichTextCompositeObject::RemoveChild(wxRichTextObject
* child
, bool deleteChild
)
1179 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(child
);
1182 wxRichTextObject
* obj
= node
->GetData();
1183 m_children
.Erase(node
);
1192 /// Delete all children
1193 bool wxRichTextCompositeObject::DeleteChildren()
1195 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1198 wxRichTextObjectList::compatibility_iterator oldNode
= node
;
1200 wxRichTextObject
* child
= node
->GetData();
1201 child
->Dereference(); // Only delete if reference count is zero
1203 node
= node
->GetNext();
1204 m_children
.Erase(oldNode
);
1210 /// Get the child count
1211 size_t wxRichTextCompositeObject::GetChildCount() const
1213 return m_children
.GetCount();
1217 void wxRichTextCompositeObject::Copy(const wxRichTextCompositeObject
& obj
)
1219 wxRichTextObject::Copy(obj
);
1223 wxRichTextObjectList::compatibility_iterator node
= obj
.m_children
.GetFirst();
1226 wxRichTextObject
* child
= node
->GetData();
1227 wxRichTextObject
* newChild
= child
->Clone();
1228 newChild
->SetParent(this);
1229 m_children
.Append(newChild
);
1231 node
= node
->GetNext();
1235 /// Hit-testing: returns a flag indicating hit test details, plus
1236 /// information about position
1237 int wxRichTextCompositeObject::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
1240 return wxRICHTEXT_HITTEST_NONE
;
1242 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1245 wxRichTextObject
* child
= node
->GetData();
1247 if (child
->IsShown() && child
->IsTopLevel() && (flags
& wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS
))
1249 // Just check if we hit the overall object
1250 int ret
= child
->wxRichTextObject::HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
1251 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
1254 else if (child
->IsShown())
1256 int ret
= child
->HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
1257 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
1261 node
= node
->GetNext();
1264 return wxRICHTEXT_HITTEST_NONE
;
1267 /// Finds the absolute position and row height for the given character position
1268 bool wxRichTextCompositeObject::FindPosition(wxDC
& dc
, wxRichTextDrawingContext
& context
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
1270 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1273 wxRichTextObject
* child
= node
->GetData();
1275 // Don't recurse if the child is a top-level object,
1276 // such as a text box, because the character position will no longer
1277 // apply. By definition, a top-level object has its own range of
1278 // character positions.
1279 if (!child
->IsTopLevel() && child
->FindPosition(dc
, context
, index
, pt
, height
, forceLineStart
))
1282 node
= node
->GetNext();
1289 void wxRichTextCompositeObject::CalculateRange(long start
, long& end
)
1291 long current
= start
;
1292 long lastEnd
= current
;
1300 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1303 wxRichTextObject
* child
= node
->GetData();
1306 child
->CalculateRange(current
, childEnd
);
1309 current
= childEnd
+ 1;
1311 node
= node
->GetNext();
1316 // A top-level object always has a range of size 1,
1317 // because its children don't count at this level.
1319 m_range
.SetRange(start
, start
);
1321 // An object with no children has zero length
1322 if (m_children
.GetCount() == 0)
1324 m_ownRange
.SetRange(0, lastEnd
);
1330 // An object with no children has zero length
1331 if (m_children
.GetCount() == 0)
1334 m_range
.SetRange(start
, end
);
1338 /// Delete range from layout.
1339 bool wxRichTextCompositeObject::DeleteRange(const wxRichTextRange
& range
)
1341 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1345 wxRichTextObject
* obj
= (wxRichTextObject
*) node
->GetData();
1346 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
1348 // Delete the range in each paragraph
1350 // When a chunk has been deleted, internally the content does not
1351 // now match the ranges.
1352 // However, so long as deletion is not done on the same object twice this is OK.
1353 // If you may delete content from the same object twice, recalculate
1354 // the ranges inbetween DeleteRange calls by calling CalculateRanges, and
1355 // adjust the range you're deleting accordingly.
1357 if (!obj
->GetRange().IsOutside(range
))
1359 // No need to delete within a top-level object; just removing this object will do fine
1360 if (!obj
->IsTopLevel())
1361 obj
->DeleteRange(range
);
1363 // Delete an empty object, or paragraph within this range.
1364 if (obj
->IsEmpty() ||
1365 (range
.GetStart() <= obj
->GetRange().GetStart() && range
.GetEnd() >= obj
->GetRange().GetEnd()))
1367 // An empty paragraph has length 1, so won't be deleted unless the
1368 // whole range is deleted.
1369 RemoveChild(obj
, true);
1379 /// Get any text in this object for the given range
1380 wxString
wxRichTextCompositeObject::GetTextForRange(const wxRichTextRange
& range
) const
1383 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1386 wxRichTextObject
* child
= node
->GetData();
1387 wxRichTextRange childRange
= range
;
1388 if (!child
->GetRange().IsOutside(range
))
1390 childRange
.LimitTo(child
->GetRange());
1392 wxString childText
= child
->GetTextForRange(childRange
);
1396 node
= node
->GetNext();
1402 /// Get the child object at the given character position
1403 wxRichTextObject
* wxRichTextCompositeObject::GetChildAtPosition(long pos
) const
1405 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1408 wxRichTextObject
* child
= node
->GetData();
1409 if (child
->GetRange().GetStart() == pos
)
1411 node
= node
->GetNext();
1416 /// Recursively merge all pieces that can be merged.
1417 bool wxRichTextCompositeObject::Defragment(const wxRichTextRange
& range
)
1419 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1422 wxRichTextObject
* child
= node
->GetData();
1423 if (range
== wxRICHTEXT_ALL
|| !child
->GetRange().IsOutside(range
))
1425 wxRichTextCompositeObject
* composite
= wxDynamicCast(child
, wxRichTextCompositeObject
);
1427 composite
->Defragment();
1429 if (node
->GetNext())
1431 wxRichTextObject
* nextChild
= node
->GetNext()->GetData();
1432 if (child
->CanMerge(nextChild
) && child
->Merge(nextChild
))
1434 nextChild
->Dereference();
1435 m_children
.Erase(node
->GetNext());
1437 // Don't set node -- we'll see if we can merge again with the next
1441 node
= node
->GetNext();
1444 node
= node
->GetNext();
1447 node
= node
->GetNext();
1450 // Delete any remaining empty objects, but leave at least one empty object per composite object.
1451 if (GetChildCount() > 1)
1453 node
= m_children
.GetFirst();
1456 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
1457 wxRichTextObject
* child
= node
->GetData();
1458 if (range
== wxRICHTEXT_ALL
|| !child
->GetRange().IsOutside(range
))
1460 if (child
->IsEmpty())
1462 child
->Dereference();
1463 m_children
.Erase(node
);
1468 node
= node
->GetNext();
1475 /// Dump to output stream for debugging
1476 void wxRichTextCompositeObject::Dump(wxTextOutputStream
& stream
)
1478 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1481 wxRichTextObject
* child
= node
->GetData();
1482 child
->Dump(stream
);
1483 node
= node
->GetNext();
1487 /// Get/set the object size for the given range. Returns false if the range
1488 /// is invalid for this object.
1489 bool wxRichTextCompositeObject::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
1491 if (!range
.IsWithin(GetRange()))
1496 wxArrayInt childExtents
;
1503 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1506 wxRichTextObject
* child
= node
->GetData();
1507 if (!child
->GetRange().IsOutside(range
))
1509 // Floating objects have a zero size within the paragraph.
1510 if (child
->IsFloating())
1515 if (partialExtents
->GetCount() > 0)
1516 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
1520 partialExtents
->Add(0 /* zero size */ + lastSize
);
1527 wxRichTextRange rangeToUse
= range
;
1528 rangeToUse
.LimitTo(child
->GetRange());
1529 if (child
->IsTopLevel())
1530 rangeToUse
= child
->GetOwnRange();
1532 int childDescent
= 0;
1534 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we're already cached the size,
1535 // but it's only going to be used after caching has taken place.
1536 if ((flags
& wxRICHTEXT_HEIGHT_ONLY
) && child
->GetCachedSize().y
!= 0)
1538 childDescent
= child
->GetDescent();
1539 childSize
= child
->GetCachedSize();
1541 sz
.y
= wxMax(sz
.y
, childSize
.y
);
1542 sz
.x
+= childSize
.x
;
1543 descent
= wxMax(descent
, childDescent
);
1545 else if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, context
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
), p
))
1547 sz
.y
= wxMax(sz
.y
, childSize
.y
);
1548 sz
.x
+= childSize
.x
;
1549 descent
= wxMax(descent
, childDescent
);
1551 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange() || child
->IsTopLevel()))
1553 child
->SetCachedSize(childSize
);
1554 child
->SetDescent(childDescent
);
1560 if (partialExtents
->GetCount() > 0)
1561 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
1566 for (i
= 0; i
< childExtents
.GetCount(); i
++)
1568 partialExtents
->Add(childExtents
[i
] + lastSize
);
1578 node
= node
->GetNext();
1584 // Invalidate the buffer. With no argument, invalidates whole buffer.
1585 void wxRichTextCompositeObject::Invalidate(const wxRichTextRange
& invalidRange
)
1587 wxRichTextObject::Invalidate(invalidRange
);
1589 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1592 wxRichTextObject
* child
= node
->GetData();
1593 if (invalidRange
!= wxRICHTEXT_ALL
&& invalidRange
!= wxRICHTEXT_NONE
&& child
->GetRange().IsOutside(invalidRange
))
1597 else if (child
->IsTopLevel())
1599 if (invalidRange
== wxRICHTEXT_NONE
)
1600 child
->Invalidate(wxRICHTEXT_NONE
);
1602 child
->Invalidate(wxRICHTEXT_ALL
); // All children must be invalidated if within parent range
1605 child
->Invalidate(invalidRange
);
1606 node
= node
->GetNext();
1610 // Move the object recursively, by adding the offset from old to new
1611 void wxRichTextCompositeObject::Move(const wxPoint
& pt
)
1613 wxPoint oldPos
= GetPosition();
1615 wxPoint offset
= pt
- oldPos
;
1617 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1620 wxRichTextObject
* child
= node
->GetData();
1621 wxPoint childPos
= child
->GetPosition() + offset
;
1622 child
->Move(childPos
);
1623 node
= node
->GetNext();
1629 * wxRichTextParagraphLayoutBox
1630 * This box knows how to lay out paragraphs.
1633 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraphLayoutBox
, wxRichTextCompositeObject
)
1635 wxRichTextParagraphLayoutBox::wxRichTextParagraphLayoutBox(wxRichTextObject
* parent
):
1636 wxRichTextCompositeObject(parent
)
1641 wxRichTextParagraphLayoutBox::~wxRichTextParagraphLayoutBox()
1643 if (m_floatCollector
)
1645 delete m_floatCollector
;
1646 m_floatCollector
= NULL
;
1650 /// Initialize the object.
1651 void wxRichTextParagraphLayoutBox::Init()
1655 // For now, assume is the only box and has no initial size.
1656 m_range
= wxRichTextRange(0, -1);
1657 m_ownRange
= wxRichTextRange(0, -1);
1659 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 // Fix the width if we're at the top level
1875 attr
.GetTextBoxAttr().GetWidth().SetValue(rect
.GetWidth(), wxTEXT_ATTR_UNITS_PIXELS
);
1877 int leftMargin
, rightMargin
, topMargin
, bottomMargin
;
1878 wxRichTextObject::GetTotalMargin(dc
, GetBuffer(), attr
, leftMargin
, rightMargin
,
1879 topMargin
, bottomMargin
);
1884 // The maximum paragraph maximum width, so we can set the overall maximum width for this object
1885 int maxMaxWidth
= 0;
1887 // The maximum paragraph minimum width, so we can set the overall minimum width for this object
1888 int maxMinWidth
= 0;
1890 // If we have vertical alignment, we must recalculate everything.
1891 bool hasVerticalAlignment
= (attr
.GetTextBoxAttr().HasVerticalAlignment() &&
1892 (attr
.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP
));
1894 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1896 bool layoutAll
= true;
1898 // Get invalid range, rounding to paragraph start/end.
1899 wxRichTextRange invalidRange
= GetInvalidRange(true);
1901 if (invalidRange
== wxRICHTEXT_NONE
&& !formatRect
)
1904 if (invalidRange
== wxRICHTEXT_ALL
|| hasVerticalAlignment
)
1906 else // If we know what range is affected, start laying out from that point on.
1907 if (invalidRange
.GetStart() >= GetOwnRange().GetStart())
1909 wxRichTextParagraph
* firstParagraph
= GetParagraphAtPosition(invalidRange
.GetStart());
1912 wxRichTextObjectList::compatibility_iterator firstNode
= m_children
.Find(firstParagraph
);
1913 wxRichTextObjectList::compatibility_iterator previousNode
;
1915 previousNode
= firstNode
->GetPrevious();
1920 wxRichTextParagraph
* previousParagraph
= wxDynamicCast(previousNode
->GetData(), wxRichTextParagraph
);
1921 availableSpace
.y
= previousParagraph
->GetPosition().y
+ previousParagraph
->GetCachedSize().y
;
1924 // Now we're going to start iterating from the first affected paragraph.
1932 // Gather information about only those floating objects that will not be formatted,
1933 // after which floats will be gathered per-paragraph during layout.
1934 UpdateFloatingObjects(availableSpace
, node
? node
->GetData() : (wxRichTextObject
*) NULL
);
1936 // A way to force speedy rest-of-buffer layout (the 'else' below)
1937 bool forceQuickLayout
= false;
1939 // First get the size of the paragraphs we won't be laying out
1940 wxRichTextObjectList::compatibility_iterator n
= m_children
.GetFirst();
1941 while (n
&& n
!= node
)
1943 wxRichTextParagraph
* child
= wxDynamicCast(n
->GetData(), wxRichTextParagraph
);
1946 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
1947 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
1948 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
1955 // Assume this box only contains paragraphs
1957 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
1958 // Unsure if this is needed
1959 // wxCHECK_MSG( child, false, wxT("Unknown object in layout") );
1961 if (child
&& child
->IsShown())
1963 // TODO: what if the child hasn't been laid out (e.g. involved in Undo) but still has 'old' lines
1964 if ( !forceQuickLayout
&&
1966 child
->GetLines().IsEmpty() ||
1967 !child
->GetRange().IsOutside(invalidRange
)) )
1969 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1970 // lays out the object again using the minimum size
1971 child
->LayoutToBestSize(dc
, context
, GetBuffer(),
1972 attr
, child
->GetAttributes(), availableSpace
, rect
, style
&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT
);
1974 // Layout must set the cached size
1975 availableSpace
.y
+= child
->GetCachedSize().y
;
1976 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
1977 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
1978 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
1980 // If we're just formatting the visible part of the buffer,
1981 // and we're now past the bottom of the window, and we don't have any
1982 // floating objects (since they may cause wrapping to change for the rest of the
1983 // the buffer), start quick layout.
1984 if (!hasVerticalAlignment
&& formatRect
&& child
->GetPosition().y
> rect
.GetBottom() && GetFloatingObjectCount() == 0)
1985 forceQuickLayout
= true;
1989 // We're outside the immediately affected range, so now let's just
1990 // move everything up or down. This assumes that all the children have previously
1991 // been laid out and have wrapped line lists associated with them.
1992 // TODO: check all paragraphs before the affected range.
1994 int inc
= availableSpace
.y
- child
->GetPosition().y
;
1998 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2001 if (child
->GetLines().GetCount() == 0)
2003 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
2004 // lays out the object again using the minimum size
2005 child
->LayoutToBestSize(dc
, context
, GetBuffer(),
2006 attr
, child
->GetAttributes(), availableSpace
, rect
, style
&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT
);
2008 //child->Layout(dc, availableChildRect, style);
2011 child
->Move(wxPoint(child
->GetPosition().x
, child
->GetPosition().y
+ inc
));
2013 availableSpace
.y
+= child
->GetCachedSize().y
;
2014 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
2015 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
2016 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
2019 node
= node
->GetNext();
2025 node
= node
->GetNext();
2028 node
= m_children
.GetLast();
2029 if (node
&& node
->GetData()->IsShown())
2031 wxRichTextObject
* child
= node
->GetData();
2032 maxHeight
= child
->GetPosition().y
- (GetPosition().y
+ topMargin
) + child
->GetCachedSize().y
;
2035 maxHeight
= 0; // topMargin + bottomMargin;
2037 // Check the bottom edge of any floating object
2038 if (GetFloatCollector() && GetFloatCollector()->HasFloats())
2040 int bottom
= GetFloatCollector()->GetLastRectBottom();
2041 if (bottom
> maxHeight
)
2045 if (attr
.GetTextBoxAttr().GetSize().GetWidth().IsValid())
2047 wxRect r
= AdjustAvailableSpace(dc
, GetBuffer(), wxRichTextAttr() /* not used */, attr
, parentRect
, parentRect
);
2048 int w
= r
.GetWidth();
2050 // Convert external to content rect
2051 w
= w
- leftMargin
- rightMargin
;
2052 maxWidth
= wxMax(maxWidth
, w
);
2053 maxMaxWidth
= wxMax(maxMaxWidth
, w
);
2056 // TODO: (also in para layout) should set the
2057 // object's size to an absolute one if specified,
2058 // but if not specified, calculate it from content.
2060 // We need to add back the margins etc.
2062 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2063 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxWidth
, maxHeight
));
2064 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2065 SetCachedSize(marginRect
.GetSize());
2068 // The maximum size is the greatest of all maximum widths for all paragraphs.
2070 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2071 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxMaxWidth
, maxHeight
)); // Actually max height is a lie, we can't know it
2072 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2073 SetMaxSize(marginRect
.GetSize());
2076 // The minimum size is the greatest of all minimum widths for all paragraphs.
2078 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2079 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxMinWidth
, maxHeight
)); // Actually max height is a lie, we can't know it
2080 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2081 SetMinSize(marginRect
.GetSize());
2084 if (attr
.GetTextBoxAttr().HasVerticalAlignment() &&
2085 (attr
.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP
))
2088 int leftOverSpace
= availableSpace
.height
- topMargin
- bottomMargin
- maxHeight
;
2089 if (leftOverSpace
> 0)
2091 if (attr
.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_CENTRE
)
2093 yOffset
= (leftOverSpace
/2);
2095 else if (attr
.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_BOTTOM
)
2097 yOffset
= leftOverSpace
;
2101 // Move all the children to vertically align the content
2102 // This doesn't take into account floating objects, unfortunately.
2105 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2108 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2110 child
->Move(wxPoint(child
->GetPosition().x
, child
->GetPosition().y
+ yOffset
));
2112 node
= node
->GetNext();
2117 m_invalidRange
= wxRICHTEXT_NONE
;
2122 /// Get/set the size for the given range.
2123 bool wxRichTextParagraphLayoutBox::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* WXUNUSED(partialExtents
)) const
2127 wxRichTextObjectList::compatibility_iterator startPara
= wxRichTextObjectList::compatibility_iterator();
2128 wxRichTextObjectList::compatibility_iterator endPara
= wxRichTextObjectList::compatibility_iterator();
2130 // First find the first paragraph whose starting position is within the range.
2131 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2134 // child is a paragraph
2135 wxRichTextObject
* child
= node
->GetData();
2136 const wxRichTextRange
& r
= child
->GetRange();
2138 if (r
.GetStart() <= range
.GetStart() && r
.GetEnd() >= range
.GetStart())
2144 node
= node
->GetNext();
2147 // Next find the last paragraph containing part of the range
2148 node
= m_children
.GetFirst();
2151 // child is a paragraph
2152 wxRichTextObject
* child
= node
->GetData();
2153 const wxRichTextRange
& r
= child
->GetRange();
2155 if (r
.GetStart() <= range
.GetEnd() && r
.GetEnd() >= range
.GetEnd())
2161 node
= node
->GetNext();
2164 if (!startPara
|| !endPara
)
2167 // Now we can add up the sizes
2168 for (node
= startPara
; node
; node
= node
->GetNext())
2170 // child is a paragraph
2171 wxRichTextObject
* child
= node
->GetData();
2172 const wxRichTextRange
& childRange
= child
->GetRange();
2173 wxRichTextRange rangeToFind
= range
;
2174 rangeToFind
.LimitTo(childRange
);
2176 if (child
->IsTopLevel())
2177 rangeToFind
= child
->GetOwnRange();
2181 int childDescent
= 0;
2182 child
->GetRangeSize(rangeToFind
, childSize
, childDescent
, dc
, context
, flags
, position
);
2184 descent
= wxMax(childDescent
, descent
);
2186 sz
.x
= wxMax(sz
.x
, childSize
.x
);
2187 sz
.y
+= childSize
.y
;
2189 if (node
== endPara
)
2198 /// Get the paragraph at the given position
2199 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphAtPosition(long pos
, bool caretPosition
) const
2204 // First find the first paragraph whose starting position is within the range.
2205 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2208 // child is a paragraph
2209 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2210 // wxASSERT (child != NULL);
2214 // Return first child in buffer if position is -1
2218 if (child
->GetRange().Contains(pos
))
2222 node
= node
->GetNext();
2227 /// Get the line at the given position
2228 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineAtPosition(long pos
, bool caretPosition
) const
2233 // First find the first paragraph whose starting position is within the range.
2234 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2237 wxRichTextObject
* obj
= (wxRichTextObject
*) node
->GetData();
2238 if (obj
->GetRange().Contains(pos
))
2240 // child is a paragraph
2241 wxRichTextParagraph
* child
= wxDynamicCast(obj
, wxRichTextParagraph
);
2242 // wxASSERT (child != NULL);
2246 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2249 wxRichTextLine
* line
= node2
->GetData();
2251 wxRichTextRange range
= line
->GetAbsoluteRange();
2253 if (range
.Contains(pos
) ||
2255 // If the position is end-of-paragraph, then return the last line of
2256 // of the paragraph.
2257 ((range
.GetEnd() == child
->GetRange().GetEnd()-1) && (pos
== child
->GetRange().GetEnd())))
2260 node2
= node2
->GetNext();
2265 node
= node
->GetNext();
2268 int lineCount
= GetLineCount();
2270 return GetLineForVisibleLineNumber(lineCount
-1);
2275 /// Get the line at the given y pixel position, or the last line.
2276 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineAtYPosition(int y
) const
2278 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2281 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2282 // wxASSERT (child != NULL);
2286 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2289 wxRichTextLine
* line
= node2
->GetData();
2291 wxRect
rect(line
->GetRect());
2293 if (y
<= rect
.GetBottom())
2296 node2
= node2
->GetNext();
2300 node
= node
->GetNext();
2304 int lineCount
= GetLineCount();
2306 return GetLineForVisibleLineNumber(lineCount
-1);
2311 /// Get the number of visible lines
2312 int wxRichTextParagraphLayoutBox::GetLineCount() const
2316 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2319 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2320 // wxASSERT (child != NULL);
2323 count
+= child
->GetLines().GetCount();
2325 node
= node
->GetNext();
2331 /// Get the paragraph for a given line
2332 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphForLine(wxRichTextLine
* line
) const
2334 return GetParagraphAtPosition(line
->GetAbsoluteRange().GetStart());
2337 /// Get the line size at the given position
2338 wxSize
wxRichTextParagraphLayoutBox::GetLineSizeAtPosition(long pos
, bool caretPosition
) const
2340 wxRichTextLine
* line
= GetLineAtPosition(pos
, caretPosition
);
2343 return line
->GetSize();
2346 return wxSize(0, 0);
2350 /// Convenience function to add a paragraph of text
2351 wxRichTextRange
wxRichTextParagraphLayoutBox::AddParagraph(const wxString
& text
, wxRichTextAttr
* paraStyle
)
2353 // Don't use the base style, just the default style, and the base style will
2354 // be combined at display time.
2355 // Divide into paragraph and character styles.
2357 wxRichTextAttr defaultCharStyle
;
2358 wxRichTextAttr defaultParaStyle
;
2360 // If the default style is a named paragraph style, don't apply any character formatting
2361 // to the initial text string.
2362 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2364 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2366 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2369 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2371 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2372 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2374 wxRichTextParagraph
* para
= new wxRichTextParagraph(text
, this, pStyle
, cStyle
);
2380 return para
->GetRange();
2383 /// Adds multiple paragraphs, based on newlines.
2384 wxRichTextRange
wxRichTextParagraphLayoutBox::AddParagraphs(const wxString
& text
, wxRichTextAttr
* paraStyle
)
2386 // Don't use the base style, just the default style, and the base style will
2387 // be combined at display time.
2388 // Divide into paragraph and character styles.
2390 wxRichTextAttr defaultCharStyle
;
2391 wxRichTextAttr defaultParaStyle
;
2393 // If the default style is a named paragraph style, don't apply any character formatting
2394 // to the initial text string.
2395 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2397 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2399 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2402 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2404 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2405 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2407 wxRichTextParagraph
* firstPara
= NULL
;
2408 wxRichTextParagraph
* lastPara
= NULL
;
2410 wxRichTextRange
range(-1, -1);
2413 size_t len
= text
.length();
2415 wxRichTextParagraph
* para
= new wxRichTextParagraph(wxEmptyString
, this, pStyle
, cStyle
);
2424 wxChar ch
= text
[i
];
2425 if (ch
== wxT('\n') || ch
== wxT('\r'))
2429 wxRichTextPlainText
* plainText
= (wxRichTextPlainText
*) para
->GetChildren().GetFirst()->GetData();
2430 plainText
->SetText(line
);
2432 para
= new wxRichTextParagraph(wxEmptyString
, this, pStyle
, cStyle
);
2437 line
= wxEmptyString
;
2448 wxRichTextPlainText
* plainText
= (wxRichTextPlainText
*) para
->GetChildren().GetFirst()->GetData();
2449 plainText
->SetText(line
);
2454 return wxRichTextRange(firstPara
->GetRange().GetStart(), lastPara
->GetRange().GetEnd());
2457 /// Convenience function to add an image
2458 wxRichTextRange
wxRichTextParagraphLayoutBox::AddImage(const wxImage
& image
, wxRichTextAttr
* paraStyle
)
2460 // Don't use the base style, just the default style, and the base style will
2461 // be combined at display time.
2462 // Divide into paragraph and character styles.
2464 wxRichTextAttr defaultCharStyle
;
2465 wxRichTextAttr defaultParaStyle
;
2467 // If the default style is a named paragraph style, don't apply any character formatting
2468 // to the initial text string.
2469 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2471 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2473 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2476 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2478 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2479 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2481 wxRichTextParagraph
* para
= new wxRichTextParagraph(this, pStyle
);
2483 para
->AppendChild(new wxRichTextImage(image
, this, cStyle
));
2487 return para
->GetRange();
2491 /// Insert fragment into this box at the given position. If partialParagraph is true,
2492 /// it is assumed that the last (or only) paragraph is just a piece of data with no paragraph
2495 bool wxRichTextParagraphLayoutBox::InsertFragment(long position
, wxRichTextParagraphLayoutBox
& fragment
)
2497 // First, find the first paragraph whose starting position is within the range.
2498 wxRichTextParagraph
* para
= GetParagraphAtPosition(position
);
2501 wxRichTextAttr originalAttr
= para
->GetAttributes();
2503 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(para
);
2505 // Now split at this position, returning the object to insert the new
2506 // ones in front of.
2507 wxRichTextObject
* nextObject
= para
->SplitAt(position
);
2509 // Special case: partial paragraph, just one paragraph. Might be a small amount of
2510 // text, for example, so let's optimize.
2512 if (fragment
.GetPartialParagraph() && fragment
.GetChildren().GetCount() == 1)
2514 // Add the first para to this para...
2515 wxRichTextObjectList::compatibility_iterator firstParaNode
= fragment
.GetChildren().GetFirst();
2519 // Iterate through the fragment paragraph inserting the content into this paragraph.
2520 wxRichTextParagraph
* firstPara
= wxDynamicCast(firstParaNode
->GetData(), wxRichTextParagraph
);
2521 wxASSERT (firstPara
!= NULL
);
2523 wxRichTextObjectList::compatibility_iterator objectNode
= firstPara
->GetChildren().GetFirst();
2526 wxRichTextObject
* newObj
= objectNode
->GetData()->Clone();
2531 para
->AppendChild(newObj
);
2535 // Insert before nextObject
2536 para
->InsertChild(newObj
, nextObject
);
2539 objectNode
= objectNode
->GetNext();
2546 // Procedure for inserting a fragment consisting of a number of
2549 // 1. Remove and save the content that's after the insertion point, for adding
2550 // back once we've added the fragment.
2551 // 2. Add the content from the first fragment paragraph to the current
2553 // 3. Add remaining fragment paragraphs after the current paragraph.
2554 // 4. Add back the saved content from the first paragraph. If partialParagraph
2555 // is true, add it to the last paragraph added and not a new one.
2557 // 1. Remove and save objects after split point.
2558 wxList savedObjects
;
2560 para
->MoveToList(nextObject
, savedObjects
);
2562 // 2. Add the content from the 1st fragment paragraph.
2563 wxRichTextObjectList::compatibility_iterator firstParaNode
= fragment
.GetChildren().GetFirst();
2567 wxRichTextParagraph
* firstPara
= wxDynamicCast(firstParaNode
->GetData(), wxRichTextParagraph
);
2568 wxASSERT(firstPara
!= NULL
);
2570 if (!(fragment
.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
))
2571 para
->SetAttributes(firstPara
->GetAttributes());
2573 // Save empty paragraph attributes for appending later
2574 // These are character attributes deliberately set for a new paragraph. Without this,
2575 // we couldn't pass default attributes when appending a new paragraph.
2576 wxRichTextAttr emptyParagraphAttributes
;
2578 wxRichTextObjectList::compatibility_iterator objectNode
= firstPara
->GetChildren().GetFirst();
2580 if (objectNode
&& firstPara
->GetChildren().GetCount() == 1 && objectNode
->GetData()->IsEmpty())
2581 emptyParagraphAttributes
= objectNode
->GetData()->GetAttributes();
2585 wxRichTextObject
* newObj
= objectNode
->GetData()->Clone();
2588 para
->AppendChild(newObj
);
2590 objectNode
= objectNode
->GetNext();
2593 // 3. Add remaining fragment paragraphs after the current paragraph.
2594 wxRichTextObjectList::compatibility_iterator nextParagraphNode
= node
->GetNext();
2595 wxRichTextObject
* nextParagraph
= NULL
;
2596 if (nextParagraphNode
)
2597 nextParagraph
= nextParagraphNode
->GetData();
2599 wxRichTextObjectList::compatibility_iterator i
= fragment
.GetChildren().GetFirst()->GetNext();
2600 wxRichTextParagraph
* finalPara
= para
;
2602 bool needExtraPara
= (!i
|| !fragment
.GetPartialParagraph());
2604 // If there was only one paragraph, we need to insert a new one.
2607 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2608 wxASSERT( para
!= NULL
);
2610 finalPara
= (wxRichTextParagraph
*) para
->Clone();
2613 InsertChild(finalPara
, nextParagraph
);
2615 AppendChild(finalPara
);
2620 // If there was only one paragraph, or we have full paragraphs in our fragment,
2621 // we need to insert a new one.
2624 finalPara
= new wxRichTextParagraph
;
2627 InsertChild(finalPara
, nextParagraph
);
2629 AppendChild(finalPara
);
2632 // 4. Add back the remaining content.
2636 finalPara
->MoveFromList(savedObjects
);
2638 // Ensure there's at least one object
2639 if (finalPara
->GetChildCount() == 0)
2641 wxRichTextPlainText
* text
= new wxRichTextPlainText(wxEmptyString
);
2642 text
->SetAttributes(emptyParagraphAttributes
);
2644 finalPara
->AppendChild(text
);
2648 if ((fragment
.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
) && firstPara
)
2649 finalPara
->SetAttributes(firstPara
->GetAttributes());
2650 else if (finalPara
&& finalPara
!= para
)
2651 finalPara
->SetAttributes(originalAttr
);
2659 wxRichTextObjectList::compatibility_iterator i
= fragment
.GetChildren().GetFirst();
2662 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2663 wxASSERT( para
!= NULL
);
2665 AppendChild(para
->Clone());
2674 /// Make a copy of the fragment corresponding to the given range, putting it in 'fragment'.
2675 /// If there was an incomplete paragraph at the end, partialParagraph is set to true.
2676 bool wxRichTextParagraphLayoutBox::CopyFragment(const wxRichTextRange
& range
, wxRichTextParagraphLayoutBox
& fragment
)
2678 wxRichTextObjectList::compatibility_iterator i
= GetChildren().GetFirst();
2681 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2682 wxASSERT( para
!= NULL
);
2684 if (!para
->GetRange().IsOutside(range
))
2686 fragment
.AppendChild(para
->Clone());
2691 // Now top and tail the first and last paragraphs in our new fragment (which might be the same).
2692 if (!fragment
.IsEmpty())
2694 wxRichTextParagraph
* firstPara
= wxDynamicCast(fragment
.GetChildren().GetFirst()->GetData(), wxRichTextParagraph
);
2695 wxASSERT( firstPara
!= NULL
);
2697 wxRichTextParagraph
* lastPara
= wxDynamicCast(fragment
.GetChildren().GetLast()->GetData(), wxRichTextParagraph
);
2698 wxASSERT( lastPara
!= NULL
);
2700 if (!firstPara
|| !lastPara
)
2703 bool isFragment
= (range
.GetEnd() < lastPara
->GetRange().GetEnd());
2705 long firstPos
= firstPara
->GetRange().GetStart();
2707 // Adjust for renumbering from zero
2708 wxRichTextRange
topTailRange(range
.GetStart() - firstPos
, range
.GetEnd() - firstPos
);
2711 fragment
.CalculateRange(0, end
);
2713 // Chop off the start of the paragraph
2714 if (topTailRange
.GetStart() > 0)
2716 wxRichTextRange
r(0, topTailRange
.GetStart()-1);
2717 firstPara
->DeleteRange(r
);
2719 // Make sure the numbering is correct
2720 fragment
.CalculateRange(0, end
);
2722 // Now, we've deleted some positions, so adjust the range
2724 topTailRange
.SetStart(range
.GetLength());
2725 topTailRange
.SetEnd(fragment
.GetOwnRange().GetEnd());
2729 topTailRange
.SetStart(range
.GetLength());
2730 topTailRange
.SetEnd(fragment
.GetOwnRange().GetEnd());
2733 if (topTailRange
.GetStart() < lastPara
->GetRange().GetEnd())
2735 lastPara
->DeleteRange(topTailRange
);
2737 // Make sure the numbering is correct
2739 fragment
.CalculateRange(0, end
);
2741 // We only have part of a paragraph at the end
2742 fragment
.SetPartialParagraph(true);
2746 // We have a partial paragraph (don't save last new paragraph marker)
2747 // or complete paragraph
2748 fragment
.SetPartialParagraph(isFragment
);
2755 /// Given a position, get the number of the visible line (potentially many to a paragraph),
2756 /// starting from zero at the start of the buffer.
2757 long wxRichTextParagraphLayoutBox::GetVisibleLineNumber(long pos
, bool caretPosition
, bool startOfLine
) const
2764 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2767 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2768 // wxASSERT( child != NULL );
2772 if (child
->GetRange().Contains(pos
))
2774 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2777 wxRichTextLine
* line
= node2
->GetData();
2778 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
2780 if (lineRange
.Contains(pos
) || pos
== lineRange
.GetStart())
2782 // If the caret is displayed at the end of the previous wrapped line,
2783 // we want to return the line it's _displayed_ at (not the actual line
2784 // containing the position).
2785 if (lineRange
.GetStart() == pos
&& !startOfLine
&& child
->GetRange().GetStart() != pos
)
2786 return lineCount
- 1;
2793 node2
= node2
->GetNext();
2795 // If we didn't find it in the lines, it must be
2796 // the last position of the paragraph. So return the last line.
2800 lineCount
+= child
->GetLines().GetCount();
2803 node
= node
->GetNext();
2810 /// Given a line number, get the corresponding wxRichTextLine object.
2811 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineForVisibleLineNumber(long lineNumber
) const
2815 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2818 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2819 // wxASSERT(child != NULL);
2823 if (lineNumber
< (int) (child
->GetLines().GetCount() + lineCount
))
2825 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2828 wxRichTextLine
* line
= node2
->GetData();
2830 if (lineCount
== lineNumber
)
2835 node2
= node2
->GetNext();
2839 lineCount
+= child
->GetLines().GetCount();
2842 node
= node
->GetNext();
2849 /// Delete range from layout.
2850 bool wxRichTextParagraphLayoutBox::DeleteRange(const wxRichTextRange
& range
)
2852 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2854 wxRichTextParagraph
* firstPara
= NULL
;
2857 wxRichTextParagraph
* obj
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2858 // wxASSERT (obj != NULL);
2860 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
2864 // Delete the range in each paragraph
2866 if (!obj
->GetRange().IsOutside(range
))
2868 // Deletes the content of this object within the given range
2869 obj
->DeleteRange(range
);
2871 wxRichTextRange thisRange
= obj
->GetRange();
2872 wxRichTextAttr thisAttr
= obj
->GetAttributes();
2874 // If the whole paragraph is within the range to delete,
2875 // delete the whole thing.
2876 if (range
.GetStart() <= thisRange
.GetStart() && range
.GetEnd() >= thisRange
.GetEnd())
2878 // Delete the whole object
2879 RemoveChild(obj
, true);
2882 else if (!firstPara
)
2885 // If the range includes the paragraph end, we need to join this
2886 // and the next paragraph.
2887 if (range
.GetEnd() <= thisRange
.GetEnd())
2889 // We need to move the objects from the next paragraph
2890 // to this paragraph
2892 wxRichTextParagraph
* nextParagraph
= NULL
;
2893 if ((range
.GetEnd() < thisRange
.GetEnd()) && obj
)
2894 nextParagraph
= obj
;
2897 // We're ending at the end of the paragraph, so merge the _next_ paragraph.
2899 nextParagraph
= wxDynamicCast(next
->GetData(), wxRichTextParagraph
);
2902 bool applyFinalParagraphStyle
= firstPara
&& nextParagraph
&& nextParagraph
!= firstPara
;
2904 wxRichTextAttr nextParaAttr
;
2905 if (applyFinalParagraphStyle
)
2907 // Special case when deleting the end of a paragraph - use _this_ paragraph's style,
2908 // not the next one.
2909 if (range
.GetStart() == range
.GetEnd() && range
.GetStart() == thisRange
.GetEnd())
2910 nextParaAttr
= thisAttr
;
2912 nextParaAttr
= nextParagraph
->GetAttributes();
2915 if (firstPara
&& nextParagraph
&& firstPara
!= nextParagraph
)
2917 // Move the objects to the previous para
2918 wxRichTextObjectList::compatibility_iterator node1
= nextParagraph
->GetChildren().GetFirst();
2922 wxRichTextObject
* obj1
= node1
->GetData();
2924 firstPara
->AppendChild(obj1
);
2926 wxRichTextObjectList::compatibility_iterator next1
= node1
->GetNext();
2927 nextParagraph
->GetChildren().Erase(node1
);
2932 // Delete the paragraph
2933 RemoveChild(nextParagraph
, true);
2936 // Avoid empty paragraphs
2937 if (firstPara
&& firstPara
->GetChildren().GetCount() == 0)
2939 wxRichTextPlainText
* text
= new wxRichTextPlainText(wxEmptyString
);
2940 firstPara
->AppendChild(text
);
2943 if (applyFinalParagraphStyle
)
2944 firstPara
->SetAttributes(nextParaAttr
);
2957 /// Get any text in this object for the given range
2958 wxString
wxRichTextParagraphLayoutBox::GetTextForRange(const wxRichTextRange
& range
) const
2962 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2965 wxRichTextObject
* child
= node
->GetData();
2966 if (!child
->GetRange().IsOutside(range
))
2968 wxRichTextRange childRange
= range
;
2969 childRange
.LimitTo(child
->GetRange());
2971 wxString childText
= child
->GetTextForRange(childRange
);
2975 if ((childRange
.GetEnd() == child
->GetRange().GetEnd()) && node
->GetNext())
2980 node
= node
->GetNext();
2986 /// Get all the text
2987 wxString
wxRichTextParagraphLayoutBox::GetText() const
2989 return GetTextForRange(GetOwnRange());
2992 /// Get the paragraph by number
2993 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphAtLine(long paragraphNumber
) const
2995 if ((size_t) paragraphNumber
>= GetChildCount())
2998 return (wxRichTextParagraph
*) GetChild((size_t) paragraphNumber
);
3001 /// Get the length of the paragraph
3002 int wxRichTextParagraphLayoutBox::GetParagraphLength(long paragraphNumber
) const
3004 wxRichTextParagraph
* para
= GetParagraphAtLine(paragraphNumber
);
3006 return para
->GetRange().GetLength() - 1; // don't include newline
3011 /// Get the text of the paragraph
3012 wxString
wxRichTextParagraphLayoutBox::GetParagraphText(long paragraphNumber
) const
3014 wxRichTextParagraph
* para
= GetParagraphAtLine(paragraphNumber
);
3016 return para
->GetTextForRange(para
->GetRange());
3018 return wxEmptyString
;
3021 /// Convert zero-based line column and paragraph number to a position.
3022 long wxRichTextParagraphLayoutBox::XYToPosition(long x
, long y
) const
3024 wxRichTextParagraph
* para
= GetParagraphAtLine(y
);
3027 return para
->GetRange().GetStart() + x
;
3033 /// Convert zero-based position to line column and paragraph number
3034 bool wxRichTextParagraphLayoutBox::PositionToXY(long pos
, long* x
, long* y
) const
3036 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
);
3040 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3043 wxRichTextObject
* child
= node
->GetData();
3047 node
= node
->GetNext();
3051 *x
= pos
- para
->GetRange().GetStart();
3059 /// Get the leaf object in a paragraph at this position.
3060 /// Given a line number, get the corresponding wxRichTextLine object.
3061 wxRichTextObject
* wxRichTextParagraphLayoutBox::GetLeafObjectAtPosition(long position
) const
3063 wxRichTextParagraph
* para
= GetParagraphAtPosition(position
);
3066 wxRichTextObjectList::compatibility_iterator node
= para
->GetChildren().GetFirst();
3070 wxRichTextObject
* child
= node
->GetData();
3071 if (child
->GetRange().Contains(position
))
3074 node
= node
->GetNext();
3076 if (position
== para
->GetRange().GetEnd() && para
->GetChildCount() > 0)
3077 return para
->GetChildren().GetLast()->GetData();
3082 /// Set character or paragraph text attributes: apply character styles only to immediate text nodes
3083 bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange
& range
, const wxRichTextAttr
& style
, int flags
)
3085 bool characterStyle
= false;
3086 bool paragraphStyle
= false;
3088 if (style
.IsCharacterStyle())
3089 characterStyle
= true;
3090 if (style
.IsParagraphStyle())
3091 paragraphStyle
= true;
3093 wxRichTextBuffer
* buffer
= GetBuffer();
3095 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
3096 bool applyMinimal
= ((flags
& wxRICHTEXT_SETSTYLE_OPTIMIZE
) != 0);
3097 bool parasOnly
= ((flags
& wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY
) != 0);
3098 bool charactersOnly
= ((flags
& wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY
) != 0);
3099 bool resetExistingStyle
= ((flags
& wxRICHTEXT_SETSTYLE_RESET
) != 0);
3100 bool removeStyle
= ((flags
& wxRICHTEXT_SETSTYLE_REMOVE
) != 0);
3102 // Apply paragraph style first, if any
3103 wxRichTextAttr
wholeStyle(style
);
3105 if (!removeStyle
&& wholeStyle
.HasParagraphStyleName() && buffer
->GetStyleSheet())
3107 wxRichTextParagraphStyleDefinition
* def
= buffer
->GetStyleSheet()->FindParagraphStyle(wholeStyle
.GetParagraphStyleName());
3109 wxRichTextApplyStyle(wholeStyle
, def
->GetStyleMergedWithBase(buffer
->GetStyleSheet()));
3112 // Limit the attributes to be set to the content to only character attributes.
3113 wxRichTextAttr
characterAttributes(wholeStyle
);
3114 characterAttributes
.SetFlags(characterAttributes
.GetFlags() & (wxTEXT_ATTR_CHARACTER
));
3116 if (!removeStyle
&& characterAttributes
.HasCharacterStyleName() && buffer
->GetStyleSheet())
3118 wxRichTextCharacterStyleDefinition
* def
= buffer
->GetStyleSheet()->FindCharacterStyle(characterAttributes
.GetCharacterStyleName());
3120 wxRichTextApplyStyle(characterAttributes
, def
->GetStyleMergedWithBase(buffer
->GetStyleSheet()));
3123 // If we are associated with a control, make undoable; otherwise, apply immediately
3126 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3128 wxRichTextAction
* action
= NULL
;
3130 if (haveControl
&& withUndo
)
3132 action
= new wxRichTextAction(NULL
, _("Change Style"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
3133 action
->SetRange(range
);
3134 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3137 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3140 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3141 // wxASSERT (para != NULL);
3143 if (para
&& para
->GetChildCount() > 0)
3145 // Stop searching if we're beyond the range of interest
3146 if (para
->GetRange().GetStart() > range
.GetEnd())
3149 if (!para
->GetRange().IsOutside(range
))
3151 // We'll be using a copy of the paragraph to make style changes,
3152 // not updating the buffer directly.
3153 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3155 if (haveControl
&& withUndo
)
3157 newPara
= new wxRichTextParagraph(*para
);
3158 action
->GetNewParagraphs().AppendChild(newPara
);
3160 // Also store the old ones for Undo
3161 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3166 // If we're specifying paragraphs only, then we really mean character formatting
3167 // to be included in the paragraph style
3168 if ((paragraphStyle
|| parasOnly
) && !charactersOnly
)
3172 // Removes the given style from the paragraph
3173 wxRichTextRemoveStyle(newPara
->GetAttributes(), style
);
3175 else if (resetExistingStyle
)
3176 newPara
->GetAttributes() = wholeStyle
;
3181 // Only apply attributes that will make a difference to the combined
3182 // style as seen on the display
3183 wxRichTextAttr
combinedAttr(para
->GetCombinedAttributes(true));
3184 wxRichTextApplyStyle(newPara
->GetAttributes(), wholeStyle
, & combinedAttr
);
3187 wxRichTextApplyStyle(newPara
->GetAttributes(), wholeStyle
);
3191 // When applying paragraph styles dynamically, don't change the text objects' attributes
3192 // since they will computed as needed. Only apply the character styling if it's _only_
3193 // character styling. This policy is subject to change and might be put under user control.
3195 // Hm. we might well be applying a mix of paragraph and character styles, in which
3196 // case we _do_ want to apply character styles regardless of what para styles are set.
3197 // But if we're applying a paragraph style, which has some character attributes, but
3198 // we only want the paragraphs to hold this character style, then we _don't_ want to
3199 // apply the character style. So we need to be able to choose.
3201 if (!parasOnly
&& (characterStyle
|charactersOnly
) && range
.GetStart() != newPara
->GetRange().GetEnd())
3203 wxRichTextRange
childRange(range
);
3204 childRange
.LimitTo(newPara
->GetRange());
3206 // Find the starting position and if necessary split it so
3207 // we can start applying a different style.
3208 // TODO: check that the style actually changes or is different
3209 // from style outside of range
3210 wxRichTextObject
* firstObject
wxDUMMY_INITIALIZE(NULL
);
3211 wxRichTextObject
* lastObject
wxDUMMY_INITIALIZE(NULL
);
3213 if (childRange
.GetStart() == newPara
->GetRange().GetStart())
3214 firstObject
= newPara
->GetChildren().GetFirst()->GetData();
3216 firstObject
= newPara
->SplitAt(range
.GetStart());
3218 // Increment by 1 because we're apply the style one _after_ the split point
3219 long splitPoint
= childRange
.GetEnd();
3220 if (splitPoint
!= newPara
->GetRange().GetEnd())
3224 if (splitPoint
== newPara
->GetRange().GetEnd())
3225 lastObject
= newPara
->GetChildren().GetLast()->GetData();
3227 // lastObject is set as a side-effect of splitting. It's
3228 // returned as the object before the new object.
3229 (void) newPara
->SplitAt(splitPoint
, & lastObject
);
3231 wxASSERT(firstObject
!= NULL
);
3232 wxASSERT(lastObject
!= NULL
);
3234 if (!firstObject
|| !lastObject
)
3237 wxRichTextObjectList::compatibility_iterator firstNode
= newPara
->GetChildren().Find(firstObject
);
3238 wxRichTextObjectList::compatibility_iterator lastNode
= newPara
->GetChildren().Find(lastObject
);
3240 wxASSERT(firstNode
);
3243 wxRichTextObjectList::compatibility_iterator node2
= firstNode
;
3247 wxRichTextObject
* child
= node2
->GetData();
3251 // Removes the given style from the paragraph
3252 wxRichTextRemoveStyle(child
->GetAttributes(), style
);
3254 else if (resetExistingStyle
)
3255 child
->GetAttributes() = characterAttributes
;
3260 // Only apply attributes that will make a difference to the combined
3261 // style as seen on the display
3262 wxRichTextAttr
combinedAttr(newPara
->GetCombinedAttributes(child
->GetAttributes(), true));
3263 wxRichTextApplyStyle(child
->GetAttributes(), characterAttributes
, & combinedAttr
);
3266 wxRichTextApplyStyle(child
->GetAttributes(), characterAttributes
);
3269 if (node2
== lastNode
)
3272 node2
= node2
->GetNext();
3278 node
= node
->GetNext();
3281 // Do action, or delay it until end of batch.
3282 if (haveControl
&& withUndo
)
3283 buffer
->SubmitAction(action
);
3288 // Just change the attributes for this single object.
3289 void wxRichTextParagraphLayoutBox::SetStyle(wxRichTextObject
* obj
, const wxRichTextAttr
& textAttr
, int flags
)
3291 wxRichTextBuffer
* buffer
= GetBuffer();
3292 bool withUndo
= flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
;
3293 bool resetExistingStyle
= ((flags
& wxRICHTEXT_SETSTYLE_RESET
) != 0);
3294 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3296 wxRichTextAction
*action
= NULL
;
3297 wxRichTextAttr newAttr
= obj
->GetAttributes();
3298 if (resetExistingStyle
)
3301 newAttr
.Apply(textAttr
);
3303 if (haveControl
&& withUndo
)
3305 action
= new wxRichTextAction(NULL
, _("Change Object Style"), wxRICHTEXT_CHANGE_ATTRIBUTES
, buffer
, obj
->GetContainer(), buffer
->GetRichTextCtrl());
3306 action
->SetRange(obj
->GetRange().FromInternal());
3307 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3308 action
->MakeObject(obj
);
3310 action
->GetAttributes() = newAttr
;
3313 obj
->GetAttributes() = newAttr
;
3315 if (haveControl
&& withUndo
)
3316 buffer
->SubmitAction(action
);
3319 /// Get the text attributes for this position.
3320 bool wxRichTextParagraphLayoutBox::GetStyle(long position
, wxRichTextAttr
& style
)
3322 return DoGetStyle(position
, style
, true);
3325 bool wxRichTextParagraphLayoutBox::GetUncombinedStyle(long position
, wxRichTextAttr
& style
)
3327 return DoGetStyle(position
, style
, false);
3330 /// Implementation helper for GetStyle. If combineStyles is true, combine base, paragraph and
3331 /// context attributes.
3332 bool wxRichTextParagraphLayoutBox::DoGetStyle(long position
, wxRichTextAttr
& style
, bool combineStyles
)
3334 wxRichTextObject
* obj
wxDUMMY_INITIALIZE(NULL
);
3336 if (style
.IsParagraphStyle())
3338 obj
= GetParagraphAtPosition(position
);
3343 // Start with the base style
3344 style
= GetAttributes();
3346 // Apply the paragraph style
3347 wxRichTextApplyStyle(style
, obj
->GetAttributes());
3350 style
= obj
->GetAttributes();
3357 obj
= GetLeafObjectAtPosition(position
);
3362 wxRichTextParagraph
* para
= wxDynamicCast(obj
->GetParent(), wxRichTextParagraph
);
3363 style
= para
? para
->GetCombinedAttributes(obj
->GetAttributes()) : obj
->GetAttributes();
3366 style
= obj
->GetAttributes();
3374 static bool wxHasStyle(long flags
, long style
)
3376 return (flags
& style
) != 0;
3379 /// Combines 'style' with 'currentStyle' for the purpose of summarising the attributes of a range of
3381 bool wxRichTextParagraphLayoutBox::CollectStyle(wxRichTextAttr
& currentStyle
, const wxRichTextAttr
& style
, wxRichTextAttr
& clashingAttr
, wxRichTextAttr
& absentAttr
)
3383 currentStyle
.CollectCommonAttributes(style
, clashingAttr
, absentAttr
);
3388 /// Get the combined style for a range - if any attribute is different within the range,
3389 /// that attribute is not present within the flags.
3390 /// *** Note that this is not recursive, and so assumes that content inside a paragraph is not itself
3392 bool wxRichTextParagraphLayoutBox::GetStyleForRange(const wxRichTextRange
& range
, wxRichTextAttr
& style
)
3394 style
= wxRichTextAttr();
3396 wxRichTextAttr clashingAttr
;
3397 wxRichTextAttr absentAttrPara
, absentAttrChar
;
3399 wxRichTextObjectList::compatibility_iterator node
= GetChildren().GetFirst();
3402 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3403 if (para
&& !(para
->GetRange().GetStart() > range
.GetEnd() || para
->GetRange().GetEnd() < range
.GetStart()))
3405 if (para
->GetChildren().GetCount() == 0)
3407 wxRichTextAttr paraStyle
= para
->GetCombinedAttributes(true /* use box attributes */);
3409 CollectStyle(style
, paraStyle
, clashingAttr
, absentAttrPara
);
3413 wxRichTextRange
paraRange(para
->GetRange());
3414 paraRange
.LimitTo(range
);
3416 // First collect paragraph attributes only
3417 wxRichTextAttr paraStyle
= para
->GetCombinedAttributes();
3418 paraStyle
.SetFlags(paraStyle
.GetFlags() & wxTEXT_ATTR_PARAGRAPH
);
3419 CollectStyle(style
, paraStyle
, clashingAttr
, absentAttrPara
);
3421 wxRichTextObjectList::compatibility_iterator childNode
= para
->GetChildren().GetFirst();
3425 wxRichTextObject
* child
= childNode
->GetData();
3426 if (!(child
->GetRange().GetStart() > range
.GetEnd() || child
->GetRange().GetEnd() < range
.GetStart()))
3428 wxRichTextAttr childStyle
= para
->GetCombinedAttributes(child
->GetAttributes(), true /* include box attributes */);
3430 // Now collect character attributes only
3431 childStyle
.SetFlags(childStyle
.GetFlags() & wxTEXT_ATTR_CHARACTER
);
3433 CollectStyle(style
, childStyle
, clashingAttr
, absentAttrChar
);
3436 childNode
= childNode
->GetNext();
3440 node
= node
->GetNext();
3445 /// Set default style
3446 bool wxRichTextParagraphLayoutBox::SetDefaultStyle(const wxRichTextAttr
& style
)
3448 m_defaultAttributes
= style
;
3452 /// Test if this whole range has character attributes of the specified kind. If any
3453 /// of the attributes are different within the range, the test fails. You
3454 /// can use this to implement, for example, bold button updating. style must have
3455 /// flags indicating which attributes are of interest.
3456 bool wxRichTextParagraphLayoutBox::HasCharacterAttributes(const wxRichTextRange
& range
, const wxRichTextAttr
& style
) const
3459 int matchingCount
= 0;
3461 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3464 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3465 // wxASSERT (para != NULL);
3469 // Stop searching if we're beyond the range of interest
3470 if (para
->GetRange().GetStart() > range
.GetEnd())
3471 return foundCount
== matchingCount
&& foundCount
!= 0;
3473 if (!para
->GetRange().IsOutside(range
))
3475 wxRichTextObjectList::compatibility_iterator node2
= para
->GetChildren().GetFirst();
3479 wxRichTextObject
* child
= node2
->GetData();
3480 // Allow for empty string if no buffer
3481 wxRichTextRange childRange
= child
->GetRange();
3482 if (childRange
.GetLength() == 0 && GetRange().GetLength() == 1)
3483 childRange
.SetEnd(childRange
.GetEnd()+1);
3485 if (!childRange
.IsOutside(range
) && child
->IsKindOf(CLASSINFO(wxRichTextPlainText
)))
3488 wxRichTextAttr textAttr
= para
->GetCombinedAttributes(child
->GetAttributes());
3490 if (wxTextAttrEqPartial(textAttr
, style
))
3494 node2
= node2
->GetNext();
3499 node
= node
->GetNext();
3502 return foundCount
== matchingCount
&& foundCount
!= 0;
3505 /// Test if this whole range has paragraph attributes of the specified kind. If any
3506 /// of the attributes are different within the range, the test fails. You
3507 /// can use this to implement, for example, centering button updating. style must have
3508 /// flags indicating which attributes are of interest.
3509 bool wxRichTextParagraphLayoutBox::HasParagraphAttributes(const wxRichTextRange
& range
, const wxRichTextAttr
& style
) const
3512 int matchingCount
= 0;
3514 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3517 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3518 // wxASSERT (para != NULL);
3522 // Stop searching if we're beyond the range of interest
3523 if (para
->GetRange().GetStart() > range
.GetEnd())
3524 return foundCount
== matchingCount
&& foundCount
!= 0;
3526 if (!para
->GetRange().IsOutside(range
))
3528 wxRichTextAttr textAttr
= GetAttributes();
3529 // Apply the paragraph style
3530 wxRichTextApplyStyle(textAttr
, para
->GetAttributes());
3533 if (wxTextAttrEqPartial(textAttr
, style
))
3538 node
= node
->GetNext();
3540 return foundCount
== matchingCount
&& foundCount
!= 0;
3543 void wxRichTextParagraphLayoutBox::PrepareContent(wxRichTextParagraphLayoutBox
& container
)
3545 wxRichTextBuffer
* buffer
= GetBuffer();
3546 if (buffer
&& buffer
->GetRichTextCtrl())
3547 buffer
->GetRichTextCtrl()->PrepareContent(container
);
3550 /// Set character or paragraph properties
3551 bool wxRichTextParagraphLayoutBox::SetProperties(const wxRichTextRange
& range
, const wxRichTextProperties
& properties
, int flags
)
3553 wxRichTextBuffer
* buffer
= GetBuffer();
3555 bool withUndo
= ((flags
& wxRICHTEXT_SETPROPERTIES_WITH_UNDO
) != 0);
3556 bool parasOnly
= ((flags
& wxRICHTEXT_SETPROPERTIES_PARAGRAPHS_ONLY
) != 0);
3557 bool charactersOnly
= ((flags
& wxRICHTEXT_SETPROPERTIES_CHARACTERS_ONLY
) != 0);
3558 bool resetExistingProperties
= ((flags
& wxRICHTEXT_SETPROPERTIES_RESET
) != 0);
3559 bool removeProperties
= ((flags
& wxRICHTEXT_SETPROPERTIES_REMOVE
) != 0);
3561 // If we are associated with a control, make undoable; otherwise, apply immediately
3564 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3566 wxRichTextAction
* action
= NULL
;
3568 if (haveControl
&& withUndo
)
3570 action
= new wxRichTextAction(NULL
, _("Change Properties"), wxRICHTEXT_CHANGE_PROPERTIES
, buffer
, this, buffer
->GetRichTextCtrl());
3571 action
->SetRange(range
);
3572 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3575 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3578 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3579 // wxASSERT (para != NULL);
3581 if (para
&& para
->GetChildCount() > 0)
3583 // Stop searching if we're beyond the range of interest
3584 if (para
->GetRange().GetStart() > range
.GetEnd())
3587 if (!para
->GetRange().IsOutside(range
))
3589 // We'll be using a copy of the paragraph to make style changes,
3590 // not updating the buffer directly.
3591 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3593 if (haveControl
&& withUndo
)
3595 newPara
= new wxRichTextParagraph(*para
);
3596 action
->GetNewParagraphs().AppendChild(newPara
);
3598 // Also store the old ones for Undo
3599 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3606 if (removeProperties
)
3608 // Removes the given style from the paragraph
3610 newPara
->GetProperties().RemoveProperties(properties
);
3612 else if (resetExistingProperties
)
3613 newPara
->GetProperties() = properties
;
3615 newPara
->GetProperties().MergeProperties(properties
);
3618 // When applying paragraph styles dynamically, don't change the text objects' attributes
3619 // since they will computed as needed. Only apply the character styling if it's _only_
3620 // character styling. This policy is subject to change and might be put under user control.
3622 // Hm. we might well be applying a mix of paragraph and character styles, in which
3623 // case we _do_ want to apply character styles regardless of what para styles are set.
3624 // But if we're applying a paragraph style, which has some character attributes, but
3625 // we only want the paragraphs to hold this character style, then we _don't_ want to
3626 // apply the character style. So we need to be able to choose.
3628 if (!parasOnly
&& charactersOnly
&& range
.GetStart() != newPara
->GetRange().GetEnd())
3630 wxRichTextRange
childRange(range
);
3631 childRange
.LimitTo(newPara
->GetRange());
3633 // Find the starting position and if necessary split it so
3634 // we can start applying different properties.
3635 // TODO: check that the properties actually change or are different
3636 // from properties outside of range
3637 wxRichTextObject
* firstObject
wxDUMMY_INITIALIZE(NULL
);
3638 wxRichTextObject
* lastObject
wxDUMMY_INITIALIZE(NULL
);
3640 if (childRange
.GetStart() == newPara
->GetRange().GetStart())
3641 firstObject
= newPara
->GetChildren().GetFirst()->GetData();
3643 firstObject
= newPara
->SplitAt(range
.GetStart());
3645 // Increment by 1 because we're apply the style one _after_ the split point
3646 long splitPoint
= childRange
.GetEnd();
3647 if (splitPoint
!= newPara
->GetRange().GetEnd())
3651 if (splitPoint
== newPara
->GetRange().GetEnd())
3652 lastObject
= newPara
->GetChildren().GetLast()->GetData();
3654 // lastObject is set as a side-effect of splitting. It's
3655 // returned as the object before the new object.
3656 (void) newPara
->SplitAt(splitPoint
, & lastObject
);
3658 wxASSERT(firstObject
!= NULL
);
3659 wxASSERT(lastObject
!= NULL
);
3661 if (!firstObject
|| !lastObject
)
3664 wxRichTextObjectList::compatibility_iterator firstNode
= newPara
->GetChildren().Find(firstObject
);
3665 wxRichTextObjectList::compatibility_iterator lastNode
= newPara
->GetChildren().Find(lastObject
);
3667 wxASSERT(firstNode
);
3670 wxRichTextObjectList::compatibility_iterator node2
= firstNode
;
3674 wxRichTextObject
* child
= node2
->GetData();
3676 if (removeProperties
)
3678 // Removes the given properties from the paragraph
3679 child
->GetProperties().RemoveProperties(properties
);
3681 else if (resetExistingProperties
)
3682 child
->GetProperties() = properties
;
3685 child
->GetProperties().MergeProperties(properties
);
3688 if (node2
== lastNode
)
3691 node2
= node2
->GetNext();
3697 node
= node
->GetNext();
3700 // Do action, or delay it until end of batch.
3701 if (haveControl
&& withUndo
)
3702 buffer
->SubmitAction(action
);
3707 void wxRichTextParagraphLayoutBox::Reset()
3711 wxRichTextBuffer
* buffer
= GetBuffer();
3712 if (buffer
&& buffer
->GetRichTextCtrl())
3714 wxRichTextEvent
event(wxEVT_COMMAND_RICHTEXT_BUFFER_RESET
, buffer
->GetRichTextCtrl()->GetId());
3715 event
.SetEventObject(buffer
->GetRichTextCtrl());
3716 event
.SetContainer(this);
3718 buffer
->SendEvent(event
, true);
3721 AddParagraph(wxEmptyString
);
3723 PrepareContent(*this);
3725 InvalidateHierarchy(wxRICHTEXT_ALL
);
3728 /// Invalidate the buffer. With no argument, invalidates whole buffer.
3729 void wxRichTextParagraphLayoutBox::Invalidate(const wxRichTextRange
& invalidRange
)
3731 wxRichTextCompositeObject::Invalidate(invalidRange
);
3733 DoInvalidate(invalidRange
);
3736 // Do the (in)validation for this object only
3737 void wxRichTextParagraphLayoutBox::DoInvalidate(const wxRichTextRange
& invalidRange
)
3739 if (invalidRange
== wxRICHTEXT_ALL
)
3741 m_invalidRange
= wxRICHTEXT_ALL
;
3743 // Already invalidating everything
3744 else if (m_invalidRange
== wxRICHTEXT_ALL
)
3749 if ((invalidRange
.GetStart() < m_invalidRange
.GetStart()) || m_invalidRange
.GetStart() == -1)
3750 m_invalidRange
.SetStart(invalidRange
.GetStart());
3751 if (invalidRange
.GetEnd() > m_invalidRange
.GetEnd())
3752 m_invalidRange
.SetEnd(invalidRange
.GetEnd());
3756 // Do the (in)validation both up and down the hierarchy
3757 void wxRichTextParagraphLayoutBox::InvalidateHierarchy(const wxRichTextRange
& invalidRange
)
3759 Invalidate(invalidRange
);
3761 if (invalidRange
!= wxRICHTEXT_NONE
)
3763 // Now go up the hierarchy
3764 wxRichTextObject
* thisObj
= this;
3765 wxRichTextObject
* p
= GetParent();
3768 wxRichTextParagraphLayoutBox
* l
= wxDynamicCast(p
, wxRichTextParagraphLayoutBox
);
3770 l
->DoInvalidate(thisObj
->GetRange());
3778 /// Get invalid range, rounding to entire paragraphs if argument is true.
3779 wxRichTextRange
wxRichTextParagraphLayoutBox::GetInvalidRange(bool wholeParagraphs
) const
3781 if (m_invalidRange
== wxRICHTEXT_ALL
|| m_invalidRange
== wxRICHTEXT_NONE
)
3782 return m_invalidRange
;
3784 wxRichTextRange range
= m_invalidRange
;
3786 if (wholeParagraphs
)
3788 wxRichTextParagraph
* para1
= GetParagraphAtPosition(range
.GetStart());
3790 range
.SetStart(para1
->GetRange().GetStart());
3791 // floating layout make all child should be relayout
3792 range
.SetEnd(GetOwnRange().GetEnd());
3797 /// Apply the style sheet to the buffer, for example if the styles have changed.
3798 bool wxRichTextParagraphLayoutBox::ApplyStyleSheet(wxRichTextStyleSheet
* styleSheet
)
3800 wxASSERT(styleSheet
!= NULL
);
3806 wxRichTextAttr
attr(GetBasicStyle());
3807 if (GetBasicStyle().HasParagraphStyleName())
3809 wxRichTextParagraphStyleDefinition
* paraDef
= styleSheet
->FindParagraphStyle(GetBasicStyle().GetParagraphStyleName());
3812 attr
.Apply(paraDef
->GetStyleMergedWithBase(styleSheet
));
3813 SetBasicStyle(attr
);
3818 if (GetBasicStyle().HasCharacterStyleName())
3820 wxRichTextCharacterStyleDefinition
* charDef
= styleSheet
->FindCharacterStyle(GetBasicStyle().GetCharacterStyleName());
3823 attr
.Apply(charDef
->GetStyleMergedWithBase(styleSheet
));
3824 SetBasicStyle(attr
);
3829 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3832 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3833 // wxASSERT (para != NULL);
3837 // Combine paragraph and list styles. If there is a list style in the original attributes,
3838 // the current indentation overrides anything else and is used to find the item indentation.
3839 // Also, for applying paragraph styles, consider having 2 modes: (1) we merge with what we have,
3840 // thereby taking into account all user changes, (2) reset the style completely (except for indentation/list
3841 // exception as above).
3842 // Problem: when changing from one list style to another, there's a danger that the level info will get lost.
3843 // So when changing a list style interactively, could retrieve level based on current style, then
3844 // set appropriate indent and apply new style.
3848 if (para
->GetAttributes().HasOutlineLevel())
3849 outline
= para
->GetAttributes().GetOutlineLevel();
3850 if (para
->GetAttributes().HasBulletNumber())
3851 num
= para
->GetAttributes().GetBulletNumber();
3853 if (!para
->GetAttributes().GetParagraphStyleName().IsEmpty() && !para
->GetAttributes().GetListStyleName().IsEmpty())
3855 int currentIndent
= para
->GetAttributes().GetLeftIndent();
3857 wxRichTextParagraphStyleDefinition
* paraDef
= styleSheet
->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
3858 wxRichTextListStyleDefinition
* listDef
= styleSheet
->FindListStyle(para
->GetAttributes().GetListStyleName());
3859 if (paraDef
&& !listDef
)
3861 para
->GetAttributes() = paraDef
->GetStyleMergedWithBase(styleSheet
);
3864 else if (listDef
&& !paraDef
)
3866 // Set overall style defined for the list style definition
3867 para
->GetAttributes() = listDef
->GetStyleMergedWithBase(styleSheet
);
3869 // Apply the style for this level
3870 wxRichTextApplyStyle(para
->GetAttributes(), * listDef
->GetLevelAttributes(listDef
->FindLevelForIndent(currentIndent
)));
3873 else if (listDef
&& paraDef
)
3875 // Combines overall list style, style for level, and paragraph style
3876 para
->GetAttributes() = listDef
->CombineWithParagraphStyle(currentIndent
, paraDef
->GetStyleMergedWithBase(styleSheet
));
3880 else if (para
->GetAttributes().GetParagraphStyleName().IsEmpty() && !para
->GetAttributes().GetListStyleName().IsEmpty())
3882 int currentIndent
= para
->GetAttributes().GetLeftIndent();
3884 wxRichTextListStyleDefinition
* listDef
= styleSheet
->FindListStyle(para
->GetAttributes().GetListStyleName());
3886 // Overall list definition style
3887 para
->GetAttributes() = listDef
->GetStyleMergedWithBase(styleSheet
);
3889 // Style for this level
3890 wxRichTextApplyStyle(para
->GetAttributes(), * listDef
->GetLevelAttributes(listDef
->FindLevelForIndent(currentIndent
)));
3894 else if (!para
->GetAttributes().GetParagraphStyleName().IsEmpty() && para
->GetAttributes().GetListStyleName().IsEmpty())
3896 wxRichTextParagraphStyleDefinition
* def
= styleSheet
->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
3899 para
->GetAttributes() = def
->GetStyleMergedWithBase(styleSheet
);
3905 para
->GetAttributes().SetOutlineLevel(outline
);
3907 para
->GetAttributes().SetBulletNumber(num
);
3910 node
= node
->GetNext();
3912 return foundCount
!= 0;
3916 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
3918 wxRichTextBuffer
* buffer
= GetBuffer();
3919 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
3921 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
3922 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
3923 bool specifyLevel
= ((flags
& wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL
) != 0);
3924 bool renumber
= ((flags
& wxRICHTEXT_SETSTYLE_RENUMBER
) != 0);
3926 // Current number, if numbering
3929 wxASSERT (!specifyLevel
|| (specifyLevel
&& (specifiedLevel
>= 0)));
3931 // If we are associated with a control, make undoable; otherwise, apply immediately
3934 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3936 wxRichTextAction
* action
= NULL
;
3938 if (haveControl
&& withUndo
)
3940 action
= new wxRichTextAction(NULL
, _("Change List Style"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
3941 action
->SetRange(range
);
3942 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3945 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3948 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3949 // wxASSERT (para != NULL);
3951 if (para
&& para
->GetChildCount() > 0)
3953 // Stop searching if we're beyond the range of interest
3954 if (para
->GetRange().GetStart() > range
.GetEnd())
3957 if (!para
->GetRange().IsOutside(range
))
3959 // We'll be using a copy of the paragraph to make style changes,
3960 // not updating the buffer directly.
3961 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3963 if (haveControl
&& withUndo
)
3965 newPara
= new wxRichTextParagraph(*para
);
3966 action
->GetNewParagraphs().AppendChild(newPara
);
3968 // Also store the old ones for Undo
3969 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3976 int thisIndent
= newPara
->GetAttributes().GetLeftIndent();
3977 int thisLevel
= specifyLevel
? specifiedLevel
: def
->FindLevelForIndent(thisIndent
);
3979 // How is numbering going to work?
3980 // If we are renumbering, or numbering for the first time, we need to keep
3981 // track of the number for each level. But we might be simply applying a different
3983 // In Word, applying a style to several paragraphs, even if at different levels,
3984 // reverts the level back to the same one. So we could do the same here.
3985 // Renumbering will need to be done when we promote/demote a paragraph.
3987 // Apply the overall list style, and item style for this level
3988 wxRichTextAttr
listStyle(def
->GetCombinedStyleForLevel(thisLevel
, styleSheet
));
3989 wxRichTextApplyStyle(newPara
->GetAttributes(), listStyle
);
3991 // Now we need to do numbering
3994 newPara
->GetAttributes().SetBulletNumber(n
);
3999 else if (!newPara
->GetAttributes().GetListStyleName().IsEmpty())
4001 // if def is NULL, remove list style, applying any associated paragraph style
4002 // to restore the attributes
4004 newPara
->GetAttributes().SetListStyleName(wxEmptyString
);
4005 newPara
->GetAttributes().SetLeftIndent(0, 0);
4006 newPara
->GetAttributes().SetBulletText(wxEmptyString
);
4008 // Eliminate the main list-related attributes
4009 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
);
4011 if (styleSheet
&& !newPara
->GetAttributes().GetParagraphStyleName().IsEmpty())
4013 wxRichTextParagraphStyleDefinition
* def
= styleSheet
->FindParagraphStyle(newPara
->GetAttributes().GetParagraphStyleName());
4016 newPara
->GetAttributes() = def
->GetStyleMergedWithBase(styleSheet
);
4023 node
= node
->GetNext();
4026 // Do action, or delay it until end of batch.
4027 if (haveControl
&& withUndo
)
4028 buffer
->SubmitAction(action
);
4033 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
)
4035 wxRichTextBuffer
* buffer
= GetBuffer();
4036 if (buffer
&& buffer
->GetStyleSheet())
4038 wxRichTextListStyleDefinition
* def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4040 return SetListStyle(range
, def
, flags
, startFrom
, specifiedLevel
);
4045 /// Clear list for given range
4046 bool wxRichTextParagraphLayoutBox::ClearListStyle(const wxRichTextRange
& range
, int flags
)
4048 return SetListStyle(range
, NULL
, flags
);
4051 /// Number/renumber any list elements in the given range
4052 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
4054 return DoNumberList(range
, range
, 0, def
, flags
, startFrom
, specifiedLevel
);
4057 /// Number/renumber any list elements in the given range. Also do promotion or demotion of items, if specified
4058 bool wxRichTextParagraphLayoutBox::DoNumberList(const wxRichTextRange
& range
, const wxRichTextRange
& promotionRange
, int promoteBy
,
4059 wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
4061 wxRichTextBuffer
* buffer
= GetBuffer();
4062 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
4064 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
4065 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
4067 bool specifyLevel
= ((flags
& wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL
) != 0);
4070 bool renumber
= ((flags
& wxRICHTEXT_SETSTYLE_RENUMBER
) != 0);
4072 // Max number of levels
4073 const int maxLevels
= 10;
4075 // The level we're looking at now
4076 int currentLevel
= -1;
4078 // The item number for each level
4079 int levels
[maxLevels
];
4082 // Reset all numbering
4083 for (i
= 0; i
< maxLevels
; i
++)
4085 if (startFrom
!= -1)
4086 levels
[i
] = startFrom
-1;
4087 else if (renumber
) // start again
4090 levels
[i
] = -1; // start from the number we found, if any
4094 wxASSERT(!specifyLevel
|| (specifyLevel
&& (specifiedLevel
>= 0)));
4097 // If we are associated with a control, make undoable; otherwise, apply immediately
4100 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
4102 wxRichTextAction
* action
= NULL
;
4104 if (haveControl
&& withUndo
)
4106 action
= new wxRichTextAction(NULL
, _("Renumber List"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
4107 action
->SetRange(range
);
4108 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
4111 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
4114 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
4115 // wxASSERT (para != NULL);
4117 if (para
&& para
->GetChildCount() > 0)
4119 // Stop searching if we're beyond the range of interest
4120 if (para
->GetRange().GetStart() > range
.GetEnd())
4123 if (!para
->GetRange().IsOutside(range
))
4125 // We'll be using a copy of the paragraph to make style changes,
4126 // not updating the buffer directly.
4127 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
4129 if (haveControl
&& withUndo
)
4131 newPara
= new wxRichTextParagraph(*para
);
4132 action
->GetNewParagraphs().AppendChild(newPara
);
4134 // Also store the old ones for Undo
4135 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
4140 wxRichTextListStyleDefinition
* defToUse
= def
;
4143 if (styleSheet
&& !newPara
->GetAttributes().GetListStyleName().IsEmpty())
4144 defToUse
= styleSheet
->FindListStyle(newPara
->GetAttributes().GetListStyleName());
4149 int thisIndent
= newPara
->GetAttributes().GetLeftIndent();
4150 int thisLevel
= defToUse
->FindLevelForIndent(thisIndent
);
4152 // If we've specified a level to apply to all, change the level.
4153 if (specifiedLevel
!= -1)
4154 thisLevel
= specifiedLevel
;
4156 // Do promotion if specified
4157 if ((promoteBy
!= 0) && !para
->GetRange().IsOutside(promotionRange
))
4159 thisLevel
= thisLevel
- promoteBy
;
4166 // Apply the overall list style, and item style for this level
4167 wxRichTextAttr
listStyle(defToUse
->GetCombinedStyleForLevel(thisLevel
, styleSheet
));
4168 wxRichTextApplyStyle(newPara
->GetAttributes(), listStyle
);
4170 // OK, we've (re)applied the style, now let's get the numbering right.
4172 if (currentLevel
== -1)
4173 currentLevel
= thisLevel
;
4175 // Same level as before, do nothing except increment level's number afterwards
4176 if (currentLevel
== thisLevel
)
4179 // A deeper level: start renumbering all levels after current level
4180 else if (thisLevel
> currentLevel
)
4182 for (i
= currentLevel
+1; i
<= thisLevel
; i
++)
4186 currentLevel
= thisLevel
;
4188 else if (thisLevel
< currentLevel
)
4190 currentLevel
= thisLevel
;
4193 // Use the current numbering if -1 and we have a bullet number already
4194 if (levels
[currentLevel
] == -1)
4196 if (newPara
->GetAttributes().HasBulletNumber())
4197 levels
[currentLevel
] = newPara
->GetAttributes().GetBulletNumber();
4199 levels
[currentLevel
] = 1;
4203 levels
[currentLevel
] ++;
4206 newPara
->GetAttributes().SetBulletNumber(levels
[currentLevel
]);
4208 // Create the bullet text if an outline list
4209 if (listStyle
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
)
4212 for (i
= 0; i
<= currentLevel
; i
++)
4214 if (!text
.IsEmpty())
4216 text
+= wxString::Format(wxT("%d"), levels
[i
]);
4218 newPara
->GetAttributes().SetBulletText(text
);
4224 node
= node
->GetNext();
4227 // Do action, or delay it until end of batch.
4228 if (haveControl
&& withUndo
)
4229 buffer
->SubmitAction(action
);
4234 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
)
4236 wxRichTextBuffer
* buffer
= GetBuffer();
4237 if (buffer
->GetStyleSheet())
4239 wxRichTextListStyleDefinition
* def
= NULL
;
4240 if (!defName
.IsEmpty())
4241 def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4242 return NumberList(range
, def
, flags
, startFrom
, specifiedLevel
);
4247 /// Promote the list items within the given range. promoteBy can be a positive or negative number, e.g. 1 or -1
4248 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy
, const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int specifiedLevel
)
4251 // One strategy is to first work out the range within which renumbering must occur. Then could pass these two ranges
4252 // to NumberList with a flag indicating promotion is required within one of the ranges.
4253 // Find first and last paragraphs in range. Then for first, calculate new indentation and look back until we find
4254 // a paragraph that either has no list style, or has one that is different or whose indentation is less.
4255 // We start renumbering from the para after that different para we found. We specify that the numbering of that
4256 // list position will start from 1.
4257 // Similarly, we look after the last para in the promote range for an indentation that is less (or no list style).
4258 // We can end the renumbering at this point.
4260 // For now, only renumber within the promotion range.
4262 return DoNumberList(range
, range
, promoteBy
, def
, flags
, 1, specifiedLevel
);
4265 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy
, const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int specifiedLevel
)
4267 wxRichTextBuffer
* buffer
= GetBuffer();
4268 if (buffer
->GetStyleSheet())
4270 wxRichTextListStyleDefinition
* def
= NULL
;
4271 if (!defName
.IsEmpty())
4272 def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4273 return PromoteList(promoteBy
, range
, def
, flags
, specifiedLevel
);
4278 /// Fills in the attributes for numbering a paragraph after previousParagraph. It also finds the
4279 /// position of the paragraph that it had to start looking from.
4280 bool wxRichTextParagraphLayoutBox::FindNextParagraphNumber(wxRichTextParagraph
* previousParagraph
, wxRichTextAttr
& attr
) const
4282 if (!previousParagraph
->GetAttributes().HasFlag(wxTEXT_ATTR_BULLET_STYLE
) || previousParagraph
->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
)
4285 wxRichTextBuffer
* buffer
= GetBuffer();
4286 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
4287 if (styleSheet
&& !previousParagraph
->GetAttributes().GetListStyleName().IsEmpty())
4289 wxRichTextListStyleDefinition
* def
= styleSheet
->FindListStyle(previousParagraph
->GetAttributes().GetListStyleName());
4292 // int thisIndent = previousParagraph->GetAttributes().GetLeftIndent();
4293 // int thisLevel = def->FindLevelForIndent(thisIndent);
4295 bool isOutline
= (previousParagraph
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
) != 0;
4297 attr
.SetFlags(previousParagraph
->GetAttributes().GetFlags() & (wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_BULLET_NUMBER
|wxTEXT_ATTR_BULLET_TEXT
|wxTEXT_ATTR_BULLET_NAME
));
4298 if (previousParagraph
->GetAttributes().HasBulletName())
4299 attr
.SetBulletName(previousParagraph
->GetAttributes().GetBulletName());
4300 attr
.SetBulletStyle(previousParagraph
->GetAttributes().GetBulletStyle());
4301 attr
.SetListStyleName(previousParagraph
->GetAttributes().GetListStyleName());
4303 int nextNumber
= previousParagraph
->GetAttributes().GetBulletNumber() + 1;
4304 attr
.SetBulletNumber(nextNumber
);
4308 wxString text
= previousParagraph
->GetAttributes().GetBulletText();
4309 if (!text
.IsEmpty())
4311 int pos
= text
.Find(wxT('.'), true);
4312 if (pos
!= wxNOT_FOUND
)
4314 text
= text
.Mid(0, text
.Length() - pos
- 1);
4317 text
= wxEmptyString
;
4318 if (!text
.IsEmpty())
4320 text
+= wxString::Format(wxT("%d"), nextNumber
);
4321 attr
.SetBulletText(text
);
4335 * wxRichTextParagraph
4336 * This object represents a single paragraph (or in a straight text editor, a line).
4339 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraph
, wxRichTextCompositeObject
)
4341 wxArrayInt
wxRichTextParagraph::sm_defaultTabs
;
4343 wxRichTextParagraph::wxRichTextParagraph(wxRichTextObject
* parent
, wxRichTextAttr
* style
):
4344 wxRichTextCompositeObject(parent
)
4347 SetAttributes(*style
);
4350 wxRichTextParagraph::wxRichTextParagraph(const wxString
& text
, wxRichTextObject
* parent
, wxRichTextAttr
* paraStyle
, wxRichTextAttr
* charStyle
):
4351 wxRichTextCompositeObject(parent
)
4354 SetAttributes(*paraStyle
);
4356 AppendChild(new wxRichTextPlainText(text
, this, charStyle
));
4359 wxRichTextParagraph::~wxRichTextParagraph()
4365 bool wxRichTextParagraph::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int WXUNUSED(descent
), int style
)
4370 // Currently we don't merge these attributes with the parent, but we
4371 // should consider whether we should (e.g. if we set a border colour
4372 // for all paragraphs). But generally box attributes are likely to be
4373 // different for different objects.
4374 wxRect paraRect
= GetRect();
4375 wxRichTextAttr attr
= GetCombinedAttributes();
4376 context
.ApplyVirtualAttributes(attr
, this);
4378 DrawBoxAttributes(dc
, GetBuffer(), attr
, paraRect
);
4380 // Draw the bullet, if any
4381 if (attr
.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE
)
4383 if (attr
.GetLeftSubIndent() != 0)
4385 int spaceBeforePara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingBefore());
4386 int leftIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftIndent());
4388 wxRichTextAttr
bulletAttr(attr
);
4390 // Combine with the font of the first piece of content, if one is specified
4391 if (GetChildren().GetCount() > 0)
4393 wxRichTextObject
* firstObj
= (wxRichTextObject
*) GetChildren().GetFirst()->GetData();
4394 if (!firstObj
->IsFloatable() && firstObj
->GetAttributes().HasFont())
4396 wxRichTextApplyStyle(bulletAttr
, firstObj
->GetAttributes());
4400 // Get line height from first line, if any
4401 wxRichTextLine
* line
= m_cachedLines
.GetFirst() ? (wxRichTextLine
* ) m_cachedLines
.GetFirst()->GetData() : NULL
;
4404 int lineHeight
wxDUMMY_INITIALIZE(0);
4407 lineHeight
= line
->GetSize().y
;
4408 linePos
= line
->GetPosition() + GetPosition();
4413 if (bulletAttr
.HasFont() && GetBuffer())
4414 font
= GetBuffer()->GetFontTable().FindFont(bulletAttr
);
4416 font
= (*wxNORMAL_FONT
);
4418 wxCheckSetFont(dc
, font
);
4420 lineHeight
= dc
.GetCharHeight();
4421 linePos
= GetPosition();
4422 linePos
.y
+= spaceBeforePara
;
4425 wxRect
bulletRect(GetPosition().x
+ leftIndent
, linePos
.y
, linePos
.x
- (GetPosition().x
+ leftIndent
), lineHeight
);
4427 if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
)
4429 if (wxRichTextBuffer::GetRenderer())
4430 wxRichTextBuffer::GetRenderer()->DrawBitmapBullet(this, dc
, bulletAttr
, bulletRect
);
4432 else if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_STANDARD
)
4434 if (wxRichTextBuffer::GetRenderer())
4435 wxRichTextBuffer::GetRenderer()->DrawStandardBullet(this, dc
, bulletAttr
, bulletRect
);
4439 wxString bulletText
= GetBulletText();
4441 if (!bulletText
.empty() && wxRichTextBuffer::GetRenderer())
4442 wxRichTextBuffer::GetRenderer()->DrawTextBullet(this, dc
, bulletAttr
, bulletRect
, bulletText
);
4447 // Draw the range for each line, one object at a time.
4449 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
4452 wxRichTextLine
* line
= node
->GetData();
4453 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
4455 // Lines are specified relative to the paragraph
4457 wxPoint linePosition
= line
->GetPosition() + GetPosition();
4459 // Don't draw if off the screen
4460 if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) != 0) || ((linePosition
.y
+ line
->GetSize().y
) >= rect
.y
&& linePosition
.y
<= rect
.y
+ rect
.height
))
4462 wxPoint objectPosition
= linePosition
;
4463 int maxDescent
= line
->GetDescent();
4465 // Loop through objects until we get to the one within range
4466 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
4471 wxRichTextObject
* child
= node2
->GetData();
4473 if (!child
->IsFloating() && child
->GetRange().GetLength() > 0 && !child
->GetRange().IsOutside(lineRange
) && !lineRange
.IsOutside(range
))
4475 // Draw this part of the line at the correct position
4476 wxRichTextRange
objectRange(child
->GetRange());
4477 objectRange
.LimitTo(lineRange
);
4480 if (child
->IsTopLevel())
4482 objectSize
= child
->GetCachedSize();
4483 objectRange
= child
->GetOwnRange();
4487 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING && wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4488 if (i
< (int) line
->GetObjectSizes().GetCount())
4490 objectSize
.x
= line
->GetObjectSizes()[(size_t) i
];
4496 child
->GetRangeSize(objectRange
, objectSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, objectPosition
);
4500 // Use the child object's width, but the whole line's height
4501 wxRect
childRect(objectPosition
, wxSize(objectSize
.x
, line
->GetSize().y
));
4502 child
->Draw(dc
, context
, objectRange
, selection
, childRect
, maxDescent
, style
);
4504 objectPosition
.x
+= objectSize
.x
;
4507 else if (child
->GetRange().GetStart() > lineRange
.GetEnd())
4508 // Can break out of inner loop now since we've passed this line's range
4511 node2
= node2
->GetNext();
4515 node
= node
->GetNext();
4521 // Get the range width using partial extents calculated for the whole paragraph.
4522 static int wxRichTextGetRangeWidth(const wxRichTextParagraph
& para
, const wxRichTextRange
& range
, const wxArrayInt
& partialExtents
)
4524 wxASSERT(partialExtents
.GetCount() >= (size_t) range
.GetLength());
4526 if (partialExtents
.GetCount() < (size_t) range
.GetLength())
4529 int leftMostPos
= 0;
4530 if (range
.GetStart() - para
.GetRange().GetStart() > 0)
4531 leftMostPos
= partialExtents
[range
.GetStart() - para
.GetRange().GetStart() - 1];
4533 int rightMostPos
= partialExtents
[range
.GetEnd() - para
.GetRange().GetStart()];
4535 int w
= rightMostPos
- leftMostPos
;
4540 /// Lay the item out
4541 bool wxRichTextParagraph::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
)
4543 // Deal with floating objects firstly before the normal layout
4544 wxRichTextBuffer
* buffer
= GetBuffer();
4546 wxRichTextFloatCollector
* collector
= GetContainer()->GetFloatCollector();
4547 wxASSERT(collector
);
4548 LayoutFloat(dc
, context
, rect
, style
, collector
);
4550 wxRichTextAttr attr
= GetCombinedAttributes();
4551 context
.ApplyVirtualAttributes(attr
, this);
4555 // Increase the size of the paragraph due to spacing
4556 int spaceBeforePara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingBefore());
4557 int spaceAfterPara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingAfter());
4558 int leftIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftIndent());
4559 int leftSubIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftSubIndent());
4560 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
4562 int lineSpacing
= 0;
4564 // Let's assume line spacing of 10 is normal, 15 is 1.5, 20 is 2, etc.
4565 if (attr
.HasLineSpacing() && attr
.GetLineSpacing() > 0 && attr
.GetFont().IsOk())
4567 wxCheckSetFont(dc
, attr
.GetFont());
4568 lineSpacing
= (int) (double(dc
.GetCharHeight()) * (double(attr
.GetLineSpacing())/10.0 - 1.0));
4571 // Start position for each line relative to the paragraph
4572 int startPositionFirstLine
= leftIndent
;
4573 int startPositionSubsequentLines
= leftIndent
+ leftSubIndent
;
4575 // If we have a bullet in this paragraph, the start position for the first line's text
4576 // is actually leftIndent + leftSubIndent.
4577 if (attr
.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE
)
4578 startPositionFirstLine
= startPositionSubsequentLines
;
4580 long lastEndPos
= GetRange().GetStart()-1;
4581 long lastCompletedEndPos
= lastEndPos
;
4583 int currentWidth
= 0;
4584 SetPosition(rect
.GetPosition());
4586 wxPoint
currentPosition(0, spaceBeforePara
); // We will calculate lines relative to paragraph
4589 int maxHeight
= currentPosition
.y
;
4594 int lineDescent
= 0;
4596 wxRichTextObjectList::compatibility_iterator node
;
4598 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4600 wxArrayInt partialExtents
;
4603 int paraDescent
= 0;
4605 // This calculates the partial text extents
4606 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, rect
.GetPosition(), & partialExtents
);
4608 node
= m_children
.GetFirst();
4611 wxRichTextObject
* child
= node
->GetData();
4613 //child->SetCachedSize(wxDefaultSize);
4614 child
->Layout(dc
, context
, rect
, style
);
4616 node
= node
->GetNext();
4622 // We may need to go back to a previous child, in which case create the new line,
4623 // find the child corresponding to the start position of the string, and
4626 wxRect availableRect
;
4628 node
= m_children
.GetFirst();
4631 wxRichTextObject
* child
= node
->GetData();
4633 // If floating, ignore. We already laid out floats.
4634 // Also ignore if empty object, except if we haven't got any
4636 if (child
->IsFloating() || !child
->IsShown() ||
4637 (child
->GetRange().GetLength() == 0 && maxHeight
> spaceBeforePara
)
4640 node
= node
->GetNext();
4644 // If this is e.g. a composite text box, it will need to be laid out itself.
4645 // But if just a text fragment or image, for example, this will
4646 // do nothing. NB: won't we need to set the position after layout?
4647 // since for example if position is dependent on vertical line size, we
4648 // can't tell the position until the size is determined. So possibly introduce
4649 // another layout phase.
4651 // We may only be looking at part of a child, if we searched back for wrapping
4652 // and found a suitable point some way into the child. So get the size for the fragment
4655 long nextBreakPos
= GetFirstLineBreakPosition(lastEndPos
+1);
4656 long lastPosToUse
= child
->GetRange().GetEnd();
4657 bool lineBreakInThisObject
= (nextBreakPos
> -1 && nextBreakPos
<= child
->GetRange().GetEnd());
4659 if (lineBreakInThisObject
)
4660 lastPosToUse
= nextBreakPos
;
4663 int childDescent
= 0;
4665 int startOffset
= (lineCount
== 0 ? startPositionFirstLine
: startPositionSubsequentLines
);
4666 availableRect
= wxRect(rect
.x
+ startOffset
, rect
.y
+ currentPosition
.y
,
4667 rect
.width
- startOffset
- rightIndent
, rect
.height
);
4669 if (child
->IsTopLevel())
4671 wxSize oldSize
= child
->GetCachedSize();
4673 child
->Invalidate(wxRICHTEXT_ALL
);
4674 child
->SetPosition(wxPoint(0, 0));
4676 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4677 // lays out the object again using the minimum size
4678 // The position will be determined by its location in its line,
4679 // and not by the child's actual position.
4680 child
->LayoutToBestSize(dc
, context
, buffer
,
4681 attr
, child
->GetAttributes(), availableRect
, parentRect
, style
);
4683 if (oldSize
!= child
->GetCachedSize())
4685 partialExtents
.Clear();
4687 // Recalculate the partial text extents since the child object changed size
4688 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), & partialExtents
);
4692 // Problem: we need to layout composites here for which we need the available width,
4693 // but we can't get the available width without using the float collector which
4694 // needs to know the object height.
4696 if ((nextBreakPos
== -1) && (lastEndPos
== child
->GetRange().GetStart() - 1)) // i.e. we want to get the whole thing
4698 childSize
= child
->GetCachedSize();
4699 childDescent
= child
->GetDescent();
4703 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4704 // Get height only, then the width using the partial extents
4705 GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
);
4706 childSize
.x
= wxRichTextGetRangeWidth(*this, wxRichTextRange(lastEndPos
+1, lastPosToUse
), partialExtents
);
4708 GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, rect
.GetPosition());
4713 int loopIterations
= 0;
4715 // If there are nested objects that need to lay themselves out, we have to do this in a
4716 // loop because the height of the object may well depend on the available width.
4717 // And because of floating object positioning, the available width depends on the
4718 // height of the object and whether it will clash with the floating objects.
4719 // So, we see whether the available width changes due to the presence of floating images.
4720 // If it does, then we'll use the new restricted width to find the object height again.
4721 // If this causes another restriction in the available width, we'll try again, until
4722 // either we lose patience or the available width settles down.
4727 wxRect oldAvailableRect
= availableRect
;
4729 // Available width depends on the floating objects and the line height.
4730 // Note: the floating objects may be placed vertically along the two sides of
4731 // buffer, so we may have different available line widths with different
4732 // [startY, endY]. So, we can't determine how wide the available
4733 // space is until we know the exact line height.
4734 if (childDescent
== 0)
4736 lineHeight
= wxMax(lineHeight
, childSize
.y
);
4737 lineDescent
= maxDescent
;
4738 lineAscent
= maxAscent
;
4742 lineDescent
= wxMax(childDescent
, maxDescent
);
4743 lineAscent
= wxMax(childSize
.y
-childDescent
, maxAscent
);
4745 lineHeight
= wxMax(lineHeight
, (lineDescent
+ lineAscent
));
4746 wxRect floatAvailableRect
= collector
->GetAvailableRect(rect
.y
+ currentPosition
.y
, rect
.y
+ currentPosition
.y
+ lineHeight
);
4748 // Adjust availableRect to the space that is available when taking floating objects into account.
4750 if (floatAvailableRect
.x
+ startOffset
> availableRect
.x
)
4752 int newX
= floatAvailableRect
.x
+ startOffset
;
4753 int newW
= availableRect
.width
- (newX
- availableRect
.x
);
4754 availableRect
.x
= newX
;
4755 availableRect
.width
= newW
;
4758 if (floatAvailableRect
.width
< availableRect
.width
)
4759 availableRect
.width
= floatAvailableRect
.width
;
4761 currentPosition
.x
= availableRect
.x
- rect
.x
;
4763 if (child
->IsTopLevel() && loopIterations
<= 20)
4765 if (availableRect
!= oldAvailableRect
)
4767 wxSize oldSize
= child
->GetCachedSize();
4769 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4770 // lays out the object again using the minimum size
4771 child
->Invalidate(wxRICHTEXT_ALL
);
4772 child
->LayoutToBestSize(dc
, context
, buffer
,
4773 attr
, child
->GetAttributes(), availableRect
, parentRect
, style
);
4774 childSize
= child
->GetCachedSize();
4775 childDescent
= child
->GetDescent();
4777 if (oldSize
!= child
->GetCachedSize())
4779 partialExtents
.Clear();
4781 // Recalculate the partial text extents since the child object changed size
4782 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), & partialExtents
);
4785 // Go around the loop finding the available rect for the given floating objects
4795 if (child
->IsTopLevel())
4797 // We can move it to the correct position at this point
4798 child
->Move(GetPosition() + wxPoint(currentWidth
, currentPosition
.y
));
4802 // 1) There was a line break BEFORE the natural break
4803 // 2) There was a line break AFTER the natural break
4804 // 3) It's the last line
4805 // 4) The child still fits (carry on) - 'else' clause
4807 if ((lineBreakInThisObject
&& (childSize
.x
+ currentWidth
<= availableRect
.width
))
4809 (childSize
.x
+ currentWidth
> availableRect
.width
)
4811 ((childSize
.x
+ currentWidth
<= availableRect
.width
) && !node
->GetNext())
4815 long wrapPosition
= 0;
4816 if ((childSize
.x
+ currentWidth
<= availableRect
.width
) && !node
->GetNext() && !lineBreakInThisObject
)
4817 wrapPosition
= child
->GetRange().GetEnd();
4820 // Find a place to wrap. This may walk back to previous children,
4821 // for example if a word spans several objects.
4822 // Note: one object must contains only one wxTextAtrr, so the line height will not
4823 // change inside one object. Thus, we can pass the remain line width to the
4824 // FindWrapPosition function.
4825 if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos
+1, child
->GetRange().GetEnd()), dc
, context
, availableRect
.width
, wrapPosition
, & partialExtents
))
4827 // If the function failed, just cut it off at the end of this child.
4828 wrapPosition
= child
->GetRange().GetEnd();
4831 // FindWrapPosition can still return a value that will put us in an endless wrapping loop
4832 if (wrapPosition
<= lastCompletedEndPos
)
4833 wrapPosition
= wxMax(lastCompletedEndPos
+1,child
->GetRange().GetEnd());
4835 // Line end position shouldn't be the same as the end, or greater.
4836 if (wrapPosition
>= GetRange().GetEnd())
4837 wrapPosition
= GetRange().GetEnd()-1;
4839 // wxLogDebug(wxT("Split at %ld"), wrapPosition);
4841 // Let's find the actual size of the current line now
4843 wxRichTextRange
actualRange(lastCompletedEndPos
+1, wrapPosition
);
4847 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4848 if (!child
->IsEmpty())
4850 // Get height only, then the width using the partial extents
4851 GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
);
4852 actualSize
.x
= wxRichTextGetRangeWidth(*this, actualRange
, partialExtents
);
4856 GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
4858 currentWidth
= actualSize
.x
;
4860 // The descent for the whole line at this point, is the correct max descent
4861 maxDescent
= childDescent
;
4863 maxAscent
= actualSize
.y
-childDescent
;
4865 // lineHeight is given by the height for the whole line, since it will
4866 // take into account ascend/descend.
4867 lineHeight
= actualSize
.y
;
4869 if (lineHeight
== 0 && buffer
)
4871 wxFont
font(buffer
->GetFontTable().FindFont(attr
));
4872 wxCheckSetFont(dc
, font
);
4873 lineHeight
= dc
.GetCharHeight();
4876 if (maxDescent
== 0)
4879 dc
.GetTextExtent(wxT("X"), & w
, &h
, & maxDescent
);
4883 wxRichTextLine
* line
= AllocateLine(lineCount
);
4885 // Set relative range so we won't have to change line ranges when paragraphs are moved
4886 line
->SetRange(wxRichTextRange(actualRange
.GetStart() - GetRange().GetStart(), actualRange
.GetEnd() - GetRange().GetStart()));
4887 line
->SetPosition(currentPosition
);
4888 line
->SetSize(wxSize(currentWidth
, lineHeight
));
4889 line
->SetDescent(maxDescent
);
4891 maxHeight
= currentPosition
.y
+ lineHeight
;
4893 // Now move down a line. TODO: add margins, spacing
4894 currentPosition
.y
+= lineHeight
;
4895 currentPosition
.y
+= lineSpacing
;
4898 maxWidth
= wxMax(maxWidth
, currentWidth
+startOffset
);
4903 // TODO: account for zero-length objects
4904 // wxASSERT(wrapPosition > lastCompletedEndPos);
4906 lastEndPos
= wrapPosition
;
4907 lastCompletedEndPos
= lastEndPos
;
4911 if (wrapPosition
< GetRange().GetEnd()-1)
4913 // May need to set the node back to a previous one, due to searching back in wrapping
4914 wxRichTextObject
* childAfterWrapPosition
= FindObjectAtPosition(wrapPosition
+1);
4915 if (childAfterWrapPosition
)
4916 node
= m_children
.Find(childAfterWrapPosition
);
4918 node
= node
->GetNext();
4921 node
= node
->GetNext();
4923 // Apply paragraph styles such as alignment to the wrapped line
4924 ApplyParagraphStyle(line
, attr
, availableRect
, dc
);
4928 // We still fit, so don't add a line, and keep going
4929 currentWidth
+= childSize
.x
;
4931 if (childDescent
== 0)
4933 // An object with a zero descend value wants to take up the whole
4934 // height regardless of baseline
4935 lineHeight
= wxMax(lineHeight
, childSize
.y
);
4939 maxDescent
= wxMax(childDescent
, maxDescent
);
4940 maxAscent
= wxMax(childSize
.y
-childDescent
, maxAscent
);
4943 lineHeight
= wxMax(lineHeight
, (maxDescent
+ maxAscent
));
4945 maxWidth
= wxMax(maxWidth
, currentWidth
+startOffset
);
4946 lastEndPos
= child
->GetRange().GetEnd();
4948 node
= node
->GetNext();
4952 //wxASSERT(!(lastCompletedEndPos != -1 && lastCompletedEndPos < GetRange().GetEnd()-1));
4954 // Remove remaining unused line objects, if any
4955 ClearUnusedLines(lineCount
);
4957 // We need to add back the margins etc.
4959 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
4960 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxWidth
, currentPosition
.y
+ spaceAfterPara
));
4961 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
4962 SetCachedSize(marginRect
.GetSize());
4965 // The maximum size is the length of the paragraph stretched out into a line.
4966 // So if there were a single word, or an image, or a fixed-size text box, the object could be shrunk around
4967 // this size. TODO: take into account line breaks.
4969 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
4970 contentRect
= wxRect(wxPoint(0, 0), wxSize(paraSize
.x
+ wxMax(leftIndent
, leftIndent
+ leftSubIndent
) + rightIndent
, currentPosition
.y
+ spaceAfterPara
));
4971 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
4972 SetMaxSize(marginRect
.GetSize());
4975 // Find the greatest minimum size. Currently we only look at non-text objects,
4976 // which isn't ideal but it would be slow to find the maximum word width to
4977 // use as the minimum.
4980 node
= m_children
.GetFirst();
4983 wxRichTextObject
* child
= node
->GetData();
4985 // If floating, ignore. We already laid out floats.
4986 // Also ignore if empty object, except if we haven't got any
4988 if (!child
->IsFloating() && child
->GetRange().GetLength() != 0 && !child
->IsKindOf(CLASSINFO(wxRichTextPlainText
)))
4990 if (child
->GetCachedSize().x
> minWidth
)
4991 minWidth
= child
->GetMinSize().x
;
4993 node
= node
->GetNext();
4996 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
4997 contentRect
= wxRect(wxPoint(0, 0), wxSize(minWidth
, currentPosition
.y
+ spaceAfterPara
));
4998 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
4999 SetMinSize(marginRect
.GetSize());
5002 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5003 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
5004 // Use the text extents to calculate the size of each fragment in each line
5005 wxRichTextLineList::compatibility_iterator lineNode
= m_cachedLines
.GetFirst();
5008 wxRichTextLine
* line
= lineNode
->GetData();
5009 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5011 // Loop through objects until we get to the one within range
5012 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
5016 wxRichTextObject
* child
= node2
->GetData();
5018 if (child
->GetRange().GetLength() > 0 && !child
->GetRange().IsOutside(lineRange
))
5020 wxRichTextRange rangeToUse
= lineRange
;
5021 rangeToUse
.LimitTo(child
->GetRange());
5023 // Find the size of the child from the text extents, and store in an array
5024 // for drawing later
5026 if (rangeToUse
.GetStart() > GetRange().GetStart())
5027 left
= partialExtents
[(rangeToUse
.GetStart()-1) - GetRange().GetStart()];
5028 int right
= partialExtents
[rangeToUse
.GetEnd() - GetRange().GetStart()];
5029 int sz
= right
- left
;
5030 line
->GetObjectSizes().Add(sz
);
5032 else if (child
->GetRange().GetStart() > lineRange
.GetEnd())
5033 // Can break out of inner loop now since we've passed this line's range
5036 node2
= node2
->GetNext();
5039 lineNode
= lineNode
->GetNext();
5047 /// Apply paragraph styles, such as centering, to wrapped lines
5048 /// TODO: take into account box attributes, possibly
5049 void wxRichTextParagraph::ApplyParagraphStyle(wxRichTextLine
* line
, const wxRichTextAttr
& attr
, const wxRect
& rect
, wxDC
& dc
)
5051 if (!attr
.HasAlignment())
5054 wxPoint pos
= line
->GetPosition();
5055 wxSize size
= line
->GetSize();
5057 // centering, right-justification
5058 if (attr
.HasAlignment() && attr
.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE
)
5060 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
5061 pos
.x
= (rect
.GetWidth() - rightIndent
- size
.x
)/2 + pos
.x
;
5062 line
->SetPosition(pos
);
5064 else if (attr
.HasAlignment() && attr
.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT
)
5066 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
5067 pos
.x
= pos
.x
+ rect
.GetWidth() - size
.x
- rightIndent
;
5068 line
->SetPosition(pos
);
5072 /// Insert text at the given position
5073 bool wxRichTextParagraph::InsertText(long pos
, const wxString
& text
)
5075 wxRichTextObject
* childToUse
= NULL
;
5076 wxRichTextObjectList::compatibility_iterator nodeToUse
= wxRichTextObjectList::compatibility_iterator();
5078 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5081 wxRichTextObject
* child
= node
->GetData();
5082 if (child
->GetRange().Contains(pos
) && child
->GetRange().GetLength() > 0)
5089 node
= node
->GetNext();
5094 wxRichTextPlainText
* textObject
= wxDynamicCast(childToUse
, wxRichTextPlainText
);
5097 int posInString
= pos
- textObject
->GetRange().GetStart();
5099 wxString newText
= textObject
->GetText().Mid(0, posInString
) +
5100 text
+ textObject
->GetText().Mid(posInString
);
5101 textObject
->SetText(newText
);
5103 int textLength
= text
.length();
5105 textObject
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart(),
5106 textObject
->GetRange().GetEnd() + textLength
));
5108 // Increment the end range of subsequent fragments in this paragraph.
5109 // We'll set the paragraph range itself at a higher level.
5111 wxRichTextObjectList::compatibility_iterator node
= nodeToUse
->GetNext();
5114 wxRichTextObject
* child
= node
->GetData();
5115 child
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart() + textLength
,
5116 textObject
->GetRange().GetEnd() + textLength
));
5118 node
= node
->GetNext();
5125 // TODO: if not a text object, insert at closest position, e.g. in front of it
5131 // Don't pass parent initially to suppress auto-setting of parent range.
5132 // We'll do that at a higher level.
5133 wxRichTextPlainText
* textObject
= new wxRichTextPlainText(text
, this);
5135 AppendChild(textObject
);
5142 void wxRichTextParagraph::Copy(const wxRichTextParagraph
& obj
)
5144 wxRichTextCompositeObject::Copy(obj
);
5147 /// Clear the cached lines
5148 void wxRichTextParagraph::ClearLines()
5150 WX_CLEAR_LIST(wxRichTextLineList
, m_cachedLines
);
5153 /// Get/set the object size for the given range. Returns false if the range
5154 /// is invalid for this object.
5155 bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
5157 if (!range
.IsWithin(GetRange()))
5160 if (flags
& wxRICHTEXT_UNFORMATTED
)
5162 // Just use unformatted data, assume no line breaks
5165 wxArrayInt childExtents
;
5174 int maxLineHeight
= 0;
5176 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5179 wxRichTextObject
* child
= node
->GetData();
5180 if (!child
->GetRange().IsOutside(range
))
5182 // Floating objects have a zero size within the paragraph.
5183 if (child
->IsFloating())
5188 if (partialExtents
->GetCount() > 0)
5189 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5193 partialExtents
->Add(0 /* zero size */ + lastSize
);
5200 wxRichTextRange rangeToUse
= range
;
5201 rangeToUse
.LimitTo(child
->GetRange());
5202 int childDescent
= 0;
5204 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we've already cached the size,
5205 // but it's only going to be used after caching has taken place.
5206 if ((flags
& wxRICHTEXT_HEIGHT_ONLY
) && child
->GetCachedSize().y
!= 0)
5208 childDescent
= child
->GetDescent();
5209 childSize
= child
->GetCachedSize();
5211 if (childDescent
== 0)
5213 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5217 maxDescent
= wxMax(maxDescent
, childDescent
);
5218 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5221 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5223 sz
.y
= wxMax(sz
.y
, maxLineHeight
);
5224 sz
.x
+= childSize
.x
;
5225 descent
= maxDescent
;
5227 else if (child
->IsTopLevel())
5229 childDescent
= child
->GetDescent();
5230 childSize
= child
->GetCachedSize();
5232 if (childDescent
== 0)
5234 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5238 maxDescent
= wxMax(maxDescent
, childDescent
);
5239 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5242 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5244 sz
.y
= wxMax(sz
.y
, maxLineHeight
);
5245 sz
.x
+= childSize
.x
;
5246 descent
= maxDescent
;
5248 // FIXME: this won't change the original values.
5249 // Should we be calling GetRangeSize above instead of using cached values?
5251 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange()))
5253 child
->SetCachedSize(childSize
);
5254 child
->SetDescent(childDescent
);
5261 if (partialExtents
->GetCount() > 0)
5262 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5266 partialExtents
->Add(childSize
.x
+ lastSize
);
5269 else if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, context
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
), p
))
5271 if (childDescent
== 0)
5273 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5277 maxDescent
= wxMax(maxDescent
, childDescent
);
5278 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5281 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5283 sz
.y
= wxMax(sz
.y
, maxLineHeight
);
5284 sz
.x
+= childSize
.x
;
5285 descent
= maxDescent
;
5287 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange()))
5289 child
->SetCachedSize(childSize
);
5290 child
->SetDescent(childDescent
);
5296 if (partialExtents
->GetCount() > 0)
5297 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5302 for (i
= 0; i
< childExtents
.GetCount(); i
++)
5304 partialExtents
->Add(childExtents
[i
] + lastSize
);
5314 node
= node
->GetNext();
5320 // Use formatted data, with line breaks
5323 // We're going to loop through each line, and then for each line,
5324 // call GetRangeSize for the fragment that comprises that line.
5325 // Only we have to do that multiple times within the line, because
5326 // the line may be broken into pieces. For now ignore line break commands
5327 // (so we can assume that getting the unformatted size for a fragment
5328 // within a line is the actual size)
5330 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5333 wxRichTextLine
* line
= node
->GetData();
5334 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5335 if (!lineRange
.IsOutside(range
))
5339 int maxLineHeight
= 0;
5340 int maxLineWidth
= 0;
5342 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
5345 wxRichTextObject
* child
= node2
->GetData();
5347 if (!child
->IsFloating() && !child
->GetRange().IsOutside(lineRange
))
5349 wxRichTextRange rangeToUse
= lineRange
;
5350 rangeToUse
.LimitTo(child
->GetRange());
5351 if (child
->IsTopLevel())
5352 rangeToUse
= child
->GetOwnRange();
5355 int childDescent
= 0;
5356 if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, context
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
)))
5358 if (childDescent
== 0)
5360 // Assume that if descent is zero, this child can occupy the full line height
5361 // and does not need space for the line's maximum descent. So we influence
5362 // the overall max line height only.
5363 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5367 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5368 maxDescent
= wxMax(maxAscent
, childDescent
);
5370 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5371 maxLineWidth
+= childSize
.x
;
5375 node2
= node2
->GetNext();
5378 descent
= wxMax(descent
, maxDescent
);
5380 // Increase size by a line (TODO: paragraph spacing)
5381 sz
.y
+= maxLineHeight
;
5382 sz
.x
= wxMax(sz
.x
, maxLineWidth
);
5384 node
= node
->GetNext();
5391 /// Finds the absolute position and row height for the given character position
5392 bool wxRichTextParagraph::FindPosition(wxDC
& dc
, wxRichTextDrawingContext
& context
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
5396 wxRichTextLine
* line
= ((wxRichTextParagraphLayoutBox
*)GetParent())->GetLineAtPosition(0);
5398 *height
= line
->GetSize().y
;
5400 *height
= dc
.GetCharHeight();
5402 // -1 means 'the start of the buffer'.
5405 pt
= pt
+ line
->GetPosition();
5410 // The final position in a paragraph is taken to mean the position
5411 // at the start of the next paragraph.
5412 if (index
== GetRange().GetEnd())
5414 wxRichTextParagraphLayoutBox
* parent
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
5415 wxASSERT( parent
!= NULL
);
5417 // Find the height at the next paragraph, if any
5418 wxRichTextLine
* line
= parent
->GetLineAtPosition(index
+ 1);
5421 *height
= line
->GetSize().y
;
5422 pt
= line
->GetAbsolutePosition();
5426 *height
= dc
.GetCharHeight();
5427 int indent
= ConvertTenthsMMToPixels(dc
, m_attributes
.GetLeftIndent());
5428 pt
= wxPoint(indent
, GetCachedSize().y
);
5434 if (index
< GetRange().GetStart() || index
> GetRange().GetEnd())
5437 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5440 wxRichTextLine
* line
= node
->GetData();
5441 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5442 if (index
>= lineRange
.GetStart() && index
<= lineRange
.GetEnd())
5444 // If this is the last point in the line, and we're forcing the
5445 // returned value to be the start of the next line, do the required
5447 if (index
== lineRange
.GetEnd() && forceLineStart
)
5449 if (node
->GetNext())
5451 wxRichTextLine
* nextLine
= node
->GetNext()->GetData();
5452 *height
= nextLine
->GetSize().y
;
5453 pt
= nextLine
->GetAbsolutePosition();
5458 pt
.y
= line
->GetPosition().y
+ GetPosition().y
;
5460 wxRichTextRange
r(lineRange
.GetStart(), index
);
5464 // We find the size of the line up to this point,
5465 // then we can add this size to the line start position and
5466 // paragraph start position to find the actual position.
5468 if (GetRangeSize(r
, rangeSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, line
->GetPosition()+ GetPosition()))
5470 pt
.x
= line
->GetPosition().x
+ GetPosition().x
+ rangeSize
.x
;
5471 *height
= line
->GetSize().y
;
5478 node
= node
->GetNext();
5484 /// Hit-testing: returns a flag indicating hit test details, plus
5485 /// information about position
5486 int wxRichTextParagraph::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
5489 return wxRICHTEXT_HITTEST_NONE
;
5491 // If we're in the top-level container, then we can return
5492 // a suitable hit test code even if the point is outside the container area,
5493 // so that we can position the caret sensibly even if we don't
5494 // click on valid content. If we're not at the top-level, and the point
5495 // is not within this paragraph object, then we don't want to stop more
5496 // precise hit-testing from working prematurely, so return immediately.
5497 // NEW STRATEGY: use the parent boundary to test whether we're in the
5498 // right region, not the paragraph, since the paragraph may be positioned
5499 // some way in from where the user clicks.
5502 wxRichTextObject
* tempObj
, *tempContextObj
;
5503 if (GetParent() && GetParent()->wxRichTextObject::HitTest(dc
, context
, pt
, tmpPos
, & tempObj
, & tempContextObj
, flags
) == wxRICHTEXT_HITTEST_NONE
)
5504 return wxRICHTEXT_HITTEST_NONE
;
5507 wxRichTextObjectList::compatibility_iterator objNode
= m_children
.GetFirst();
5510 wxRichTextObject
* child
= objNode
->GetData();
5511 // Don't recurse if we have wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS,
5512 // and also, if this seems composite but actually is marked as atomic,
5514 if (child
->IsTopLevel() && ((flags
& wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS
) == 0) &&
5515 (! (((flags
& wxRICHTEXT_HITTEST_HONOUR_ATOMIC
) != 0) && child
->IsAtomic())))
5518 int hitTest
= child
->HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
);
5519 if (hitTest
!= wxRICHTEXT_HITTEST_NONE
)
5524 objNode
= objNode
->GetNext();
5527 wxPoint paraPos
= GetPosition();
5529 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5532 wxRichTextLine
* line
= node
->GetData();
5533 wxPoint linePos
= paraPos
+ line
->GetPosition();
5534 wxSize lineSize
= line
->GetSize();
5535 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5537 if (pt
.y
<= linePos
.y
+ lineSize
.y
)
5539 if (pt
.x
< linePos
.x
)
5541 textPosition
= lineRange
.GetStart();
5542 *obj
= FindObjectAtPosition(textPosition
);
5543 *contextObj
= GetContainer();
5544 return wxRICHTEXT_HITTEST_BEFORE
|wxRICHTEXT_HITTEST_OUTSIDE
;
5546 else if (pt
.x
>= (linePos
.x
+ lineSize
.x
))
5548 textPosition
= lineRange
.GetEnd();
5549 *obj
= FindObjectAtPosition(textPosition
);
5550 *contextObj
= GetContainer();
5551 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
;
5555 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5556 wxArrayInt partialExtents
;
5561 // This calculates the partial text extents
5562 GetRangeSize(lineRange
, paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, linePos
, & partialExtents
);
5564 int lastX
= linePos
.x
;
5566 for (i
= 0; i
< partialExtents
.GetCount(); i
++)
5568 int nextX
= partialExtents
[i
] + linePos
.x
;
5570 if (pt
.x
>= lastX
&& pt
.x
<= nextX
)
5572 textPosition
= i
+ lineRange
.GetStart(); // minus 1?
5574 *obj
= FindObjectAtPosition(textPosition
);
5575 *contextObj
= GetContainer();
5577 // So now we know it's between i-1 and i.
5578 // Let's see if we can be more precise about
5579 // which side of the position it's on.
5581 int midPoint
= (nextX
+ lastX
)/2;
5582 if (pt
.x
>= midPoint
)
5583 return wxRICHTEXT_HITTEST_AFTER
;
5585 return wxRICHTEXT_HITTEST_BEFORE
;
5592 int lastX
= linePos
.x
;
5593 for (i
= lineRange
.GetStart(); i
<= lineRange
.GetEnd(); i
++)
5598 wxRichTextRange
rangeToUse(lineRange
.GetStart(), i
);
5600 GetRangeSize(rangeToUse
, childSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, linePos
);
5602 int nextX
= childSize
.x
+ linePos
.x
;
5604 if (pt
.x
>= lastX
&& pt
.x
<= nextX
)
5608 *obj
= FindObjectAtPosition(textPosition
);
5609 *contextObj
= GetContainer();
5611 // So now we know it's between i-1 and i.
5612 // Let's see if we can be more precise about
5613 // which side of the position it's on.
5615 int midPoint
= (nextX
+ lastX
)/2;
5616 if (pt
.x
>= midPoint
)
5617 return wxRICHTEXT_HITTEST_AFTER
;
5619 return wxRICHTEXT_HITTEST_BEFORE
;
5630 node
= node
->GetNext();
5633 return wxRICHTEXT_HITTEST_NONE
;
5636 /// Split an object at this position if necessary, and return
5637 /// the previous object, or NULL if inserting at beginning.
5638 wxRichTextObject
* wxRichTextParagraph::SplitAt(long pos
, wxRichTextObject
** previousObject
)
5640 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5643 wxRichTextObject
* child
= node
->GetData();
5645 if (pos
== child
->GetRange().GetStart())
5649 if (node
->GetPrevious())
5650 *previousObject
= node
->GetPrevious()->GetData();
5652 *previousObject
= NULL
;
5658 if (child
->GetRange().Contains(pos
))
5660 // This should create a new object, transferring part of
5661 // the content to the old object and the rest to the new object.
5662 wxRichTextObject
* newObject
= child
->DoSplit(pos
);
5664 // If we couldn't split this object, just insert in front of it.
5667 // Maybe this is an empty string, try the next one
5672 // Insert the new object after 'child'
5673 if (node
->GetNext())
5674 m_children
.Insert(node
->GetNext(), newObject
);
5676 m_children
.Append(newObject
);
5677 newObject
->SetParent(this);
5680 *previousObject
= child
;
5686 node
= node
->GetNext();
5689 *previousObject
= NULL
;
5693 /// Move content to a list from obj on
5694 void wxRichTextParagraph::MoveToList(wxRichTextObject
* obj
, wxList
& list
)
5696 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(obj
);
5699 wxRichTextObject
* child
= node
->GetData();
5702 wxRichTextObjectList::compatibility_iterator oldNode
= node
;
5704 node
= node
->GetNext();
5706 m_children
.DeleteNode(oldNode
);
5710 /// Add content back from list
5711 void wxRichTextParagraph::MoveFromList(wxList
& list
)
5713 for (wxList::compatibility_iterator node
= list
.GetFirst(); node
; node
= node
->GetNext())
5715 AppendChild((wxRichTextObject
*) node
->GetData());
5720 void wxRichTextParagraph::CalculateRange(long start
, long& end
)
5722 wxRichTextCompositeObject::CalculateRange(start
, end
);
5724 // Add one for end of paragraph
5727 m_range
.SetRange(start
, end
);
5730 /// Find the object at the given position
5731 wxRichTextObject
* wxRichTextParagraph::FindObjectAtPosition(long position
)
5733 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5736 wxRichTextObject
* obj
= node
->GetData();
5737 if (obj
->GetRange().Contains(position
) ||
5738 obj
->GetRange().GetStart() == position
||
5739 obj
->GetRange().GetEnd() == position
)
5742 node
= node
->GetNext();
5747 /// Get the plain text searching from the start or end of the range.
5748 /// The resulting string may be shorter than the range given.
5749 bool wxRichTextParagraph::GetContiguousPlainText(wxString
& text
, const wxRichTextRange
& range
, bool fromStart
)
5751 text
= wxEmptyString
;
5755 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5758 wxRichTextObject
* obj
= node
->GetData();
5759 if (!obj
->GetRange().IsOutside(range
))
5761 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5764 text
+= textObj
->GetTextForRange(range
);
5772 node
= node
->GetNext();
5777 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetLast();
5780 wxRichTextObject
* obj
= node
->GetData();
5781 if (!obj
->GetRange().IsOutside(range
))
5783 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5786 text
= textObj
->GetTextForRange(range
) + text
;
5790 text
= wxT(" ") + text
;
5794 node
= node
->GetPrevious();
5801 /// Find a suitable wrap position.
5802 bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange
& range
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int availableSpace
, long& wrapPosition
, wxArrayInt
* partialExtents
)
5804 if (range
.GetLength() <= 0)
5807 // Find the first position where the line exceeds the available space.
5809 long breakPosition
= range
.GetEnd();
5811 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5812 if (partialExtents
&& partialExtents
->GetCount() >= (size_t) (GetRange().GetLength()-1)) // the final position in a paragraph is the newline
5816 if (range
.GetStart() > GetRange().GetStart())
5817 widthBefore
= (*partialExtents
)[range
.GetStart() - GetRange().GetStart() - 1];
5822 for (i
= (size_t) range
.GetStart(); i
<= (size_t) range
.GetEnd(); i
++)
5824 int widthFromStartOfThisRange
= (*partialExtents
)[i
- GetRange().GetStart()] - widthBefore
;
5826 if (widthFromStartOfThisRange
> availableSpace
)
5828 breakPosition
= i
-1;
5836 // Binary chop for speed
5837 long minPos
= range
.GetStart();
5838 long maxPos
= range
.GetEnd();
5841 if (minPos
== maxPos
)
5844 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
5846 if (sz
.x
> availableSpace
)
5847 breakPosition
= minPos
- 1;
5850 else if ((maxPos
- minPos
) == 1)
5853 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
5855 if (sz
.x
> availableSpace
)
5856 breakPosition
= minPos
- 1;
5859 GetRangeSize(wxRichTextRange(range
.GetStart(), maxPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
5860 if (sz
.x
> availableSpace
)
5861 breakPosition
= maxPos
-1;
5867 long nextPos
= minPos
+ ((maxPos
- minPos
) / 2);
5870 GetRangeSize(wxRichTextRange(range
.GetStart(), nextPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
5872 if (sz
.x
> availableSpace
)
5884 // Now we know the last position on the line.
5885 // Let's try to find a word break.
5888 if (GetContiguousPlainText(plainText
, wxRichTextRange(range
.GetStart(), breakPosition
), false))
5890 int newLinePos
= plainText
.Find(wxRichTextLineBreakChar
);
5891 if (newLinePos
!= wxNOT_FOUND
)
5893 breakPosition
= wxMax(0, range
.GetStart() + newLinePos
);
5897 int spacePos
= plainText
.Find(wxT(' '), true);
5898 int tabPos
= plainText
.Find(wxT('\t'), true);
5899 int pos
= wxMax(spacePos
, tabPos
);
5900 if (pos
!= wxNOT_FOUND
)
5902 int positionsFromEndOfString
= plainText
.length() - pos
- 1;
5903 breakPosition
= breakPosition
- positionsFromEndOfString
;
5908 wrapPosition
= breakPosition
;
5913 /// Get the bullet text for this paragraph.
5914 wxString
wxRichTextParagraph::GetBulletText()
5916 if (GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
||
5917 (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
))
5918 return wxEmptyString
;
5920 int number
= GetAttributes().GetBulletNumber();
5923 if ((GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ARABIC
) || (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
))
5925 text
.Printf(wxT("%d"), number
);
5927 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_UPPER
)
5929 // TODO: Unicode, and also check if number > 26
5930 text
.Printf(wxT("%c"), (wxChar
) (number
+64));
5932 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_LOWER
)
5934 // TODO: Unicode, and also check if number > 26
5935 text
.Printf(wxT("%c"), (wxChar
) (number
+96));
5937 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER
)
5939 text
= wxRichTextDecimalToRoman(number
);
5941 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER
)
5943 text
= wxRichTextDecimalToRoman(number
);
5946 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
)
5948 text
= GetAttributes().GetBulletText();
5951 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
)
5953 // The outline style relies on the text being computed statically,
5954 // since it depends on other levels points (e.g. 1.2.1.1). So normally the bullet text
5955 // should be stored in the attributes; if not, just use the number for this
5956 // level, as previously computed.
5957 if (!GetAttributes().GetBulletText().IsEmpty())
5958 text
= GetAttributes().GetBulletText();
5961 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PARENTHESES
)
5963 text
= wxT("(") + text
+ wxT(")");
5965 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_RIGHT_PARENTHESIS
)
5967 text
= text
+ wxT(")");
5970 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PERIOD
)
5978 /// Allocate or reuse a line object
5979 wxRichTextLine
* wxRichTextParagraph::AllocateLine(int pos
)
5981 if (pos
< (int) m_cachedLines
.GetCount())
5983 wxRichTextLine
* line
= m_cachedLines
.Item(pos
)->GetData();
5989 wxRichTextLine
* line
= new wxRichTextLine(this);
5990 m_cachedLines
.Append(line
);
5995 /// Clear remaining unused line objects, if any
5996 bool wxRichTextParagraph::ClearUnusedLines(int lineCount
)
5998 int cachedLineCount
= m_cachedLines
.GetCount();
5999 if ((int) cachedLineCount
> lineCount
)
6001 for (int i
= 0; i
< (int) (cachedLineCount
- lineCount
); i
++)
6003 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetLast();
6004 wxRichTextLine
* line
= node
->GetData();
6005 m_cachedLines
.Erase(node
);
6012 /// Get combined attributes of the base style, paragraph style and character style. We use this to dynamically
6013 /// retrieve the actual style.
6014 wxRichTextAttr
wxRichTextParagraph::GetCombinedAttributes(const wxRichTextAttr
& contentStyle
, bool includingBoxAttr
) const
6016 wxRichTextAttr attr
;
6017 wxRichTextParagraphLayoutBox
* buf
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
6020 attr
= buf
->GetBasicStyle();
6021 if (!includingBoxAttr
)
6023 attr
.GetTextBoxAttr().Reset();
6024 // The background colour will be painted by the container, and we don't
6025 // want to unnecessarily overwrite the background when we're drawing text
6026 // because this may erase the guideline (which appears just under the text
6027 // if there's no padding).
6028 attr
.SetFlags(attr
.GetFlags() & ~wxTEXT_ATTR_BACKGROUND_COLOUR
);
6030 wxRichTextApplyStyle(attr
, GetAttributes());
6033 attr
= GetAttributes();
6035 wxRichTextApplyStyle(attr
, contentStyle
);
6039 /// Get combined attributes of the base style and paragraph style.
6040 wxRichTextAttr
wxRichTextParagraph::GetCombinedAttributes(bool includingBoxAttr
) const
6042 wxRichTextAttr attr
;
6043 wxRichTextParagraphLayoutBox
* buf
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
6046 attr
= buf
->GetBasicStyle();
6047 if (!includingBoxAttr
)
6048 attr
.GetTextBoxAttr().Reset();
6049 wxRichTextApplyStyle(attr
, GetAttributes());
6052 attr
= GetAttributes();
6057 // Create default tabstop array
6058 void wxRichTextParagraph::InitDefaultTabs()
6060 // create a default tab list at 10 mm each.
6061 for (int i
= 0; i
< 20; ++i
)
6063 sm_defaultTabs
.Add(i
*100);
6067 // Clear default tabstop array
6068 void wxRichTextParagraph::ClearDefaultTabs()
6070 sm_defaultTabs
.Clear();
6073 void wxRichTextParagraph::LayoutFloat(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, int style
, wxRichTextFloatCollector
* floatCollector
)
6075 wxRichTextObjectList::compatibility_iterator node
= GetChildren().GetFirst();
6078 wxRichTextObject
* anchored
= node
->GetData();
6079 if (anchored
&& anchored
->IsFloating() && !floatCollector
->HasFloat(anchored
))
6083 anchored
->GetRangeSize(anchored
->GetRange(), size
, descent
, dc
, context
, style
);
6086 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().IsValid())
6088 offsetY
= anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetValue();
6089 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
6091 offsetY
= ConvertTenthsMMToPixels(dc
, offsetY
);
6095 int pos
= floatCollector
->GetFitPosition(anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode(), rect
.y
+ offsetY
, size
.y
);
6097 /* Update the offset */
6098 int newOffsetY
= pos
- rect
.y
;
6099 if (newOffsetY
!= offsetY
)
6101 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
6102 newOffsetY
= ConvertPixelsToTenthsMM(dc
, newOffsetY
);
6103 anchored
->GetAttributes().GetTextBoxAttr().GetTop().SetValue(newOffsetY
);
6106 if (anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT
)
6108 else if (anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT
)
6109 x
= rect
.x
+ rect
.width
- size
.x
;
6111 anchored
->SetPosition(wxPoint(x
, pos
));
6112 anchored
->SetCachedSize(size
);
6113 floatCollector
->CollectFloat(this, anchored
);
6116 node
= node
->GetNext();
6120 // Get the first position from pos that has a line break character.
6121 long wxRichTextParagraph::GetFirstLineBreakPosition(long pos
)
6123 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
6126 wxRichTextObject
* obj
= node
->GetData();
6127 if (pos
>= obj
->GetRange().GetStart() && pos
<= obj
->GetRange().GetEnd())
6129 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
6132 long breakPos
= textObj
->GetFirstLineBreakPosition(pos
);
6137 node
= node
->GetNext();
6144 * This object represents a line in a paragraph, and stores
6145 * offsets from the start of the paragraph representing the
6146 * start and end positions of the line.
6149 wxRichTextLine::wxRichTextLine(wxRichTextParagraph
* parent
)
6155 void wxRichTextLine::Init(wxRichTextParagraph
* parent
)
6158 m_range
.SetRange(-1, -1);
6159 m_pos
= wxPoint(0, 0);
6160 m_size
= wxSize(0, 0);
6162 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6163 m_objectSizes
.Clear();
6168 void wxRichTextLine::Copy(const wxRichTextLine
& obj
)
6170 m_range
= obj
.m_range
;
6171 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6172 m_objectSizes
= obj
.m_objectSizes
;
6176 /// Get the absolute object position
6177 wxPoint
wxRichTextLine::GetAbsolutePosition() const
6179 return m_parent
->GetPosition() + m_pos
;
6182 /// Get the absolute range
6183 wxRichTextRange
wxRichTextLine::GetAbsoluteRange() const
6185 wxRichTextRange
range(m_range
.GetStart() + m_parent
->GetRange().GetStart(), 0);
6186 range
.SetEnd(range
.GetStart() + m_range
.GetLength()-1);
6191 * wxRichTextPlainText
6192 * This object represents a single piece of text.
6195 IMPLEMENT_DYNAMIC_CLASS(wxRichTextPlainText
, wxRichTextObject
)
6197 wxRichTextPlainText::wxRichTextPlainText(const wxString
& text
, wxRichTextObject
* parent
, wxRichTextAttr
* style
):
6198 wxRichTextObject(parent
)
6201 SetAttributes(*style
);
6206 #define USE_KERNING_FIX 1
6208 // If insufficient tabs are defined, this is the tab width used
6209 #define WIDTH_FOR_DEFAULT_TABS 50
6212 bool wxRichTextPlainText::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int WXUNUSED(style
))
6214 wxRichTextParagraph
* para
= wxDynamicCast(GetParent(), wxRichTextParagraph
);
6215 wxASSERT (para
!= NULL
);
6217 wxRichTextAttr
textAttr(para
? para
->GetCombinedAttributes(GetAttributes(), false /* no box attributes */) : GetAttributes());
6218 context
.ApplyVirtualAttributes(textAttr
, this);
6220 // Let's make the assumption for now that for content in a paragraph, including
6221 // text, we never have a discontinuous selection. So we only deal with a
6223 wxRichTextRange selectionRange
;
6224 if (selection
.IsValid())
6226 wxRichTextRangeArray selectionRanges
= selection
.GetSelectionForObject(this);
6227 if (selectionRanges
.GetCount() > 0)
6228 selectionRange
= selectionRanges
[0];
6230 selectionRange
= wxRICHTEXT_NO_SELECTION
;
6233 selectionRange
= wxRICHTEXT_NO_SELECTION
;
6235 int offset
= GetRange().GetStart();
6237 // Replace line break characters with spaces
6238 wxString str
= m_text
;
6239 wxString toRemove
= wxRichTextLineBreakChar
;
6240 str
.Replace(toRemove
, wxT(" "));
6241 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS
))
6244 long len
= range
.GetLength();
6245 wxString stringChunk
= str
.Mid(range
.GetStart() - offset
, (size_t) len
);
6247 // Test for the optimized situations where all is selected, or none
6250 wxFont
textFont(GetBuffer()->GetFontTable().FindFont(textAttr
));
6251 wxCheckSetFont(dc
, textFont
);
6252 int charHeight
= dc
.GetCharHeight();
6255 if ( textFont
.IsOk() )
6257 if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
) )
6259 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6260 textFont
.SetPointSize( static_cast<int>(size
) );
6263 wxCheckSetFont(dc
, textFont
);
6265 else if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) )
6267 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6268 textFont
.SetPointSize( static_cast<int>(size
) );
6270 int sub_height
= static_cast<int>( static_cast<double>(charHeight
) / wxSCRIPT_MUL_FACTOR
);
6271 y
= rect
.y
+ (rect
.height
- sub_height
+ (descent
- m_descent
));
6272 wxCheckSetFont(dc
, textFont
);
6277 y
= rect
.y
+ (rect
.height
- charHeight
- (descent
- m_descent
));
6283 y
= rect
.y
+ (rect
.height
- charHeight
- (descent
- m_descent
));
6286 // TODO: new selection code
6288 // (a) All selected.
6289 if (selectionRange
.GetStart() <= range
.GetStart() && selectionRange
.GetEnd() >= range
.GetEnd())
6291 DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, true);
6293 // (b) None selected.
6294 else if (selectionRange
.GetEnd() < range
.GetStart() || selectionRange
.GetStart() > range
.GetEnd())
6296 // Draw all unselected
6297 DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, false);
6301 // (c) Part selected, part not
6302 // Let's draw unselected chunk, selected chunk, then unselected chunk.
6304 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6306 // 1. Initial unselected chunk, if any, up until start of selection.
6307 if (selectionRange
.GetStart() > range
.GetStart() && selectionRange
.GetStart() <= range
.GetEnd())
6309 int r1
= range
.GetStart();
6310 int s1
= selectionRange
.GetStart()-1;
6311 int fragmentLen
= s1
- r1
+ 1;
6312 if (fragmentLen
< 0)
6314 wxLogDebug(wxT("Mid(%d, %d"), (int)(r1
- offset
), (int)fragmentLen
);
6316 wxString stringFragment
= str
.Mid(r1
- offset
, fragmentLen
);
6318 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false);
6321 if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
)
6323 // Compensate for kerning difference
6324 wxString
stringFragment2(str
.Mid(r1
- offset
, fragmentLen
+1));
6325 wxString
stringFragment3(str
.Mid(r1
- offset
+ fragmentLen
, 1));
6327 wxCoord w1
, h1
, w2
, h2
, w3
, h3
;
6328 dc
.GetTextExtent(stringFragment
, & w1
, & h1
);
6329 dc
.GetTextExtent(stringFragment2
, & w2
, & h2
);
6330 dc
.GetTextExtent(stringFragment3
, & w3
, & h3
);
6332 int kerningDiff
= (w1
+ w3
) - w2
;
6333 x
= x
- kerningDiff
;
6338 // 2. Selected chunk, if any.
6339 if (selectionRange
.GetEnd() >= range
.GetStart())
6341 int s1
= wxMax(selectionRange
.GetStart(), range
.GetStart());
6342 int s2
= wxMin(selectionRange
.GetEnd(), range
.GetEnd());
6344 int fragmentLen
= s2
- s1
+ 1;
6345 if (fragmentLen
< 0)
6347 wxLogDebug(wxT("Mid(%d, %d"), (int)(s1
- offset
), (int)fragmentLen
);
6349 wxString stringFragment
= str
.Mid(s1
- offset
, fragmentLen
);
6351 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, true);
6354 if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
)
6356 // Compensate for kerning difference
6357 wxString
stringFragment2(str
.Mid(s1
- offset
, fragmentLen
+1));
6358 wxString
stringFragment3(str
.Mid(s1
- offset
+ fragmentLen
, 1));
6360 wxCoord w1
, h1
, w2
, h2
, w3
, h3
;
6361 dc
.GetTextExtent(stringFragment
, & w1
, & h1
);
6362 dc
.GetTextExtent(stringFragment2
, & w2
, & h2
);
6363 dc
.GetTextExtent(stringFragment3
, & w3
, & h3
);
6365 int kerningDiff
= (w1
+ w3
) - w2
;
6366 x
= x
- kerningDiff
;
6371 // 3. Remaining unselected chunk, if any
6372 if (selectionRange
.GetEnd() < range
.GetEnd())
6374 int s2
= wxMin(selectionRange
.GetEnd()+1, range
.GetEnd());
6375 int r2
= range
.GetEnd();
6377 int fragmentLen
= r2
- s2
+ 1;
6378 if (fragmentLen
< 0)
6380 wxLogDebug(wxT("Mid(%d, %d"), (int)(s2
- offset
), (int)fragmentLen
);
6382 wxString stringFragment
= str
.Mid(s2
- offset
, fragmentLen
);
6384 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false);
6391 bool wxRichTextPlainText::DrawTabbedString(wxDC
& dc
, const wxRichTextAttr
& attr
, const wxRect
& rect
,wxString
& str
, wxCoord
& x
, wxCoord
& y
, bool selected
)
6393 bool hasTabs
= (str
.Find(wxT('\t')) != wxNOT_FOUND
);
6395 wxArrayInt tabArray
;
6399 if (attr
.GetTabs().IsEmpty())
6400 tabArray
= wxRichTextParagraph::GetDefaultTabs();
6402 tabArray
= attr
.GetTabs();
6403 tabCount
= tabArray
.GetCount();
6405 for (int i
= 0; i
< tabCount
; ++i
)
6407 int pos
= tabArray
[i
];
6408 pos
= ConvertTenthsMMToPixels(dc
, pos
);
6415 int nextTabPos
= -1;
6421 wxColour
highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
));
6422 wxColour
highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
));
6424 wxCheckSetBrush(dc
, wxBrush(highlightColour
));
6425 wxCheckSetPen(dc
, wxPen(highlightColour
));
6426 dc
.SetTextForeground(highlightTextColour
);
6427 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6431 dc
.SetTextForeground(attr
.GetTextColour());
6433 if (attr
.HasFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
) && attr
.GetBackgroundColour().IsOk())
6435 dc
.SetBackgroundMode(wxBRUSHSTYLE_SOLID
);
6436 dc
.SetTextBackground(attr
.GetBackgroundColour());
6439 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6442 wxCoord x_orig
= GetParent()->GetPosition().x
;
6445 // the string has a tab
6446 // break up the string at the Tab
6447 wxString stringChunk
= str
.BeforeFirst(wxT('\t'));
6448 str
= str
.AfterFirst(wxT('\t'));
6449 dc
.GetTextExtent(stringChunk
, & w
, & h
);
6451 bool not_found
= true;
6452 for (int i
= 0; i
< tabCount
&& not_found
; ++i
)
6454 nextTabPos
= tabArray
.Item(i
) + x_orig
;
6456 // Find the next tab position.
6457 // Even if we're at the end of the tab array, we must still draw the chunk.
6459 if (nextTabPos
> tabPos
|| (i
== (tabCount
- 1)))
6461 if (nextTabPos
<= tabPos
)
6463 int defaultTabWidth
= ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
);
6464 nextTabPos
= tabPos
+ defaultTabWidth
;
6471 wxRect
selRect(x
, rect
.y
, w
, rect
.GetHeight());
6472 dc
.DrawRectangle(selRect
);
6474 dc
.DrawText(stringChunk
, x
, y
);
6476 if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
))
6478 wxPen oldPen
= dc
.GetPen();
6479 wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1));
6480 dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5));
6481 wxCheckSetPen(dc
, oldPen
);
6487 hasTabs
= (str
.Find(wxT('\t')) != wxNOT_FOUND
);
6492 dc
.GetTextExtent(str
, & w
, & h
);
6495 wxRect
selRect(x
, rect
.y
, w
, rect
.GetHeight());
6496 dc
.DrawRectangle(selRect
);
6498 dc
.DrawText(str
, x
, y
);
6500 if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
))
6502 wxPen oldPen
= dc
.GetPen();
6503 wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1));
6504 dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5));
6505 wxCheckSetPen(dc
, oldPen
);
6514 /// Lay the item out
6515 bool wxRichTextPlainText::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& WXUNUSED(rect
), const wxRect
& WXUNUSED(parentRect
), int WXUNUSED(style
))
6517 // Only lay out if we haven't already cached the size
6519 GetRangeSize(GetRange(), m_size
, m_descent
, dc
, context
, 0, wxPoint(0, 0));
6521 // Eventually we want to have a reasonable estimate of minimum size.
6522 m_minSize
= wxSize(0, 0);
6527 void wxRichTextPlainText::Copy(const wxRichTextPlainText
& obj
)
6529 wxRichTextObject::Copy(obj
);
6531 m_text
= obj
.m_text
;
6534 /// Get/set the object size for the given range. Returns false if the range
6535 /// is invalid for this object.
6536 bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int WXUNUSED(flags
), wxPoint position
, wxArrayInt
* partialExtents
) const
6538 if (!range
.IsWithin(GetRange()))
6541 wxRichTextParagraph
* para
= wxDynamicCast(GetParent(), wxRichTextParagraph
);
6542 wxASSERT (para
!= NULL
);
6544 int relativeX
= position
.x
- GetParent()->GetPosition().x
;
6546 wxRichTextAttr
textAttr(para
? para
->GetCombinedAttributes(GetAttributes()) : GetAttributes());
6547 context
.ApplyVirtualAttributes(textAttr
, (wxRichTextObject
*) this);
6549 // Always assume unformatted text, since at this level we have no knowledge
6550 // of line breaks - and we don't need it, since we'll calculate size within
6551 // formatted text by doing it in chunks according to the line ranges
6553 bool bScript(false);
6554 wxFont
font(GetBuffer()->GetFontTable().FindFont(textAttr
));
6557 if ( textAttr
.HasTextEffects() && ( (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
)
6558 || (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) ) )
6560 wxFont textFont
= font
;
6561 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6562 textFont
.SetPointSize( static_cast<int>(size
) );
6563 wxCheckSetFont(dc
, textFont
);
6568 wxCheckSetFont(dc
, font
);
6572 bool haveDescent
= false;
6573 int startPos
= range
.GetStart() - GetRange().GetStart();
6574 long len
= range
.GetLength();
6576 wxString
str(m_text
);
6577 wxString toReplace
= wxRichTextLineBreakChar
;
6578 str
.Replace(toReplace
, wxT(" "));
6580 wxString stringChunk
= str
.Mid(startPos
, (size_t) len
);
6582 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS
))
6583 stringChunk
.MakeUpper();
6587 if (stringChunk
.Find(wxT('\t')) != wxNOT_FOUND
)
6589 // the string has a tab
6590 wxArrayInt tabArray
;
6591 if (textAttr
.GetTabs().IsEmpty())
6592 tabArray
= wxRichTextParagraph::GetDefaultTabs();
6594 tabArray
= textAttr
.GetTabs();
6596 int tabCount
= tabArray
.GetCount();
6598 for (int i
= 0; i
< tabCount
; ++i
)
6600 int pos
= tabArray
[i
];
6601 pos
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, pos
);
6605 int nextTabPos
= -1;
6607 while (stringChunk
.Find(wxT('\t')) >= 0)
6609 int absoluteWidth
= 0;
6611 // the string has a tab
6612 // break up the string at the Tab
6613 wxString stringFragment
= stringChunk
.BeforeFirst(wxT('\t'));
6614 stringChunk
= stringChunk
.AfterFirst(wxT('\t'));
6619 if (partialExtents
->GetCount() > 0)
6620 oldWidth
= (*partialExtents
)[partialExtents
->GetCount()-1];
6624 // Add these partial extents
6626 dc
.GetPartialTextExtents(stringFragment
, p
);
6628 for (j
= 0; j
< p
.GetCount(); j
++)
6629 partialExtents
->Add(oldWidth
+ p
[j
]);
6631 if (partialExtents
->GetCount() > 0)
6632 absoluteWidth
= (*partialExtents
)[(*partialExtents
).GetCount()-1] + relativeX
;
6634 absoluteWidth
= relativeX
;
6638 dc
.GetTextExtent(stringFragment
, & w
, & h
);
6640 absoluteWidth
= width
+ relativeX
;
6644 bool notFound
= true;
6645 for (int i
= 0; i
< tabCount
&& notFound
; ++i
)
6647 nextTabPos
= tabArray
.Item(i
);
6649 // Find the next tab position.
6650 // Even if we're at the end of the tab array, we must still process the chunk.
6652 if (nextTabPos
> absoluteWidth
|| (i
== (tabCount
- 1)))
6654 if (nextTabPos
<= absoluteWidth
)
6656 int defaultTabWidth
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
);
6657 nextTabPos
= absoluteWidth
+ defaultTabWidth
;
6661 width
= nextTabPos
- relativeX
;
6664 partialExtents
->Add(width
);
6670 if (!stringChunk
.IsEmpty())
6675 if (partialExtents
->GetCount() > 0)
6676 oldWidth
= (*partialExtents
)[partialExtents
->GetCount()-1];
6680 // Add these partial extents
6682 dc
.GetPartialTextExtents(stringChunk
, p
);
6684 for (j
= 0; j
< p
.GetCount(); j
++)
6685 partialExtents
->Add(oldWidth
+ p
[j
]);
6689 dc
.GetTextExtent(stringChunk
, & w
, & h
, & descent
);
6697 int charHeight
= dc
.GetCharHeight();
6698 if ((*partialExtents
).GetCount() > 0)
6699 w
= (*partialExtents
)[partialExtents
->GetCount()-1];
6702 size
= wxSize(w
, charHeight
);
6706 size
= wxSize(width
, dc
.GetCharHeight());
6710 dc
.GetTextExtent(wxT("X"), & w
, & h
, & descent
);
6718 /// Do a split, returning an object containing the second part, and setting
6719 /// the first part in 'this'.
6720 wxRichTextObject
* wxRichTextPlainText::DoSplit(long pos
)
6722 long index
= pos
- GetRange().GetStart();
6724 if (index
< 0 || index
>= (int) m_text
.length())
6727 wxString firstPart
= m_text
.Mid(0, index
);
6728 wxString secondPart
= m_text
.Mid(index
);
6732 wxRichTextPlainText
* newObject
= new wxRichTextPlainText(secondPart
);
6733 newObject
->SetAttributes(GetAttributes());
6734 newObject
->SetProperties(GetProperties());
6736 newObject
->SetRange(wxRichTextRange(pos
, GetRange().GetEnd()));
6737 GetRange().SetEnd(pos
-1);
6743 void wxRichTextPlainText::CalculateRange(long start
, long& end
)
6745 end
= start
+ m_text
.length() - 1;
6746 m_range
.SetRange(start
, end
);
6750 bool wxRichTextPlainText::DeleteRange(const wxRichTextRange
& range
)
6752 wxRichTextRange r
= range
;
6754 r
.LimitTo(GetRange());
6756 if (r
.GetStart() == GetRange().GetStart() && r
.GetEnd() == GetRange().GetEnd())
6762 long startIndex
= r
.GetStart() - GetRange().GetStart();
6763 long len
= r
.GetLength();
6765 m_text
= m_text
.Mid(0, startIndex
) + m_text
.Mid(startIndex
+len
);
6769 /// Get text for the given range.
6770 wxString
wxRichTextPlainText::GetTextForRange(const wxRichTextRange
& range
) const
6772 wxRichTextRange r
= range
;
6774 r
.LimitTo(GetRange());
6776 long startIndex
= r
.GetStart() - GetRange().GetStart();
6777 long len
= r
.GetLength();
6779 return m_text
.Mid(startIndex
, len
);
6782 /// Returns true if this object can merge itself with the given one.
6783 bool wxRichTextPlainText::CanMerge(wxRichTextObject
* object
) const
6785 return object
->GetClassInfo() == CLASSINFO(wxRichTextPlainText
) &&
6786 (m_text
.empty() || (wxTextAttrEq(GetAttributes(), object
->GetAttributes()) && m_properties
== object
->GetProperties()));
6789 /// Returns true if this object merged itself with the given one.
6790 /// The calling code will then delete the given object.
6791 bool wxRichTextPlainText::Merge(wxRichTextObject
* object
)
6793 wxRichTextPlainText
* textObject
= wxDynamicCast(object
, wxRichTextPlainText
);
6794 wxASSERT( textObject
!= NULL
);
6798 m_text
+= textObject
->GetText();
6799 wxRichTextApplyStyle(m_attributes
, textObject
->GetAttributes());
6806 /// Dump to output stream for debugging
6807 void wxRichTextPlainText::Dump(wxTextOutputStream
& stream
)
6809 wxRichTextObject::Dump(stream
);
6810 stream
<< m_text
<< wxT("\n");
6813 /// Get the first position from pos that has a line break character.
6814 long wxRichTextPlainText::GetFirstLineBreakPosition(long pos
)
6817 int len
= m_text
.length();
6818 int startPos
= pos
- m_range
.GetStart();
6819 for (i
= startPos
; i
< len
; i
++)
6821 wxChar ch
= m_text
[i
];
6822 if (ch
== wxRichTextLineBreakChar
)
6824 return i
+ m_range
.GetStart();
6832 * This is a kind of box, used to represent the whole buffer
6835 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBuffer
, wxRichTextParagraphLayoutBox
)
6837 wxList
wxRichTextBuffer::sm_handlers
;
6838 wxList
wxRichTextBuffer::sm_drawingHandlers
;
6839 wxRichTextFieldTypeHashMap
wxRichTextBuffer::sm_fieldTypes
;
6840 wxRichTextRenderer
* wxRichTextBuffer::sm_renderer
= NULL
;
6841 int wxRichTextBuffer::sm_bulletRightMargin
= 20;
6842 float wxRichTextBuffer::sm_bulletProportion
= (float) 0.3;
6845 void wxRichTextBuffer::Init()
6847 m_commandProcessor
= new wxCommandProcessor
;
6848 m_styleSheet
= NULL
;
6850 m_batchedCommandDepth
= 0;
6851 m_batchedCommand
= NULL
;
6859 wxRichTextBuffer::~wxRichTextBuffer()
6861 delete m_commandProcessor
;
6862 delete m_batchedCommand
;
6865 ClearEventHandlers();
6868 void wxRichTextBuffer::ResetAndClearCommands()
6872 GetCommandProcessor()->ClearCommands();
6875 Invalidate(wxRICHTEXT_ALL
);
6878 void wxRichTextBuffer::Copy(const wxRichTextBuffer
& obj
)
6880 wxRichTextParagraphLayoutBox::Copy(obj
);
6882 m_styleSheet
= obj
.m_styleSheet
;
6883 m_modified
= obj
.m_modified
;
6884 m_batchedCommandDepth
= 0;
6885 if (m_batchedCommand
)
6886 delete m_batchedCommand
;
6887 m_batchedCommand
= NULL
;
6888 m_suppressUndo
= obj
.m_suppressUndo
;
6889 m_invalidRange
= obj
.m_invalidRange
;
6892 /// Push style sheet to top of stack
6893 bool wxRichTextBuffer::PushStyleSheet(wxRichTextStyleSheet
* styleSheet
)
6896 styleSheet
->InsertSheet(m_styleSheet
);
6898 SetStyleSheet(styleSheet
);
6903 /// Pop style sheet from top of stack
6904 wxRichTextStyleSheet
* wxRichTextBuffer::PopStyleSheet()
6908 wxRichTextStyleSheet
* oldSheet
= m_styleSheet
;
6909 m_styleSheet
= oldSheet
->GetNextSheet();
6918 /// Submit command to insert paragraphs
6919 bool wxRichTextBuffer::InsertParagraphsWithUndo(long pos
, const wxRichTextParagraphLayoutBox
& paragraphs
, wxRichTextCtrl
* ctrl
, int flags
)
6921 return ctrl
->GetFocusObject()->InsertParagraphsWithUndo(this, pos
, paragraphs
, ctrl
, flags
);
6924 /// Submit command to insert paragraphs
6925 bool wxRichTextParagraphLayoutBox::InsertParagraphsWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxRichTextParagraphLayoutBox
& paragraphs
, wxRichTextCtrl
* ctrl
, int WXUNUSED(flags
))
6927 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6929 action
->GetNewParagraphs() = paragraphs
;
6931 action
->SetPosition(pos
);
6933 wxRichTextRange range
= wxRichTextRange(pos
, pos
+ paragraphs
.GetOwnRange().GetEnd() - 1);
6934 if (!paragraphs
.GetPartialParagraph())
6935 range
.SetEnd(range
.GetEnd()+1);
6937 // Set the range we'll need to delete in Undo
6938 action
->SetRange(range
);
6940 buffer
->SubmitAction(action
);
6945 /// Submit command to insert the given text
6946 bool wxRichTextBuffer::InsertTextWithUndo(long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
, int flags
)
6948 return ctrl
->GetFocusObject()->InsertTextWithUndo(this, pos
, text
, ctrl
, flags
);
6951 /// Submit command to insert the given text
6952 bool wxRichTextParagraphLayoutBox::InsertTextWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
, int flags
)
6954 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6956 wxRichTextAttr
* p
= NULL
;
6957 wxRichTextAttr paraAttr
;
6958 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
6960 // Get appropriate paragraph style
6961 paraAttr
= GetStyleForNewParagraph(buffer
, pos
, false, false);
6962 if (!paraAttr
.IsDefault())
6966 action
->GetNewParagraphs().AddParagraphs(text
, p
);
6968 int length
= action
->GetNewParagraphs().GetOwnRange().GetLength();
6970 if (!text
.empty() && text
.Last() != wxT('\n'))
6972 // Don't count the newline when undoing
6974 action
->GetNewParagraphs().SetPartialParagraph(true);
6976 else if (!text
.empty() && text
.Last() == wxT('\n'))
6979 action
->SetPosition(pos
);
6981 // Set the range we'll need to delete in Undo
6982 action
->SetRange(wxRichTextRange(pos
, pos
+ length
- 1));
6984 buffer
->SubmitAction(action
);
6989 /// Submit command to insert the given text
6990 bool wxRichTextBuffer::InsertNewlineWithUndo(long pos
, wxRichTextCtrl
* ctrl
, int flags
)
6992 return ctrl
->GetFocusObject()->InsertNewlineWithUndo(this, pos
, ctrl
, flags
);
6995 /// Submit command to insert the given text
6996 bool wxRichTextParagraphLayoutBox::InsertNewlineWithUndo(wxRichTextBuffer
* buffer
, long pos
, wxRichTextCtrl
* ctrl
, int flags
)
6998 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7000 wxRichTextAttr
* p
= NULL
;
7001 wxRichTextAttr paraAttr
;
7002 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7004 paraAttr
= GetStyleForNewParagraph(buffer
, pos
, false, true /* look for next paragraph style */);
7005 if (!paraAttr
.IsDefault())
7009 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7011 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(wxEmptyString
, this, & attr
);
7012 action
->GetNewParagraphs().AppendChild(newPara
);
7013 action
->GetNewParagraphs().UpdateRanges();
7014 action
->GetNewParagraphs().SetPartialParagraph(false);
7015 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
, false);
7019 newPara
->SetAttributes(*p
);
7021 if (flags
& wxRICHTEXT_INSERT_INTERACTIVE
)
7023 if (para
&& para
->GetRange().GetEnd() == pos
)
7026 // Now see if we need to number the paragraph.
7027 if (newPara
->GetAttributes().HasBulletNumber())
7029 wxRichTextAttr numberingAttr
;
7030 if (FindNextParagraphNumber(para
, numberingAttr
))
7031 wxRichTextApplyStyle(newPara
->GetAttributes(), (const wxRichTextAttr
&) numberingAttr
);
7035 action
->SetPosition(pos
);
7037 // Use the default character style
7038 // Use the default character style
7039 if (!buffer
->GetDefaultStyle().IsDefault() && newPara
->GetChildren().GetFirst())
7041 // Check whether the default style merely reflects the paragraph/basic style,
7042 // in which case don't apply it.
7043 wxRichTextAttr
defaultStyle(buffer
->GetDefaultStyle());
7044 wxRichTextAttr toApply
;
7047 wxRichTextAttr combinedAttr
= para
->GetCombinedAttributes(true /* include box attributes */);
7048 wxRichTextAttr newAttr
;
7049 // This filters out attributes that are accounted for by the current
7050 // paragraph/basic style
7051 wxRichTextApplyStyle(toApply
, defaultStyle
, & combinedAttr
);
7054 toApply
= defaultStyle
;
7056 if (!toApply
.IsDefault())
7057 newPara
->GetChildren().GetFirst()->GetData()->SetAttributes(toApply
);
7060 // Set the range we'll need to delete in Undo
7061 action
->SetRange(wxRichTextRange(pos1
, pos1
));
7063 buffer
->SubmitAction(action
);
7068 /// Submit command to insert the given image
7069 bool wxRichTextBuffer::InsertImageWithUndo(long pos
, const wxRichTextImageBlock
& imageBlock
, wxRichTextCtrl
* ctrl
, int flags
,
7070 const wxRichTextAttr
& textAttr
)
7072 return ctrl
->GetFocusObject()->InsertImageWithUndo(this, pos
, imageBlock
, ctrl
, flags
, textAttr
);
7075 /// Submit command to insert the given image
7076 bool wxRichTextParagraphLayoutBox::InsertImageWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxRichTextImageBlock
& imageBlock
,
7077 wxRichTextCtrl
* ctrl
, int flags
,
7078 const wxRichTextAttr
& textAttr
)
7080 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7082 wxRichTextAttr
* p
= NULL
;
7083 wxRichTextAttr paraAttr
;
7084 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7086 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
7087 if (!paraAttr
.IsDefault())
7091 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7093 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7095 newPara
->SetAttributes(*p
);
7097 wxRichTextImage
* imageObject
= new wxRichTextImage(imageBlock
, newPara
);
7098 newPara
->AppendChild(imageObject
);
7099 imageObject
->SetAttributes(textAttr
);
7100 action
->GetNewParagraphs().AppendChild(newPara
);
7101 action
->GetNewParagraphs().UpdateRanges();
7103 action
->GetNewParagraphs().SetPartialParagraph(true);
7105 action
->SetPosition(pos
);
7107 // Set the range we'll need to delete in Undo
7108 action
->SetRange(wxRichTextRange(pos
, pos
));
7110 buffer
->SubmitAction(action
);
7115 // Insert an object with no change of it
7116 wxRichTextObject
* wxRichTextBuffer::InsertObjectWithUndo(long pos
, wxRichTextObject
*object
, wxRichTextCtrl
* ctrl
, int flags
)
7118 return ctrl
->GetFocusObject()->InsertObjectWithUndo(this, pos
, object
, ctrl
, flags
);
7121 // Insert an object with no change of it
7122 wxRichTextObject
* wxRichTextParagraphLayoutBox::InsertObjectWithUndo(wxRichTextBuffer
* buffer
, long pos
, wxRichTextObject
*object
, wxRichTextCtrl
* ctrl
, int flags
)
7124 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Object"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7126 wxRichTextAttr
* p
= NULL
;
7127 wxRichTextAttr paraAttr
;
7128 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7130 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
7131 if (!paraAttr
.IsDefault())
7135 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7137 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7139 newPara
->SetAttributes(*p
);
7141 newPara
->AppendChild(object
);
7142 action
->GetNewParagraphs().AppendChild(newPara
);
7143 action
->GetNewParagraphs().UpdateRanges();
7145 action
->GetNewParagraphs().SetPartialParagraph(true);
7147 action
->SetPosition(pos
);
7149 // Set the range we'll need to delete in Undo
7150 action
->SetRange(wxRichTextRange(pos
, pos
));
7152 buffer
->SubmitAction(action
);
7154 wxRichTextObject
* obj
= GetLeafObjectAtPosition(pos
);
7158 wxRichTextField
* wxRichTextParagraphLayoutBox::InsertFieldWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxString
& fieldType
,
7159 const wxRichTextProperties
& properties
,
7160 wxRichTextCtrl
* ctrl
, int flags
,
7161 const wxRichTextAttr
& textAttr
)
7163 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Field"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7165 wxRichTextAttr
* p
= NULL
;
7166 wxRichTextAttr paraAttr
;
7167 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7169 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
7170 if (!paraAttr
.IsDefault())
7174 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7176 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7178 newPara
->SetAttributes(*p
);
7180 wxRichTextField
* fieldObject
= new wxRichTextField();
7181 fieldObject
->wxRichTextObject::SetProperties(properties
);
7182 fieldObject
->SetFieldType(fieldType
);
7183 fieldObject
->SetAttributes(textAttr
);
7184 newPara
->AppendChild(fieldObject
);
7185 action
->GetNewParagraphs().AppendChild(newPara
);
7186 action
->GetNewParagraphs().UpdateRanges();
7187 action
->GetNewParagraphs().SetPartialParagraph(true);
7188 action
->SetPosition(pos
);
7190 // Set the range we'll need to delete in Undo
7191 action
->SetRange(wxRichTextRange(pos
, pos
));
7193 buffer
->SubmitAction(action
);
7195 wxRichTextField
* obj
= wxDynamicCast(GetLeafObjectAtPosition(pos
), wxRichTextField
);
7199 /// Get the style that is appropriate for a new paragraph at this position.
7200 /// If the previous paragraph has a paragraph style name, look up the next-paragraph
7202 wxRichTextAttr
wxRichTextParagraphLayoutBox::GetStyleForNewParagraph(wxRichTextBuffer
* buffer
, long pos
, bool caretPosition
, bool lookUpNewParaStyle
) const
7204 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
, caretPosition
);
7207 wxRichTextAttr attr
;
7208 bool foundAttributes
= false;
7210 // Look for a matching paragraph style
7211 if (lookUpNewParaStyle
&& !para
->GetAttributes().GetParagraphStyleName().IsEmpty() && buffer
->GetStyleSheet())
7213 wxRichTextParagraphStyleDefinition
* paraDef
= buffer
->GetStyleSheet()->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
7216 // If we're not at the end of the paragraph, then we apply THIS style, and not the designated next style.
7217 if (para
->GetRange().GetEnd() == pos
&& !paraDef
->GetNextStyle().IsEmpty())
7219 wxRichTextParagraphStyleDefinition
* nextParaDef
= buffer
->GetStyleSheet()->FindParagraphStyle(paraDef
->GetNextStyle());
7222 foundAttributes
= true;
7223 attr
= nextParaDef
->GetStyleMergedWithBase(buffer
->GetStyleSheet());
7227 // If we didn't find the 'next style', use this style instead.
7228 if (!foundAttributes
)
7230 foundAttributes
= true;
7231 attr
= paraDef
->GetStyleMergedWithBase(buffer
->GetStyleSheet());
7236 // Also apply list style if present
7237 if (lookUpNewParaStyle
&& !para
->GetAttributes().GetListStyleName().IsEmpty() && buffer
->GetStyleSheet())
7239 wxRichTextListStyleDefinition
* listDef
= buffer
->GetStyleSheet()->FindListStyle(para
->GetAttributes().GetListStyleName());
7242 int thisIndent
= para
->GetAttributes().GetLeftIndent();
7243 int thisLevel
= para
->GetAttributes().HasOutlineLevel() ? para
->GetAttributes().GetOutlineLevel() : listDef
->FindLevelForIndent(thisIndent
);
7245 // Apply the overall list style, and item style for this level
7246 wxRichTextAttr
listStyle(listDef
->GetCombinedStyleForLevel(thisLevel
, buffer
->GetStyleSheet()));
7247 wxRichTextApplyStyle(attr
, listStyle
);
7248 attr
.SetOutlineLevel(thisLevel
);
7249 if (para
->GetAttributes().HasBulletNumber())
7250 attr
.SetBulletNumber(para
->GetAttributes().GetBulletNumber());
7254 if (!foundAttributes
)
7256 attr
= para
->GetAttributes();
7257 int flags
= attr
.GetFlags();
7259 // Eliminate character styles
7260 flags
&= ( (~ wxTEXT_ATTR_FONT
) |
7261 (~ wxTEXT_ATTR_TEXT_COLOUR
) |
7262 (~ wxTEXT_ATTR_BACKGROUND_COLOUR
) );
7263 attr
.SetFlags(flags
);
7269 return wxRichTextAttr();
7272 /// Submit command to delete this range
7273 bool wxRichTextBuffer::DeleteRangeWithUndo(const wxRichTextRange
& range
, wxRichTextCtrl
* ctrl
)
7275 return ctrl
->GetFocusObject()->DeleteRangeWithUndo(range
, ctrl
, this);
7278 /// Submit command to delete this range
7279 bool wxRichTextParagraphLayoutBox::DeleteRangeWithUndo(const wxRichTextRange
& range
, wxRichTextCtrl
* ctrl
, wxRichTextBuffer
* buffer
)
7281 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Delete"), wxRICHTEXT_DELETE
, buffer
, this, ctrl
);
7283 action
->SetPosition(ctrl
->GetCaretPosition());
7285 // Set the range to delete
7286 action
->SetRange(range
);
7288 // Copy the fragment that we'll need to restore in Undo
7289 CopyFragment(range
, action
->GetOldParagraphs());
7291 // See if we're deleting a paragraph marker, in which case we need to
7292 // make a note not to copy the attributes from the 2nd paragraph to the 1st.
7293 if (range
.GetStart() == range
.GetEnd())
7295 wxRichTextParagraph
* para
= GetParagraphAtPosition(range
.GetStart());
7296 if (para
&& para
->GetRange().GetEnd() == range
.GetEnd())
7298 wxRichTextParagraph
* nextPara
= GetParagraphAtPosition(range
.GetStart()+1);
7299 if (nextPara
&& nextPara
!= para
)
7301 action
->GetOldParagraphs().GetChildren().GetFirst()->GetData()->SetAttributes(nextPara
->GetAttributes());
7302 action
->GetOldParagraphs().GetAttributes().SetFlags(action
->GetOldParagraphs().GetAttributes().GetFlags() | wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
);
7307 buffer
->SubmitAction(action
);
7312 /// Collapse undo/redo commands
7313 bool wxRichTextBuffer::BeginBatchUndo(const wxString
& cmdName
)
7315 if (m_batchedCommandDepth
== 0)
7317 wxASSERT(m_batchedCommand
== NULL
);
7318 if (m_batchedCommand
)
7320 GetCommandProcessor()->Store(m_batchedCommand
);
7322 m_batchedCommand
= new wxRichTextCommand(cmdName
);
7325 m_batchedCommandDepth
++;
7330 /// Collapse undo/redo commands
7331 bool wxRichTextBuffer::EndBatchUndo()
7333 m_batchedCommandDepth
--;
7335 wxASSERT(m_batchedCommandDepth
>= 0);
7336 wxASSERT(m_batchedCommand
!= NULL
);
7338 if (m_batchedCommandDepth
== 0)
7340 GetCommandProcessor()->Store(m_batchedCommand
);
7341 m_batchedCommand
= NULL
;
7347 /// Submit immediately, or delay according to whether collapsing is on
7348 bool wxRichTextBuffer::SubmitAction(wxRichTextAction
* action
)
7350 if (action
&& !action
->GetNewParagraphs().IsEmpty())
7351 PrepareContent(action
->GetNewParagraphs());
7353 if (BatchingUndo() && m_batchedCommand
&& !SuppressingUndo())
7355 wxRichTextCommand
* cmd
= new wxRichTextCommand(action
->GetName());
7356 cmd
->AddAction(action
);
7358 cmd
->GetActions().Clear();
7361 m_batchedCommand
->AddAction(action
);
7365 wxRichTextCommand
* cmd
= new wxRichTextCommand(action
->GetName());
7366 cmd
->AddAction(action
);
7368 // Only store it if we're not suppressing undo.
7369 return GetCommandProcessor()->Submit(cmd
, !SuppressingUndo());
7375 /// Begin suppressing undo/redo commands.
7376 bool wxRichTextBuffer::BeginSuppressUndo()
7383 /// End suppressing undo/redo commands.
7384 bool wxRichTextBuffer::EndSuppressUndo()
7391 /// Begin using a style
7392 bool wxRichTextBuffer::BeginStyle(const wxRichTextAttr
& style
)
7394 wxRichTextAttr
newStyle(GetDefaultStyle());
7396 // Save the old default style
7397 m_attributeStack
.Append((wxObject
*) new wxRichTextAttr(GetDefaultStyle()));
7399 wxRichTextApplyStyle(newStyle
, style
);
7400 newStyle
.SetFlags(style
.GetFlags()|newStyle
.GetFlags());
7402 SetDefaultStyle(newStyle
);
7408 bool wxRichTextBuffer::EndStyle()
7410 if (!m_attributeStack
.GetFirst())
7412 wxLogDebug(_("Too many EndStyle calls!"));
7416 wxList::compatibility_iterator node
= m_attributeStack
.GetLast();
7417 wxRichTextAttr
* attr
= (wxRichTextAttr
*)node
->GetData();
7418 m_attributeStack
.Erase(node
);
7420 SetDefaultStyle(*attr
);
7427 bool wxRichTextBuffer::EndAllStyles()
7429 while (m_attributeStack
.GetCount() != 0)
7434 /// Clear the style stack
7435 void wxRichTextBuffer::ClearStyleStack()
7437 for (wxList::compatibility_iterator node
= m_attributeStack
.GetFirst(); node
; node
= node
->GetNext())
7438 delete (wxRichTextAttr
*) node
->GetData();
7439 m_attributeStack
.Clear();
7442 /// Begin using bold
7443 bool wxRichTextBuffer::BeginBold()
7445 wxRichTextAttr attr
;
7446 attr
.SetFontWeight(wxFONTWEIGHT_BOLD
);
7448 return BeginStyle(attr
);
7451 /// Begin using italic
7452 bool wxRichTextBuffer::BeginItalic()
7454 wxRichTextAttr attr
;
7455 attr
.SetFontStyle(wxFONTSTYLE_ITALIC
);
7457 return BeginStyle(attr
);
7460 /// Begin using underline
7461 bool wxRichTextBuffer::BeginUnderline()
7463 wxRichTextAttr attr
;
7464 attr
.SetFontUnderlined(true);
7466 return BeginStyle(attr
);
7469 /// Begin using point size
7470 bool wxRichTextBuffer::BeginFontSize(int pointSize
)
7472 wxRichTextAttr attr
;
7473 attr
.SetFontSize(pointSize
);
7475 return BeginStyle(attr
);
7478 /// Begin using this font
7479 bool wxRichTextBuffer::BeginFont(const wxFont
& font
)
7481 wxRichTextAttr attr
;
7484 return BeginStyle(attr
);
7487 /// Begin using this colour
7488 bool wxRichTextBuffer::BeginTextColour(const wxColour
& colour
)
7490 wxRichTextAttr attr
;
7491 attr
.SetFlags(wxTEXT_ATTR_TEXT_COLOUR
);
7492 attr
.SetTextColour(colour
);
7494 return BeginStyle(attr
);
7497 /// Begin using alignment
7498 bool wxRichTextBuffer::BeginAlignment(wxTextAttrAlignment alignment
)
7500 wxRichTextAttr attr
;
7501 attr
.SetFlags(wxTEXT_ATTR_ALIGNMENT
);
7502 attr
.SetAlignment(alignment
);
7504 return BeginStyle(attr
);
7507 /// Begin left indent
7508 bool wxRichTextBuffer::BeginLeftIndent(int leftIndent
, int leftSubIndent
)
7510 wxRichTextAttr attr
;
7511 attr
.SetFlags(wxTEXT_ATTR_LEFT_INDENT
);
7512 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7514 return BeginStyle(attr
);
7517 /// Begin right indent
7518 bool wxRichTextBuffer::BeginRightIndent(int rightIndent
)
7520 wxRichTextAttr attr
;
7521 attr
.SetFlags(wxTEXT_ATTR_RIGHT_INDENT
);
7522 attr
.SetRightIndent(rightIndent
);
7524 return BeginStyle(attr
);
7527 /// Begin paragraph spacing
7528 bool wxRichTextBuffer::BeginParagraphSpacing(int before
, int after
)
7532 flags
|= wxTEXT_ATTR_PARA_SPACING_BEFORE
;
7534 flags
|= wxTEXT_ATTR_PARA_SPACING_AFTER
;
7536 wxRichTextAttr attr
;
7537 attr
.SetFlags(flags
);
7538 attr
.SetParagraphSpacingBefore(before
);
7539 attr
.SetParagraphSpacingAfter(after
);
7541 return BeginStyle(attr
);
7544 /// Begin line spacing
7545 bool wxRichTextBuffer::BeginLineSpacing(int lineSpacing
)
7547 wxRichTextAttr attr
;
7548 attr
.SetFlags(wxTEXT_ATTR_LINE_SPACING
);
7549 attr
.SetLineSpacing(lineSpacing
);
7551 return BeginStyle(attr
);
7554 /// Begin numbered bullet
7555 bool wxRichTextBuffer::BeginNumberedBullet(int bulletNumber
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7557 wxRichTextAttr attr
;
7558 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7559 attr
.SetBulletStyle(bulletStyle
);
7560 attr
.SetBulletNumber(bulletNumber
);
7561 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7563 return BeginStyle(attr
);
7566 /// Begin symbol bullet
7567 bool wxRichTextBuffer::BeginSymbolBullet(const wxString
& symbol
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7569 wxRichTextAttr attr
;
7570 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7571 attr
.SetBulletStyle(bulletStyle
);
7572 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7573 attr
.SetBulletText(symbol
);
7575 return BeginStyle(attr
);
7578 /// Begin standard bullet
7579 bool wxRichTextBuffer::BeginStandardBullet(const wxString
& bulletName
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7581 wxRichTextAttr attr
;
7582 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7583 attr
.SetBulletStyle(bulletStyle
);
7584 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7585 attr
.SetBulletName(bulletName
);
7587 return BeginStyle(attr
);
7590 /// Begin named character style
7591 bool wxRichTextBuffer::BeginCharacterStyle(const wxString
& characterStyle
)
7593 if (GetStyleSheet())
7595 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->FindCharacterStyle(characterStyle
);
7598 wxRichTextAttr attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7599 return BeginStyle(attr
);
7605 /// Begin named paragraph style
7606 bool wxRichTextBuffer::BeginParagraphStyle(const wxString
& paragraphStyle
)
7608 if (GetStyleSheet())
7610 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(paragraphStyle
);
7613 wxRichTextAttr attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7614 return BeginStyle(attr
);
7620 /// Begin named list style
7621 bool wxRichTextBuffer::BeginListStyle(const wxString
& listStyle
, int level
, int number
)
7623 if (GetStyleSheet())
7625 wxRichTextListStyleDefinition
* def
= GetStyleSheet()->FindListStyle(listStyle
);
7628 wxRichTextAttr
attr(def
->GetCombinedStyleForLevel(level
));
7630 attr
.SetBulletNumber(number
);
7632 return BeginStyle(attr
);
7639 bool wxRichTextBuffer::BeginURL(const wxString
& url
, const wxString
& characterStyle
)
7641 wxRichTextAttr attr
;
7643 if (!characterStyle
.IsEmpty() && GetStyleSheet())
7645 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->FindCharacterStyle(characterStyle
);
7648 attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7653 return BeginStyle(attr
);
7656 /// Adds a handler to the end
7657 void wxRichTextBuffer::AddHandler(wxRichTextFileHandler
*handler
)
7659 sm_handlers
.Append(handler
);
7662 /// Inserts a handler at the front
7663 void wxRichTextBuffer::InsertHandler(wxRichTextFileHandler
*handler
)
7665 sm_handlers
.Insert( handler
);
7668 /// Removes a handler
7669 bool wxRichTextBuffer::RemoveHandler(const wxString
& name
)
7671 wxRichTextFileHandler
*handler
= FindHandler(name
);
7674 sm_handlers
.DeleteObject(handler
);
7682 /// Finds a handler by filename or, if supplied, type
7683 wxRichTextFileHandler
*wxRichTextBuffer::FindHandlerFilenameOrType(const wxString
& filename
,
7684 wxRichTextFileType imageType
)
7686 if (imageType
!= wxRICHTEXT_TYPE_ANY
)
7687 return FindHandler(imageType
);
7688 else if (!filename
.IsEmpty())
7690 wxString path
, file
, ext
;
7691 wxFileName::SplitPath(filename
, & path
, & file
, & ext
);
7692 return FindHandler(ext
, imageType
);
7699 /// Finds a handler by name
7700 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(const wxString
& name
)
7702 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7705 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7706 if (handler
->GetName().Lower() == name
.Lower()) return handler
;
7708 node
= node
->GetNext();
7713 /// Finds a handler by extension and type
7714 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(const wxString
& extension
, wxRichTextFileType type
)
7716 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7719 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7720 if ( handler
->GetExtension().Lower() == extension
.Lower() &&
7721 (type
== wxRICHTEXT_TYPE_ANY
|| handler
->GetType() == type
) )
7723 node
= node
->GetNext();
7728 /// Finds a handler by type
7729 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(wxRichTextFileType type
)
7731 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7734 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7735 if (handler
->GetType() == type
) return handler
;
7736 node
= node
->GetNext();
7741 void wxRichTextBuffer::InitStandardHandlers()
7743 if (!FindHandler(wxRICHTEXT_TYPE_TEXT
))
7744 AddHandler(new wxRichTextPlainTextHandler
);
7747 void wxRichTextBuffer::CleanUpHandlers()
7749 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7752 wxRichTextFileHandler
* handler
= (wxRichTextFileHandler
*)node
->GetData();
7753 wxList::compatibility_iterator next
= node
->GetNext();
7758 sm_handlers
.Clear();
7761 wxString
wxRichTextBuffer::GetExtWildcard(bool combine
, bool save
, wxArrayInt
* types
)
7768 wxList::compatibility_iterator node
= GetHandlers().GetFirst();
7772 wxRichTextFileHandler
* handler
= (wxRichTextFileHandler
*) node
->GetData();
7773 if (handler
->IsVisible() && ((save
&& handler
->CanSave()) || (!save
&& handler
->CanLoad())))
7778 wildcard
+= wxT(";");
7779 wildcard
+= wxT("*.") + handler
->GetExtension();
7784 wildcard
+= wxT("|");
7785 wildcard
+= handler
->GetName();
7786 wildcard
+= wxT(" ");
7787 wildcard
+= _("files");
7788 wildcard
+= wxT(" (*.");
7789 wildcard
+= handler
->GetExtension();
7790 wildcard
+= wxT(")|*.");
7791 wildcard
+= handler
->GetExtension();
7793 types
->Add(handler
->GetType());
7798 node
= node
->GetNext();
7802 wildcard
= wxT("(") + wildcard
+ wxT(")|") + wildcard
;
7807 bool wxRichTextBuffer::LoadFile(const wxString
& filename
, wxRichTextFileType type
)
7809 wxRichTextFileHandler
* handler
= FindHandlerFilenameOrType(filename
, type
);
7812 SetDefaultStyle(wxRichTextAttr());
7813 handler
->SetFlags(GetHandlerFlags());
7814 bool success
= handler
->LoadFile(this, filename
);
7815 Invalidate(wxRICHTEXT_ALL
);
7823 bool wxRichTextBuffer::SaveFile(const wxString
& filename
, wxRichTextFileType type
)
7825 wxRichTextFileHandler
* handler
= FindHandlerFilenameOrType(filename
, type
);
7828 handler
->SetFlags(GetHandlerFlags());
7829 return handler
->SaveFile(this, filename
);
7835 /// Load from a stream
7836 bool wxRichTextBuffer::LoadFile(wxInputStream
& stream
, wxRichTextFileType type
)
7838 wxRichTextFileHandler
* handler
= FindHandler(type
);
7841 SetDefaultStyle(wxRichTextAttr());
7842 handler
->SetFlags(GetHandlerFlags());
7843 bool success
= handler
->LoadFile(this, stream
);
7844 Invalidate(wxRICHTEXT_ALL
);
7851 /// Save to a stream
7852 bool wxRichTextBuffer::SaveFile(wxOutputStream
& stream
, wxRichTextFileType type
)
7854 wxRichTextFileHandler
* handler
= FindHandler(type
);
7857 handler
->SetFlags(GetHandlerFlags());
7858 return handler
->SaveFile(this, stream
);
7864 /// Copy the range to the clipboard
7865 bool wxRichTextBuffer::CopyToClipboard(const wxRichTextRange
& range
)
7867 bool success
= false;
7868 wxRichTextParagraphLayoutBox
* container
= this;
7869 if (GetRichTextCtrl())
7870 container
= GetRichTextCtrl()->GetFocusObject();
7872 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
7874 if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open())
7876 wxTheClipboard
->Clear();
7878 // Add composite object
7880 wxDataObjectComposite
* compositeObject
= new wxDataObjectComposite();
7883 wxString text
= container
->GetTextForRange(range
);
7886 text
= wxTextFile::Translate(text
, wxTextFileType_Dos
);
7889 compositeObject
->Add(new wxTextDataObject(text
), false /* not preferred */);
7892 // Add rich text buffer data object. This needs the XML handler to be present.
7894 if (FindHandler(wxRICHTEXT_TYPE_XML
))
7896 wxRichTextBuffer
* richTextBuf
= new wxRichTextBuffer
;
7897 container
->CopyFragment(range
, *richTextBuf
);
7899 compositeObject
->Add(new wxRichTextBufferDataObject(richTextBuf
), true /* preferred */);
7902 if (wxTheClipboard
->SetData(compositeObject
))
7905 wxTheClipboard
->Close();
7914 /// Paste the clipboard content to the buffer
7915 bool wxRichTextBuffer::PasteFromClipboard(long position
)
7917 bool success
= false;
7918 wxRichTextParagraphLayoutBox
* container
= this;
7919 if (GetRichTextCtrl())
7920 container
= GetRichTextCtrl()->GetFocusObject();
7922 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
7923 if (CanPasteFromClipboard())
7925 if (wxTheClipboard
->Open())
7927 if (wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())))
7929 wxRichTextBufferDataObject data
;
7930 wxTheClipboard
->GetData(data
);
7931 wxRichTextBuffer
* richTextBuffer
= data
.GetRichTextBuffer();
7934 container
->InsertParagraphsWithUndo(this, position
+1, *richTextBuffer
, GetRichTextCtrl(), 0);
7935 if (GetRichTextCtrl())
7936 GetRichTextCtrl()->ShowPosition(position
+ richTextBuffer
->GetOwnRange().GetEnd());
7937 delete richTextBuffer
;
7940 else if (wxTheClipboard
->IsSupported(wxDF_TEXT
)
7942 || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
)
7946 wxTextDataObject data
;
7947 wxTheClipboard
->GetData(data
);
7948 wxString
text(data
.GetText());
7951 text2
.Alloc(text
.Length()+1);
7953 for (i
= 0; i
< text
.Length(); i
++)
7955 wxChar ch
= text
[i
];
7956 if (ch
!= wxT('\r'))
7960 wxString text2
= text
;
7962 container
->InsertTextWithUndo(this, position
+1, text2
, GetRichTextCtrl(), wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
);
7964 if (GetRichTextCtrl())
7965 GetRichTextCtrl()->ShowPosition(position
+ text2
.Length());
7969 else if (wxTheClipboard
->IsSupported(wxDF_BITMAP
))
7971 wxBitmapDataObject data
;
7972 wxTheClipboard
->GetData(data
);
7973 wxBitmap
bitmap(data
.GetBitmap());
7974 wxImage
image(bitmap
.ConvertToImage());
7976 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, this, container
, GetRichTextCtrl(), false);
7978 action
->GetNewParagraphs().AddImage(image
);
7980 if (action
->GetNewParagraphs().GetChildCount() == 1)
7981 action
->GetNewParagraphs().SetPartialParagraph(true);
7983 action
->SetPosition(position
+1);
7985 // Set the range we'll need to delete in Undo
7986 action
->SetRange(wxRichTextRange(position
+1, position
+1));
7988 SubmitAction(action
);
7992 wxTheClipboard
->Close();
7996 wxUnusedVar(position
);
8001 /// Can we paste from the clipboard?
8002 bool wxRichTextBuffer::CanPasteFromClipboard() const
8004 bool canPaste
= false;
8005 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
8006 if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open())
8008 if (wxTheClipboard
->IsSupported(wxDF_TEXT
)
8010 || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
)
8012 || wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())) ||
8013 wxTheClipboard
->IsSupported(wxDF_BITMAP
))
8017 wxTheClipboard
->Close();
8023 /// Dumps contents of buffer for debugging purposes
8024 void wxRichTextBuffer::Dump()
8028 wxStringOutputStream
stream(& text
);
8029 wxTextOutputStream
textStream(stream
);
8036 /// Add an event handler
8037 bool wxRichTextBuffer::AddEventHandler(wxEvtHandler
* handler
)
8039 m_eventHandlers
.Append(handler
);
8043 /// Remove an event handler
8044 bool wxRichTextBuffer::RemoveEventHandler(wxEvtHandler
* handler
, bool deleteHandler
)
8046 wxList::compatibility_iterator node
= m_eventHandlers
.Find(handler
);
8049 m_eventHandlers
.Erase(node
);
8059 /// Clear event handlers
8060 void wxRichTextBuffer::ClearEventHandlers()
8062 m_eventHandlers
.Clear();
8065 /// Send event to event handlers. If sendToAll is true, will send to all event handlers,
8066 /// otherwise will stop at the first successful one.
8067 bool wxRichTextBuffer::SendEvent(wxEvent
& event
, bool sendToAll
)
8069 bool success
= false;
8070 for (wxList::compatibility_iterator node
= m_eventHandlers
.GetFirst(); node
; node
= node
->GetNext())
8072 wxEvtHandler
* handler
= (wxEvtHandler
*) node
->GetData();
8073 if (handler
->ProcessEvent(event
))
8083 /// Set style sheet and notify of the change
8084 bool wxRichTextBuffer::SetStyleSheetAndNotify(wxRichTextStyleSheet
* sheet
)
8086 wxRichTextStyleSheet
* oldSheet
= GetStyleSheet();
8088 wxWindowID winid
= wxID_ANY
;
8089 if (GetRichTextCtrl())
8090 winid
= GetRichTextCtrl()->GetId();
8092 wxRichTextEvent
event(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACING
, winid
);
8093 event
.SetEventObject(GetRichTextCtrl());
8094 event
.SetContainer(GetRichTextCtrl()->GetFocusObject());
8095 event
.SetOldStyleSheet(oldSheet
);
8096 event
.SetNewStyleSheet(sheet
);
8099 if (SendEvent(event
) && !event
.IsAllowed())
8101 if (sheet
!= oldSheet
)
8107 if (oldSheet
&& oldSheet
!= sheet
)
8110 SetStyleSheet(sheet
);
8112 event
.SetEventType(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACED
);
8113 event
.SetOldStyleSheet(NULL
);
8116 return SendEvent(event
);
8119 /// Set renderer, deleting old one
8120 void wxRichTextBuffer::SetRenderer(wxRichTextRenderer
* renderer
)
8124 sm_renderer
= renderer
;
8127 /// Hit-testing: returns a flag indicating hit test details, plus
8128 /// information about position
8129 int wxRichTextBuffer::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
8131 int ret
= wxRichTextParagraphLayoutBox::HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
8132 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
8138 textPosition
= m_ownRange
.GetEnd()-1;
8141 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
;
8145 bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxRichTextAttr
& bulletAttr
, const wxRect
& rect
)
8147 if (bulletAttr
.GetTextColour().IsOk())
8149 wxCheckSetPen(dc
, wxPen(bulletAttr
.GetTextColour()));
8150 wxCheckSetBrush(dc
, wxBrush(bulletAttr
.GetTextColour()));
8154 wxCheckSetPen(dc
, *wxBLACK_PEN
);
8155 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
8159 if (bulletAttr
.HasFont())
8161 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(bulletAttr
);
8164 font
= (*wxNORMAL_FONT
);
8166 wxCheckSetFont(dc
, font
);
8168 int charHeight
= dc
.GetCharHeight();
8170 int bulletWidth
= (int) (((float) charHeight
) * wxRichTextBuffer::GetBulletProportion());
8171 int bulletHeight
= bulletWidth
;
8175 // Calculate the top position of the character (as opposed to the whole line height)
8176 int y
= rect
.y
+ (rect
.height
- charHeight
);
8178 // Calculate where the bullet should be positioned
8179 y
= y
+ (charHeight
+1)/2 - (bulletHeight
+1)/2;
8181 // The margin between a bullet and text.
8182 int margin
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer::GetBulletRightMargin());
8184 if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
)
8185 x
= rect
.x
+ rect
.width
- bulletWidth
- margin
;
8186 else if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
)
8187 x
= x
+ (rect
.width
)/2 - bulletWidth
/2;
8189 if (bulletAttr
.GetBulletName() == wxT("standard/square"))
8191 dc
.DrawRectangle(x
, y
, bulletWidth
, bulletHeight
);
8193 else if (bulletAttr
.GetBulletName() == wxT("standard/diamond"))
8196 pts
[0].x
= x
; pts
[0].y
= y
+ bulletHeight
/2;
8197 pts
[1].x
= x
+ bulletWidth
/2; pts
[1].y
= y
;
8198 pts
[2].x
= x
+ bulletWidth
; pts
[2].y
= y
+ bulletHeight
/2;
8199 pts
[3].x
= x
+ bulletWidth
/2; pts
[3].y
= y
+ bulletHeight
;
8201 dc
.DrawPolygon(4, pts
);
8203 else if (bulletAttr
.GetBulletName() == wxT("standard/triangle"))
8206 pts
[0].x
= x
; pts
[0].y
= y
;
8207 pts
[1].x
= x
+ bulletWidth
; pts
[1].y
= y
+ bulletHeight
/2;
8208 pts
[2].x
= x
; pts
[2].y
= y
+ bulletHeight
;
8210 dc
.DrawPolygon(3, pts
);
8212 else if (bulletAttr
.GetBulletName() == wxT("standard/circle-outline"))
8214 wxCheckSetBrush(dc
, *wxTRANSPARENT_BRUSH
);
8215 dc
.DrawEllipse(x
, y
, bulletWidth
, bulletHeight
);
8217 else // "standard/circle", and catch-all
8219 dc
.DrawEllipse(x
, y
, bulletWidth
, bulletHeight
);
8225 bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxRichTextAttr
& attr
, const wxRect
& rect
, const wxString
& text
)
8230 if ((attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
) && !attr
.GetBulletFont().IsEmpty() && attr
.HasFont())
8232 wxRichTextAttr fontAttr
;
8233 fontAttr
.SetFontSize(attr
.GetFontSize());
8234 fontAttr
.SetFontStyle(attr
.GetFontStyle());
8235 fontAttr
.SetFontWeight(attr
.GetFontWeight());
8236 fontAttr
.SetFontUnderlined(attr
.GetFontUnderlined());
8237 fontAttr
.SetFontFaceName(attr
.GetBulletFont());
8238 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(fontAttr
);
8240 else if (attr
.HasFont())
8241 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(attr
);
8243 font
= (*wxNORMAL_FONT
);
8245 wxCheckSetFont(dc
, font
);
8247 if (attr
.GetTextColour().IsOk())
8248 dc
.SetTextForeground(attr
.GetTextColour());
8250 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
8252 int charHeight
= dc
.GetCharHeight();
8254 dc
.GetTextExtent(text
, & tw
, & th
);
8258 // Calculate the top position of the character (as opposed to the whole line height)
8259 int y
= rect
.y
+ (rect
.height
- charHeight
);
8261 // The margin between a bullet and text.
8262 int margin
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer::GetBulletRightMargin());
8264 if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
)
8265 x
= (rect
.x
+ rect
.width
) - tw
- margin
;
8266 else if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
)
8267 x
= x
+ (rect
.width
)/2 - tw
/2;
8269 dc
.DrawText(text
, x
, y
);
8277 bool wxRichTextStdRenderer::DrawBitmapBullet(wxRichTextParagraph
* WXUNUSED(paragraph
), wxDC
& WXUNUSED(dc
), const wxRichTextAttr
& WXUNUSED(attr
), const wxRect
& WXUNUSED(rect
))
8279 // Currently unimplemented. The intention is to store bitmaps by name in a media store associated
8280 // with the buffer. The store will allow retrieval from memory, disk or other means.
8284 /// Enumerate the standard bullet names currently supported
8285 bool wxRichTextStdRenderer::EnumerateStandardBulletNames(wxArrayString
& bulletNames
)
8287 bulletNames
.Add(wxTRANSLATE("standard/circle"));
8288 bulletNames
.Add(wxTRANSLATE("standard/circle-outline"));
8289 bulletNames
.Add(wxTRANSLATE("standard/square"));
8290 bulletNames
.Add(wxTRANSLATE("standard/diamond"));
8291 bulletNames
.Add(wxTRANSLATE("standard/triangle"));
8300 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBox
, wxRichTextParagraphLayoutBox
)
8302 wxRichTextBox::wxRichTextBox(wxRichTextObject
* parent
):
8303 wxRichTextParagraphLayoutBox(parent
)
8308 bool wxRichTextBox::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8313 // TODO: if the active object in the control, draw an indication.
8314 // We need to add the concept of active object, and not just focus object,
8315 // so we can apply commands (properties, delete, ...) to objects such as text boxes and images.
8316 // Ultimately we would like to be able to interactively resize an active object
8317 // using drag handles.
8318 return wxRichTextParagraphLayoutBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8322 void wxRichTextBox::Copy(const wxRichTextBox
& obj
)
8324 wxRichTextParagraphLayoutBox::Copy(obj
);
8327 // Edit properties via a GUI
8328 bool wxRichTextBox::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8330 wxRichTextObjectPropertiesDialog
boxDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Box Properties"));
8331 boxDlg
.SetAttributes(GetAttributes());
8333 if (boxDlg
.ShowModal() == wxID_OK
)
8335 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
8336 // indeterminate in the object.
8337 boxDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
8348 IMPLEMENT_DYNAMIC_CLASS(wxRichTextField
, wxRichTextParagraphLayoutBox
)
8350 wxRichTextField::wxRichTextField(const wxString
& fieldType
, wxRichTextObject
* parent
):
8351 wxRichTextParagraphLayoutBox(parent
)
8353 SetFieldType(fieldType
);
8357 bool wxRichTextField::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8362 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8363 if (fieldType
&& fieldType
->Draw(this, dc
, context
, range
, selection
, rect
, descent
, style
))
8366 // Fallback; but don't draw guidelines.
8367 style
&= ~wxRICHTEXT_DRAW_GUIDELINES
;
8368 return wxRichTextParagraphLayoutBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8371 bool wxRichTextField::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
)
8373 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8374 if (fieldType
&& fieldType
->Layout(this, dc
, context
, rect
, parentRect
, style
))
8378 return wxRichTextParagraphLayoutBox::Layout(dc
, context
, rect
, parentRect
, style
);
8381 bool wxRichTextField::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
8383 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8385 return fieldType
->GetRangeSize((wxRichTextField
*) this, range
, size
, descent
, dc
, context
, flags
, position
, partialExtents
);
8387 return wxRichTextParagraphLayoutBox::GetRangeSize(range
, size
, descent
, dc
, context
, flags
, position
, partialExtents
);
8391 void wxRichTextField::CalculateRange(long start
, long& end
)
8394 wxRichTextParagraphLayoutBox::CalculateRange(start
, end
);
8396 wxRichTextObject::CalculateRange(start
, end
);
8400 void wxRichTextField::Copy(const wxRichTextField
& obj
)
8402 wxRichTextParagraphLayoutBox::Copy(obj
);
8407 // Edit properties via a GUI
8408 bool wxRichTextField::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8410 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8412 return fieldType
->EditProperties(this, parent
, buffer
);
8417 bool wxRichTextField::CanEditProperties() const
8419 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8421 return fieldType
->CanEditProperties((wxRichTextField
*) this);
8426 wxString
wxRichTextField::GetPropertiesMenuLabel() const
8428 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8430 return fieldType
->GetPropertiesMenuLabel((wxRichTextField
*) this);
8432 return wxEmptyString
;
8435 bool wxRichTextField::UpdateField()
8437 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8439 return fieldType
->UpdateField((wxRichTextField
*) this);
8444 bool wxRichTextField::IsTopLevel() const
8446 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8448 return fieldType
->IsTopLevel((wxRichTextField
*) this);
8453 IMPLEMENT_CLASS(wxRichTextFieldType
, wxObject
)
8455 IMPLEMENT_CLASS(wxRichTextFieldTypeStandard
, wxRichTextFieldType
)
8457 wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString
& name
, const wxString
& label
, int displayStyle
)
8463 SetDisplayStyle(displayStyle
);
8466 wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString
& name
, const wxBitmap
& bitmap
, int displayStyle
)
8472 SetDisplayStyle(displayStyle
);
8475 void wxRichTextFieldTypeStandard::Init()
8477 m_displayStyle
= wxRICHTEXT_FIELD_STYLE_RECTANGLE
;
8478 m_font
= wxFont(6, wxFONTFAMILY_SWISS
, wxFONTSTYLE_NORMAL
, wxFONTWEIGHT_NORMAL
);
8479 m_textColour
= *wxWHITE
;
8480 m_borderColour
= *wxBLACK
;
8481 m_backgroundColour
= *wxBLACK
;
8482 m_verticalPadding
= 1;
8483 m_horizontalPadding
= 3;
8484 m_horizontalMargin
= 2;
8485 m_verticalMargin
= 0;
8488 void wxRichTextFieldTypeStandard::Copy(const wxRichTextFieldTypeStandard
& field
)
8490 wxRichTextFieldType::Copy(field
);
8492 m_label
= field
.m_label
;
8493 m_displayStyle
= field
.m_displayStyle
;
8494 m_font
= field
.m_font
;
8495 m_textColour
= field
.m_textColour
;
8496 m_borderColour
= field
.m_borderColour
;
8497 m_backgroundColour
= field
.m_backgroundColour
;
8498 m_verticalPadding
= field
.m_verticalPadding
;
8499 m_horizontalPadding
= field
.m_horizontalPadding
;
8500 m_horizontalMargin
= field
.m_horizontalMargin
;
8501 m_bitmap
= field
.m_bitmap
;
8504 bool wxRichTextFieldTypeStandard::Draw(wxRichTextField
* obj
, wxDC
& dc
, wxRichTextDrawingContext
& WXUNUSED(context
), const wxRichTextRange
& WXUNUSED(range
), const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int WXUNUSED(style
))
8506 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_COMPOSITE
)
8507 return false; // USe default composite drawing
8508 else // if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_RECTANGLE || m_displayStyle == wxRICHTEXT_FIELD_STYLE_NOBORDER)
8512 wxPen
borderPen(m_borderColour
, 1, wxSOLID
);
8513 wxBrush
backgroundBrush(m_backgroundColour
);
8514 wxColour
textColour(m_textColour
);
8516 if (selection
.WithinSelection(obj
->GetRange().GetStart(), obj
))
8518 wxColour
highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
));
8519 wxColour
highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
));
8521 borderPen
= wxPen(highlightTextColour
, 1, wxSOLID
);
8522 backgroundBrush
= wxBrush(highlightColour
);
8524 wxCheckSetBrush(dc
, backgroundBrush
);
8525 wxCheckSetPen(dc
, wxPen(highlightColour
, 1, wxSOLID
));
8526 dc
.DrawRectangle(rect
);
8529 if (m_displayStyle
!= wxRICHTEXT_FIELD_STYLE_NO_BORDER
)
8532 // objectRect is the area where the content is drawn, after margins around it have been taken into account
8533 wxRect objectRect
= wxRect(wxPoint(rect
.x
+ m_horizontalMargin
, rect
.y
+ wxMax(0, rect
.height
- descent
- obj
->GetCachedSize().y
)),
8534 wxSize(obj
->GetCachedSize().x
- 2*m_horizontalMargin
- borderSize
, obj
->GetCachedSize().y
));
8536 // clientArea is where the text is actually written
8537 wxRect clientArea
= objectRect
;
8539 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_RECTANGLE
)
8541 dc
.SetPen(borderPen
);
8542 dc
.SetBrush(backgroundBrush
);
8543 dc
.DrawRoundedRectangle(objectRect
, 4.0);
8545 else if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_START_TAG
)
8547 int arrowLength
= objectRect
.height
/2;
8548 clientArea
.width
-= (arrowLength
- m_horizontalPadding
);
8551 pts
[0].x
= objectRect
.x
; pts
[0].y
= objectRect
.y
;
8552 pts
[1].x
= objectRect
.x
+ objectRect
.width
- arrowLength
; pts
[1].y
= objectRect
.y
;
8553 pts
[2].x
= objectRect
.x
+ objectRect
.width
; pts
[2].y
= objectRect
.y
+ (objectRect
.height
/2);
8554 pts
[3].x
= objectRect
.x
+ objectRect
.width
- arrowLength
; pts
[3].y
= objectRect
.y
+ objectRect
.height
;
8555 pts
[4].x
= objectRect
.x
; pts
[4].y
= objectRect
.y
+ objectRect
.height
;
8556 dc
.SetPen(borderPen
);
8557 dc
.SetBrush(backgroundBrush
);
8558 dc
.DrawPolygon(5, pts
);
8560 else if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_END_TAG
)
8562 int arrowLength
= objectRect
.height
/2;
8563 clientArea
.width
-= (arrowLength
- m_horizontalPadding
);
8564 clientArea
.x
+= (arrowLength
- m_horizontalPadding
);
8567 pts
[0].x
= objectRect
.x
+ objectRect
.width
; pts
[0].y
= objectRect
.y
;
8568 pts
[1].x
= objectRect
.x
+ arrowLength
; pts
[1].y
= objectRect
.y
;
8569 pts
[2].x
= objectRect
.x
; pts
[2].y
= objectRect
.y
+ (objectRect
.height
/2);
8570 pts
[3].x
= objectRect
.x
+ arrowLength
; pts
[3].y
= objectRect
.y
+ objectRect
.height
;
8571 pts
[4].x
= objectRect
.x
+ objectRect
.width
; pts
[4].y
= objectRect
.y
+ objectRect
.height
;
8572 dc
.SetPen(borderPen
);
8573 dc
.SetBrush(backgroundBrush
);
8574 dc
.DrawPolygon(5, pts
);
8577 if (m_bitmap
.IsOk())
8579 int x
= clientArea
.x
+ (clientArea
.width
- m_bitmap
.GetWidth())/2;
8580 int y
= clientArea
.y
+ m_verticalPadding
;
8581 dc
.DrawBitmap(m_bitmap
, x
, y
, true);
8583 if (selection
.WithinSelection(obj
->GetRange().GetStart(), obj
))
8585 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
8586 wxCheckSetPen(dc
, *wxBLACK_PEN
);
8587 dc
.SetLogicalFunction(wxINVERT
);
8588 dc
.DrawRectangle(wxRect(x
, y
, m_bitmap
.GetWidth(), m_bitmap
.GetHeight()));
8589 dc
.SetLogicalFunction(wxCOPY
);
8594 wxString
label(m_label
);
8595 if (label
.IsEmpty())
8597 int w
, h
, maxDescent
;
8599 dc
.GetTextExtent(m_label
, & w
, &h
, & maxDescent
);
8600 dc
.SetTextForeground(textColour
);
8602 int x
= clientArea
.x
+ (clientArea
.width
- w
)/2;
8603 int y
= clientArea
.y
+ (clientArea
.height
- (h
- maxDescent
))/2;
8604 dc
.DrawText(m_label
, x
, y
);
8611 bool wxRichTextFieldTypeStandard::Layout(wxRichTextField
* obj
, wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& WXUNUSED(rect
), const wxRect
& WXUNUSED(parentRect
), int style
)
8613 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_COMPOSITE
)
8614 return false; // USe default composite layout
8616 wxSize size
= GetSize(obj
, dc
, context
, style
);
8617 obj
->SetCachedSize(size
);
8618 obj
->SetMinSize(size
);
8619 obj
->SetMaxSize(size
);
8623 bool wxRichTextFieldTypeStandard::GetRangeSize(wxRichTextField
* obj
, const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
8625 if (IsTopLevel(obj
))
8626 return obj
->wxRichTextParagraphLayoutBox::GetRangeSize(range
, size
, descent
, dc
, context
, flags
, position
);
8629 wxSize sz
= GetSize(obj
, dc
, context
, 0);
8633 if (partialExtents
->GetCount() > 0)
8634 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
8637 partialExtents
->Add(lastSize
+ sz
.x
);
8644 wxSize
wxRichTextFieldTypeStandard::GetSize(wxRichTextField
* WXUNUSED(obj
), wxDC
& dc
, wxRichTextDrawingContext
& WXUNUSED(context
), int WXUNUSED(style
)) const
8647 int w
= 0, h
= 0, maxDescent
= 0;
8650 if (m_bitmap
.IsOk())
8652 w
= m_bitmap
.GetWidth();
8653 h
= m_bitmap
.GetHeight();
8655 sz
= wxSize(w
+ m_horizontalMargin
*2, h
+ m_verticalMargin
*2);
8659 wxString
label(m_label
);
8660 if (label
.IsEmpty())
8663 dc
.GetTextExtent(label
, & w
, &h
, & maxDescent
);
8665 sz
= wxSize(w
+ m_horizontalPadding
*2 + m_horizontalMargin
*2, h
+ m_verticalPadding
*2 + m_verticalMargin
*2);
8668 if (m_displayStyle
!= wxRICHTEXT_FIELD_STYLE_NO_BORDER
)
8670 sz
.x
+= borderSize
*2;
8671 sz
.y
+= borderSize
*2;
8674 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_START_TAG
|| m_displayStyle
== wxRICHTEXT_FIELD_STYLE_END_TAG
)
8676 // Add space for the arrow
8677 sz
.x
+= (sz
.y
/2 - m_horizontalPadding
);
8683 IMPLEMENT_DYNAMIC_CLASS(wxRichTextCell
, wxRichTextBox
)
8685 wxRichTextCell::wxRichTextCell(wxRichTextObject
* parent
):
8686 wxRichTextBox(parent
)
8691 bool wxRichTextCell::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8693 return wxRichTextBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8697 void wxRichTextCell::Copy(const wxRichTextCell
& obj
)
8699 wxRichTextBox::Copy(obj
);
8702 // Edit properties via a GUI
8703 bool wxRichTextCell::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8705 // We need to gather common attributes for all selected cells.
8707 wxRichTextTable
* table
= wxDynamicCast(GetParent(), wxRichTextTable
);
8708 bool multipleCells
= false;
8709 wxRichTextAttr attr
;
8711 if (table
&& buffer
&& buffer
->GetRichTextCtrl() && buffer
->GetRichTextCtrl()->GetSelection().IsValid() &&
8712 buffer
->GetRichTextCtrl()->GetSelection().GetContainer() == GetParent())
8714 wxRichTextAttr clashingAttr
, absentAttr
;
8715 const wxRichTextSelection
& sel
= buffer
->GetRichTextCtrl()->GetSelection();
8717 int selectedCellCount
= 0;
8718 for (i
= 0; i
< sel
.GetCount(); i
++)
8720 const wxRichTextRange
& range
= sel
[i
];
8721 wxRichTextCell
* cell
= table
->GetCell(range
.GetStart());
8724 wxRichTextAttr cellStyle
= cell
->GetAttributes();
8726 CollectStyle(attr
, cellStyle
, clashingAttr
, absentAttr
);
8728 selectedCellCount
++;
8731 multipleCells
= selectedCellCount
> 1;
8735 attr
= GetAttributes();
8740 caption
= _("Multiple Cell Properties");
8742 caption
= _("Cell Properties");
8744 wxRichTextObjectPropertiesDialog
cellDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, caption
);
8745 cellDlg
.SetAttributes(attr
);
8747 wxRichTextSizePage
* sizePage
= wxDynamicCast(cellDlg
.FindPage(CLASSINFO(wxRichTextSizePage
)), wxRichTextSizePage
);
8750 // We don't want position and floating controls for a cell.
8751 sizePage
->ShowPositionControls(false);
8752 sizePage
->ShowFloatingControls(false);
8755 if (cellDlg
.ShowModal() == wxID_OK
)
8759 const wxRichTextSelection
& sel
= buffer
->GetRichTextCtrl()->GetSelection();
8760 // Apply the style; we interpret indeterminate attributes as 'don't touch this attribute'
8761 // since it may represent clashing attributes across multiple objects.
8762 table
->SetCellStyle(sel
, attr
);
8765 // For a single object, indeterminate attributes set by the user should be reflected in the
8766 // actual object style, so pass the wxRICHTEXT_SETSTYLE_RESET flag to assign
8767 // the style directly instead of applying (which ignores indeterminate attributes,
8768 // leaving them as they were).
8769 cellDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
8776 WX_DEFINE_OBJARRAY(wxRichTextObjectPtrArrayArray
)
8778 IMPLEMENT_DYNAMIC_CLASS(wxRichTextTable
, wxRichTextBox
)
8780 wxRichTextTable::wxRichTextTable(wxRichTextObject
* parent
): wxRichTextBox(parent
)
8786 // Draws the object.
8787 bool wxRichTextTable::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8789 return wxRichTextBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8792 WX_DECLARE_OBJARRAY(wxRect
, wxRichTextRectArray
);
8793 WX_DEFINE_OBJARRAY(wxRichTextRectArray
);
8795 // Lays the object out. rect is the space available for layout. Often it will
8796 // be the specified overall space for this object, if trying to constrain
8797 // layout to a particular size, or it could be the total space available in the
8798 // parent. rect is the overall size, so we must subtract margins and padding.
8799 // to get the actual available space.
8800 bool wxRichTextTable::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& WXUNUSED(parentRect
), int style
)
8802 SetPosition(rect
.GetPosition());
8804 // TODO: the meaty bit. Calculate sizes of all cells and rows. Try to use
8805 // minimum size if within alloted size, then divide up remaining size
8806 // between rows/cols.
8809 wxRichTextBuffer
* buffer
= GetBuffer();
8810 if (buffer
) scale
= buffer
->GetScale();
8812 wxRect availableSpace
= GetAvailableContentArea(dc
, context
, rect
);
8813 wxTextAttrDimensionConverter
converter(dc
, scale
, availableSpace
.GetSize());
8815 wxRichTextAttr
attr(GetAttributes());
8816 context
.ApplyVirtualAttributes(attr
, this);
8818 // If we have no fixed table size, and assuming we're not pushed for
8819 // space, then we don't have to try to stretch the table to fit the contents.
8820 bool stretchToFitTableWidth
= false;
8822 int tableWidth
= rect
.width
;
8823 if (attr
.GetTextBoxAttr().GetWidth().IsValid())
8825 tableWidth
= converter
.GetPixels(attr
.GetTextBoxAttr().GetWidth());
8827 // Fixed table width, so we do want to stretch columns out if necessary.
8828 stretchToFitTableWidth
= true;
8830 // Shouldn't be able to exceed the size passed to this function
8831 tableWidth
= wxMin(rect
.width
, tableWidth
);
8834 // Get internal padding
8835 int paddingLeft
= 0, paddingTop
= 0;
8836 if (attr
.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
8837 paddingLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetLeft());
8838 if (attr
.GetTextBoxAttr().GetPadding().GetTop().IsValid())
8839 paddingTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetTop());
8841 // Assume that left and top padding are also used for inter-cell padding.
8842 int paddingX
= paddingLeft
;
8843 int paddingY
= paddingTop
;
8845 int totalLeftMargin
= 0, totalRightMargin
= 0, totalTopMargin
= 0, totalBottomMargin
= 0;
8846 GetTotalMargin(dc
, buffer
, attr
, totalLeftMargin
, totalRightMargin
, totalTopMargin
, totalBottomMargin
);
8848 // Internal table width - the area for content
8849 int internalTableWidth
= tableWidth
- totalLeftMargin
- totalRightMargin
;
8851 int rowCount
= m_cells
.GetCount();
8852 if (m_colCount
== 0 || rowCount
== 0)
8854 wxRect
overallRect(rect
.x
, rect
.y
, totalLeftMargin
+ totalRightMargin
, totalTopMargin
+ totalBottomMargin
);
8855 SetCachedSize(overallRect
.GetSize());
8857 // Zero content size
8858 SetMinSize(overallRect
.GetSize());
8859 SetMaxSize(GetMinSize());
8863 // The final calculated widths
8864 wxArrayInt colWidths
;
8865 colWidths
.Add(0, m_colCount
);
8867 wxArrayInt absoluteColWidths
;
8868 absoluteColWidths
.Add(0, m_colCount
);
8870 wxArrayInt percentageColWidths
;
8871 percentageColWidths
.Add(0, m_colCount
);
8872 // wxArrayInt percentageColWidthsSpanning(m_colCount);
8873 // These are only relevant when the first column contains spanning information.
8874 // wxArrayInt columnSpans(m_colCount); // Each contains 1 for non-spanning cell, > 1 for spanning cell.
8875 wxArrayInt maxColWidths
;
8876 maxColWidths
.Add(0, m_colCount
);
8877 wxArrayInt minColWidths
;
8878 minColWidths
.Add(0, m_colCount
);
8880 wxSize
tableSize(tableWidth
, 0);
8884 for (i
= 0; i
< m_colCount
; i
++)
8886 absoluteColWidths
[i
] = 0;
8887 // absoluteColWidthsSpanning[i] = 0;
8888 percentageColWidths
[i
] = -1;
8889 // percentageColWidthsSpanning[i] = -1;
8891 maxColWidths
[i
] = 0;
8892 minColWidths
[i
] = 0;
8893 // columnSpans[i] = 1;
8896 // (0) Determine which cells are visible according to spans
8898 // __________________
8903 // |------------------|
8904 // |__________________| 4
8906 // To calculate cell visibility:
8907 // First find all spanning cells. Build an array of span records with start x, y and end x, y.
8908 // Then for each cell, test whether we're within one of those cells, and unless we're at the start of
8909 // that cell, hide the cell.
8911 // We can also use this array to match the size of spanning cells to the grid. Or just do
8912 // this when we iterate through all cells.
8914 // 0.1: add spanning cells to an array
8915 wxRichTextRectArray rectArray
;
8916 for (j
= 0; j
< m_rowCount
; j
++)
8918 for (i
= 0; i
< m_colCount
; i
++)
8920 wxRichTextBox
* cell
= GetCell(j
, i
);
8921 int colSpan
= 1, rowSpan
= 1;
8922 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8923 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8924 if (cell
->GetProperties().HasProperty(wxT("rowspan")))
8925 rowSpan
= cell
->GetProperties().GetPropertyLong(wxT("rowspan"));
8926 if (colSpan
> 1 || rowSpan
> 1)
8928 rectArray
.Add(wxRect(i
, j
, colSpan
, rowSpan
));
8932 // 0.2: find which cells are subsumed by a spanning cell
8933 for (j
= 0; j
< m_rowCount
; j
++)
8935 for (i
= 0; i
< m_colCount
; i
++)
8937 wxRichTextBox
* cell
= GetCell(j
, i
);
8938 if (rectArray
.GetCount() == 0)
8944 int colSpan
= 1, rowSpan
= 1;
8945 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8946 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8947 if (cell
->GetProperties().HasProperty(wxT("rowspan")))
8948 rowSpan
= cell
->GetProperties().GetPropertyLong(wxT("rowspan"));
8949 if (colSpan
> 1 || rowSpan
> 1)
8951 // Assume all spanning cells are shown
8957 for (k
= 0; k
< (int) rectArray
.GetCount(); k
++)
8959 if (rectArray
[k
].Contains(wxPoint(i
, j
)))
8971 // TODO: find the first spanned cell in each row that spans the most columns and doesn't
8972 // overlap with a spanned cell starting at a previous column position.
8973 // This means we need to keep an array of rects so we can check. However
8974 // it does also mean that some spans simply may not be taken into account
8975 // where there are different spans happening on different rows. In these cases,
8976 // they will simply be as wide as their constituent columns.
8978 // (1) Do an initial layout for all cells to get minimum and maximum size, and get
8979 // the absolute or percentage width of each column.
8981 for (j
= 0; j
< m_rowCount
; j
++)
8983 // First get the overall margins so we can calculate percentage widths based on
8984 // the available content space for all cells on the row
8986 int overallRowContentMargin
= 0;
8987 int visibleCellCount
= 0;
8989 for (i
= 0; i
< m_colCount
; i
++)
8991 wxRichTextBox
* cell
= GetCell(j
, i
);
8992 if (cell
->IsShown())
8994 int cellTotalLeftMargin
= 0, cellTotalRightMargin
= 0, cellTotalTopMargin
= 0, cellTotalBottomMargin
= 0;
8995 GetTotalMargin(dc
, buffer
, cell
->GetAttributes(), cellTotalLeftMargin
, cellTotalRightMargin
, cellTotalTopMargin
, cellTotalBottomMargin
);
8997 overallRowContentMargin
+= (cellTotalLeftMargin
+ cellTotalRightMargin
);
8998 visibleCellCount
++;
9002 // Add in inter-cell padding
9003 overallRowContentMargin
+= ((visibleCellCount
-1) * paddingX
);
9005 int rowContentWidth
= internalTableWidth
- overallRowContentMargin
;
9006 wxSize
rowTableSize(rowContentWidth
, 0);
9007 wxTextAttrDimensionConverter
converter(dc
, scale
, rowTableSize
);
9009 for (i
= 0; i
< m_colCount
; i
++)
9011 wxRichTextBox
* cell
= GetCell(j
, i
);
9012 if (cell
->IsShown())
9015 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9016 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9018 // Lay out cell to find min/max widths
9019 cell
->Invalidate(wxRICHTEXT_ALL
);
9020 cell
->Layout(dc
, context
, availableSpace
, availableSpace
, style
);
9024 int absoluteCellWidth
= -1;
9025 int percentageCellWidth
= -1;
9027 // I think we need to calculate percentages from the internal table size,
9028 // minus the padding between cells which we'll need to calculate from the
9029 // (number of VISIBLE cells - 1)*paddingX. Then percentages that add up to 100%
9030 // will add up to 100%. In CSS, the width specifies the cell's content rect width,
9031 // so if we want to conform to that we'll need to add in the overall cell margins.
9032 // However, this will make it difficult to specify percentages that add up to
9033 // 100% and still fit within the table width.
9034 // Let's say two cells have 50% width. They have 10 pixels of overall margin each.
9035 // The table content rect is 500 pixels and the inter-cell padding is 20 pixels.
9036 // If we're using internal content size for the width, we would calculate the
9037 // the overall cell width for n cells as:
9038 // (500 - 20*(n-1) - overallCellMargin1 - overallCellMargin2 - ...) * percentage / 100
9039 // + thisOverallCellMargin
9040 // = 500 - 20 - 10 - 10) * 0.5 + 10 = 240 pixels overall cell width.
9041 // Adding this back, we get 240 + 240 + 20 = 500 pixels.
9043 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9045 int w
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetWidth());
9046 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
9048 percentageCellWidth
= w
;
9052 absoluteCellWidth
= w
;
9054 // Override absolute width with minimum width if necessary
9055 if (cell
->GetMinSize().x
> 0 && absoluteCellWidth
!=1 && cell
->GetMinSize().x
> absoluteCellWidth
)
9056 absoluteCellWidth
= cell
->GetMinSize().x
;
9059 if (absoluteCellWidth
!= -1)
9061 if (absoluteCellWidth
> absoluteColWidths
[i
])
9062 absoluteColWidths
[i
] = absoluteCellWidth
;
9065 if (percentageCellWidth
!= -1)
9067 if (percentageCellWidth
> percentageColWidths
[i
])
9068 percentageColWidths
[i
] = percentageCellWidth
;
9071 if (colSpan
== 1 && cell
->GetMinSize().x
&& cell
->GetMinSize().x
> minColWidths
[i
])
9072 minColWidths
[i
] = cell
->GetMinSize().x
;
9073 if (colSpan
== 1 && cell
->GetMaxSize().x
&& cell
->GetMaxSize().x
> maxColWidths
[i
])
9074 maxColWidths
[i
] = cell
->GetMaxSize().x
;
9080 // (2) Allocate initial column widths from minimum widths, absolute values and proportions
9081 // TODO: simply merge this into (1).
9082 for (i
= 0; i
< m_colCount
; i
++)
9084 if (absoluteColWidths
[i
] > 0)
9086 colWidths
[i
] = absoluteColWidths
[i
];
9088 else if (percentageColWidths
[i
] > 0)
9090 colWidths
[i
] = percentageColWidths
[i
];
9092 // This is rubbish - we calculated the absolute widths from percentages, so
9093 // we can't do it again here.
9094 //colWidths[i] = (int) (double(percentageColWidths[i]) * double(tableWidth) / 100.0 + 0.5);
9098 // (3) Process absolute or proportional widths of spanning columns,
9099 // now that we know what our fixed column widths are going to be.
9100 // Spanned cells will try to adjust columns so the span will fit.
9101 // Even existing fixed column widths can be expanded if necessary.
9102 // Actually, currently fixed columns widths aren't adjusted; instead,
9103 // the algorithm favours earlier rows and adjusts unspecified column widths
9104 // the first time only. After that, we can't know whether the column has been
9105 // specified explicitly or not. (We could make a note if necessary.)
9106 for (j
= 0; j
< m_rowCount
; j
++)
9108 // First get the overall margins so we can calculate percentage widths based on
9109 // the available content space for all cells on the row
9111 int overallRowContentMargin
= 0;
9112 int visibleCellCount
= 0;
9114 for (i
= 0; i
< m_colCount
; i
++)
9116 wxRichTextBox
* cell
= GetCell(j
, i
);
9117 if (cell
->IsShown())
9119 int cellTotalLeftMargin
= 0, cellTotalRightMargin
= 0, cellTotalTopMargin
= 0, cellTotalBottomMargin
= 0;
9120 GetTotalMargin(dc
, buffer
, cell
->GetAttributes(), cellTotalLeftMargin
, cellTotalRightMargin
, cellTotalTopMargin
, cellTotalBottomMargin
);
9122 overallRowContentMargin
+= (cellTotalLeftMargin
+ cellTotalRightMargin
);
9123 visibleCellCount
++;
9127 // Add in inter-cell padding
9128 overallRowContentMargin
+= ((visibleCellCount
-1) * paddingX
);
9130 int rowContentWidth
= internalTableWidth
- overallRowContentMargin
;
9131 wxSize
rowTableSize(rowContentWidth
, 0);
9132 wxTextAttrDimensionConverter
converter(dc
, scale
, rowTableSize
);
9134 for (i
= 0; i
< m_colCount
; i
++)
9136 wxRichTextBox
* cell
= GetCell(j
, i
);
9137 if (cell
->IsShown())
9140 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9141 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9145 int spans
= wxMin(colSpan
, m_colCount
- i
);
9149 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9151 cellWidth
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetWidth());
9152 // Override absolute width with minimum width if necessary
9153 if (cell
->GetMinSize().x
> 0 && cellWidth
!=1 && cell
->GetMinSize().x
> cellWidth
)
9154 cellWidth
= cell
->GetMinSize().x
;
9158 // Do we want to do this? It's the only chance we get to
9159 // use the cell's min/max sizes, so we need to work out
9160 // how we're going to balance the unspecified spanning cell
9161 // width with the possibility more-constrained constituent cell widths.
9162 // Say there's a tiny bitmap giving it a max width of 10 pixels. We
9163 // don't want to constraint all the spanned columns to fit into this cell.
9164 // OK, let's say that if any of the constituent columns don't fit,
9165 // then we simply stop constraining the columns; instead, we'll just fit the spanning
9166 // cells to the columns later.
9167 cellWidth
= cell
->GetMinSize().x
;
9168 if (cell
->GetMaxSize().x
> cellWidth
)
9169 cellWidth
= cell
->GetMaxSize().x
;
9172 // Subtract the padding between cells
9173 int spanningWidth
= cellWidth
;
9174 spanningWidth
-= paddingX
* (spans
-1);
9176 if (spanningWidth
> 0)
9178 // Now share the spanning width between columns within that span
9179 // TODO: take into account min widths of columns within the span
9180 int spanningWidthLeft
= spanningWidth
;
9181 int stretchColCount
= 0;
9182 for (k
= i
; k
< (i
+spans
); k
++)
9184 if (colWidths
[k
] > 0) // absolute or proportional width has been specified
9185 spanningWidthLeft
-= colWidths
[k
];
9189 // Now divide what's left between the remaining columns
9191 if (stretchColCount
> 0)
9192 colShare
= spanningWidthLeft
/ stretchColCount
;
9193 int colShareRemainder
= spanningWidthLeft
- (colShare
* stretchColCount
);
9195 // If fixed-width columns are currently too big, then we'll later
9196 // stretch the spanned cell to fit.
9198 if (spanningWidthLeft
> 0)
9200 for (k
= i
; k
< (i
+spans
); k
++)
9202 if (colWidths
[k
] <= 0) // absolute or proportional width has not been specified
9204 int newWidth
= colShare
;
9205 if (k
== (i
+spans
-1))
9206 newWidth
+= colShareRemainder
; // ensure all pixels are filled
9207 colWidths
[k
] = newWidth
;
9218 // (4) Next, share any remaining space out between columns that have not yet been calculated.
9219 // TODO: take into account min widths of columns within the span
9220 int tableWidthMinusPadding
= internalTableWidth
- (m_colCount
-1)*paddingX
;
9221 int widthLeft
= tableWidthMinusPadding
;
9222 int stretchColCount
= 0;
9223 for (i
= 0; i
< m_colCount
; i
++)
9225 // TODO: we need to take into account min widths.
9226 // Subtract min width from width left, then
9227 // add the colShare to the min width
9228 if (colWidths
[i
] > 0) // absolute or proportional width has been specified
9229 widthLeft
-= colWidths
[i
];
9232 if (minColWidths
[i
] > 0)
9233 widthLeft
-= minColWidths
[i
];
9239 // Now divide what's left between the remaining columns
9241 if (stretchColCount
> 0)
9242 colShare
= widthLeft
/ stretchColCount
;
9243 int colShareRemainder
= widthLeft
- (colShare
* stretchColCount
);
9245 // Check we don't have enough space, in which case shrink all columns, overriding
9246 // any absolute/proportional widths
9247 // TODO: actually we would like to divide up the shrinkage according to size.
9248 // How do we calculate the proportions that will achieve this?
9249 // Could first choose an arbitrary value for stretching cells, and then calculate
9250 // factors to multiply each width by.
9251 // TODO: want to record this fact and pass to an iteration that tries e.g. min widths
9252 if (widthLeft
< 0 || (stretchToFitTableWidth
&& (stretchColCount
== 0)))
9254 colShare
= tableWidthMinusPadding
/ m_colCount
;
9255 colShareRemainder
= tableWidthMinusPadding
- (colShare
* m_colCount
);
9256 for (i
= 0; i
< m_colCount
; i
++)
9259 minColWidths
[i
] = 0;
9263 // We have to adjust the columns if either we need to shrink the
9264 // table to fit the parent/table width, or we explicitly set the
9265 // table width and need to stretch out the table.
9266 if (widthLeft
< 0 || stretchToFitTableWidth
)
9268 for (i
= 0; i
< m_colCount
; i
++)
9270 if (colWidths
[i
] <= 0) // absolute or proportional width has not been specified
9272 if (minColWidths
[i
] > 0)
9273 colWidths
[i
] = minColWidths
[i
] + colShare
;
9275 colWidths
[i
] = colShare
;
9276 if (i
== (m_colCount
-1))
9277 colWidths
[i
] += colShareRemainder
; // ensure all pixels are filled
9282 // TODO: if spanned cells have no specified or max width, make them the
9283 // as big as the columns they span. Do this for all spanned cells in all
9284 // rows, of course. Size any spanned cells left over at the end - even if they
9285 // have width > 0, make sure they're limited to the appropriate column edge.
9289 Sort out confusion between content width
9290 and overall width later. For now, assume we specify overall width.
9292 So, now we've laid out the table to fit into the given space
9293 and have used specified widths and minimum widths.
9295 Now we need to consider how we will try to take maximum width into account.
9299 // (??) TODO: take max width into account
9301 // (6) Lay out all cells again with the current values
9304 int y
= availableSpace
.y
;
9305 for (j
= 0; j
< m_rowCount
; j
++)
9307 int x
= availableSpace
.x
; // TODO: take into account centering etc.
9308 int maxCellHeight
= 0;
9309 int maxSpecifiedCellHeight
= 0;
9311 wxArrayInt actualWidths
;
9312 actualWidths
.Add(0, m_colCount
);
9314 wxTextAttrDimensionConverter
converter(dc
, scale
);
9315 for (i
= 0; i
< m_colCount
; i
++)
9317 wxRichTextCell
* cell
= GetCell(j
, i
);
9318 if (cell
->IsShown())
9320 // Get max specified cell height
9321 // Don't handle percentages for height
9322 if (cell
->GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && cell
->GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() != wxTEXT_ATTR_UNITS_PERCENTAGE
)
9324 int h
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetHeight());
9325 if (h
> maxSpecifiedCellHeight
)
9326 maxSpecifiedCellHeight
= h
;
9329 if (colWidths
[i
] > 0) // absolute or proportional width has been specified
9332 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9333 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9335 wxRect availableCellSpace
;
9337 // TODO: take into acount spans
9340 // Calculate the size of this spanning cell from its constituent columns
9342 int spans
= wxMin(colSpan
, m_colCount
- i
);
9343 for (k
= i
; k
< spans
; k
++)
9349 availableCellSpace
= wxRect(x
, y
, xx
, -1);
9352 availableCellSpace
= wxRect(x
, y
, colWidths
[i
], -1);
9354 // Store actual width so we can force cell to be the appropriate width on the final loop
9355 actualWidths
[i
] = availableCellSpace
.GetWidth();
9358 cell
->Invalidate(wxRICHTEXT_ALL
);
9359 cell
->Layout(dc
, context
, availableCellSpace
, availableSpace
, style
);
9361 // TODO: use GetCachedSize().x to compute 'natural' size
9363 x
+= (availableCellSpace
.GetWidth() + paddingX
);
9364 if (cell
->GetCachedSize().y
> maxCellHeight
)
9365 maxCellHeight
= cell
->GetCachedSize().y
;
9370 maxCellHeight
= wxMax(maxCellHeight
, maxSpecifiedCellHeight
);
9372 for (i
= 0; i
< m_colCount
; i
++)
9374 wxRichTextCell
* cell
= GetCell(j
, i
);
9375 if (cell
->IsShown())
9377 wxRect availableCellSpace
= wxRect(cell
->GetPosition(), wxSize(actualWidths
[i
], maxCellHeight
));
9378 // Lay out cell with new height
9379 cell
->Invalidate(wxRICHTEXT_ALL
);
9380 cell
->Layout(dc
, context
, availableCellSpace
, availableSpace
, style
);
9382 // Make sure the cell size really is the appropriate size,
9383 // not the calculated box size
9384 cell
->SetCachedSize(wxSize(actualWidths
[i
], maxCellHeight
));
9386 maxRight
= wxMax(maxRight
, cell
->GetPosition().x
+ cell
->GetCachedSize().x
);
9391 if (j
< (m_rowCount
-1))
9395 // We need to add back the margins etc.
9397 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
9398 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxRight
- availableSpace
.x
, y
- availableSpace
.y
));
9399 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
9400 SetCachedSize(marginRect
.GetSize());
9403 // TODO: calculate max size
9405 SetMaxSize(GetCachedSize());
9408 // TODO: calculate min size
9410 SetMinSize(GetCachedSize());
9413 // TODO: currently we use either a fixed table width or the parent's size.
9414 // We also want to be able to calculate the table width from its content,
9415 // whether using fixed column widths or cell content min/max width.
9416 // Probably need a boolean flag to say whether we need to stretch cells
9417 // to fit the table width, or to simply use min/max cell widths. The
9418 // trouble with this is that if cell widths are not specified, they
9419 // will be tiny; we could use arbitrary defaults but this seems unsatisfactory.
9420 // Anyway, ignoring that problem, we probably need to factor layout into a function
9421 // that can can calculate the maximum unconstrained layout in case table size is
9422 // not specified. Then LayoutToBestSize() can choose to use either parent size to
9423 // constrain Layout(), or the previously-calculated max size to constraint layout.
9428 // Finds the absolute position and row height for the given character position
9429 bool wxRichTextTable::FindPosition(wxDC
& dc
, wxRichTextDrawingContext
& context
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
9431 wxRichTextCell
* child
= GetCell(index
+1);
9434 // Find the position at the start of the child cell, since the table doesn't
9435 // have any caret position of its own.
9436 return child
->FindPosition(dc
, context
, -1, pt
, height
, forceLineStart
);
9442 // Get the cell at the given character position (in the range of the table).
9443 wxRichTextCell
* wxRichTextTable::GetCell(long pos
) const
9445 int row
= 0, col
= 0;
9446 if (GetCellRowColumnPosition(pos
, row
, col
))
9448 return GetCell(row
, col
);
9454 // Get the row/column for a given character position
9455 bool wxRichTextTable::GetCellRowColumnPosition(long pos
, int& row
, int& col
) const
9457 if (m_colCount
== 0 || m_rowCount
== 0)
9460 row
= (int) (pos
/ m_colCount
);
9461 col
= pos
- (row
* m_colCount
);
9463 wxASSERT(row
< m_rowCount
&& col
< m_colCount
);
9465 if (row
< m_rowCount
&& col
< m_colCount
)
9471 // Calculate range, taking row/cell ordering into account instead of relying
9472 // on list ordering.
9473 void wxRichTextTable::CalculateRange(long start
, long& end
)
9475 long current
= start
;
9476 long lastEnd
= current
;
9485 for (i
= 0; i
< m_rowCount
; i
++)
9487 for (j
= 0; j
< m_colCount
; j
++)
9489 wxRichTextCell
* child
= GetCell(i
, j
);
9494 child
->CalculateRange(current
, childEnd
);
9497 current
= childEnd
+ 1;
9502 // A top-level object always has a range of size 1,
9503 // because its children don't count at this level.
9505 m_range
.SetRange(start
, start
);
9507 // An object with no children has zero length
9508 if (m_children
.GetCount() == 0)
9510 m_ownRange
.SetRange(0, lastEnd
);
9513 // Gets the range size.
9514 bool wxRichTextTable::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
9516 return wxRichTextBox::GetRangeSize(range
, size
, descent
, dc
, context
, flags
, position
, partialExtents
);
9519 // Deletes content in the given range.
9520 bool wxRichTextTable::DeleteRange(const wxRichTextRange
& WXUNUSED(range
))
9522 // TODO: implement deletion of cells
9526 // Gets any text in this object for the given range.
9527 wxString
wxRichTextTable::GetTextForRange(const wxRichTextRange
& range
) const
9529 return wxRichTextBox::GetTextForRange(range
);
9532 // Copies this object.
9533 void wxRichTextTable::Copy(const wxRichTextTable
& obj
)
9535 wxRichTextBox::Copy(obj
);
9539 m_rowCount
= obj
.m_rowCount
;
9540 m_colCount
= obj
.m_colCount
;
9542 m_cells
.Add(wxRichTextObjectPtrArray(), m_rowCount
);
9545 for (i
= 0; i
< m_rowCount
; i
++)
9547 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
9548 for (j
= 0; j
< m_colCount
; j
++)
9550 wxRichTextCell
* cell
= wxDynamicCast(obj
.GetCell(i
, j
)->Clone(), wxRichTextCell
);
9558 void wxRichTextTable::ClearTable()
9564 bool wxRichTextTable::CreateTable(int rows
, int cols
)
9571 m_cells
.Add(wxRichTextObjectPtrArray(), rows
);
9574 for (i
= 0; i
< rows
; i
++)
9576 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
9577 for (j
= 0; j
< cols
; j
++)
9579 wxRichTextCell
* cell
= new wxRichTextCell
;
9581 cell
->AddParagraph(wxEmptyString
);
9590 wxRichTextCell
* wxRichTextTable::GetCell(int row
, int col
) const
9592 wxASSERT(row
< m_rowCount
);
9593 wxASSERT(col
< m_colCount
);
9595 if (row
< m_rowCount
&& col
< m_colCount
)
9597 wxRichTextObjectPtrArray
& colArray
= m_cells
[row
];
9598 wxRichTextObject
* obj
= colArray
[col
];
9599 return wxDynamicCast(obj
, wxRichTextCell
);
9605 // Returns a selection object specifying the selections between start and end character positions.
9606 // For example, a table would deduce what cells (of range length 1) are selected when dragging across the table.
9607 wxRichTextSelection
wxRichTextTable::GetSelection(long start
, long end
) const
9609 wxRichTextSelection selection
;
9610 selection
.SetContainer((wxRichTextTable
*) this);
9619 wxASSERT( start
>= 0 && end
< (m_colCount
* m_rowCount
));
9621 if (end
>= (m_colCount
* m_rowCount
))
9624 // We need to find the rectangle of cells that is described by the rectangle
9625 // with start, end as the diagonal. Make sure we don't add cells that are
9626 // not currenty visible because they are overlapped by spanning cells.
9628 --------------------------
9629 | 0 | 1 | 2 | 3 | 4 |
9630 --------------------------
9631 | 5 | 6 | 7 | 8 | 9 |
9632 --------------------------
9633 | 10 | 11 | 12 | 13 | 14 |
9634 --------------------------
9635 | 15 | 16 | 17 | 18 | 19 |
9636 --------------------------
9638 Let's say we select 6 -> 18.
9640 Left and right edge cols of rectangle are 1 and 3 inclusive. Find least/greatest to find
9641 which is left and which is right.
9643 Top and bottom edge rows are 1 and 3 inclusive. Again, find least/greatest to find top and bottom.
9645 Now go through rows from 1 to 3 and only add cells that are (a) within above column range
9651 int leftCol
= start
- m_colCount
* int(start
/m_colCount
);
9652 int rightCol
= end
- m_colCount
* int(end
/m_colCount
);
9654 int topRow
= int(start
/m_colCount
);
9655 int bottomRow
= int(end
/m_colCount
);
9657 if (leftCol
> rightCol
)
9664 if (topRow
> bottomRow
)
9666 int tmp
= bottomRow
;
9672 for (i
= topRow
; i
<= bottomRow
; i
++)
9674 for (j
= leftCol
; j
<= rightCol
; j
++)
9676 wxRichTextCell
* cell
= GetCell(i
, j
);
9677 if (cell
&& cell
->IsShown())
9678 selection
.Add(cell
->GetRange());
9685 // Sets the attributes for the cells specified by the selection.
9686 bool wxRichTextTable::SetCellStyle(const wxRichTextSelection
& selection
, const wxRichTextAttr
& style
, int flags
)
9688 if (selection
.GetContainer() != this)
9691 wxRichTextBuffer
* buffer
= GetBuffer();
9692 bool haveControl
= (buffer
&& buffer
->GetRichTextCtrl() != NULL
);
9693 bool withUndo
= haveControl
&& ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
9696 buffer
->BeginBatchUndo(_("Set Cell Style"));
9698 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
9701 wxRichTextCell
* cell
= wxDynamicCast(node
->GetData(), wxRichTextCell
);
9702 if (cell
&& selection
.WithinSelection(cell
->GetRange().GetStart()))
9703 SetStyle(cell
, style
, flags
);
9704 node
= node
->GetNext();
9707 // Do action, or delay it until end of batch.
9709 buffer
->EndBatchUndo();
9714 bool wxRichTextTable::DeleteRows(int startRow
, int noRows
)
9716 wxASSERT((startRow
+ noRows
) < m_rowCount
);
9717 if ((startRow
+ noRows
) >= m_rowCount
)
9721 for (i
= startRow
; i
< (startRow
+noRows
); i
++)
9723 wxRichTextObjectPtrArray
& colArray
= m_cells
[startRow
];
9724 for (j
= 0; j
< (int) colArray
.GetCount(); j
++)
9726 wxRichTextObject
* cell
= colArray
[j
];
9727 RemoveChild(cell
, true);
9730 // Keep deleting at the same position, since we move all
9732 m_cells
.RemoveAt(startRow
);
9735 m_rowCount
= m_rowCount
- noRows
;
9740 bool wxRichTextTable::DeleteColumns(int startCol
, int noCols
)
9742 wxASSERT((startCol
+ noCols
) < m_colCount
);
9743 if ((startCol
+ noCols
) >= m_colCount
)
9746 bool deleteRows
= (noCols
== m_colCount
);
9749 for (i
= 0; i
< m_rowCount
; i
++)
9751 wxRichTextObjectPtrArray
& colArray
= m_cells
[deleteRows
? 0 : i
];
9752 for (j
= startCol
; j
< (startCol
+noCols
); j
++)
9754 wxRichTextObject
* cell
= colArray
[j
];
9755 RemoveChild(cell
, true);
9759 m_cells
.RemoveAt(0);
9764 m_colCount
= m_colCount
- noCols
;
9769 bool wxRichTextTable::AddRows(int startRow
, int noRows
, const wxRichTextAttr
& attr
)
9771 wxASSERT(startRow
<= m_rowCount
);
9772 if (startRow
> m_rowCount
)
9776 for (i
= 0; i
< noRows
; i
++)
9779 if (startRow
== m_rowCount
)
9781 m_cells
.Add(wxRichTextObjectPtrArray());
9782 idx
= m_cells
.GetCount() - 1;
9786 m_cells
.Insert(wxRichTextObjectPtrArray(), startRow
+i
);
9790 wxRichTextObjectPtrArray
& colArray
= m_cells
[idx
];
9791 for (j
= 0; j
< m_colCount
; j
++)
9793 wxRichTextCell
* cell
= new wxRichTextCell
;
9794 cell
->GetAttributes() = attr
;
9801 m_rowCount
= m_rowCount
+ noRows
;
9805 bool wxRichTextTable::AddColumns(int startCol
, int noCols
, const wxRichTextAttr
& attr
)
9807 wxASSERT(startCol
<= m_colCount
);
9808 if (startCol
> m_colCount
)
9812 for (i
= 0; i
< m_rowCount
; i
++)
9814 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
9815 for (j
= 0; j
< noCols
; j
++)
9817 wxRichTextCell
* cell
= new wxRichTextCell
;
9818 cell
->GetAttributes() = attr
;
9822 if (startCol
== m_colCount
)
9825 colArray
.Insert(cell
, startCol
+j
);
9829 m_colCount
= m_colCount
+ noCols
;
9834 // Edit properties via a GUI
9835 bool wxRichTextTable::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
9837 wxRichTextObjectPropertiesDialog
boxDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Table Properties"));
9838 boxDlg
.SetAttributes(GetAttributes());
9840 if (boxDlg
.ShowModal() == wxID_OK
)
9842 boxDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
9850 * Module to initialise and clean up handlers
9853 class wxRichTextModule
: public wxModule
9855 DECLARE_DYNAMIC_CLASS(wxRichTextModule
)
9857 wxRichTextModule() {}
9860 wxRichTextBuffer::SetRenderer(new wxRichTextStdRenderer
);
9861 wxRichTextBuffer::InitStandardHandlers();
9862 wxRichTextParagraph::InitDefaultTabs();
9864 wxRichTextXMLHandler::RegisterNodeName(wxT("text"), wxT("wxRichTextPlainText"));
9865 wxRichTextXMLHandler::RegisterNodeName(wxT("symbol"), wxT("wxRichTextPlainText"));
9866 wxRichTextXMLHandler::RegisterNodeName(wxT("image"), wxT("wxRichTextImage"));
9867 wxRichTextXMLHandler::RegisterNodeName(wxT("paragraph"), wxT("wxRichTextParagraph"));
9868 wxRichTextXMLHandler::RegisterNodeName(wxT("paragraphlayout"), wxT("wxRichTextParagraphLayoutBox"));
9869 wxRichTextXMLHandler::RegisterNodeName(wxT("textbox"), wxT("wxRichTextBox"));
9870 wxRichTextXMLHandler::RegisterNodeName(wxT("cell"), wxT("wxRichTextCell"));
9871 wxRichTextXMLHandler::RegisterNodeName(wxT("table"), wxT("wxRichTextTable"));
9872 wxRichTextXMLHandler::RegisterNodeName(wxT("field"), wxT("wxRichTextField"));
9878 wxRichTextBuffer::CleanUpHandlers();
9879 wxRichTextBuffer::CleanUpDrawingHandlers();
9880 wxRichTextBuffer::CleanUpFieldTypes();
9881 wxRichTextXMLHandler::ClearNodeToClassMap();
9882 wxRichTextDecimalToRoman(-1);
9883 wxRichTextParagraph::ClearDefaultTabs();
9884 wxRichTextCtrl::ClearAvailableFontNames();
9885 wxRichTextBuffer::SetRenderer(NULL
);
9889 IMPLEMENT_DYNAMIC_CLASS(wxRichTextModule
, wxModule
)
9892 // If the richtext lib is dynamically loaded after the app has already started
9893 // (such as from wxPython) then the built-in module system will not init this
9894 // module. Provide this function to do it manually.
9895 void wxRichTextModuleInit()
9897 wxModule
* module = new wxRichTextModule
;
9899 wxModule::RegisterModule(module);
9904 * Commands for undo/redo
9908 wxRichTextCommand::wxRichTextCommand(const wxString
& name
, wxRichTextCommandId id
, wxRichTextBuffer
* buffer
,
9909 wxRichTextParagraphLayoutBox
* container
, wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
): wxCommand(true, name
)
9911 /* wxRichTextAction* action = */ new wxRichTextAction(this, name
, id
, buffer
, container
, ctrl
, ignoreFirstTime
);
9914 wxRichTextCommand::wxRichTextCommand(const wxString
& name
): wxCommand(true, name
)
9918 wxRichTextCommand::~wxRichTextCommand()
9923 void wxRichTextCommand::AddAction(wxRichTextAction
* action
)
9925 if (!m_actions
.Member(action
))
9926 m_actions
.Append(action
);
9929 bool wxRichTextCommand::Do()
9931 for (wxList::compatibility_iterator node
= m_actions
.GetFirst(); node
; node
= node
->GetNext())
9933 wxRichTextAction
* action
= (wxRichTextAction
*) node
->GetData();
9940 bool wxRichTextCommand::Undo()
9942 for (wxList::compatibility_iterator node
= m_actions
.GetLast(); node
; node
= node
->GetPrevious())
9944 wxRichTextAction
* action
= (wxRichTextAction
*) node
->GetData();
9951 void wxRichTextCommand::ClearActions()
9953 WX_CLEAR_LIST(wxList
, m_actions
);
9961 wxRichTextAction::wxRichTextAction(wxRichTextCommand
* cmd
, const wxString
& name
, wxRichTextCommandId id
,
9962 wxRichTextBuffer
* buffer
, wxRichTextParagraphLayoutBox
* container
,
9963 wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
)
9967 m_containerAddress
.Create(buffer
, container
);
9968 m_ignoreThis
= ignoreFirstTime
;
9973 m_newParagraphs
.SetDefaultStyle(buffer
->GetDefaultStyle());
9974 m_newParagraphs
.SetBasicStyle(buffer
->GetBasicStyle());
9976 cmd
->AddAction(this);
9979 wxRichTextAction::~wxRichTextAction()
9985 // Returns the container that this action refers to, using the container address and top-level buffer.
9986 wxRichTextParagraphLayoutBox
* wxRichTextAction::GetContainer() const
9988 wxRichTextParagraphLayoutBox
* container
= wxDynamicCast(GetContainerAddress().GetObject(m_buffer
), wxRichTextParagraphLayoutBox
);
9993 void wxRichTextAction::CalculateRefreshOptimizations(wxArrayInt
& optimizationLineCharPositions
, wxArrayInt
& optimizationLineYPositions
)
9995 // Store a list of line start character and y positions so we can figure out which area
9996 // we need to refresh
9998 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9999 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10000 wxASSERT(container
!= NULL
);
10004 // NOTE: we're assuming that the buffer is laid out correctly at this point.
10005 // If we had several actions, which only invalidate and leave layout until the
10006 // paint handler is called, then this might not be true. So we may need to switch
10007 // optimisation on only when we're simply adding text and not simultaneously
10008 // deleting a selection, for example. Or, we make sure the buffer is laid out correctly
10009 // first, but of course this means we'll be doing it twice.
10010 if (!m_buffer
->IsDirty() && m_ctrl
) // can only do optimisation if the buffer is already laid out correctly
10012 wxSize clientSize
= m_ctrl
->GetClientSize();
10013 wxPoint firstVisiblePt
= m_ctrl
->GetFirstVisiblePoint();
10014 int lastY
= firstVisiblePt
.y
+ clientSize
.y
;
10016 wxRichTextParagraph
* para
= container
->GetParagraphAtPosition(GetRange().GetStart());
10017 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(para
);
10020 wxRichTextParagraph
* child
= (wxRichTextParagraph
*) node
->GetData();
10021 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
10024 wxRichTextLine
* line
= node2
->GetData();
10025 wxPoint pt
= line
->GetAbsolutePosition();
10026 wxRichTextRange range
= line
->GetAbsoluteRange();
10030 node2
= wxRichTextLineList::compatibility_iterator();
10031 node
= wxRichTextObjectList::compatibility_iterator();
10033 else if (range
.GetStart() > GetPosition() && pt
.y
>= firstVisiblePt
.y
)
10035 optimizationLineCharPositions
.Add(range
.GetStart());
10036 optimizationLineYPositions
.Add(pt
.y
);
10040 node2
= node2
->GetNext();
10044 node
= node
->GetNext();
10050 bool wxRichTextAction::Do()
10052 m_buffer
->Modify(true);
10054 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10055 wxASSERT(container
!= NULL
);
10061 case wxRICHTEXT_INSERT
:
10063 // Store a list of line start character and y positions so we can figure out which area
10064 // we need to refresh
10065 wxArrayInt optimizationLineCharPositions
;
10066 wxArrayInt optimizationLineYPositions
;
10068 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10069 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
10072 container
->InsertFragment(GetRange().GetStart(), m_newParagraphs
);
10073 container
->UpdateRanges();
10075 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10076 // Layout() would stop prematurely at the top level.
10077 container
->InvalidateHierarchy(wxRichTextRange(wxMax(0, GetRange().GetStart()-1), GetRange().GetEnd()));
10079 long newCaretPosition
= GetPosition() + m_newParagraphs
.GetOwnRange().GetLength();
10081 // Character position to caret position
10082 newCaretPosition
--;
10084 // Don't take into account the last newline
10085 if (m_newParagraphs
.GetPartialParagraph())
10086 newCaretPosition
--;
10088 if (m_newParagraphs
.GetChildren().GetCount() > 1)
10090 wxRichTextObject
* p
= (wxRichTextObject
*) m_newParagraphs
.GetChildren().GetLast()->GetData();
10091 if (p
->GetRange().GetLength() == 1)
10092 newCaretPosition
--;
10095 newCaretPosition
= wxMin(newCaretPosition
, (container
->GetOwnRange().GetEnd()-1));
10097 UpdateAppearance(newCaretPosition
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */);
10099 wxRichTextEvent
cmdEvent(
10100 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED
,
10101 m_ctrl
? m_ctrl
->GetId() : -1);
10102 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10103 cmdEvent
.SetRange(GetRange());
10104 cmdEvent
.SetPosition(GetRange().GetStart());
10105 cmdEvent
.SetContainer(container
);
10107 m_buffer
->SendEvent(cmdEvent
);
10111 case wxRICHTEXT_DELETE
:
10113 wxArrayInt optimizationLineCharPositions
;
10114 wxArrayInt optimizationLineYPositions
;
10116 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10117 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
10120 container
->DeleteRange(GetRange());
10121 container
->UpdateRanges();
10122 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10123 // Layout() would stop prematurely at the top level.
10124 container
->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
10126 long caretPos
= GetRange().GetStart()-1;
10127 if (caretPos
>= container
->GetOwnRange().GetEnd())
10130 UpdateAppearance(caretPos
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */);
10132 wxRichTextEvent
cmdEvent(
10133 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED
,
10134 m_ctrl
? m_ctrl
->GetId() : -1);
10135 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10136 cmdEvent
.SetRange(GetRange());
10137 cmdEvent
.SetPosition(GetRange().GetStart());
10138 cmdEvent
.SetContainer(container
);
10140 m_buffer
->SendEvent(cmdEvent
);
10144 case wxRICHTEXT_CHANGE_STYLE
:
10145 case wxRICHTEXT_CHANGE_PROPERTIES
:
10147 ApplyParagraphs(GetNewParagraphs());
10149 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10150 // Layout() would stop prematurely at the top level.
10151 container
->InvalidateHierarchy(GetRange());
10153 UpdateAppearance(GetPosition());
10155 wxRichTextEvent
cmdEvent(
10156 m_cmdId
== wxRICHTEXT_CHANGE_STYLE
? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
: wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED
,
10157 m_ctrl
? m_ctrl
->GetId() : -1);
10158 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10159 cmdEvent
.SetRange(GetRange());
10160 cmdEvent
.SetPosition(GetRange().GetStart());
10161 cmdEvent
.SetContainer(container
);
10163 m_buffer
->SendEvent(cmdEvent
);
10167 case wxRICHTEXT_CHANGE_ATTRIBUTES
:
10169 wxRichTextObject
* obj
= m_objectAddress
.GetObject(m_buffer
); // container->GetChildAtPosition(GetRange().GetStart());
10172 wxRichTextAttr oldAttr
= obj
->GetAttributes();
10173 obj
->GetAttributes() = m_attributes
;
10174 m_attributes
= oldAttr
;
10177 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10178 // Layout() would stop prematurely at the top level.
10179 container
->InvalidateHierarchy(GetRange());
10181 UpdateAppearance(GetPosition());
10183 wxRichTextEvent
cmdEvent(
10184 wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
,
10185 m_ctrl
? m_ctrl
->GetId() : -1);
10186 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10187 cmdEvent
.SetRange(GetRange());
10188 cmdEvent
.SetPosition(GetRange().GetStart());
10189 cmdEvent
.SetContainer(container
);
10191 m_buffer
->SendEvent(cmdEvent
);
10195 case wxRICHTEXT_CHANGE_OBJECT
:
10197 wxRichTextObject
* obj
= m_objectAddress
.GetObject(m_buffer
);
10198 // wxRichTextObject* obj = container->GetChildAtPosition(GetRange().GetStart());
10199 if (obj
&& m_object
)
10201 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(obj
);
10204 wxRichTextObject
* obj
= node
->GetData();
10205 node
->SetData(m_object
);
10210 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10211 // Layout() would stop prematurely at the top level.
10212 container
->InvalidateHierarchy(GetRange());
10214 UpdateAppearance(GetPosition());
10216 // TODO: send new kind of modification event
10227 bool wxRichTextAction::Undo()
10229 m_buffer
->Modify(true);
10231 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10232 wxASSERT(container
!= NULL
);
10238 case wxRICHTEXT_INSERT
:
10240 wxArrayInt optimizationLineCharPositions
;
10241 wxArrayInt optimizationLineYPositions
;
10243 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10244 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
10247 container
->DeleteRange(GetRange());
10248 container
->UpdateRanges();
10250 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10251 // Layout() would stop prematurely at the top level.
10252 container
->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
10254 long newCaretPosition
= GetPosition() - 1;
10256 UpdateAppearance(newCaretPosition
, true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */);
10258 wxRichTextEvent
cmdEvent(
10259 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED
,
10260 m_ctrl
? m_ctrl
->GetId() : -1);
10261 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10262 cmdEvent
.SetRange(GetRange());
10263 cmdEvent
.SetPosition(GetRange().GetStart());
10264 cmdEvent
.SetContainer(container
);
10266 m_buffer
->SendEvent(cmdEvent
);
10270 case wxRICHTEXT_DELETE
:
10272 wxArrayInt optimizationLineCharPositions
;
10273 wxArrayInt optimizationLineYPositions
;
10275 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10276 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
10279 container
->InsertFragment(GetRange().GetStart(), m_oldParagraphs
);
10280 container
->UpdateRanges();
10282 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10283 // Layout() would stop prematurely at the top level.
10284 container
->InvalidateHierarchy(GetRange());
10286 UpdateAppearance(GetPosition(), true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */);
10288 wxRichTextEvent
cmdEvent(
10289 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED
,
10290 m_ctrl
? m_ctrl
->GetId() : -1);
10291 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10292 cmdEvent
.SetRange(GetRange());
10293 cmdEvent
.SetPosition(GetRange().GetStart());
10294 cmdEvent
.SetContainer(container
);
10296 m_buffer
->SendEvent(cmdEvent
);
10300 case wxRICHTEXT_CHANGE_STYLE
:
10301 case wxRICHTEXT_CHANGE_PROPERTIES
:
10303 ApplyParagraphs(GetOldParagraphs());
10304 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10305 // Layout() would stop prematurely at the top level.
10306 container
->InvalidateHierarchy(GetRange());
10308 UpdateAppearance(GetPosition());
10310 wxRichTextEvent
cmdEvent(
10311 m_cmdId
== wxRICHTEXT_CHANGE_STYLE
? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
: wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED
,
10312 m_ctrl
? m_ctrl
->GetId() : -1);
10313 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10314 cmdEvent
.SetRange(GetRange());
10315 cmdEvent
.SetPosition(GetRange().GetStart());
10316 cmdEvent
.SetContainer(container
);
10318 m_buffer
->SendEvent(cmdEvent
);
10322 case wxRICHTEXT_CHANGE_ATTRIBUTES
:
10323 case wxRICHTEXT_CHANGE_OBJECT
:
10334 /// Update the control appearance
10335 void wxRichTextAction::UpdateAppearance(long caretPosition
, bool sendUpdateEvent
, wxArrayInt
* optimizationLineCharPositions
, wxArrayInt
* optimizationLineYPositions
, bool isDoCmd
)
10337 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10338 wxASSERT(container
!= NULL
);
10344 m_ctrl
->SetFocusObject(container
);
10345 m_ctrl
->SetCaretPosition(caretPosition
);
10347 if (!m_ctrl
->IsFrozen())
10349 wxRect containerRect
= container
->GetRect();
10351 m_ctrl
->LayoutContent();
10353 // Refresh everything if there were floating objects or the container changed size
10354 // (we can't yet optimize in these cases, since more complex interaction with other content occurs)
10355 if (container
->GetFloatingObjectCount() > 0 || (container
->GetParent() && containerRect
!= container
->GetRect()))
10357 m_ctrl
->Refresh(false);
10361 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10362 // Find refresh rectangle if we are in a position to optimise refresh
10363 if ((m_cmdId
== wxRICHTEXT_INSERT
|| m_cmdId
== wxRICHTEXT_DELETE
) && optimizationLineCharPositions
)
10367 wxSize clientSize
= m_ctrl
->GetClientSize();
10368 wxPoint firstVisiblePt
= m_ctrl
->GetFirstVisiblePoint();
10370 // Start/end positions
10372 int lastY
= firstVisiblePt
.y
+ clientSize
.y
;
10374 bool foundEnd
= false;
10376 // position offset - how many characters were inserted
10377 int positionOffset
= GetRange().GetLength();
10379 // Determine whether this is Do or Undo, and adjust positionOffset accordingly
10380 if ((m_cmdId
== wxRICHTEXT_DELETE
&& isDoCmd
) || (m_cmdId
== wxRICHTEXT_INSERT
&& !isDoCmd
))
10381 positionOffset
= - positionOffset
;
10383 // find the first line which is being drawn at the same position as it was
10384 // before. Since we're talking about a simple insertion, we can assume
10385 // that the rest of the window does not need to be redrawn.
10387 wxRichTextParagraph
* para
= container
->GetParagraphAtPosition(GetPosition());
10388 // Since we support floating layout, we should redraw the whole para instead of just
10389 // the first line touching the invalid range.
10392 firstY
= para
->GetPosition().y
;
10395 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(para
);
10398 wxRichTextParagraph
* child
= (wxRichTextParagraph
*) node
->GetData();
10399 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
10402 wxRichTextLine
* line
= node2
->GetData();
10403 wxPoint pt
= line
->GetAbsolutePosition();
10404 wxRichTextRange range
= line
->GetAbsoluteRange();
10406 // we want to find the first line that is in the same position
10407 // as before. This will mean we're at the end of the changed text.
10409 if (pt
.y
> lastY
) // going past the end of the window, no more info
10411 node2
= wxRichTextLineList::compatibility_iterator();
10412 node
= wxRichTextObjectList::compatibility_iterator();
10414 // Detect last line in the buffer
10415 else if (!node2
->GetNext() && para
->GetRange().Contains(container
->GetOwnRange().GetEnd()))
10417 // If deleting text, make sure we refresh below as well as above
10418 if (positionOffset
>= 0)
10421 lastY
= pt
.y
+ line
->GetSize().y
;
10424 node2
= wxRichTextLineList::compatibility_iterator();
10425 node
= wxRichTextObjectList::compatibility_iterator();
10431 // search for this line being at the same position as before
10432 for (i
= 0; i
< optimizationLineCharPositions
->GetCount(); i
++)
10434 if (((*optimizationLineCharPositions
)[i
] + positionOffset
== range
.GetStart()) &&
10435 ((*optimizationLineYPositions
)[i
] == pt
.y
))
10437 // Stop, we're now the same as we were
10442 node2
= wxRichTextLineList::compatibility_iterator();
10443 node
= wxRichTextObjectList::compatibility_iterator();
10451 node2
= node2
->GetNext();
10455 node
= node
->GetNext();
10458 firstY
= wxMax(firstVisiblePt
.y
, firstY
);
10460 lastY
= firstVisiblePt
.y
+ clientSize
.y
;
10462 // Convert to device coordinates
10463 wxRect
rect(m_ctrl
->GetPhysicalPoint(wxPoint(firstVisiblePt
.x
, firstY
)), wxSize(clientSize
.x
, lastY
- firstY
));
10464 m_ctrl
->RefreshRect(rect
);
10468 m_ctrl
->Refresh(false);
10470 m_ctrl
->PositionCaret();
10472 // This causes styles to persist when doing programmatic
10473 // content creation except when Freeze/Thaw is used, so
10474 // disable this and check for the consequences.
10475 // m_ctrl->SetDefaultStyleToCursorStyle();
10477 if (sendUpdateEvent
)
10478 wxTextCtrl::SendTextUpdatedEvent(m_ctrl
);
10483 /// Replace the buffer paragraphs with the new ones.
10484 void wxRichTextAction::ApplyParagraphs(const wxRichTextParagraphLayoutBox
& fragment
)
10486 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10487 wxASSERT(container
!= NULL
);
10491 wxRichTextObjectList::compatibility_iterator node
= fragment
.GetChildren().GetFirst();
10494 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
10495 wxASSERT (para
!= NULL
);
10497 // We'll replace the existing paragraph by finding the paragraph at this position,
10498 // delete its node data, and setting a copy as the new node data.
10499 // TODO: make more efficient by simply swapping old and new paragraph objects.
10501 wxRichTextParagraph
* existingPara
= container
->GetParagraphAtPosition(para
->GetRange().GetStart());
10504 wxRichTextObjectList::compatibility_iterator bufferParaNode
= container
->GetChildren().Find(existingPara
);
10505 if (bufferParaNode
)
10507 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(*para
);
10508 newPara
->SetParent(container
);
10510 bufferParaNode
->SetData(newPara
);
10512 delete existingPara
;
10516 node
= node
->GetNext();
10523 * This stores beginning and end positions for a range of data.
10526 WX_DEFINE_OBJARRAY(wxRichTextRangeArray
);
10528 /// Limit this range to be within 'range'
10529 bool wxRichTextRange::LimitTo(const wxRichTextRange
& range
)
10531 if (m_start
< range
.m_start
)
10532 m_start
= range
.m_start
;
10534 if (m_end
> range
.m_end
)
10535 m_end
= range
.m_end
;
10541 * wxRichTextImage implementation
10542 * This object represents an image.
10545 IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage
, wxRichTextObject
)
10547 wxRichTextImage::wxRichTextImage(const wxImage
& image
, wxRichTextObject
* parent
, wxRichTextAttr
* charStyle
):
10548 wxRichTextObject(parent
)
10551 m_imageBlock
.MakeImageBlockDefaultQuality(image
, wxBITMAP_TYPE_PNG
);
10553 SetAttributes(*charStyle
);
10556 wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock
& imageBlock
, wxRichTextObject
* parent
, wxRichTextAttr
* charStyle
):
10557 wxRichTextObject(parent
)
10560 m_imageBlock
= imageBlock
;
10562 SetAttributes(*charStyle
);
10565 void wxRichTextImage::Init()
10567 m_originalImageSize
= wxSize(-1, -1);
10570 /// Create a cached image at the required size
10571 bool wxRichTextImage::LoadImageCache(wxDC
& dc
, bool resetCache
)
10573 if (!m_imageBlock
.IsOk())
10576 // If we have an original image size, use that to compute the cached bitmap size
10577 // instead of loading the image each time. This way we can avoid loading
10578 // the image so long as the new cached bitmap size hasn't changed.
10581 if (resetCache
|| m_originalImageSize
== wxSize(-1, -1))
10583 m_imageCache
= wxNullBitmap
;
10585 m_imageBlock
.Load(image
);
10589 m_originalImageSize
= wxSize(image
.GetWidth(), image
.GetHeight());
10592 int width
= m_originalImageSize
.GetWidth();
10593 int height
= m_originalImageSize
.GetHeight();
10595 int parentWidth
= 0;
10596 int parentHeight
= 0;
10599 int maxHeight
= -1;
10601 wxRichTextBuffer
* buffer
= GetBuffer();
10605 if (buffer
->GetRichTextCtrl())
10607 // Subtract borders
10608 sz
= buffer
->GetRichTextCtrl()->GetClientSize();
10610 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
10611 marginRect
= wxRect(0, 0, sz
.x
, sz
.y
);
10612 buffer
->GetBoxRects(dc
, buffer
, buffer
->GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
10614 sz
= contentRect
.GetSize();
10616 // Start with a maximum width of the control size, even if not specified by the content,
10617 // to minimize the amount of picture overlapping the right-hand side
10621 sz
= buffer
->GetCachedSize();
10622 parentWidth
= sz
.GetWidth();
10623 parentHeight
= sz
.GetHeight();
10626 if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetWidth().GetValue() > 0)
10628 if (parentWidth
> 0 && GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
10629 width
= (int) ((GetAttributes().GetTextBoxAttr().GetWidth().GetValue() * parentWidth
)/100.0);
10630 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
10631 width
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetWidth().GetValue());
10632 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
10633 width
= GetAttributes().GetTextBoxAttr().GetWidth().GetValue();
10636 // Limit to max width
10638 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() > 0)
10642 if (parentWidth
> 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
10643 mw
= (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() * parentWidth
)/100.0);
10644 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
10645 mw
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue());
10646 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
10647 mw
= GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue();
10649 // If we already have a smaller max width due to the constraints of the control size,
10650 // don't use the larger max width.
10651 if (mw
!= -1 && ((maxWidth
== -1) || (mw
< maxWidth
)))
10655 if (maxWidth
> 0 && width
> maxWidth
)
10658 // Preserve the aspect ratio
10659 if (width
!= m_originalImageSize
.GetWidth())
10660 height
= (int) (float(m_originalImageSize
.GetHeight()) * (float(width
)/float(m_originalImageSize
.GetWidth())));
10662 if (GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetHeight().GetValue() > 0)
10664 if (parentHeight
> 0 && GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
10665 height
= (int) ((GetAttributes().GetTextBoxAttr().GetHeight().GetValue() * parentHeight
)/100.0);
10666 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
10667 height
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetHeight().GetValue());
10668 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
10669 height
= GetAttributes().GetTextBoxAttr().GetHeight().GetValue();
10671 // Preserve the aspect ratio
10672 if (height
!= m_originalImageSize
.GetHeight())
10673 width
= (int) (float(m_originalImageSize
.GetWidth()) * (float(height
)/float(m_originalImageSize
.GetHeight())));
10676 // Limit to max height
10678 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() > 0)
10680 if (parentHeight
> 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
10681 maxHeight
= (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() * parentHeight
)/100.0);
10682 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
10683 maxHeight
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue());
10684 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
10685 maxHeight
= GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue();
10688 if (maxHeight
> 0 && height
> maxHeight
)
10690 height
= maxHeight
;
10692 // Preserve the aspect ratio
10693 if (height
!= m_originalImageSize
.GetHeight())
10694 width
= (int) (float(m_originalImageSize
.GetWidth()) * (float(height
)/float(m_originalImageSize
.GetHeight())));
10697 if (m_imageCache
.IsOk() && m_imageCache
.GetWidth() == width
&& m_imageCache
.GetHeight() == height
)
10699 // Do nothing, we didn't need to change the image cache
10705 m_imageBlock
.Load(image
);
10710 if (image
.GetWidth() == width
&& image
.GetHeight() == height
)
10711 m_imageCache
= wxBitmap(image
);
10714 // If the original width and height is small, e.g. 400 or below,
10715 // scale up and then down to improve image quality. This can make
10716 // a big difference, with not much performance hit.
10717 int upscaleThreshold
= 400;
10719 if (image
.GetWidth() <= upscaleThreshold
|| image
.GetHeight() <= upscaleThreshold
)
10721 img
= image
.Scale(image
.GetWidth()*2, image
.GetHeight()*2);
10722 img
.Rescale(width
, height
, wxIMAGE_QUALITY_HIGH
);
10725 img
= image
.Scale(width
, height
, wxIMAGE_QUALITY_HIGH
);
10726 m_imageCache
= wxBitmap(img
);
10730 return m_imageCache
.IsOk();
10734 bool wxRichTextImage::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& WXUNUSED(range
), const wxRichTextSelection
& selection
, const wxRect
& rect
, int WXUNUSED(descent
), int WXUNUSED(style
))
10739 // Don't need cached size AFAIK
10740 // wxSize size = GetCachedSize();
10741 if (!LoadImageCache(dc
))
10744 wxRichTextAttr
attr(GetAttributes());
10745 context
.ApplyVirtualAttributes(attr
, this);
10747 DrawBoxAttributes(dc
, GetBuffer(), attr
, wxRect(rect
.GetPosition(), GetCachedSize()));
10749 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
10750 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
10751 marginRect
= rect
; // outer rectangle, will calculate contentRect
10752 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
10754 dc
.DrawBitmap(m_imageCache
, contentRect
.x
, contentRect
.y
, true);
10756 if (selection
.WithinSelection(GetRange().GetStart(), this))
10758 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
10759 wxCheckSetPen(dc
, *wxBLACK_PEN
);
10760 dc
.SetLogicalFunction(wxINVERT
);
10761 dc
.DrawRectangle(contentRect
);
10762 dc
.SetLogicalFunction(wxCOPY
);
10768 /// Lay the item out
10769 bool wxRichTextImage::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& WXUNUSED(parentRect
), int WXUNUSED(style
))
10771 if (!LoadImageCache(dc
))
10774 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
10775 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
10776 contentRect
= wxRect(wxPoint(0,0), imageSize
);
10778 wxRichTextAttr
attr(GetAttributes());
10779 context
.ApplyVirtualAttributes(attr
, this);
10781 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
10783 wxSize overallSize
= marginRect
.GetSize();
10785 SetCachedSize(overallSize
);
10786 SetMaxSize(overallSize
);
10787 SetMinSize(overallSize
);
10788 SetPosition(rect
.GetPosition());
10793 /// Get/set the object size for the given range. Returns false if the range
10794 /// is invalid for this object.
10795 bool wxRichTextImage::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& WXUNUSED(descent
), wxDC
& dc
, wxRichTextDrawingContext
& context
, int WXUNUSED(flags
), wxPoint
WXUNUSED(position
), wxArrayInt
* partialExtents
) const
10797 if (!range
.IsWithin(GetRange()))
10800 if (!((wxRichTextImage
*)this)->LoadImageCache(dc
))
10802 size
.x
= 0; size
.y
= 0;
10803 if (partialExtents
)
10804 partialExtents
->Add(0);
10808 wxRichTextAttr
attr(GetAttributes());
10809 context
.ApplyVirtualAttributes(attr
, (wxRichTextObject
*) this);
10811 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
10812 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
10813 contentRect
= wxRect(wxPoint(0,0), imageSize
);
10814 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
10816 wxSize overallSize
= marginRect
.GetSize();
10818 if (partialExtents
)
10819 partialExtents
->Add(overallSize
.x
);
10821 size
= overallSize
;
10826 // Get the 'natural' size for an object. For an image, it would be the
10828 wxTextAttrSize
wxRichTextImage::GetNaturalSize() const
10830 wxTextAttrSize size
;
10831 if (GetImageCache().IsOk())
10833 size
.SetWidth(GetImageCache().GetWidth(), wxTEXT_ATTR_UNITS_PIXELS
);
10834 size
.SetHeight(GetImageCache().GetHeight(), wxTEXT_ATTR_UNITS_PIXELS
);
10841 void wxRichTextImage::Copy(const wxRichTextImage
& obj
)
10843 wxRichTextObject::Copy(obj
);
10845 m_imageBlock
= obj
.m_imageBlock
;
10846 m_originalImageSize
= obj
.m_originalImageSize
;
10849 /// Edit properties via a GUI
10850 bool wxRichTextImage::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
10852 wxRichTextObjectPropertiesDialog
imageDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Picture Properties"));
10853 imageDlg
.SetAttributes(GetAttributes());
10855 if (imageDlg
.ShowModal() == wxID_OK
)
10857 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
10858 // indeterminate in the object.
10859 imageDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
10871 /// Compare two attribute objects
10872 bool wxTextAttrEq(const wxRichTextAttr
& attr1
, const wxRichTextAttr
& attr2
)
10874 return (attr1
== attr2
);
10877 // Partial equality test taking flags into account
10878 bool wxTextAttrEqPartial(const wxRichTextAttr
& attr1
, const wxRichTextAttr
& attr2
)
10880 return attr1
.EqPartial(attr2
);
10884 bool wxRichTextTabsEq(const wxArrayInt
& tabs1
, const wxArrayInt
& tabs2
)
10886 if (tabs1
.GetCount() != tabs2
.GetCount())
10890 for (i
= 0; i
< tabs1
.GetCount(); i
++)
10892 if (tabs1
[i
] != tabs2
[i
])
10898 bool wxRichTextApplyStyle(wxRichTextAttr
& destStyle
, const wxRichTextAttr
& style
, wxRichTextAttr
* compareWith
)
10900 return destStyle
.Apply(style
, compareWith
);
10903 // Remove attributes
10904 bool wxRichTextRemoveStyle(wxRichTextAttr
& destStyle
, const wxRichTextAttr
& style
)
10906 return destStyle
.RemoveStyle(style
);
10909 /// Combine two bitlists, specifying the bits of interest with separate flags.
10910 bool wxRichTextCombineBitlists(int& valueA
, int valueB
, int& flagsA
, int flagsB
)
10912 return wxRichTextAttr::CombineBitlists(valueA
, valueB
, flagsA
, flagsB
);
10915 /// Compare two bitlists
10916 bool wxRichTextBitlistsEqPartial(int valueA
, int valueB
, int flags
)
10918 return wxRichTextAttr::BitlistsEqPartial(valueA
, valueB
, flags
);
10921 /// Split into paragraph and character styles
10922 bool wxRichTextSplitParaCharStyles(const wxRichTextAttr
& style
, wxRichTextAttr
& parStyle
, wxRichTextAttr
& charStyle
)
10924 return wxRichTextAttr::SplitParaCharStyles(style
, parStyle
, charStyle
);
10927 /// Convert a decimal to Roman numerals
10928 wxString
wxRichTextDecimalToRoman(long n
)
10930 static wxArrayInt decimalNumbers
;
10931 static wxArrayString romanNumbers
;
10936 decimalNumbers
.Clear();
10937 romanNumbers
.Clear();
10938 return wxEmptyString
;
10941 if (decimalNumbers
.GetCount() == 0)
10943 #define wxRichTextAddDecRom(n, r) decimalNumbers.Add(n); romanNumbers.Add(r);
10945 wxRichTextAddDecRom(1000, wxT("M"));
10946 wxRichTextAddDecRom(900, wxT("CM"));
10947 wxRichTextAddDecRom(500, wxT("D"));
10948 wxRichTextAddDecRom(400, wxT("CD"));
10949 wxRichTextAddDecRom(100, wxT("C"));
10950 wxRichTextAddDecRom(90, wxT("XC"));
10951 wxRichTextAddDecRom(50, wxT("L"));
10952 wxRichTextAddDecRom(40, wxT("XL"));
10953 wxRichTextAddDecRom(10, wxT("X"));
10954 wxRichTextAddDecRom(9, wxT("IX"));
10955 wxRichTextAddDecRom(5, wxT("V"));
10956 wxRichTextAddDecRom(4, wxT("IV"));
10957 wxRichTextAddDecRom(1, wxT("I"));
10963 while (n
> 0 && i
< 13)
10965 if (n
>= decimalNumbers
[i
])
10967 n
-= decimalNumbers
[i
];
10968 roman
+= romanNumbers
[i
];
10975 if (roman
.IsEmpty())
10981 * wxRichTextFileHandler
10982 * Base class for file handlers
10985 IMPLEMENT_CLASS(wxRichTextFileHandler
, wxObject
)
10987 #if wxUSE_FFILE && wxUSE_STREAMS
10988 bool wxRichTextFileHandler::LoadFile(wxRichTextBuffer
*buffer
, const wxString
& filename
)
10990 wxFFileInputStream
stream(filename
);
10992 return LoadFile(buffer
, stream
);
10997 bool wxRichTextFileHandler::SaveFile(wxRichTextBuffer
*buffer
, const wxString
& filename
)
10999 wxFFileOutputStream
stream(filename
);
11001 return SaveFile(buffer
, stream
);
11005 #endif // wxUSE_FFILE && wxUSE_STREAMS
11007 /// Can we handle this filename (if using files)? By default, checks the extension.
11008 bool wxRichTextFileHandler::CanHandle(const wxString
& filename
) const
11010 wxString path
, file
, ext
;
11011 wxFileName::SplitPath(filename
, & path
, & file
, & ext
);
11013 return (ext
.Lower() == GetExtension());
11017 * wxRichTextTextHandler
11018 * Plain text handler
11021 IMPLEMENT_CLASS(wxRichTextPlainTextHandler
, wxRichTextFileHandler
)
11024 bool wxRichTextPlainTextHandler::DoLoadFile(wxRichTextBuffer
*buffer
, wxInputStream
& stream
)
11026 if (!stream
.IsOk())
11032 while (!stream
.Eof())
11034 int ch
= stream
.GetC();
11038 if (ch
== 10 && lastCh
!= 13)
11041 if (ch
> 0 && ch
!= 10)
11048 buffer
->ResetAndClearCommands();
11050 buffer
->AddParagraphs(str
);
11051 buffer
->UpdateRanges();
11056 bool wxRichTextPlainTextHandler::DoSaveFile(wxRichTextBuffer
*buffer
, wxOutputStream
& stream
)
11058 if (!stream
.IsOk())
11061 wxString text
= buffer
->GetText();
11063 wxString newLine
= wxRichTextLineBreakChar
;
11064 text
.Replace(newLine
, wxT("\n"));
11066 wxCharBuffer buf
= text
.ToAscii();
11068 stream
.Write((const char*) buf
, text
.length());
11071 #endif // wxUSE_STREAMS
11074 * Stores information about an image, in binary in-memory form
11077 wxRichTextImageBlock::wxRichTextImageBlock()
11082 wxRichTextImageBlock::wxRichTextImageBlock(const wxRichTextImageBlock
& block
):wxObject()
11088 wxRichTextImageBlock::~wxRichTextImageBlock()
11093 void wxRichTextImageBlock::Init()
11097 m_imageType
= wxBITMAP_TYPE_INVALID
;
11100 void wxRichTextImageBlock::Clear()
11104 m_imageType
= wxBITMAP_TYPE_INVALID
;
11108 // Load the original image into a memory block.
11109 // If the image is not a JPEG, we must convert it into a JPEG
11110 // to conserve space.
11111 // If it's not a JPEG we can make use of 'image', already scaled, so we don't have to
11112 // load the image a 2nd time.
11114 bool wxRichTextImageBlock::MakeImageBlock(const wxString
& filename
, wxBitmapType imageType
,
11115 wxImage
& image
, bool convertToJPEG
)
11117 m_imageType
= imageType
;
11119 wxString
filenameToRead(filename
);
11120 bool removeFile
= false;
11122 if (imageType
== wxBITMAP_TYPE_INVALID
)
11123 return false; // Could not determine image type
11125 if ((imageType
!= wxBITMAP_TYPE_JPEG
) && convertToJPEG
)
11127 wxString tempFile
=
11128 wxFileName::CreateTempFileName(_("image"));
11130 wxASSERT(!tempFile
.IsEmpty());
11132 image
.SaveFile(tempFile
, wxBITMAP_TYPE_JPEG
);
11133 filenameToRead
= tempFile
;
11136 m_imageType
= wxBITMAP_TYPE_JPEG
;
11139 if (!file
.Open(filenameToRead
))
11142 m_dataSize
= (size_t) file
.Length();
11147 m_data
= ReadBlock(filenameToRead
, m_dataSize
);
11150 wxRemoveFile(filenameToRead
);
11152 return (m_data
!= NULL
);
11155 // Make an image block from the wxImage in the given
11157 bool wxRichTextImageBlock::MakeImageBlock(wxImage
& image
, wxBitmapType imageType
, int quality
)
11159 image
.SetOption(wxT("quality"), quality
);
11161 if (imageType
== wxBITMAP_TYPE_INVALID
)
11162 return false; // Could not determine image type
11164 return DoMakeImageBlock(image
, imageType
);
11167 // Uses a const wxImage for efficiency, but can't set quality (only relevant for JPEG)
11168 bool wxRichTextImageBlock::MakeImageBlockDefaultQuality(const wxImage
& image
, wxBitmapType imageType
)
11170 if (imageType
== wxBITMAP_TYPE_INVALID
)
11171 return false; // Could not determine image type
11173 return DoMakeImageBlock(image
, imageType
);
11176 // Makes the image block
11177 bool wxRichTextImageBlock::DoMakeImageBlock(const wxImage
& image
, wxBitmapType imageType
)
11179 wxMemoryOutputStream memStream
;
11180 if (!image
.SaveFile(memStream
, imageType
))
11185 unsigned char* block
= new unsigned char[memStream
.GetSize()];
11193 m_imageType
= imageType
;
11194 m_dataSize
= memStream
.GetSize();
11196 memStream
.CopyTo(m_data
, m_dataSize
);
11198 return (m_data
!= NULL
);
11202 bool wxRichTextImageBlock::Write(const wxString
& filename
)
11204 return WriteBlock(filename
, m_data
, m_dataSize
);
11207 void wxRichTextImageBlock::Copy(const wxRichTextImageBlock
& block
)
11209 m_imageType
= block
.m_imageType
;
11211 m_dataSize
= block
.m_dataSize
;
11212 if (m_dataSize
== 0)
11215 m_data
= new unsigned char[m_dataSize
];
11217 for (i
= 0; i
< m_dataSize
; i
++)
11218 m_data
[i
] = block
.m_data
[i
];
11222 void wxRichTextImageBlock::operator=(const wxRichTextImageBlock
& block
)
11227 // Load a wxImage from the block
11228 bool wxRichTextImageBlock::Load(wxImage
& image
)
11233 // Read in the image.
11235 wxMemoryInputStream
mstream(m_data
, m_dataSize
);
11236 bool success
= image
.LoadFile(mstream
, GetImageType());
11238 wxString tempFile
= wxFileName::CreateTempFileName(_("image"));
11239 wxASSERT(!tempFile
.IsEmpty());
11241 if (!WriteBlock(tempFile
, m_data
, m_dataSize
))
11245 success
= image
.LoadFile(tempFile
, GetImageType());
11246 wxRemoveFile(tempFile
);
11252 // Write data in hex to a stream
11253 bool wxRichTextImageBlock::WriteHex(wxOutputStream
& stream
)
11255 if (m_dataSize
== 0)
11258 int bufSize
= 100000;
11259 if (int(2*m_dataSize
) < bufSize
)
11260 bufSize
= 2*m_dataSize
;
11261 char* buf
= new char[bufSize
+1];
11263 int left
= m_dataSize
;
11268 if (left
*2 > bufSize
)
11270 n
= bufSize
; left
-= (bufSize
/2);
11274 n
= left
*2; left
= 0;
11278 for (i
= 0; i
< (n
/2); i
++)
11280 wxDecToHex(m_data
[j
], b
, b
+1);
11285 stream
.Write((const char*) buf
, n
);
11291 // Read data in hex from a stream
11292 bool wxRichTextImageBlock::ReadHex(wxInputStream
& stream
, int length
, wxBitmapType imageType
)
11294 int dataSize
= length
/2;
11299 // create a null terminated temporary string:
11303 m_data
= new unsigned char[dataSize
];
11305 for (i
= 0; i
< dataSize
; i
++)
11307 str
[0] = (char)stream
.GetC();
11308 str
[1] = (char)stream
.GetC();
11310 m_data
[i
] = (unsigned char)wxHexToDec(str
);
11313 m_dataSize
= dataSize
;
11314 m_imageType
= imageType
;
11319 // Allocate and read from stream as a block of memory
11320 unsigned char* wxRichTextImageBlock::ReadBlock(wxInputStream
& stream
, size_t size
)
11322 unsigned char* block
= new unsigned char[size
];
11326 stream
.Read(block
, size
);
11331 unsigned char* wxRichTextImageBlock::ReadBlock(const wxString
& filename
, size_t size
)
11333 wxFileInputStream
stream(filename
);
11334 if (!stream
.IsOk())
11337 return ReadBlock(stream
, size
);
11340 // Write memory block to stream
11341 bool wxRichTextImageBlock::WriteBlock(wxOutputStream
& stream
, unsigned char* block
, size_t size
)
11343 stream
.Write((void*) block
, size
);
11344 return stream
.IsOk();
11348 // Write memory block to file
11349 bool wxRichTextImageBlock::WriteBlock(const wxString
& filename
, unsigned char* block
, size_t size
)
11351 wxFileOutputStream
outStream(filename
);
11352 if (!outStream
.IsOk())
11355 return WriteBlock(outStream
, block
, size
);
11358 // Gets the extension for the block's type
11359 wxString
wxRichTextImageBlock::GetExtension() const
11361 wxImageHandler
* handler
= wxImage::FindHandler(GetImageType());
11363 return handler
->GetExtension();
11365 return wxEmptyString
;
11371 * The data object for a wxRichTextBuffer
11374 const wxChar
*wxRichTextBufferDataObject::ms_richTextBufferFormatId
= wxT("wxShape");
11376 wxRichTextBufferDataObject::wxRichTextBufferDataObject(wxRichTextBuffer
* richTextBuffer
)
11378 m_richTextBuffer
= richTextBuffer
;
11380 // this string should uniquely identify our format, but is otherwise
11382 m_formatRichTextBuffer
.SetId(GetRichTextBufferFormatId());
11384 SetFormat(m_formatRichTextBuffer
);
11387 wxRichTextBufferDataObject::~wxRichTextBufferDataObject()
11389 delete m_richTextBuffer
;
11392 // after a call to this function, the richTextBuffer is owned by the caller and it
11393 // is responsible for deleting it!
11394 wxRichTextBuffer
* wxRichTextBufferDataObject::GetRichTextBuffer()
11396 wxRichTextBuffer
* richTextBuffer
= m_richTextBuffer
;
11397 m_richTextBuffer
= NULL
;
11399 return richTextBuffer
;
11402 wxDataFormat
wxRichTextBufferDataObject::GetPreferredFormat(Direction
WXUNUSED(dir
)) const
11404 return m_formatRichTextBuffer
;
11407 size_t wxRichTextBufferDataObject::GetDataSize() const
11409 if (!m_richTextBuffer
)
11415 wxStringOutputStream
stream(& bufXML
);
11416 if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
))
11418 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
11424 wxCharBuffer buffer
= bufXML
.mb_str(wxConvUTF8
);
11425 return strlen(buffer
) + 1;
11427 return bufXML
.Length()+1;
11431 bool wxRichTextBufferDataObject::GetDataHere(void *pBuf
) const
11433 if (!pBuf
|| !m_richTextBuffer
)
11439 wxStringOutputStream
stream(& bufXML
);
11440 if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
))
11442 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
11448 wxCharBuffer buffer
= bufXML
.mb_str(wxConvUTF8
);
11449 size_t len
= strlen(buffer
);
11450 memcpy((char*) pBuf
, (const char*) buffer
, len
);
11451 ((char*) pBuf
)[len
] = 0;
11453 size_t len
= bufXML
.Length();
11454 memcpy((char*) pBuf
, (const char*) bufXML
.c_str(), len
);
11455 ((char*) pBuf
)[len
] = 0;
11461 bool wxRichTextBufferDataObject::SetData(size_t WXUNUSED(len
), const void *buf
)
11463 wxDELETE(m_richTextBuffer
);
11465 wxString
bufXML((const char*) buf
, wxConvUTF8
);
11467 m_richTextBuffer
= new wxRichTextBuffer
;
11469 wxStringInputStream
stream(bufXML
);
11470 if (!m_richTextBuffer
->LoadFile(stream
, wxRICHTEXT_TYPE_XML
))
11472 wxLogError(wxT("Could not read the buffer from an XML stream.\nYou may have forgotten to add the XML file handler."));
11474 wxDELETE(m_richTextBuffer
);
11486 * wxRichTextFontTable
11487 * Manages quick access to a pool of fonts for rendering rich text
11490 WX_DECLARE_STRING_HASH_MAP_WITH_DECL(wxFont
, wxRichTextFontTableHashMap
, class WXDLLIMPEXP_RICHTEXT
);
11492 class wxRichTextFontTableData
: public wxObjectRefData
11495 wxRichTextFontTableData() {}
11497 wxFont
FindFont(const wxRichTextAttr
& fontSpec
);
11499 wxRichTextFontTableHashMap m_hashMap
;
11502 wxFont
wxRichTextFontTableData::FindFont(const wxRichTextAttr
& fontSpec
)
11504 wxString
facename(fontSpec
.GetFontFaceName());
11505 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()));
11506 wxRichTextFontTableHashMap::iterator entry
= m_hashMap
.find(spec
);
11508 if ( entry
== m_hashMap
.end() )
11510 wxFont
font(fontSpec
.GetFontSize(), wxDEFAULT
, fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), fontSpec
.GetFontUnderlined(), facename
.c_str());
11511 m_hashMap
[spec
] = font
;
11516 return entry
->second
;
11520 IMPLEMENT_DYNAMIC_CLASS(wxRichTextFontTable
, wxObject
)
11522 wxRichTextFontTable::wxRichTextFontTable()
11524 m_refData
= new wxRichTextFontTableData
;
11527 wxRichTextFontTable::wxRichTextFontTable(const wxRichTextFontTable
& table
)
11533 wxRichTextFontTable::~wxRichTextFontTable()
11538 bool wxRichTextFontTable::operator == (const wxRichTextFontTable
& table
) const
11540 return (m_refData
== table
.m_refData
);
11543 void wxRichTextFontTable::operator= (const wxRichTextFontTable
& table
)
11548 wxFont
wxRichTextFontTable::FindFont(const wxRichTextAttr
& fontSpec
)
11550 wxRichTextFontTableData
* data
= (wxRichTextFontTableData
*) m_refData
;
11552 return data
->FindFont(fontSpec
);
11557 void wxRichTextFontTable::Clear()
11559 wxRichTextFontTableData
* data
= (wxRichTextFontTableData
*) m_refData
;
11561 data
->m_hashMap
.clear();
11566 void wxTextBoxAttr::Reset()
11569 m_floatMode
= wxTEXT_BOX_ATTR_FLOAT_NONE
;
11570 m_clearMode
= wxTEXT_BOX_ATTR_CLEAR_NONE
;
11571 m_collapseMode
= wxTEXT_BOX_ATTR_COLLAPSE_NONE
;
11572 m_verticalAlignment
= wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_NONE
;
11573 m_boxStyleName
= wxEmptyString
;
11577 m_position
.Reset();
11588 bool wxTextBoxAttr::operator== (const wxTextBoxAttr
& attr
) const
11591 m_flags
== attr
.m_flags
&&
11592 m_floatMode
== attr
.m_floatMode
&&
11593 m_clearMode
== attr
.m_clearMode
&&
11594 m_collapseMode
== attr
.m_collapseMode
&&
11595 m_verticalAlignment
== attr
.m_verticalAlignment
&&
11597 m_margins
== attr
.m_margins
&&
11598 m_padding
== attr
.m_padding
&&
11599 m_position
== attr
.m_position
&&
11601 m_size
== attr
.m_size
&&
11602 m_minSize
== attr
.m_minSize
&&
11603 m_maxSize
== attr
.m_maxSize
&&
11605 m_border
== attr
.m_border
&&
11606 m_outline
== attr
.m_outline
&&
11608 m_boxStyleName
== attr
.m_boxStyleName
11612 // Partial equality test
11613 bool wxTextBoxAttr::EqPartial(const wxTextBoxAttr
& attr
) const
11615 if (attr
.HasFloatMode() && HasFloatMode() && (GetFloatMode() != attr
.GetFloatMode()))
11618 if (attr
.HasClearMode() && HasClearMode() && (GetClearMode() != attr
.GetClearMode()))
11621 if (attr
.HasCollapseBorders() && HasCollapseBorders() && (attr
.GetCollapseBorders() != GetCollapseBorders()))
11624 if (attr
.HasVerticalAlignment() && HasVerticalAlignment() && (attr
.GetVerticalAlignment() != GetVerticalAlignment()))
11627 if (attr
.HasBoxStyleName() && HasBoxStyleName() && (attr
.GetBoxStyleName() != GetBoxStyleName()))
11632 if (!m_position
.EqPartial(attr
.m_position
))
11637 if (!m_size
.EqPartial(attr
.m_size
))
11639 if (!m_minSize
.EqPartial(attr
.m_minSize
))
11641 if (!m_maxSize
.EqPartial(attr
.m_maxSize
))
11646 if (!m_margins
.EqPartial(attr
.m_margins
))
11651 if (!m_padding
.EqPartial(attr
.m_padding
))
11656 if (!GetBorder().EqPartial(attr
.GetBorder()))
11661 if (!GetOutline().EqPartial(attr
.GetOutline()))
11667 // Merges the given attributes. If compareWith
11668 // is non-NULL, then it will be used to mask out those attributes that are the same in style
11669 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
11670 bool wxTextBoxAttr::Apply(const wxTextBoxAttr
& attr
, const wxTextBoxAttr
* compareWith
)
11672 if (attr
.HasFloatMode())
11674 if (!(compareWith
&& compareWith
->HasFloatMode() && compareWith
->GetFloatMode() == attr
.GetFloatMode()))
11675 SetFloatMode(attr
.GetFloatMode());
11678 if (attr
.HasClearMode())
11680 if (!(compareWith
&& compareWith
->HasClearMode() && compareWith
->GetClearMode() == attr
.GetClearMode()))
11681 SetClearMode(attr
.GetClearMode());
11684 if (attr
.HasCollapseBorders())
11686 if (!(compareWith
&& compareWith
->HasCollapseBorders() && compareWith
->GetCollapseBorders() == attr
.GetCollapseBorders()))
11687 SetCollapseBorders(attr
.GetCollapseBorders());
11690 if (attr
.HasVerticalAlignment())
11692 if (!(compareWith
&& compareWith
->HasVerticalAlignment() && compareWith
->GetVerticalAlignment() == attr
.GetVerticalAlignment()))
11693 SetVerticalAlignment(attr
.GetVerticalAlignment());
11696 if (attr
.HasBoxStyleName())
11698 if (!(compareWith
&& compareWith
->HasBoxStyleName() && compareWith
->GetBoxStyleName() == attr
.GetBoxStyleName()))
11699 SetBoxStyleName(attr
.GetBoxStyleName());
11702 m_margins
.Apply(attr
.m_margins
, compareWith
? (& attr
.m_margins
) : (const wxTextAttrDimensions
*) NULL
);
11703 m_padding
.Apply(attr
.m_padding
, compareWith
? (& attr
.m_padding
) : (const wxTextAttrDimensions
*) NULL
);
11704 m_position
.Apply(attr
.m_position
, compareWith
? (& attr
.m_position
) : (const wxTextAttrDimensions
*) NULL
);
11706 m_size
.Apply(attr
.m_size
, compareWith
? (& attr
.m_size
) : (const wxTextAttrSize
*) NULL
);
11707 m_minSize
.Apply(attr
.m_minSize
, compareWith
? (& attr
.m_minSize
) : (const wxTextAttrSize
*) NULL
);
11708 m_maxSize
.Apply(attr
.m_maxSize
, compareWith
? (& attr
.m_maxSize
) : (const wxTextAttrSize
*) NULL
);
11710 m_border
.Apply(attr
.m_border
, compareWith
? (& attr
.m_border
) : (const wxTextAttrBorders
*) NULL
);
11711 m_outline
.Apply(attr
.m_outline
, compareWith
? (& attr
.m_outline
) : (const wxTextAttrBorders
*) NULL
);
11716 // Remove specified attributes from this object
11717 bool wxTextBoxAttr::RemoveStyle(const wxTextBoxAttr
& attr
)
11719 if (attr
.HasFloatMode())
11720 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT
);
11722 if (attr
.HasClearMode())
11723 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR
);
11725 if (attr
.HasCollapseBorders())
11726 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
11728 if (attr
.HasVerticalAlignment())
11729 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
11731 if (attr
.HasBoxStyleName())
11733 SetBoxStyleName(wxEmptyString
);
11734 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
11737 m_margins
.RemoveStyle(attr
.m_margins
);
11738 m_padding
.RemoveStyle(attr
.m_padding
);
11739 m_position
.RemoveStyle(attr
.m_position
);
11741 m_size
.RemoveStyle(attr
.m_size
);
11742 m_minSize
.RemoveStyle(attr
.m_minSize
);
11743 m_maxSize
.RemoveStyle(attr
.m_maxSize
);
11745 m_border
.RemoveStyle(attr
.m_border
);
11746 m_outline
.RemoveStyle(attr
.m_outline
);
11751 // Collects the attributes that are common to a range of content, building up a note of
11752 // which attributes are absent in some objects and which clash in some objects.
11753 void wxTextBoxAttr::CollectCommonAttributes(const wxTextBoxAttr
& attr
, wxTextBoxAttr
& clashingAttr
, wxTextBoxAttr
& absentAttr
)
11755 if (attr
.HasFloatMode())
11757 if (!clashingAttr
.HasFloatMode() && !absentAttr
.HasFloatMode())
11759 if (HasFloatMode())
11761 if (GetFloatMode() != attr
.GetFloatMode())
11763 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_FLOAT
);
11764 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT
);
11768 SetFloatMode(attr
.GetFloatMode());
11772 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_FLOAT
);
11774 if (attr
.HasClearMode())
11776 if (!clashingAttr
.HasClearMode() && !absentAttr
.HasClearMode())
11778 if (HasClearMode())
11780 if (GetClearMode() != attr
.GetClearMode())
11782 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_CLEAR
);
11783 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR
);
11787 SetClearMode(attr
.GetClearMode());
11791 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_CLEAR
);
11793 if (attr
.HasCollapseBorders())
11795 if (!clashingAttr
.HasCollapseBorders() && !absentAttr
.HasCollapseBorders())
11797 if (HasCollapseBorders())
11799 if (GetCollapseBorders() != attr
.GetCollapseBorders())
11801 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
11802 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
11806 SetCollapseBorders(attr
.GetCollapseBorders());
11810 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
11812 if (attr
.HasVerticalAlignment())
11814 if (!clashingAttr
.HasVerticalAlignment() && !absentAttr
.HasVerticalAlignment())
11816 if (HasVerticalAlignment())
11818 if (GetVerticalAlignment() != attr
.GetVerticalAlignment())
11820 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
11821 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
11825 SetVerticalAlignment(attr
.GetVerticalAlignment());
11829 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
11831 if (attr
.HasBoxStyleName())
11833 if (!clashingAttr
.HasBoxStyleName() && !absentAttr
.HasBoxStyleName())
11835 if (HasBoxStyleName())
11837 if (GetBoxStyleName() != attr
.GetBoxStyleName())
11839 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
11840 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
11844 SetBoxStyleName(attr
.GetBoxStyleName());
11848 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
11850 m_margins
.CollectCommonAttributes(attr
.m_margins
, clashingAttr
.m_margins
, absentAttr
.m_margins
);
11851 m_padding
.CollectCommonAttributes(attr
.m_padding
, clashingAttr
.m_padding
, absentAttr
.m_padding
);
11852 m_position
.CollectCommonAttributes(attr
.m_position
, clashingAttr
.m_position
, absentAttr
.m_position
);
11854 m_size
.CollectCommonAttributes(attr
.m_size
, clashingAttr
.m_size
, absentAttr
.m_size
);
11855 m_minSize
.CollectCommonAttributes(attr
.m_minSize
, clashingAttr
.m_minSize
, absentAttr
.m_minSize
);
11856 m_maxSize
.CollectCommonAttributes(attr
.m_maxSize
, clashingAttr
.m_maxSize
, absentAttr
.m_maxSize
);
11858 m_border
.CollectCommonAttributes(attr
.m_border
, clashingAttr
.m_border
, absentAttr
.m_border
);
11859 m_outline
.CollectCommonAttributes(attr
.m_outline
, clashingAttr
.m_outline
, absentAttr
.m_outline
);
11862 bool wxTextBoxAttr::IsDefault() const
11864 return GetFlags() == 0 && !m_border
.IsValid() && !m_outline
.IsValid() &&
11865 !m_size
.IsValid() && !m_minSize
.IsValid() && !m_maxSize
.IsValid() &&
11866 !m_position
.IsValid() && !m_padding
.IsValid() && !m_margins
.IsValid();
11871 void wxRichTextAttr::Copy(const wxRichTextAttr
& attr
)
11873 wxTextAttr::Copy(attr
);
11875 m_textBoxAttr
= attr
.m_textBoxAttr
;
11878 bool wxRichTextAttr::operator==(const wxRichTextAttr
& attr
) const
11880 if (!(wxTextAttr::operator==(attr
)))
11883 return (m_textBoxAttr
== attr
.m_textBoxAttr
);
11886 // Partial equality test taking comparison object into account
11887 bool wxRichTextAttr::EqPartial(const wxRichTextAttr
& attr
) const
11889 if (!(wxTextAttr::EqPartial(attr
)))
11892 return m_textBoxAttr
.EqPartial(attr
.m_textBoxAttr
);
11895 // Merges the given attributes. If compareWith
11896 // is non-NULL, then it will be used to mask out those attributes that are the same in style
11897 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
11898 bool wxRichTextAttr::Apply(const wxRichTextAttr
& style
, const wxRichTextAttr
* compareWith
)
11900 wxTextAttr::Apply(style
, compareWith
);
11902 return m_textBoxAttr
.Apply(style
.m_textBoxAttr
, compareWith
? (& compareWith
->m_textBoxAttr
) : (const wxTextBoxAttr
*) NULL
);
11905 // Remove specified attributes from this object
11906 bool wxRichTextAttr::RemoveStyle(const wxRichTextAttr
& attr
)
11908 wxTextAttr::RemoveStyle(*this, attr
);
11910 return m_textBoxAttr
.RemoveStyle(attr
.m_textBoxAttr
);
11913 // Collects the attributes that are common to a range of content, building up a note of
11914 // which attributes are absent in some objects and which clash in some objects.
11915 void wxRichTextAttr::CollectCommonAttributes(const wxRichTextAttr
& attr
, wxRichTextAttr
& clashingAttr
, wxRichTextAttr
& absentAttr
)
11917 wxTextAttrCollectCommonAttributes(*this, attr
, clashingAttr
, absentAttr
);
11919 m_textBoxAttr
.CollectCommonAttributes(attr
.m_textBoxAttr
, clashingAttr
.m_textBoxAttr
, absentAttr
.m_textBoxAttr
);
11922 // Partial equality test
11923 bool wxTextAttrBorder::EqPartial(const wxTextAttrBorder
& border
) const
11925 if (border
.HasStyle() && !HasStyle() && (border
.GetStyle() != GetStyle()))
11928 if (border
.HasColour() && !HasColour() && (border
.GetColourLong() != GetColourLong()))
11931 if (border
.HasWidth() && !HasWidth() && !(border
.GetWidth() == GetWidth()))
11937 // Apply border to 'this', but not if the same as compareWith
11938 bool wxTextAttrBorder::Apply(const wxTextAttrBorder
& border
, const wxTextAttrBorder
* compareWith
)
11940 if (border
.HasStyle())
11942 if (!(compareWith
&& (border
.GetStyle() == compareWith
->GetStyle())))
11943 SetStyle(border
.GetStyle());
11945 if (border
.HasColour())
11947 if (!(compareWith
&& (border
.GetColourLong() == compareWith
->GetColourLong())))
11948 SetColour(border
.GetColourLong());
11950 if (border
.HasWidth())
11952 if (!(compareWith
&& (border
.GetWidth() == compareWith
->GetWidth())))
11953 SetWidth(border
.GetWidth());
11959 // Remove specified attributes from this object
11960 bool wxTextAttrBorder::RemoveStyle(const wxTextAttrBorder
& attr
)
11962 if (attr
.HasStyle() && HasStyle())
11963 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_STYLE
);
11964 if (attr
.HasColour() && HasColour())
11965 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_COLOUR
);
11966 if (attr
.HasWidth() && HasWidth())
11967 m_borderWidth
.Reset();
11972 // Collects the attributes that are common to a range of content, building up a note of
11973 // which attributes are absent in some objects and which clash in some objects.
11974 void wxTextAttrBorder::CollectCommonAttributes(const wxTextAttrBorder
& attr
, wxTextAttrBorder
& clashingAttr
, wxTextAttrBorder
& absentAttr
)
11976 if (attr
.HasStyle())
11978 if (!clashingAttr
.HasStyle() && !absentAttr
.HasStyle())
11982 if (GetStyle() != attr
.GetStyle())
11984 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
11985 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
11989 SetStyle(attr
.GetStyle());
11993 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
11995 if (attr
.HasColour())
11997 if (!clashingAttr
.HasColour() && !absentAttr
.HasColour())
12001 if (GetColour() != attr
.GetColour())
12003 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
12004 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
12008 SetColour(attr
.GetColourLong());
12012 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
12014 m_borderWidth
.CollectCommonAttributes(attr
.m_borderWidth
, clashingAttr
.m_borderWidth
, absentAttr
.m_borderWidth
);
12017 // Partial equality test
12018 bool wxTextAttrBorders::EqPartial(const wxTextAttrBorders
& borders
) const
12020 return m_left
.EqPartial(borders
.m_left
) && m_right
.EqPartial(borders
.m_right
) &&
12021 m_top
.EqPartial(borders
.m_top
) && m_bottom
.EqPartial(borders
.m_bottom
);
12024 // Apply border to 'this', but not if the same as compareWith
12025 bool wxTextAttrBorders::Apply(const wxTextAttrBorders
& borders
, const wxTextAttrBorders
* compareWith
)
12027 m_left
.Apply(borders
.m_left
, compareWith
? (& compareWith
->m_left
) : (const wxTextAttrBorder
*) NULL
);
12028 m_right
.Apply(borders
.m_right
, compareWith
? (& compareWith
->m_right
) : (const wxTextAttrBorder
*) NULL
);
12029 m_top
.Apply(borders
.m_top
, compareWith
? (& compareWith
->m_top
) : (const wxTextAttrBorder
*) NULL
);
12030 m_bottom
.Apply(borders
.m_bottom
, compareWith
? (& compareWith
->m_bottom
) : (const wxTextAttrBorder
*) NULL
);
12034 // Remove specified attributes from this object
12035 bool wxTextAttrBorders::RemoveStyle(const wxTextAttrBorders
& attr
)
12037 m_left
.RemoveStyle(attr
.m_left
);
12038 m_right
.RemoveStyle(attr
.m_right
);
12039 m_top
.RemoveStyle(attr
.m_top
);
12040 m_bottom
.RemoveStyle(attr
.m_bottom
);
12044 // Collects the attributes that are common to a range of content, building up a note of
12045 // which attributes are absent in some objects and which clash in some objects.
12046 void wxTextAttrBorders::CollectCommonAttributes(const wxTextAttrBorders
& attr
, wxTextAttrBorders
& clashingAttr
, wxTextAttrBorders
& absentAttr
)
12048 m_left
.CollectCommonAttributes(attr
.m_left
, clashingAttr
.m_left
, absentAttr
.m_left
);
12049 m_right
.CollectCommonAttributes(attr
.m_right
, clashingAttr
.m_right
, absentAttr
.m_right
);
12050 m_top
.CollectCommonAttributes(attr
.m_top
, clashingAttr
.m_top
, absentAttr
.m_top
);
12051 m_bottom
.CollectCommonAttributes(attr
.m_bottom
, clashingAttr
.m_bottom
, absentAttr
.m_bottom
);
12054 // Set style of all borders
12055 void wxTextAttrBorders::SetStyle(int style
)
12057 m_left
.SetStyle(style
);
12058 m_right
.SetStyle(style
);
12059 m_top
.SetStyle(style
);
12060 m_bottom
.SetStyle(style
);
12063 // Set colour of all borders
12064 void wxTextAttrBorders::SetColour(unsigned long colour
)
12066 m_left
.SetColour(colour
);
12067 m_right
.SetColour(colour
);
12068 m_top
.SetColour(colour
);
12069 m_bottom
.SetColour(colour
);
12072 void wxTextAttrBorders::SetColour(const wxColour
& colour
)
12074 m_left
.SetColour(colour
);
12075 m_right
.SetColour(colour
);
12076 m_top
.SetColour(colour
);
12077 m_bottom
.SetColour(colour
);
12080 // Set width of all borders
12081 void wxTextAttrBorders::SetWidth(const wxTextAttrDimension
& width
)
12083 m_left
.SetWidth(width
);
12084 m_right
.SetWidth(width
);
12085 m_top
.SetWidth(width
);
12086 m_bottom
.SetWidth(width
);
12089 // Partial equality test
12090 bool wxTextAttrDimension::EqPartial(const wxTextAttrDimension
& dim
) const
12092 if (dim
.IsValid() && IsValid() && !((*this) == dim
))
12098 bool wxTextAttrDimension::Apply(const wxTextAttrDimension
& dim
, const wxTextAttrDimension
* compareWith
)
12102 if (!(compareWith
&& dim
== (*compareWith
)))
12109 // Collects the attributes that are common to a range of content, building up a note of
12110 // which attributes are absent in some objects and which clash in some objects.
12111 void wxTextAttrDimension::CollectCommonAttributes(const wxTextAttrDimension
& attr
, wxTextAttrDimension
& clashingAttr
, wxTextAttrDimension
& absentAttr
)
12113 if (attr
.IsValid())
12115 if (!clashingAttr
.IsValid() && !absentAttr
.IsValid())
12119 if (!((*this) == attr
))
12121 clashingAttr
.SetValid(true);
12130 absentAttr
.SetValid(true);
12133 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(wxDC
& dc
, double scale
, const wxSize
& parentSize
)
12135 m_ppi
= dc
.GetPPI().x
; m_scale
= scale
; m_parentSize
= parentSize
;
12138 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(int ppi
, double scale
, const wxSize
& parentSize
)
12140 m_ppi
= ppi
; m_scale
= scale
; m_parentSize
= parentSize
;
12143 int wxTextAttrDimensionConverter::ConvertTenthsMMToPixels(int units
) const
12145 return wxRichTextObject::ConvertTenthsMMToPixels(m_ppi
, units
, m_scale
);
12148 int wxTextAttrDimensionConverter::ConvertPixelsToTenthsMM(int pixels
) const
12150 return wxRichTextObject::ConvertPixelsToTenthsMM(m_ppi
, pixels
, m_scale
);
12153 int wxTextAttrDimensionConverter::GetPixels(const wxTextAttrDimension
& dim
, int direction
) const
12155 if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
12156 return ConvertTenthsMMToPixels(dim
.GetValue());
12157 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
12158 return dim
.GetValue();
12159 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
12161 wxASSERT(m_parentSize
!= wxDefaultSize
);
12162 if (direction
== wxHORIZONTAL
)
12163 return (int) (double(m_parentSize
.x
) * double(dim
.GetValue()) / 100.0);
12165 return (int) (double(m_parentSize
.y
) * double(dim
.GetValue()) / 100.0);
12174 int wxTextAttrDimensionConverter::GetTenthsMM(const wxTextAttrDimension
& dim
) const
12176 if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
12177 return dim
.GetValue();
12178 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
12179 return ConvertPixelsToTenthsMM(dim
.GetValue());
12187 // Partial equality test
12188 bool wxTextAttrDimensions::EqPartial(const wxTextAttrDimensions
& dims
) const
12190 if (!m_left
.EqPartial(dims
.m_left
))
12193 if (!m_right
.EqPartial(dims
.m_right
))
12196 if (!m_top
.EqPartial(dims
.m_top
))
12199 if (!m_bottom
.EqPartial(dims
.m_bottom
))
12205 // Apply border to 'this', but not if the same as compareWith
12206 bool wxTextAttrDimensions::Apply(const wxTextAttrDimensions
& dims
, const wxTextAttrDimensions
* compareWith
)
12208 m_left
.Apply(dims
.m_left
, compareWith
? (& compareWith
->m_left
) : (const wxTextAttrDimension
*) NULL
);
12209 m_right
.Apply(dims
.m_right
, compareWith
? (& compareWith
->m_right
): (const wxTextAttrDimension
*) NULL
);
12210 m_top
.Apply(dims
.m_top
, compareWith
? (& compareWith
->m_top
): (const wxTextAttrDimension
*) NULL
);
12211 m_bottom
.Apply(dims
.m_bottom
, compareWith
? (& compareWith
->m_bottom
): (const wxTextAttrDimension
*) NULL
);
12216 // Remove specified attributes from this object
12217 bool wxTextAttrDimensions::RemoveStyle(const wxTextAttrDimensions
& attr
)
12219 if (attr
.m_left
.IsValid())
12221 if (attr
.m_right
.IsValid())
12223 if (attr
.m_top
.IsValid())
12225 if (attr
.m_bottom
.IsValid())
12231 // Collects the attributes that are common to a range of content, building up a note of
12232 // which attributes are absent in some objects and which clash in some objects.
12233 void wxTextAttrDimensions::CollectCommonAttributes(const wxTextAttrDimensions
& attr
, wxTextAttrDimensions
& clashingAttr
, wxTextAttrDimensions
& absentAttr
)
12235 m_left
.CollectCommonAttributes(attr
.m_left
, clashingAttr
.m_left
, absentAttr
.m_left
);
12236 m_right
.CollectCommonAttributes(attr
.m_right
, clashingAttr
.m_right
, absentAttr
.m_right
);
12237 m_top
.CollectCommonAttributes(attr
.m_top
, clashingAttr
.m_top
, absentAttr
.m_top
);
12238 m_bottom
.CollectCommonAttributes(attr
.m_bottom
, clashingAttr
.m_bottom
, absentAttr
.m_bottom
);
12241 // Partial equality test
12242 bool wxTextAttrSize::EqPartial(const wxTextAttrSize
& size
) const
12244 if (!m_width
.EqPartial(size
.m_width
))
12247 if (!m_height
.EqPartial(size
.m_height
))
12253 // Apply border to 'this', but not if the same as compareWith
12254 bool wxTextAttrSize::Apply(const wxTextAttrSize
& size
, const wxTextAttrSize
* compareWith
)
12256 m_width
.Apply(size
.m_width
, compareWith
? (& compareWith
->m_width
) : (const wxTextAttrDimension
*) NULL
);
12257 m_height
.Apply(size
.m_height
, compareWith
? (& compareWith
->m_height
): (const wxTextAttrDimension
*) NULL
);
12262 // Remove specified attributes from this object
12263 bool wxTextAttrSize::RemoveStyle(const wxTextAttrSize
& attr
)
12265 if (attr
.m_width
.IsValid())
12267 if (attr
.m_height
.IsValid())
12273 // Collects the attributes that are common to a range of content, building up a note of
12274 // which attributes are absent in some objects and which clash in some objects.
12275 void wxTextAttrSize::CollectCommonAttributes(const wxTextAttrSize
& attr
, wxTextAttrSize
& clashingAttr
, wxTextAttrSize
& absentAttr
)
12277 m_width
.CollectCommonAttributes(attr
.m_width
, clashingAttr
.m_width
, absentAttr
.m_width
);
12278 m_height
.CollectCommonAttributes(attr
.m_height
, clashingAttr
.m_height
, absentAttr
.m_height
);
12281 // Collects the attributes that are common to a range of content, building up a note of
12282 // which attributes are absent in some objects and which clash in some objects.
12283 void wxTextAttrCollectCommonAttributes(wxTextAttr
& currentStyle
, const wxTextAttr
& attr
, wxTextAttr
& clashingAttr
, wxTextAttr
& absentAttr
)
12285 absentAttr
.SetFlags(absentAttr
.GetFlags() | (~attr
.GetFlags() & wxTEXT_ATTR_ALL
));
12286 absentAttr
.SetTextEffectFlags(absentAttr
.GetTextEffectFlags() | (~attr
.GetTextEffectFlags() & 0xFFFF));
12288 long forbiddenFlags
= clashingAttr
.GetFlags()|absentAttr
.GetFlags();
12290 if (attr
.HasFont())
12292 if (attr
.HasFontSize() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_SIZE
))
12294 if (currentStyle
.HasFontSize())
12296 if (currentStyle
.GetFontSize() != attr
.GetFontSize())
12298 // Clash of attr - mark as such
12299 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_SIZE
);
12300 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_SIZE
);
12304 currentStyle
.SetFontSize(attr
.GetFontSize());
12307 if (attr
.HasFontItalic() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_ITALIC
))
12309 if (currentStyle
.HasFontItalic())
12311 if (currentStyle
.GetFontStyle() != attr
.GetFontStyle())
12313 // Clash of attr - mark as such
12314 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_ITALIC
);
12315 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC
);
12319 currentStyle
.SetFontStyle(attr
.GetFontStyle());
12322 if (attr
.HasFontFamily() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_FAMILY
))
12324 if (currentStyle
.HasFontFamily())
12326 if (currentStyle
.GetFontFamily() != attr
.GetFontFamily())
12328 // Clash of attr - mark as such
12329 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FAMILY
);
12330 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY
);
12334 currentStyle
.SetFontFamily(attr
.GetFontFamily());
12337 if (attr
.HasFontWeight() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_WEIGHT
))
12339 if (currentStyle
.HasFontWeight())
12341 if (currentStyle
.GetFontWeight() != attr
.GetFontWeight())
12343 // Clash of attr - mark as such
12344 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_WEIGHT
);
12345 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT
);
12349 currentStyle
.SetFontWeight(attr
.GetFontWeight());
12352 if (attr
.HasFontFaceName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_FACE
))
12354 if (currentStyle
.HasFontFaceName())
12356 wxString
faceName1(currentStyle
.GetFontFaceName());
12357 wxString
faceName2(attr
.GetFontFaceName());
12359 if (faceName1
!= faceName2
)
12361 // Clash of attr - mark as such
12362 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FACE
);
12363 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FACE
);
12367 currentStyle
.SetFontFaceName(attr
.GetFontFaceName());
12370 if (attr
.HasFontUnderlined() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_UNDERLINE
))
12372 if (currentStyle
.HasFontUnderlined())
12374 if (currentStyle
.GetFontUnderlined() != attr
.GetFontUnderlined())
12376 // Clash of attr - mark as such
12377 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
12378 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
12382 currentStyle
.SetFontUnderlined(attr
.GetFontUnderlined());
12386 if (attr
.HasTextColour() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_TEXT_COLOUR
))
12388 if (currentStyle
.HasTextColour())
12390 if (currentStyle
.GetTextColour() != attr
.GetTextColour())
12392 // Clash of attr - mark as such
12393 clashingAttr
.AddFlag(wxTEXT_ATTR_TEXT_COLOUR
);
12394 currentStyle
.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR
);
12398 currentStyle
.SetTextColour(attr
.GetTextColour());
12401 if (attr
.HasBackgroundColour() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BACKGROUND_COLOUR
))
12403 if (currentStyle
.HasBackgroundColour())
12405 if (currentStyle
.GetBackgroundColour() != attr
.GetBackgroundColour())
12407 // Clash of attr - mark as such
12408 clashingAttr
.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
12409 currentStyle
.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
12413 currentStyle
.SetBackgroundColour(attr
.GetBackgroundColour());
12416 if (attr
.HasAlignment() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_ALIGNMENT
))
12418 if (currentStyle
.HasAlignment())
12420 if (currentStyle
.GetAlignment() != attr
.GetAlignment())
12422 // Clash of attr - mark as such
12423 clashingAttr
.AddFlag(wxTEXT_ATTR_ALIGNMENT
);
12424 currentStyle
.RemoveFlag(wxTEXT_ATTR_ALIGNMENT
);
12428 currentStyle
.SetAlignment(attr
.GetAlignment());
12431 if (attr
.HasTabs() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_TABS
))
12433 if (currentStyle
.HasTabs())
12435 if (!wxRichTextTabsEq(currentStyle
.GetTabs(), attr
.GetTabs()))
12437 // Clash of attr - mark as such
12438 clashingAttr
.AddFlag(wxTEXT_ATTR_TABS
);
12439 currentStyle
.RemoveFlag(wxTEXT_ATTR_TABS
);
12443 currentStyle
.SetTabs(attr
.GetTabs());
12446 if (attr
.HasLeftIndent() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LEFT_INDENT
))
12448 if (currentStyle
.HasLeftIndent())
12450 if (currentStyle
.GetLeftIndent() != attr
.GetLeftIndent() || currentStyle
.GetLeftSubIndent() != attr
.GetLeftSubIndent())
12452 // Clash of attr - mark as such
12453 clashingAttr
.AddFlag(wxTEXT_ATTR_LEFT_INDENT
);
12454 currentStyle
.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT
);
12458 currentStyle
.SetLeftIndent(attr
.GetLeftIndent(), attr
.GetLeftSubIndent());
12461 if (attr
.HasRightIndent() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_RIGHT_INDENT
))
12463 if (currentStyle
.HasRightIndent())
12465 if (currentStyle
.GetRightIndent() != attr
.GetRightIndent())
12467 // Clash of attr - mark as such
12468 clashingAttr
.AddFlag(wxTEXT_ATTR_RIGHT_INDENT
);
12469 currentStyle
.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT
);
12473 currentStyle
.SetRightIndent(attr
.GetRightIndent());
12476 if (attr
.HasParagraphSpacingAfter() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARA_SPACING_AFTER
))
12478 if (currentStyle
.HasParagraphSpacingAfter())
12480 if (currentStyle
.GetParagraphSpacingAfter() != attr
.GetParagraphSpacingAfter())
12482 // Clash of attr - mark as such
12483 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
12484 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
12488 currentStyle
.SetParagraphSpacingAfter(attr
.GetParagraphSpacingAfter());
12491 if (attr
.HasParagraphSpacingBefore() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARA_SPACING_BEFORE
))
12493 if (currentStyle
.HasParagraphSpacingBefore())
12495 if (currentStyle
.GetParagraphSpacingBefore() != attr
.GetParagraphSpacingBefore())
12497 // Clash of attr - mark as such
12498 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
12499 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
12503 currentStyle
.SetParagraphSpacingBefore(attr
.GetParagraphSpacingBefore());
12506 if (attr
.HasLineSpacing() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LINE_SPACING
))
12508 if (currentStyle
.HasLineSpacing())
12510 if (currentStyle
.GetLineSpacing() != attr
.GetLineSpacing())
12512 // Clash of attr - mark as such
12513 clashingAttr
.AddFlag(wxTEXT_ATTR_LINE_SPACING
);
12514 currentStyle
.RemoveFlag(wxTEXT_ATTR_LINE_SPACING
);
12518 currentStyle
.SetLineSpacing(attr
.GetLineSpacing());
12521 if (attr
.HasCharacterStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_CHARACTER_STYLE_NAME
))
12523 if (currentStyle
.HasCharacterStyleName())
12525 if (currentStyle
.GetCharacterStyleName() != attr
.GetCharacterStyleName())
12527 // Clash of attr - mark as such
12528 clashingAttr
.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
12529 currentStyle
.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
12533 currentStyle
.SetCharacterStyleName(attr
.GetCharacterStyleName());
12536 if (attr
.HasParagraphStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
))
12538 if (currentStyle
.HasParagraphStyleName())
12540 if (currentStyle
.GetParagraphStyleName() != attr
.GetParagraphStyleName())
12542 // Clash of attr - mark as such
12543 clashingAttr
.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
12544 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
12548 currentStyle
.SetParagraphStyleName(attr
.GetParagraphStyleName());
12551 if (attr
.HasListStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LIST_STYLE_NAME
))
12553 if (currentStyle
.HasListStyleName())
12555 if (currentStyle
.GetListStyleName() != attr
.GetListStyleName())
12557 // Clash of attr - mark as such
12558 clashingAttr
.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
12559 currentStyle
.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
12563 currentStyle
.SetListStyleName(attr
.GetListStyleName());
12566 if (attr
.HasBulletStyle() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_STYLE
))
12568 if (currentStyle
.HasBulletStyle())
12570 if (currentStyle
.GetBulletStyle() != attr
.GetBulletStyle())
12572 // Clash of attr - mark as such
12573 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_STYLE
);
12574 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE
);
12578 currentStyle
.SetBulletStyle(attr
.GetBulletStyle());
12581 if (attr
.HasBulletNumber() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_NUMBER
))
12583 if (currentStyle
.HasBulletNumber())
12585 if (currentStyle
.GetBulletNumber() != attr
.GetBulletNumber())
12587 // Clash of attr - mark as such
12588 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NUMBER
);
12589 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER
);
12593 currentStyle
.SetBulletNumber(attr
.GetBulletNumber());
12596 if (attr
.HasBulletText() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_TEXT
))
12598 if (currentStyle
.HasBulletText())
12600 if (currentStyle
.GetBulletText() != attr
.GetBulletText())
12602 // Clash of attr - mark as such
12603 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_TEXT
);
12604 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT
);
12609 currentStyle
.SetBulletText(attr
.GetBulletText());
12610 currentStyle
.SetBulletFont(attr
.GetBulletFont());
12614 if (attr
.HasBulletName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_NAME
))
12616 if (currentStyle
.HasBulletName())
12618 if (currentStyle
.GetBulletName() != attr
.GetBulletName())
12620 // Clash of attr - mark as such
12621 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NAME
);
12622 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NAME
);
12627 currentStyle
.SetBulletName(attr
.GetBulletName());
12631 if (attr
.HasURL() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_URL
))
12633 if (currentStyle
.HasURL())
12635 if (currentStyle
.GetURL() != attr
.GetURL())
12637 // Clash of attr - mark as such
12638 clashingAttr
.AddFlag(wxTEXT_ATTR_URL
);
12639 currentStyle
.RemoveFlag(wxTEXT_ATTR_URL
);
12644 currentStyle
.SetURL(attr
.GetURL());
12648 if (attr
.HasTextEffects() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_EFFECTS
))
12650 if (currentStyle
.HasTextEffects())
12652 // We need to find the bits in the new attr that are different:
12653 // just look at those bits that are specified by the new attr.
12655 // We need to remove the bits and flags that are not common between current attr
12656 // and new attr. In so doing we need to take account of the styles absent from one or more of the
12657 // previous styles.
12659 int currentRelevantTextEffects
= currentStyle
.GetTextEffects() & attr
.GetTextEffectFlags();
12660 int newRelevantTextEffects
= attr
.GetTextEffects() & attr
.GetTextEffectFlags();
12662 if (currentRelevantTextEffects
!= newRelevantTextEffects
)
12664 // Find the text effects that were different, using XOR
12665 int differentEffects
= currentRelevantTextEffects
^ newRelevantTextEffects
;
12667 // Clash of attr - mark as such
12668 clashingAttr
.SetTextEffectFlags(clashingAttr
.GetTextEffectFlags() | differentEffects
);
12669 currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~differentEffects
);
12674 currentStyle
.SetTextEffects(attr
.GetTextEffects());
12675 currentStyle
.SetTextEffectFlags(attr
.GetTextEffectFlags());
12678 // Mask out the flags and values that cannot be common because they were absent in one or more objecrs
12679 // that we've looked at so far
12680 currentStyle
.SetTextEffects(currentStyle
.GetTextEffects() & ~absentAttr
.GetTextEffectFlags());
12681 currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~absentAttr
.GetTextEffectFlags());
12683 if (currentStyle
.GetTextEffectFlags() == 0)
12684 currentStyle
.RemoveFlag(wxTEXT_ATTR_EFFECTS
);
12687 if (attr
.HasOutlineLevel() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_OUTLINE_LEVEL
))
12689 if (currentStyle
.HasOutlineLevel())
12691 if (currentStyle
.GetOutlineLevel() != attr
.GetOutlineLevel())
12693 // Clash of attr - mark as such
12694 clashingAttr
.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
12695 currentStyle
.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
12699 currentStyle
.SetOutlineLevel(attr
.GetOutlineLevel());
12703 WX_DEFINE_OBJARRAY(wxRichTextVariantArray
);
12705 IMPLEMENT_DYNAMIC_CLASS(wxRichTextProperties
, wxObject
)
12707 bool wxRichTextProperties::operator==(const wxRichTextProperties
& props
) const
12709 if (m_properties
.GetCount() != props
.GetCount())
12713 for (i
= 0; i
< m_properties
.GetCount(); i
++)
12715 const wxVariant
& var1
= m_properties
[i
];
12716 int idx
= props
.Find(var1
.GetName());
12719 const wxVariant
& var2
= props
.m_properties
[idx
];
12720 if (!(var1
== var2
))
12727 wxArrayString
wxRichTextProperties::GetPropertyNames() const
12731 for (i
= 0; i
< m_properties
.GetCount(); i
++)
12733 arr
.Add(m_properties
[i
].GetName());
12738 int wxRichTextProperties::Find(const wxString
& name
) const
12741 for (i
= 0; i
< m_properties
.GetCount(); i
++)
12743 if (m_properties
[i
].GetName() == name
)
12749 bool wxRichTextProperties::Remove(const wxString
& name
)
12751 int idx
= Find(name
);
12754 m_properties
.RemoveAt(idx
);
12761 wxVariant
* wxRichTextProperties::FindOrCreateProperty(const wxString
& name
)
12763 int idx
= Find(name
);
12764 if (idx
== wxNOT_FOUND
)
12765 SetProperty(name
, wxString());
12767 if (idx
!= wxNOT_FOUND
)
12769 return & (*this)[idx
];
12775 const wxVariant
& wxRichTextProperties::GetProperty(const wxString
& name
) const
12777 static const wxVariant nullVariant
;
12778 int idx
= Find(name
);
12780 return m_properties
[idx
];
12782 return nullVariant
;
12785 wxString
wxRichTextProperties::GetPropertyString(const wxString
& name
) const
12787 return GetProperty(name
).GetString();
12790 long wxRichTextProperties::GetPropertyLong(const wxString
& name
) const
12792 return GetProperty(name
).GetLong();
12795 bool wxRichTextProperties::GetPropertyBool(const wxString
& name
) const
12797 return GetProperty(name
).GetBool();
12800 double wxRichTextProperties::GetPropertyDouble(const wxString
& name
) const
12802 return GetProperty(name
).GetDouble();
12805 void wxRichTextProperties::SetProperty(const wxVariant
& variant
)
12807 wxASSERT(!variant
.GetName().IsEmpty());
12809 int idx
= Find(variant
.GetName());
12812 m_properties
.Add(variant
);
12814 m_properties
[idx
] = variant
;
12817 void wxRichTextProperties::SetProperty(const wxString
& name
, const wxVariant
& variant
)
12819 int idx
= Find(name
);
12820 wxVariant
var(variant
);
12824 m_properties
.Add(var
);
12826 m_properties
[idx
] = var
;
12829 void wxRichTextProperties::SetProperty(const wxString
& name
, const wxString
& value
)
12831 SetProperty(name
, wxVariant(value
, name
));
12834 void wxRichTextProperties::SetProperty(const wxString
& name
, long value
)
12836 SetProperty(name
, wxVariant(value
, name
));
12839 void wxRichTextProperties::SetProperty(const wxString
& name
, double value
)
12841 SetProperty(name
, wxVariant(value
, name
));
12844 void wxRichTextProperties::SetProperty(const wxString
& name
, bool value
)
12846 SetProperty(name
, wxVariant(value
, name
));
12849 void wxRichTextProperties::RemoveProperties(const wxRichTextProperties
& properties
)
12852 for (i
= 0; i
< properties
.GetCount(); i
++)
12854 wxString name
= properties
.GetProperties()[i
].GetName();
12855 if (HasProperty(name
))
12860 void wxRichTextProperties::MergeProperties(const wxRichTextProperties
& properties
)
12863 for (i
= 0; i
< properties
.GetCount(); i
++)
12865 SetProperty(properties
.GetProperties()[i
]);
12869 wxRichTextObject
* wxRichTextObjectAddress::GetObject(wxRichTextParagraphLayoutBox
* topLevelContainer
) const
12871 if (m_address
.GetCount() == 0)
12872 return topLevelContainer
;
12874 wxRichTextCompositeObject
* p
= topLevelContainer
;
12876 while (p
&& i
< m_address
.GetCount())
12878 int pos
= m_address
[i
];
12879 wxASSERT(pos
>= 0 && pos
< (int) p
->GetChildren().GetCount());
12880 if (pos
< 0 || pos
>= (int) p
->GetChildren().GetCount())
12883 wxRichTextObject
* p1
= p
->GetChild(pos
);
12884 if (i
== (m_address
.GetCount()-1))
12887 p
= wxDynamicCast(p1
, wxRichTextCompositeObject
);
12893 bool wxRichTextObjectAddress::Create(wxRichTextParagraphLayoutBox
* topLevelContainer
, wxRichTextObject
* obj
)
12897 if (topLevelContainer
== obj
)
12900 wxRichTextObject
* o
= obj
;
12903 wxRichTextCompositeObject
* p
= wxDynamicCast(o
->GetParent(), wxRichTextCompositeObject
);
12907 int pos
= p
->GetChildren().IndexOf(o
);
12911 m_address
.Insert(pos
, 0);
12913 if (p
== topLevelContainer
)
12922 bool wxRichTextSelection::operator==(const wxRichTextSelection
& sel
) const
12924 if (m_container
!= sel
.m_container
)
12926 if (m_ranges
.GetCount() != sel
.m_ranges
.GetCount())
12929 for (i
= 0; i
< m_ranges
.GetCount(); i
++)
12930 if (!(m_ranges
[i
] == sel
.m_ranges
[i
]))
12935 // Get the selections appropriate to the specified object, if any; returns wxRICHTEXT_NO_SELECTION if none
12936 // or none at the level of the object's container.
12937 wxRichTextRangeArray
wxRichTextSelection::GetSelectionForObject(wxRichTextObject
* obj
) const
12941 wxRichTextParagraphLayoutBox
* container
= obj
->GetParentContainer();
12943 if (container
== m_container
)
12946 container
= obj
->GetContainer();
12949 if (container
->GetParent())
12951 // If we found that our object's container is within the range of
12952 // a selection higher up, then assume the whole original object
12953 // is also selected.
12954 wxRichTextParagraphLayoutBox
* parentContainer
= container
->GetParentContainer();
12955 if (parentContainer
== m_container
)
12957 if (WithinSelection(container
->GetRange().GetStart(), m_ranges
))
12959 wxRichTextRangeArray ranges
;
12960 ranges
.Add(obj
->GetRange());
12965 container
= parentContainer
;
12974 return wxRichTextRangeArray();
12977 // Is the given position within the selection?
12978 bool wxRichTextSelection::WithinSelection(long pos
, wxRichTextObject
* obj
) const
12984 wxRichTextRangeArray selectionRanges
= GetSelectionForObject(obj
);
12985 return WithinSelection(pos
, selectionRanges
);
12989 // Is the given position within the selection range?
12990 bool wxRichTextSelection::WithinSelection(long pos
, const wxRichTextRangeArray
& ranges
)
12993 for (i
= 0; i
< ranges
.GetCount(); i
++)
12995 const wxRichTextRange
& range
= ranges
[i
];
12996 if (pos
>= range
.GetStart() && pos
<= range
.GetEnd())
13002 // Is the given range completely within the selection range?
13003 bool wxRichTextSelection::WithinSelection(const wxRichTextRange
& range
, const wxRichTextRangeArray
& ranges
)
13006 for (i
= 0; i
< ranges
.GetCount(); i
++)
13008 const wxRichTextRange
& eachRange
= ranges
[i
];
13009 if (range
.IsWithin(eachRange
))
13015 IMPLEMENT_CLASS(wxRichTextDrawingHandler
, wxObject
)
13016 IMPLEMENT_CLASS(wxRichTextDrawingContext
, wxObject
)
13018 bool wxRichTextDrawingContext::HasVirtualAttributes(wxRichTextObject
* obj
) const
13020 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
13023 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13024 if (handler
->HasVirtualAttributes(obj
))
13027 node
= node
->GetNext();
13032 wxRichTextAttr
wxRichTextDrawingContext::GetVirtualAttributes(wxRichTextObject
* obj
) const
13034 wxRichTextAttr attr
;
13035 // We apply all handlers, so we can may combine several different attributes
13036 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
13039 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13040 if (handler
->HasVirtualAttributes(obj
))
13042 bool success
= handler
->GetVirtualAttributes(attr
, obj
);
13044 wxUnusedVar(success
);
13047 node
= node
->GetNext();
13052 bool wxRichTextDrawingContext::ApplyVirtualAttributes(wxRichTextAttr
& attr
, wxRichTextObject
* obj
) const
13054 if (HasVirtualAttributes(obj
))
13056 wxRichTextAttr
a(GetVirtualAttributes(obj
));
13064 /// Adds a handler to the end
13065 void wxRichTextBuffer::AddDrawingHandler(wxRichTextDrawingHandler
*handler
)
13067 sm_drawingHandlers
.Append(handler
);
13070 /// Inserts a handler at the front
13071 void wxRichTextBuffer::InsertDrawingHandler(wxRichTextDrawingHandler
*handler
)
13073 sm_drawingHandlers
.Insert( handler
);
13076 /// Removes a handler
13077 bool wxRichTextBuffer::RemoveDrawingHandler(const wxString
& name
)
13079 wxRichTextDrawingHandler
*handler
= FindDrawingHandler(name
);
13082 sm_drawingHandlers
.DeleteObject(handler
);
13090 wxRichTextDrawingHandler
* wxRichTextBuffer::FindDrawingHandler(const wxString
& name
)
13092 wxList::compatibility_iterator node
= sm_drawingHandlers
.GetFirst();
13095 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13096 if (handler
->GetName().Lower() == name
.Lower()) return handler
;
13098 node
= node
->GetNext();
13103 void wxRichTextBuffer::CleanUpDrawingHandlers()
13105 wxList::compatibility_iterator node
= sm_drawingHandlers
.GetFirst();
13108 wxRichTextDrawingHandler
* handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13109 wxList::compatibility_iterator next
= node
->GetNext();
13114 sm_drawingHandlers
.Clear();
13117 void wxRichTextBuffer::AddFieldType(wxRichTextFieldType
*fieldType
)
13119 sm_fieldTypes
[fieldType
->GetName()] = fieldType
;
13122 bool wxRichTextBuffer::RemoveFieldType(const wxString
& name
)
13124 wxRichTextFieldTypeHashMap::iterator it
= sm_fieldTypes
.find(name
);
13125 if (it
== sm_fieldTypes
.end())
13129 wxRichTextFieldType
* fieldType
= it
->second
;
13130 sm_fieldTypes
.erase(it
);
13136 wxRichTextFieldType
*wxRichTextBuffer::FindFieldType(const wxString
& name
)
13138 wxRichTextFieldTypeHashMap::iterator it
= sm_fieldTypes
.find(name
);
13139 if (it
== sm_fieldTypes
.end())
13145 void wxRichTextBuffer::CleanUpFieldTypes()
13147 wxRichTextFieldTypeHashMap::iterator it
;
13148 for( it
= sm_fieldTypes
.begin(); it
!= sm_fieldTypes
.end(); ++it
)
13150 wxRichTextFieldType
* fieldType
= it
->second
;
13154 sm_fieldTypes
.clear();