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 int leftMargin
, rightMargin
, topMargin
, bottomMargin
;
1874 wxRichTextObject::GetTotalMargin(dc
, GetBuffer(), attr
, leftMargin
, rightMargin
,
1875 topMargin
, bottomMargin
);
1880 // The maximum paragraph maximum width, so we can set the overall maximum width for this object
1881 int maxMaxWidth
= 0;
1883 // The maximum paragraph minimum width, so we can set the overall minimum width for this object
1884 int maxMinWidth
= 0;
1886 // If we have vertical alignment, we must recalculate everything.
1887 bool hasVerticalAlignment
= (attr
.GetTextBoxAttr().HasVerticalAlignment() &&
1888 (attr
.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP
));
1890 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1892 bool layoutAll
= true;
1894 // Get invalid range, rounding to paragraph start/end.
1895 wxRichTextRange invalidRange
= GetInvalidRange(true);
1897 if (invalidRange
== wxRICHTEXT_NONE
&& !formatRect
)
1900 if (invalidRange
== wxRICHTEXT_ALL
|| hasVerticalAlignment
)
1902 else // If we know what range is affected, start laying out from that point on.
1903 if (invalidRange
.GetStart() >= GetOwnRange().GetStart())
1905 wxRichTextParagraph
* firstParagraph
= GetParagraphAtPosition(invalidRange
.GetStart());
1908 wxRichTextObjectList::compatibility_iterator firstNode
= m_children
.Find(firstParagraph
);
1909 wxRichTextObjectList::compatibility_iterator previousNode
;
1911 previousNode
= firstNode
->GetPrevious();
1916 wxRichTextParagraph
* previousParagraph
= wxDynamicCast(previousNode
->GetData(), wxRichTextParagraph
);
1917 availableSpace
.y
= previousParagraph
->GetPosition().y
+ previousParagraph
->GetCachedSize().y
;
1920 // Now we're going to start iterating from the first affected paragraph.
1928 // Gather information about only those floating objects that will not be formatted,
1929 // after which floats will be gathered per-paragraph during layout.
1930 UpdateFloatingObjects(availableSpace
, node
? node
->GetData() : (wxRichTextObject
*) NULL
);
1932 // A way to force speedy rest-of-buffer layout (the 'else' below)
1933 bool forceQuickLayout
= false;
1935 // First get the size of the paragraphs we won't be laying out
1936 wxRichTextObjectList::compatibility_iterator n
= m_children
.GetFirst();
1937 while (n
&& n
!= node
)
1939 wxRichTextParagraph
* child
= wxDynamicCast(n
->GetData(), wxRichTextParagraph
);
1942 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
1943 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
1944 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
1951 // Assume this box only contains paragraphs
1953 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
1954 // Unsure if this is needed
1955 // wxCHECK_MSG( child, false, wxT("Unknown object in layout") );
1957 if (child
&& child
->IsShown())
1959 // TODO: what if the child hasn't been laid out (e.g. involved in Undo) but still has 'old' lines
1960 if ( !forceQuickLayout
&&
1962 child
->GetLines().IsEmpty() ||
1963 !child
->GetRange().IsOutside(invalidRange
)) )
1965 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1966 // lays out the object again using the minimum size
1967 child
->LayoutToBestSize(dc
, context
, GetBuffer(),
1968 attr
, child
->GetAttributes(), availableSpace
, rect
, style
&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT
);
1970 // Layout must set the cached size
1971 availableSpace
.y
+= child
->GetCachedSize().y
;
1972 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
1973 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
1974 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
1976 // If we're just formatting the visible part of the buffer,
1977 // and we're now past the bottom of the window, and we don't have any
1978 // floating objects (since they may cause wrapping to change for the rest of the
1979 // the buffer), start quick layout.
1980 if (!hasVerticalAlignment
&& formatRect
&& child
->GetPosition().y
> rect
.GetBottom() && GetFloatingObjectCount() == 0)
1981 forceQuickLayout
= true;
1985 // We're outside the immediately affected range, so now let's just
1986 // move everything up or down. This assumes that all the children have previously
1987 // been laid out and have wrapped line lists associated with them.
1988 // TODO: check all paragraphs before the affected range.
1990 int inc
= availableSpace
.y
- child
->GetPosition().y
;
1994 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
1997 if (child
->GetLines().GetCount() == 0)
1999 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
2000 // lays out the object again using the minimum size
2001 child
->LayoutToBestSize(dc
, context
, GetBuffer(),
2002 attr
, child
->GetAttributes(), availableSpace
, rect
, style
&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT
);
2004 //child->Layout(dc, availableChildRect, style);
2007 child
->Move(wxPoint(child
->GetPosition().x
, child
->GetPosition().y
+ inc
));
2009 availableSpace
.y
+= child
->GetCachedSize().y
;
2010 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
2011 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
2012 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
2015 node
= node
->GetNext();
2021 node
= node
->GetNext();
2024 node
= m_children
.GetLast();
2025 if (node
&& node
->GetData()->IsShown())
2027 wxRichTextObject
* child
= node
->GetData();
2028 maxHeight
= child
->GetPosition().y
- (GetPosition().y
+ topMargin
) + child
->GetCachedSize().y
;
2031 maxHeight
= 0; // topMargin + bottomMargin;
2033 // Check the bottom edge of any floating object
2034 if (GetFloatCollector() && GetFloatCollector()->HasFloats())
2036 int bottom
= GetFloatCollector()->GetLastRectBottom();
2037 if (bottom
> maxHeight
)
2041 if (attr
.GetTextBoxAttr().GetSize().GetWidth().IsValid())
2043 wxRect r
= AdjustAvailableSpace(dc
, GetBuffer(), wxRichTextAttr() /* not used */, attr
, parentRect
, parentRect
);
2044 int w
= r
.GetWidth();
2046 // Convert external to content rect
2047 w
= w
- leftMargin
- rightMargin
;
2048 maxWidth
= wxMax(maxWidth
, w
);
2049 maxMaxWidth
= wxMax(maxMaxWidth
, w
);
2052 // TODO: (also in para layout) should set the
2053 // object's size to an absolute one if specified,
2054 // but if not specified, calculate it from content.
2056 // We need to add back the margins etc.
2058 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2059 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxWidth
, maxHeight
));
2060 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2061 SetCachedSize(marginRect
.GetSize());
2064 // The maximum size is the greatest of all maximum widths for all paragraphs.
2066 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2067 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxMaxWidth
, maxHeight
)); // Actually max height is a lie, we can't know it
2068 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2069 SetMaxSize(marginRect
.GetSize());
2072 // The minimum size is the greatest of all minimum widths for all paragraphs.
2074 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2075 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxMinWidth
, maxHeight
)); // Actually max height is a lie, we can't know it
2076 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2077 SetMinSize(marginRect
.GetSize());
2080 if (attr
.GetTextBoxAttr().HasVerticalAlignment() &&
2081 (attr
.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP
))
2084 int leftOverSpace
= availableSpace
.height
- topMargin
- bottomMargin
- maxHeight
;
2085 if (leftOverSpace
> 0)
2087 if (attr
.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_CENTRE
)
2089 yOffset
= (leftOverSpace
/2);
2091 else if (attr
.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_BOTTOM
)
2093 yOffset
= leftOverSpace
;
2097 // Move all the children to vertically align the content
2098 // This doesn't take into account floating objects, unfortunately.
2101 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2104 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2106 child
->Move(wxPoint(child
->GetPosition().x
, child
->GetPosition().y
+ yOffset
));
2108 node
= node
->GetNext();
2113 m_invalidRange
= wxRICHTEXT_NONE
;
2118 /// Get/set the size for the given range.
2119 bool wxRichTextParagraphLayoutBox::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* WXUNUSED(partialExtents
)) const
2123 wxRichTextObjectList::compatibility_iterator startPara
= wxRichTextObjectList::compatibility_iterator();
2124 wxRichTextObjectList::compatibility_iterator endPara
= wxRichTextObjectList::compatibility_iterator();
2126 // First find the first paragraph whose starting position is within the range.
2127 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2130 // child is a paragraph
2131 wxRichTextObject
* child
= node
->GetData();
2132 const wxRichTextRange
& r
= child
->GetRange();
2134 if (r
.GetStart() <= range
.GetStart() && r
.GetEnd() >= range
.GetStart())
2140 node
= node
->GetNext();
2143 // Next find the last paragraph containing part of the range
2144 node
= m_children
.GetFirst();
2147 // child is a paragraph
2148 wxRichTextObject
* child
= node
->GetData();
2149 const wxRichTextRange
& r
= child
->GetRange();
2151 if (r
.GetStart() <= range
.GetEnd() && r
.GetEnd() >= range
.GetEnd())
2157 node
= node
->GetNext();
2160 if (!startPara
|| !endPara
)
2163 // Now we can add up the sizes
2164 for (node
= startPara
; node
; node
= node
->GetNext())
2166 // child is a paragraph
2167 wxRichTextObject
* child
= node
->GetData();
2168 const wxRichTextRange
& childRange
= child
->GetRange();
2169 wxRichTextRange rangeToFind
= range
;
2170 rangeToFind
.LimitTo(childRange
);
2172 if (child
->IsTopLevel())
2173 rangeToFind
= child
->GetOwnRange();
2177 int childDescent
= 0;
2178 child
->GetRangeSize(rangeToFind
, childSize
, childDescent
, dc
, context
, flags
, position
);
2180 descent
= wxMax(childDescent
, descent
);
2182 sz
.x
= wxMax(sz
.x
, childSize
.x
);
2183 sz
.y
+= childSize
.y
;
2185 if (node
== endPara
)
2194 /// Get the paragraph at the given position
2195 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphAtPosition(long pos
, bool caretPosition
) const
2200 // First find the first paragraph whose starting position is within the range.
2201 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2204 // child is a paragraph
2205 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2206 // wxASSERT (child != NULL);
2210 // Return first child in buffer if position is -1
2214 if (child
->GetRange().Contains(pos
))
2218 node
= node
->GetNext();
2223 /// Get the line at the given position
2224 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineAtPosition(long pos
, bool caretPosition
) const
2229 // First find the first paragraph whose starting position is within the range.
2230 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2233 wxRichTextObject
* obj
= (wxRichTextObject
*) node
->GetData();
2234 if (obj
->GetRange().Contains(pos
))
2236 // child is a paragraph
2237 wxRichTextParagraph
* child
= wxDynamicCast(obj
, wxRichTextParagraph
);
2238 // wxASSERT (child != NULL);
2242 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2245 wxRichTextLine
* line
= node2
->GetData();
2247 wxRichTextRange range
= line
->GetAbsoluteRange();
2249 if (range
.Contains(pos
) ||
2251 // If the position is end-of-paragraph, then return the last line of
2252 // of the paragraph.
2253 ((range
.GetEnd() == child
->GetRange().GetEnd()-1) && (pos
== child
->GetRange().GetEnd())))
2256 node2
= node2
->GetNext();
2261 node
= node
->GetNext();
2264 int lineCount
= GetLineCount();
2266 return GetLineForVisibleLineNumber(lineCount
-1);
2271 /// Get the line at the given y pixel position, or the last line.
2272 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineAtYPosition(int y
) const
2274 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2277 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2278 // wxASSERT (child != NULL);
2282 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2285 wxRichTextLine
* line
= node2
->GetData();
2287 wxRect
rect(line
->GetRect());
2289 if (y
<= rect
.GetBottom())
2292 node2
= node2
->GetNext();
2296 node
= node
->GetNext();
2300 int lineCount
= GetLineCount();
2302 return GetLineForVisibleLineNumber(lineCount
-1);
2307 /// Get the number of visible lines
2308 int wxRichTextParagraphLayoutBox::GetLineCount() const
2312 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2315 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2316 // wxASSERT (child != NULL);
2319 count
+= child
->GetLines().GetCount();
2321 node
= node
->GetNext();
2327 /// Get the paragraph for a given line
2328 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphForLine(wxRichTextLine
* line
) const
2330 return GetParagraphAtPosition(line
->GetAbsoluteRange().GetStart());
2333 /// Get the line size at the given position
2334 wxSize
wxRichTextParagraphLayoutBox::GetLineSizeAtPosition(long pos
, bool caretPosition
) const
2336 wxRichTextLine
* line
= GetLineAtPosition(pos
, caretPosition
);
2339 return line
->GetSize();
2342 return wxSize(0, 0);
2346 /// Convenience function to add a paragraph of text
2347 wxRichTextRange
wxRichTextParagraphLayoutBox::AddParagraph(const wxString
& text
, wxRichTextAttr
* paraStyle
)
2349 // Don't use the base style, just the default style, and the base style will
2350 // be combined at display time.
2351 // Divide into paragraph and character styles.
2353 wxRichTextAttr defaultCharStyle
;
2354 wxRichTextAttr defaultParaStyle
;
2356 // If the default style is a named paragraph style, don't apply any character formatting
2357 // to the initial text string.
2358 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2360 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2362 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2365 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2367 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2368 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2370 wxRichTextParagraph
* para
= new wxRichTextParagraph(text
, this, pStyle
, cStyle
);
2376 return para
->GetRange();
2379 /// Adds multiple paragraphs, based on newlines.
2380 wxRichTextRange
wxRichTextParagraphLayoutBox::AddParagraphs(const wxString
& text
, wxRichTextAttr
* paraStyle
)
2382 // Don't use the base style, just the default style, and the base style will
2383 // be combined at display time.
2384 // Divide into paragraph and character styles.
2386 wxRichTextAttr defaultCharStyle
;
2387 wxRichTextAttr defaultParaStyle
;
2389 // If the default style is a named paragraph style, don't apply any character formatting
2390 // to the initial text string.
2391 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2393 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2395 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2398 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2400 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2401 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2403 wxRichTextParagraph
* firstPara
= NULL
;
2404 wxRichTextParagraph
* lastPara
= NULL
;
2406 wxRichTextRange
range(-1, -1);
2409 size_t len
= text
.length();
2411 wxRichTextParagraph
* para
= new wxRichTextParagraph(wxEmptyString
, this, pStyle
, cStyle
);
2420 wxChar ch
= text
[i
];
2421 if (ch
== wxT('\n') || ch
== wxT('\r'))
2425 wxRichTextPlainText
* plainText
= (wxRichTextPlainText
*) para
->GetChildren().GetFirst()->GetData();
2426 plainText
->SetText(line
);
2428 para
= new wxRichTextParagraph(wxEmptyString
, this, pStyle
, cStyle
);
2433 line
= wxEmptyString
;
2444 wxRichTextPlainText
* plainText
= (wxRichTextPlainText
*) para
->GetChildren().GetFirst()->GetData();
2445 plainText
->SetText(line
);
2450 return wxRichTextRange(firstPara
->GetRange().GetStart(), lastPara
->GetRange().GetEnd());
2453 /// Convenience function to add an image
2454 wxRichTextRange
wxRichTextParagraphLayoutBox::AddImage(const wxImage
& image
, wxRichTextAttr
* paraStyle
)
2456 // Don't use the base style, just the default style, and the base style will
2457 // be combined at display time.
2458 // Divide into paragraph and character styles.
2460 wxRichTextAttr defaultCharStyle
;
2461 wxRichTextAttr defaultParaStyle
;
2463 // If the default style is a named paragraph style, don't apply any character formatting
2464 // to the initial text string.
2465 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2467 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2469 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2472 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2474 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2475 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2477 wxRichTextParagraph
* para
= new wxRichTextParagraph(this, pStyle
);
2479 para
->AppendChild(new wxRichTextImage(image
, this, cStyle
));
2483 return para
->GetRange();
2487 /// Insert fragment into this box at the given position. If partialParagraph is true,
2488 /// it is assumed that the last (or only) paragraph is just a piece of data with no paragraph
2491 bool wxRichTextParagraphLayoutBox::InsertFragment(long position
, wxRichTextParagraphLayoutBox
& fragment
)
2493 // First, find the first paragraph whose starting position is within the range.
2494 wxRichTextParagraph
* para
= GetParagraphAtPosition(position
);
2497 wxRichTextAttr originalAttr
= para
->GetAttributes();
2499 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(para
);
2501 // Now split at this position, returning the object to insert the new
2502 // ones in front of.
2503 wxRichTextObject
* nextObject
= para
->SplitAt(position
);
2505 // Special case: partial paragraph, just one paragraph. Might be a small amount of
2506 // text, for example, so let's optimize.
2508 if (fragment
.GetPartialParagraph() && fragment
.GetChildren().GetCount() == 1)
2510 // Add the first para to this para...
2511 wxRichTextObjectList::compatibility_iterator firstParaNode
= fragment
.GetChildren().GetFirst();
2515 // Iterate through the fragment paragraph inserting the content into this paragraph.
2516 wxRichTextParagraph
* firstPara
= wxDynamicCast(firstParaNode
->GetData(), wxRichTextParagraph
);
2517 wxASSERT (firstPara
!= NULL
);
2519 wxRichTextObjectList::compatibility_iterator objectNode
= firstPara
->GetChildren().GetFirst();
2522 wxRichTextObject
* newObj
= objectNode
->GetData()->Clone();
2527 para
->AppendChild(newObj
);
2531 // Insert before nextObject
2532 para
->InsertChild(newObj
, nextObject
);
2535 objectNode
= objectNode
->GetNext();
2542 // Procedure for inserting a fragment consisting of a number of
2545 // 1. Remove and save the content that's after the insertion point, for adding
2546 // back once we've added the fragment.
2547 // 2. Add the content from the first fragment paragraph to the current
2549 // 3. Add remaining fragment paragraphs after the current paragraph.
2550 // 4. Add back the saved content from the first paragraph. If partialParagraph
2551 // is true, add it to the last paragraph added and not a new one.
2553 // 1. Remove and save objects after split point.
2554 wxList savedObjects
;
2556 para
->MoveToList(nextObject
, savedObjects
);
2558 // 2. Add the content from the 1st fragment paragraph.
2559 wxRichTextObjectList::compatibility_iterator firstParaNode
= fragment
.GetChildren().GetFirst();
2563 wxRichTextParagraph
* firstPara
= wxDynamicCast(firstParaNode
->GetData(), wxRichTextParagraph
);
2564 wxASSERT(firstPara
!= NULL
);
2566 if (!(fragment
.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
))
2567 para
->SetAttributes(firstPara
->GetAttributes());
2569 // Save empty paragraph attributes for appending later
2570 // These are character attributes deliberately set for a new paragraph. Without this,
2571 // we couldn't pass default attributes when appending a new paragraph.
2572 wxRichTextAttr emptyParagraphAttributes
;
2574 wxRichTextObjectList::compatibility_iterator objectNode
= firstPara
->GetChildren().GetFirst();
2576 if (objectNode
&& firstPara
->GetChildren().GetCount() == 1 && objectNode
->GetData()->IsEmpty())
2577 emptyParagraphAttributes
= objectNode
->GetData()->GetAttributes();
2581 wxRichTextObject
* newObj
= objectNode
->GetData()->Clone();
2584 para
->AppendChild(newObj
);
2586 objectNode
= objectNode
->GetNext();
2589 // 3. Add remaining fragment paragraphs after the current paragraph.
2590 wxRichTextObjectList::compatibility_iterator nextParagraphNode
= node
->GetNext();
2591 wxRichTextObject
* nextParagraph
= NULL
;
2592 if (nextParagraphNode
)
2593 nextParagraph
= nextParagraphNode
->GetData();
2595 wxRichTextObjectList::compatibility_iterator i
= fragment
.GetChildren().GetFirst()->GetNext();
2596 wxRichTextParagraph
* finalPara
= para
;
2598 bool needExtraPara
= (!i
|| !fragment
.GetPartialParagraph());
2600 // If there was only one paragraph, we need to insert a new one.
2603 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2604 wxASSERT( para
!= NULL
);
2606 finalPara
= (wxRichTextParagraph
*) para
->Clone();
2609 InsertChild(finalPara
, nextParagraph
);
2611 AppendChild(finalPara
);
2616 // If there was only one paragraph, or we have full paragraphs in our fragment,
2617 // we need to insert a new one.
2620 finalPara
= new wxRichTextParagraph
;
2623 InsertChild(finalPara
, nextParagraph
);
2625 AppendChild(finalPara
);
2628 // 4. Add back the remaining content.
2632 finalPara
->MoveFromList(savedObjects
);
2634 // Ensure there's at least one object
2635 if (finalPara
->GetChildCount() == 0)
2637 wxRichTextPlainText
* text
= new wxRichTextPlainText(wxEmptyString
);
2638 text
->SetAttributes(emptyParagraphAttributes
);
2640 finalPara
->AppendChild(text
);
2644 if ((fragment
.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
) && firstPara
)
2645 finalPara
->SetAttributes(firstPara
->GetAttributes());
2646 else if (finalPara
&& finalPara
!= para
)
2647 finalPara
->SetAttributes(originalAttr
);
2655 wxRichTextObjectList::compatibility_iterator i
= fragment
.GetChildren().GetFirst();
2658 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2659 wxASSERT( para
!= NULL
);
2661 AppendChild(para
->Clone());
2670 /// Make a copy of the fragment corresponding to the given range, putting it in 'fragment'.
2671 /// If there was an incomplete paragraph at the end, partialParagraph is set to true.
2672 bool wxRichTextParagraphLayoutBox::CopyFragment(const wxRichTextRange
& range
, wxRichTextParagraphLayoutBox
& fragment
)
2674 wxRichTextObjectList::compatibility_iterator i
= GetChildren().GetFirst();
2677 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2678 wxASSERT( para
!= NULL
);
2680 if (!para
->GetRange().IsOutside(range
))
2682 fragment
.AppendChild(para
->Clone());
2687 // Now top and tail the first and last paragraphs in our new fragment (which might be the same).
2688 if (!fragment
.IsEmpty())
2690 wxRichTextParagraph
* firstPara
= wxDynamicCast(fragment
.GetChildren().GetFirst()->GetData(), wxRichTextParagraph
);
2691 wxASSERT( firstPara
!= NULL
);
2693 wxRichTextParagraph
* lastPara
= wxDynamicCast(fragment
.GetChildren().GetLast()->GetData(), wxRichTextParagraph
);
2694 wxASSERT( lastPara
!= NULL
);
2696 if (!firstPara
|| !lastPara
)
2699 bool isFragment
= (range
.GetEnd() < lastPara
->GetRange().GetEnd());
2701 long firstPos
= firstPara
->GetRange().GetStart();
2703 // Adjust for renumbering from zero
2704 wxRichTextRange
topTailRange(range
.GetStart() - firstPos
, range
.GetEnd() - firstPos
);
2707 fragment
.CalculateRange(0, end
);
2709 // Chop off the start of the paragraph
2710 if (topTailRange
.GetStart() > 0)
2712 wxRichTextRange
r(0, topTailRange
.GetStart()-1);
2713 firstPara
->DeleteRange(r
);
2715 // Make sure the numbering is correct
2716 fragment
.CalculateRange(0, end
);
2718 // Now, we've deleted some positions, so adjust the range
2720 topTailRange
.SetStart(range
.GetLength());
2721 topTailRange
.SetEnd(fragment
.GetOwnRange().GetEnd());
2725 topTailRange
.SetStart(range
.GetLength());
2726 topTailRange
.SetEnd(fragment
.GetOwnRange().GetEnd());
2729 if (topTailRange
.GetStart() < lastPara
->GetRange().GetEnd())
2731 lastPara
->DeleteRange(topTailRange
);
2733 // Make sure the numbering is correct
2735 fragment
.CalculateRange(0, end
);
2737 // We only have part of a paragraph at the end
2738 fragment
.SetPartialParagraph(true);
2742 // We have a partial paragraph (don't save last new paragraph marker)
2743 // or complete paragraph
2744 fragment
.SetPartialParagraph(isFragment
);
2751 /// Given a position, get the number of the visible line (potentially many to a paragraph),
2752 /// starting from zero at the start of the buffer.
2753 long wxRichTextParagraphLayoutBox::GetVisibleLineNumber(long pos
, bool caretPosition
, bool startOfLine
) const
2760 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2763 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2764 // wxASSERT( child != NULL );
2768 if (child
->GetRange().Contains(pos
))
2770 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2773 wxRichTextLine
* line
= node2
->GetData();
2774 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
2776 if (lineRange
.Contains(pos
) || pos
== lineRange
.GetStart())
2778 // If the caret is displayed at the end of the previous wrapped line,
2779 // we want to return the line it's _displayed_ at (not the actual line
2780 // containing the position).
2781 if (lineRange
.GetStart() == pos
&& !startOfLine
&& child
->GetRange().GetStart() != pos
)
2782 return lineCount
- 1;
2789 node2
= node2
->GetNext();
2791 // If we didn't find it in the lines, it must be
2792 // the last position of the paragraph. So return the last line.
2796 lineCount
+= child
->GetLines().GetCount();
2799 node
= node
->GetNext();
2806 /// Given a line number, get the corresponding wxRichTextLine object.
2807 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineForVisibleLineNumber(long lineNumber
) const
2811 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2814 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2815 // wxASSERT(child != NULL);
2819 if (lineNumber
< (int) (child
->GetLines().GetCount() + lineCount
))
2821 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2824 wxRichTextLine
* line
= node2
->GetData();
2826 if (lineCount
== lineNumber
)
2831 node2
= node2
->GetNext();
2835 lineCount
+= child
->GetLines().GetCount();
2838 node
= node
->GetNext();
2845 /// Delete range from layout.
2846 bool wxRichTextParagraphLayoutBox::DeleteRange(const wxRichTextRange
& range
)
2848 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2850 wxRichTextParagraph
* firstPara
= NULL
;
2853 wxRichTextParagraph
* obj
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2854 // wxASSERT (obj != NULL);
2856 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
2860 // Delete the range in each paragraph
2862 if (!obj
->GetRange().IsOutside(range
))
2864 // Deletes the content of this object within the given range
2865 obj
->DeleteRange(range
);
2867 wxRichTextRange thisRange
= obj
->GetRange();
2868 wxRichTextAttr thisAttr
= obj
->GetAttributes();
2870 // If the whole paragraph is within the range to delete,
2871 // delete the whole thing.
2872 if (range
.GetStart() <= thisRange
.GetStart() && range
.GetEnd() >= thisRange
.GetEnd())
2874 // Delete the whole object
2875 RemoveChild(obj
, true);
2878 else if (!firstPara
)
2881 // If the range includes the paragraph end, we need to join this
2882 // and the next paragraph.
2883 if (range
.GetEnd() <= thisRange
.GetEnd())
2885 // We need to move the objects from the next paragraph
2886 // to this paragraph
2888 wxRichTextParagraph
* nextParagraph
= NULL
;
2889 if ((range
.GetEnd() < thisRange
.GetEnd()) && obj
)
2890 nextParagraph
= obj
;
2893 // We're ending at the end of the paragraph, so merge the _next_ paragraph.
2895 nextParagraph
= wxDynamicCast(next
->GetData(), wxRichTextParagraph
);
2898 bool applyFinalParagraphStyle
= firstPara
&& nextParagraph
&& nextParagraph
!= firstPara
;
2900 wxRichTextAttr nextParaAttr
;
2901 if (applyFinalParagraphStyle
)
2903 // Special case when deleting the end of a paragraph - use _this_ paragraph's style,
2904 // not the next one.
2905 if (range
.GetStart() == range
.GetEnd() && range
.GetStart() == thisRange
.GetEnd())
2906 nextParaAttr
= thisAttr
;
2908 nextParaAttr
= nextParagraph
->GetAttributes();
2911 if (firstPara
&& nextParagraph
&& firstPara
!= nextParagraph
)
2913 // Move the objects to the previous para
2914 wxRichTextObjectList::compatibility_iterator node1
= nextParagraph
->GetChildren().GetFirst();
2918 wxRichTextObject
* obj1
= node1
->GetData();
2920 firstPara
->AppendChild(obj1
);
2922 wxRichTextObjectList::compatibility_iterator next1
= node1
->GetNext();
2923 nextParagraph
->GetChildren().Erase(node1
);
2928 // Delete the paragraph
2929 RemoveChild(nextParagraph
, true);
2932 // Avoid empty paragraphs
2933 if (firstPara
&& firstPara
->GetChildren().GetCount() == 0)
2935 wxRichTextPlainText
* text
= new wxRichTextPlainText(wxEmptyString
);
2936 firstPara
->AppendChild(text
);
2939 if (applyFinalParagraphStyle
)
2940 firstPara
->SetAttributes(nextParaAttr
);
2953 /// Get any text in this object for the given range
2954 wxString
wxRichTextParagraphLayoutBox::GetTextForRange(const wxRichTextRange
& range
) const
2958 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2961 wxRichTextObject
* child
= node
->GetData();
2962 if (!child
->GetRange().IsOutside(range
))
2964 wxRichTextRange childRange
= range
;
2965 childRange
.LimitTo(child
->GetRange());
2967 wxString childText
= child
->GetTextForRange(childRange
);
2971 if ((childRange
.GetEnd() == child
->GetRange().GetEnd()) && node
->GetNext())
2976 node
= node
->GetNext();
2982 /// Get all the text
2983 wxString
wxRichTextParagraphLayoutBox::GetText() const
2985 return GetTextForRange(GetOwnRange());
2988 /// Get the paragraph by number
2989 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphAtLine(long paragraphNumber
) const
2991 if ((size_t) paragraphNumber
>= GetChildCount())
2994 return (wxRichTextParagraph
*) GetChild((size_t) paragraphNumber
);
2997 /// Get the length of the paragraph
2998 int wxRichTextParagraphLayoutBox::GetParagraphLength(long paragraphNumber
) const
3000 wxRichTextParagraph
* para
= GetParagraphAtLine(paragraphNumber
);
3002 return para
->GetRange().GetLength() - 1; // don't include newline
3007 /// Get the text of the paragraph
3008 wxString
wxRichTextParagraphLayoutBox::GetParagraphText(long paragraphNumber
) const
3010 wxRichTextParagraph
* para
= GetParagraphAtLine(paragraphNumber
);
3012 return para
->GetTextForRange(para
->GetRange());
3014 return wxEmptyString
;
3017 /// Convert zero-based line column and paragraph number to a position.
3018 long wxRichTextParagraphLayoutBox::XYToPosition(long x
, long y
) const
3020 wxRichTextParagraph
* para
= GetParagraphAtLine(y
);
3023 return para
->GetRange().GetStart() + x
;
3029 /// Convert zero-based position to line column and paragraph number
3030 bool wxRichTextParagraphLayoutBox::PositionToXY(long pos
, long* x
, long* y
) const
3032 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
);
3036 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3039 wxRichTextObject
* child
= node
->GetData();
3043 node
= node
->GetNext();
3047 *x
= pos
- para
->GetRange().GetStart();
3055 /// Get the leaf object in a paragraph at this position.
3056 /// Given a line number, get the corresponding wxRichTextLine object.
3057 wxRichTextObject
* wxRichTextParagraphLayoutBox::GetLeafObjectAtPosition(long position
) const
3059 wxRichTextParagraph
* para
= GetParagraphAtPosition(position
);
3062 wxRichTextObjectList::compatibility_iterator node
= para
->GetChildren().GetFirst();
3066 wxRichTextObject
* child
= node
->GetData();
3067 if (child
->GetRange().Contains(position
))
3070 node
= node
->GetNext();
3072 if (position
== para
->GetRange().GetEnd() && para
->GetChildCount() > 0)
3073 return para
->GetChildren().GetLast()->GetData();
3078 /// Set character or paragraph text attributes: apply character styles only to immediate text nodes
3079 bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange
& range
, const wxRichTextAttr
& style
, int flags
)
3081 bool characterStyle
= false;
3082 bool paragraphStyle
= false;
3084 if (style
.IsCharacterStyle())
3085 characterStyle
= true;
3086 if (style
.IsParagraphStyle())
3087 paragraphStyle
= true;
3089 wxRichTextBuffer
* buffer
= GetBuffer();
3091 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
3092 bool applyMinimal
= ((flags
& wxRICHTEXT_SETSTYLE_OPTIMIZE
) != 0);
3093 bool parasOnly
= ((flags
& wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY
) != 0);
3094 bool charactersOnly
= ((flags
& wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY
) != 0);
3095 bool resetExistingStyle
= ((flags
& wxRICHTEXT_SETSTYLE_RESET
) != 0);
3096 bool removeStyle
= ((flags
& wxRICHTEXT_SETSTYLE_REMOVE
) != 0);
3098 // Apply paragraph style first, if any
3099 wxRichTextAttr
wholeStyle(style
);
3101 if (!removeStyle
&& wholeStyle
.HasParagraphStyleName() && buffer
->GetStyleSheet())
3103 wxRichTextParagraphStyleDefinition
* def
= buffer
->GetStyleSheet()->FindParagraphStyle(wholeStyle
.GetParagraphStyleName());
3105 wxRichTextApplyStyle(wholeStyle
, def
->GetStyleMergedWithBase(buffer
->GetStyleSheet()));
3108 // Limit the attributes to be set to the content to only character attributes.
3109 wxRichTextAttr
characterAttributes(wholeStyle
);
3110 characterAttributes
.SetFlags(characterAttributes
.GetFlags() & (wxTEXT_ATTR_CHARACTER
));
3112 if (!removeStyle
&& characterAttributes
.HasCharacterStyleName() && buffer
->GetStyleSheet())
3114 wxRichTextCharacterStyleDefinition
* def
= buffer
->GetStyleSheet()->FindCharacterStyle(characterAttributes
.GetCharacterStyleName());
3116 wxRichTextApplyStyle(characterAttributes
, def
->GetStyleMergedWithBase(buffer
->GetStyleSheet()));
3119 // If we are associated with a control, make undoable; otherwise, apply immediately
3122 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3124 wxRichTextAction
* action
= NULL
;
3126 if (haveControl
&& withUndo
)
3128 action
= new wxRichTextAction(NULL
, _("Change Style"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
3129 action
->SetRange(range
);
3130 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3133 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3136 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3137 // wxASSERT (para != NULL);
3139 if (para
&& para
->GetChildCount() > 0)
3141 // Stop searching if we're beyond the range of interest
3142 if (para
->GetRange().GetStart() > range
.GetEnd())
3145 if (!para
->GetRange().IsOutside(range
))
3147 // We'll be using a copy of the paragraph to make style changes,
3148 // not updating the buffer directly.
3149 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3151 if (haveControl
&& withUndo
)
3153 newPara
= new wxRichTextParagraph(*para
);
3154 action
->GetNewParagraphs().AppendChild(newPara
);
3156 // Also store the old ones for Undo
3157 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3162 // If we're specifying paragraphs only, then we really mean character formatting
3163 // to be included in the paragraph style
3164 if ((paragraphStyle
|| parasOnly
) && !charactersOnly
)
3168 // Removes the given style from the paragraph
3169 wxRichTextRemoveStyle(newPara
->GetAttributes(), style
);
3171 else if (resetExistingStyle
)
3172 newPara
->GetAttributes() = wholeStyle
;
3177 // Only apply attributes that will make a difference to the combined
3178 // style as seen on the display
3179 wxRichTextAttr
combinedAttr(para
->GetCombinedAttributes(true));
3180 wxRichTextApplyStyle(newPara
->GetAttributes(), wholeStyle
, & combinedAttr
);
3183 wxRichTextApplyStyle(newPara
->GetAttributes(), wholeStyle
);
3187 // When applying paragraph styles dynamically, don't change the text objects' attributes
3188 // since they will computed as needed. Only apply the character styling if it's _only_
3189 // character styling. This policy is subject to change and might be put under user control.
3191 // Hm. we might well be applying a mix of paragraph and character styles, in which
3192 // case we _do_ want to apply character styles regardless of what para styles are set.
3193 // But if we're applying a paragraph style, which has some character attributes, but
3194 // we only want the paragraphs to hold this character style, then we _don't_ want to
3195 // apply the character style. So we need to be able to choose.
3197 if (!parasOnly
&& (characterStyle
|charactersOnly
) && range
.GetStart() != newPara
->GetRange().GetEnd())
3199 wxRichTextRange
childRange(range
);
3200 childRange
.LimitTo(newPara
->GetRange());
3202 // Find the starting position and if necessary split it so
3203 // we can start applying a different style.
3204 // TODO: check that the style actually changes or is different
3205 // from style outside of range
3206 wxRichTextObject
* firstObject
wxDUMMY_INITIALIZE(NULL
);
3207 wxRichTextObject
* lastObject
wxDUMMY_INITIALIZE(NULL
);
3209 if (childRange
.GetStart() == newPara
->GetRange().GetStart())
3210 firstObject
= newPara
->GetChildren().GetFirst()->GetData();
3212 firstObject
= newPara
->SplitAt(range
.GetStart());
3214 // Increment by 1 because we're apply the style one _after_ the split point
3215 long splitPoint
= childRange
.GetEnd();
3216 if (splitPoint
!= newPara
->GetRange().GetEnd())
3220 if (splitPoint
== newPara
->GetRange().GetEnd())
3221 lastObject
= newPara
->GetChildren().GetLast()->GetData();
3223 // lastObject is set as a side-effect of splitting. It's
3224 // returned as the object before the new object.
3225 (void) newPara
->SplitAt(splitPoint
, & lastObject
);
3227 wxASSERT(firstObject
!= NULL
);
3228 wxASSERT(lastObject
!= NULL
);
3230 if (!firstObject
|| !lastObject
)
3233 wxRichTextObjectList::compatibility_iterator firstNode
= newPara
->GetChildren().Find(firstObject
);
3234 wxRichTextObjectList::compatibility_iterator lastNode
= newPara
->GetChildren().Find(lastObject
);
3236 wxASSERT(firstNode
);
3239 wxRichTextObjectList::compatibility_iterator node2
= firstNode
;
3243 wxRichTextObject
* child
= node2
->GetData();
3247 // Removes the given style from the paragraph
3248 wxRichTextRemoveStyle(child
->GetAttributes(), style
);
3250 else if (resetExistingStyle
)
3251 child
->GetAttributes() = characterAttributes
;
3256 // Only apply attributes that will make a difference to the combined
3257 // style as seen on the display
3258 wxRichTextAttr
combinedAttr(newPara
->GetCombinedAttributes(child
->GetAttributes(), true));
3259 wxRichTextApplyStyle(child
->GetAttributes(), characterAttributes
, & combinedAttr
);
3262 wxRichTextApplyStyle(child
->GetAttributes(), characterAttributes
);
3265 if (node2
== lastNode
)
3268 node2
= node2
->GetNext();
3274 node
= node
->GetNext();
3277 // Do action, or delay it until end of batch.
3278 if (haveControl
&& withUndo
)
3279 buffer
->SubmitAction(action
);
3284 // Just change the attributes for this single object.
3285 void wxRichTextParagraphLayoutBox::SetStyle(wxRichTextObject
* obj
, const wxRichTextAttr
& textAttr
, int flags
)
3287 wxRichTextBuffer
* buffer
= GetBuffer();
3288 bool withUndo
= flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
;
3289 bool resetExistingStyle
= ((flags
& wxRICHTEXT_SETSTYLE_RESET
) != 0);
3290 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3292 wxRichTextAction
*action
= NULL
;
3293 wxRichTextAttr newAttr
= obj
->GetAttributes();
3294 if (resetExistingStyle
)
3297 newAttr
.Apply(textAttr
);
3299 if (haveControl
&& withUndo
)
3301 action
= new wxRichTextAction(NULL
, _("Change Object Style"), wxRICHTEXT_CHANGE_ATTRIBUTES
, buffer
, obj
->GetContainer(), buffer
->GetRichTextCtrl());
3302 action
->SetRange(obj
->GetRange().FromInternal());
3303 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3304 action
->MakeObject(obj
);
3306 action
->GetAttributes() = newAttr
;
3309 obj
->GetAttributes() = newAttr
;
3311 if (haveControl
&& withUndo
)
3312 buffer
->SubmitAction(action
);
3315 /// Get the text attributes for this position.
3316 bool wxRichTextParagraphLayoutBox::GetStyle(long position
, wxRichTextAttr
& style
)
3318 return DoGetStyle(position
, style
, true);
3321 bool wxRichTextParagraphLayoutBox::GetUncombinedStyle(long position
, wxRichTextAttr
& style
)
3323 return DoGetStyle(position
, style
, false);
3326 /// Implementation helper for GetStyle. If combineStyles is true, combine base, paragraph and
3327 /// context attributes.
3328 bool wxRichTextParagraphLayoutBox::DoGetStyle(long position
, wxRichTextAttr
& style
, bool combineStyles
)
3330 wxRichTextObject
* obj
wxDUMMY_INITIALIZE(NULL
);
3332 if (style
.IsParagraphStyle())
3334 obj
= GetParagraphAtPosition(position
);
3339 // Start with the base style
3340 style
= GetAttributes();
3342 // Apply the paragraph style
3343 wxRichTextApplyStyle(style
, obj
->GetAttributes());
3346 style
= obj
->GetAttributes();
3353 obj
= GetLeafObjectAtPosition(position
);
3358 wxRichTextParagraph
* para
= wxDynamicCast(obj
->GetParent(), wxRichTextParagraph
);
3359 style
= para
? para
->GetCombinedAttributes(obj
->GetAttributes()) : obj
->GetAttributes();
3362 style
= obj
->GetAttributes();
3370 static bool wxHasStyle(long flags
, long style
)
3372 return (flags
& style
) != 0;
3375 /// Combines 'style' with 'currentStyle' for the purpose of summarising the attributes of a range of
3377 bool wxRichTextParagraphLayoutBox::CollectStyle(wxRichTextAttr
& currentStyle
, const wxRichTextAttr
& style
, wxRichTextAttr
& clashingAttr
, wxRichTextAttr
& absentAttr
)
3379 currentStyle
.CollectCommonAttributes(style
, clashingAttr
, absentAttr
);
3384 /// Get the combined style for a range - if any attribute is different within the range,
3385 /// that attribute is not present within the flags.
3386 /// *** Note that this is not recursive, and so assumes that content inside a paragraph is not itself
3388 bool wxRichTextParagraphLayoutBox::GetStyleForRange(const wxRichTextRange
& range
, wxRichTextAttr
& style
)
3390 style
= wxRichTextAttr();
3392 wxRichTextAttr clashingAttr
;
3393 wxRichTextAttr absentAttrPara
, absentAttrChar
;
3395 wxRichTextObjectList::compatibility_iterator node
= GetChildren().GetFirst();
3398 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3399 if (para
&& !(para
->GetRange().GetStart() > range
.GetEnd() || para
->GetRange().GetEnd() < range
.GetStart()))
3401 if (para
->GetChildren().GetCount() == 0)
3403 wxRichTextAttr paraStyle
= para
->GetCombinedAttributes(true /* use box attributes */);
3405 CollectStyle(style
, paraStyle
, clashingAttr
, absentAttrPara
);
3409 wxRichTextRange
paraRange(para
->GetRange());
3410 paraRange
.LimitTo(range
);
3412 // First collect paragraph attributes only
3413 wxRichTextAttr paraStyle
= para
->GetCombinedAttributes();
3414 paraStyle
.SetFlags(paraStyle
.GetFlags() & wxTEXT_ATTR_PARAGRAPH
);
3415 CollectStyle(style
, paraStyle
, clashingAttr
, absentAttrPara
);
3417 wxRichTextObjectList::compatibility_iterator childNode
= para
->GetChildren().GetFirst();
3421 wxRichTextObject
* child
= childNode
->GetData();
3422 if (!(child
->GetRange().GetStart() > range
.GetEnd() || child
->GetRange().GetEnd() < range
.GetStart()))
3424 wxRichTextAttr childStyle
= para
->GetCombinedAttributes(child
->GetAttributes(), true /* include box attributes */);
3426 // Now collect character attributes only
3427 childStyle
.SetFlags(childStyle
.GetFlags() & wxTEXT_ATTR_CHARACTER
);
3429 CollectStyle(style
, childStyle
, clashingAttr
, absentAttrChar
);
3432 childNode
= childNode
->GetNext();
3436 node
= node
->GetNext();
3441 /// Set default style
3442 bool wxRichTextParagraphLayoutBox::SetDefaultStyle(const wxRichTextAttr
& style
)
3444 m_defaultAttributes
= style
;
3448 /// Test if this whole range has character attributes of the specified kind. If any
3449 /// of the attributes are different within the range, the test fails. You
3450 /// can use this to implement, for example, bold button updating. style must have
3451 /// flags indicating which attributes are of interest.
3452 bool wxRichTextParagraphLayoutBox::HasCharacterAttributes(const wxRichTextRange
& range
, const wxRichTextAttr
& style
) const
3455 int matchingCount
= 0;
3457 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3460 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3461 // wxASSERT (para != NULL);
3465 // Stop searching if we're beyond the range of interest
3466 if (para
->GetRange().GetStart() > range
.GetEnd())
3467 return foundCount
== matchingCount
&& foundCount
!= 0;
3469 if (!para
->GetRange().IsOutside(range
))
3471 wxRichTextObjectList::compatibility_iterator node2
= para
->GetChildren().GetFirst();
3475 wxRichTextObject
* child
= node2
->GetData();
3476 // Allow for empty string if no buffer
3477 wxRichTextRange childRange
= child
->GetRange();
3478 if (childRange
.GetLength() == 0 && GetRange().GetLength() == 1)
3479 childRange
.SetEnd(childRange
.GetEnd()+1);
3481 if (!childRange
.IsOutside(range
) && child
->IsKindOf(CLASSINFO(wxRichTextPlainText
)))
3484 wxRichTextAttr textAttr
= para
->GetCombinedAttributes(child
->GetAttributes());
3486 if (wxTextAttrEqPartial(textAttr
, style
))
3490 node2
= node2
->GetNext();
3495 node
= node
->GetNext();
3498 return foundCount
== matchingCount
&& foundCount
!= 0;
3501 /// Test if this whole range has paragraph attributes of the specified kind. If any
3502 /// of the attributes are different within the range, the test fails. You
3503 /// can use this to implement, for example, centering button updating. style must have
3504 /// flags indicating which attributes are of interest.
3505 bool wxRichTextParagraphLayoutBox::HasParagraphAttributes(const wxRichTextRange
& range
, const wxRichTextAttr
& style
) const
3508 int matchingCount
= 0;
3510 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3513 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3514 // wxASSERT (para != NULL);
3518 // Stop searching if we're beyond the range of interest
3519 if (para
->GetRange().GetStart() > range
.GetEnd())
3520 return foundCount
== matchingCount
&& foundCount
!= 0;
3522 if (!para
->GetRange().IsOutside(range
))
3524 wxRichTextAttr textAttr
= GetAttributes();
3525 // Apply the paragraph style
3526 wxRichTextApplyStyle(textAttr
, para
->GetAttributes());
3529 if (wxTextAttrEqPartial(textAttr
, style
))
3534 node
= node
->GetNext();
3536 return foundCount
== matchingCount
&& foundCount
!= 0;
3539 void wxRichTextParagraphLayoutBox::PrepareContent(wxRichTextParagraphLayoutBox
& container
)
3541 wxRichTextBuffer
* buffer
= GetBuffer();
3542 if (buffer
&& buffer
->GetRichTextCtrl())
3543 buffer
->GetRichTextCtrl()->PrepareContent(container
);
3546 /// Set character or paragraph properties
3547 bool wxRichTextParagraphLayoutBox::SetProperties(const wxRichTextRange
& range
, const wxRichTextProperties
& properties
, int flags
)
3549 wxRichTextBuffer
* buffer
= GetBuffer();
3551 bool withUndo
= ((flags
& wxRICHTEXT_SETPROPERTIES_WITH_UNDO
) != 0);
3552 bool parasOnly
= ((flags
& wxRICHTEXT_SETPROPERTIES_PARAGRAPHS_ONLY
) != 0);
3553 bool charactersOnly
= ((flags
& wxRICHTEXT_SETPROPERTIES_CHARACTERS_ONLY
) != 0);
3554 bool resetExistingProperties
= ((flags
& wxRICHTEXT_SETPROPERTIES_RESET
) != 0);
3555 bool removeProperties
= ((flags
& wxRICHTEXT_SETPROPERTIES_REMOVE
) != 0);
3557 // If we are associated with a control, make undoable; otherwise, apply immediately
3560 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3562 wxRichTextAction
* action
= NULL
;
3564 if (haveControl
&& withUndo
)
3566 action
= new wxRichTextAction(NULL
, _("Change Properties"), wxRICHTEXT_CHANGE_PROPERTIES
, buffer
, this, buffer
->GetRichTextCtrl());
3567 action
->SetRange(range
);
3568 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3571 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3574 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3575 // wxASSERT (para != NULL);
3577 if (para
&& para
->GetChildCount() > 0)
3579 // Stop searching if we're beyond the range of interest
3580 if (para
->GetRange().GetStart() > range
.GetEnd())
3583 if (!para
->GetRange().IsOutside(range
))
3585 // We'll be using a copy of the paragraph to make style changes,
3586 // not updating the buffer directly.
3587 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3589 if (haveControl
&& withUndo
)
3591 newPara
= new wxRichTextParagraph(*para
);
3592 action
->GetNewParagraphs().AppendChild(newPara
);
3594 // Also store the old ones for Undo
3595 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3602 if (removeProperties
)
3604 // Removes the given style from the paragraph
3606 newPara
->GetProperties().RemoveProperties(properties
);
3608 else if (resetExistingProperties
)
3609 newPara
->GetProperties() = properties
;
3611 newPara
->GetProperties().MergeProperties(properties
);
3614 // When applying paragraph styles dynamically, don't change the text objects' attributes
3615 // since they will computed as needed. Only apply the character styling if it's _only_
3616 // character styling. This policy is subject to change and might be put under user control.
3618 // Hm. we might well be applying a mix of paragraph and character styles, in which
3619 // case we _do_ want to apply character styles regardless of what para styles are set.
3620 // But if we're applying a paragraph style, which has some character attributes, but
3621 // we only want the paragraphs to hold this character style, then we _don't_ want to
3622 // apply the character style. So we need to be able to choose.
3624 if (!parasOnly
&& charactersOnly
&& range
.GetStart() != newPara
->GetRange().GetEnd())
3626 wxRichTextRange
childRange(range
);
3627 childRange
.LimitTo(newPara
->GetRange());
3629 // Find the starting position and if necessary split it so
3630 // we can start applying different properties.
3631 // TODO: check that the properties actually change or are different
3632 // from properties outside of range
3633 wxRichTextObject
* firstObject
wxDUMMY_INITIALIZE(NULL
);
3634 wxRichTextObject
* lastObject
wxDUMMY_INITIALIZE(NULL
);
3636 if (childRange
.GetStart() == newPara
->GetRange().GetStart())
3637 firstObject
= newPara
->GetChildren().GetFirst()->GetData();
3639 firstObject
= newPara
->SplitAt(range
.GetStart());
3641 // Increment by 1 because we're apply the style one _after_ the split point
3642 long splitPoint
= childRange
.GetEnd();
3643 if (splitPoint
!= newPara
->GetRange().GetEnd())
3647 if (splitPoint
== newPara
->GetRange().GetEnd())
3648 lastObject
= newPara
->GetChildren().GetLast()->GetData();
3650 // lastObject is set as a side-effect of splitting. It's
3651 // returned as the object before the new object.
3652 (void) newPara
->SplitAt(splitPoint
, & lastObject
);
3654 wxASSERT(firstObject
!= NULL
);
3655 wxASSERT(lastObject
!= NULL
);
3657 if (!firstObject
|| !lastObject
)
3660 wxRichTextObjectList::compatibility_iterator firstNode
= newPara
->GetChildren().Find(firstObject
);
3661 wxRichTextObjectList::compatibility_iterator lastNode
= newPara
->GetChildren().Find(lastObject
);
3663 wxASSERT(firstNode
);
3666 wxRichTextObjectList::compatibility_iterator node2
= firstNode
;
3670 wxRichTextObject
* child
= node2
->GetData();
3672 if (removeProperties
)
3674 // Removes the given properties from the paragraph
3675 child
->GetProperties().RemoveProperties(properties
);
3677 else if (resetExistingProperties
)
3678 child
->GetProperties() = properties
;
3681 child
->GetProperties().MergeProperties(properties
);
3684 if (node2
== lastNode
)
3687 node2
= node2
->GetNext();
3693 node
= node
->GetNext();
3696 // Do action, or delay it until end of batch.
3697 if (haveControl
&& withUndo
)
3698 buffer
->SubmitAction(action
);
3703 void wxRichTextParagraphLayoutBox::Reset()
3707 wxRichTextBuffer
* buffer
= GetBuffer();
3708 if (buffer
&& buffer
->GetRichTextCtrl())
3710 wxRichTextEvent
event(wxEVT_COMMAND_RICHTEXT_BUFFER_RESET
, buffer
->GetRichTextCtrl()->GetId());
3711 event
.SetEventObject(buffer
->GetRichTextCtrl());
3712 event
.SetContainer(this);
3714 buffer
->SendEvent(event
, true);
3717 AddParagraph(wxEmptyString
);
3719 PrepareContent(*this);
3721 InvalidateHierarchy(wxRICHTEXT_ALL
);
3724 /// Invalidate the buffer. With no argument, invalidates whole buffer.
3725 void wxRichTextParagraphLayoutBox::Invalidate(const wxRichTextRange
& invalidRange
)
3727 wxRichTextCompositeObject::Invalidate(invalidRange
);
3729 DoInvalidate(invalidRange
);
3732 // Do the (in)validation for this object only
3733 void wxRichTextParagraphLayoutBox::DoInvalidate(const wxRichTextRange
& invalidRange
)
3735 if (invalidRange
== wxRICHTEXT_ALL
)
3737 m_invalidRange
= wxRICHTEXT_ALL
;
3739 // Already invalidating everything
3740 else if (m_invalidRange
== wxRICHTEXT_ALL
)
3745 if ((invalidRange
.GetStart() < m_invalidRange
.GetStart()) || m_invalidRange
.GetStart() == -1)
3746 m_invalidRange
.SetStart(invalidRange
.GetStart());
3747 if (invalidRange
.GetEnd() > m_invalidRange
.GetEnd())
3748 m_invalidRange
.SetEnd(invalidRange
.GetEnd());
3752 // Do the (in)validation both up and down the hierarchy
3753 void wxRichTextParagraphLayoutBox::InvalidateHierarchy(const wxRichTextRange
& invalidRange
)
3755 Invalidate(invalidRange
);
3757 if (invalidRange
!= wxRICHTEXT_NONE
)
3759 // Now go up the hierarchy
3760 wxRichTextObject
* thisObj
= this;
3761 wxRichTextObject
* p
= GetParent();
3764 wxRichTextParagraphLayoutBox
* l
= wxDynamicCast(p
, wxRichTextParagraphLayoutBox
);
3766 l
->DoInvalidate(thisObj
->GetRange());
3774 /// Get invalid range, rounding to entire paragraphs if argument is true.
3775 wxRichTextRange
wxRichTextParagraphLayoutBox::GetInvalidRange(bool wholeParagraphs
) const
3777 if (m_invalidRange
== wxRICHTEXT_ALL
|| m_invalidRange
== wxRICHTEXT_NONE
)
3778 return m_invalidRange
;
3780 wxRichTextRange range
= m_invalidRange
;
3782 if (wholeParagraphs
)
3784 wxRichTextParagraph
* para1
= GetParagraphAtPosition(range
.GetStart());
3786 range
.SetStart(para1
->GetRange().GetStart());
3787 // floating layout make all child should be relayout
3788 range
.SetEnd(GetOwnRange().GetEnd());
3793 /// Apply the style sheet to the buffer, for example if the styles have changed.
3794 bool wxRichTextParagraphLayoutBox::ApplyStyleSheet(wxRichTextStyleSheet
* styleSheet
)
3796 wxASSERT(styleSheet
!= NULL
);
3802 wxRichTextAttr
attr(GetBasicStyle());
3803 if (GetBasicStyle().HasParagraphStyleName())
3805 wxRichTextParagraphStyleDefinition
* paraDef
= styleSheet
->FindParagraphStyle(GetBasicStyle().GetParagraphStyleName());
3808 attr
.Apply(paraDef
->GetStyleMergedWithBase(styleSheet
));
3809 SetBasicStyle(attr
);
3814 if (GetBasicStyle().HasCharacterStyleName())
3816 wxRichTextCharacterStyleDefinition
* charDef
= styleSheet
->FindCharacterStyle(GetBasicStyle().GetCharacterStyleName());
3819 attr
.Apply(charDef
->GetStyleMergedWithBase(styleSheet
));
3820 SetBasicStyle(attr
);
3825 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3828 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3829 // wxASSERT (para != NULL);
3833 // Combine paragraph and list styles. If there is a list style in the original attributes,
3834 // the current indentation overrides anything else and is used to find the item indentation.
3835 // Also, for applying paragraph styles, consider having 2 modes: (1) we merge with what we have,
3836 // thereby taking into account all user changes, (2) reset the style completely (except for indentation/list
3837 // exception as above).
3838 // Problem: when changing from one list style to another, there's a danger that the level info will get lost.
3839 // So when changing a list style interactively, could retrieve level based on current style, then
3840 // set appropriate indent and apply new style.
3844 if (para
->GetAttributes().HasOutlineLevel())
3845 outline
= para
->GetAttributes().GetOutlineLevel();
3846 if (para
->GetAttributes().HasBulletNumber())
3847 num
= para
->GetAttributes().GetBulletNumber();
3849 if (!para
->GetAttributes().GetParagraphStyleName().IsEmpty() && !para
->GetAttributes().GetListStyleName().IsEmpty())
3851 int currentIndent
= para
->GetAttributes().GetLeftIndent();
3853 wxRichTextParagraphStyleDefinition
* paraDef
= styleSheet
->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
3854 wxRichTextListStyleDefinition
* listDef
= styleSheet
->FindListStyle(para
->GetAttributes().GetListStyleName());
3855 if (paraDef
&& !listDef
)
3857 para
->GetAttributes() = paraDef
->GetStyleMergedWithBase(styleSheet
);
3860 else if (listDef
&& !paraDef
)
3862 // Set overall style defined for the list style definition
3863 para
->GetAttributes() = listDef
->GetStyleMergedWithBase(styleSheet
);
3865 // Apply the style for this level
3866 wxRichTextApplyStyle(para
->GetAttributes(), * listDef
->GetLevelAttributes(listDef
->FindLevelForIndent(currentIndent
)));
3869 else if (listDef
&& paraDef
)
3871 // Combines overall list style, style for level, and paragraph style
3872 para
->GetAttributes() = listDef
->CombineWithParagraphStyle(currentIndent
, paraDef
->GetStyleMergedWithBase(styleSheet
));
3876 else if (para
->GetAttributes().GetParagraphStyleName().IsEmpty() && !para
->GetAttributes().GetListStyleName().IsEmpty())
3878 int currentIndent
= para
->GetAttributes().GetLeftIndent();
3880 wxRichTextListStyleDefinition
* listDef
= styleSheet
->FindListStyle(para
->GetAttributes().GetListStyleName());
3882 // Overall list definition style
3883 para
->GetAttributes() = listDef
->GetStyleMergedWithBase(styleSheet
);
3885 // Style for this level
3886 wxRichTextApplyStyle(para
->GetAttributes(), * listDef
->GetLevelAttributes(listDef
->FindLevelForIndent(currentIndent
)));
3890 else if (!para
->GetAttributes().GetParagraphStyleName().IsEmpty() && para
->GetAttributes().GetListStyleName().IsEmpty())
3892 wxRichTextParagraphStyleDefinition
* def
= styleSheet
->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
3895 para
->GetAttributes() = def
->GetStyleMergedWithBase(styleSheet
);
3901 para
->GetAttributes().SetOutlineLevel(outline
);
3903 para
->GetAttributes().SetBulletNumber(num
);
3906 node
= node
->GetNext();
3908 return foundCount
!= 0;
3912 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
3914 wxRichTextBuffer
* buffer
= GetBuffer();
3915 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
3917 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
3918 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
3919 bool specifyLevel
= ((flags
& wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL
) != 0);
3920 bool renumber
= ((flags
& wxRICHTEXT_SETSTYLE_RENUMBER
) != 0);
3922 // Current number, if numbering
3925 wxASSERT (!specifyLevel
|| (specifyLevel
&& (specifiedLevel
>= 0)));
3927 // If we are associated with a control, make undoable; otherwise, apply immediately
3930 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3932 wxRichTextAction
* action
= NULL
;
3934 if (haveControl
&& withUndo
)
3936 action
= new wxRichTextAction(NULL
, _("Change List Style"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
3937 action
->SetRange(range
);
3938 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3941 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3944 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3945 // wxASSERT (para != NULL);
3947 if (para
&& para
->GetChildCount() > 0)
3949 // Stop searching if we're beyond the range of interest
3950 if (para
->GetRange().GetStart() > range
.GetEnd())
3953 if (!para
->GetRange().IsOutside(range
))
3955 // We'll be using a copy of the paragraph to make style changes,
3956 // not updating the buffer directly.
3957 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3959 if (haveControl
&& withUndo
)
3961 newPara
= new wxRichTextParagraph(*para
);
3962 action
->GetNewParagraphs().AppendChild(newPara
);
3964 // Also store the old ones for Undo
3965 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3972 int thisIndent
= newPara
->GetAttributes().GetLeftIndent();
3973 int thisLevel
= specifyLevel
? specifiedLevel
: def
->FindLevelForIndent(thisIndent
);
3975 // How is numbering going to work?
3976 // If we are renumbering, or numbering for the first time, we need to keep
3977 // track of the number for each level. But we might be simply applying a different
3979 // In Word, applying a style to several paragraphs, even if at different levels,
3980 // reverts the level back to the same one. So we could do the same here.
3981 // Renumbering will need to be done when we promote/demote a paragraph.
3983 // Apply the overall list style, and item style for this level
3984 wxRichTextAttr
listStyle(def
->GetCombinedStyleForLevel(thisLevel
, styleSheet
));
3985 wxRichTextApplyStyle(newPara
->GetAttributes(), listStyle
);
3987 // Now we need to do numbering
3990 newPara
->GetAttributes().SetBulletNumber(n
);
3995 else if (!newPara
->GetAttributes().GetListStyleName().IsEmpty())
3997 // if def is NULL, remove list style, applying any associated paragraph style
3998 // to restore the attributes
4000 newPara
->GetAttributes().SetListStyleName(wxEmptyString
);
4001 newPara
->GetAttributes().SetLeftIndent(0, 0);
4002 newPara
->GetAttributes().SetBulletText(wxEmptyString
);
4004 // Eliminate the main list-related attributes
4005 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
);
4007 if (styleSheet
&& !newPara
->GetAttributes().GetParagraphStyleName().IsEmpty())
4009 wxRichTextParagraphStyleDefinition
* def
= styleSheet
->FindParagraphStyle(newPara
->GetAttributes().GetParagraphStyleName());
4012 newPara
->GetAttributes() = def
->GetStyleMergedWithBase(styleSheet
);
4019 node
= node
->GetNext();
4022 // Do action, or delay it until end of batch.
4023 if (haveControl
&& withUndo
)
4024 buffer
->SubmitAction(action
);
4029 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
)
4031 wxRichTextBuffer
* buffer
= GetBuffer();
4032 if (buffer
&& buffer
->GetStyleSheet())
4034 wxRichTextListStyleDefinition
* def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4036 return SetListStyle(range
, def
, flags
, startFrom
, specifiedLevel
);
4041 /// Clear list for given range
4042 bool wxRichTextParagraphLayoutBox::ClearListStyle(const wxRichTextRange
& range
, int flags
)
4044 return SetListStyle(range
, NULL
, flags
);
4047 /// Number/renumber any list elements in the given range
4048 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
4050 return DoNumberList(range
, range
, 0, def
, flags
, startFrom
, specifiedLevel
);
4053 /// Number/renumber any list elements in the given range. Also do promotion or demotion of items, if specified
4054 bool wxRichTextParagraphLayoutBox::DoNumberList(const wxRichTextRange
& range
, const wxRichTextRange
& promotionRange
, int promoteBy
,
4055 wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
4057 wxRichTextBuffer
* buffer
= GetBuffer();
4058 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
4060 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
4061 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
4063 bool specifyLevel
= ((flags
& wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL
) != 0);
4066 bool renumber
= ((flags
& wxRICHTEXT_SETSTYLE_RENUMBER
) != 0);
4068 // Max number of levels
4069 const int maxLevels
= 10;
4071 // The level we're looking at now
4072 int currentLevel
= -1;
4074 // The item number for each level
4075 int levels
[maxLevels
];
4078 // Reset all numbering
4079 for (i
= 0; i
< maxLevels
; i
++)
4081 if (startFrom
!= -1)
4082 levels
[i
] = startFrom
-1;
4083 else if (renumber
) // start again
4086 levels
[i
] = -1; // start from the number we found, if any
4090 wxASSERT(!specifyLevel
|| (specifyLevel
&& (specifiedLevel
>= 0)));
4093 // If we are associated with a control, make undoable; otherwise, apply immediately
4096 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
4098 wxRichTextAction
* action
= NULL
;
4100 if (haveControl
&& withUndo
)
4102 action
= new wxRichTextAction(NULL
, _("Renumber List"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
4103 action
->SetRange(range
);
4104 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
4107 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
4110 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
4111 // wxASSERT (para != NULL);
4113 if (para
&& para
->GetChildCount() > 0)
4115 // Stop searching if we're beyond the range of interest
4116 if (para
->GetRange().GetStart() > range
.GetEnd())
4119 if (!para
->GetRange().IsOutside(range
))
4121 // We'll be using a copy of the paragraph to make style changes,
4122 // not updating the buffer directly.
4123 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
4125 if (haveControl
&& withUndo
)
4127 newPara
= new wxRichTextParagraph(*para
);
4128 action
->GetNewParagraphs().AppendChild(newPara
);
4130 // Also store the old ones for Undo
4131 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
4136 wxRichTextListStyleDefinition
* defToUse
= def
;
4139 if (styleSheet
&& !newPara
->GetAttributes().GetListStyleName().IsEmpty())
4140 defToUse
= styleSheet
->FindListStyle(newPara
->GetAttributes().GetListStyleName());
4145 int thisIndent
= newPara
->GetAttributes().GetLeftIndent();
4146 int thisLevel
= defToUse
->FindLevelForIndent(thisIndent
);
4148 // If we've specified a level to apply to all, change the level.
4149 if (specifiedLevel
!= -1)
4150 thisLevel
= specifiedLevel
;
4152 // Do promotion if specified
4153 if ((promoteBy
!= 0) && !para
->GetRange().IsOutside(promotionRange
))
4155 thisLevel
= thisLevel
- promoteBy
;
4162 // Apply the overall list style, and item style for this level
4163 wxRichTextAttr
listStyle(defToUse
->GetCombinedStyleForLevel(thisLevel
, styleSheet
));
4164 wxRichTextApplyStyle(newPara
->GetAttributes(), listStyle
);
4166 // OK, we've (re)applied the style, now let's get the numbering right.
4168 if (currentLevel
== -1)
4169 currentLevel
= thisLevel
;
4171 // Same level as before, do nothing except increment level's number afterwards
4172 if (currentLevel
== thisLevel
)
4175 // A deeper level: start renumbering all levels after current level
4176 else if (thisLevel
> currentLevel
)
4178 for (i
= currentLevel
+1; i
<= thisLevel
; i
++)
4182 currentLevel
= thisLevel
;
4184 else if (thisLevel
< currentLevel
)
4186 currentLevel
= thisLevel
;
4189 // Use the current numbering if -1 and we have a bullet number already
4190 if (levels
[currentLevel
] == -1)
4192 if (newPara
->GetAttributes().HasBulletNumber())
4193 levels
[currentLevel
] = newPara
->GetAttributes().GetBulletNumber();
4195 levels
[currentLevel
] = 1;
4199 levels
[currentLevel
] ++;
4202 newPara
->GetAttributes().SetBulletNumber(levels
[currentLevel
]);
4204 // Create the bullet text if an outline list
4205 if (listStyle
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
)
4208 for (i
= 0; i
<= currentLevel
; i
++)
4210 if (!text
.IsEmpty())
4212 text
+= wxString::Format(wxT("%d"), levels
[i
]);
4214 newPara
->GetAttributes().SetBulletText(text
);
4220 node
= node
->GetNext();
4223 // Do action, or delay it until end of batch.
4224 if (haveControl
&& withUndo
)
4225 buffer
->SubmitAction(action
);
4230 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
)
4232 wxRichTextBuffer
* buffer
= GetBuffer();
4233 if (buffer
->GetStyleSheet())
4235 wxRichTextListStyleDefinition
* def
= NULL
;
4236 if (!defName
.IsEmpty())
4237 def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4238 return NumberList(range
, def
, flags
, startFrom
, specifiedLevel
);
4243 /// Promote the list items within the given range. promoteBy can be a positive or negative number, e.g. 1 or -1
4244 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy
, const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int specifiedLevel
)
4247 // One strategy is to first work out the range within which renumbering must occur. Then could pass these two ranges
4248 // to NumberList with a flag indicating promotion is required within one of the ranges.
4249 // Find first and last paragraphs in range. Then for first, calculate new indentation and look back until we find
4250 // a paragraph that either has no list style, or has one that is different or whose indentation is less.
4251 // We start renumbering from the para after that different para we found. We specify that the numbering of that
4252 // list position will start from 1.
4253 // Similarly, we look after the last para in the promote range for an indentation that is less (or no list style).
4254 // We can end the renumbering at this point.
4256 // For now, only renumber within the promotion range.
4258 return DoNumberList(range
, range
, promoteBy
, def
, flags
, 1, specifiedLevel
);
4261 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy
, const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int specifiedLevel
)
4263 wxRichTextBuffer
* buffer
= GetBuffer();
4264 if (buffer
->GetStyleSheet())
4266 wxRichTextListStyleDefinition
* def
= NULL
;
4267 if (!defName
.IsEmpty())
4268 def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4269 return PromoteList(promoteBy
, range
, def
, flags
, specifiedLevel
);
4274 /// Fills in the attributes for numbering a paragraph after previousParagraph. It also finds the
4275 /// position of the paragraph that it had to start looking from.
4276 bool wxRichTextParagraphLayoutBox::FindNextParagraphNumber(wxRichTextParagraph
* previousParagraph
, wxRichTextAttr
& attr
) const
4278 if (!previousParagraph
->GetAttributes().HasFlag(wxTEXT_ATTR_BULLET_STYLE
) || previousParagraph
->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
)
4281 wxRichTextBuffer
* buffer
= GetBuffer();
4282 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
4283 if (styleSheet
&& !previousParagraph
->GetAttributes().GetListStyleName().IsEmpty())
4285 wxRichTextListStyleDefinition
* def
= styleSheet
->FindListStyle(previousParagraph
->GetAttributes().GetListStyleName());
4288 // int thisIndent = previousParagraph->GetAttributes().GetLeftIndent();
4289 // int thisLevel = def->FindLevelForIndent(thisIndent);
4291 bool isOutline
= (previousParagraph
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
) != 0;
4293 attr
.SetFlags(previousParagraph
->GetAttributes().GetFlags() & (wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_BULLET_NUMBER
|wxTEXT_ATTR_BULLET_TEXT
|wxTEXT_ATTR_BULLET_NAME
));
4294 if (previousParagraph
->GetAttributes().HasBulletName())
4295 attr
.SetBulletName(previousParagraph
->GetAttributes().GetBulletName());
4296 attr
.SetBulletStyle(previousParagraph
->GetAttributes().GetBulletStyle());
4297 attr
.SetListStyleName(previousParagraph
->GetAttributes().GetListStyleName());
4299 int nextNumber
= previousParagraph
->GetAttributes().GetBulletNumber() + 1;
4300 attr
.SetBulletNumber(nextNumber
);
4304 wxString text
= previousParagraph
->GetAttributes().GetBulletText();
4305 if (!text
.IsEmpty())
4307 int pos
= text
.Find(wxT('.'), true);
4308 if (pos
!= wxNOT_FOUND
)
4310 text
= text
.Mid(0, text
.Length() - pos
- 1);
4313 text
= wxEmptyString
;
4314 if (!text
.IsEmpty())
4316 text
+= wxString::Format(wxT("%d"), nextNumber
);
4317 attr
.SetBulletText(text
);
4331 * wxRichTextParagraph
4332 * This object represents a single paragraph (or in a straight text editor, a line).
4335 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraph
, wxRichTextCompositeObject
)
4337 wxArrayInt
wxRichTextParagraph::sm_defaultTabs
;
4339 wxRichTextParagraph::wxRichTextParagraph(wxRichTextObject
* parent
, wxRichTextAttr
* style
):
4340 wxRichTextCompositeObject(parent
)
4343 SetAttributes(*style
);
4346 wxRichTextParagraph::wxRichTextParagraph(const wxString
& text
, wxRichTextObject
* parent
, wxRichTextAttr
* paraStyle
, wxRichTextAttr
* charStyle
):
4347 wxRichTextCompositeObject(parent
)
4350 SetAttributes(*paraStyle
);
4352 AppendChild(new wxRichTextPlainText(text
, this, charStyle
));
4355 wxRichTextParagraph::~wxRichTextParagraph()
4361 bool wxRichTextParagraph::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int WXUNUSED(descent
), int style
)
4366 // Currently we don't merge these attributes with the parent, but we
4367 // should consider whether we should (e.g. if we set a border colour
4368 // for all paragraphs). But generally box attributes are likely to be
4369 // different for different objects.
4370 wxRect paraRect
= GetRect();
4371 wxRichTextAttr attr
= GetCombinedAttributes();
4372 context
.ApplyVirtualAttributes(attr
, this);
4374 DrawBoxAttributes(dc
, GetBuffer(), attr
, paraRect
);
4376 // Draw the bullet, if any
4377 if (attr
.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE
)
4379 if (attr
.GetLeftSubIndent() != 0)
4381 int spaceBeforePara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingBefore());
4382 int leftIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftIndent());
4384 wxRichTextAttr
bulletAttr(attr
);
4386 // Combine with the font of the first piece of content, if one is specified
4387 if (GetChildren().GetCount() > 0)
4389 wxRichTextObject
* firstObj
= (wxRichTextObject
*) GetChildren().GetFirst()->GetData();
4390 if (!firstObj
->IsFloatable() && firstObj
->GetAttributes().HasFont())
4392 wxRichTextApplyStyle(bulletAttr
, firstObj
->GetAttributes());
4396 // Get line height from first line, if any
4397 wxRichTextLine
* line
= m_cachedLines
.GetFirst() ? (wxRichTextLine
* ) m_cachedLines
.GetFirst()->GetData() : NULL
;
4400 int lineHeight
wxDUMMY_INITIALIZE(0);
4403 lineHeight
= line
->GetSize().y
;
4404 linePos
= line
->GetPosition() + GetPosition();
4409 if (bulletAttr
.HasFont() && GetBuffer())
4410 font
= GetBuffer()->GetFontTable().FindFont(bulletAttr
);
4412 font
= (*wxNORMAL_FONT
);
4414 wxCheckSetFont(dc
, font
);
4416 lineHeight
= dc
.GetCharHeight();
4417 linePos
= GetPosition();
4418 linePos
.y
+= spaceBeforePara
;
4421 wxRect
bulletRect(GetPosition().x
+ leftIndent
, linePos
.y
, linePos
.x
- (GetPosition().x
+ leftIndent
), lineHeight
);
4423 if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
)
4425 if (wxRichTextBuffer::GetRenderer())
4426 wxRichTextBuffer::GetRenderer()->DrawBitmapBullet(this, dc
, bulletAttr
, bulletRect
);
4428 else if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_STANDARD
)
4430 if (wxRichTextBuffer::GetRenderer())
4431 wxRichTextBuffer::GetRenderer()->DrawStandardBullet(this, dc
, bulletAttr
, bulletRect
);
4435 wxString bulletText
= GetBulletText();
4437 if (!bulletText
.empty() && wxRichTextBuffer::GetRenderer())
4438 wxRichTextBuffer::GetRenderer()->DrawTextBullet(this, dc
, bulletAttr
, bulletRect
, bulletText
);
4443 // Draw the range for each line, one object at a time.
4445 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
4448 wxRichTextLine
* line
= node
->GetData();
4449 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
4451 // Lines are specified relative to the paragraph
4453 wxPoint linePosition
= line
->GetPosition() + GetPosition();
4455 // Don't draw if off the screen
4456 if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) != 0) || ((linePosition
.y
+ line
->GetSize().y
) >= rect
.y
&& linePosition
.y
<= rect
.y
+ rect
.height
))
4458 wxPoint objectPosition
= linePosition
;
4459 int maxDescent
= line
->GetDescent();
4461 // Loop through objects until we get to the one within range
4462 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
4467 wxRichTextObject
* child
= node2
->GetData();
4469 if (!child
->IsFloating() && child
->GetRange().GetLength() > 0 && !child
->GetRange().IsOutside(lineRange
) && !lineRange
.IsOutside(range
))
4471 // Draw this part of the line at the correct position
4472 wxRichTextRange
objectRange(child
->GetRange());
4473 objectRange
.LimitTo(lineRange
);
4476 if (child
->IsTopLevel())
4478 objectSize
= child
->GetCachedSize();
4479 objectRange
= child
->GetOwnRange();
4483 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING && wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4484 if (i
< (int) line
->GetObjectSizes().GetCount())
4486 objectSize
.x
= line
->GetObjectSizes()[(size_t) i
];
4492 child
->GetRangeSize(objectRange
, objectSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, objectPosition
);
4496 // Use the child object's width, but the whole line's height
4497 wxRect
childRect(objectPosition
, wxSize(objectSize
.x
, line
->GetSize().y
));
4498 child
->Draw(dc
, context
, objectRange
, selection
, childRect
, maxDescent
, style
);
4500 objectPosition
.x
+= objectSize
.x
;
4503 else if (child
->GetRange().GetStart() > lineRange
.GetEnd())
4504 // Can break out of inner loop now since we've passed this line's range
4507 node2
= node2
->GetNext();
4511 node
= node
->GetNext();
4517 // Get the range width using partial extents calculated for the whole paragraph.
4518 static int wxRichTextGetRangeWidth(const wxRichTextParagraph
& para
, const wxRichTextRange
& range
, const wxArrayInt
& partialExtents
)
4520 wxASSERT(partialExtents
.GetCount() >= (size_t) range
.GetLength());
4522 if (partialExtents
.GetCount() < (size_t) range
.GetLength())
4525 int leftMostPos
= 0;
4526 if (range
.GetStart() - para
.GetRange().GetStart() > 0)
4527 leftMostPos
= partialExtents
[range
.GetStart() - para
.GetRange().GetStart() - 1];
4529 int rightMostPos
= partialExtents
[range
.GetEnd() - para
.GetRange().GetStart()];
4531 int w
= rightMostPos
- leftMostPos
;
4536 /// Lay the item out
4537 bool wxRichTextParagraph::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
)
4539 // Deal with floating objects firstly before the normal layout
4540 wxRichTextBuffer
* buffer
= GetBuffer();
4542 wxRichTextFloatCollector
* collector
= GetContainer()->GetFloatCollector();
4543 wxASSERT(collector
);
4544 LayoutFloat(dc
, context
, rect
, style
, collector
);
4546 wxRichTextAttr attr
= GetCombinedAttributes();
4547 context
.ApplyVirtualAttributes(attr
, this);
4551 // Increase the size of the paragraph due to spacing
4552 int spaceBeforePara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingBefore());
4553 int spaceAfterPara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingAfter());
4554 int leftIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftIndent());
4555 int leftSubIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftSubIndent());
4556 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
4558 int lineSpacing
= 0;
4560 // Let's assume line spacing of 10 is normal, 15 is 1.5, 20 is 2, etc.
4561 if (attr
.HasLineSpacing() && attr
.GetLineSpacing() > 0 && attr
.GetFont().IsOk())
4563 wxCheckSetFont(dc
, attr
.GetFont());
4564 lineSpacing
= (int) (double(dc
.GetCharHeight()) * (double(attr
.GetLineSpacing())/10.0 - 1.0));
4567 // Start position for each line relative to the paragraph
4568 int startPositionFirstLine
= leftIndent
;
4569 int startPositionSubsequentLines
= leftIndent
+ leftSubIndent
;
4571 // If we have a bullet in this paragraph, the start position for the first line's text
4572 // is actually leftIndent + leftSubIndent.
4573 if (attr
.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE
)
4574 startPositionFirstLine
= startPositionSubsequentLines
;
4576 long lastEndPos
= GetRange().GetStart()-1;
4577 long lastCompletedEndPos
= lastEndPos
;
4579 int currentWidth
= 0;
4580 SetPosition(rect
.GetPosition());
4582 wxPoint
currentPosition(0, spaceBeforePara
); // We will calculate lines relative to paragraph
4585 int maxHeight
= currentPosition
.y
;
4590 int lineDescent
= 0;
4592 wxRichTextObjectList::compatibility_iterator node
;
4594 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4596 wxArrayInt partialExtents
;
4599 int paraDescent
= 0;
4601 // This calculates the partial text extents
4602 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, rect
.GetPosition(), & partialExtents
);
4604 node
= m_children
.GetFirst();
4607 wxRichTextObject
* child
= node
->GetData();
4609 //child->SetCachedSize(wxDefaultSize);
4610 child
->Layout(dc
, context
, rect
, style
);
4612 node
= node
->GetNext();
4618 // We may need to go back to a previous child, in which case create the new line,
4619 // find the child corresponding to the start position of the string, and
4622 wxRect availableRect
;
4624 node
= m_children
.GetFirst();
4627 wxRichTextObject
* child
= node
->GetData();
4629 // If floating, ignore. We already laid out floats.
4630 // Also ignore if empty object, except if we haven't got any
4632 if (child
->IsFloating() || !child
->IsShown() ||
4633 (child
->GetRange().GetLength() == 0 && maxHeight
> spaceBeforePara
)
4636 node
= node
->GetNext();
4640 // If this is e.g. a composite text box, it will need to be laid out itself.
4641 // But if just a text fragment or image, for example, this will
4642 // do nothing. NB: won't we need to set the position after layout?
4643 // since for example if position is dependent on vertical line size, we
4644 // can't tell the position until the size is determined. So possibly introduce
4645 // another layout phase.
4647 // We may only be looking at part of a child, if we searched back for wrapping
4648 // and found a suitable point some way into the child. So get the size for the fragment
4651 long nextBreakPos
= GetFirstLineBreakPosition(lastEndPos
+1);
4652 long lastPosToUse
= child
->GetRange().GetEnd();
4653 bool lineBreakInThisObject
= (nextBreakPos
> -1 && nextBreakPos
<= child
->GetRange().GetEnd());
4655 if (lineBreakInThisObject
)
4656 lastPosToUse
= nextBreakPos
;
4659 int childDescent
= 0;
4661 int startOffset
= (lineCount
== 0 ? startPositionFirstLine
: startPositionSubsequentLines
);
4662 availableRect
= wxRect(rect
.x
+ startOffset
, rect
.y
+ currentPosition
.y
,
4663 rect
.width
- startOffset
- rightIndent
, rect
.height
);
4665 if (child
->IsTopLevel())
4667 wxSize oldSize
= child
->GetCachedSize();
4669 child
->Invalidate(wxRICHTEXT_ALL
);
4670 child
->SetPosition(wxPoint(0, 0));
4672 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4673 // lays out the object again using the minimum size
4674 // The position will be determined by its location in its line,
4675 // and not by the child's actual position.
4676 child
->LayoutToBestSize(dc
, context
, buffer
,
4677 attr
, child
->GetAttributes(), availableRect
, parentRect
, style
);
4679 if (oldSize
!= child
->GetCachedSize())
4681 partialExtents
.Clear();
4683 // Recalculate the partial text extents since the child object changed size
4684 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), & partialExtents
);
4688 // Problem: we need to layout composites here for which we need the available width,
4689 // but we can't get the available width without using the float collector which
4690 // needs to know the object height.
4692 if ((nextBreakPos
== -1) && (lastEndPos
== child
->GetRange().GetStart() - 1)) // i.e. we want to get the whole thing
4694 childSize
= child
->GetCachedSize();
4695 childDescent
= child
->GetDescent();
4699 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4700 // Get height only, then the width using the partial extents
4701 GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
);
4702 childSize
.x
= wxRichTextGetRangeWidth(*this, wxRichTextRange(lastEndPos
+1, lastPosToUse
), partialExtents
);
4704 GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, rect
.GetPosition());
4709 int loopIterations
= 0;
4711 // If there are nested objects that need to lay themselves out, we have to do this in a
4712 // loop because the height of the object may well depend on the available width.
4713 // And because of floating object positioning, the available width depends on the
4714 // height of the object and whether it will clash with the floating objects.
4715 // So, we see whether the available width changes due to the presence of floating images.
4716 // If it does, then we'll use the new restricted width to find the object height again.
4717 // If this causes another restriction in the available width, we'll try again, until
4718 // either we lose patience or the available width settles down.
4723 wxRect oldAvailableRect
= availableRect
;
4725 // Available width depends on the floating objects and the line height.
4726 // Note: the floating objects may be placed vertically along the two sides of
4727 // buffer, so we may have different available line widths with different
4728 // [startY, endY]. So, we can't determine how wide the available
4729 // space is until we know the exact line height.
4730 if (childDescent
== 0)
4732 lineHeight
= wxMax(lineHeight
, childSize
.y
);
4733 lineDescent
= maxDescent
;
4734 lineAscent
= maxAscent
;
4738 lineDescent
= wxMax(childDescent
, maxDescent
);
4739 lineAscent
= wxMax(childSize
.y
-childDescent
, maxAscent
);
4741 lineHeight
= wxMax(lineHeight
, (lineDescent
+ lineAscent
));
4742 wxRect floatAvailableRect
= collector
->GetAvailableRect(rect
.y
+ currentPosition
.y
, rect
.y
+ currentPosition
.y
+ lineHeight
);
4744 // Adjust availableRect to the space that is available when taking floating objects into account.
4746 if (floatAvailableRect
.x
+ startOffset
> availableRect
.x
)
4748 int newX
= floatAvailableRect
.x
+ startOffset
;
4749 int newW
= availableRect
.width
- (newX
- availableRect
.x
);
4750 availableRect
.x
= newX
;
4751 availableRect
.width
= newW
;
4754 if (floatAvailableRect
.width
< availableRect
.width
)
4755 availableRect
.width
= floatAvailableRect
.width
;
4757 currentPosition
.x
= availableRect
.x
- rect
.x
;
4759 if (child
->IsTopLevel() && loopIterations
<= 20)
4761 if (availableRect
!= oldAvailableRect
)
4763 wxSize oldSize
= child
->GetCachedSize();
4765 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4766 // lays out the object again using the minimum size
4767 child
->Invalidate(wxRICHTEXT_ALL
);
4768 child
->LayoutToBestSize(dc
, context
, buffer
,
4769 attr
, child
->GetAttributes(), availableRect
, parentRect
, style
);
4770 childSize
= child
->GetCachedSize();
4771 childDescent
= child
->GetDescent();
4773 if (oldSize
!= child
->GetCachedSize())
4775 partialExtents
.Clear();
4777 // Recalculate the partial text extents since the child object changed size
4778 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), & partialExtents
);
4781 // Go around the loop finding the available rect for the given floating objects
4791 if (child
->IsTopLevel())
4793 // We can move it to the correct position at this point
4794 child
->Move(GetPosition() + wxPoint(currentWidth
, currentPosition
.y
));
4798 // 1) There was a line break BEFORE the natural break
4799 // 2) There was a line break AFTER the natural break
4800 // 3) It's the last line
4801 // 4) The child still fits (carry on) - 'else' clause
4803 if ((lineBreakInThisObject
&& (childSize
.x
+ currentWidth
<= availableRect
.width
))
4805 (childSize
.x
+ currentWidth
> availableRect
.width
)
4807 ((childSize
.x
+ currentWidth
<= availableRect
.width
) && !node
->GetNext())
4811 long wrapPosition
= 0;
4812 if ((childSize
.x
+ currentWidth
<= availableRect
.width
) && !node
->GetNext() && !lineBreakInThisObject
)
4813 wrapPosition
= child
->GetRange().GetEnd();
4816 // Find a place to wrap. This may walk back to previous children,
4817 // for example if a word spans several objects.
4818 // Note: one object must contains only one wxTextAtrr, so the line height will not
4819 // change inside one object. Thus, we can pass the remain line width to the
4820 // FindWrapPosition function.
4821 if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos
+1, child
->GetRange().GetEnd()), dc
, context
, availableRect
.width
, wrapPosition
, & partialExtents
))
4823 // If the function failed, just cut it off at the end of this child.
4824 wrapPosition
= child
->GetRange().GetEnd();
4827 // FindWrapPosition can still return a value that will put us in an endless wrapping loop
4828 if (wrapPosition
<= lastCompletedEndPos
)
4829 wrapPosition
= wxMax(lastCompletedEndPos
+1,child
->GetRange().GetEnd());
4831 // Line end position shouldn't be the same as the end, or greater.
4832 if (wrapPosition
>= GetRange().GetEnd())
4833 wrapPosition
= GetRange().GetEnd()-1;
4835 // wxLogDebug(wxT("Split at %ld"), wrapPosition);
4837 // Let's find the actual size of the current line now
4839 wxRichTextRange
actualRange(lastCompletedEndPos
+1, wrapPosition
);
4843 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4844 if (!child
->IsEmpty())
4846 // Get height only, then the width using the partial extents
4847 GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
);
4848 actualSize
.x
= wxRichTextGetRangeWidth(*this, actualRange
, partialExtents
);
4852 GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
4854 currentWidth
= actualSize
.x
;
4856 // The descent for the whole line at this point, is the correct max descent
4857 maxDescent
= childDescent
;
4859 maxAscent
= actualSize
.y
-childDescent
;
4861 // lineHeight is given by the height for the whole line, since it will
4862 // take into account ascend/descend.
4863 lineHeight
= actualSize
.y
;
4865 if (lineHeight
== 0 && buffer
)
4867 wxFont
font(buffer
->GetFontTable().FindFont(attr
));
4868 wxCheckSetFont(dc
, font
);
4869 lineHeight
= dc
.GetCharHeight();
4872 if (maxDescent
== 0)
4875 dc
.GetTextExtent(wxT("X"), & w
, &h
, & maxDescent
);
4879 wxRichTextLine
* line
= AllocateLine(lineCount
);
4881 // Set relative range so we won't have to change line ranges when paragraphs are moved
4882 line
->SetRange(wxRichTextRange(actualRange
.GetStart() - GetRange().GetStart(), actualRange
.GetEnd() - GetRange().GetStart()));
4883 line
->SetPosition(currentPosition
);
4884 line
->SetSize(wxSize(currentWidth
, lineHeight
));
4885 line
->SetDescent(maxDescent
);
4887 maxHeight
= currentPosition
.y
+ lineHeight
;
4889 // Now move down a line. TODO: add margins, spacing
4890 currentPosition
.y
+= lineHeight
;
4891 currentPosition
.y
+= lineSpacing
;
4894 maxWidth
= wxMax(maxWidth
, currentWidth
+startOffset
);
4899 // TODO: account for zero-length objects
4900 // wxASSERT(wrapPosition > lastCompletedEndPos);
4902 lastEndPos
= wrapPosition
;
4903 lastCompletedEndPos
= lastEndPos
;
4907 if (wrapPosition
< GetRange().GetEnd()-1)
4909 // May need to set the node back to a previous one, due to searching back in wrapping
4910 wxRichTextObject
* childAfterWrapPosition
= FindObjectAtPosition(wrapPosition
+1);
4911 if (childAfterWrapPosition
)
4912 node
= m_children
.Find(childAfterWrapPosition
);
4914 node
= node
->GetNext();
4917 node
= node
->GetNext();
4919 // Apply paragraph styles such as alignment to the wrapped line
4920 ApplyParagraphStyle(line
, attr
, availableRect
, dc
);
4924 // We still fit, so don't add a line, and keep going
4925 currentWidth
+= childSize
.x
;
4927 if (childDescent
== 0)
4929 // An object with a zero descend value wants to take up the whole
4930 // height regardless of baseline
4931 lineHeight
= wxMax(lineHeight
, childSize
.y
);
4935 maxDescent
= wxMax(childDescent
, maxDescent
);
4936 maxAscent
= wxMax(childSize
.y
-childDescent
, maxAscent
);
4939 lineHeight
= wxMax(lineHeight
, (maxDescent
+ maxAscent
));
4941 maxWidth
= wxMax(maxWidth
, currentWidth
+startOffset
);
4942 lastEndPos
= child
->GetRange().GetEnd();
4944 node
= node
->GetNext();
4948 //wxASSERT(!(lastCompletedEndPos != -1 && lastCompletedEndPos < GetRange().GetEnd()-1));
4950 // Remove remaining unused line objects, if any
4951 ClearUnusedLines(lineCount
);
4953 // We need to add back the margins etc.
4955 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
4956 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxWidth
, currentPosition
.y
+ spaceAfterPara
));
4957 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
4958 SetCachedSize(marginRect
.GetSize());
4961 // The maximum size is the length of the paragraph stretched out into a line.
4962 // So if there were a single word, or an image, or a fixed-size text box, the object could be shrunk around
4963 // this size. TODO: take into account line breaks.
4965 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
4966 contentRect
= wxRect(wxPoint(0, 0), wxSize(paraSize
.x
+ wxMax(leftIndent
, leftIndent
+ leftSubIndent
) + rightIndent
, currentPosition
.y
+ spaceAfterPara
));
4967 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
4968 SetMaxSize(marginRect
.GetSize());
4971 // Find the greatest minimum size. Currently we only look at non-text objects,
4972 // which isn't ideal but it would be slow to find the maximum word width to
4973 // use as the minimum.
4976 node
= m_children
.GetFirst();
4979 wxRichTextObject
* child
= node
->GetData();
4981 // If floating, ignore. We already laid out floats.
4982 // Also ignore if empty object, except if we haven't got any
4984 if (!child
->IsFloating() && child
->GetRange().GetLength() != 0 && !child
->IsKindOf(CLASSINFO(wxRichTextPlainText
)))
4986 if (child
->GetCachedSize().x
> minWidth
)
4987 minWidth
= child
->GetMinSize().x
;
4989 node
= node
->GetNext();
4992 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
4993 contentRect
= wxRect(wxPoint(0, 0), wxSize(minWidth
, currentPosition
.y
+ spaceAfterPara
));
4994 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
4995 SetMinSize(marginRect
.GetSize());
4998 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4999 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
5000 // Use the text extents to calculate the size of each fragment in each line
5001 wxRichTextLineList::compatibility_iterator lineNode
= m_cachedLines
.GetFirst();
5004 wxRichTextLine
* line
= lineNode
->GetData();
5005 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5007 // Loop through objects until we get to the one within range
5008 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
5012 wxRichTextObject
* child
= node2
->GetData();
5014 if (child
->GetRange().GetLength() > 0 && !child
->GetRange().IsOutside(lineRange
))
5016 wxRichTextRange rangeToUse
= lineRange
;
5017 rangeToUse
.LimitTo(child
->GetRange());
5019 // Find the size of the child from the text extents, and store in an array
5020 // for drawing later
5022 if (rangeToUse
.GetStart() > GetRange().GetStart())
5023 left
= partialExtents
[(rangeToUse
.GetStart()-1) - GetRange().GetStart()];
5024 int right
= partialExtents
[rangeToUse
.GetEnd() - GetRange().GetStart()];
5025 int sz
= right
- left
;
5026 line
->GetObjectSizes().Add(sz
);
5028 else if (child
->GetRange().GetStart() > lineRange
.GetEnd())
5029 // Can break out of inner loop now since we've passed this line's range
5032 node2
= node2
->GetNext();
5035 lineNode
= lineNode
->GetNext();
5043 /// Apply paragraph styles, such as centering, to wrapped lines
5044 /// TODO: take into account box attributes, possibly
5045 void wxRichTextParagraph::ApplyParagraphStyle(wxRichTextLine
* line
, const wxRichTextAttr
& attr
, const wxRect
& rect
, wxDC
& dc
)
5047 if (!attr
.HasAlignment())
5050 wxPoint pos
= line
->GetPosition();
5051 wxSize size
= line
->GetSize();
5053 // centering, right-justification
5054 if (attr
.HasAlignment() && attr
.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE
)
5056 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
5057 pos
.x
= (rect
.GetWidth() - rightIndent
- size
.x
)/2 + pos
.x
;
5058 line
->SetPosition(pos
);
5060 else if (attr
.HasAlignment() && attr
.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT
)
5062 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
5063 pos
.x
= pos
.x
+ rect
.GetWidth() - size
.x
- rightIndent
;
5064 line
->SetPosition(pos
);
5068 /// Insert text at the given position
5069 bool wxRichTextParagraph::InsertText(long pos
, const wxString
& text
)
5071 wxRichTextObject
* childToUse
= NULL
;
5072 wxRichTextObjectList::compatibility_iterator nodeToUse
= wxRichTextObjectList::compatibility_iterator();
5074 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5077 wxRichTextObject
* child
= node
->GetData();
5078 if (child
->GetRange().Contains(pos
) && child
->GetRange().GetLength() > 0)
5085 node
= node
->GetNext();
5090 wxRichTextPlainText
* textObject
= wxDynamicCast(childToUse
, wxRichTextPlainText
);
5093 int posInString
= pos
- textObject
->GetRange().GetStart();
5095 wxString newText
= textObject
->GetText().Mid(0, posInString
) +
5096 text
+ textObject
->GetText().Mid(posInString
);
5097 textObject
->SetText(newText
);
5099 int textLength
= text
.length();
5101 textObject
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart(),
5102 textObject
->GetRange().GetEnd() + textLength
));
5104 // Increment the end range of subsequent fragments in this paragraph.
5105 // We'll set the paragraph range itself at a higher level.
5107 wxRichTextObjectList::compatibility_iterator node
= nodeToUse
->GetNext();
5110 wxRichTextObject
* child
= node
->GetData();
5111 child
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart() + textLength
,
5112 textObject
->GetRange().GetEnd() + textLength
));
5114 node
= node
->GetNext();
5121 // TODO: if not a text object, insert at closest position, e.g. in front of it
5127 // Don't pass parent initially to suppress auto-setting of parent range.
5128 // We'll do that at a higher level.
5129 wxRichTextPlainText
* textObject
= new wxRichTextPlainText(text
, this);
5131 AppendChild(textObject
);
5138 void wxRichTextParagraph::Copy(const wxRichTextParagraph
& obj
)
5140 wxRichTextCompositeObject::Copy(obj
);
5143 /// Clear the cached lines
5144 void wxRichTextParagraph::ClearLines()
5146 WX_CLEAR_LIST(wxRichTextLineList
, m_cachedLines
);
5149 /// Get/set the object size for the given range. Returns false if the range
5150 /// is invalid for this object.
5151 bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
5153 if (!range
.IsWithin(GetRange()))
5156 if (flags
& wxRICHTEXT_UNFORMATTED
)
5158 // Just use unformatted data, assume no line breaks
5161 wxArrayInt childExtents
;
5170 int maxLineHeight
= 0;
5172 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5175 wxRichTextObject
* child
= node
->GetData();
5176 if (!child
->GetRange().IsOutside(range
))
5178 // Floating objects have a zero size within the paragraph.
5179 if (child
->IsFloating())
5184 if (partialExtents
->GetCount() > 0)
5185 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5189 partialExtents
->Add(0 /* zero size */ + lastSize
);
5196 wxRichTextRange rangeToUse
= range
;
5197 rangeToUse
.LimitTo(child
->GetRange());
5198 int childDescent
= 0;
5200 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we've already cached the size,
5201 // but it's only going to be used after caching has taken place.
5202 if ((flags
& wxRICHTEXT_HEIGHT_ONLY
) && child
->GetCachedSize().y
!= 0)
5204 childDescent
= child
->GetDescent();
5205 childSize
= child
->GetCachedSize();
5207 if (childDescent
== 0)
5209 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5213 maxDescent
= wxMax(maxDescent
, childDescent
);
5214 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5217 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5219 sz
.y
= wxMax(sz
.y
, maxLineHeight
);
5220 sz
.x
+= childSize
.x
;
5221 descent
= maxDescent
;
5223 else if (child
->IsTopLevel())
5225 childDescent
= child
->GetDescent();
5226 childSize
= child
->GetCachedSize();
5228 if (childDescent
== 0)
5230 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5234 maxDescent
= wxMax(maxDescent
, childDescent
);
5235 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5238 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5240 sz
.y
= wxMax(sz
.y
, maxLineHeight
);
5241 sz
.x
+= childSize
.x
;
5242 descent
= maxDescent
;
5244 // FIXME: this won't change the original values.
5245 // Should we be calling GetRangeSize above instead of using cached values?
5247 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange()))
5249 child
->SetCachedSize(childSize
);
5250 child
->SetDescent(childDescent
);
5257 if (partialExtents
->GetCount() > 0)
5258 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5262 partialExtents
->Add(childSize
.x
+ lastSize
);
5265 else if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, context
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
), p
))
5267 if (childDescent
== 0)
5269 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5273 maxDescent
= wxMax(maxDescent
, childDescent
);
5274 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5277 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5279 sz
.y
= wxMax(sz
.y
, maxLineHeight
);
5280 sz
.x
+= childSize
.x
;
5281 descent
= maxDescent
;
5283 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange()))
5285 child
->SetCachedSize(childSize
);
5286 child
->SetDescent(childDescent
);
5292 if (partialExtents
->GetCount() > 0)
5293 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5298 for (i
= 0; i
< childExtents
.GetCount(); i
++)
5300 partialExtents
->Add(childExtents
[i
] + lastSize
);
5310 node
= node
->GetNext();
5316 // Use formatted data, with line breaks
5319 // We're going to loop through each line, and then for each line,
5320 // call GetRangeSize for the fragment that comprises that line.
5321 // Only we have to do that multiple times within the line, because
5322 // the line may be broken into pieces. For now ignore line break commands
5323 // (so we can assume that getting the unformatted size for a fragment
5324 // within a line is the actual size)
5326 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5329 wxRichTextLine
* line
= node
->GetData();
5330 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5331 if (!lineRange
.IsOutside(range
))
5335 int maxLineHeight
= 0;
5336 int maxLineWidth
= 0;
5338 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
5341 wxRichTextObject
* child
= node2
->GetData();
5343 if (!child
->IsFloating() && !child
->GetRange().IsOutside(lineRange
))
5345 wxRichTextRange rangeToUse
= lineRange
;
5346 rangeToUse
.LimitTo(child
->GetRange());
5347 if (child
->IsTopLevel())
5348 rangeToUse
= child
->GetOwnRange();
5351 int childDescent
= 0;
5352 if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, context
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
)))
5354 if (childDescent
== 0)
5356 // Assume that if descent is zero, this child can occupy the full line height
5357 // and does not need space for the line's maximum descent. So we influence
5358 // the overall max line height only.
5359 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5363 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5364 maxDescent
= wxMax(maxAscent
, childDescent
);
5366 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5367 maxLineWidth
+= childSize
.x
;
5371 node2
= node2
->GetNext();
5374 descent
= wxMax(descent
, maxDescent
);
5376 // Increase size by a line (TODO: paragraph spacing)
5377 sz
.y
+= maxLineHeight
;
5378 sz
.x
= wxMax(sz
.x
, maxLineWidth
);
5380 node
= node
->GetNext();
5387 /// Finds the absolute position and row height for the given character position
5388 bool wxRichTextParagraph::FindPosition(wxDC
& dc
, wxRichTextDrawingContext
& context
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
5392 wxRichTextLine
* line
= ((wxRichTextParagraphLayoutBox
*)GetParent())->GetLineAtPosition(0);
5394 *height
= line
->GetSize().y
;
5396 *height
= dc
.GetCharHeight();
5398 // -1 means 'the start of the buffer'.
5401 pt
= pt
+ line
->GetPosition();
5406 // The final position in a paragraph is taken to mean the position
5407 // at the start of the next paragraph.
5408 if (index
== GetRange().GetEnd())
5410 wxRichTextParagraphLayoutBox
* parent
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
5411 wxASSERT( parent
!= NULL
);
5413 // Find the height at the next paragraph, if any
5414 wxRichTextLine
* line
= parent
->GetLineAtPosition(index
+ 1);
5417 *height
= line
->GetSize().y
;
5418 pt
= line
->GetAbsolutePosition();
5422 *height
= dc
.GetCharHeight();
5423 int indent
= ConvertTenthsMMToPixels(dc
, m_attributes
.GetLeftIndent());
5424 pt
= wxPoint(indent
, GetCachedSize().y
);
5430 if (index
< GetRange().GetStart() || index
> GetRange().GetEnd())
5433 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5436 wxRichTextLine
* line
= node
->GetData();
5437 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5438 if (index
>= lineRange
.GetStart() && index
<= lineRange
.GetEnd())
5440 // If this is the last point in the line, and we're forcing the
5441 // returned value to be the start of the next line, do the required
5443 if (index
== lineRange
.GetEnd() && forceLineStart
)
5445 if (node
->GetNext())
5447 wxRichTextLine
* nextLine
= node
->GetNext()->GetData();
5448 *height
= nextLine
->GetSize().y
;
5449 pt
= nextLine
->GetAbsolutePosition();
5454 pt
.y
= line
->GetPosition().y
+ GetPosition().y
;
5456 wxRichTextRange
r(lineRange
.GetStart(), index
);
5460 // We find the size of the line up to this point,
5461 // then we can add this size to the line start position and
5462 // paragraph start position to find the actual position.
5464 if (GetRangeSize(r
, rangeSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, line
->GetPosition()+ GetPosition()))
5466 pt
.x
= line
->GetPosition().x
+ GetPosition().x
+ rangeSize
.x
;
5467 *height
= line
->GetSize().y
;
5474 node
= node
->GetNext();
5480 /// Hit-testing: returns a flag indicating hit test details, plus
5481 /// information about position
5482 int wxRichTextParagraph::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
5485 return wxRICHTEXT_HITTEST_NONE
;
5487 // If we're in the top-level container, then we can return
5488 // a suitable hit test code even if the point is outside the container area,
5489 // so that we can position the caret sensibly even if we don't
5490 // click on valid content. If we're not at the top-level, and the point
5491 // is not within this paragraph object, then we don't want to stop more
5492 // precise hit-testing from working prematurely, so return immediately.
5493 // NEW STRATEGY: use the parent boundary to test whether we're in the
5494 // right region, not the paragraph, since the paragraph may be positioned
5495 // some way in from where the user clicks.
5498 wxRichTextObject
* tempObj
, *tempContextObj
;
5499 if (GetParent() && GetParent()->wxRichTextObject::HitTest(dc
, context
, pt
, tmpPos
, & tempObj
, & tempContextObj
, flags
) == wxRICHTEXT_HITTEST_NONE
)
5500 return wxRICHTEXT_HITTEST_NONE
;
5503 wxRichTextObjectList::compatibility_iterator objNode
= m_children
.GetFirst();
5506 wxRichTextObject
* child
= objNode
->GetData();
5507 // Don't recurse if we have wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS,
5508 // and also, if this seems composite but actually is marked as atomic,
5510 if (child
->IsTopLevel() && ((flags
& wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS
) == 0) &&
5511 (! (((flags
& wxRICHTEXT_HITTEST_HONOUR_ATOMIC
) != 0) && child
->IsAtomic())))
5514 int hitTest
= child
->HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
);
5515 if (hitTest
!= wxRICHTEXT_HITTEST_NONE
)
5520 objNode
= objNode
->GetNext();
5523 wxPoint paraPos
= GetPosition();
5525 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5528 wxRichTextLine
* line
= node
->GetData();
5529 wxPoint linePos
= paraPos
+ line
->GetPosition();
5530 wxSize lineSize
= line
->GetSize();
5531 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5533 if (pt
.y
<= linePos
.y
+ lineSize
.y
)
5535 if (pt
.x
< linePos
.x
)
5537 textPosition
= lineRange
.GetStart();
5538 *obj
= FindObjectAtPosition(textPosition
);
5539 *contextObj
= GetContainer();
5540 return wxRICHTEXT_HITTEST_BEFORE
|wxRICHTEXT_HITTEST_OUTSIDE
;
5542 else if (pt
.x
>= (linePos
.x
+ lineSize
.x
))
5544 textPosition
= lineRange
.GetEnd();
5545 *obj
= FindObjectAtPosition(textPosition
);
5546 *contextObj
= GetContainer();
5547 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
;
5551 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5552 wxArrayInt partialExtents
;
5557 // This calculates the partial text extents
5558 GetRangeSize(lineRange
, paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, linePos
, & partialExtents
);
5560 int lastX
= linePos
.x
;
5562 for (i
= 0; i
< partialExtents
.GetCount(); i
++)
5564 int nextX
= partialExtents
[i
] + linePos
.x
;
5566 if (pt
.x
>= lastX
&& pt
.x
<= nextX
)
5568 textPosition
= i
+ lineRange
.GetStart(); // minus 1?
5570 *obj
= FindObjectAtPosition(textPosition
);
5571 *contextObj
= GetContainer();
5573 // So now we know it's between i-1 and i.
5574 // Let's see if we can be more precise about
5575 // which side of the position it's on.
5577 int midPoint
= (nextX
+ lastX
)/2;
5578 if (pt
.x
>= midPoint
)
5579 return wxRICHTEXT_HITTEST_AFTER
;
5581 return wxRICHTEXT_HITTEST_BEFORE
;
5588 int lastX
= linePos
.x
;
5589 for (i
= lineRange
.GetStart(); i
<= lineRange
.GetEnd(); i
++)
5594 wxRichTextRange
rangeToUse(lineRange
.GetStart(), i
);
5596 GetRangeSize(rangeToUse
, childSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, linePos
);
5598 int nextX
= childSize
.x
+ linePos
.x
;
5600 if (pt
.x
>= lastX
&& pt
.x
<= nextX
)
5604 *obj
= FindObjectAtPosition(textPosition
);
5605 *contextObj
= GetContainer();
5607 // So now we know it's between i-1 and i.
5608 // Let's see if we can be more precise about
5609 // which side of the position it's on.
5611 int midPoint
= (nextX
+ lastX
)/2;
5612 if (pt
.x
>= midPoint
)
5613 return wxRICHTEXT_HITTEST_AFTER
;
5615 return wxRICHTEXT_HITTEST_BEFORE
;
5626 node
= node
->GetNext();
5629 return wxRICHTEXT_HITTEST_NONE
;
5632 /// Split an object at this position if necessary, and return
5633 /// the previous object, or NULL if inserting at beginning.
5634 wxRichTextObject
* wxRichTextParagraph::SplitAt(long pos
, wxRichTextObject
** previousObject
)
5636 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5639 wxRichTextObject
* child
= node
->GetData();
5641 if (pos
== child
->GetRange().GetStart())
5645 if (node
->GetPrevious())
5646 *previousObject
= node
->GetPrevious()->GetData();
5648 *previousObject
= NULL
;
5654 if (child
->GetRange().Contains(pos
))
5656 // This should create a new object, transferring part of
5657 // the content to the old object and the rest to the new object.
5658 wxRichTextObject
* newObject
= child
->DoSplit(pos
);
5660 // If we couldn't split this object, just insert in front of it.
5663 // Maybe this is an empty string, try the next one
5668 // Insert the new object after 'child'
5669 if (node
->GetNext())
5670 m_children
.Insert(node
->GetNext(), newObject
);
5672 m_children
.Append(newObject
);
5673 newObject
->SetParent(this);
5676 *previousObject
= child
;
5682 node
= node
->GetNext();
5685 *previousObject
= NULL
;
5689 /// Move content to a list from obj on
5690 void wxRichTextParagraph::MoveToList(wxRichTextObject
* obj
, wxList
& list
)
5692 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(obj
);
5695 wxRichTextObject
* child
= node
->GetData();
5698 wxRichTextObjectList::compatibility_iterator oldNode
= node
;
5700 node
= node
->GetNext();
5702 m_children
.DeleteNode(oldNode
);
5706 /// Add content back from list
5707 void wxRichTextParagraph::MoveFromList(wxList
& list
)
5709 for (wxList::compatibility_iterator node
= list
.GetFirst(); node
; node
= node
->GetNext())
5711 AppendChild((wxRichTextObject
*) node
->GetData());
5716 void wxRichTextParagraph::CalculateRange(long start
, long& end
)
5718 wxRichTextCompositeObject::CalculateRange(start
, end
);
5720 // Add one for end of paragraph
5723 m_range
.SetRange(start
, end
);
5726 /// Find the object at the given position
5727 wxRichTextObject
* wxRichTextParagraph::FindObjectAtPosition(long position
)
5729 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5732 wxRichTextObject
* obj
= node
->GetData();
5733 if (obj
->GetRange().Contains(position
) ||
5734 obj
->GetRange().GetStart() == position
||
5735 obj
->GetRange().GetEnd() == position
)
5738 node
= node
->GetNext();
5743 /// Get the plain text searching from the start or end of the range.
5744 /// The resulting string may be shorter than the range given.
5745 bool wxRichTextParagraph::GetContiguousPlainText(wxString
& text
, const wxRichTextRange
& range
, bool fromStart
)
5747 text
= wxEmptyString
;
5751 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5754 wxRichTextObject
* obj
= node
->GetData();
5755 if (!obj
->GetRange().IsOutside(range
))
5757 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5760 text
+= textObj
->GetTextForRange(range
);
5768 node
= node
->GetNext();
5773 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetLast();
5776 wxRichTextObject
* obj
= node
->GetData();
5777 if (!obj
->GetRange().IsOutside(range
))
5779 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5782 text
= textObj
->GetTextForRange(range
) + text
;
5786 text
= wxT(" ") + text
;
5790 node
= node
->GetPrevious();
5797 /// Find a suitable wrap position.
5798 bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange
& range
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int availableSpace
, long& wrapPosition
, wxArrayInt
* partialExtents
)
5800 if (range
.GetLength() <= 0)
5803 // Find the first position where the line exceeds the available space.
5805 long breakPosition
= range
.GetEnd();
5807 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5808 if (partialExtents
&& partialExtents
->GetCount() >= (size_t) (GetRange().GetLength()-1)) // the final position in a paragraph is the newline
5812 if (range
.GetStart() > GetRange().GetStart())
5813 widthBefore
= (*partialExtents
)[range
.GetStart() - GetRange().GetStart() - 1];
5818 for (i
= (size_t) range
.GetStart(); i
<= (size_t) range
.GetEnd(); i
++)
5820 int widthFromStartOfThisRange
= (*partialExtents
)[i
- GetRange().GetStart()] - widthBefore
;
5822 if (widthFromStartOfThisRange
> availableSpace
)
5824 breakPosition
= i
-1;
5832 // Binary chop for speed
5833 long minPos
= range
.GetStart();
5834 long maxPos
= range
.GetEnd();
5837 if (minPos
== maxPos
)
5840 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
5842 if (sz
.x
> availableSpace
)
5843 breakPosition
= minPos
- 1;
5846 else if ((maxPos
- minPos
) == 1)
5849 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
5851 if (sz
.x
> availableSpace
)
5852 breakPosition
= minPos
- 1;
5855 GetRangeSize(wxRichTextRange(range
.GetStart(), maxPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
5856 if (sz
.x
> availableSpace
)
5857 breakPosition
= maxPos
-1;
5863 long nextPos
= minPos
+ ((maxPos
- minPos
) / 2);
5866 GetRangeSize(wxRichTextRange(range
.GetStart(), nextPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
5868 if (sz
.x
> availableSpace
)
5880 // Now we know the last position on the line.
5881 // Let's try to find a word break.
5884 if (GetContiguousPlainText(plainText
, wxRichTextRange(range
.GetStart(), breakPosition
), false))
5886 int newLinePos
= plainText
.Find(wxRichTextLineBreakChar
);
5887 if (newLinePos
!= wxNOT_FOUND
)
5889 breakPosition
= wxMax(0, range
.GetStart() + newLinePos
);
5893 int spacePos
= plainText
.Find(wxT(' '), true);
5894 int tabPos
= plainText
.Find(wxT('\t'), true);
5895 int pos
= wxMax(spacePos
, tabPos
);
5896 if (pos
!= wxNOT_FOUND
)
5898 int positionsFromEndOfString
= plainText
.length() - pos
- 1;
5899 breakPosition
= breakPosition
- positionsFromEndOfString
;
5904 wrapPosition
= breakPosition
;
5909 /// Get the bullet text for this paragraph.
5910 wxString
wxRichTextParagraph::GetBulletText()
5912 if (GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
||
5913 (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
))
5914 return wxEmptyString
;
5916 int number
= GetAttributes().GetBulletNumber();
5919 if ((GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ARABIC
) || (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
))
5921 text
.Printf(wxT("%d"), number
);
5923 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_UPPER
)
5925 // TODO: Unicode, and also check if number > 26
5926 text
.Printf(wxT("%c"), (wxChar
) (number
+64));
5928 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_LOWER
)
5930 // TODO: Unicode, and also check if number > 26
5931 text
.Printf(wxT("%c"), (wxChar
) (number
+96));
5933 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER
)
5935 text
= wxRichTextDecimalToRoman(number
);
5937 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER
)
5939 text
= wxRichTextDecimalToRoman(number
);
5942 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
)
5944 text
= GetAttributes().GetBulletText();
5947 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
)
5949 // The outline style relies on the text being computed statically,
5950 // since it depends on other levels points (e.g. 1.2.1.1). So normally the bullet text
5951 // should be stored in the attributes; if not, just use the number for this
5952 // level, as previously computed.
5953 if (!GetAttributes().GetBulletText().IsEmpty())
5954 text
= GetAttributes().GetBulletText();
5957 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PARENTHESES
)
5959 text
= wxT("(") + text
+ wxT(")");
5961 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_RIGHT_PARENTHESIS
)
5963 text
= text
+ wxT(")");
5966 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PERIOD
)
5974 /// Allocate or reuse a line object
5975 wxRichTextLine
* wxRichTextParagraph::AllocateLine(int pos
)
5977 if (pos
< (int) m_cachedLines
.GetCount())
5979 wxRichTextLine
* line
= m_cachedLines
.Item(pos
)->GetData();
5985 wxRichTextLine
* line
= new wxRichTextLine(this);
5986 m_cachedLines
.Append(line
);
5991 /// Clear remaining unused line objects, if any
5992 bool wxRichTextParagraph::ClearUnusedLines(int lineCount
)
5994 int cachedLineCount
= m_cachedLines
.GetCount();
5995 if ((int) cachedLineCount
> lineCount
)
5997 for (int i
= 0; i
< (int) (cachedLineCount
- lineCount
); i
++)
5999 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetLast();
6000 wxRichTextLine
* line
= node
->GetData();
6001 m_cachedLines
.Erase(node
);
6008 /// Get combined attributes of the base style, paragraph style and character style. We use this to dynamically
6009 /// retrieve the actual style.
6010 wxRichTextAttr
wxRichTextParagraph::GetCombinedAttributes(const wxRichTextAttr
& contentStyle
, bool includingBoxAttr
) const
6012 wxRichTextAttr attr
;
6013 wxRichTextParagraphLayoutBox
* buf
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
6016 attr
= buf
->GetBasicStyle();
6017 if (!includingBoxAttr
)
6019 attr
.GetTextBoxAttr().Reset();
6020 // The background colour will be painted by the container, and we don't
6021 // want to unnecessarily overwrite the background when we're drawing text
6022 // because this may erase the guideline (which appears just under the text
6023 // if there's no padding).
6024 attr
.SetFlags(attr
.GetFlags() & ~wxTEXT_ATTR_BACKGROUND_COLOUR
);
6026 wxRichTextApplyStyle(attr
, GetAttributes());
6029 attr
= GetAttributes();
6031 wxRichTextApplyStyle(attr
, contentStyle
);
6035 /// Get combined attributes of the base style and paragraph style.
6036 wxRichTextAttr
wxRichTextParagraph::GetCombinedAttributes(bool includingBoxAttr
) const
6038 wxRichTextAttr attr
;
6039 wxRichTextParagraphLayoutBox
* buf
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
6042 attr
= buf
->GetBasicStyle();
6043 if (!includingBoxAttr
)
6044 attr
.GetTextBoxAttr().Reset();
6045 wxRichTextApplyStyle(attr
, GetAttributes());
6048 attr
= GetAttributes();
6053 // Create default tabstop array
6054 void wxRichTextParagraph::InitDefaultTabs()
6056 // create a default tab list at 10 mm each.
6057 for (int i
= 0; i
< 20; ++i
)
6059 sm_defaultTabs
.Add(i
*100);
6063 // Clear default tabstop array
6064 void wxRichTextParagraph::ClearDefaultTabs()
6066 sm_defaultTabs
.Clear();
6069 void wxRichTextParagraph::LayoutFloat(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, int style
, wxRichTextFloatCollector
* floatCollector
)
6071 wxRichTextObjectList::compatibility_iterator node
= GetChildren().GetFirst();
6074 wxRichTextObject
* anchored
= node
->GetData();
6075 if (anchored
&& anchored
->IsFloating() && !floatCollector
->HasFloat(anchored
))
6079 anchored
->GetRangeSize(anchored
->GetRange(), size
, descent
, dc
, context
, style
);
6082 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().IsValid())
6084 offsetY
= anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetValue();
6085 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
6087 offsetY
= ConvertTenthsMMToPixels(dc
, offsetY
);
6091 int pos
= floatCollector
->GetFitPosition(anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode(), rect
.y
+ offsetY
, size
.y
);
6093 /* Update the offset */
6094 int newOffsetY
= pos
- rect
.y
;
6095 if (newOffsetY
!= offsetY
)
6097 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
6098 newOffsetY
= ConvertPixelsToTenthsMM(dc
, newOffsetY
);
6099 anchored
->GetAttributes().GetTextBoxAttr().GetTop().SetValue(newOffsetY
);
6102 if (anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT
)
6104 else if (anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT
)
6105 x
= rect
.x
+ rect
.width
- size
.x
;
6107 anchored
->SetPosition(wxPoint(x
, pos
));
6108 anchored
->SetCachedSize(size
);
6109 floatCollector
->CollectFloat(this, anchored
);
6112 node
= node
->GetNext();
6116 // Get the first position from pos that has a line break character.
6117 long wxRichTextParagraph::GetFirstLineBreakPosition(long pos
)
6119 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
6122 wxRichTextObject
* obj
= node
->GetData();
6123 if (pos
>= obj
->GetRange().GetStart() && pos
<= obj
->GetRange().GetEnd())
6125 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
6128 long breakPos
= textObj
->GetFirstLineBreakPosition(pos
);
6133 node
= node
->GetNext();
6140 * This object represents a line in a paragraph, and stores
6141 * offsets from the start of the paragraph representing the
6142 * start and end positions of the line.
6145 wxRichTextLine::wxRichTextLine(wxRichTextParagraph
* parent
)
6151 void wxRichTextLine::Init(wxRichTextParagraph
* parent
)
6154 m_range
.SetRange(-1, -1);
6155 m_pos
= wxPoint(0, 0);
6156 m_size
= wxSize(0, 0);
6158 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6159 m_objectSizes
.Clear();
6164 void wxRichTextLine::Copy(const wxRichTextLine
& obj
)
6166 m_range
= obj
.m_range
;
6167 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6168 m_objectSizes
= obj
.m_objectSizes
;
6172 /// Get the absolute object position
6173 wxPoint
wxRichTextLine::GetAbsolutePosition() const
6175 return m_parent
->GetPosition() + m_pos
;
6178 /// Get the absolute range
6179 wxRichTextRange
wxRichTextLine::GetAbsoluteRange() const
6181 wxRichTextRange
range(m_range
.GetStart() + m_parent
->GetRange().GetStart(), 0);
6182 range
.SetEnd(range
.GetStart() + m_range
.GetLength()-1);
6187 * wxRichTextPlainText
6188 * This object represents a single piece of text.
6191 IMPLEMENT_DYNAMIC_CLASS(wxRichTextPlainText
, wxRichTextObject
)
6193 wxRichTextPlainText::wxRichTextPlainText(const wxString
& text
, wxRichTextObject
* parent
, wxRichTextAttr
* style
):
6194 wxRichTextObject(parent
)
6197 SetAttributes(*style
);
6202 #define USE_KERNING_FIX 1
6204 // If insufficient tabs are defined, this is the tab width used
6205 #define WIDTH_FOR_DEFAULT_TABS 50
6208 bool wxRichTextPlainText::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int WXUNUSED(style
))
6210 wxRichTextParagraph
* para
= wxDynamicCast(GetParent(), wxRichTextParagraph
);
6211 wxASSERT (para
!= NULL
);
6213 wxRichTextAttr
textAttr(para
? para
->GetCombinedAttributes(GetAttributes(), false /* no box attributes */) : GetAttributes());
6214 context
.ApplyVirtualAttributes(textAttr
, this);
6216 // Let's make the assumption for now that for content in a paragraph, including
6217 // text, we never have a discontinuous selection. So we only deal with a
6219 wxRichTextRange selectionRange
;
6220 if (selection
.IsValid())
6222 wxRichTextRangeArray selectionRanges
= selection
.GetSelectionForObject(this);
6223 if (selectionRanges
.GetCount() > 0)
6224 selectionRange
= selectionRanges
[0];
6226 selectionRange
= wxRICHTEXT_NO_SELECTION
;
6229 selectionRange
= wxRICHTEXT_NO_SELECTION
;
6231 int offset
= GetRange().GetStart();
6233 // Replace line break characters with spaces
6234 wxString str
= m_text
;
6235 wxString toRemove
= wxRichTextLineBreakChar
;
6236 str
.Replace(toRemove
, wxT(" "));
6237 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS
))
6240 long len
= range
.GetLength();
6241 wxString stringChunk
= str
.Mid(range
.GetStart() - offset
, (size_t) len
);
6243 // Test for the optimized situations where all is selected, or none
6246 wxFont
textFont(GetBuffer()->GetFontTable().FindFont(textAttr
));
6247 wxCheckSetFont(dc
, textFont
);
6248 int charHeight
= dc
.GetCharHeight();
6251 if ( textFont
.IsOk() )
6253 if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
) )
6255 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6256 textFont
.SetPointSize( static_cast<int>(size
) );
6259 wxCheckSetFont(dc
, textFont
);
6261 else if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) )
6263 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6264 textFont
.SetPointSize( static_cast<int>(size
) );
6266 int sub_height
= static_cast<int>( static_cast<double>(charHeight
) / wxSCRIPT_MUL_FACTOR
);
6267 y
= rect
.y
+ (rect
.height
- sub_height
+ (descent
- m_descent
));
6268 wxCheckSetFont(dc
, textFont
);
6273 y
= rect
.y
+ (rect
.height
- charHeight
- (descent
- m_descent
));
6279 y
= rect
.y
+ (rect
.height
- charHeight
- (descent
- m_descent
));
6282 // TODO: new selection code
6284 // (a) All selected.
6285 if (selectionRange
.GetStart() <= range
.GetStart() && selectionRange
.GetEnd() >= range
.GetEnd())
6287 DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, true);
6289 // (b) None selected.
6290 else if (selectionRange
.GetEnd() < range
.GetStart() || selectionRange
.GetStart() > range
.GetEnd())
6292 // Draw all unselected
6293 DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, false);
6297 // (c) Part selected, part not
6298 // Let's draw unselected chunk, selected chunk, then unselected chunk.
6300 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6302 // 1. Initial unselected chunk, if any, up until start of selection.
6303 if (selectionRange
.GetStart() > range
.GetStart() && selectionRange
.GetStart() <= range
.GetEnd())
6305 int r1
= range
.GetStart();
6306 int s1
= selectionRange
.GetStart()-1;
6307 int fragmentLen
= s1
- r1
+ 1;
6308 if (fragmentLen
< 0)
6310 wxLogDebug(wxT("Mid(%d, %d"), (int)(r1
- offset
), (int)fragmentLen
);
6312 wxString stringFragment
= str
.Mid(r1
- offset
, fragmentLen
);
6314 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false);
6317 if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
)
6319 // Compensate for kerning difference
6320 wxString
stringFragment2(str
.Mid(r1
- offset
, fragmentLen
+1));
6321 wxString
stringFragment3(str
.Mid(r1
- offset
+ fragmentLen
, 1));
6323 wxCoord w1
, h1
, w2
, h2
, w3
, h3
;
6324 dc
.GetTextExtent(stringFragment
, & w1
, & h1
);
6325 dc
.GetTextExtent(stringFragment2
, & w2
, & h2
);
6326 dc
.GetTextExtent(stringFragment3
, & w3
, & h3
);
6328 int kerningDiff
= (w1
+ w3
) - w2
;
6329 x
= x
- kerningDiff
;
6334 // 2. Selected chunk, if any.
6335 if (selectionRange
.GetEnd() >= range
.GetStart())
6337 int s1
= wxMax(selectionRange
.GetStart(), range
.GetStart());
6338 int s2
= wxMin(selectionRange
.GetEnd(), range
.GetEnd());
6340 int fragmentLen
= s2
- s1
+ 1;
6341 if (fragmentLen
< 0)
6343 wxLogDebug(wxT("Mid(%d, %d"), (int)(s1
- offset
), (int)fragmentLen
);
6345 wxString stringFragment
= str
.Mid(s1
- offset
, fragmentLen
);
6347 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, true);
6350 if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
)
6352 // Compensate for kerning difference
6353 wxString
stringFragment2(str
.Mid(s1
- offset
, fragmentLen
+1));
6354 wxString
stringFragment3(str
.Mid(s1
- offset
+ fragmentLen
, 1));
6356 wxCoord w1
, h1
, w2
, h2
, w3
, h3
;
6357 dc
.GetTextExtent(stringFragment
, & w1
, & h1
);
6358 dc
.GetTextExtent(stringFragment2
, & w2
, & h2
);
6359 dc
.GetTextExtent(stringFragment3
, & w3
, & h3
);
6361 int kerningDiff
= (w1
+ w3
) - w2
;
6362 x
= x
- kerningDiff
;
6367 // 3. Remaining unselected chunk, if any
6368 if (selectionRange
.GetEnd() < range
.GetEnd())
6370 int s2
= wxMin(selectionRange
.GetEnd()+1, range
.GetEnd());
6371 int r2
= range
.GetEnd();
6373 int fragmentLen
= r2
- s2
+ 1;
6374 if (fragmentLen
< 0)
6376 wxLogDebug(wxT("Mid(%d, %d"), (int)(s2
- offset
), (int)fragmentLen
);
6378 wxString stringFragment
= str
.Mid(s2
- offset
, fragmentLen
);
6380 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false);
6387 bool wxRichTextPlainText::DrawTabbedString(wxDC
& dc
, const wxRichTextAttr
& attr
, const wxRect
& rect
,wxString
& str
, wxCoord
& x
, wxCoord
& y
, bool selected
)
6389 bool hasTabs
= (str
.Find(wxT('\t')) != wxNOT_FOUND
);
6391 wxArrayInt tabArray
;
6395 if (attr
.GetTabs().IsEmpty())
6396 tabArray
= wxRichTextParagraph::GetDefaultTabs();
6398 tabArray
= attr
.GetTabs();
6399 tabCount
= tabArray
.GetCount();
6401 for (int i
= 0; i
< tabCount
; ++i
)
6403 int pos
= tabArray
[i
];
6404 pos
= ConvertTenthsMMToPixels(dc
, pos
);
6411 int nextTabPos
= -1;
6417 wxColour
highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
));
6418 wxColour
highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
));
6420 wxCheckSetBrush(dc
, wxBrush(highlightColour
));
6421 wxCheckSetPen(dc
, wxPen(highlightColour
));
6422 dc
.SetTextForeground(highlightTextColour
);
6423 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6427 dc
.SetTextForeground(attr
.GetTextColour());
6429 if (attr
.HasFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
) && attr
.GetBackgroundColour().IsOk())
6431 dc
.SetBackgroundMode(wxBRUSHSTYLE_SOLID
);
6432 dc
.SetTextBackground(attr
.GetBackgroundColour());
6435 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6438 wxCoord x_orig
= GetParent()->GetPosition().x
;
6441 // the string has a tab
6442 // break up the string at the Tab
6443 wxString stringChunk
= str
.BeforeFirst(wxT('\t'));
6444 str
= str
.AfterFirst(wxT('\t'));
6445 dc
.GetTextExtent(stringChunk
, & w
, & h
);
6447 bool not_found
= true;
6448 for (int i
= 0; i
< tabCount
&& not_found
; ++i
)
6450 nextTabPos
= tabArray
.Item(i
) + x_orig
;
6452 // Find the next tab position.
6453 // Even if we're at the end of the tab array, we must still draw the chunk.
6455 if (nextTabPos
> tabPos
|| (i
== (tabCount
- 1)))
6457 if (nextTabPos
<= tabPos
)
6459 int defaultTabWidth
= ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
);
6460 nextTabPos
= tabPos
+ defaultTabWidth
;
6467 wxRect
selRect(x
, rect
.y
, w
, rect
.GetHeight());
6468 dc
.DrawRectangle(selRect
);
6470 dc
.DrawText(stringChunk
, x
, y
);
6472 if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
))
6474 wxPen oldPen
= dc
.GetPen();
6475 wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1));
6476 dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5));
6477 wxCheckSetPen(dc
, oldPen
);
6483 hasTabs
= (str
.Find(wxT('\t')) != wxNOT_FOUND
);
6488 dc
.GetTextExtent(str
, & w
, & h
);
6491 wxRect
selRect(x
, rect
.y
, w
, rect
.GetHeight());
6492 dc
.DrawRectangle(selRect
);
6494 dc
.DrawText(str
, x
, y
);
6496 if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
))
6498 wxPen oldPen
= dc
.GetPen();
6499 wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1));
6500 dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5));
6501 wxCheckSetPen(dc
, oldPen
);
6510 /// Lay the item out
6511 bool wxRichTextPlainText::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& WXUNUSED(rect
), const wxRect
& WXUNUSED(parentRect
), int WXUNUSED(style
))
6513 // Only lay out if we haven't already cached the size
6515 GetRangeSize(GetRange(), m_size
, m_descent
, dc
, context
, 0, wxPoint(0, 0));
6517 // Eventually we want to have a reasonable estimate of minimum size.
6518 m_minSize
= wxSize(0, 0);
6523 void wxRichTextPlainText::Copy(const wxRichTextPlainText
& obj
)
6525 wxRichTextObject::Copy(obj
);
6527 m_text
= obj
.m_text
;
6530 /// Get/set the object size for the given range. Returns false if the range
6531 /// is invalid for this object.
6532 bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int WXUNUSED(flags
), wxPoint position
, wxArrayInt
* partialExtents
) const
6534 if (!range
.IsWithin(GetRange()))
6537 wxRichTextParagraph
* para
= wxDynamicCast(GetParent(), wxRichTextParagraph
);
6538 wxASSERT (para
!= NULL
);
6540 int relativeX
= position
.x
- GetParent()->GetPosition().x
;
6542 wxRichTextAttr
textAttr(para
? para
->GetCombinedAttributes(GetAttributes()) : GetAttributes());
6543 context
.ApplyVirtualAttributes(textAttr
, (wxRichTextObject
*) this);
6545 // Always assume unformatted text, since at this level we have no knowledge
6546 // of line breaks - and we don't need it, since we'll calculate size within
6547 // formatted text by doing it in chunks according to the line ranges
6549 bool bScript(false);
6550 wxFont
font(GetBuffer()->GetFontTable().FindFont(textAttr
));
6553 if ( textAttr
.HasTextEffects() && ( (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
)
6554 || (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) ) )
6556 wxFont textFont
= font
;
6557 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6558 textFont
.SetPointSize( static_cast<int>(size
) );
6559 wxCheckSetFont(dc
, textFont
);
6564 wxCheckSetFont(dc
, font
);
6568 bool haveDescent
= false;
6569 int startPos
= range
.GetStart() - GetRange().GetStart();
6570 long len
= range
.GetLength();
6572 wxString
str(m_text
);
6573 wxString toReplace
= wxRichTextLineBreakChar
;
6574 str
.Replace(toReplace
, wxT(" "));
6576 wxString stringChunk
= str
.Mid(startPos
, (size_t) len
);
6578 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS
))
6579 stringChunk
.MakeUpper();
6583 if (stringChunk
.Find(wxT('\t')) != wxNOT_FOUND
)
6585 // the string has a tab
6586 wxArrayInt tabArray
;
6587 if (textAttr
.GetTabs().IsEmpty())
6588 tabArray
= wxRichTextParagraph::GetDefaultTabs();
6590 tabArray
= textAttr
.GetTabs();
6592 int tabCount
= tabArray
.GetCount();
6594 for (int i
= 0; i
< tabCount
; ++i
)
6596 int pos
= tabArray
[i
];
6597 pos
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, pos
);
6601 int nextTabPos
= -1;
6603 while (stringChunk
.Find(wxT('\t')) >= 0)
6605 int absoluteWidth
= 0;
6607 // the string has a tab
6608 // break up the string at the Tab
6609 wxString stringFragment
= stringChunk
.BeforeFirst(wxT('\t'));
6610 stringChunk
= stringChunk
.AfterFirst(wxT('\t'));
6615 if (partialExtents
->GetCount() > 0)
6616 oldWidth
= (*partialExtents
)[partialExtents
->GetCount()-1];
6620 // Add these partial extents
6622 dc
.GetPartialTextExtents(stringFragment
, p
);
6624 for (j
= 0; j
< p
.GetCount(); j
++)
6625 partialExtents
->Add(oldWidth
+ p
[j
]);
6627 if (partialExtents
->GetCount() > 0)
6628 absoluteWidth
= (*partialExtents
)[(*partialExtents
).GetCount()-1] + relativeX
;
6630 absoluteWidth
= relativeX
;
6634 dc
.GetTextExtent(stringFragment
, & w
, & h
);
6636 absoluteWidth
= width
+ relativeX
;
6640 bool notFound
= true;
6641 for (int i
= 0; i
< tabCount
&& notFound
; ++i
)
6643 nextTabPos
= tabArray
.Item(i
);
6645 // Find the next tab position.
6646 // Even if we're at the end of the tab array, we must still process the chunk.
6648 if (nextTabPos
> absoluteWidth
|| (i
== (tabCount
- 1)))
6650 if (nextTabPos
<= absoluteWidth
)
6652 int defaultTabWidth
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
);
6653 nextTabPos
= absoluteWidth
+ defaultTabWidth
;
6657 width
= nextTabPos
- relativeX
;
6660 partialExtents
->Add(width
);
6666 if (!stringChunk
.IsEmpty())
6671 if (partialExtents
->GetCount() > 0)
6672 oldWidth
= (*partialExtents
)[partialExtents
->GetCount()-1];
6676 // Add these partial extents
6678 dc
.GetPartialTextExtents(stringChunk
, p
);
6680 for (j
= 0; j
< p
.GetCount(); j
++)
6681 partialExtents
->Add(oldWidth
+ p
[j
]);
6685 dc
.GetTextExtent(stringChunk
, & w
, & h
, & descent
);
6693 int charHeight
= dc
.GetCharHeight();
6694 if ((*partialExtents
).GetCount() > 0)
6695 w
= (*partialExtents
)[partialExtents
->GetCount()-1];
6698 size
= wxSize(w
, charHeight
);
6702 size
= wxSize(width
, dc
.GetCharHeight());
6706 dc
.GetTextExtent(wxT("X"), & w
, & h
, & descent
);
6714 /// Do a split, returning an object containing the second part, and setting
6715 /// the first part in 'this'.
6716 wxRichTextObject
* wxRichTextPlainText::DoSplit(long pos
)
6718 long index
= pos
- GetRange().GetStart();
6720 if (index
< 0 || index
>= (int) m_text
.length())
6723 wxString firstPart
= m_text
.Mid(0, index
);
6724 wxString secondPart
= m_text
.Mid(index
);
6728 wxRichTextPlainText
* newObject
= new wxRichTextPlainText(secondPart
);
6729 newObject
->SetAttributes(GetAttributes());
6730 newObject
->SetProperties(GetProperties());
6732 newObject
->SetRange(wxRichTextRange(pos
, GetRange().GetEnd()));
6733 GetRange().SetEnd(pos
-1);
6739 void wxRichTextPlainText::CalculateRange(long start
, long& end
)
6741 end
= start
+ m_text
.length() - 1;
6742 m_range
.SetRange(start
, end
);
6746 bool wxRichTextPlainText::DeleteRange(const wxRichTextRange
& range
)
6748 wxRichTextRange r
= range
;
6750 r
.LimitTo(GetRange());
6752 if (r
.GetStart() == GetRange().GetStart() && r
.GetEnd() == GetRange().GetEnd())
6758 long startIndex
= r
.GetStart() - GetRange().GetStart();
6759 long len
= r
.GetLength();
6761 m_text
= m_text
.Mid(0, startIndex
) + m_text
.Mid(startIndex
+len
);
6765 /// Get text for the given range.
6766 wxString
wxRichTextPlainText::GetTextForRange(const wxRichTextRange
& range
) const
6768 wxRichTextRange r
= range
;
6770 r
.LimitTo(GetRange());
6772 long startIndex
= r
.GetStart() - GetRange().GetStart();
6773 long len
= r
.GetLength();
6775 return m_text
.Mid(startIndex
, len
);
6778 /// Returns true if this object can merge itself with the given one.
6779 bool wxRichTextPlainText::CanMerge(wxRichTextObject
* object
) const
6781 return object
->GetClassInfo() == CLASSINFO(wxRichTextPlainText
) &&
6782 (m_text
.empty() || (wxTextAttrEq(GetAttributes(), object
->GetAttributes()) && m_properties
== object
->GetProperties()));
6785 /// Returns true if this object merged itself with the given one.
6786 /// The calling code will then delete the given object.
6787 bool wxRichTextPlainText::Merge(wxRichTextObject
* object
)
6789 wxRichTextPlainText
* textObject
= wxDynamicCast(object
, wxRichTextPlainText
);
6790 wxASSERT( textObject
!= NULL
);
6794 m_text
+= textObject
->GetText();
6795 wxRichTextApplyStyle(m_attributes
, textObject
->GetAttributes());
6802 /// Dump to output stream for debugging
6803 void wxRichTextPlainText::Dump(wxTextOutputStream
& stream
)
6805 wxRichTextObject::Dump(stream
);
6806 stream
<< m_text
<< wxT("\n");
6809 /// Get the first position from pos that has a line break character.
6810 long wxRichTextPlainText::GetFirstLineBreakPosition(long pos
)
6813 int len
= m_text
.length();
6814 int startPos
= pos
- m_range
.GetStart();
6815 for (i
= startPos
; i
< len
; i
++)
6817 wxChar ch
= m_text
[i
];
6818 if (ch
== wxRichTextLineBreakChar
)
6820 return i
+ m_range
.GetStart();
6828 * This is a kind of box, used to represent the whole buffer
6831 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBuffer
, wxRichTextParagraphLayoutBox
)
6833 wxList
wxRichTextBuffer::sm_handlers
;
6834 wxList
wxRichTextBuffer::sm_drawingHandlers
;
6835 wxRichTextFieldTypeHashMap
wxRichTextBuffer::sm_fieldTypes
;
6836 wxRichTextRenderer
* wxRichTextBuffer::sm_renderer
= NULL
;
6837 int wxRichTextBuffer::sm_bulletRightMargin
= 20;
6838 float wxRichTextBuffer::sm_bulletProportion
= (float) 0.3;
6841 void wxRichTextBuffer::Init()
6843 m_commandProcessor
= new wxCommandProcessor
;
6844 m_styleSheet
= NULL
;
6846 m_batchedCommandDepth
= 0;
6847 m_batchedCommand
= NULL
;
6855 wxRichTextBuffer::~wxRichTextBuffer()
6857 delete m_commandProcessor
;
6858 delete m_batchedCommand
;
6861 ClearEventHandlers();
6864 void wxRichTextBuffer::ResetAndClearCommands()
6868 GetCommandProcessor()->ClearCommands();
6871 Invalidate(wxRICHTEXT_ALL
);
6874 void wxRichTextBuffer::Copy(const wxRichTextBuffer
& obj
)
6876 wxRichTextParagraphLayoutBox::Copy(obj
);
6878 m_styleSheet
= obj
.m_styleSheet
;
6879 m_modified
= obj
.m_modified
;
6880 m_batchedCommandDepth
= 0;
6881 if (m_batchedCommand
)
6882 delete m_batchedCommand
;
6883 m_batchedCommand
= NULL
;
6884 m_suppressUndo
= obj
.m_suppressUndo
;
6885 m_invalidRange
= obj
.m_invalidRange
;
6888 /// Push style sheet to top of stack
6889 bool wxRichTextBuffer::PushStyleSheet(wxRichTextStyleSheet
* styleSheet
)
6892 styleSheet
->InsertSheet(m_styleSheet
);
6894 SetStyleSheet(styleSheet
);
6899 /// Pop style sheet from top of stack
6900 wxRichTextStyleSheet
* wxRichTextBuffer::PopStyleSheet()
6904 wxRichTextStyleSheet
* oldSheet
= m_styleSheet
;
6905 m_styleSheet
= oldSheet
->GetNextSheet();
6914 /// Submit command to insert paragraphs
6915 bool wxRichTextBuffer::InsertParagraphsWithUndo(long pos
, const wxRichTextParagraphLayoutBox
& paragraphs
, wxRichTextCtrl
* ctrl
, int flags
)
6917 return ctrl
->GetFocusObject()->InsertParagraphsWithUndo(this, pos
, paragraphs
, ctrl
, flags
);
6920 /// Submit command to insert paragraphs
6921 bool wxRichTextParagraphLayoutBox::InsertParagraphsWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxRichTextParagraphLayoutBox
& paragraphs
, wxRichTextCtrl
* ctrl
, int WXUNUSED(flags
))
6923 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6925 action
->GetNewParagraphs() = paragraphs
;
6927 action
->SetPosition(pos
);
6929 wxRichTextRange range
= wxRichTextRange(pos
, pos
+ paragraphs
.GetOwnRange().GetEnd() - 1);
6930 if (!paragraphs
.GetPartialParagraph())
6931 range
.SetEnd(range
.GetEnd()+1);
6933 // Set the range we'll need to delete in Undo
6934 action
->SetRange(range
);
6936 buffer
->SubmitAction(action
);
6941 /// Submit command to insert the given text
6942 bool wxRichTextBuffer::InsertTextWithUndo(long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
, int flags
)
6944 return ctrl
->GetFocusObject()->InsertTextWithUndo(this, pos
, text
, ctrl
, flags
);
6947 /// Submit command to insert the given text
6948 bool wxRichTextParagraphLayoutBox::InsertTextWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
, int flags
)
6950 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6952 wxRichTextAttr
* p
= NULL
;
6953 wxRichTextAttr paraAttr
;
6954 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
6956 // Get appropriate paragraph style
6957 paraAttr
= GetStyleForNewParagraph(buffer
, pos
, false, false);
6958 if (!paraAttr
.IsDefault())
6962 action
->GetNewParagraphs().AddParagraphs(text
, p
);
6964 int length
= action
->GetNewParagraphs().GetOwnRange().GetLength();
6966 if (!text
.empty() && text
.Last() != wxT('\n'))
6968 // Don't count the newline when undoing
6970 action
->GetNewParagraphs().SetPartialParagraph(true);
6972 else if (!text
.empty() && text
.Last() == wxT('\n'))
6975 action
->SetPosition(pos
);
6977 // Set the range we'll need to delete in Undo
6978 action
->SetRange(wxRichTextRange(pos
, pos
+ length
- 1));
6980 buffer
->SubmitAction(action
);
6985 /// Submit command to insert the given text
6986 bool wxRichTextBuffer::InsertNewlineWithUndo(long pos
, wxRichTextCtrl
* ctrl
, int flags
)
6988 return ctrl
->GetFocusObject()->InsertNewlineWithUndo(this, pos
, ctrl
, flags
);
6991 /// Submit command to insert the given text
6992 bool wxRichTextParagraphLayoutBox::InsertNewlineWithUndo(wxRichTextBuffer
* buffer
, long pos
, wxRichTextCtrl
* ctrl
, int flags
)
6994 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6996 wxRichTextAttr
* p
= NULL
;
6997 wxRichTextAttr paraAttr
;
6998 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7000 paraAttr
= GetStyleForNewParagraph(buffer
, pos
, false, true /* look for next paragraph style */);
7001 if (!paraAttr
.IsDefault())
7005 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7007 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(wxEmptyString
, this, & attr
);
7008 action
->GetNewParagraphs().AppendChild(newPara
);
7009 action
->GetNewParagraphs().UpdateRanges();
7010 action
->GetNewParagraphs().SetPartialParagraph(false);
7011 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
, false);
7015 newPara
->SetAttributes(*p
);
7017 if (flags
& wxRICHTEXT_INSERT_INTERACTIVE
)
7019 if (para
&& para
->GetRange().GetEnd() == pos
)
7022 // Now see if we need to number the paragraph.
7023 if (newPara
->GetAttributes().HasBulletNumber())
7025 wxRichTextAttr numberingAttr
;
7026 if (FindNextParagraphNumber(para
, numberingAttr
))
7027 wxRichTextApplyStyle(newPara
->GetAttributes(), (const wxRichTextAttr
&) numberingAttr
);
7031 action
->SetPosition(pos
);
7033 // Use the default character style
7034 // Use the default character style
7035 if (!buffer
->GetDefaultStyle().IsDefault() && newPara
->GetChildren().GetFirst())
7037 // Check whether the default style merely reflects the paragraph/basic style,
7038 // in which case don't apply it.
7039 wxRichTextAttr
defaultStyle(buffer
->GetDefaultStyle());
7040 wxRichTextAttr toApply
;
7043 wxRichTextAttr combinedAttr
= para
->GetCombinedAttributes(true /* include box attributes */);
7044 wxRichTextAttr newAttr
;
7045 // This filters out attributes that are accounted for by the current
7046 // paragraph/basic style
7047 wxRichTextApplyStyle(toApply
, defaultStyle
, & combinedAttr
);
7050 toApply
= defaultStyle
;
7052 if (!toApply
.IsDefault())
7053 newPara
->GetChildren().GetFirst()->GetData()->SetAttributes(toApply
);
7056 // Set the range we'll need to delete in Undo
7057 action
->SetRange(wxRichTextRange(pos1
, pos1
));
7059 buffer
->SubmitAction(action
);
7064 /// Submit command to insert the given image
7065 bool wxRichTextBuffer::InsertImageWithUndo(long pos
, const wxRichTextImageBlock
& imageBlock
, wxRichTextCtrl
* ctrl
, int flags
,
7066 const wxRichTextAttr
& textAttr
)
7068 return ctrl
->GetFocusObject()->InsertImageWithUndo(this, pos
, imageBlock
, ctrl
, flags
, textAttr
);
7071 /// Submit command to insert the given image
7072 bool wxRichTextParagraphLayoutBox::InsertImageWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxRichTextImageBlock
& imageBlock
,
7073 wxRichTextCtrl
* ctrl
, int flags
,
7074 const wxRichTextAttr
& textAttr
)
7076 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7078 wxRichTextAttr
* p
= NULL
;
7079 wxRichTextAttr paraAttr
;
7080 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7082 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
7083 if (!paraAttr
.IsDefault())
7087 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7089 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7091 newPara
->SetAttributes(*p
);
7093 wxRichTextImage
* imageObject
= new wxRichTextImage(imageBlock
, newPara
);
7094 newPara
->AppendChild(imageObject
);
7095 imageObject
->SetAttributes(textAttr
);
7096 action
->GetNewParagraphs().AppendChild(newPara
);
7097 action
->GetNewParagraphs().UpdateRanges();
7099 action
->GetNewParagraphs().SetPartialParagraph(true);
7101 action
->SetPosition(pos
);
7103 // Set the range we'll need to delete in Undo
7104 action
->SetRange(wxRichTextRange(pos
, pos
));
7106 buffer
->SubmitAction(action
);
7111 // Insert an object with no change of it
7112 wxRichTextObject
* wxRichTextBuffer::InsertObjectWithUndo(long pos
, wxRichTextObject
*object
, wxRichTextCtrl
* ctrl
, int flags
)
7114 return ctrl
->GetFocusObject()->InsertObjectWithUndo(this, pos
, object
, ctrl
, flags
);
7117 // Insert an object with no change of it
7118 wxRichTextObject
* wxRichTextParagraphLayoutBox::InsertObjectWithUndo(wxRichTextBuffer
* buffer
, long pos
, wxRichTextObject
*object
, wxRichTextCtrl
* ctrl
, int flags
)
7120 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Object"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7122 wxRichTextAttr
* p
= NULL
;
7123 wxRichTextAttr paraAttr
;
7124 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7126 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
7127 if (!paraAttr
.IsDefault())
7131 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7133 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7135 newPara
->SetAttributes(*p
);
7137 newPara
->AppendChild(object
);
7138 action
->GetNewParagraphs().AppendChild(newPara
);
7139 action
->GetNewParagraphs().UpdateRanges();
7141 action
->GetNewParagraphs().SetPartialParagraph(true);
7143 action
->SetPosition(pos
);
7145 // Set the range we'll need to delete in Undo
7146 action
->SetRange(wxRichTextRange(pos
, pos
));
7148 buffer
->SubmitAction(action
);
7150 wxRichTextObject
* obj
= GetLeafObjectAtPosition(pos
);
7154 wxRichTextField
* wxRichTextParagraphLayoutBox::InsertFieldWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxString
& fieldType
,
7155 const wxRichTextProperties
& properties
,
7156 wxRichTextCtrl
* ctrl
, int flags
,
7157 const wxRichTextAttr
& textAttr
)
7159 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Field"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7161 wxRichTextAttr
* p
= NULL
;
7162 wxRichTextAttr paraAttr
;
7163 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7165 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
7166 if (!paraAttr
.IsDefault())
7170 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7172 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7174 newPara
->SetAttributes(*p
);
7176 wxRichTextField
* fieldObject
= new wxRichTextField();
7177 fieldObject
->wxRichTextObject::SetProperties(properties
);
7178 fieldObject
->SetFieldType(fieldType
);
7179 fieldObject
->SetAttributes(textAttr
);
7180 newPara
->AppendChild(fieldObject
);
7181 action
->GetNewParagraphs().AppendChild(newPara
);
7182 action
->GetNewParagraphs().UpdateRanges();
7183 action
->GetNewParagraphs().SetPartialParagraph(true);
7184 action
->SetPosition(pos
);
7186 // Set the range we'll need to delete in Undo
7187 action
->SetRange(wxRichTextRange(pos
, pos
));
7189 buffer
->SubmitAction(action
);
7191 wxRichTextField
* obj
= wxDynamicCast(GetLeafObjectAtPosition(pos
), wxRichTextField
);
7195 /// Get the style that is appropriate for a new paragraph at this position.
7196 /// If the previous paragraph has a paragraph style name, look up the next-paragraph
7198 wxRichTextAttr
wxRichTextParagraphLayoutBox::GetStyleForNewParagraph(wxRichTextBuffer
* buffer
, long pos
, bool caretPosition
, bool lookUpNewParaStyle
) const
7200 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
, caretPosition
);
7203 wxRichTextAttr attr
;
7204 bool foundAttributes
= false;
7206 // Look for a matching paragraph style
7207 if (lookUpNewParaStyle
&& !para
->GetAttributes().GetParagraphStyleName().IsEmpty() && buffer
->GetStyleSheet())
7209 wxRichTextParagraphStyleDefinition
* paraDef
= buffer
->GetStyleSheet()->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
7212 // If we're not at the end of the paragraph, then we apply THIS style, and not the designated next style.
7213 if (para
->GetRange().GetEnd() == pos
&& !paraDef
->GetNextStyle().IsEmpty())
7215 wxRichTextParagraphStyleDefinition
* nextParaDef
= buffer
->GetStyleSheet()->FindParagraphStyle(paraDef
->GetNextStyle());
7218 foundAttributes
= true;
7219 attr
= nextParaDef
->GetStyleMergedWithBase(buffer
->GetStyleSheet());
7223 // If we didn't find the 'next style', use this style instead.
7224 if (!foundAttributes
)
7226 foundAttributes
= true;
7227 attr
= paraDef
->GetStyleMergedWithBase(buffer
->GetStyleSheet());
7232 // Also apply list style if present
7233 if (lookUpNewParaStyle
&& !para
->GetAttributes().GetListStyleName().IsEmpty() && buffer
->GetStyleSheet())
7235 wxRichTextListStyleDefinition
* listDef
= buffer
->GetStyleSheet()->FindListStyle(para
->GetAttributes().GetListStyleName());
7238 int thisIndent
= para
->GetAttributes().GetLeftIndent();
7239 int thisLevel
= para
->GetAttributes().HasOutlineLevel() ? para
->GetAttributes().GetOutlineLevel() : listDef
->FindLevelForIndent(thisIndent
);
7241 // Apply the overall list style, and item style for this level
7242 wxRichTextAttr
listStyle(listDef
->GetCombinedStyleForLevel(thisLevel
, buffer
->GetStyleSheet()));
7243 wxRichTextApplyStyle(attr
, listStyle
);
7244 attr
.SetOutlineLevel(thisLevel
);
7245 if (para
->GetAttributes().HasBulletNumber())
7246 attr
.SetBulletNumber(para
->GetAttributes().GetBulletNumber());
7250 if (!foundAttributes
)
7252 attr
= para
->GetAttributes();
7253 int flags
= attr
.GetFlags();
7255 // Eliminate character styles
7256 flags
&= ( (~ wxTEXT_ATTR_FONT
) |
7257 (~ wxTEXT_ATTR_TEXT_COLOUR
) |
7258 (~ wxTEXT_ATTR_BACKGROUND_COLOUR
) );
7259 attr
.SetFlags(flags
);
7265 return wxRichTextAttr();
7268 /// Submit command to delete this range
7269 bool wxRichTextBuffer::DeleteRangeWithUndo(const wxRichTextRange
& range
, wxRichTextCtrl
* ctrl
)
7271 return ctrl
->GetFocusObject()->DeleteRangeWithUndo(range
, ctrl
, this);
7274 /// Submit command to delete this range
7275 bool wxRichTextParagraphLayoutBox::DeleteRangeWithUndo(const wxRichTextRange
& range
, wxRichTextCtrl
* ctrl
, wxRichTextBuffer
* buffer
)
7277 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Delete"), wxRICHTEXT_DELETE
, buffer
, this, ctrl
);
7279 action
->SetPosition(ctrl
->GetCaretPosition());
7281 // Set the range to delete
7282 action
->SetRange(range
);
7284 // Copy the fragment that we'll need to restore in Undo
7285 CopyFragment(range
, action
->GetOldParagraphs());
7287 // See if we're deleting a paragraph marker, in which case we need to
7288 // make a note not to copy the attributes from the 2nd paragraph to the 1st.
7289 if (range
.GetStart() == range
.GetEnd())
7291 wxRichTextParagraph
* para
= GetParagraphAtPosition(range
.GetStart());
7292 if (para
&& para
->GetRange().GetEnd() == range
.GetEnd())
7294 wxRichTextParagraph
* nextPara
= GetParagraphAtPosition(range
.GetStart()+1);
7295 if (nextPara
&& nextPara
!= para
)
7297 action
->GetOldParagraphs().GetChildren().GetFirst()->GetData()->SetAttributes(nextPara
->GetAttributes());
7298 action
->GetOldParagraphs().GetAttributes().SetFlags(action
->GetOldParagraphs().GetAttributes().GetFlags() | wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
);
7303 buffer
->SubmitAction(action
);
7308 /// Collapse undo/redo commands
7309 bool wxRichTextBuffer::BeginBatchUndo(const wxString
& cmdName
)
7311 if (m_batchedCommandDepth
== 0)
7313 wxASSERT(m_batchedCommand
== NULL
);
7314 if (m_batchedCommand
)
7316 GetCommandProcessor()->Store(m_batchedCommand
);
7318 m_batchedCommand
= new wxRichTextCommand(cmdName
);
7321 m_batchedCommandDepth
++;
7326 /// Collapse undo/redo commands
7327 bool wxRichTextBuffer::EndBatchUndo()
7329 m_batchedCommandDepth
--;
7331 wxASSERT(m_batchedCommandDepth
>= 0);
7332 wxASSERT(m_batchedCommand
!= NULL
);
7334 if (m_batchedCommandDepth
== 0)
7336 GetCommandProcessor()->Store(m_batchedCommand
);
7337 m_batchedCommand
= NULL
;
7343 /// Submit immediately, or delay according to whether collapsing is on
7344 bool wxRichTextBuffer::SubmitAction(wxRichTextAction
* action
)
7346 if (action
&& !action
->GetNewParagraphs().IsEmpty())
7347 PrepareContent(action
->GetNewParagraphs());
7349 if (BatchingUndo() && m_batchedCommand
&& !SuppressingUndo())
7351 wxRichTextCommand
* cmd
= new wxRichTextCommand(action
->GetName());
7352 cmd
->AddAction(action
);
7354 cmd
->GetActions().Clear();
7357 m_batchedCommand
->AddAction(action
);
7361 wxRichTextCommand
* cmd
= new wxRichTextCommand(action
->GetName());
7362 cmd
->AddAction(action
);
7364 // Only store it if we're not suppressing undo.
7365 return GetCommandProcessor()->Submit(cmd
, !SuppressingUndo());
7371 /// Begin suppressing undo/redo commands.
7372 bool wxRichTextBuffer::BeginSuppressUndo()
7379 /// End suppressing undo/redo commands.
7380 bool wxRichTextBuffer::EndSuppressUndo()
7387 /// Begin using a style
7388 bool wxRichTextBuffer::BeginStyle(const wxRichTextAttr
& style
)
7390 wxRichTextAttr
newStyle(GetDefaultStyle());
7392 // Save the old default style
7393 m_attributeStack
.Append((wxObject
*) new wxRichTextAttr(GetDefaultStyle()));
7395 wxRichTextApplyStyle(newStyle
, style
);
7396 newStyle
.SetFlags(style
.GetFlags()|newStyle
.GetFlags());
7398 SetDefaultStyle(newStyle
);
7404 bool wxRichTextBuffer::EndStyle()
7406 if (!m_attributeStack
.GetFirst())
7408 wxLogDebug(_("Too many EndStyle calls!"));
7412 wxList::compatibility_iterator node
= m_attributeStack
.GetLast();
7413 wxRichTextAttr
* attr
= (wxRichTextAttr
*)node
->GetData();
7414 m_attributeStack
.Erase(node
);
7416 SetDefaultStyle(*attr
);
7423 bool wxRichTextBuffer::EndAllStyles()
7425 while (m_attributeStack
.GetCount() != 0)
7430 /// Clear the style stack
7431 void wxRichTextBuffer::ClearStyleStack()
7433 for (wxList::compatibility_iterator node
= m_attributeStack
.GetFirst(); node
; node
= node
->GetNext())
7434 delete (wxRichTextAttr
*) node
->GetData();
7435 m_attributeStack
.Clear();
7438 /// Begin using bold
7439 bool wxRichTextBuffer::BeginBold()
7441 wxRichTextAttr attr
;
7442 attr
.SetFontWeight(wxFONTWEIGHT_BOLD
);
7444 return BeginStyle(attr
);
7447 /// Begin using italic
7448 bool wxRichTextBuffer::BeginItalic()
7450 wxRichTextAttr attr
;
7451 attr
.SetFontStyle(wxFONTSTYLE_ITALIC
);
7453 return BeginStyle(attr
);
7456 /// Begin using underline
7457 bool wxRichTextBuffer::BeginUnderline()
7459 wxRichTextAttr attr
;
7460 attr
.SetFontUnderlined(true);
7462 return BeginStyle(attr
);
7465 /// Begin using point size
7466 bool wxRichTextBuffer::BeginFontSize(int pointSize
)
7468 wxRichTextAttr attr
;
7469 attr
.SetFontSize(pointSize
);
7471 return BeginStyle(attr
);
7474 /// Begin using this font
7475 bool wxRichTextBuffer::BeginFont(const wxFont
& font
)
7477 wxRichTextAttr attr
;
7480 return BeginStyle(attr
);
7483 /// Begin using this colour
7484 bool wxRichTextBuffer::BeginTextColour(const wxColour
& colour
)
7486 wxRichTextAttr attr
;
7487 attr
.SetFlags(wxTEXT_ATTR_TEXT_COLOUR
);
7488 attr
.SetTextColour(colour
);
7490 return BeginStyle(attr
);
7493 /// Begin using alignment
7494 bool wxRichTextBuffer::BeginAlignment(wxTextAttrAlignment alignment
)
7496 wxRichTextAttr attr
;
7497 attr
.SetFlags(wxTEXT_ATTR_ALIGNMENT
);
7498 attr
.SetAlignment(alignment
);
7500 return BeginStyle(attr
);
7503 /// Begin left indent
7504 bool wxRichTextBuffer::BeginLeftIndent(int leftIndent
, int leftSubIndent
)
7506 wxRichTextAttr attr
;
7507 attr
.SetFlags(wxTEXT_ATTR_LEFT_INDENT
);
7508 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7510 return BeginStyle(attr
);
7513 /// Begin right indent
7514 bool wxRichTextBuffer::BeginRightIndent(int rightIndent
)
7516 wxRichTextAttr attr
;
7517 attr
.SetFlags(wxTEXT_ATTR_RIGHT_INDENT
);
7518 attr
.SetRightIndent(rightIndent
);
7520 return BeginStyle(attr
);
7523 /// Begin paragraph spacing
7524 bool wxRichTextBuffer::BeginParagraphSpacing(int before
, int after
)
7528 flags
|= wxTEXT_ATTR_PARA_SPACING_BEFORE
;
7530 flags
|= wxTEXT_ATTR_PARA_SPACING_AFTER
;
7532 wxRichTextAttr attr
;
7533 attr
.SetFlags(flags
);
7534 attr
.SetParagraphSpacingBefore(before
);
7535 attr
.SetParagraphSpacingAfter(after
);
7537 return BeginStyle(attr
);
7540 /// Begin line spacing
7541 bool wxRichTextBuffer::BeginLineSpacing(int lineSpacing
)
7543 wxRichTextAttr attr
;
7544 attr
.SetFlags(wxTEXT_ATTR_LINE_SPACING
);
7545 attr
.SetLineSpacing(lineSpacing
);
7547 return BeginStyle(attr
);
7550 /// Begin numbered bullet
7551 bool wxRichTextBuffer::BeginNumberedBullet(int bulletNumber
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7553 wxRichTextAttr attr
;
7554 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7555 attr
.SetBulletStyle(bulletStyle
);
7556 attr
.SetBulletNumber(bulletNumber
);
7557 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7559 return BeginStyle(attr
);
7562 /// Begin symbol bullet
7563 bool wxRichTextBuffer::BeginSymbolBullet(const wxString
& symbol
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7565 wxRichTextAttr attr
;
7566 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7567 attr
.SetBulletStyle(bulletStyle
);
7568 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7569 attr
.SetBulletText(symbol
);
7571 return BeginStyle(attr
);
7574 /// Begin standard bullet
7575 bool wxRichTextBuffer::BeginStandardBullet(const wxString
& bulletName
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7577 wxRichTextAttr attr
;
7578 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7579 attr
.SetBulletStyle(bulletStyle
);
7580 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7581 attr
.SetBulletName(bulletName
);
7583 return BeginStyle(attr
);
7586 /// Begin named character style
7587 bool wxRichTextBuffer::BeginCharacterStyle(const wxString
& characterStyle
)
7589 if (GetStyleSheet())
7591 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->FindCharacterStyle(characterStyle
);
7594 wxRichTextAttr attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7595 return BeginStyle(attr
);
7601 /// Begin named paragraph style
7602 bool wxRichTextBuffer::BeginParagraphStyle(const wxString
& paragraphStyle
)
7604 if (GetStyleSheet())
7606 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(paragraphStyle
);
7609 wxRichTextAttr attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7610 return BeginStyle(attr
);
7616 /// Begin named list style
7617 bool wxRichTextBuffer::BeginListStyle(const wxString
& listStyle
, int level
, int number
)
7619 if (GetStyleSheet())
7621 wxRichTextListStyleDefinition
* def
= GetStyleSheet()->FindListStyle(listStyle
);
7624 wxRichTextAttr
attr(def
->GetCombinedStyleForLevel(level
));
7626 attr
.SetBulletNumber(number
);
7628 return BeginStyle(attr
);
7635 bool wxRichTextBuffer::BeginURL(const wxString
& url
, const wxString
& characterStyle
)
7637 wxRichTextAttr attr
;
7639 if (!characterStyle
.IsEmpty() && GetStyleSheet())
7641 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->FindCharacterStyle(characterStyle
);
7644 attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7649 return BeginStyle(attr
);
7652 /// Adds a handler to the end
7653 void wxRichTextBuffer::AddHandler(wxRichTextFileHandler
*handler
)
7655 sm_handlers
.Append(handler
);
7658 /// Inserts a handler at the front
7659 void wxRichTextBuffer::InsertHandler(wxRichTextFileHandler
*handler
)
7661 sm_handlers
.Insert( handler
);
7664 /// Removes a handler
7665 bool wxRichTextBuffer::RemoveHandler(const wxString
& name
)
7667 wxRichTextFileHandler
*handler
= FindHandler(name
);
7670 sm_handlers
.DeleteObject(handler
);
7678 /// Finds a handler by filename or, if supplied, type
7679 wxRichTextFileHandler
*wxRichTextBuffer::FindHandlerFilenameOrType(const wxString
& filename
,
7680 wxRichTextFileType imageType
)
7682 if (imageType
!= wxRICHTEXT_TYPE_ANY
)
7683 return FindHandler(imageType
);
7684 else if (!filename
.IsEmpty())
7686 wxString path
, file
, ext
;
7687 wxFileName::SplitPath(filename
, & path
, & file
, & ext
);
7688 return FindHandler(ext
, imageType
);
7695 /// Finds a handler by name
7696 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(const wxString
& name
)
7698 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7701 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7702 if (handler
->GetName().Lower() == name
.Lower()) return handler
;
7704 node
= node
->GetNext();
7709 /// Finds a handler by extension and type
7710 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(const wxString
& extension
, wxRichTextFileType type
)
7712 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7715 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7716 if ( handler
->GetExtension().Lower() == extension
.Lower() &&
7717 (type
== wxRICHTEXT_TYPE_ANY
|| handler
->GetType() == type
) )
7719 node
= node
->GetNext();
7724 /// Finds a handler by type
7725 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(wxRichTextFileType type
)
7727 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7730 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7731 if (handler
->GetType() == type
) return handler
;
7732 node
= node
->GetNext();
7737 void wxRichTextBuffer::InitStandardHandlers()
7739 if (!FindHandler(wxRICHTEXT_TYPE_TEXT
))
7740 AddHandler(new wxRichTextPlainTextHandler
);
7743 void wxRichTextBuffer::CleanUpHandlers()
7745 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7748 wxRichTextFileHandler
* handler
= (wxRichTextFileHandler
*)node
->GetData();
7749 wxList::compatibility_iterator next
= node
->GetNext();
7754 sm_handlers
.Clear();
7757 wxString
wxRichTextBuffer::GetExtWildcard(bool combine
, bool save
, wxArrayInt
* types
)
7764 wxList::compatibility_iterator node
= GetHandlers().GetFirst();
7768 wxRichTextFileHandler
* handler
= (wxRichTextFileHandler
*) node
->GetData();
7769 if (handler
->IsVisible() && ((save
&& handler
->CanSave()) || (!save
&& handler
->CanLoad())))
7774 wildcard
+= wxT(";");
7775 wildcard
+= wxT("*.") + handler
->GetExtension();
7780 wildcard
+= wxT("|");
7781 wildcard
+= handler
->GetName();
7782 wildcard
+= wxT(" ");
7783 wildcard
+= _("files");
7784 wildcard
+= wxT(" (*.");
7785 wildcard
+= handler
->GetExtension();
7786 wildcard
+= wxT(")|*.");
7787 wildcard
+= handler
->GetExtension();
7789 types
->Add(handler
->GetType());
7794 node
= node
->GetNext();
7798 wildcard
= wxT("(") + wildcard
+ wxT(")|") + wildcard
;
7803 bool wxRichTextBuffer::LoadFile(const wxString
& filename
, wxRichTextFileType type
)
7805 wxRichTextFileHandler
* handler
= FindHandlerFilenameOrType(filename
, type
);
7808 SetDefaultStyle(wxRichTextAttr());
7809 handler
->SetFlags(GetHandlerFlags());
7810 bool success
= handler
->LoadFile(this, filename
);
7811 Invalidate(wxRICHTEXT_ALL
);
7819 bool wxRichTextBuffer::SaveFile(const wxString
& filename
, wxRichTextFileType type
)
7821 wxRichTextFileHandler
* handler
= FindHandlerFilenameOrType(filename
, type
);
7824 handler
->SetFlags(GetHandlerFlags());
7825 return handler
->SaveFile(this, filename
);
7831 /// Load from a stream
7832 bool wxRichTextBuffer::LoadFile(wxInputStream
& stream
, wxRichTextFileType type
)
7834 wxRichTextFileHandler
* handler
= FindHandler(type
);
7837 SetDefaultStyle(wxRichTextAttr());
7838 handler
->SetFlags(GetHandlerFlags());
7839 bool success
= handler
->LoadFile(this, stream
);
7840 Invalidate(wxRICHTEXT_ALL
);
7847 /// Save to a stream
7848 bool wxRichTextBuffer::SaveFile(wxOutputStream
& stream
, wxRichTextFileType type
)
7850 wxRichTextFileHandler
* handler
= FindHandler(type
);
7853 handler
->SetFlags(GetHandlerFlags());
7854 return handler
->SaveFile(this, stream
);
7860 /// Copy the range to the clipboard
7861 bool wxRichTextBuffer::CopyToClipboard(const wxRichTextRange
& range
)
7863 bool success
= false;
7864 wxRichTextParagraphLayoutBox
* container
= this;
7865 if (GetRichTextCtrl())
7866 container
= GetRichTextCtrl()->GetFocusObject();
7868 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
7870 if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open())
7872 wxTheClipboard
->Clear();
7874 // Add composite object
7876 wxDataObjectComposite
* compositeObject
= new wxDataObjectComposite();
7879 wxString text
= container
->GetTextForRange(range
);
7882 text
= wxTextFile::Translate(text
, wxTextFileType_Dos
);
7885 compositeObject
->Add(new wxTextDataObject(text
), false /* not preferred */);
7888 // Add rich text buffer data object. This needs the XML handler to be present.
7890 if (FindHandler(wxRICHTEXT_TYPE_XML
))
7892 wxRichTextBuffer
* richTextBuf
= new wxRichTextBuffer
;
7893 container
->CopyFragment(range
, *richTextBuf
);
7895 compositeObject
->Add(new wxRichTextBufferDataObject(richTextBuf
), true /* preferred */);
7898 if (wxTheClipboard
->SetData(compositeObject
))
7901 wxTheClipboard
->Close();
7910 /// Paste the clipboard content to the buffer
7911 bool wxRichTextBuffer::PasteFromClipboard(long position
)
7913 bool success
= false;
7914 wxRichTextParagraphLayoutBox
* container
= this;
7915 if (GetRichTextCtrl())
7916 container
= GetRichTextCtrl()->GetFocusObject();
7918 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
7919 if (CanPasteFromClipboard())
7921 if (wxTheClipboard
->Open())
7923 if (wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())))
7925 wxRichTextBufferDataObject data
;
7926 wxTheClipboard
->GetData(data
);
7927 wxRichTextBuffer
* richTextBuffer
= data
.GetRichTextBuffer();
7930 container
->InsertParagraphsWithUndo(this, position
+1, *richTextBuffer
, GetRichTextCtrl(), 0);
7931 if (GetRichTextCtrl())
7932 GetRichTextCtrl()->ShowPosition(position
+ richTextBuffer
->GetOwnRange().GetEnd());
7933 delete richTextBuffer
;
7936 else if (wxTheClipboard
->IsSupported(wxDF_TEXT
)
7938 || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
)
7942 wxTextDataObject data
;
7943 wxTheClipboard
->GetData(data
);
7944 wxString
text(data
.GetText());
7947 text2
.Alloc(text
.Length()+1);
7949 for (i
= 0; i
< text
.Length(); i
++)
7951 wxChar ch
= text
[i
];
7952 if (ch
!= wxT('\r'))
7956 wxString text2
= text
;
7958 container
->InsertTextWithUndo(this, position
+1, text2
, GetRichTextCtrl(), wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
);
7960 if (GetRichTextCtrl())
7961 GetRichTextCtrl()->ShowPosition(position
+ text2
.Length());
7965 else if (wxTheClipboard
->IsSupported(wxDF_BITMAP
))
7967 wxBitmapDataObject data
;
7968 wxTheClipboard
->GetData(data
);
7969 wxBitmap
bitmap(data
.GetBitmap());
7970 wxImage
image(bitmap
.ConvertToImage());
7972 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, this, container
, GetRichTextCtrl(), false);
7974 action
->GetNewParagraphs().AddImage(image
);
7976 if (action
->GetNewParagraphs().GetChildCount() == 1)
7977 action
->GetNewParagraphs().SetPartialParagraph(true);
7979 action
->SetPosition(position
+1);
7981 // Set the range we'll need to delete in Undo
7982 action
->SetRange(wxRichTextRange(position
+1, position
+1));
7984 SubmitAction(action
);
7988 wxTheClipboard
->Close();
7992 wxUnusedVar(position
);
7997 /// Can we paste from the clipboard?
7998 bool wxRichTextBuffer::CanPasteFromClipboard() const
8000 bool canPaste
= false;
8001 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
8002 if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open())
8004 if (wxTheClipboard
->IsSupported(wxDF_TEXT
)
8006 || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
)
8008 || wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())) ||
8009 wxTheClipboard
->IsSupported(wxDF_BITMAP
))
8013 wxTheClipboard
->Close();
8019 /// Dumps contents of buffer for debugging purposes
8020 void wxRichTextBuffer::Dump()
8024 wxStringOutputStream
stream(& text
);
8025 wxTextOutputStream
textStream(stream
);
8032 /// Add an event handler
8033 bool wxRichTextBuffer::AddEventHandler(wxEvtHandler
* handler
)
8035 m_eventHandlers
.Append(handler
);
8039 /// Remove an event handler
8040 bool wxRichTextBuffer::RemoveEventHandler(wxEvtHandler
* handler
, bool deleteHandler
)
8042 wxList::compatibility_iterator node
= m_eventHandlers
.Find(handler
);
8045 m_eventHandlers
.Erase(node
);
8055 /// Clear event handlers
8056 void wxRichTextBuffer::ClearEventHandlers()
8058 m_eventHandlers
.Clear();
8061 /// Send event to event handlers. If sendToAll is true, will send to all event handlers,
8062 /// otherwise will stop at the first successful one.
8063 bool wxRichTextBuffer::SendEvent(wxEvent
& event
, bool sendToAll
)
8065 bool success
= false;
8066 for (wxList::compatibility_iterator node
= m_eventHandlers
.GetFirst(); node
; node
= node
->GetNext())
8068 wxEvtHandler
* handler
= (wxEvtHandler
*) node
->GetData();
8069 if (handler
->ProcessEvent(event
))
8079 /// Set style sheet and notify of the change
8080 bool wxRichTextBuffer::SetStyleSheetAndNotify(wxRichTextStyleSheet
* sheet
)
8082 wxRichTextStyleSheet
* oldSheet
= GetStyleSheet();
8084 wxWindowID winid
= wxID_ANY
;
8085 if (GetRichTextCtrl())
8086 winid
= GetRichTextCtrl()->GetId();
8088 wxRichTextEvent
event(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACING
, winid
);
8089 event
.SetEventObject(GetRichTextCtrl());
8090 event
.SetContainer(GetRichTextCtrl()->GetFocusObject());
8091 event
.SetOldStyleSheet(oldSheet
);
8092 event
.SetNewStyleSheet(sheet
);
8095 if (SendEvent(event
) && !event
.IsAllowed())
8097 if (sheet
!= oldSheet
)
8103 if (oldSheet
&& oldSheet
!= sheet
)
8106 SetStyleSheet(sheet
);
8108 event
.SetEventType(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACED
);
8109 event
.SetOldStyleSheet(NULL
);
8112 return SendEvent(event
);
8115 /// Set renderer, deleting old one
8116 void wxRichTextBuffer::SetRenderer(wxRichTextRenderer
* renderer
)
8120 sm_renderer
= renderer
;
8123 /// Hit-testing: returns a flag indicating hit test details, plus
8124 /// information about position
8125 int wxRichTextBuffer::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
8127 int ret
= wxRichTextParagraphLayoutBox::HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
8128 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
8134 textPosition
= m_ownRange
.GetEnd()-1;
8137 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
;
8141 bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxRichTextAttr
& bulletAttr
, const wxRect
& rect
)
8143 if (bulletAttr
.GetTextColour().IsOk())
8145 wxCheckSetPen(dc
, wxPen(bulletAttr
.GetTextColour()));
8146 wxCheckSetBrush(dc
, wxBrush(bulletAttr
.GetTextColour()));
8150 wxCheckSetPen(dc
, *wxBLACK_PEN
);
8151 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
8155 if (bulletAttr
.HasFont())
8157 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(bulletAttr
);
8160 font
= (*wxNORMAL_FONT
);
8162 wxCheckSetFont(dc
, font
);
8164 int charHeight
= dc
.GetCharHeight();
8166 int bulletWidth
= (int) (((float) charHeight
) * wxRichTextBuffer::GetBulletProportion());
8167 int bulletHeight
= bulletWidth
;
8171 // Calculate the top position of the character (as opposed to the whole line height)
8172 int y
= rect
.y
+ (rect
.height
- charHeight
);
8174 // Calculate where the bullet should be positioned
8175 y
= y
+ (charHeight
+1)/2 - (bulletHeight
+1)/2;
8177 // The margin between a bullet and text.
8178 int margin
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer::GetBulletRightMargin());
8180 if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
)
8181 x
= rect
.x
+ rect
.width
- bulletWidth
- margin
;
8182 else if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
)
8183 x
= x
+ (rect
.width
)/2 - bulletWidth
/2;
8185 if (bulletAttr
.GetBulletName() == wxT("standard/square"))
8187 dc
.DrawRectangle(x
, y
, bulletWidth
, bulletHeight
);
8189 else if (bulletAttr
.GetBulletName() == wxT("standard/diamond"))
8192 pts
[0].x
= x
; pts
[0].y
= y
+ bulletHeight
/2;
8193 pts
[1].x
= x
+ bulletWidth
/2; pts
[1].y
= y
;
8194 pts
[2].x
= x
+ bulletWidth
; pts
[2].y
= y
+ bulletHeight
/2;
8195 pts
[3].x
= x
+ bulletWidth
/2; pts
[3].y
= y
+ bulletHeight
;
8197 dc
.DrawPolygon(4, pts
);
8199 else if (bulletAttr
.GetBulletName() == wxT("standard/triangle"))
8202 pts
[0].x
= x
; pts
[0].y
= y
;
8203 pts
[1].x
= x
+ bulletWidth
; pts
[1].y
= y
+ bulletHeight
/2;
8204 pts
[2].x
= x
; pts
[2].y
= y
+ bulletHeight
;
8206 dc
.DrawPolygon(3, pts
);
8208 else if (bulletAttr
.GetBulletName() == wxT("standard/circle-outline"))
8210 wxCheckSetBrush(dc
, *wxTRANSPARENT_BRUSH
);
8211 dc
.DrawEllipse(x
, y
, bulletWidth
, bulletHeight
);
8213 else // "standard/circle", and catch-all
8215 dc
.DrawEllipse(x
, y
, bulletWidth
, bulletHeight
);
8221 bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxRichTextAttr
& attr
, const wxRect
& rect
, const wxString
& text
)
8226 if ((attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
) && !attr
.GetBulletFont().IsEmpty() && attr
.HasFont())
8228 wxRichTextAttr fontAttr
;
8229 fontAttr
.SetFontSize(attr
.GetFontSize());
8230 fontAttr
.SetFontStyle(attr
.GetFontStyle());
8231 fontAttr
.SetFontWeight(attr
.GetFontWeight());
8232 fontAttr
.SetFontUnderlined(attr
.GetFontUnderlined());
8233 fontAttr
.SetFontFaceName(attr
.GetBulletFont());
8234 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(fontAttr
);
8236 else if (attr
.HasFont())
8237 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(attr
);
8239 font
= (*wxNORMAL_FONT
);
8241 wxCheckSetFont(dc
, font
);
8243 if (attr
.GetTextColour().IsOk())
8244 dc
.SetTextForeground(attr
.GetTextColour());
8246 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
8248 int charHeight
= dc
.GetCharHeight();
8250 dc
.GetTextExtent(text
, & tw
, & th
);
8254 // Calculate the top position of the character (as opposed to the whole line height)
8255 int y
= rect
.y
+ (rect
.height
- charHeight
);
8257 // The margin between a bullet and text.
8258 int margin
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer::GetBulletRightMargin());
8260 if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
)
8261 x
= (rect
.x
+ rect
.width
) - tw
- margin
;
8262 else if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
)
8263 x
= x
+ (rect
.width
)/2 - tw
/2;
8265 dc
.DrawText(text
, x
, y
);
8273 bool wxRichTextStdRenderer::DrawBitmapBullet(wxRichTextParagraph
* WXUNUSED(paragraph
), wxDC
& WXUNUSED(dc
), const wxRichTextAttr
& WXUNUSED(attr
), const wxRect
& WXUNUSED(rect
))
8275 // Currently unimplemented. The intention is to store bitmaps by name in a media store associated
8276 // with the buffer. The store will allow retrieval from memory, disk or other means.
8280 /// Enumerate the standard bullet names currently supported
8281 bool wxRichTextStdRenderer::EnumerateStandardBulletNames(wxArrayString
& bulletNames
)
8283 bulletNames
.Add(wxTRANSLATE("standard/circle"));
8284 bulletNames
.Add(wxTRANSLATE("standard/circle-outline"));
8285 bulletNames
.Add(wxTRANSLATE("standard/square"));
8286 bulletNames
.Add(wxTRANSLATE("standard/diamond"));
8287 bulletNames
.Add(wxTRANSLATE("standard/triangle"));
8296 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBox
, wxRichTextParagraphLayoutBox
)
8298 wxRichTextBox::wxRichTextBox(wxRichTextObject
* parent
):
8299 wxRichTextParagraphLayoutBox(parent
)
8304 bool wxRichTextBox::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8309 // TODO: if the active object in the control, draw an indication.
8310 // We need to add the concept of active object, and not just focus object,
8311 // so we can apply commands (properties, delete, ...) to objects such as text boxes and images.
8312 // Ultimately we would like to be able to interactively resize an active object
8313 // using drag handles.
8314 return wxRichTextParagraphLayoutBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8318 void wxRichTextBox::Copy(const wxRichTextBox
& obj
)
8320 wxRichTextParagraphLayoutBox::Copy(obj
);
8323 // Edit properties via a GUI
8324 bool wxRichTextBox::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8326 wxRichTextObjectPropertiesDialog
boxDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Box Properties"));
8327 boxDlg
.SetAttributes(GetAttributes());
8329 if (boxDlg
.ShowModal() == wxID_OK
)
8331 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
8332 // indeterminate in the object.
8333 boxDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
8344 IMPLEMENT_DYNAMIC_CLASS(wxRichTextField
, wxRichTextParagraphLayoutBox
)
8346 wxRichTextField::wxRichTextField(const wxString
& fieldType
, wxRichTextObject
* parent
):
8347 wxRichTextParagraphLayoutBox(parent
)
8349 SetFieldType(fieldType
);
8353 bool wxRichTextField::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8358 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8359 if (fieldType
&& fieldType
->Draw(this, dc
, context
, range
, selection
, rect
, descent
, style
))
8362 // Fallback; but don't draw guidelines.
8363 style
&= ~wxRICHTEXT_DRAW_GUIDELINES
;
8364 return wxRichTextParagraphLayoutBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8367 bool wxRichTextField::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
)
8369 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8370 if (fieldType
&& fieldType
->Layout(this, dc
, context
, rect
, parentRect
, style
))
8374 return wxRichTextParagraphLayoutBox::Layout(dc
, context
, rect
, parentRect
, style
);
8377 bool wxRichTextField::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
8379 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8381 return fieldType
->GetRangeSize((wxRichTextField
*) this, range
, size
, descent
, dc
, context
, flags
, position
, partialExtents
);
8383 return wxRichTextParagraphLayoutBox::GetRangeSize(range
, size
, descent
, dc
, context
, flags
, position
, partialExtents
);
8387 void wxRichTextField::CalculateRange(long start
, long& end
)
8390 wxRichTextParagraphLayoutBox::CalculateRange(start
, end
);
8392 wxRichTextObject::CalculateRange(start
, end
);
8396 void wxRichTextField::Copy(const wxRichTextField
& obj
)
8398 wxRichTextParagraphLayoutBox::Copy(obj
);
8403 // Edit properties via a GUI
8404 bool wxRichTextField::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8406 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8408 return fieldType
->EditProperties(this, parent
, buffer
);
8413 bool wxRichTextField::CanEditProperties() const
8415 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8417 return fieldType
->CanEditProperties((wxRichTextField
*) this);
8422 wxString
wxRichTextField::GetPropertiesMenuLabel() const
8424 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8426 return fieldType
->GetPropertiesMenuLabel((wxRichTextField
*) this);
8428 return wxEmptyString
;
8431 bool wxRichTextField::UpdateField()
8433 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8435 return fieldType
->UpdateField((wxRichTextField
*) this);
8440 bool wxRichTextField::IsTopLevel() const
8442 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8444 return fieldType
->IsTopLevel((wxRichTextField
*) this);
8449 IMPLEMENT_CLASS(wxRichTextFieldType
, wxObject
)
8451 IMPLEMENT_CLASS(wxRichTextFieldTypeStandard
, wxRichTextFieldType
)
8453 wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString
& name
, const wxString
& label
, int displayStyle
)
8459 SetDisplayStyle(displayStyle
);
8462 wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString
& name
, const wxBitmap
& bitmap
, int displayStyle
)
8468 SetDisplayStyle(displayStyle
);
8471 void wxRichTextFieldTypeStandard::Init()
8473 m_displayStyle
= wxRICHTEXT_FIELD_STYLE_RECTANGLE
;
8474 m_font
= wxFont(6, wxFONTFAMILY_SWISS
, wxFONTSTYLE_NORMAL
, wxFONTWEIGHT_NORMAL
);
8475 m_textColour
= *wxWHITE
;
8476 m_borderColour
= *wxBLACK
;
8477 m_backgroundColour
= *wxBLACK
;
8478 m_verticalPadding
= 1;
8479 m_horizontalPadding
= 3;
8480 m_horizontalMargin
= 2;
8481 m_verticalMargin
= 0;
8484 void wxRichTextFieldTypeStandard::Copy(const wxRichTextFieldTypeStandard
& field
)
8486 wxRichTextFieldType::Copy(field
);
8488 m_label
= field
.m_label
;
8489 m_displayStyle
= field
.m_displayStyle
;
8490 m_font
= field
.m_font
;
8491 m_textColour
= field
.m_textColour
;
8492 m_borderColour
= field
.m_borderColour
;
8493 m_backgroundColour
= field
.m_backgroundColour
;
8494 m_verticalPadding
= field
.m_verticalPadding
;
8495 m_horizontalPadding
= field
.m_horizontalPadding
;
8496 m_horizontalMargin
= field
.m_horizontalMargin
;
8497 m_bitmap
= field
.m_bitmap
;
8500 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
))
8502 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_COMPOSITE
)
8503 return false; // USe default composite drawing
8504 else // if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_RECTANGLE || m_displayStyle == wxRICHTEXT_FIELD_STYLE_NOBORDER)
8508 wxPen
borderPen(m_borderColour
, 1, wxSOLID
);
8509 wxBrush
backgroundBrush(m_backgroundColour
);
8510 wxColour
textColour(m_textColour
);
8512 if (selection
.WithinSelection(obj
->GetRange().GetStart(), obj
))
8514 wxColour
highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
));
8515 wxColour
highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
));
8517 borderPen
= wxPen(highlightTextColour
, 1, wxSOLID
);
8518 backgroundBrush
= wxBrush(highlightColour
);
8520 wxCheckSetBrush(dc
, backgroundBrush
);
8521 wxCheckSetPen(dc
, wxPen(highlightColour
, 1, wxSOLID
));
8522 dc
.DrawRectangle(rect
);
8525 if (m_displayStyle
!= wxRICHTEXT_FIELD_STYLE_NO_BORDER
)
8528 // objectRect is the area where the content is drawn, after margins around it have been taken into account
8529 wxRect objectRect
= wxRect(wxPoint(rect
.x
+ m_horizontalMargin
, rect
.y
+ wxMax(0, rect
.height
- descent
- obj
->GetCachedSize().y
)),
8530 wxSize(obj
->GetCachedSize().x
- 2*m_horizontalMargin
- borderSize
, obj
->GetCachedSize().y
));
8532 // clientArea is where the text is actually written
8533 wxRect clientArea
= objectRect
;
8535 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_RECTANGLE
)
8537 dc
.SetPen(borderPen
);
8538 dc
.SetBrush(backgroundBrush
);
8539 dc
.DrawRoundedRectangle(objectRect
, 4.0);
8541 else if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_START_TAG
)
8543 int arrowLength
= objectRect
.height
/2;
8544 clientArea
.width
-= (arrowLength
- m_horizontalPadding
);
8547 pts
[0].x
= objectRect
.x
; pts
[0].y
= objectRect
.y
;
8548 pts
[1].x
= objectRect
.x
+ objectRect
.width
- arrowLength
; pts
[1].y
= objectRect
.y
;
8549 pts
[2].x
= objectRect
.x
+ objectRect
.width
; pts
[2].y
= objectRect
.y
+ (objectRect
.height
/2);
8550 pts
[3].x
= objectRect
.x
+ objectRect
.width
- arrowLength
; pts
[3].y
= objectRect
.y
+ objectRect
.height
;
8551 pts
[4].x
= objectRect
.x
; pts
[4].y
= objectRect
.y
+ objectRect
.height
;
8552 dc
.SetPen(borderPen
);
8553 dc
.SetBrush(backgroundBrush
);
8554 dc
.DrawPolygon(5, pts
);
8556 else if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_END_TAG
)
8558 int arrowLength
= objectRect
.height
/2;
8559 clientArea
.width
-= (arrowLength
- m_horizontalPadding
);
8560 clientArea
.x
+= (arrowLength
- m_horizontalPadding
);
8563 pts
[0].x
= objectRect
.x
+ objectRect
.width
; pts
[0].y
= objectRect
.y
;
8564 pts
[1].x
= objectRect
.x
+ arrowLength
; pts
[1].y
= objectRect
.y
;
8565 pts
[2].x
= objectRect
.x
; pts
[2].y
= objectRect
.y
+ (objectRect
.height
/2);
8566 pts
[3].x
= objectRect
.x
+ arrowLength
; pts
[3].y
= objectRect
.y
+ objectRect
.height
;
8567 pts
[4].x
= objectRect
.x
+ objectRect
.width
; pts
[4].y
= objectRect
.y
+ objectRect
.height
;
8568 dc
.SetPen(borderPen
);
8569 dc
.SetBrush(backgroundBrush
);
8570 dc
.DrawPolygon(5, pts
);
8573 if (m_bitmap
.IsOk())
8575 int x
= clientArea
.x
+ (clientArea
.width
- m_bitmap
.GetWidth())/2;
8576 int y
= clientArea
.y
+ m_verticalPadding
;
8577 dc
.DrawBitmap(m_bitmap
, x
, y
, true);
8579 if (selection
.WithinSelection(obj
->GetRange().GetStart(), obj
))
8581 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
8582 wxCheckSetPen(dc
, *wxBLACK_PEN
);
8583 dc
.SetLogicalFunction(wxINVERT
);
8584 dc
.DrawRectangle(wxRect(x
, y
, m_bitmap
.GetWidth(), m_bitmap
.GetHeight()));
8585 dc
.SetLogicalFunction(wxCOPY
);
8590 wxString
label(m_label
);
8591 if (label
.IsEmpty())
8593 int w
, h
, maxDescent
;
8595 dc
.GetTextExtent(m_label
, & w
, &h
, & maxDescent
);
8596 dc
.SetTextForeground(textColour
);
8598 int x
= clientArea
.x
+ (clientArea
.width
- w
)/2;
8599 int y
= clientArea
.y
+ (clientArea
.height
- (h
- maxDescent
))/2;
8600 dc
.DrawText(m_label
, x
, y
);
8607 bool wxRichTextFieldTypeStandard::Layout(wxRichTextField
* obj
, wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& WXUNUSED(rect
), const wxRect
& WXUNUSED(parentRect
), int style
)
8609 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_COMPOSITE
)
8610 return false; // USe default composite layout
8612 wxSize size
= GetSize(obj
, dc
, context
, style
);
8613 obj
->SetCachedSize(size
);
8614 obj
->SetMinSize(size
);
8615 obj
->SetMaxSize(size
);
8619 bool wxRichTextFieldTypeStandard::GetRangeSize(wxRichTextField
* obj
, const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
8621 if (IsTopLevel(obj
))
8622 return obj
->wxRichTextParagraphLayoutBox::GetRangeSize(range
, size
, descent
, dc
, context
, flags
, position
);
8625 wxSize sz
= GetSize(obj
, dc
, context
, 0);
8629 if (partialExtents
->GetCount() > 0)
8630 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
8633 partialExtents
->Add(lastSize
+ sz
.x
);
8640 wxSize
wxRichTextFieldTypeStandard::GetSize(wxRichTextField
* WXUNUSED(obj
), wxDC
& dc
, wxRichTextDrawingContext
& WXUNUSED(context
), int WXUNUSED(style
)) const
8643 int w
= 0, h
= 0, maxDescent
= 0;
8646 if (m_bitmap
.IsOk())
8648 w
= m_bitmap
.GetWidth();
8649 h
= m_bitmap
.GetHeight();
8651 sz
= wxSize(w
+ m_horizontalMargin
*2, h
+ m_verticalMargin
*2);
8655 wxString
label(m_label
);
8656 if (label
.IsEmpty())
8659 dc
.GetTextExtent(label
, & w
, &h
, & maxDescent
);
8661 sz
= wxSize(w
+ m_horizontalPadding
*2 + m_horizontalMargin
*2, h
+ m_verticalPadding
*2 + m_verticalMargin
*2);
8664 if (m_displayStyle
!= wxRICHTEXT_FIELD_STYLE_NO_BORDER
)
8666 sz
.x
+= borderSize
*2;
8667 sz
.y
+= borderSize
*2;
8670 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_START_TAG
|| m_displayStyle
== wxRICHTEXT_FIELD_STYLE_END_TAG
)
8672 // Add space for the arrow
8673 sz
.x
+= (sz
.y
/2 - m_horizontalPadding
);
8679 IMPLEMENT_DYNAMIC_CLASS(wxRichTextCell
, wxRichTextBox
)
8681 wxRichTextCell::wxRichTextCell(wxRichTextObject
* parent
):
8682 wxRichTextBox(parent
)
8687 bool wxRichTextCell::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8689 return wxRichTextBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8693 void wxRichTextCell::Copy(const wxRichTextCell
& obj
)
8695 wxRichTextBox::Copy(obj
);
8698 // Edit properties via a GUI
8699 bool wxRichTextCell::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8701 // We need to gather common attributes for all selected cells.
8703 wxRichTextTable
* table
= wxDynamicCast(GetParent(), wxRichTextTable
);
8704 bool multipleCells
= false;
8705 wxRichTextAttr attr
;
8707 if (table
&& buffer
&& buffer
->GetRichTextCtrl() && buffer
->GetRichTextCtrl()->GetSelection().IsValid() &&
8708 buffer
->GetRichTextCtrl()->GetSelection().GetContainer() == GetParent())
8710 wxRichTextAttr clashingAttr
, absentAttr
;
8711 const wxRichTextSelection
& sel
= buffer
->GetRichTextCtrl()->GetSelection();
8713 int selectedCellCount
= 0;
8714 for (i
= 0; i
< sel
.GetCount(); i
++)
8716 const wxRichTextRange
& range
= sel
[i
];
8717 wxRichTextCell
* cell
= table
->GetCell(range
.GetStart());
8720 wxRichTextAttr cellStyle
= cell
->GetAttributes();
8722 CollectStyle(attr
, cellStyle
, clashingAttr
, absentAttr
);
8724 selectedCellCount
++;
8727 multipleCells
= selectedCellCount
> 1;
8731 attr
= GetAttributes();
8736 caption
= _("Multiple Cell Properties");
8738 caption
= _("Cell Properties");
8740 wxRichTextObjectPropertiesDialog
cellDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, caption
);
8741 cellDlg
.SetAttributes(attr
);
8743 wxRichTextSizePage
* sizePage
= wxDynamicCast(cellDlg
.FindPage(CLASSINFO(wxRichTextSizePage
)), wxRichTextSizePage
);
8746 // We don't want position and floating controls for a cell.
8747 sizePage
->ShowPositionControls(false);
8748 sizePage
->ShowFloatingControls(false);
8751 if (cellDlg
.ShowModal() == wxID_OK
)
8755 const wxRichTextSelection
& sel
= buffer
->GetRichTextCtrl()->GetSelection();
8756 // Apply the style; we interpret indeterminate attributes as 'don't touch this attribute'
8757 // since it may represent clashing attributes across multiple objects.
8758 table
->SetCellStyle(sel
, attr
);
8761 // For a single object, indeterminate attributes set by the user should be reflected in the
8762 // actual object style, so pass the wxRICHTEXT_SETSTYLE_RESET flag to assign
8763 // the style directly instead of applying (which ignores indeterminate attributes,
8764 // leaving them as they were).
8765 cellDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
8772 WX_DEFINE_OBJARRAY(wxRichTextObjectPtrArrayArray
)
8774 IMPLEMENT_DYNAMIC_CLASS(wxRichTextTable
, wxRichTextBox
)
8776 wxRichTextTable::wxRichTextTable(wxRichTextObject
* parent
): wxRichTextBox(parent
)
8782 // Draws the object.
8783 bool wxRichTextTable::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8785 return wxRichTextBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8788 WX_DECLARE_OBJARRAY(wxRect
, wxRichTextRectArray
);
8789 WX_DEFINE_OBJARRAY(wxRichTextRectArray
);
8791 // Lays the object out. rect is the space available for layout. Often it will
8792 // be the specified overall space for this object, if trying to constrain
8793 // layout to a particular size, or it could be the total space available in the
8794 // parent. rect is the overall size, so we must subtract margins and padding.
8795 // to get the actual available space.
8796 bool wxRichTextTable::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& WXUNUSED(parentRect
), int style
)
8798 SetPosition(rect
.GetPosition());
8800 // TODO: the meaty bit. Calculate sizes of all cells and rows. Try to use
8801 // minimum size if within alloted size, then divide up remaining size
8802 // between rows/cols.
8805 wxRichTextBuffer
* buffer
= GetBuffer();
8806 if (buffer
) scale
= buffer
->GetScale();
8808 wxRect availableSpace
= GetAvailableContentArea(dc
, context
, rect
);
8809 wxTextAttrDimensionConverter
converter(dc
, scale
, availableSpace
.GetSize());
8811 wxRichTextAttr
attr(GetAttributes());
8812 context
.ApplyVirtualAttributes(attr
, this);
8814 // If we have no fixed table size, and assuming we're not pushed for
8815 // space, then we don't have to try to stretch the table to fit the contents.
8816 bool stretchToFitTableWidth
= false;
8818 int tableWidth
= rect
.width
;
8819 if (attr
.GetTextBoxAttr().GetWidth().IsValid())
8821 tableWidth
= converter
.GetPixels(attr
.GetTextBoxAttr().GetWidth());
8823 // Fixed table width, so we do want to stretch columns out if necessary.
8824 stretchToFitTableWidth
= true;
8826 // Shouldn't be able to exceed the size passed to this function
8827 tableWidth
= wxMin(rect
.width
, tableWidth
);
8830 // Get internal padding
8831 int paddingLeft
= 0, paddingTop
= 0;
8832 if (attr
.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
8833 paddingLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetLeft());
8834 if (attr
.GetTextBoxAttr().GetPadding().GetTop().IsValid())
8835 paddingTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetTop());
8837 // Assume that left and top padding are also used for inter-cell padding.
8838 int paddingX
= paddingLeft
;
8839 int paddingY
= paddingTop
;
8841 int totalLeftMargin
= 0, totalRightMargin
= 0, totalTopMargin
= 0, totalBottomMargin
= 0;
8842 GetTotalMargin(dc
, buffer
, attr
, totalLeftMargin
, totalRightMargin
, totalTopMargin
, totalBottomMargin
);
8844 // Internal table width - the area for content
8845 int internalTableWidth
= tableWidth
- totalLeftMargin
- totalRightMargin
;
8847 int rowCount
= m_cells
.GetCount();
8848 if (m_colCount
== 0 || rowCount
== 0)
8850 wxRect
overallRect(rect
.x
, rect
.y
, totalLeftMargin
+ totalRightMargin
, totalTopMargin
+ totalBottomMargin
);
8851 SetCachedSize(overallRect
.GetSize());
8853 // Zero content size
8854 SetMinSize(overallRect
.GetSize());
8855 SetMaxSize(GetMinSize());
8859 // The final calculated widths
8860 wxArrayInt colWidths
;
8861 colWidths
.Add(0, m_colCount
);
8863 wxArrayInt absoluteColWidths
;
8864 absoluteColWidths
.Add(0, m_colCount
);
8866 wxArrayInt percentageColWidths
;
8867 percentageColWidths
.Add(0, m_colCount
);
8868 // wxArrayInt percentageColWidthsSpanning(m_colCount);
8869 // These are only relevant when the first column contains spanning information.
8870 // wxArrayInt columnSpans(m_colCount); // Each contains 1 for non-spanning cell, > 1 for spanning cell.
8871 wxArrayInt maxColWidths
;
8872 maxColWidths
.Add(0, m_colCount
);
8873 wxArrayInt minColWidths
;
8874 minColWidths
.Add(0, m_colCount
);
8876 wxSize
tableSize(tableWidth
, 0);
8880 for (i
= 0; i
< m_colCount
; i
++)
8882 absoluteColWidths
[i
] = 0;
8883 // absoluteColWidthsSpanning[i] = 0;
8884 percentageColWidths
[i
] = -1;
8885 // percentageColWidthsSpanning[i] = -1;
8887 maxColWidths
[i
] = 0;
8888 minColWidths
[i
] = 0;
8889 // columnSpans[i] = 1;
8892 // (0) Determine which cells are visible according to spans
8894 // __________________
8899 // |------------------|
8900 // |__________________| 4
8902 // To calculate cell visibility:
8903 // First find all spanning cells. Build an array of span records with start x, y and end x, y.
8904 // Then for each cell, test whether we're within one of those cells, and unless we're at the start of
8905 // that cell, hide the cell.
8907 // We can also use this array to match the size of spanning cells to the grid. Or just do
8908 // this when we iterate through all cells.
8910 // 0.1: add spanning cells to an array
8911 wxRichTextRectArray rectArray
;
8912 for (j
= 0; j
< m_rowCount
; j
++)
8914 for (i
= 0; i
< m_colCount
; i
++)
8916 wxRichTextBox
* cell
= GetCell(j
, i
);
8917 int colSpan
= 1, rowSpan
= 1;
8918 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8919 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8920 if (cell
->GetProperties().HasProperty(wxT("rowspan")))
8921 rowSpan
= cell
->GetProperties().GetPropertyLong(wxT("rowspan"));
8922 if (colSpan
> 1 || rowSpan
> 1)
8924 rectArray
.Add(wxRect(i
, j
, colSpan
, rowSpan
));
8928 // 0.2: find which cells are subsumed by a spanning cell
8929 for (j
= 0; j
< m_rowCount
; j
++)
8931 for (i
= 0; i
< m_colCount
; i
++)
8933 wxRichTextBox
* cell
= GetCell(j
, i
);
8934 if (rectArray
.GetCount() == 0)
8940 int colSpan
= 1, rowSpan
= 1;
8941 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8942 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8943 if (cell
->GetProperties().HasProperty(wxT("rowspan")))
8944 rowSpan
= cell
->GetProperties().GetPropertyLong(wxT("rowspan"));
8945 if (colSpan
> 1 || rowSpan
> 1)
8947 // Assume all spanning cells are shown
8953 for (k
= 0; k
< (int) rectArray
.GetCount(); k
++)
8955 if (rectArray
[k
].Contains(wxPoint(i
, j
)))
8967 // TODO: find the first spanned cell in each row that spans the most columns and doesn't
8968 // overlap with a spanned cell starting at a previous column position.
8969 // This means we need to keep an array of rects so we can check. However
8970 // it does also mean that some spans simply may not be taken into account
8971 // where there are different spans happening on different rows. In these cases,
8972 // they will simply be as wide as their constituent columns.
8974 // (1) Do an initial layout for all cells to get minimum and maximum size, and get
8975 // the absolute or percentage width of each column.
8977 for (j
= 0; j
< m_rowCount
; j
++)
8979 // First get the overall margins so we can calculate percentage widths based on
8980 // the available content space for all cells on the row
8982 int overallRowContentMargin
= 0;
8983 int visibleCellCount
= 0;
8985 for (i
= 0; i
< m_colCount
; i
++)
8987 wxRichTextBox
* cell
= GetCell(j
, i
);
8988 if (cell
->IsShown())
8990 int cellTotalLeftMargin
= 0, cellTotalRightMargin
= 0, cellTotalTopMargin
= 0, cellTotalBottomMargin
= 0;
8991 GetTotalMargin(dc
, buffer
, cell
->GetAttributes(), cellTotalLeftMargin
, cellTotalRightMargin
, cellTotalTopMargin
, cellTotalBottomMargin
);
8993 overallRowContentMargin
+= (cellTotalLeftMargin
+ cellTotalRightMargin
);
8994 visibleCellCount
++;
8998 // Add in inter-cell padding
8999 overallRowContentMargin
+= ((visibleCellCount
-1) * paddingX
);
9001 int rowContentWidth
= internalTableWidth
- overallRowContentMargin
;
9002 wxSize
rowTableSize(rowContentWidth
, 0);
9003 wxTextAttrDimensionConverter
converter(dc
, scale
, rowTableSize
);
9005 for (i
= 0; i
< m_colCount
; i
++)
9007 wxRichTextBox
* cell
= GetCell(j
, i
);
9008 if (cell
->IsShown())
9011 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9012 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9014 // Lay out cell to find min/max widths
9015 cell
->Invalidate(wxRICHTEXT_ALL
);
9016 cell
->Layout(dc
, context
, availableSpace
, availableSpace
, style
);
9020 int absoluteCellWidth
= -1;
9021 int percentageCellWidth
= -1;
9023 // I think we need to calculate percentages from the internal table size,
9024 // minus the padding between cells which we'll need to calculate from the
9025 // (number of VISIBLE cells - 1)*paddingX. Then percentages that add up to 100%
9026 // will add up to 100%. In CSS, the width specifies the cell's content rect width,
9027 // so if we want to conform to that we'll need to add in the overall cell margins.
9028 // However, this will make it difficult to specify percentages that add up to
9029 // 100% and still fit within the table width.
9030 // Let's say two cells have 50% width. They have 10 pixels of overall margin each.
9031 // The table content rect is 500 pixels and the inter-cell padding is 20 pixels.
9032 // If we're using internal content size for the width, we would calculate the
9033 // the overall cell width for n cells as:
9034 // (500 - 20*(n-1) - overallCellMargin1 - overallCellMargin2 - ...) * percentage / 100
9035 // + thisOverallCellMargin
9036 // = 500 - 20 - 10 - 10) * 0.5 + 10 = 240 pixels overall cell width.
9037 // Adding this back, we get 240 + 240 + 20 = 500 pixels.
9039 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9041 int w
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetWidth());
9042 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
9044 percentageCellWidth
= w
;
9048 absoluteCellWidth
= w
;
9050 // Override absolute width with minimum width if necessary
9051 if (cell
->GetMinSize().x
> 0 && absoluteCellWidth
!=1 && cell
->GetMinSize().x
> absoluteCellWidth
)
9052 absoluteCellWidth
= cell
->GetMinSize().x
;
9055 if (absoluteCellWidth
!= -1)
9057 if (absoluteCellWidth
> absoluteColWidths
[i
])
9058 absoluteColWidths
[i
] = absoluteCellWidth
;
9061 if (percentageCellWidth
!= -1)
9063 if (percentageCellWidth
> percentageColWidths
[i
])
9064 percentageColWidths
[i
] = percentageCellWidth
;
9067 if (colSpan
== 1 && cell
->GetMinSize().x
&& cell
->GetMinSize().x
> minColWidths
[i
])
9068 minColWidths
[i
] = cell
->GetMinSize().x
;
9069 if (colSpan
== 1 && cell
->GetMaxSize().x
&& cell
->GetMaxSize().x
> maxColWidths
[i
])
9070 maxColWidths
[i
] = cell
->GetMaxSize().x
;
9076 // (2) Allocate initial column widths from minimum widths, absolute values and proportions
9077 // TODO: simply merge this into (1).
9078 for (i
= 0; i
< m_colCount
; i
++)
9080 if (absoluteColWidths
[i
] > 0)
9082 colWidths
[i
] = absoluteColWidths
[i
];
9084 else if (percentageColWidths
[i
] > 0)
9086 colWidths
[i
] = percentageColWidths
[i
];
9088 // This is rubbish - we calculated the absolute widths from percentages, so
9089 // we can't do it again here.
9090 //colWidths[i] = (int) (double(percentageColWidths[i]) * double(tableWidth) / 100.0 + 0.5);
9094 // (3) Process absolute or proportional widths of spanning columns,
9095 // now that we know what our fixed column widths are going to be.
9096 // Spanned cells will try to adjust columns so the span will fit.
9097 // Even existing fixed column widths can be expanded if necessary.
9098 // Actually, currently fixed columns widths aren't adjusted; instead,
9099 // the algorithm favours earlier rows and adjusts unspecified column widths
9100 // the first time only. After that, we can't know whether the column has been
9101 // specified explicitly or not. (We could make a note if necessary.)
9102 for (j
= 0; j
< m_rowCount
; j
++)
9104 // First get the overall margins so we can calculate percentage widths based on
9105 // the available content space for all cells on the row
9107 int overallRowContentMargin
= 0;
9108 int visibleCellCount
= 0;
9110 for (i
= 0; i
< m_colCount
; i
++)
9112 wxRichTextBox
* cell
= GetCell(j
, i
);
9113 if (cell
->IsShown())
9115 int cellTotalLeftMargin
= 0, cellTotalRightMargin
= 0, cellTotalTopMargin
= 0, cellTotalBottomMargin
= 0;
9116 GetTotalMargin(dc
, buffer
, cell
->GetAttributes(), cellTotalLeftMargin
, cellTotalRightMargin
, cellTotalTopMargin
, cellTotalBottomMargin
);
9118 overallRowContentMargin
+= (cellTotalLeftMargin
+ cellTotalRightMargin
);
9119 visibleCellCount
++;
9123 // Add in inter-cell padding
9124 overallRowContentMargin
+= ((visibleCellCount
-1) * paddingX
);
9126 int rowContentWidth
= internalTableWidth
- overallRowContentMargin
;
9127 wxSize
rowTableSize(rowContentWidth
, 0);
9128 wxTextAttrDimensionConverter
converter(dc
, scale
, rowTableSize
);
9130 for (i
= 0; i
< m_colCount
; i
++)
9132 wxRichTextBox
* cell
= GetCell(j
, i
);
9133 if (cell
->IsShown())
9136 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9137 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9141 int spans
= wxMin(colSpan
, m_colCount
- i
);
9145 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9147 cellWidth
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetWidth());
9148 // Override absolute width with minimum width if necessary
9149 if (cell
->GetMinSize().x
> 0 && cellWidth
!=1 && cell
->GetMinSize().x
> cellWidth
)
9150 cellWidth
= cell
->GetMinSize().x
;
9154 // Do we want to do this? It's the only chance we get to
9155 // use the cell's min/max sizes, so we need to work out
9156 // how we're going to balance the unspecified spanning cell
9157 // width with the possibility more-constrained constituent cell widths.
9158 // Say there's a tiny bitmap giving it a max width of 10 pixels. We
9159 // don't want to constraint all the spanned columns to fit into this cell.
9160 // OK, let's say that if any of the constituent columns don't fit,
9161 // then we simply stop constraining the columns; instead, we'll just fit the spanning
9162 // cells to the columns later.
9163 cellWidth
= cell
->GetMinSize().x
;
9164 if (cell
->GetMaxSize().x
> cellWidth
)
9165 cellWidth
= cell
->GetMaxSize().x
;
9168 // Subtract the padding between cells
9169 int spanningWidth
= cellWidth
;
9170 spanningWidth
-= paddingX
* (spans
-1);
9172 if (spanningWidth
> 0)
9174 // Now share the spanning width between columns within that span
9175 // TODO: take into account min widths of columns within the span
9176 int spanningWidthLeft
= spanningWidth
;
9177 int stretchColCount
= 0;
9178 for (k
= i
; k
< (i
+spans
); k
++)
9180 if (colWidths
[k
] > 0) // absolute or proportional width has been specified
9181 spanningWidthLeft
-= colWidths
[k
];
9185 // Now divide what's left between the remaining columns
9187 if (stretchColCount
> 0)
9188 colShare
= spanningWidthLeft
/ stretchColCount
;
9189 int colShareRemainder
= spanningWidthLeft
- (colShare
* stretchColCount
);
9191 // If fixed-width columns are currently too big, then we'll later
9192 // stretch the spanned cell to fit.
9194 if (spanningWidthLeft
> 0)
9196 for (k
= i
; k
< (i
+spans
); k
++)
9198 if (colWidths
[k
] <= 0) // absolute or proportional width has not been specified
9200 int newWidth
= colShare
;
9201 if (k
== (i
+spans
-1))
9202 newWidth
+= colShareRemainder
; // ensure all pixels are filled
9203 colWidths
[k
] = newWidth
;
9214 // (4) Next, share any remaining space out between columns that have not yet been calculated.
9215 // TODO: take into account min widths of columns within the span
9216 int tableWidthMinusPadding
= internalTableWidth
- (m_colCount
-1)*paddingX
;
9217 int widthLeft
= tableWidthMinusPadding
;
9218 int stretchColCount
= 0;
9219 for (i
= 0; i
< m_colCount
; i
++)
9221 // TODO: we need to take into account min widths.
9222 // Subtract min width from width left, then
9223 // add the colShare to the min width
9224 if (colWidths
[i
] > 0) // absolute or proportional width has been specified
9225 widthLeft
-= colWidths
[i
];
9228 if (minColWidths
[i
] > 0)
9229 widthLeft
-= minColWidths
[i
];
9235 // Now divide what's left between the remaining columns
9237 if (stretchColCount
> 0)
9238 colShare
= widthLeft
/ stretchColCount
;
9239 int colShareRemainder
= widthLeft
- (colShare
* stretchColCount
);
9241 // Check we don't have enough space, in which case shrink all columns, overriding
9242 // any absolute/proportional widths
9243 // TODO: actually we would like to divide up the shrinkage according to size.
9244 // How do we calculate the proportions that will achieve this?
9245 // Could first choose an arbitrary value for stretching cells, and then calculate
9246 // factors to multiply each width by.
9247 // TODO: want to record this fact and pass to an iteration that tries e.g. min widths
9248 if (widthLeft
< 0 || (stretchToFitTableWidth
&& (stretchColCount
== 0)))
9250 colShare
= tableWidthMinusPadding
/ m_colCount
;
9251 colShareRemainder
= tableWidthMinusPadding
- (colShare
* m_colCount
);
9252 for (i
= 0; i
< m_colCount
; i
++)
9255 minColWidths
[i
] = 0;
9259 // We have to adjust the columns if either we need to shrink the
9260 // table to fit the parent/table width, or we explicitly set the
9261 // table width and need to stretch out the table.
9262 if (widthLeft
< 0 || stretchToFitTableWidth
)
9264 for (i
= 0; i
< m_colCount
; i
++)
9266 if (colWidths
[i
] <= 0) // absolute or proportional width has not been specified
9268 if (minColWidths
[i
] > 0)
9269 colWidths
[i
] = minColWidths
[i
] + colShare
;
9271 colWidths
[i
] = colShare
;
9272 if (i
== (m_colCount
-1))
9273 colWidths
[i
] += colShareRemainder
; // ensure all pixels are filled
9278 // TODO: if spanned cells have no specified or max width, make them the
9279 // as big as the columns they span. Do this for all spanned cells in all
9280 // rows, of course. Size any spanned cells left over at the end - even if they
9281 // have width > 0, make sure they're limited to the appropriate column edge.
9285 Sort out confusion between content width
9286 and overall width later. For now, assume we specify overall width.
9288 So, now we've laid out the table to fit into the given space
9289 and have used specified widths and minimum widths.
9291 Now we need to consider how we will try to take maximum width into account.
9295 // (??) TODO: take max width into account
9297 // (6) Lay out all cells again with the current values
9300 int y
= availableSpace
.y
;
9301 for (j
= 0; j
< m_rowCount
; j
++)
9303 int x
= availableSpace
.x
; // TODO: take into account centering etc.
9304 int maxCellHeight
= 0;
9305 int maxSpecifiedCellHeight
= 0;
9307 wxArrayInt actualWidths
;
9308 actualWidths
.Add(0, m_colCount
);
9310 wxTextAttrDimensionConverter
converter(dc
, scale
);
9311 for (i
= 0; i
< m_colCount
; i
++)
9313 wxRichTextCell
* cell
= GetCell(j
, i
);
9314 if (cell
->IsShown())
9316 // Get max specified cell height
9317 // Don't handle percentages for height
9318 if (cell
->GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && cell
->GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() != wxTEXT_ATTR_UNITS_PERCENTAGE
)
9320 int h
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetHeight());
9321 if (h
> maxSpecifiedCellHeight
)
9322 maxSpecifiedCellHeight
= h
;
9325 if (colWidths
[i
] > 0) // absolute or proportional width has been specified
9328 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9329 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9331 wxRect availableCellSpace
;
9333 // TODO: take into acount spans
9336 // Calculate the size of this spanning cell from its constituent columns
9338 int spans
= wxMin(colSpan
, m_colCount
- i
);
9339 for (k
= i
; k
< spans
; k
++)
9345 availableCellSpace
= wxRect(x
, y
, xx
, -1);
9348 availableCellSpace
= wxRect(x
, y
, colWidths
[i
], -1);
9350 // Store actual width so we can force cell to be the appropriate width on the final loop
9351 actualWidths
[i
] = availableCellSpace
.GetWidth();
9354 cell
->Invalidate(wxRICHTEXT_ALL
);
9355 cell
->Layout(dc
, context
, availableCellSpace
, availableSpace
, style
);
9357 // TODO: use GetCachedSize().x to compute 'natural' size
9359 x
+= (availableCellSpace
.GetWidth() + paddingX
);
9360 if (cell
->GetCachedSize().y
> maxCellHeight
)
9361 maxCellHeight
= cell
->GetCachedSize().y
;
9366 maxCellHeight
= wxMax(maxCellHeight
, maxSpecifiedCellHeight
);
9368 for (i
= 0; i
< m_colCount
; i
++)
9370 wxRichTextCell
* cell
= GetCell(j
, i
);
9371 if (cell
->IsShown())
9373 wxRect availableCellSpace
= wxRect(cell
->GetPosition(), wxSize(actualWidths
[i
], maxCellHeight
));
9374 // Lay out cell with new height
9375 cell
->Invalidate(wxRICHTEXT_ALL
);
9376 cell
->Layout(dc
, context
, availableCellSpace
, availableSpace
, style
);
9378 // Make sure the cell size really is the appropriate size,
9379 // not the calculated box size
9380 cell
->SetCachedSize(wxSize(actualWidths
[i
], maxCellHeight
));
9382 maxRight
= wxMax(maxRight
, cell
->GetPosition().x
+ cell
->GetCachedSize().x
);
9387 if (j
< (m_rowCount
-1))
9391 // We need to add back the margins etc.
9393 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
9394 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxRight
- availableSpace
.x
, y
- availableSpace
.y
));
9395 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
9396 SetCachedSize(marginRect
.GetSize());
9399 // TODO: calculate max size
9401 SetMaxSize(GetCachedSize());
9404 // TODO: calculate min size
9406 SetMinSize(GetCachedSize());
9409 // TODO: currently we use either a fixed table width or the parent's size.
9410 // We also want to be able to calculate the table width from its content,
9411 // whether using fixed column widths or cell content min/max width.
9412 // Probably need a boolean flag to say whether we need to stretch cells
9413 // to fit the table width, or to simply use min/max cell widths. The
9414 // trouble with this is that if cell widths are not specified, they
9415 // will be tiny; we could use arbitrary defaults but this seems unsatisfactory.
9416 // Anyway, ignoring that problem, we probably need to factor layout into a function
9417 // that can can calculate the maximum unconstrained layout in case table size is
9418 // not specified. Then LayoutToBestSize() can choose to use either parent size to
9419 // constrain Layout(), or the previously-calculated max size to constraint layout.
9424 // Finds the absolute position and row height for the given character position
9425 bool wxRichTextTable::FindPosition(wxDC
& dc
, wxRichTextDrawingContext
& context
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
9427 wxRichTextCell
* child
= GetCell(index
+1);
9430 // Find the position at the start of the child cell, since the table doesn't
9431 // have any caret position of its own.
9432 return child
->FindPosition(dc
, context
, -1, pt
, height
, forceLineStart
);
9438 // Get the cell at the given character position (in the range of the table).
9439 wxRichTextCell
* wxRichTextTable::GetCell(long pos
) const
9441 int row
= 0, col
= 0;
9442 if (GetCellRowColumnPosition(pos
, row
, col
))
9444 return GetCell(row
, col
);
9450 // Get the row/column for a given character position
9451 bool wxRichTextTable::GetCellRowColumnPosition(long pos
, int& row
, int& col
) const
9453 if (m_colCount
== 0 || m_rowCount
== 0)
9456 row
= (int) (pos
/ m_colCount
);
9457 col
= pos
- (row
* m_colCount
);
9459 wxASSERT(row
< m_rowCount
&& col
< m_colCount
);
9461 if (row
< m_rowCount
&& col
< m_colCount
)
9467 // Calculate range, taking row/cell ordering into account instead of relying
9468 // on list ordering.
9469 void wxRichTextTable::CalculateRange(long start
, long& end
)
9471 long current
= start
;
9472 long lastEnd
= current
;
9481 for (i
= 0; i
< m_rowCount
; i
++)
9483 for (j
= 0; j
< m_colCount
; j
++)
9485 wxRichTextCell
* child
= GetCell(i
, j
);
9490 child
->CalculateRange(current
, childEnd
);
9493 current
= childEnd
+ 1;
9498 // A top-level object always has a range of size 1,
9499 // because its children don't count at this level.
9501 m_range
.SetRange(start
, start
);
9503 // An object with no children has zero length
9504 if (m_children
.GetCount() == 0)
9506 m_ownRange
.SetRange(0, lastEnd
);
9509 // Gets the range size.
9510 bool wxRichTextTable::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
9512 return wxRichTextBox::GetRangeSize(range
, size
, descent
, dc
, context
, flags
, position
, partialExtents
);
9515 // Deletes content in the given range.
9516 bool wxRichTextTable::DeleteRange(const wxRichTextRange
& WXUNUSED(range
))
9518 // TODO: implement deletion of cells
9522 // Gets any text in this object for the given range.
9523 wxString
wxRichTextTable::GetTextForRange(const wxRichTextRange
& range
) const
9525 return wxRichTextBox::GetTextForRange(range
);
9528 // Copies this object.
9529 void wxRichTextTable::Copy(const wxRichTextTable
& obj
)
9531 wxRichTextBox::Copy(obj
);
9535 m_rowCount
= obj
.m_rowCount
;
9536 m_colCount
= obj
.m_colCount
;
9538 m_cells
.Add(wxRichTextObjectPtrArray(), m_rowCount
);
9541 for (i
= 0; i
< m_rowCount
; i
++)
9543 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
9544 for (j
= 0; j
< m_colCount
; j
++)
9546 wxRichTextCell
* cell
= wxDynamicCast(obj
.GetCell(i
, j
)->Clone(), wxRichTextCell
);
9554 void wxRichTextTable::ClearTable()
9560 bool wxRichTextTable::CreateTable(int rows
, int cols
)
9567 m_cells
.Add(wxRichTextObjectPtrArray(), rows
);
9570 for (i
= 0; i
< rows
; i
++)
9572 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
9573 for (j
= 0; j
< cols
; j
++)
9575 wxRichTextCell
* cell
= new wxRichTextCell
;
9577 cell
->AddParagraph(wxEmptyString
);
9586 wxRichTextCell
* wxRichTextTable::GetCell(int row
, int col
) const
9588 wxASSERT(row
< m_rowCount
);
9589 wxASSERT(col
< m_colCount
);
9591 if (row
< m_rowCount
&& col
< m_colCount
)
9593 wxRichTextObjectPtrArray
& colArray
= m_cells
[row
];
9594 wxRichTextObject
* obj
= colArray
[col
];
9595 return wxDynamicCast(obj
, wxRichTextCell
);
9601 // Returns a selection object specifying the selections between start and end character positions.
9602 // For example, a table would deduce what cells (of range length 1) are selected when dragging across the table.
9603 wxRichTextSelection
wxRichTextTable::GetSelection(long start
, long end
) const
9605 wxRichTextSelection selection
;
9606 selection
.SetContainer((wxRichTextTable
*) this);
9615 wxASSERT( start
>= 0 && end
< (m_colCount
* m_rowCount
));
9617 if (end
>= (m_colCount
* m_rowCount
))
9620 // We need to find the rectangle of cells that is described by the rectangle
9621 // with start, end as the diagonal. Make sure we don't add cells that are
9622 // not currenty visible because they are overlapped by spanning cells.
9624 --------------------------
9625 | 0 | 1 | 2 | 3 | 4 |
9626 --------------------------
9627 | 5 | 6 | 7 | 8 | 9 |
9628 --------------------------
9629 | 10 | 11 | 12 | 13 | 14 |
9630 --------------------------
9631 | 15 | 16 | 17 | 18 | 19 |
9632 --------------------------
9634 Let's say we select 6 -> 18.
9636 Left and right edge cols of rectangle are 1 and 3 inclusive. Find least/greatest to find
9637 which is left and which is right.
9639 Top and bottom edge rows are 1 and 3 inclusive. Again, find least/greatest to find top and bottom.
9641 Now go through rows from 1 to 3 and only add cells that are (a) within above column range
9647 int leftCol
= start
- m_colCount
* int(start
/m_colCount
);
9648 int rightCol
= end
- m_colCount
* int(end
/m_colCount
);
9650 int topRow
= int(start
/m_colCount
);
9651 int bottomRow
= int(end
/m_colCount
);
9653 if (leftCol
> rightCol
)
9660 if (topRow
> bottomRow
)
9662 int tmp
= bottomRow
;
9668 for (i
= topRow
; i
<= bottomRow
; i
++)
9670 for (j
= leftCol
; j
<= rightCol
; j
++)
9672 wxRichTextCell
* cell
= GetCell(i
, j
);
9673 if (cell
&& cell
->IsShown())
9674 selection
.Add(cell
->GetRange());
9681 // Sets the attributes for the cells specified by the selection.
9682 bool wxRichTextTable::SetCellStyle(const wxRichTextSelection
& selection
, const wxRichTextAttr
& style
, int flags
)
9684 if (selection
.GetContainer() != this)
9687 wxRichTextBuffer
* buffer
= GetBuffer();
9688 bool haveControl
= (buffer
&& buffer
->GetRichTextCtrl() != NULL
);
9689 bool withUndo
= haveControl
&& ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
9692 buffer
->BeginBatchUndo(_("Set Cell Style"));
9694 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
9697 wxRichTextCell
* cell
= wxDynamicCast(node
->GetData(), wxRichTextCell
);
9698 if (cell
&& selection
.WithinSelection(cell
->GetRange().GetStart()))
9699 SetStyle(cell
, style
, flags
);
9700 node
= node
->GetNext();
9703 // Do action, or delay it until end of batch.
9705 buffer
->EndBatchUndo();
9710 bool wxRichTextTable::DeleteRows(int startRow
, int noRows
)
9712 wxASSERT((startRow
+ noRows
) < m_rowCount
);
9713 if ((startRow
+ noRows
) >= m_rowCount
)
9717 for (i
= startRow
; i
< (startRow
+noRows
); i
++)
9719 wxRichTextObjectPtrArray
& colArray
= m_cells
[startRow
];
9720 for (j
= 0; j
< (int) colArray
.GetCount(); j
++)
9722 wxRichTextObject
* cell
= colArray
[j
];
9723 RemoveChild(cell
, true);
9726 // Keep deleting at the same position, since we move all
9728 m_cells
.RemoveAt(startRow
);
9731 m_rowCount
= m_rowCount
- noRows
;
9736 bool wxRichTextTable::DeleteColumns(int startCol
, int noCols
)
9738 wxASSERT((startCol
+ noCols
) < m_colCount
);
9739 if ((startCol
+ noCols
) >= m_colCount
)
9742 bool deleteRows
= (noCols
== m_colCount
);
9745 for (i
= 0; i
< m_rowCount
; i
++)
9747 wxRichTextObjectPtrArray
& colArray
= m_cells
[deleteRows
? 0 : i
];
9748 for (j
= startCol
; j
< (startCol
+noCols
); j
++)
9750 wxRichTextObject
* cell
= colArray
[j
];
9751 RemoveChild(cell
, true);
9755 m_cells
.RemoveAt(0);
9760 m_colCount
= m_colCount
- noCols
;
9765 bool wxRichTextTable::AddRows(int startRow
, int noRows
, const wxRichTextAttr
& attr
)
9767 wxASSERT(startRow
<= m_rowCount
);
9768 if (startRow
> m_rowCount
)
9772 for (i
= 0; i
< noRows
; i
++)
9775 if (startRow
== m_rowCount
)
9777 m_cells
.Add(wxRichTextObjectPtrArray());
9778 idx
= m_cells
.GetCount() - 1;
9782 m_cells
.Insert(wxRichTextObjectPtrArray(), startRow
+i
);
9786 wxRichTextObjectPtrArray
& colArray
= m_cells
[idx
];
9787 for (j
= 0; j
< m_colCount
; j
++)
9789 wxRichTextCell
* cell
= new wxRichTextCell
;
9790 cell
->GetAttributes() = attr
;
9797 m_rowCount
= m_rowCount
+ noRows
;
9801 bool wxRichTextTable::AddColumns(int startCol
, int noCols
, const wxRichTextAttr
& attr
)
9803 wxASSERT(startCol
<= m_colCount
);
9804 if (startCol
> m_colCount
)
9808 for (i
= 0; i
< m_rowCount
; i
++)
9810 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
9811 for (j
= 0; j
< noCols
; j
++)
9813 wxRichTextCell
* cell
= new wxRichTextCell
;
9814 cell
->GetAttributes() = attr
;
9818 if (startCol
== m_colCount
)
9821 colArray
.Insert(cell
, startCol
+j
);
9825 m_colCount
= m_colCount
+ noCols
;
9830 // Edit properties via a GUI
9831 bool wxRichTextTable::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
9833 wxRichTextObjectPropertiesDialog
boxDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Table Properties"));
9834 boxDlg
.SetAttributes(GetAttributes());
9836 if (boxDlg
.ShowModal() == wxID_OK
)
9838 boxDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
9846 * Module to initialise and clean up handlers
9849 class wxRichTextModule
: public wxModule
9851 DECLARE_DYNAMIC_CLASS(wxRichTextModule
)
9853 wxRichTextModule() {}
9856 wxRichTextBuffer::SetRenderer(new wxRichTextStdRenderer
);
9857 wxRichTextBuffer::InitStandardHandlers();
9858 wxRichTextParagraph::InitDefaultTabs();
9860 wxRichTextXMLHandler::RegisterNodeName(wxT("text"), wxT("wxRichTextPlainText"));
9861 wxRichTextXMLHandler::RegisterNodeName(wxT("symbol"), wxT("wxRichTextPlainText"));
9862 wxRichTextXMLHandler::RegisterNodeName(wxT("image"), wxT("wxRichTextImage"));
9863 wxRichTextXMLHandler::RegisterNodeName(wxT("paragraph"), wxT("wxRichTextParagraph"));
9864 wxRichTextXMLHandler::RegisterNodeName(wxT("paragraphlayout"), wxT("wxRichTextParagraphLayoutBox"));
9865 wxRichTextXMLHandler::RegisterNodeName(wxT("textbox"), wxT("wxRichTextBox"));
9866 wxRichTextXMLHandler::RegisterNodeName(wxT("cell"), wxT("wxRichTextCell"));
9867 wxRichTextXMLHandler::RegisterNodeName(wxT("table"), wxT("wxRichTextTable"));
9868 wxRichTextXMLHandler::RegisterNodeName(wxT("field"), wxT("wxRichTextField"));
9874 wxRichTextBuffer::CleanUpHandlers();
9875 wxRichTextBuffer::CleanUpDrawingHandlers();
9876 wxRichTextBuffer::CleanUpFieldTypes();
9877 wxRichTextXMLHandler::ClearNodeToClassMap();
9878 wxRichTextDecimalToRoman(-1);
9879 wxRichTextParagraph::ClearDefaultTabs();
9880 wxRichTextCtrl::ClearAvailableFontNames();
9881 wxRichTextBuffer::SetRenderer(NULL
);
9885 IMPLEMENT_DYNAMIC_CLASS(wxRichTextModule
, wxModule
)
9888 // If the richtext lib is dynamically loaded after the app has already started
9889 // (such as from wxPython) then the built-in module system will not init this
9890 // module. Provide this function to do it manually.
9891 void wxRichTextModuleInit()
9893 wxModule
* module = new wxRichTextModule
;
9895 wxModule::RegisterModule(module);
9900 * Commands for undo/redo
9904 wxRichTextCommand::wxRichTextCommand(const wxString
& name
, wxRichTextCommandId id
, wxRichTextBuffer
* buffer
,
9905 wxRichTextParagraphLayoutBox
* container
, wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
): wxCommand(true, name
)
9907 /* wxRichTextAction* action = */ new wxRichTextAction(this, name
, id
, buffer
, container
, ctrl
, ignoreFirstTime
);
9910 wxRichTextCommand::wxRichTextCommand(const wxString
& name
): wxCommand(true, name
)
9914 wxRichTextCommand::~wxRichTextCommand()
9919 void wxRichTextCommand::AddAction(wxRichTextAction
* action
)
9921 if (!m_actions
.Member(action
))
9922 m_actions
.Append(action
);
9925 bool wxRichTextCommand::Do()
9927 for (wxList::compatibility_iterator node
= m_actions
.GetFirst(); node
; node
= node
->GetNext())
9929 wxRichTextAction
* action
= (wxRichTextAction
*) node
->GetData();
9936 bool wxRichTextCommand::Undo()
9938 for (wxList::compatibility_iterator node
= m_actions
.GetLast(); node
; node
= node
->GetPrevious())
9940 wxRichTextAction
* action
= (wxRichTextAction
*) node
->GetData();
9947 void wxRichTextCommand::ClearActions()
9949 WX_CLEAR_LIST(wxList
, m_actions
);
9957 wxRichTextAction::wxRichTextAction(wxRichTextCommand
* cmd
, const wxString
& name
, wxRichTextCommandId id
,
9958 wxRichTextBuffer
* buffer
, wxRichTextParagraphLayoutBox
* container
,
9959 wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
)
9963 m_containerAddress
.Create(buffer
, container
);
9964 m_ignoreThis
= ignoreFirstTime
;
9969 m_newParagraphs
.SetDefaultStyle(buffer
->GetDefaultStyle());
9970 m_newParagraphs
.SetBasicStyle(buffer
->GetBasicStyle());
9972 cmd
->AddAction(this);
9975 wxRichTextAction::~wxRichTextAction()
9981 // Returns the container that this action refers to, using the container address and top-level buffer.
9982 wxRichTextParagraphLayoutBox
* wxRichTextAction::GetContainer() const
9984 wxRichTextParagraphLayoutBox
* container
= wxDynamicCast(GetContainerAddress().GetObject(m_buffer
), wxRichTextParagraphLayoutBox
);
9989 void wxRichTextAction::CalculateRefreshOptimizations(wxArrayInt
& optimizationLineCharPositions
, wxArrayInt
& optimizationLineYPositions
)
9991 // Store a list of line start character and y positions so we can figure out which area
9992 // we need to refresh
9994 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9995 wxRichTextParagraphLayoutBox
* container
= GetContainer();
9996 wxASSERT(container
!= NULL
);
10000 // NOTE: we're assuming that the buffer is laid out correctly at this point.
10001 // If we had several actions, which only invalidate and leave layout until the
10002 // paint handler is called, then this might not be true. So we may need to switch
10003 // optimisation on only when we're simply adding text and not simultaneously
10004 // deleting a selection, for example. Or, we make sure the buffer is laid out correctly
10005 // first, but of course this means we'll be doing it twice.
10006 if (!m_buffer
->IsDirty() && m_ctrl
) // can only do optimisation if the buffer is already laid out correctly
10008 wxSize clientSize
= m_ctrl
->GetClientSize();
10009 wxPoint firstVisiblePt
= m_ctrl
->GetFirstVisiblePoint();
10010 int lastY
= firstVisiblePt
.y
+ clientSize
.y
;
10012 wxRichTextParagraph
* para
= container
->GetParagraphAtPosition(GetRange().GetStart());
10013 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(para
);
10016 wxRichTextParagraph
* child
= (wxRichTextParagraph
*) node
->GetData();
10017 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
10020 wxRichTextLine
* line
= node2
->GetData();
10021 wxPoint pt
= line
->GetAbsolutePosition();
10022 wxRichTextRange range
= line
->GetAbsoluteRange();
10026 node2
= wxRichTextLineList::compatibility_iterator();
10027 node
= wxRichTextObjectList::compatibility_iterator();
10029 else if (range
.GetStart() > GetPosition() && pt
.y
>= firstVisiblePt
.y
)
10031 optimizationLineCharPositions
.Add(range
.GetStart());
10032 optimizationLineYPositions
.Add(pt
.y
);
10036 node2
= node2
->GetNext();
10040 node
= node
->GetNext();
10046 bool wxRichTextAction::Do()
10048 m_buffer
->Modify(true);
10050 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10051 wxASSERT(container
!= NULL
);
10057 case wxRICHTEXT_INSERT
:
10059 // Store a list of line start character and y positions so we can figure out which area
10060 // we need to refresh
10061 wxArrayInt optimizationLineCharPositions
;
10062 wxArrayInt optimizationLineYPositions
;
10064 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10065 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
10068 container
->InsertFragment(GetRange().GetStart(), m_newParagraphs
);
10069 container
->UpdateRanges();
10071 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10072 // Layout() would stop prematurely at the top level.
10073 container
->InvalidateHierarchy(wxRichTextRange(wxMax(0, GetRange().GetStart()-1), GetRange().GetEnd()));
10075 long newCaretPosition
= GetPosition() + m_newParagraphs
.GetOwnRange().GetLength();
10077 // Character position to caret position
10078 newCaretPosition
--;
10080 // Don't take into account the last newline
10081 if (m_newParagraphs
.GetPartialParagraph())
10082 newCaretPosition
--;
10084 if (m_newParagraphs
.GetChildren().GetCount() > 1)
10086 wxRichTextObject
* p
= (wxRichTextObject
*) m_newParagraphs
.GetChildren().GetLast()->GetData();
10087 if (p
->GetRange().GetLength() == 1)
10088 newCaretPosition
--;
10091 newCaretPosition
= wxMin(newCaretPosition
, (container
->GetOwnRange().GetEnd()-1));
10093 UpdateAppearance(newCaretPosition
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */);
10095 wxRichTextEvent
cmdEvent(
10096 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED
,
10097 m_ctrl
? m_ctrl
->GetId() : -1);
10098 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10099 cmdEvent
.SetRange(GetRange());
10100 cmdEvent
.SetPosition(GetRange().GetStart());
10101 cmdEvent
.SetContainer(container
);
10103 m_buffer
->SendEvent(cmdEvent
);
10107 case wxRICHTEXT_DELETE
:
10109 wxArrayInt optimizationLineCharPositions
;
10110 wxArrayInt optimizationLineYPositions
;
10112 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10113 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
10116 container
->DeleteRange(GetRange());
10117 container
->UpdateRanges();
10118 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10119 // Layout() would stop prematurely at the top level.
10120 container
->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
10122 long caretPos
= GetRange().GetStart()-1;
10123 if (caretPos
>= container
->GetOwnRange().GetEnd())
10126 UpdateAppearance(caretPos
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */);
10128 wxRichTextEvent
cmdEvent(
10129 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED
,
10130 m_ctrl
? m_ctrl
->GetId() : -1);
10131 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10132 cmdEvent
.SetRange(GetRange());
10133 cmdEvent
.SetPosition(GetRange().GetStart());
10134 cmdEvent
.SetContainer(container
);
10136 m_buffer
->SendEvent(cmdEvent
);
10140 case wxRICHTEXT_CHANGE_STYLE
:
10141 case wxRICHTEXT_CHANGE_PROPERTIES
:
10143 ApplyParagraphs(GetNewParagraphs());
10145 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10146 // Layout() would stop prematurely at the top level.
10147 container
->InvalidateHierarchy(GetRange());
10149 UpdateAppearance(GetPosition());
10151 wxRichTextEvent
cmdEvent(
10152 m_cmdId
== wxRICHTEXT_CHANGE_STYLE
? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
: wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED
,
10153 m_ctrl
? m_ctrl
->GetId() : -1);
10154 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10155 cmdEvent
.SetRange(GetRange());
10156 cmdEvent
.SetPosition(GetRange().GetStart());
10157 cmdEvent
.SetContainer(container
);
10159 m_buffer
->SendEvent(cmdEvent
);
10163 case wxRICHTEXT_CHANGE_ATTRIBUTES
:
10165 wxRichTextObject
* obj
= m_objectAddress
.GetObject(m_buffer
); // container->GetChildAtPosition(GetRange().GetStart());
10168 wxRichTextAttr oldAttr
= obj
->GetAttributes();
10169 obj
->GetAttributes() = m_attributes
;
10170 m_attributes
= oldAttr
;
10173 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10174 // Layout() would stop prematurely at the top level.
10175 container
->InvalidateHierarchy(GetRange());
10177 UpdateAppearance(GetPosition());
10179 wxRichTextEvent
cmdEvent(
10180 wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
,
10181 m_ctrl
? m_ctrl
->GetId() : -1);
10182 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10183 cmdEvent
.SetRange(GetRange());
10184 cmdEvent
.SetPosition(GetRange().GetStart());
10185 cmdEvent
.SetContainer(container
);
10187 m_buffer
->SendEvent(cmdEvent
);
10191 case wxRICHTEXT_CHANGE_OBJECT
:
10193 wxRichTextObject
* obj
= m_objectAddress
.GetObject(m_buffer
);
10194 // wxRichTextObject* obj = container->GetChildAtPosition(GetRange().GetStart());
10195 if (obj
&& m_object
)
10197 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(obj
);
10200 wxRichTextObject
* obj
= node
->GetData();
10201 node
->SetData(m_object
);
10206 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10207 // Layout() would stop prematurely at the top level.
10208 container
->InvalidateHierarchy(GetRange());
10210 UpdateAppearance(GetPosition());
10212 // TODO: send new kind of modification event
10223 bool wxRichTextAction::Undo()
10225 m_buffer
->Modify(true);
10227 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10228 wxASSERT(container
!= NULL
);
10234 case wxRICHTEXT_INSERT
:
10236 wxArrayInt optimizationLineCharPositions
;
10237 wxArrayInt optimizationLineYPositions
;
10239 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10240 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
10243 container
->DeleteRange(GetRange());
10244 container
->UpdateRanges();
10246 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10247 // Layout() would stop prematurely at the top level.
10248 container
->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
10250 long newCaretPosition
= GetPosition() - 1;
10252 UpdateAppearance(newCaretPosition
, true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */);
10254 wxRichTextEvent
cmdEvent(
10255 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED
,
10256 m_ctrl
? m_ctrl
->GetId() : -1);
10257 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10258 cmdEvent
.SetRange(GetRange());
10259 cmdEvent
.SetPosition(GetRange().GetStart());
10260 cmdEvent
.SetContainer(container
);
10262 m_buffer
->SendEvent(cmdEvent
);
10266 case wxRICHTEXT_DELETE
:
10268 wxArrayInt optimizationLineCharPositions
;
10269 wxArrayInt optimizationLineYPositions
;
10271 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10272 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
10275 container
->InsertFragment(GetRange().GetStart(), m_oldParagraphs
);
10276 container
->UpdateRanges();
10278 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10279 // Layout() would stop prematurely at the top level.
10280 container
->InvalidateHierarchy(GetRange());
10282 UpdateAppearance(GetPosition(), true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */);
10284 wxRichTextEvent
cmdEvent(
10285 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED
,
10286 m_ctrl
? m_ctrl
->GetId() : -1);
10287 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10288 cmdEvent
.SetRange(GetRange());
10289 cmdEvent
.SetPosition(GetRange().GetStart());
10290 cmdEvent
.SetContainer(container
);
10292 m_buffer
->SendEvent(cmdEvent
);
10296 case wxRICHTEXT_CHANGE_STYLE
:
10297 case wxRICHTEXT_CHANGE_PROPERTIES
:
10299 ApplyParagraphs(GetOldParagraphs());
10300 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10301 // Layout() would stop prematurely at the top level.
10302 container
->InvalidateHierarchy(GetRange());
10304 UpdateAppearance(GetPosition());
10306 wxRichTextEvent
cmdEvent(
10307 m_cmdId
== wxRICHTEXT_CHANGE_STYLE
? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
: wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED
,
10308 m_ctrl
? m_ctrl
->GetId() : -1);
10309 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10310 cmdEvent
.SetRange(GetRange());
10311 cmdEvent
.SetPosition(GetRange().GetStart());
10312 cmdEvent
.SetContainer(container
);
10314 m_buffer
->SendEvent(cmdEvent
);
10318 case wxRICHTEXT_CHANGE_ATTRIBUTES
:
10319 case wxRICHTEXT_CHANGE_OBJECT
:
10330 /// Update the control appearance
10331 void wxRichTextAction::UpdateAppearance(long caretPosition
, bool sendUpdateEvent
, wxArrayInt
* optimizationLineCharPositions
, wxArrayInt
* optimizationLineYPositions
, bool isDoCmd
)
10333 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10334 wxASSERT(container
!= NULL
);
10340 m_ctrl
->SetFocusObject(container
);
10341 m_ctrl
->SetCaretPosition(caretPosition
);
10343 if (!m_ctrl
->IsFrozen())
10345 wxRect containerRect
= container
->GetRect();
10347 m_ctrl
->LayoutContent();
10349 // Refresh everything if there were floating objects or the container changed size
10350 // (we can't yet optimize in these cases, since more complex interaction with other content occurs)
10351 if (container
->GetFloatingObjectCount() > 0 || (container
->GetParent() && containerRect
!= container
->GetRect()))
10353 m_ctrl
->Refresh(false);
10357 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10358 // Find refresh rectangle if we are in a position to optimise refresh
10359 if ((m_cmdId
== wxRICHTEXT_INSERT
|| m_cmdId
== wxRICHTEXT_DELETE
) && optimizationLineCharPositions
)
10363 wxSize clientSize
= m_ctrl
->GetClientSize();
10364 wxPoint firstVisiblePt
= m_ctrl
->GetFirstVisiblePoint();
10366 // Start/end positions
10368 int lastY
= firstVisiblePt
.y
+ clientSize
.y
;
10370 bool foundEnd
= false;
10372 // position offset - how many characters were inserted
10373 int positionOffset
= GetRange().GetLength();
10375 // Determine whether this is Do or Undo, and adjust positionOffset accordingly
10376 if ((m_cmdId
== wxRICHTEXT_DELETE
&& isDoCmd
) || (m_cmdId
== wxRICHTEXT_INSERT
&& !isDoCmd
))
10377 positionOffset
= - positionOffset
;
10379 // find the first line which is being drawn at the same position as it was
10380 // before. Since we're talking about a simple insertion, we can assume
10381 // that the rest of the window does not need to be redrawn.
10383 wxRichTextParagraph
* para
= container
->GetParagraphAtPosition(GetPosition());
10384 // Since we support floating layout, we should redraw the whole para instead of just
10385 // the first line touching the invalid range.
10388 firstY
= para
->GetPosition().y
;
10391 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(para
);
10394 wxRichTextParagraph
* child
= (wxRichTextParagraph
*) node
->GetData();
10395 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
10398 wxRichTextLine
* line
= node2
->GetData();
10399 wxPoint pt
= line
->GetAbsolutePosition();
10400 wxRichTextRange range
= line
->GetAbsoluteRange();
10402 // we want to find the first line that is in the same position
10403 // as before. This will mean we're at the end of the changed text.
10405 if (pt
.y
> lastY
) // going past the end of the window, no more info
10407 node2
= wxRichTextLineList::compatibility_iterator();
10408 node
= wxRichTextObjectList::compatibility_iterator();
10410 // Detect last line in the buffer
10411 else if (!node2
->GetNext() && para
->GetRange().Contains(container
->GetOwnRange().GetEnd()))
10413 // If deleting text, make sure we refresh below as well as above
10414 if (positionOffset
>= 0)
10417 lastY
= pt
.y
+ line
->GetSize().y
;
10420 node2
= wxRichTextLineList::compatibility_iterator();
10421 node
= wxRichTextObjectList::compatibility_iterator();
10427 // search for this line being at the same position as before
10428 for (i
= 0; i
< optimizationLineCharPositions
->GetCount(); i
++)
10430 if (((*optimizationLineCharPositions
)[i
] + positionOffset
== range
.GetStart()) &&
10431 ((*optimizationLineYPositions
)[i
] == pt
.y
))
10433 // Stop, we're now the same as we were
10438 node2
= wxRichTextLineList::compatibility_iterator();
10439 node
= wxRichTextObjectList::compatibility_iterator();
10447 node2
= node2
->GetNext();
10451 node
= node
->GetNext();
10454 firstY
= wxMax(firstVisiblePt
.y
, firstY
);
10456 lastY
= firstVisiblePt
.y
+ clientSize
.y
;
10458 // Convert to device coordinates
10459 wxRect
rect(m_ctrl
->GetPhysicalPoint(wxPoint(firstVisiblePt
.x
, firstY
)), wxSize(clientSize
.x
, lastY
- firstY
));
10460 m_ctrl
->RefreshRect(rect
);
10464 m_ctrl
->Refresh(false);
10466 m_ctrl
->PositionCaret();
10468 // This causes styles to persist when doing programmatic
10469 // content creation except when Freeze/Thaw is used, so
10470 // disable this and check for the consequences.
10471 // m_ctrl->SetDefaultStyleToCursorStyle();
10473 if (sendUpdateEvent
)
10474 wxTextCtrl::SendTextUpdatedEvent(m_ctrl
);
10479 /// Replace the buffer paragraphs with the new ones.
10480 void wxRichTextAction::ApplyParagraphs(const wxRichTextParagraphLayoutBox
& fragment
)
10482 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10483 wxASSERT(container
!= NULL
);
10487 wxRichTextObjectList::compatibility_iterator node
= fragment
.GetChildren().GetFirst();
10490 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
10491 wxASSERT (para
!= NULL
);
10493 // We'll replace the existing paragraph by finding the paragraph at this position,
10494 // delete its node data, and setting a copy as the new node data.
10495 // TODO: make more efficient by simply swapping old and new paragraph objects.
10497 wxRichTextParagraph
* existingPara
= container
->GetParagraphAtPosition(para
->GetRange().GetStart());
10500 wxRichTextObjectList::compatibility_iterator bufferParaNode
= container
->GetChildren().Find(existingPara
);
10501 if (bufferParaNode
)
10503 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(*para
);
10504 newPara
->SetParent(container
);
10506 bufferParaNode
->SetData(newPara
);
10508 delete existingPara
;
10512 node
= node
->GetNext();
10519 * This stores beginning and end positions for a range of data.
10522 WX_DEFINE_OBJARRAY(wxRichTextRangeArray
);
10524 /// Limit this range to be within 'range'
10525 bool wxRichTextRange::LimitTo(const wxRichTextRange
& range
)
10527 if (m_start
< range
.m_start
)
10528 m_start
= range
.m_start
;
10530 if (m_end
> range
.m_end
)
10531 m_end
= range
.m_end
;
10537 * wxRichTextImage implementation
10538 * This object represents an image.
10541 IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage
, wxRichTextObject
)
10543 wxRichTextImage::wxRichTextImage(const wxImage
& image
, wxRichTextObject
* parent
, wxRichTextAttr
* charStyle
):
10544 wxRichTextObject(parent
)
10547 m_imageBlock
.MakeImageBlockDefaultQuality(image
, wxBITMAP_TYPE_PNG
);
10549 SetAttributes(*charStyle
);
10552 wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock
& imageBlock
, wxRichTextObject
* parent
, wxRichTextAttr
* charStyle
):
10553 wxRichTextObject(parent
)
10556 m_imageBlock
= imageBlock
;
10558 SetAttributes(*charStyle
);
10561 void wxRichTextImage::Init()
10563 m_originalImageSize
= wxSize(-1, -1);
10566 /// Create a cached image at the required size
10567 bool wxRichTextImage::LoadImageCache(wxDC
& dc
, bool resetCache
)
10569 if (!m_imageBlock
.IsOk())
10572 // If we have an original image size, use that to compute the cached bitmap size
10573 // instead of loading the image each time. This way we can avoid loading
10574 // the image so long as the new cached bitmap size hasn't changed.
10577 if (resetCache
|| m_originalImageSize
== wxSize(-1, -1))
10579 m_imageCache
= wxNullBitmap
;
10581 m_imageBlock
.Load(image
);
10585 m_originalImageSize
= wxSize(image
.GetWidth(), image
.GetHeight());
10588 int width
= m_originalImageSize
.GetWidth();
10589 int height
= m_originalImageSize
.GetHeight();
10591 int parentWidth
= 0;
10592 int parentHeight
= 0;
10595 int maxHeight
= -1;
10597 wxRichTextBuffer
* buffer
= GetBuffer();
10601 if (buffer
->GetRichTextCtrl())
10603 // Subtract borders
10604 sz
= buffer
->GetRichTextCtrl()->GetClientSize();
10606 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
10607 marginRect
= wxRect(0, 0, sz
.x
, sz
.y
);
10608 buffer
->GetBoxRects(dc
, buffer
, buffer
->GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
10610 sz
= contentRect
.GetSize();
10612 // Start with a maximum width of the control size, even if not specified by the content,
10613 // to minimize the amount of picture overlapping the right-hand side
10617 sz
= buffer
->GetCachedSize();
10618 parentWidth
= sz
.GetWidth();
10619 parentHeight
= sz
.GetHeight();
10622 if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetWidth().GetValue() > 0)
10624 if (parentWidth
> 0 && GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
10625 width
= (int) ((GetAttributes().GetTextBoxAttr().GetWidth().GetValue() * parentWidth
)/100.0);
10626 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
10627 width
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetWidth().GetValue());
10628 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
10629 width
= GetAttributes().GetTextBoxAttr().GetWidth().GetValue();
10632 // Limit to max width
10634 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() > 0)
10638 if (parentWidth
> 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
10639 mw
= (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() * parentWidth
)/100.0);
10640 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
10641 mw
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue());
10642 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
10643 mw
= GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue();
10645 // If we already have a smaller max width due to the constraints of the control size,
10646 // don't use the larger max width.
10647 if (mw
!= -1 && ((maxWidth
== -1) || (mw
< maxWidth
)))
10651 if (maxWidth
> 0 && width
> maxWidth
)
10654 // Preserve the aspect ratio
10655 if (width
!= m_originalImageSize
.GetWidth())
10656 height
= (int) (float(m_originalImageSize
.GetHeight()) * (float(width
)/float(m_originalImageSize
.GetWidth())));
10658 if (GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetHeight().GetValue() > 0)
10660 if (parentHeight
> 0 && GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
10661 height
= (int) ((GetAttributes().GetTextBoxAttr().GetHeight().GetValue() * parentHeight
)/100.0);
10662 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
10663 height
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetHeight().GetValue());
10664 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
10665 height
= GetAttributes().GetTextBoxAttr().GetHeight().GetValue();
10667 // Preserve the aspect ratio
10668 if (height
!= m_originalImageSize
.GetHeight())
10669 width
= (int) (float(m_originalImageSize
.GetWidth()) * (float(height
)/float(m_originalImageSize
.GetHeight())));
10672 // Limit to max height
10674 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() > 0)
10676 if (parentHeight
> 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
10677 maxHeight
= (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() * parentHeight
)/100.0);
10678 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
10679 maxHeight
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue());
10680 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
10681 maxHeight
= GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue();
10684 if (maxHeight
> 0 && height
> maxHeight
)
10686 height
= maxHeight
;
10688 // Preserve the aspect ratio
10689 if (height
!= m_originalImageSize
.GetHeight())
10690 width
= (int) (float(m_originalImageSize
.GetWidth()) * (float(height
)/float(m_originalImageSize
.GetHeight())));
10693 if (m_imageCache
.IsOk() && m_imageCache
.GetWidth() == width
&& m_imageCache
.GetHeight() == height
)
10695 // Do nothing, we didn't need to change the image cache
10701 m_imageBlock
.Load(image
);
10706 if (image
.GetWidth() == width
&& image
.GetHeight() == height
)
10707 m_imageCache
= wxBitmap(image
);
10710 // If the original width and height is small, e.g. 400 or below,
10711 // scale up and then down to improve image quality. This can make
10712 // a big difference, with not much performance hit.
10713 int upscaleThreshold
= 400;
10715 if (image
.GetWidth() <= upscaleThreshold
|| image
.GetHeight() <= upscaleThreshold
)
10717 img
= image
.Scale(image
.GetWidth()*2, image
.GetHeight()*2);
10718 img
.Rescale(width
, height
, wxIMAGE_QUALITY_HIGH
);
10721 img
= image
.Scale(width
, height
, wxIMAGE_QUALITY_HIGH
);
10722 m_imageCache
= wxBitmap(img
);
10726 return m_imageCache
.IsOk();
10730 bool wxRichTextImage::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& WXUNUSED(range
), const wxRichTextSelection
& selection
, const wxRect
& rect
, int WXUNUSED(descent
), int WXUNUSED(style
))
10735 // Don't need cached size AFAIK
10736 // wxSize size = GetCachedSize();
10737 if (!LoadImageCache(dc
))
10740 wxRichTextAttr
attr(GetAttributes());
10741 context
.ApplyVirtualAttributes(attr
, this);
10743 DrawBoxAttributes(dc
, GetBuffer(), attr
, wxRect(rect
.GetPosition(), GetCachedSize()));
10745 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
10746 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
10747 marginRect
= rect
; // outer rectangle, will calculate contentRect
10748 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
10750 dc
.DrawBitmap(m_imageCache
, contentRect
.x
, contentRect
.y
, true);
10752 if (selection
.WithinSelection(GetRange().GetStart(), this))
10754 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
10755 wxCheckSetPen(dc
, *wxBLACK_PEN
);
10756 dc
.SetLogicalFunction(wxINVERT
);
10757 dc
.DrawRectangle(contentRect
);
10758 dc
.SetLogicalFunction(wxCOPY
);
10764 /// Lay the item out
10765 bool wxRichTextImage::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& WXUNUSED(parentRect
), int WXUNUSED(style
))
10767 if (!LoadImageCache(dc
))
10770 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
10771 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
10772 contentRect
= wxRect(wxPoint(0,0), imageSize
);
10774 wxRichTextAttr
attr(GetAttributes());
10775 context
.ApplyVirtualAttributes(attr
, this);
10777 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
10779 wxSize overallSize
= marginRect
.GetSize();
10781 SetCachedSize(overallSize
);
10782 SetMaxSize(overallSize
);
10783 SetMinSize(overallSize
);
10784 SetPosition(rect
.GetPosition());
10789 /// Get/set the object size for the given range. Returns false if the range
10790 /// is invalid for this object.
10791 bool wxRichTextImage::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& WXUNUSED(descent
), wxDC
& dc
, wxRichTextDrawingContext
& context
, int WXUNUSED(flags
), wxPoint
WXUNUSED(position
), wxArrayInt
* partialExtents
) const
10793 if (!range
.IsWithin(GetRange()))
10796 if (!((wxRichTextImage
*)this)->LoadImageCache(dc
))
10798 size
.x
= 0; size
.y
= 0;
10799 if (partialExtents
)
10800 partialExtents
->Add(0);
10804 wxRichTextAttr
attr(GetAttributes());
10805 context
.ApplyVirtualAttributes(attr
, (wxRichTextObject
*) this);
10807 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
10808 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
10809 contentRect
= wxRect(wxPoint(0,0), imageSize
);
10810 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
10812 wxSize overallSize
= marginRect
.GetSize();
10814 if (partialExtents
)
10815 partialExtents
->Add(overallSize
.x
);
10817 size
= overallSize
;
10822 // Get the 'natural' size for an object. For an image, it would be the
10824 wxTextAttrSize
wxRichTextImage::GetNaturalSize() const
10826 wxTextAttrSize size
;
10827 if (GetImageCache().IsOk())
10829 size
.SetWidth(GetImageCache().GetWidth(), wxTEXT_ATTR_UNITS_PIXELS
);
10830 size
.SetHeight(GetImageCache().GetHeight(), wxTEXT_ATTR_UNITS_PIXELS
);
10837 void wxRichTextImage::Copy(const wxRichTextImage
& obj
)
10839 wxRichTextObject::Copy(obj
);
10841 m_imageBlock
= obj
.m_imageBlock
;
10842 m_originalImageSize
= obj
.m_originalImageSize
;
10845 /// Edit properties via a GUI
10846 bool wxRichTextImage::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
10848 wxRichTextObjectPropertiesDialog
imageDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Picture Properties"));
10849 imageDlg
.SetAttributes(GetAttributes());
10851 if (imageDlg
.ShowModal() == wxID_OK
)
10853 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
10854 // indeterminate in the object.
10855 imageDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
10867 /// Compare two attribute objects
10868 bool wxTextAttrEq(const wxRichTextAttr
& attr1
, const wxRichTextAttr
& attr2
)
10870 return (attr1
== attr2
);
10873 // Partial equality test taking flags into account
10874 bool wxTextAttrEqPartial(const wxRichTextAttr
& attr1
, const wxRichTextAttr
& attr2
)
10876 return attr1
.EqPartial(attr2
);
10880 bool wxRichTextTabsEq(const wxArrayInt
& tabs1
, const wxArrayInt
& tabs2
)
10882 if (tabs1
.GetCount() != tabs2
.GetCount())
10886 for (i
= 0; i
< tabs1
.GetCount(); i
++)
10888 if (tabs1
[i
] != tabs2
[i
])
10894 bool wxRichTextApplyStyle(wxRichTextAttr
& destStyle
, const wxRichTextAttr
& style
, wxRichTextAttr
* compareWith
)
10896 return destStyle
.Apply(style
, compareWith
);
10899 // Remove attributes
10900 bool wxRichTextRemoveStyle(wxRichTextAttr
& destStyle
, const wxRichTextAttr
& style
)
10902 return destStyle
.RemoveStyle(style
);
10905 /// Combine two bitlists, specifying the bits of interest with separate flags.
10906 bool wxRichTextCombineBitlists(int& valueA
, int valueB
, int& flagsA
, int flagsB
)
10908 return wxRichTextAttr::CombineBitlists(valueA
, valueB
, flagsA
, flagsB
);
10911 /// Compare two bitlists
10912 bool wxRichTextBitlistsEqPartial(int valueA
, int valueB
, int flags
)
10914 return wxRichTextAttr::BitlistsEqPartial(valueA
, valueB
, flags
);
10917 /// Split into paragraph and character styles
10918 bool wxRichTextSplitParaCharStyles(const wxRichTextAttr
& style
, wxRichTextAttr
& parStyle
, wxRichTextAttr
& charStyle
)
10920 return wxRichTextAttr::SplitParaCharStyles(style
, parStyle
, charStyle
);
10923 /// Convert a decimal to Roman numerals
10924 wxString
wxRichTextDecimalToRoman(long n
)
10926 static wxArrayInt decimalNumbers
;
10927 static wxArrayString romanNumbers
;
10932 decimalNumbers
.Clear();
10933 romanNumbers
.Clear();
10934 return wxEmptyString
;
10937 if (decimalNumbers
.GetCount() == 0)
10939 #define wxRichTextAddDecRom(n, r) decimalNumbers.Add(n); romanNumbers.Add(r);
10941 wxRichTextAddDecRom(1000, wxT("M"));
10942 wxRichTextAddDecRom(900, wxT("CM"));
10943 wxRichTextAddDecRom(500, wxT("D"));
10944 wxRichTextAddDecRom(400, wxT("CD"));
10945 wxRichTextAddDecRom(100, wxT("C"));
10946 wxRichTextAddDecRom(90, wxT("XC"));
10947 wxRichTextAddDecRom(50, wxT("L"));
10948 wxRichTextAddDecRom(40, wxT("XL"));
10949 wxRichTextAddDecRom(10, wxT("X"));
10950 wxRichTextAddDecRom(9, wxT("IX"));
10951 wxRichTextAddDecRom(5, wxT("V"));
10952 wxRichTextAddDecRom(4, wxT("IV"));
10953 wxRichTextAddDecRom(1, wxT("I"));
10959 while (n
> 0 && i
< 13)
10961 if (n
>= decimalNumbers
[i
])
10963 n
-= decimalNumbers
[i
];
10964 roman
+= romanNumbers
[i
];
10971 if (roman
.IsEmpty())
10977 * wxRichTextFileHandler
10978 * Base class for file handlers
10981 IMPLEMENT_CLASS(wxRichTextFileHandler
, wxObject
)
10983 #if wxUSE_FFILE && wxUSE_STREAMS
10984 bool wxRichTextFileHandler::LoadFile(wxRichTextBuffer
*buffer
, const wxString
& filename
)
10986 wxFFileInputStream
stream(filename
);
10988 return LoadFile(buffer
, stream
);
10993 bool wxRichTextFileHandler::SaveFile(wxRichTextBuffer
*buffer
, const wxString
& filename
)
10995 wxFFileOutputStream
stream(filename
);
10997 return SaveFile(buffer
, stream
);
11001 #endif // wxUSE_FFILE && wxUSE_STREAMS
11003 /// Can we handle this filename (if using files)? By default, checks the extension.
11004 bool wxRichTextFileHandler::CanHandle(const wxString
& filename
) const
11006 wxString path
, file
, ext
;
11007 wxFileName::SplitPath(filename
, & path
, & file
, & ext
);
11009 return (ext
.Lower() == GetExtension());
11013 * wxRichTextTextHandler
11014 * Plain text handler
11017 IMPLEMENT_CLASS(wxRichTextPlainTextHandler
, wxRichTextFileHandler
)
11020 bool wxRichTextPlainTextHandler::DoLoadFile(wxRichTextBuffer
*buffer
, wxInputStream
& stream
)
11022 if (!stream
.IsOk())
11028 while (!stream
.Eof())
11030 int ch
= stream
.GetC();
11034 if (ch
== 10 && lastCh
!= 13)
11037 if (ch
> 0 && ch
!= 10)
11044 buffer
->ResetAndClearCommands();
11046 buffer
->AddParagraphs(str
);
11047 buffer
->UpdateRanges();
11052 bool wxRichTextPlainTextHandler::DoSaveFile(wxRichTextBuffer
*buffer
, wxOutputStream
& stream
)
11054 if (!stream
.IsOk())
11057 wxString text
= buffer
->GetText();
11059 wxString newLine
= wxRichTextLineBreakChar
;
11060 text
.Replace(newLine
, wxT("\n"));
11062 wxCharBuffer buf
= text
.ToAscii();
11064 stream
.Write((const char*) buf
, text
.length());
11067 #endif // wxUSE_STREAMS
11070 * Stores information about an image, in binary in-memory form
11073 wxRichTextImageBlock::wxRichTextImageBlock()
11078 wxRichTextImageBlock::wxRichTextImageBlock(const wxRichTextImageBlock
& block
):wxObject()
11084 wxRichTextImageBlock::~wxRichTextImageBlock()
11089 void wxRichTextImageBlock::Init()
11093 m_imageType
= wxBITMAP_TYPE_INVALID
;
11096 void wxRichTextImageBlock::Clear()
11100 m_imageType
= wxBITMAP_TYPE_INVALID
;
11104 // Load the original image into a memory block.
11105 // If the image is not a JPEG, we must convert it into a JPEG
11106 // to conserve space.
11107 // If it's not a JPEG we can make use of 'image', already scaled, so we don't have to
11108 // load the image a 2nd time.
11110 bool wxRichTextImageBlock::MakeImageBlock(const wxString
& filename
, wxBitmapType imageType
,
11111 wxImage
& image
, bool convertToJPEG
)
11113 m_imageType
= imageType
;
11115 wxString
filenameToRead(filename
);
11116 bool removeFile
= false;
11118 if (imageType
== wxBITMAP_TYPE_INVALID
)
11119 return false; // Could not determine image type
11121 if ((imageType
!= wxBITMAP_TYPE_JPEG
) && convertToJPEG
)
11123 wxString tempFile
=
11124 wxFileName::CreateTempFileName(_("image"));
11126 wxASSERT(!tempFile
.IsEmpty());
11128 image
.SaveFile(tempFile
, wxBITMAP_TYPE_JPEG
);
11129 filenameToRead
= tempFile
;
11132 m_imageType
= wxBITMAP_TYPE_JPEG
;
11135 if (!file
.Open(filenameToRead
))
11138 m_dataSize
= (size_t) file
.Length();
11143 m_data
= ReadBlock(filenameToRead
, m_dataSize
);
11146 wxRemoveFile(filenameToRead
);
11148 return (m_data
!= NULL
);
11151 // Make an image block from the wxImage in the given
11153 bool wxRichTextImageBlock::MakeImageBlock(wxImage
& image
, wxBitmapType imageType
, int quality
)
11155 image
.SetOption(wxT("quality"), quality
);
11157 if (imageType
== wxBITMAP_TYPE_INVALID
)
11158 return false; // Could not determine image type
11160 return DoMakeImageBlock(image
, imageType
);
11163 // Uses a const wxImage for efficiency, but can't set quality (only relevant for JPEG)
11164 bool wxRichTextImageBlock::MakeImageBlockDefaultQuality(const wxImage
& image
, wxBitmapType imageType
)
11166 if (imageType
== wxBITMAP_TYPE_INVALID
)
11167 return false; // Could not determine image type
11169 return DoMakeImageBlock(image
, imageType
);
11172 // Makes the image block
11173 bool wxRichTextImageBlock::DoMakeImageBlock(const wxImage
& image
, wxBitmapType imageType
)
11175 wxMemoryOutputStream memStream
;
11176 if (!image
.SaveFile(memStream
, imageType
))
11181 unsigned char* block
= new unsigned char[memStream
.GetSize()];
11189 m_imageType
= imageType
;
11190 m_dataSize
= memStream
.GetSize();
11192 memStream
.CopyTo(m_data
, m_dataSize
);
11194 return (m_data
!= NULL
);
11198 bool wxRichTextImageBlock::Write(const wxString
& filename
)
11200 return WriteBlock(filename
, m_data
, m_dataSize
);
11203 void wxRichTextImageBlock::Copy(const wxRichTextImageBlock
& block
)
11205 m_imageType
= block
.m_imageType
;
11207 m_dataSize
= block
.m_dataSize
;
11208 if (m_dataSize
== 0)
11211 m_data
= new unsigned char[m_dataSize
];
11213 for (i
= 0; i
< m_dataSize
; i
++)
11214 m_data
[i
] = block
.m_data
[i
];
11218 void wxRichTextImageBlock::operator=(const wxRichTextImageBlock
& block
)
11223 // Load a wxImage from the block
11224 bool wxRichTextImageBlock::Load(wxImage
& image
)
11229 // Read in the image.
11231 wxMemoryInputStream
mstream(m_data
, m_dataSize
);
11232 bool success
= image
.LoadFile(mstream
, GetImageType());
11234 wxString tempFile
= wxFileName::CreateTempFileName(_("image"));
11235 wxASSERT(!tempFile
.IsEmpty());
11237 if (!WriteBlock(tempFile
, m_data
, m_dataSize
))
11241 success
= image
.LoadFile(tempFile
, GetImageType());
11242 wxRemoveFile(tempFile
);
11248 // Write data in hex to a stream
11249 bool wxRichTextImageBlock::WriteHex(wxOutputStream
& stream
)
11251 if (m_dataSize
== 0)
11254 int bufSize
= 100000;
11255 if (int(2*m_dataSize
) < bufSize
)
11256 bufSize
= 2*m_dataSize
;
11257 char* buf
= new char[bufSize
+1];
11259 int left
= m_dataSize
;
11264 if (left
*2 > bufSize
)
11266 n
= bufSize
; left
-= (bufSize
/2);
11270 n
= left
*2; left
= 0;
11274 for (i
= 0; i
< (n
/2); i
++)
11276 wxDecToHex(m_data
[j
], b
, b
+1);
11281 stream
.Write((const char*) buf
, n
);
11287 // Read data in hex from a stream
11288 bool wxRichTextImageBlock::ReadHex(wxInputStream
& stream
, int length
, wxBitmapType imageType
)
11290 int dataSize
= length
/2;
11295 // create a null terminated temporary string:
11299 m_data
= new unsigned char[dataSize
];
11301 for (i
= 0; i
< dataSize
; i
++)
11303 str
[0] = (char)stream
.GetC();
11304 str
[1] = (char)stream
.GetC();
11306 m_data
[i
] = (unsigned char)wxHexToDec(str
);
11309 m_dataSize
= dataSize
;
11310 m_imageType
= imageType
;
11315 // Allocate and read from stream as a block of memory
11316 unsigned char* wxRichTextImageBlock::ReadBlock(wxInputStream
& stream
, size_t size
)
11318 unsigned char* block
= new unsigned char[size
];
11322 stream
.Read(block
, size
);
11327 unsigned char* wxRichTextImageBlock::ReadBlock(const wxString
& filename
, size_t size
)
11329 wxFileInputStream
stream(filename
);
11330 if (!stream
.IsOk())
11333 return ReadBlock(stream
, size
);
11336 // Write memory block to stream
11337 bool wxRichTextImageBlock::WriteBlock(wxOutputStream
& stream
, unsigned char* block
, size_t size
)
11339 stream
.Write((void*) block
, size
);
11340 return stream
.IsOk();
11344 // Write memory block to file
11345 bool wxRichTextImageBlock::WriteBlock(const wxString
& filename
, unsigned char* block
, size_t size
)
11347 wxFileOutputStream
outStream(filename
);
11348 if (!outStream
.IsOk())
11351 return WriteBlock(outStream
, block
, size
);
11354 // Gets the extension for the block's type
11355 wxString
wxRichTextImageBlock::GetExtension() const
11357 wxImageHandler
* handler
= wxImage::FindHandler(GetImageType());
11359 return handler
->GetExtension();
11361 return wxEmptyString
;
11367 * The data object for a wxRichTextBuffer
11370 const wxChar
*wxRichTextBufferDataObject::ms_richTextBufferFormatId
= wxT("wxShape");
11372 wxRichTextBufferDataObject::wxRichTextBufferDataObject(wxRichTextBuffer
* richTextBuffer
)
11374 m_richTextBuffer
= richTextBuffer
;
11376 // this string should uniquely identify our format, but is otherwise
11378 m_formatRichTextBuffer
.SetId(GetRichTextBufferFormatId());
11380 SetFormat(m_formatRichTextBuffer
);
11383 wxRichTextBufferDataObject::~wxRichTextBufferDataObject()
11385 delete m_richTextBuffer
;
11388 // after a call to this function, the richTextBuffer is owned by the caller and it
11389 // is responsible for deleting it!
11390 wxRichTextBuffer
* wxRichTextBufferDataObject::GetRichTextBuffer()
11392 wxRichTextBuffer
* richTextBuffer
= m_richTextBuffer
;
11393 m_richTextBuffer
= NULL
;
11395 return richTextBuffer
;
11398 wxDataFormat
wxRichTextBufferDataObject::GetPreferredFormat(Direction
WXUNUSED(dir
)) const
11400 return m_formatRichTextBuffer
;
11403 size_t wxRichTextBufferDataObject::GetDataSize() const
11405 if (!m_richTextBuffer
)
11411 wxStringOutputStream
stream(& bufXML
);
11412 if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
))
11414 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
11420 wxCharBuffer buffer
= bufXML
.mb_str(wxConvUTF8
);
11421 return strlen(buffer
) + 1;
11423 return bufXML
.Length()+1;
11427 bool wxRichTextBufferDataObject::GetDataHere(void *pBuf
) const
11429 if (!pBuf
|| !m_richTextBuffer
)
11435 wxStringOutputStream
stream(& bufXML
);
11436 if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
))
11438 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
11444 wxCharBuffer buffer
= bufXML
.mb_str(wxConvUTF8
);
11445 size_t len
= strlen(buffer
);
11446 memcpy((char*) pBuf
, (const char*) buffer
, len
);
11447 ((char*) pBuf
)[len
] = 0;
11449 size_t len
= bufXML
.Length();
11450 memcpy((char*) pBuf
, (const char*) bufXML
.c_str(), len
);
11451 ((char*) pBuf
)[len
] = 0;
11457 bool wxRichTextBufferDataObject::SetData(size_t WXUNUSED(len
), const void *buf
)
11459 wxDELETE(m_richTextBuffer
);
11461 wxString
bufXML((const char*) buf
, wxConvUTF8
);
11463 m_richTextBuffer
= new wxRichTextBuffer
;
11465 wxStringInputStream
stream(bufXML
);
11466 if (!m_richTextBuffer
->LoadFile(stream
, wxRICHTEXT_TYPE_XML
))
11468 wxLogError(wxT("Could not read the buffer from an XML stream.\nYou may have forgotten to add the XML file handler."));
11470 wxDELETE(m_richTextBuffer
);
11482 * wxRichTextFontTable
11483 * Manages quick access to a pool of fonts for rendering rich text
11486 WX_DECLARE_STRING_HASH_MAP_WITH_DECL(wxFont
, wxRichTextFontTableHashMap
, class WXDLLIMPEXP_RICHTEXT
);
11488 class wxRichTextFontTableData
: public wxObjectRefData
11491 wxRichTextFontTableData() {}
11493 wxFont
FindFont(const wxRichTextAttr
& fontSpec
);
11495 wxRichTextFontTableHashMap m_hashMap
;
11498 wxFont
wxRichTextFontTableData::FindFont(const wxRichTextAttr
& fontSpec
)
11500 wxString
facename(fontSpec
.GetFontFaceName());
11501 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()));
11502 wxRichTextFontTableHashMap::iterator entry
= m_hashMap
.find(spec
);
11504 if ( entry
== m_hashMap
.end() )
11506 wxFont
font(fontSpec
.GetFontSize(), wxDEFAULT
, fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), fontSpec
.GetFontUnderlined(), facename
.c_str());
11507 m_hashMap
[spec
] = font
;
11512 return entry
->second
;
11516 IMPLEMENT_DYNAMIC_CLASS(wxRichTextFontTable
, wxObject
)
11518 wxRichTextFontTable::wxRichTextFontTable()
11520 m_refData
= new wxRichTextFontTableData
;
11523 wxRichTextFontTable::wxRichTextFontTable(const wxRichTextFontTable
& table
)
11529 wxRichTextFontTable::~wxRichTextFontTable()
11534 bool wxRichTextFontTable::operator == (const wxRichTextFontTable
& table
) const
11536 return (m_refData
== table
.m_refData
);
11539 void wxRichTextFontTable::operator= (const wxRichTextFontTable
& table
)
11544 wxFont
wxRichTextFontTable::FindFont(const wxRichTextAttr
& fontSpec
)
11546 wxRichTextFontTableData
* data
= (wxRichTextFontTableData
*) m_refData
;
11548 return data
->FindFont(fontSpec
);
11553 void wxRichTextFontTable::Clear()
11555 wxRichTextFontTableData
* data
= (wxRichTextFontTableData
*) m_refData
;
11557 data
->m_hashMap
.clear();
11562 void wxTextBoxAttr::Reset()
11565 m_floatMode
= wxTEXT_BOX_ATTR_FLOAT_NONE
;
11566 m_clearMode
= wxTEXT_BOX_ATTR_CLEAR_NONE
;
11567 m_collapseMode
= wxTEXT_BOX_ATTR_COLLAPSE_NONE
;
11568 m_verticalAlignment
= wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_NONE
;
11569 m_boxStyleName
= wxEmptyString
;
11573 m_position
.Reset();
11584 bool wxTextBoxAttr::operator== (const wxTextBoxAttr
& attr
) const
11587 m_flags
== attr
.m_flags
&&
11588 m_floatMode
== attr
.m_floatMode
&&
11589 m_clearMode
== attr
.m_clearMode
&&
11590 m_collapseMode
== attr
.m_collapseMode
&&
11591 m_verticalAlignment
== attr
.m_verticalAlignment
&&
11593 m_margins
== attr
.m_margins
&&
11594 m_padding
== attr
.m_padding
&&
11595 m_position
== attr
.m_position
&&
11597 m_size
== attr
.m_size
&&
11598 m_minSize
== attr
.m_minSize
&&
11599 m_maxSize
== attr
.m_maxSize
&&
11601 m_border
== attr
.m_border
&&
11602 m_outline
== attr
.m_outline
&&
11604 m_boxStyleName
== attr
.m_boxStyleName
11608 // Partial equality test
11609 bool wxTextBoxAttr::EqPartial(const wxTextBoxAttr
& attr
) const
11611 if (attr
.HasFloatMode() && HasFloatMode() && (GetFloatMode() != attr
.GetFloatMode()))
11614 if (attr
.HasClearMode() && HasClearMode() && (GetClearMode() != attr
.GetClearMode()))
11617 if (attr
.HasCollapseBorders() && HasCollapseBorders() && (attr
.GetCollapseBorders() != GetCollapseBorders()))
11620 if (attr
.HasVerticalAlignment() && HasVerticalAlignment() && (attr
.GetVerticalAlignment() != GetVerticalAlignment()))
11623 if (attr
.HasBoxStyleName() && HasBoxStyleName() && (attr
.GetBoxStyleName() != GetBoxStyleName()))
11628 if (!m_position
.EqPartial(attr
.m_position
))
11633 if (!m_size
.EqPartial(attr
.m_size
))
11635 if (!m_minSize
.EqPartial(attr
.m_minSize
))
11637 if (!m_maxSize
.EqPartial(attr
.m_maxSize
))
11642 if (!m_margins
.EqPartial(attr
.m_margins
))
11647 if (!m_padding
.EqPartial(attr
.m_padding
))
11652 if (!GetBorder().EqPartial(attr
.GetBorder()))
11657 if (!GetOutline().EqPartial(attr
.GetOutline()))
11663 // Merges the given attributes. If compareWith
11664 // is non-NULL, then it will be used to mask out those attributes that are the same in style
11665 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
11666 bool wxTextBoxAttr::Apply(const wxTextBoxAttr
& attr
, const wxTextBoxAttr
* compareWith
)
11668 if (attr
.HasFloatMode())
11670 if (!(compareWith
&& compareWith
->HasFloatMode() && compareWith
->GetFloatMode() == attr
.GetFloatMode()))
11671 SetFloatMode(attr
.GetFloatMode());
11674 if (attr
.HasClearMode())
11676 if (!(compareWith
&& compareWith
->HasClearMode() && compareWith
->GetClearMode() == attr
.GetClearMode()))
11677 SetClearMode(attr
.GetClearMode());
11680 if (attr
.HasCollapseBorders())
11682 if (!(compareWith
&& compareWith
->HasCollapseBorders() && compareWith
->GetCollapseBorders() == attr
.GetCollapseBorders()))
11683 SetCollapseBorders(attr
.GetCollapseBorders());
11686 if (attr
.HasVerticalAlignment())
11688 if (!(compareWith
&& compareWith
->HasVerticalAlignment() && compareWith
->GetVerticalAlignment() == attr
.GetVerticalAlignment()))
11689 SetVerticalAlignment(attr
.GetVerticalAlignment());
11692 if (attr
.HasBoxStyleName())
11694 if (!(compareWith
&& compareWith
->HasBoxStyleName() && compareWith
->GetBoxStyleName() == attr
.GetBoxStyleName()))
11695 SetBoxStyleName(attr
.GetBoxStyleName());
11698 m_margins
.Apply(attr
.m_margins
, compareWith
? (& attr
.m_margins
) : (const wxTextAttrDimensions
*) NULL
);
11699 m_padding
.Apply(attr
.m_padding
, compareWith
? (& attr
.m_padding
) : (const wxTextAttrDimensions
*) NULL
);
11700 m_position
.Apply(attr
.m_position
, compareWith
? (& attr
.m_position
) : (const wxTextAttrDimensions
*) NULL
);
11702 m_size
.Apply(attr
.m_size
, compareWith
? (& attr
.m_size
) : (const wxTextAttrSize
*) NULL
);
11703 m_minSize
.Apply(attr
.m_minSize
, compareWith
? (& attr
.m_minSize
) : (const wxTextAttrSize
*) NULL
);
11704 m_maxSize
.Apply(attr
.m_maxSize
, compareWith
? (& attr
.m_maxSize
) : (const wxTextAttrSize
*) NULL
);
11706 m_border
.Apply(attr
.m_border
, compareWith
? (& attr
.m_border
) : (const wxTextAttrBorders
*) NULL
);
11707 m_outline
.Apply(attr
.m_outline
, compareWith
? (& attr
.m_outline
) : (const wxTextAttrBorders
*) NULL
);
11712 // Remove specified attributes from this object
11713 bool wxTextBoxAttr::RemoveStyle(const wxTextBoxAttr
& attr
)
11715 if (attr
.HasFloatMode())
11716 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT
);
11718 if (attr
.HasClearMode())
11719 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR
);
11721 if (attr
.HasCollapseBorders())
11722 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
11724 if (attr
.HasVerticalAlignment())
11725 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
11727 if (attr
.HasBoxStyleName())
11729 SetBoxStyleName(wxEmptyString
);
11730 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
11733 m_margins
.RemoveStyle(attr
.m_margins
);
11734 m_padding
.RemoveStyle(attr
.m_padding
);
11735 m_position
.RemoveStyle(attr
.m_position
);
11737 m_size
.RemoveStyle(attr
.m_size
);
11738 m_minSize
.RemoveStyle(attr
.m_minSize
);
11739 m_maxSize
.RemoveStyle(attr
.m_maxSize
);
11741 m_border
.RemoveStyle(attr
.m_border
);
11742 m_outline
.RemoveStyle(attr
.m_outline
);
11747 // Collects the attributes that are common to a range of content, building up a note of
11748 // which attributes are absent in some objects and which clash in some objects.
11749 void wxTextBoxAttr::CollectCommonAttributes(const wxTextBoxAttr
& attr
, wxTextBoxAttr
& clashingAttr
, wxTextBoxAttr
& absentAttr
)
11751 if (attr
.HasFloatMode())
11753 if (!clashingAttr
.HasFloatMode() && !absentAttr
.HasFloatMode())
11755 if (HasFloatMode())
11757 if (GetFloatMode() != attr
.GetFloatMode())
11759 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_FLOAT
);
11760 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT
);
11764 SetFloatMode(attr
.GetFloatMode());
11768 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_FLOAT
);
11770 if (attr
.HasClearMode())
11772 if (!clashingAttr
.HasClearMode() && !absentAttr
.HasClearMode())
11774 if (HasClearMode())
11776 if (GetClearMode() != attr
.GetClearMode())
11778 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_CLEAR
);
11779 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR
);
11783 SetClearMode(attr
.GetClearMode());
11787 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_CLEAR
);
11789 if (attr
.HasCollapseBorders())
11791 if (!clashingAttr
.HasCollapseBorders() && !absentAttr
.HasCollapseBorders())
11793 if (HasCollapseBorders())
11795 if (GetCollapseBorders() != attr
.GetCollapseBorders())
11797 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
11798 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
11802 SetCollapseBorders(attr
.GetCollapseBorders());
11806 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
11808 if (attr
.HasVerticalAlignment())
11810 if (!clashingAttr
.HasVerticalAlignment() && !absentAttr
.HasVerticalAlignment())
11812 if (HasVerticalAlignment())
11814 if (GetVerticalAlignment() != attr
.GetVerticalAlignment())
11816 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
11817 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
11821 SetVerticalAlignment(attr
.GetVerticalAlignment());
11825 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
11827 if (attr
.HasBoxStyleName())
11829 if (!clashingAttr
.HasBoxStyleName() && !absentAttr
.HasBoxStyleName())
11831 if (HasBoxStyleName())
11833 if (GetBoxStyleName() != attr
.GetBoxStyleName())
11835 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
11836 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
11840 SetBoxStyleName(attr
.GetBoxStyleName());
11844 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
11846 m_margins
.CollectCommonAttributes(attr
.m_margins
, clashingAttr
.m_margins
, absentAttr
.m_margins
);
11847 m_padding
.CollectCommonAttributes(attr
.m_padding
, clashingAttr
.m_padding
, absentAttr
.m_padding
);
11848 m_position
.CollectCommonAttributes(attr
.m_position
, clashingAttr
.m_position
, absentAttr
.m_position
);
11850 m_size
.CollectCommonAttributes(attr
.m_size
, clashingAttr
.m_size
, absentAttr
.m_size
);
11851 m_minSize
.CollectCommonAttributes(attr
.m_minSize
, clashingAttr
.m_minSize
, absentAttr
.m_minSize
);
11852 m_maxSize
.CollectCommonAttributes(attr
.m_maxSize
, clashingAttr
.m_maxSize
, absentAttr
.m_maxSize
);
11854 m_border
.CollectCommonAttributes(attr
.m_border
, clashingAttr
.m_border
, absentAttr
.m_border
);
11855 m_outline
.CollectCommonAttributes(attr
.m_outline
, clashingAttr
.m_outline
, absentAttr
.m_outline
);
11858 bool wxTextBoxAttr::IsDefault() const
11860 return GetFlags() == 0 && !m_border
.IsValid() && !m_outline
.IsValid() &&
11861 !m_size
.IsValid() && !m_minSize
.IsValid() && !m_maxSize
.IsValid() &&
11862 !m_position
.IsValid() && !m_padding
.IsValid() && !m_margins
.IsValid();
11867 void wxRichTextAttr::Copy(const wxRichTextAttr
& attr
)
11869 wxTextAttr::Copy(attr
);
11871 m_textBoxAttr
= attr
.m_textBoxAttr
;
11874 bool wxRichTextAttr::operator==(const wxRichTextAttr
& attr
) const
11876 if (!(wxTextAttr::operator==(attr
)))
11879 return (m_textBoxAttr
== attr
.m_textBoxAttr
);
11882 // Partial equality test taking comparison object into account
11883 bool wxRichTextAttr::EqPartial(const wxRichTextAttr
& attr
) const
11885 if (!(wxTextAttr::EqPartial(attr
)))
11888 return m_textBoxAttr
.EqPartial(attr
.m_textBoxAttr
);
11891 // Merges the given attributes. If compareWith
11892 // is non-NULL, then it will be used to mask out those attributes that are the same in style
11893 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
11894 bool wxRichTextAttr::Apply(const wxRichTextAttr
& style
, const wxRichTextAttr
* compareWith
)
11896 wxTextAttr::Apply(style
, compareWith
);
11898 return m_textBoxAttr
.Apply(style
.m_textBoxAttr
, compareWith
? (& compareWith
->m_textBoxAttr
) : (const wxTextBoxAttr
*) NULL
);
11901 // Remove specified attributes from this object
11902 bool wxRichTextAttr::RemoveStyle(const wxRichTextAttr
& attr
)
11904 wxTextAttr::RemoveStyle(*this, attr
);
11906 return m_textBoxAttr
.RemoveStyle(attr
.m_textBoxAttr
);
11909 // Collects the attributes that are common to a range of content, building up a note of
11910 // which attributes are absent in some objects and which clash in some objects.
11911 void wxRichTextAttr::CollectCommonAttributes(const wxRichTextAttr
& attr
, wxRichTextAttr
& clashingAttr
, wxRichTextAttr
& absentAttr
)
11913 wxTextAttrCollectCommonAttributes(*this, attr
, clashingAttr
, absentAttr
);
11915 m_textBoxAttr
.CollectCommonAttributes(attr
.m_textBoxAttr
, clashingAttr
.m_textBoxAttr
, absentAttr
.m_textBoxAttr
);
11918 // Partial equality test
11919 bool wxTextAttrBorder::EqPartial(const wxTextAttrBorder
& border
) const
11921 if (border
.HasStyle() && !HasStyle() && (border
.GetStyle() != GetStyle()))
11924 if (border
.HasColour() && !HasColour() && (border
.GetColourLong() != GetColourLong()))
11927 if (border
.HasWidth() && !HasWidth() && !(border
.GetWidth() == GetWidth()))
11933 // Apply border to 'this', but not if the same as compareWith
11934 bool wxTextAttrBorder::Apply(const wxTextAttrBorder
& border
, const wxTextAttrBorder
* compareWith
)
11936 if (border
.HasStyle())
11938 if (!(compareWith
&& (border
.GetStyle() == compareWith
->GetStyle())))
11939 SetStyle(border
.GetStyle());
11941 if (border
.HasColour())
11943 if (!(compareWith
&& (border
.GetColourLong() == compareWith
->GetColourLong())))
11944 SetColour(border
.GetColourLong());
11946 if (border
.HasWidth())
11948 if (!(compareWith
&& (border
.GetWidth() == compareWith
->GetWidth())))
11949 SetWidth(border
.GetWidth());
11955 // Remove specified attributes from this object
11956 bool wxTextAttrBorder::RemoveStyle(const wxTextAttrBorder
& attr
)
11958 if (attr
.HasStyle() && HasStyle())
11959 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_STYLE
);
11960 if (attr
.HasColour() && HasColour())
11961 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_COLOUR
);
11962 if (attr
.HasWidth() && HasWidth())
11963 m_borderWidth
.Reset();
11968 // Collects the attributes that are common to a range of content, building up a note of
11969 // which attributes are absent in some objects and which clash in some objects.
11970 void wxTextAttrBorder::CollectCommonAttributes(const wxTextAttrBorder
& attr
, wxTextAttrBorder
& clashingAttr
, wxTextAttrBorder
& absentAttr
)
11972 if (attr
.HasStyle())
11974 if (!clashingAttr
.HasStyle() && !absentAttr
.HasStyle())
11978 if (GetStyle() != attr
.GetStyle())
11980 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
11981 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
11985 SetStyle(attr
.GetStyle());
11989 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
11991 if (attr
.HasColour())
11993 if (!clashingAttr
.HasColour() && !absentAttr
.HasColour())
11997 if (GetColour() != attr
.GetColour())
11999 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
12000 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
12004 SetColour(attr
.GetColourLong());
12008 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
12010 m_borderWidth
.CollectCommonAttributes(attr
.m_borderWidth
, clashingAttr
.m_borderWidth
, absentAttr
.m_borderWidth
);
12013 // Partial equality test
12014 bool wxTextAttrBorders::EqPartial(const wxTextAttrBorders
& borders
) const
12016 return m_left
.EqPartial(borders
.m_left
) && m_right
.EqPartial(borders
.m_right
) &&
12017 m_top
.EqPartial(borders
.m_top
) && m_bottom
.EqPartial(borders
.m_bottom
);
12020 // Apply border to 'this', but not if the same as compareWith
12021 bool wxTextAttrBorders::Apply(const wxTextAttrBorders
& borders
, const wxTextAttrBorders
* compareWith
)
12023 m_left
.Apply(borders
.m_left
, compareWith
? (& compareWith
->m_left
) : (const wxTextAttrBorder
*) NULL
);
12024 m_right
.Apply(borders
.m_right
, compareWith
? (& compareWith
->m_right
) : (const wxTextAttrBorder
*) NULL
);
12025 m_top
.Apply(borders
.m_top
, compareWith
? (& compareWith
->m_top
) : (const wxTextAttrBorder
*) NULL
);
12026 m_bottom
.Apply(borders
.m_bottom
, compareWith
? (& compareWith
->m_bottom
) : (const wxTextAttrBorder
*) NULL
);
12030 // Remove specified attributes from this object
12031 bool wxTextAttrBorders::RemoveStyle(const wxTextAttrBorders
& attr
)
12033 m_left
.RemoveStyle(attr
.m_left
);
12034 m_right
.RemoveStyle(attr
.m_right
);
12035 m_top
.RemoveStyle(attr
.m_top
);
12036 m_bottom
.RemoveStyle(attr
.m_bottom
);
12040 // Collects the attributes that are common to a range of content, building up a note of
12041 // which attributes are absent in some objects and which clash in some objects.
12042 void wxTextAttrBorders::CollectCommonAttributes(const wxTextAttrBorders
& attr
, wxTextAttrBorders
& clashingAttr
, wxTextAttrBorders
& absentAttr
)
12044 m_left
.CollectCommonAttributes(attr
.m_left
, clashingAttr
.m_left
, absentAttr
.m_left
);
12045 m_right
.CollectCommonAttributes(attr
.m_right
, clashingAttr
.m_right
, absentAttr
.m_right
);
12046 m_top
.CollectCommonAttributes(attr
.m_top
, clashingAttr
.m_top
, absentAttr
.m_top
);
12047 m_bottom
.CollectCommonAttributes(attr
.m_bottom
, clashingAttr
.m_bottom
, absentAttr
.m_bottom
);
12050 // Set style of all borders
12051 void wxTextAttrBorders::SetStyle(int style
)
12053 m_left
.SetStyle(style
);
12054 m_right
.SetStyle(style
);
12055 m_top
.SetStyle(style
);
12056 m_bottom
.SetStyle(style
);
12059 // Set colour of all borders
12060 void wxTextAttrBorders::SetColour(unsigned long colour
)
12062 m_left
.SetColour(colour
);
12063 m_right
.SetColour(colour
);
12064 m_top
.SetColour(colour
);
12065 m_bottom
.SetColour(colour
);
12068 void wxTextAttrBorders::SetColour(const wxColour
& colour
)
12070 m_left
.SetColour(colour
);
12071 m_right
.SetColour(colour
);
12072 m_top
.SetColour(colour
);
12073 m_bottom
.SetColour(colour
);
12076 // Set width of all borders
12077 void wxTextAttrBorders::SetWidth(const wxTextAttrDimension
& width
)
12079 m_left
.SetWidth(width
);
12080 m_right
.SetWidth(width
);
12081 m_top
.SetWidth(width
);
12082 m_bottom
.SetWidth(width
);
12085 // Partial equality test
12086 bool wxTextAttrDimension::EqPartial(const wxTextAttrDimension
& dim
) const
12088 if (dim
.IsValid() && IsValid() && !((*this) == dim
))
12094 bool wxTextAttrDimension::Apply(const wxTextAttrDimension
& dim
, const wxTextAttrDimension
* compareWith
)
12098 if (!(compareWith
&& dim
== (*compareWith
)))
12105 // Collects the attributes that are common to a range of content, building up a note of
12106 // which attributes are absent in some objects and which clash in some objects.
12107 void wxTextAttrDimension::CollectCommonAttributes(const wxTextAttrDimension
& attr
, wxTextAttrDimension
& clashingAttr
, wxTextAttrDimension
& absentAttr
)
12109 if (attr
.IsValid())
12111 if (!clashingAttr
.IsValid() && !absentAttr
.IsValid())
12115 if (!((*this) == attr
))
12117 clashingAttr
.SetValid(true);
12126 absentAttr
.SetValid(true);
12129 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(wxDC
& dc
, double scale
, const wxSize
& parentSize
)
12131 m_ppi
= dc
.GetPPI().x
; m_scale
= scale
; m_parentSize
= parentSize
;
12134 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(int ppi
, double scale
, const wxSize
& parentSize
)
12136 m_ppi
= ppi
; m_scale
= scale
; m_parentSize
= parentSize
;
12139 int wxTextAttrDimensionConverter::ConvertTenthsMMToPixels(int units
) const
12141 return wxRichTextObject::ConvertTenthsMMToPixels(m_ppi
, units
, m_scale
);
12144 int wxTextAttrDimensionConverter::ConvertPixelsToTenthsMM(int pixels
) const
12146 return wxRichTextObject::ConvertPixelsToTenthsMM(m_ppi
, pixels
, m_scale
);
12149 int wxTextAttrDimensionConverter::GetPixels(const wxTextAttrDimension
& dim
, int direction
) const
12151 if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
12152 return ConvertTenthsMMToPixels(dim
.GetValue());
12153 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
12154 return dim
.GetValue();
12155 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
12157 wxASSERT(m_parentSize
!= wxDefaultSize
);
12158 if (direction
== wxHORIZONTAL
)
12159 return (int) (double(m_parentSize
.x
) * double(dim
.GetValue()) / 100.0);
12161 return (int) (double(m_parentSize
.y
) * double(dim
.GetValue()) / 100.0);
12170 int wxTextAttrDimensionConverter::GetTenthsMM(const wxTextAttrDimension
& dim
) const
12172 if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
12173 return dim
.GetValue();
12174 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
12175 return ConvertPixelsToTenthsMM(dim
.GetValue());
12183 // Partial equality test
12184 bool wxTextAttrDimensions::EqPartial(const wxTextAttrDimensions
& dims
) const
12186 if (!m_left
.EqPartial(dims
.m_left
))
12189 if (!m_right
.EqPartial(dims
.m_right
))
12192 if (!m_top
.EqPartial(dims
.m_top
))
12195 if (!m_bottom
.EqPartial(dims
.m_bottom
))
12201 // Apply border to 'this', but not if the same as compareWith
12202 bool wxTextAttrDimensions::Apply(const wxTextAttrDimensions
& dims
, const wxTextAttrDimensions
* compareWith
)
12204 m_left
.Apply(dims
.m_left
, compareWith
? (& compareWith
->m_left
) : (const wxTextAttrDimension
*) NULL
);
12205 m_right
.Apply(dims
.m_right
, compareWith
? (& compareWith
->m_right
): (const wxTextAttrDimension
*) NULL
);
12206 m_top
.Apply(dims
.m_top
, compareWith
? (& compareWith
->m_top
): (const wxTextAttrDimension
*) NULL
);
12207 m_bottom
.Apply(dims
.m_bottom
, compareWith
? (& compareWith
->m_bottom
): (const wxTextAttrDimension
*) NULL
);
12212 // Remove specified attributes from this object
12213 bool wxTextAttrDimensions::RemoveStyle(const wxTextAttrDimensions
& attr
)
12215 if (attr
.m_left
.IsValid())
12217 if (attr
.m_right
.IsValid())
12219 if (attr
.m_top
.IsValid())
12221 if (attr
.m_bottom
.IsValid())
12227 // Collects the attributes that are common to a range of content, building up a note of
12228 // which attributes are absent in some objects and which clash in some objects.
12229 void wxTextAttrDimensions::CollectCommonAttributes(const wxTextAttrDimensions
& attr
, wxTextAttrDimensions
& clashingAttr
, wxTextAttrDimensions
& absentAttr
)
12231 m_left
.CollectCommonAttributes(attr
.m_left
, clashingAttr
.m_left
, absentAttr
.m_left
);
12232 m_right
.CollectCommonAttributes(attr
.m_right
, clashingAttr
.m_right
, absentAttr
.m_right
);
12233 m_top
.CollectCommonAttributes(attr
.m_top
, clashingAttr
.m_top
, absentAttr
.m_top
);
12234 m_bottom
.CollectCommonAttributes(attr
.m_bottom
, clashingAttr
.m_bottom
, absentAttr
.m_bottom
);
12237 // Partial equality test
12238 bool wxTextAttrSize::EqPartial(const wxTextAttrSize
& size
) const
12240 if (!m_width
.EqPartial(size
.m_width
))
12243 if (!m_height
.EqPartial(size
.m_height
))
12249 // Apply border to 'this', but not if the same as compareWith
12250 bool wxTextAttrSize::Apply(const wxTextAttrSize
& size
, const wxTextAttrSize
* compareWith
)
12252 m_width
.Apply(size
.m_width
, compareWith
? (& compareWith
->m_width
) : (const wxTextAttrDimension
*) NULL
);
12253 m_height
.Apply(size
.m_height
, compareWith
? (& compareWith
->m_height
): (const wxTextAttrDimension
*) NULL
);
12258 // Remove specified attributes from this object
12259 bool wxTextAttrSize::RemoveStyle(const wxTextAttrSize
& attr
)
12261 if (attr
.m_width
.IsValid())
12263 if (attr
.m_height
.IsValid())
12269 // Collects the attributes that are common to a range of content, building up a note of
12270 // which attributes are absent in some objects and which clash in some objects.
12271 void wxTextAttrSize::CollectCommonAttributes(const wxTextAttrSize
& attr
, wxTextAttrSize
& clashingAttr
, wxTextAttrSize
& absentAttr
)
12273 m_width
.CollectCommonAttributes(attr
.m_width
, clashingAttr
.m_width
, absentAttr
.m_width
);
12274 m_height
.CollectCommonAttributes(attr
.m_height
, clashingAttr
.m_height
, absentAttr
.m_height
);
12277 // Collects the attributes that are common to a range of content, building up a note of
12278 // which attributes are absent in some objects and which clash in some objects.
12279 void wxTextAttrCollectCommonAttributes(wxTextAttr
& currentStyle
, const wxTextAttr
& attr
, wxTextAttr
& clashingAttr
, wxTextAttr
& absentAttr
)
12281 absentAttr
.SetFlags(absentAttr
.GetFlags() | (~attr
.GetFlags() & wxTEXT_ATTR_ALL
));
12282 absentAttr
.SetTextEffectFlags(absentAttr
.GetTextEffectFlags() | (~attr
.GetTextEffectFlags() & 0xFFFF));
12284 long forbiddenFlags
= clashingAttr
.GetFlags()|absentAttr
.GetFlags();
12286 if (attr
.HasFont())
12288 if (attr
.HasFontSize() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_SIZE
))
12290 if (currentStyle
.HasFontSize())
12292 if (currentStyle
.GetFontSize() != attr
.GetFontSize())
12294 // Clash of attr - mark as such
12295 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_SIZE
);
12296 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_SIZE
);
12300 currentStyle
.SetFontSize(attr
.GetFontSize());
12303 if (attr
.HasFontItalic() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_ITALIC
))
12305 if (currentStyle
.HasFontItalic())
12307 if (currentStyle
.GetFontStyle() != attr
.GetFontStyle())
12309 // Clash of attr - mark as such
12310 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_ITALIC
);
12311 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC
);
12315 currentStyle
.SetFontStyle(attr
.GetFontStyle());
12318 if (attr
.HasFontFamily() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_FAMILY
))
12320 if (currentStyle
.HasFontFamily())
12322 if (currentStyle
.GetFontFamily() != attr
.GetFontFamily())
12324 // Clash of attr - mark as such
12325 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FAMILY
);
12326 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY
);
12330 currentStyle
.SetFontFamily(attr
.GetFontFamily());
12333 if (attr
.HasFontWeight() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_WEIGHT
))
12335 if (currentStyle
.HasFontWeight())
12337 if (currentStyle
.GetFontWeight() != attr
.GetFontWeight())
12339 // Clash of attr - mark as such
12340 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_WEIGHT
);
12341 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT
);
12345 currentStyle
.SetFontWeight(attr
.GetFontWeight());
12348 if (attr
.HasFontFaceName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_FACE
))
12350 if (currentStyle
.HasFontFaceName())
12352 wxString
faceName1(currentStyle
.GetFontFaceName());
12353 wxString
faceName2(attr
.GetFontFaceName());
12355 if (faceName1
!= faceName2
)
12357 // Clash of attr - mark as such
12358 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FACE
);
12359 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FACE
);
12363 currentStyle
.SetFontFaceName(attr
.GetFontFaceName());
12366 if (attr
.HasFontUnderlined() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_UNDERLINE
))
12368 if (currentStyle
.HasFontUnderlined())
12370 if (currentStyle
.GetFontUnderlined() != attr
.GetFontUnderlined())
12372 // Clash of attr - mark as such
12373 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
12374 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
12378 currentStyle
.SetFontUnderlined(attr
.GetFontUnderlined());
12382 if (attr
.HasTextColour() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_TEXT_COLOUR
))
12384 if (currentStyle
.HasTextColour())
12386 if (currentStyle
.GetTextColour() != attr
.GetTextColour())
12388 // Clash of attr - mark as such
12389 clashingAttr
.AddFlag(wxTEXT_ATTR_TEXT_COLOUR
);
12390 currentStyle
.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR
);
12394 currentStyle
.SetTextColour(attr
.GetTextColour());
12397 if (attr
.HasBackgroundColour() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BACKGROUND_COLOUR
))
12399 if (currentStyle
.HasBackgroundColour())
12401 if (currentStyle
.GetBackgroundColour() != attr
.GetBackgroundColour())
12403 // Clash of attr - mark as such
12404 clashingAttr
.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
12405 currentStyle
.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
12409 currentStyle
.SetBackgroundColour(attr
.GetBackgroundColour());
12412 if (attr
.HasAlignment() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_ALIGNMENT
))
12414 if (currentStyle
.HasAlignment())
12416 if (currentStyle
.GetAlignment() != attr
.GetAlignment())
12418 // Clash of attr - mark as such
12419 clashingAttr
.AddFlag(wxTEXT_ATTR_ALIGNMENT
);
12420 currentStyle
.RemoveFlag(wxTEXT_ATTR_ALIGNMENT
);
12424 currentStyle
.SetAlignment(attr
.GetAlignment());
12427 if (attr
.HasTabs() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_TABS
))
12429 if (currentStyle
.HasTabs())
12431 if (!wxRichTextTabsEq(currentStyle
.GetTabs(), attr
.GetTabs()))
12433 // Clash of attr - mark as such
12434 clashingAttr
.AddFlag(wxTEXT_ATTR_TABS
);
12435 currentStyle
.RemoveFlag(wxTEXT_ATTR_TABS
);
12439 currentStyle
.SetTabs(attr
.GetTabs());
12442 if (attr
.HasLeftIndent() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LEFT_INDENT
))
12444 if (currentStyle
.HasLeftIndent())
12446 if (currentStyle
.GetLeftIndent() != attr
.GetLeftIndent() || currentStyle
.GetLeftSubIndent() != attr
.GetLeftSubIndent())
12448 // Clash of attr - mark as such
12449 clashingAttr
.AddFlag(wxTEXT_ATTR_LEFT_INDENT
);
12450 currentStyle
.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT
);
12454 currentStyle
.SetLeftIndent(attr
.GetLeftIndent(), attr
.GetLeftSubIndent());
12457 if (attr
.HasRightIndent() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_RIGHT_INDENT
))
12459 if (currentStyle
.HasRightIndent())
12461 if (currentStyle
.GetRightIndent() != attr
.GetRightIndent())
12463 // Clash of attr - mark as such
12464 clashingAttr
.AddFlag(wxTEXT_ATTR_RIGHT_INDENT
);
12465 currentStyle
.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT
);
12469 currentStyle
.SetRightIndent(attr
.GetRightIndent());
12472 if (attr
.HasParagraphSpacingAfter() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARA_SPACING_AFTER
))
12474 if (currentStyle
.HasParagraphSpacingAfter())
12476 if (currentStyle
.GetParagraphSpacingAfter() != attr
.GetParagraphSpacingAfter())
12478 // Clash of attr - mark as such
12479 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
12480 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
12484 currentStyle
.SetParagraphSpacingAfter(attr
.GetParagraphSpacingAfter());
12487 if (attr
.HasParagraphSpacingBefore() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARA_SPACING_BEFORE
))
12489 if (currentStyle
.HasParagraphSpacingBefore())
12491 if (currentStyle
.GetParagraphSpacingBefore() != attr
.GetParagraphSpacingBefore())
12493 // Clash of attr - mark as such
12494 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
12495 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
12499 currentStyle
.SetParagraphSpacingBefore(attr
.GetParagraphSpacingBefore());
12502 if (attr
.HasLineSpacing() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LINE_SPACING
))
12504 if (currentStyle
.HasLineSpacing())
12506 if (currentStyle
.GetLineSpacing() != attr
.GetLineSpacing())
12508 // Clash of attr - mark as such
12509 clashingAttr
.AddFlag(wxTEXT_ATTR_LINE_SPACING
);
12510 currentStyle
.RemoveFlag(wxTEXT_ATTR_LINE_SPACING
);
12514 currentStyle
.SetLineSpacing(attr
.GetLineSpacing());
12517 if (attr
.HasCharacterStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_CHARACTER_STYLE_NAME
))
12519 if (currentStyle
.HasCharacterStyleName())
12521 if (currentStyle
.GetCharacterStyleName() != attr
.GetCharacterStyleName())
12523 // Clash of attr - mark as such
12524 clashingAttr
.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
12525 currentStyle
.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
12529 currentStyle
.SetCharacterStyleName(attr
.GetCharacterStyleName());
12532 if (attr
.HasParagraphStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
))
12534 if (currentStyle
.HasParagraphStyleName())
12536 if (currentStyle
.GetParagraphStyleName() != attr
.GetParagraphStyleName())
12538 // Clash of attr - mark as such
12539 clashingAttr
.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
12540 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
12544 currentStyle
.SetParagraphStyleName(attr
.GetParagraphStyleName());
12547 if (attr
.HasListStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LIST_STYLE_NAME
))
12549 if (currentStyle
.HasListStyleName())
12551 if (currentStyle
.GetListStyleName() != attr
.GetListStyleName())
12553 // Clash of attr - mark as such
12554 clashingAttr
.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
12555 currentStyle
.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
12559 currentStyle
.SetListStyleName(attr
.GetListStyleName());
12562 if (attr
.HasBulletStyle() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_STYLE
))
12564 if (currentStyle
.HasBulletStyle())
12566 if (currentStyle
.GetBulletStyle() != attr
.GetBulletStyle())
12568 // Clash of attr - mark as such
12569 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_STYLE
);
12570 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE
);
12574 currentStyle
.SetBulletStyle(attr
.GetBulletStyle());
12577 if (attr
.HasBulletNumber() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_NUMBER
))
12579 if (currentStyle
.HasBulletNumber())
12581 if (currentStyle
.GetBulletNumber() != attr
.GetBulletNumber())
12583 // Clash of attr - mark as such
12584 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NUMBER
);
12585 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER
);
12589 currentStyle
.SetBulletNumber(attr
.GetBulletNumber());
12592 if (attr
.HasBulletText() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_TEXT
))
12594 if (currentStyle
.HasBulletText())
12596 if (currentStyle
.GetBulletText() != attr
.GetBulletText())
12598 // Clash of attr - mark as such
12599 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_TEXT
);
12600 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT
);
12605 currentStyle
.SetBulletText(attr
.GetBulletText());
12606 currentStyle
.SetBulletFont(attr
.GetBulletFont());
12610 if (attr
.HasBulletName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_NAME
))
12612 if (currentStyle
.HasBulletName())
12614 if (currentStyle
.GetBulletName() != attr
.GetBulletName())
12616 // Clash of attr - mark as such
12617 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NAME
);
12618 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NAME
);
12623 currentStyle
.SetBulletName(attr
.GetBulletName());
12627 if (attr
.HasURL() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_URL
))
12629 if (currentStyle
.HasURL())
12631 if (currentStyle
.GetURL() != attr
.GetURL())
12633 // Clash of attr - mark as such
12634 clashingAttr
.AddFlag(wxTEXT_ATTR_URL
);
12635 currentStyle
.RemoveFlag(wxTEXT_ATTR_URL
);
12640 currentStyle
.SetURL(attr
.GetURL());
12644 if (attr
.HasTextEffects() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_EFFECTS
))
12646 if (currentStyle
.HasTextEffects())
12648 // We need to find the bits in the new attr that are different:
12649 // just look at those bits that are specified by the new attr.
12651 // We need to remove the bits and flags that are not common between current attr
12652 // and new attr. In so doing we need to take account of the styles absent from one or more of the
12653 // previous styles.
12655 int currentRelevantTextEffects
= currentStyle
.GetTextEffects() & attr
.GetTextEffectFlags();
12656 int newRelevantTextEffects
= attr
.GetTextEffects() & attr
.GetTextEffectFlags();
12658 if (currentRelevantTextEffects
!= newRelevantTextEffects
)
12660 // Find the text effects that were different, using XOR
12661 int differentEffects
= currentRelevantTextEffects
^ newRelevantTextEffects
;
12663 // Clash of attr - mark as such
12664 clashingAttr
.SetTextEffectFlags(clashingAttr
.GetTextEffectFlags() | differentEffects
);
12665 currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~differentEffects
);
12670 currentStyle
.SetTextEffects(attr
.GetTextEffects());
12671 currentStyle
.SetTextEffectFlags(attr
.GetTextEffectFlags());
12674 // Mask out the flags and values that cannot be common because they were absent in one or more objecrs
12675 // that we've looked at so far
12676 currentStyle
.SetTextEffects(currentStyle
.GetTextEffects() & ~absentAttr
.GetTextEffectFlags());
12677 currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~absentAttr
.GetTextEffectFlags());
12679 if (currentStyle
.GetTextEffectFlags() == 0)
12680 currentStyle
.RemoveFlag(wxTEXT_ATTR_EFFECTS
);
12683 if (attr
.HasOutlineLevel() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_OUTLINE_LEVEL
))
12685 if (currentStyle
.HasOutlineLevel())
12687 if (currentStyle
.GetOutlineLevel() != attr
.GetOutlineLevel())
12689 // Clash of attr - mark as such
12690 clashingAttr
.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
12691 currentStyle
.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
12695 currentStyle
.SetOutlineLevel(attr
.GetOutlineLevel());
12699 WX_DEFINE_OBJARRAY(wxRichTextVariantArray
);
12701 IMPLEMENT_DYNAMIC_CLASS(wxRichTextProperties
, wxObject
)
12703 bool wxRichTextProperties::operator==(const wxRichTextProperties
& props
) const
12705 if (m_properties
.GetCount() != props
.GetCount())
12709 for (i
= 0; i
< m_properties
.GetCount(); i
++)
12711 const wxVariant
& var1
= m_properties
[i
];
12712 int idx
= props
.Find(var1
.GetName());
12715 const wxVariant
& var2
= props
.m_properties
[idx
];
12716 if (!(var1
== var2
))
12723 wxArrayString
wxRichTextProperties::GetPropertyNames() const
12727 for (i
= 0; i
< m_properties
.GetCount(); i
++)
12729 arr
.Add(m_properties
[i
].GetName());
12734 int wxRichTextProperties::Find(const wxString
& name
) const
12737 for (i
= 0; i
< m_properties
.GetCount(); i
++)
12739 if (m_properties
[i
].GetName() == name
)
12745 bool wxRichTextProperties::Remove(const wxString
& name
)
12747 int idx
= Find(name
);
12750 m_properties
.RemoveAt(idx
);
12757 wxVariant
* wxRichTextProperties::FindOrCreateProperty(const wxString
& name
)
12759 int idx
= Find(name
);
12760 if (idx
== wxNOT_FOUND
)
12761 SetProperty(name
, wxString());
12763 if (idx
!= wxNOT_FOUND
)
12765 return & (*this)[idx
];
12771 const wxVariant
& wxRichTextProperties::GetProperty(const wxString
& name
) const
12773 static const wxVariant nullVariant
;
12774 int idx
= Find(name
);
12776 return m_properties
[idx
];
12778 return nullVariant
;
12781 wxString
wxRichTextProperties::GetPropertyString(const wxString
& name
) const
12783 return GetProperty(name
).GetString();
12786 long wxRichTextProperties::GetPropertyLong(const wxString
& name
) const
12788 return GetProperty(name
).GetLong();
12791 bool wxRichTextProperties::GetPropertyBool(const wxString
& name
) const
12793 return GetProperty(name
).GetBool();
12796 double wxRichTextProperties::GetPropertyDouble(const wxString
& name
) const
12798 return GetProperty(name
).GetDouble();
12801 void wxRichTextProperties::SetProperty(const wxVariant
& variant
)
12803 wxASSERT(!variant
.GetName().IsEmpty());
12805 int idx
= Find(variant
.GetName());
12808 m_properties
.Add(variant
);
12810 m_properties
[idx
] = variant
;
12813 void wxRichTextProperties::SetProperty(const wxString
& name
, const wxVariant
& variant
)
12815 int idx
= Find(name
);
12816 wxVariant
var(variant
);
12820 m_properties
.Add(var
);
12822 m_properties
[idx
] = var
;
12825 void wxRichTextProperties::SetProperty(const wxString
& name
, const wxString
& value
)
12827 SetProperty(name
, wxVariant(value
, name
));
12830 void wxRichTextProperties::SetProperty(const wxString
& name
, long value
)
12832 SetProperty(name
, wxVariant(value
, name
));
12835 void wxRichTextProperties::SetProperty(const wxString
& name
, double value
)
12837 SetProperty(name
, wxVariant(value
, name
));
12840 void wxRichTextProperties::SetProperty(const wxString
& name
, bool value
)
12842 SetProperty(name
, wxVariant(value
, name
));
12845 void wxRichTextProperties::RemoveProperties(const wxRichTextProperties
& properties
)
12848 for (i
= 0; i
< properties
.GetCount(); i
++)
12850 wxString name
= properties
.GetProperties()[i
].GetName();
12851 if (HasProperty(name
))
12856 void wxRichTextProperties::MergeProperties(const wxRichTextProperties
& properties
)
12859 for (i
= 0; i
< properties
.GetCount(); i
++)
12861 SetProperty(properties
.GetProperties()[i
]);
12865 wxRichTextObject
* wxRichTextObjectAddress::GetObject(wxRichTextParagraphLayoutBox
* topLevelContainer
) const
12867 if (m_address
.GetCount() == 0)
12868 return topLevelContainer
;
12870 wxRichTextCompositeObject
* p
= topLevelContainer
;
12872 while (p
&& i
< m_address
.GetCount())
12874 int pos
= m_address
[i
];
12875 wxASSERT(pos
>= 0 && pos
< (int) p
->GetChildren().GetCount());
12876 if (pos
< 0 || pos
>= (int) p
->GetChildren().GetCount())
12879 wxRichTextObject
* p1
= p
->GetChild(pos
);
12880 if (i
== (m_address
.GetCount()-1))
12883 p
= wxDynamicCast(p1
, wxRichTextCompositeObject
);
12889 bool wxRichTextObjectAddress::Create(wxRichTextParagraphLayoutBox
* topLevelContainer
, wxRichTextObject
* obj
)
12893 if (topLevelContainer
== obj
)
12896 wxRichTextObject
* o
= obj
;
12899 wxRichTextCompositeObject
* p
= wxDynamicCast(o
->GetParent(), wxRichTextCompositeObject
);
12903 int pos
= p
->GetChildren().IndexOf(o
);
12907 m_address
.Insert(pos
, 0);
12909 if (p
== topLevelContainer
)
12918 bool wxRichTextSelection::operator==(const wxRichTextSelection
& sel
) const
12920 if (m_container
!= sel
.m_container
)
12922 if (m_ranges
.GetCount() != sel
.m_ranges
.GetCount())
12925 for (i
= 0; i
< m_ranges
.GetCount(); i
++)
12926 if (!(m_ranges
[i
] == sel
.m_ranges
[i
]))
12931 // Get the selections appropriate to the specified object, if any; returns wxRICHTEXT_NO_SELECTION if none
12932 // or none at the level of the object's container.
12933 wxRichTextRangeArray
wxRichTextSelection::GetSelectionForObject(wxRichTextObject
* obj
) const
12937 wxRichTextParagraphLayoutBox
* container
= obj
->GetParentContainer();
12939 if (container
== m_container
)
12942 container
= obj
->GetContainer();
12945 if (container
->GetParent())
12947 // If we found that our object's container is within the range of
12948 // a selection higher up, then assume the whole original object
12949 // is also selected.
12950 wxRichTextParagraphLayoutBox
* parentContainer
= container
->GetParentContainer();
12951 if (parentContainer
== m_container
)
12953 if (WithinSelection(container
->GetRange().GetStart(), m_ranges
))
12955 wxRichTextRangeArray ranges
;
12956 ranges
.Add(obj
->GetRange());
12961 container
= parentContainer
;
12970 return wxRichTextRangeArray();
12973 // Is the given position within the selection?
12974 bool wxRichTextSelection::WithinSelection(long pos
, wxRichTextObject
* obj
) const
12980 wxRichTextRangeArray selectionRanges
= GetSelectionForObject(obj
);
12981 return WithinSelection(pos
, selectionRanges
);
12985 // Is the given position within the selection range?
12986 bool wxRichTextSelection::WithinSelection(long pos
, const wxRichTextRangeArray
& ranges
)
12989 for (i
= 0; i
< ranges
.GetCount(); i
++)
12991 const wxRichTextRange
& range
= ranges
[i
];
12992 if (pos
>= range
.GetStart() && pos
<= range
.GetEnd())
12998 // Is the given range completely within the selection range?
12999 bool wxRichTextSelection::WithinSelection(const wxRichTextRange
& range
, const wxRichTextRangeArray
& ranges
)
13002 for (i
= 0; i
< ranges
.GetCount(); i
++)
13004 const wxRichTextRange
& eachRange
= ranges
[i
];
13005 if (range
.IsWithin(eachRange
))
13011 IMPLEMENT_CLASS(wxRichTextDrawingHandler
, wxObject
)
13012 IMPLEMENT_CLASS(wxRichTextDrawingContext
, wxObject
)
13014 bool wxRichTextDrawingContext::HasVirtualAttributes(wxRichTextObject
* obj
) const
13016 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
13019 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13020 if (handler
->HasVirtualAttributes(obj
))
13023 node
= node
->GetNext();
13028 wxRichTextAttr
wxRichTextDrawingContext::GetVirtualAttributes(wxRichTextObject
* obj
) const
13030 wxRichTextAttr attr
;
13031 // We apply all handlers, so we can may combine several different attributes
13032 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
13035 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13036 if (handler
->HasVirtualAttributes(obj
))
13038 bool success
= handler
->GetVirtualAttributes(attr
, obj
);
13040 wxUnusedVar(success
);
13043 node
= node
->GetNext();
13048 bool wxRichTextDrawingContext::ApplyVirtualAttributes(wxRichTextAttr
& attr
, wxRichTextObject
* obj
) const
13050 if (HasVirtualAttributes(obj
))
13052 wxRichTextAttr
a(GetVirtualAttributes(obj
));
13060 /// Adds a handler to the end
13061 void wxRichTextBuffer::AddDrawingHandler(wxRichTextDrawingHandler
*handler
)
13063 sm_drawingHandlers
.Append(handler
);
13066 /// Inserts a handler at the front
13067 void wxRichTextBuffer::InsertDrawingHandler(wxRichTextDrawingHandler
*handler
)
13069 sm_drawingHandlers
.Insert( handler
);
13072 /// Removes a handler
13073 bool wxRichTextBuffer::RemoveDrawingHandler(const wxString
& name
)
13075 wxRichTextDrawingHandler
*handler
= FindDrawingHandler(name
);
13078 sm_drawingHandlers
.DeleteObject(handler
);
13086 wxRichTextDrawingHandler
* wxRichTextBuffer::FindDrawingHandler(const wxString
& name
)
13088 wxList::compatibility_iterator node
= sm_drawingHandlers
.GetFirst();
13091 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13092 if (handler
->GetName().Lower() == name
.Lower()) return handler
;
13094 node
= node
->GetNext();
13099 void wxRichTextBuffer::CleanUpDrawingHandlers()
13101 wxList::compatibility_iterator node
= sm_drawingHandlers
.GetFirst();
13104 wxRichTextDrawingHandler
* handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13105 wxList::compatibility_iterator next
= node
->GetNext();
13110 sm_drawingHandlers
.Clear();
13113 void wxRichTextBuffer::AddFieldType(wxRichTextFieldType
*fieldType
)
13115 sm_fieldTypes
[fieldType
->GetName()] = fieldType
;
13118 bool wxRichTextBuffer::RemoveFieldType(const wxString
& name
)
13120 wxRichTextFieldTypeHashMap::iterator it
= sm_fieldTypes
.find(name
);
13121 if (it
== sm_fieldTypes
.end())
13125 wxRichTextFieldType
* fieldType
= it
->second
;
13126 sm_fieldTypes
.erase(it
);
13132 wxRichTextFieldType
*wxRichTextBuffer::FindFieldType(const wxString
& name
)
13134 wxRichTextFieldTypeHashMap::iterator it
= sm_fieldTypes
.find(name
);
13135 if (it
== sm_fieldTypes
.end())
13141 void wxRichTextBuffer::CleanUpFieldTypes()
13143 wxRichTextFieldTypeHashMap::iterator it
;
13144 for( it
= sm_fieldTypes
.begin(); it
!= sm_fieldTypes
.end(); ++it
)
13146 wxRichTextFieldType
* fieldType
= it
->second
;
13150 sm_fieldTypes
.clear();