1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/richtext/richtextbuffer.cpp
3 // Purpose: Buffer for wxRichTextCtrl
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // For compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
21 #include "wx/richtext/richtextbuffer.h"
27 #include "wx/dataobj.h"
28 #include "wx/module.h"
31 #include "wx/settings.h"
32 #include "wx/filename.h"
33 #include "wx/clipbrd.h"
34 #include "wx/wfstream.h"
35 #include "wx/mstream.h"
36 #include "wx/sstream.h"
37 #include "wx/textfile.h"
38 #include "wx/hashmap.h"
39 #include "wx/dynarray.h"
41 #include "wx/richtext/richtextctrl.h"
42 #include "wx/richtext/richtextstyles.h"
43 #include "wx/richtext/richtextimagedlg.h"
44 #include "wx/richtext/richtextsizepage.h"
45 #include "wx/richtext/richtextxml.h"
47 #include "wx/listimpl.cpp"
48 #include "wx/arrimpl.cpp"
50 WX_DEFINE_LIST(wxRichTextObjectList
)
51 WX_DEFINE_LIST(wxRichTextLineList
)
53 // Switch off if the platform doesn't like it for some reason
54 #define wxRICHTEXT_USE_OPTIMIZED_DRAWING 1
56 // Use GetPartialTextExtents for platforms that support it natively
57 #define wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS 1
59 const wxChar wxRichTextLineBreakChar
= (wxChar
) 29;
61 // Helper classes for floating layout
62 struct wxRichTextFloatRectMap
64 wxRichTextFloatRectMap(int sY
, int eY
, int w
, wxRichTextObject
* obj
)
74 wxRichTextObject
* anchor
;
77 WX_DEFINE_SORTED_ARRAY(wxRichTextFloatRectMap
*, wxRichTextFloatRectMapArray
);
79 int wxRichTextFloatRectMapCmp(wxRichTextFloatRectMap
* r1
, wxRichTextFloatRectMap
* r2
)
81 return r1
->startY
- r2
->startY
;
84 class wxRichTextFloatCollector
87 wxRichTextFloatCollector(const wxRect
& availableRect
);
88 ~wxRichTextFloatCollector();
90 // Collect the floating objects info in the given paragraph
91 void CollectFloat(wxRichTextParagraph
* para
);
92 void CollectFloat(wxRichTextParagraph
* para
, wxRichTextObject
* floating
);
94 // Return the last paragraph we collected
95 wxRichTextParagraph
* LastParagraph();
97 // Given the start y position and the height of the line,
98 // find out how wide the line can be
99 wxRect
GetAvailableRect(int startY
, int endY
);
101 // Given a floating box, find its fit position
102 int GetFitPosition(int direction
, int start
, int height
) const;
103 int GetFitPosition(const wxRichTextFloatRectMapArray
& array
, int start
, int height
) const;
105 // Find the last y position
106 int GetLastRectBottom();
108 // Draw the floats inside a rect
109 void Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
);
111 // HitTest the floats
112 int HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, int flags
);
114 // Get floating object count
115 int GetFloatingObjectCount() const { return m_left
.GetCount() + m_right
.GetCount(); }
117 // Get floating objects
118 bool GetFloatingObjects(wxRichTextObjectList
& objects
) const;
121 bool DeleteFloat(wxRichTextObject
* obj
);
123 // Do we have this float already?
124 bool HasFloat(wxRichTextObject
* obj
);
126 bool HasFloats() const { return m_left
.GetCount() >0 || m_right
.GetCount() > 0; }
128 static int SearchAdjacentRect(const wxRichTextFloatRectMapArray
& array
, int point
);
130 static int GetWidthFromFloatRect(const wxRichTextFloatRectMapArray
& array
, int index
, int startY
, int endY
);
132 static void FreeFloatRectMapArray(wxRichTextFloatRectMapArray
& array
);
134 static void DrawFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
);
136 static int HitTestFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, int flags
);
139 wxRichTextFloatRectMapArray m_left
;
140 wxRichTextFloatRectMapArray m_right
;
142 wxRect m_availableRect
;
143 wxRichTextParagraph
* m_para
;
147 bool wxRichTextFloatCollector::DeleteFloat(wxRichTextObject
* obj
)
150 for (i
= 0; i
< m_left
.GetCount(); i
++)
152 if (m_left
[i
]->anchor
== obj
)
158 for (i
= 0; i
< m_right
.GetCount(); i
++)
160 if (m_right
[i
]->anchor
== obj
)
169 // Do we have this float already?
170 bool wxRichTextFloatCollector::HasFloat(wxRichTextObject
* obj
)
173 for (i
= 0; i
< m_left
.GetCount(); i
++)
175 if (m_left
[i
]->anchor
== obj
)
180 for (i
= 0; i
< m_right
.GetCount(); i
++)
182 if (m_right
[i
]->anchor
== obj
)
190 // Get floating objects
191 bool wxRichTextFloatCollector::GetFloatingObjects(wxRichTextObjectList
& objects
) const
194 for (i
= 0; i
< m_left
.GetCount(); i
++)
195 objects
.Append(m_left
[i
]->anchor
);
196 for (i
= 0; i
< m_right
.GetCount(); i
++)
197 objects
.Append(m_right
[i
]->anchor
);
203 * Binary search helper function
204 * The argument point is the Y coordinate, and this fuction
205 * always return the floating rect that contain this coordinate
206 * or under this coordinate.
208 int wxRichTextFloatCollector::SearchAdjacentRect(const wxRichTextFloatRectMapArray
& array
, int point
)
210 int end
= array
.GetCount() - 1;
223 int mid
= (start
+ end
) / 2;
224 if (array
[mid
]->startY
<= point
&& array
[mid
]->endY
>= point
)
226 else if (array
[mid
]->startY
> point
)
231 else if (array
[mid
]->endY
< point
)
241 int wxRichTextFloatCollector::GetWidthFromFloatRect(const wxRichTextFloatRectMapArray
& array
, int index
, int startY
, int endY
)
244 int len
= array
.GetCount();
246 wxASSERT(index
>= 0 && index
< len
);
248 if (array
[index
]->startY
< startY
&& array
[index
]->endY
> startY
)
249 ret
= ret
< array
[index
]->width
? array
[index
]->width
: ret
;
250 while (index
< len
&& array
[index
]->startY
<= endY
)
252 ret
= ret
< array
[index
]->width
? array
[index
]->width
: ret
;
259 wxRichTextFloatCollector::wxRichTextFloatCollector(const wxRect
& rect
) : m_left(wxRichTextFloatRectMapCmp
), m_right(wxRichTextFloatRectMapCmp
)
261 m_availableRect
= rect
;
265 void wxRichTextFloatCollector::FreeFloatRectMapArray(wxRichTextFloatRectMapArray
& array
)
267 int len
= array
.GetCount();
268 for (int i
= 0; i
< len
; i
++)
272 wxRichTextFloatCollector::~wxRichTextFloatCollector()
274 FreeFloatRectMapArray(m_left
);
275 FreeFloatRectMapArray(m_right
);
278 int wxRichTextFloatCollector::GetFitPosition(const wxRichTextFloatRectMapArray
& array
, int start
, int height
) const
280 if (array
.GetCount() == 0)
283 int i
= SearchAdjacentRect(array
, start
);
285 while (i
< (int) array
.GetCount())
287 if (array
[i
]->startY
- last
>= height
)
289 last
= array
[i
]->endY
;
296 int wxRichTextFloatCollector::GetFitPosition(int direction
, int start
, int height
) const
298 if (direction
== wxTEXT_BOX_ATTR_FLOAT_LEFT
)
299 return GetFitPosition(m_left
, start
, height
);
300 else if (direction
== wxTEXT_BOX_ATTR_FLOAT_RIGHT
)
301 return GetFitPosition(m_right
, start
, height
);
304 wxASSERT("Never should be here");
309 // Adds a floating image to the float collector.
310 // The actual positioning is done by wxRichTextParagraph::LayoutFloat.
311 void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph
* para
, wxRichTextObject
* floating
)
313 int direction
= floating
->GetFloatDirection();
315 wxPoint pos
= floating
->GetPosition();
316 wxSize size
= floating
->GetCachedSize();
317 wxRichTextFloatRectMap
*map
= new wxRichTextFloatRectMap(pos
.y
, pos
.y
+ size
.y
, size
.x
, floating
);
320 case wxTEXT_BOX_ATTR_FLOAT_NONE
:
323 case wxTEXT_BOX_ATTR_FLOAT_LEFT
:
324 // Just a not-enough simple assertion
325 wxASSERT (m_left
.Index(map
) == wxNOT_FOUND
);
328 case wxTEXT_BOX_ATTR_FLOAT_RIGHT
:
329 wxASSERT (m_right
.Index(map
) == wxNOT_FOUND
);
334 wxASSERT("Unrecognised float attribute.");
340 void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph
* para
)
342 wxRichTextObjectList::compatibility_iterator node
= para
->GetChildren().GetFirst();
345 wxRichTextObject
* floating
= node
->GetData();
347 if (floating
->IsFloating())
349 CollectFloat(para
, floating
);
352 node
= node
->GetNext();
358 wxRichTextParagraph
* wxRichTextFloatCollector::LastParagraph()
363 wxRect
wxRichTextFloatCollector::GetAvailableRect(int startY
, int endY
)
365 int widthLeft
= 0, widthRight
= 0;
366 if (m_left
.GetCount() != 0)
368 int i
= SearchAdjacentRect(m_left
, startY
);
369 if (i
< (int) m_left
.GetCount())
370 widthLeft
= GetWidthFromFloatRect(m_left
, i
, startY
, endY
);
372 if (m_right
.GetCount() != 0)
374 int j
= SearchAdjacentRect(m_right
, startY
);
375 if (j
< (int) m_right
.GetCount())
376 widthRight
= GetWidthFromFloatRect(m_right
, j
, startY
, endY
);
379 // TODO: actually we want to use the actual image positions to find the
380 // available remaining space, since the image might not be right up against
381 // the left or right edge of the container.
382 return wxRect(widthLeft
+ m_availableRect
.x
, 0, m_availableRect
.width
- widthLeft
- widthRight
, 0);
385 int wxRichTextFloatCollector::GetLastRectBottom()
388 int len
= m_left
.GetCount();
390 ret
= ret
> m_left
[len
-1]->endY
? ret
: m_left
[len
-1]->endY
;
392 len
= m_right
.GetCount();
394 ret
= ret
> m_right
[len
-1]->endY
? ret
: m_right
[len
-1]->endY
;
400 void wxRichTextFloatCollector::DrawFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& WXUNUSED(range
), const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
403 int end
= rect
.y
+ rect
.height
;
405 i
= SearchAdjacentRect(array
, start
);
406 if (i
< 0 || i
>= (int) array
.GetCount())
408 j
= SearchAdjacentRect(array
, end
);
409 if (j
< 0 || j
>= (int) array
.GetCount())
410 j
= array
.GetCount() - 1;
413 wxRichTextObject
* obj
= array
[i
]->anchor
;
414 wxRichTextRange r
= obj
->GetRange();
415 obj
->Draw(dc
, context
, r
, selection
, wxRect(obj
->GetPosition(), obj
->GetCachedSize()), descent
, style
);
420 void wxRichTextFloatCollector::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
422 if (m_left
.GetCount() > 0)
423 DrawFloat(m_left
, dc
, context
, range
, selection
, rect
, descent
, style
);
424 if (m_right
.GetCount() > 0)
425 DrawFloat(m_right
, dc
, context
, range
, selection
, rect
, descent
, style
);
428 int wxRichTextFloatCollector::HitTestFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& WXUNUSED(dc
), wxRichTextDrawingContext
& WXUNUSED(context
), const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, int WXUNUSED(flags
))
431 if (array
.GetCount() == 0)
432 return wxRICHTEXT_HITTEST_NONE
;
433 i
= SearchAdjacentRect(array
, pt
.y
);
434 if (i
< 0 || i
>= (int) array
.GetCount())
435 return wxRICHTEXT_HITTEST_NONE
;
436 if (!array
[i
]->anchor
->IsShown())
437 return wxRICHTEXT_HITTEST_NONE
;
439 wxPoint point
= array
[i
]->anchor
->GetPosition();
440 wxSize size
= array
[i
]->anchor
->GetCachedSize();
441 if (point
.x
<= pt
.x
&& point
.x
+ size
.x
>= pt
.x
442 && point
.y
<= pt
.y
&& point
.y
+ size
.y
>= pt
.y
)
444 textPosition
= array
[i
]->anchor
->GetRange().GetStart();
445 * obj
= array
[i
]->anchor
;
446 if (pt
.x
> (pt
.x
+ pt
.x
+ size
.x
) / 2)
447 return wxRICHTEXT_HITTEST_BEFORE
;
449 return wxRICHTEXT_HITTEST_AFTER
;
452 return wxRICHTEXT_HITTEST_NONE
;
455 int wxRichTextFloatCollector::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, int flags
)
457 int ret
= HitTestFloat(m_left
, dc
, context
, pt
, textPosition
, obj
, flags
);
458 if (ret
== wxRICHTEXT_HITTEST_NONE
)
460 ret
= HitTestFloat(m_right
, dc
, context
, pt
, textPosition
, obj
, flags
);
465 // Helpers for efficiency
466 inline void wxCheckSetFont(wxDC
& dc
, const wxFont
& font
)
471 inline void wxCheckSetPen(wxDC
& dc
, const wxPen
& pen
)
473 const wxPen
& pen1
= dc
.GetPen();
474 if (pen1
.IsOk() && pen
.IsOk())
476 if (pen1
.GetWidth() == pen
.GetWidth() &&
477 pen1
.GetStyle() == pen
.GetStyle() &&
478 pen1
.GetColour() == pen
.GetColour())
484 inline void wxCheckSetBrush(wxDC
& dc
, const wxBrush
& brush
)
486 const wxBrush
& brush1
= dc
.GetBrush();
487 if (brush1
.IsOk() && brush
.IsOk())
489 if (brush1
.GetStyle() == brush
.GetStyle() &&
490 brush1
.GetColour() == brush
.GetColour())
498 * This is the base for drawable objects.
501 IMPLEMENT_CLASS(wxRichTextObject
, wxObject
)
503 wxRichTextObject::wxRichTextObject(wxRichTextObject
* parent
)
511 wxRichTextObject::~wxRichTextObject()
515 void wxRichTextObject::Dereference()
523 void wxRichTextObject::Copy(const wxRichTextObject
& obj
)
526 m_maxSize
= obj
.m_maxSize
;
527 m_minSize
= obj
.m_minSize
;
529 m_range
= obj
.m_range
;
530 m_ownRange
= obj
.m_ownRange
;
531 m_attributes
= obj
.m_attributes
;
532 m_properties
= obj
.m_properties
;
533 m_descent
= obj
.m_descent
;
537 // Get/set the top-level container of this object.
538 wxRichTextParagraphLayoutBox
* wxRichTextObject::GetContainer() const
540 const wxRichTextObject
* p
= this;
545 return wxDynamicCast(p
, wxRichTextParagraphLayoutBox
);
552 void wxRichTextObject::SetMargins(int margin
)
554 SetMargins(margin
, margin
, margin
, margin
);
557 void wxRichTextObject::SetMargins(int leftMargin
, int rightMargin
, int topMargin
, int bottomMargin
)
559 GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().SetValue(leftMargin
, wxTEXT_ATTR_UNITS_PIXELS
);
560 GetAttributes().GetTextBoxAttr().GetMargins().GetRight().SetValue(rightMargin
, wxTEXT_ATTR_UNITS_PIXELS
);
561 GetAttributes().GetTextBoxAttr().GetMargins().GetTop().SetValue(topMargin
, wxTEXT_ATTR_UNITS_PIXELS
);
562 GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().SetValue(bottomMargin
, wxTEXT_ATTR_UNITS_PIXELS
);
565 int wxRichTextObject::GetLeftMargin() const
567 return GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().GetValue();
570 int wxRichTextObject::GetRightMargin() const
572 return GetAttributes().GetTextBoxAttr().GetMargins().GetRight().GetValue();
575 int wxRichTextObject::GetTopMargin() const
577 return GetAttributes().GetTextBoxAttr().GetMargins().GetTop().GetValue();
580 int wxRichTextObject::GetBottomMargin() const
582 return GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().GetValue();
585 // Calculate the available content space in the given rectangle, given the
586 // margins, border and padding specified in the object's attributes.
587 wxRect
wxRichTextObject::GetAvailableContentArea(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& outerRect
) const
589 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
590 marginRect
= outerRect
;
591 wxRichTextAttr
attr(GetAttributes());
592 context
.ApplyVirtualAttributes(attr
, (wxRichTextObject
*) this);
593 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
597 // Invalidate the buffer. With no argument, invalidates whole buffer.
598 void wxRichTextObject::Invalidate(const wxRichTextRange
& invalidRange
)
600 if (invalidRange
!= wxRICHTEXT_NONE
)
602 // If this is a floating object, size may not be recalculated
603 // after floats have been collected in an early stage of Layout.
604 // So avoid resetting the cache for floating objects during layout.
605 if (!IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode())
606 SetCachedSize(wxDefaultSize
);
607 SetMaxSize(wxDefaultSize
);
608 SetMinSize(wxDefaultSize
);
612 // Convert units in tenths of a millimetre to device units
613 int wxRichTextObject::ConvertTenthsMMToPixels(wxDC
& dc
, int units
) const
618 scale
= GetBuffer()->GetScale() / GetBuffer()->GetDimensionScale();
619 int p
= ConvertTenthsMMToPixels(dc
.GetPPI().x
, units
, scale
);
624 // Convert units in tenths of a millimetre to device units
625 int wxRichTextObject::ConvertTenthsMMToPixels(int ppi
, int units
, double scale
)
627 // There are ppi pixels in 254.1 "1/10 mm"
629 double pixels
= ((double) units
* (double)ppi
) / 254.1;
633 // If the result is very small, make it at least one pixel in size.
634 if (pixels
== 0 && units
> 0)
640 // Convert units in pixels to tenths of a millimetre
641 int wxRichTextObject::ConvertPixelsToTenthsMM(wxDC
& dc
, int pixels
) const
646 scale
= GetBuffer()->GetScale();
648 return ConvertPixelsToTenthsMM(dc
.GetPPI().x
, p
, scale
);
651 int wxRichTextObject::ConvertPixelsToTenthsMM(int ppi
, int pixels
, double scale
)
653 // There are ppi pixels in 254.1 "1/10 mm"
655 double p
= double(pixels
);
660 int units
= int( p
* 254.1 / (double) ppi
);
664 // Draw the borders and background for the given rectangle and attributes.
665 // Width and height are taken to be the outer margin size, not the content.
666 bool wxRichTextObject::DrawBoxAttributes(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& attr
, const wxRect
& boxRect
, int flags
)
668 // Assume boxRect is the area around the content
669 wxRect marginRect
= boxRect
;
670 wxRect contentRect
, borderRect
, paddingRect
, outlineRect
;
672 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
674 // Margin is transparent. Draw background from margin.
675 if (attr
.HasBackgroundColour() || (flags
& wxRICHTEXT_DRAW_SELECTED
))
678 if (flags
& wxRICHTEXT_DRAW_SELECTED
)
680 // TODO: get selection colour from control?
681 colour
= wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
);
684 colour
= attr
.GetBackgroundColour();
687 wxBrush
brush(colour
);
691 dc
.DrawRectangle(borderRect
);
694 if (flags
& wxRICHTEXT_DRAW_GUIDELINES
)
696 wxRichTextAttr editBorderAttr
= attr
;
697 // TODO: make guideline colour configurable
698 editBorderAttr
.GetTextBoxAttr().GetBorder().SetColour(*wxLIGHT_GREY
);
699 editBorderAttr
.GetTextBoxAttr().GetBorder().SetWidth(1, wxTEXT_ATTR_UNITS_PIXELS
);
700 editBorderAttr
.GetTextBoxAttr().GetBorder().SetStyle(wxTEXT_BOX_ATTR_BORDER_SOLID
);
702 DrawBorder(dc
, buffer
, editBorderAttr
.GetTextBoxAttr().GetBorder(), borderRect
, flags
);
705 if (attr
.GetTextBoxAttr().GetBorder().IsValid())
706 DrawBorder(dc
, buffer
, attr
.GetTextBoxAttr().GetBorder(), borderRect
);
708 if (attr
.GetTextBoxAttr().GetOutline().IsValid())
709 DrawBorder(dc
, buffer
, attr
.GetTextBoxAttr().GetOutline(), outlineRect
);
715 bool wxRichTextObject::DrawBorder(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxTextAttrBorders
& attr
, const wxRect
& rect
, int WXUNUSED(flags
))
717 int borderLeft
= 0, borderRight
= 0, borderTop
= 0, borderBottom
= 0;
718 wxTextAttrDimensionConverter
converter(dc
, buffer
? buffer
->GetScale() : 1.0);
720 if (attr
.GetLeft().IsValid() && attr
.GetLeft().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
722 borderLeft
= converter
.GetPixels(attr
.GetLeft().GetWidth());
723 wxColour
col(attr
.GetLeft().GetColour());
725 // If pen width is > 1, resorts to a solid rectangle.
728 int penStyle
= wxSOLID
;
729 if (attr
.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
731 else if (attr
.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
732 penStyle
= wxLONG_DASH
;
733 wxPen
pen(col
, 1, penStyle
);
735 dc
.DrawLine(rect
.x
, rect
.y
, rect
.x
, rect
.y
+ rect
.height
);
738 else if (borderLeft
> 1)
744 dc
.DrawRectangle(rect
.x
, rect
.y
, borderLeft
, rect
.height
);
748 if (attr
.GetRight().IsValid() && attr
.GetRight().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
750 borderRight
= converter
.GetPixels(attr
.GetRight().GetWidth());
752 wxColour
col(attr
.GetRight().GetColour());
754 // If pen width is > 1, resorts to a solid rectangle.
755 if (borderRight
== 1)
757 int penStyle
= wxSOLID
;
758 if (attr
.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
760 else if (attr
.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
761 penStyle
= wxLONG_DASH
;
762 wxPen
pen(col
, 1, penStyle
);
764 dc
.DrawLine(rect
.x
+ rect
.width
, rect
.y
, rect
.x
+ rect
.width
, rect
.y
+ rect
.height
+ 1);
767 else if (borderRight
> 1)
773 dc
.DrawRectangle(rect
.x
+ rect
.width
- borderRight
, rect
.y
, borderRight
, rect
.height
);
777 if (attr
.GetTop().IsValid() && attr
.GetTop().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
779 borderTop
= converter
.GetPixels(attr
.GetTop().GetWidth());
781 wxColour
col(attr
.GetTop().GetColour());
783 // If pen width is > 1, resorts to a solid rectangle.
786 int penStyle
= wxSOLID
;
787 if (attr
.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
789 else if (attr
.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
790 penStyle
= wxLONG_DASH
;
791 wxPen
pen(col
, 1, penStyle
);
793 dc
.DrawLine(rect
.x
, rect
.y
, rect
.x
+ rect
.width
, rect
.y
);
796 else if (borderTop
> 1)
802 dc
.DrawRectangle(rect
.x
, rect
.y
, rect
.width
, borderTop
);
806 if (attr
.GetBottom().IsValid() && attr
.GetBottom().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
808 borderBottom
= converter
.GetPixels(attr
.GetBottom().GetWidth());
809 wxColour
col(attr
.GetTop().GetColour());
811 // If pen width is > 1, resorts to a solid rectangle.
812 if (borderBottom
== 1)
814 int penStyle
= wxSOLID
;
815 if (attr
.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
817 else if (attr
.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
818 penStyle
= wxLONG_DASH
;
819 wxPen
pen(col
, 1, penStyle
);
821 dc
.DrawLine(rect
.x
, rect
.y
+ rect
.height
, rect
.x
+ rect
.width
, rect
.y
+ rect
.height
);
824 else if (borderBottom
> 1)
830 dc
.DrawRectangle(rect
.x
, rect
.y
+ rect
.height
- borderBottom
, rect
.width
, borderBottom
);
837 // Get the various rectangles of the box model in pixels. You can either specify contentRect (inner)
838 // or marginRect (outer), and the other must be the default rectangle (no width or height).
839 // Note that the outline doesn't affect the position of the rectangle, it's drawn in whatever space
842 // | Margin | Border | Padding | CONTENT | Padding | Border | Margin |
844 bool wxRichTextObject::GetBoxRects(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& attr
, wxRect
& marginRect
, wxRect
& borderRect
, wxRect
& contentRect
, wxRect
& paddingRect
, wxRect
& outlineRect
)
846 int borderLeft
= 0, borderRight
= 0, borderTop
= 0, borderBottom
= 0;
847 int outlineLeft
= 0, outlineRight
= 0, outlineTop
= 0, outlineBottom
= 0;
848 int paddingLeft
= 0, paddingRight
= 0, paddingTop
= 0, paddingBottom
= 0;
849 int marginLeft
= 0, marginRight
= 0, marginTop
= 0, marginBottom
= 0;
851 wxTextAttrDimensionConverter
converter(dc
, buffer
? buffer
->GetScale() : 1.0);
853 if (attr
.GetTextBoxAttr().GetMargins().GetLeft().IsValid())
854 marginLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetLeft());
855 if (attr
.GetTextBoxAttr().GetMargins().GetRight().IsValid())
856 marginRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetRight());
857 if (attr
.GetTextBoxAttr().GetMargins().GetTop().IsValid())
858 marginTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetTop());
859 if (attr
.GetTextBoxAttr().GetMargins().GetBottom().IsValid())
860 marginBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetBottom());
862 if (attr
.GetTextBoxAttr().GetBorder().GetLeft().GetWidth().IsValid())
863 borderLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetLeft().GetWidth());
864 if (attr
.GetTextBoxAttr().GetBorder().GetRight().GetWidth().IsValid())
865 borderRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetRight().GetWidth());
866 if (attr
.GetTextBoxAttr().GetBorder().GetTop().GetWidth().IsValid())
867 borderTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetTop().GetWidth());
868 if (attr
.GetTextBoxAttr().GetBorder().GetBottom().GetWidth().IsValid())
869 borderBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetBottom().GetWidth());
871 if (attr
.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
872 paddingLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetLeft());
873 if (attr
.GetTextBoxAttr().GetPadding().GetRight().IsValid())
874 paddingRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetRight());
875 if (attr
.GetTextBoxAttr().GetPadding().GetTop().IsValid())
876 paddingTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetTop());
877 if (attr
.GetTextBoxAttr().GetPadding().GetBottom().IsValid())
878 paddingBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetBottom());
880 if (attr
.GetTextBoxAttr().GetOutline().GetLeft().GetWidth().IsValid())
881 outlineLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetLeft().GetWidth());
882 if (attr
.GetTextBoxAttr().GetOutline().GetRight().GetWidth().IsValid())
883 outlineRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetRight().GetWidth());
884 if (attr
.GetTextBoxAttr().GetOutline().GetTop().GetWidth().IsValid())
885 outlineTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetTop().GetWidth());
886 if (attr
.GetTextBoxAttr().GetOutline().GetBottom().GetWidth().IsValid())
887 outlineBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetBottom().GetWidth());
889 int leftTotal
= marginLeft
+ borderLeft
+ paddingLeft
;
890 int rightTotal
= marginRight
+ borderRight
+ paddingRight
;
891 int topTotal
= marginTop
+ borderTop
+ paddingTop
;
892 int bottomTotal
= marginBottom
+ borderBottom
+ paddingBottom
;
894 if (marginRect
!= wxRect())
896 contentRect
.x
= marginRect
.x
+ leftTotal
;
897 contentRect
.y
= marginRect
.y
+ topTotal
;
898 contentRect
.width
= marginRect
.width
- (leftTotal
+ rightTotal
);
899 contentRect
.height
= marginRect
.height
- (topTotal
+ bottomTotal
);
903 marginRect
.x
= contentRect
.x
- leftTotal
;
904 marginRect
.y
= contentRect
.y
- topTotal
;
905 marginRect
.width
= contentRect
.width
+ (leftTotal
+ rightTotal
);
906 marginRect
.height
= contentRect
.height
+ (topTotal
+ bottomTotal
);
909 borderRect
.x
= marginRect
.x
+ marginLeft
;
910 borderRect
.y
= marginRect
.y
+ marginTop
;
911 borderRect
.width
= marginRect
.width
- (marginLeft
+ marginRight
);
912 borderRect
.height
= marginRect
.height
- (marginTop
+ marginBottom
);
914 paddingRect
.x
= marginRect
.x
+ marginLeft
+ borderLeft
;
915 paddingRect
.y
= marginRect
.y
+ marginTop
+ borderTop
;
916 paddingRect
.width
= marginRect
.width
- (marginLeft
+ marginRight
+ borderLeft
+ borderRight
);
917 paddingRect
.height
= marginRect
.height
- (marginTop
+ marginBottom
+ borderTop
+ borderBottom
);
919 // The outline is outside the margin and doesn't influence the overall box position or content size.
920 outlineRect
.x
= marginRect
.x
- outlineLeft
;
921 outlineRect
.y
= marginRect
.y
- outlineTop
;
922 outlineRect
.width
= marginRect
.width
+ (outlineLeft
+ outlineRight
);
923 outlineRect
.height
= marginRect
.height
+ (outlineTop
+ outlineBottom
);
928 // Get the total margin for the object in pixels, taking into account margin, padding and border size
929 bool wxRichTextObject::GetTotalMargin(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& attr
, int& leftMargin
, int& rightMargin
,
930 int& topMargin
, int& bottomMargin
)
932 // Assume boxRect is the area around the content
933 wxRect contentRect
, marginRect
, borderRect
, paddingRect
, outlineRect
;
934 marginRect
= wxRect(0, 0, 1000, 1000);
936 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
938 leftMargin
= contentRect
.GetLeft() - marginRect
.GetLeft();
939 rightMargin
= marginRect
.GetRight() - contentRect
.GetRight();
940 topMargin
= contentRect
.GetTop() - marginRect
.GetTop();
941 bottomMargin
= marginRect
.GetBottom() - contentRect
.GetBottom();
946 // Returns the rectangle which the child has available to it given restrictions specified in the
947 // child attribute, e.g. 50% width of the parent, 400 pixels, x position 20% of the parent, etc.
948 // availableContainerSpace might be a parent that the cell has to compute its width relative to.
949 // E.g. a cell that's 50% of its parent.
950 wxRect
wxRichTextObject::AdjustAvailableSpace(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& WXUNUSED(parentAttr
), const wxRichTextAttr
& childAttr
, const wxRect
& availableParentSpace
, const wxRect
& availableContainerSpace
)
952 wxRect rect
= availableParentSpace
;
955 scale
= buffer
->GetScale();
957 wxTextAttrDimensionConverter
converter(dc
, scale
, availableContainerSpace
.GetSize());
959 if (childAttr
.GetTextBoxAttr().GetWidth().IsValid())
960 rect
.width
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetWidth());
962 if (childAttr
.GetTextBoxAttr().GetHeight().IsValid())
963 rect
.height
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetHeight());
965 // Can specify either left or right for the position (we're assuming we can't
966 // set the left and right edges to effectively set the size. Would we want to do that?)
967 if (childAttr
.GetTextBoxAttr().GetPosition().GetLeft().IsValid())
969 rect
.x
= rect
.x
+ converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetLeft());
971 else if (childAttr
.GetTextBoxAttr().GetPosition().GetRight().IsValid())
973 int x
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetRight());
974 if (childAttr
.GetTextBoxAttr().GetPosition().GetRight().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE
)
975 rect
.x
= availableContainerSpace
.x
+ availableContainerSpace
.width
- rect
.width
;
980 if (childAttr
.GetTextBoxAttr().GetPosition().GetTop().IsValid())
982 rect
.y
= rect
.y
+ converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetTop());
984 else if (childAttr
.GetTextBoxAttr().GetPosition().GetBottom().IsValid())
986 int y
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetBottom());
987 if (childAttr
.GetTextBoxAttr().GetPosition().GetBottom().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE
)
988 rect
.y
= availableContainerSpace
.y
+ availableContainerSpace
.height
- rect
.height
;
993 if (rect
.GetWidth() > availableParentSpace
.GetWidth())
994 rect
.SetWidth(availableParentSpace
.GetWidth());
999 // Dump to output stream for debugging
1000 void wxRichTextObject::Dump(wxTextOutputStream
& stream
)
1002 stream
<< GetClassInfo()->GetClassName() << wxT("\n");
1003 stream
<< wxString::Format(wxT("Size: %d,%d. Position: %d,%d, Range: %ld,%ld"), m_size
.x
, m_size
.y
, m_pos
.x
, m_pos
.y
, m_range
.GetStart(), m_range
.GetEnd()) << wxT("\n");
1004 stream
<< wxString::Format(wxT("Text colour: %d,%d,%d."), (int) m_attributes
.GetTextColour().Red(), (int) m_attributes
.GetTextColour().Green(), (int) m_attributes
.GetTextColour().Blue()) << wxT("\n");
1007 // Gets the containing buffer
1008 wxRichTextBuffer
* wxRichTextObject::GetBuffer() const
1010 const wxRichTextObject
* obj
= this;
1011 while (obj
&& !wxDynamicCast(obj
, wxRichTextBuffer
))
1012 obj
= obj
->GetParent();
1013 return wxDynamicCast(obj
, wxRichTextBuffer
);
1016 // Get the absolute object position, by traversing up the child/parent hierarchy
1017 wxPoint
wxRichTextObject::GetAbsolutePosition() const
1019 wxPoint pt
= GetPosition();
1021 wxRichTextObject
* p
= GetParent();
1024 pt
= pt
+ p
->GetPosition();
1031 // Hit-testing: returns a flag indicating hit test details, plus
1032 // information about position
1033 int wxRichTextObject::HitTest(wxDC
& WXUNUSED(dc
), wxRichTextDrawingContext
& WXUNUSED(context
), const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int WXUNUSED(flags
))
1036 return wxRICHTEXT_HITTEST_NONE
;
1038 wxRect rect
= GetRect();
1039 if (pt
.x
>= rect
.x
&& pt
.x
< rect
.x
+ rect
.width
&&
1040 pt
.y
>= rect
.y
&& pt
.y
< rect
.y
+ rect
.height
)
1043 *contextObj
= GetParentContainer();
1044 textPosition
= GetRange().GetStart();
1045 return wxRICHTEXT_HITTEST_ON
;
1048 return wxRICHTEXT_HITTEST_NONE
;
1051 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1052 // lays out the object again using the maximum ('best') size
1053 bool wxRichTextObject::LayoutToBestSize(wxDC
& dc
, wxRichTextDrawingContext
& context
, wxRichTextBuffer
* buffer
,
1054 const wxRichTextAttr
& parentAttr
, const wxRichTextAttr
& attr
,
1055 const wxRect
& availableParentSpace
, const wxRect
& availableContainerSpace
,
1058 wxRect availableChildRect
= AdjustAvailableSpace(dc
, buffer
, parentAttr
, attr
, availableParentSpace
, availableContainerSpace
);
1059 wxRect originalAvailableRect
= availableChildRect
;
1060 Layout(dc
, context
, availableChildRect
, availableContainerSpace
, style
);
1062 wxSize maxSize
= GetMaxSize();
1064 // Don't ignore if maxSize.x is zero, since we need to redo the paragraph's lines
1066 if (!attr
.GetTextBoxAttr().GetWidth().IsValid() && maxSize
.x
< availableChildRect
.width
)
1068 // Redo the layout with a fixed, minimum size this time.
1069 Invalidate(wxRICHTEXT_ALL
);
1070 wxRichTextAttr
newAttr(attr
);
1071 newAttr
.GetTextBoxAttr().GetWidth().SetValue(maxSize
.x
, wxTEXT_ATTR_UNITS_PIXELS
);
1072 newAttr
.GetTextBoxAttr().GetWidth().SetPosition(wxTEXT_BOX_ATTR_POSITION_ABSOLUTE
);
1074 availableChildRect
= AdjustAvailableSpace(dc
, buffer
, parentAttr
, newAttr
, availableParentSpace
, availableContainerSpace
);
1076 // If a paragraph, align the whole paragraph.
1077 // Problem with this: if we're limited by a floating object, a line may be centered
1078 // w.r.t. the smaller resulting box rather than the actual available width.
1079 // FIXME: aligning whole paragraph not compatible with floating objects
1080 if (attr
.HasAlignment() && (!wxRichTextBuffer::GetFloatingLayoutMode() || (GetContainer()->GetFloatCollector() && !GetContainer()->GetFloatCollector()->HasFloats())))
1082 // centering, right-justification
1083 if (attr
.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE
)
1085 availableChildRect
.x
= (originalAvailableRect
.GetWidth() - availableChildRect
.GetWidth())/2 + availableChildRect
.x
;
1087 else if (attr
.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT
)
1089 availableChildRect
.x
= availableChildRect
.x
+ originalAvailableRect
.GetWidth() - availableChildRect
.GetWidth();
1093 Layout(dc
, context
, availableChildRect
, availableContainerSpace
, style
);
1107 // Move the object recursively, by adding the offset from old to new
1108 void wxRichTextObject::Move(const wxPoint
& pt
)
1115 * wxRichTextCompositeObject
1116 * This is the base for drawable objects.
1119 IMPLEMENT_CLASS(wxRichTextCompositeObject
, wxRichTextObject
)
1121 wxRichTextCompositeObject::wxRichTextCompositeObject(wxRichTextObject
* parent
):
1122 wxRichTextObject(parent
)
1126 wxRichTextCompositeObject::~wxRichTextCompositeObject()
1131 /// Get the nth child
1132 wxRichTextObject
* wxRichTextCompositeObject::GetChild(size_t n
) const
1134 wxASSERT ( n
< m_children
.GetCount() );
1136 return m_children
.Item(n
)->GetData();
1139 /// Append a child, returning the position
1140 size_t wxRichTextCompositeObject::AppendChild(wxRichTextObject
* child
)
1142 m_children
.Append(child
);
1143 child
->SetParent(this);
1144 return m_children
.GetCount() - 1;
1147 /// Insert the child in front of the given object, or at the beginning
1148 bool wxRichTextCompositeObject::InsertChild(wxRichTextObject
* child
, wxRichTextObject
* inFrontOf
)
1152 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(inFrontOf
);
1153 m_children
.Insert(node
, child
);
1156 m_children
.Insert(child
);
1157 child
->SetParent(this);
1162 /// Delete the child
1163 bool wxRichTextCompositeObject::RemoveChild(wxRichTextObject
* child
, bool deleteChild
)
1165 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(child
);
1168 wxRichTextObject
* obj
= node
->GetData();
1169 m_children
.Erase(node
);
1178 /// Delete all children
1179 bool wxRichTextCompositeObject::DeleteChildren()
1181 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1184 wxRichTextObjectList::compatibility_iterator oldNode
= node
;
1186 wxRichTextObject
* child
= node
->GetData();
1187 child
->Dereference(); // Only delete if reference count is zero
1189 node
= node
->GetNext();
1190 m_children
.Erase(oldNode
);
1196 /// Get the child count
1197 size_t wxRichTextCompositeObject::GetChildCount() const
1199 return m_children
.GetCount();
1203 void wxRichTextCompositeObject::Copy(const wxRichTextCompositeObject
& obj
)
1205 wxRichTextObject::Copy(obj
);
1209 wxRichTextObjectList::compatibility_iterator node
= obj
.m_children
.GetFirst();
1212 wxRichTextObject
* child
= node
->GetData();
1213 wxRichTextObject
* newChild
= child
->Clone();
1214 newChild
->SetParent(this);
1215 m_children
.Append(newChild
);
1217 node
= node
->GetNext();
1221 /// Hit-testing: returns a flag indicating hit test details, plus
1222 /// information about position
1223 int wxRichTextCompositeObject::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
1226 return wxRICHTEXT_HITTEST_NONE
;
1228 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1231 wxRichTextObject
* child
= node
->GetData();
1233 if (child
->IsShown() && child
->IsTopLevel() && (flags
& wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS
))
1235 // Just check if we hit the overall object
1236 int ret
= child
->wxRichTextObject::HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
1237 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
1240 else if (child
->IsShown())
1242 int ret
= child
->HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
1243 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
1247 node
= node
->GetNext();
1250 return wxRICHTEXT_HITTEST_NONE
;
1253 /// Finds the absolute position and row height for the given character position
1254 bool wxRichTextCompositeObject::FindPosition(wxDC
& dc
, wxRichTextDrawingContext
& context
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
1256 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1259 wxRichTextObject
* child
= node
->GetData();
1261 // Don't recurse if the child is a top-level object,
1262 // such as a text box, because the character position will no longer
1263 // apply. By definition, a top-level object has its own range of
1264 // character positions.
1265 if (!child
->IsTopLevel() && child
->FindPosition(dc
, context
, index
, pt
, height
, forceLineStart
))
1268 node
= node
->GetNext();
1275 void wxRichTextCompositeObject::CalculateRange(long start
, long& end
)
1277 long current
= start
;
1278 long lastEnd
= current
;
1286 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1289 wxRichTextObject
* child
= node
->GetData();
1292 child
->CalculateRange(current
, childEnd
);
1295 current
= childEnd
+ 1;
1297 node
= node
->GetNext();
1302 // A top-level object always has a range of size 1,
1303 // because its children don't count at this level.
1305 m_range
.SetRange(start
, start
);
1307 // An object with no children has zero length
1308 if (m_children
.GetCount() == 0)
1310 m_ownRange
.SetRange(0, lastEnd
);
1316 // An object with no children has zero length
1317 if (m_children
.GetCount() == 0)
1320 m_range
.SetRange(start
, end
);
1324 /// Delete range from layout.
1325 bool wxRichTextCompositeObject::DeleteRange(const wxRichTextRange
& range
)
1327 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1331 wxRichTextObject
* obj
= (wxRichTextObject
*) node
->GetData();
1332 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
1334 // Delete the range in each paragraph
1336 // When a chunk has been deleted, internally the content does not
1337 // now match the ranges.
1338 // However, so long as deletion is not done on the same object twice this is OK.
1339 // If you may delete content from the same object twice, recalculate
1340 // the ranges inbetween DeleteRange calls by calling CalculateRanges, and
1341 // adjust the range you're deleting accordingly.
1343 if (!obj
->GetRange().IsOutside(range
))
1345 // No need to delete within a top-level object; just removing this object will do fine
1346 if (!obj
->IsTopLevel())
1347 obj
->DeleteRange(range
);
1349 // Delete an empty object, or paragraph within this range.
1350 if (obj
->IsEmpty() ||
1351 (range
.GetStart() <= obj
->GetRange().GetStart() && range
.GetEnd() >= obj
->GetRange().GetEnd()))
1353 // An empty paragraph has length 1, so won't be deleted unless the
1354 // whole range is deleted.
1355 RemoveChild(obj
, true);
1365 /// Get any text in this object for the given range
1366 wxString
wxRichTextCompositeObject::GetTextForRange(const wxRichTextRange
& range
) const
1369 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1372 wxRichTextObject
* child
= node
->GetData();
1373 wxRichTextRange childRange
= range
;
1374 if (!child
->GetRange().IsOutside(range
))
1376 childRange
.LimitTo(child
->GetRange());
1378 wxString childText
= child
->GetTextForRange(childRange
);
1382 node
= node
->GetNext();
1388 /// Get the child object at the given character position
1389 wxRichTextObject
* wxRichTextCompositeObject::GetChildAtPosition(long pos
) const
1391 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1394 wxRichTextObject
* child
= node
->GetData();
1395 if (child
->GetRange().GetStart() == pos
)
1397 node
= node
->GetNext();
1402 /// Recursively merge all pieces that can be merged.
1403 bool wxRichTextCompositeObject::Defragment(const wxRichTextRange
& range
)
1405 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1408 wxRichTextObject
* child
= node
->GetData();
1409 if (range
== wxRICHTEXT_ALL
|| !child
->GetRange().IsOutside(range
))
1411 wxRichTextCompositeObject
* composite
= wxDynamicCast(child
, wxRichTextCompositeObject
);
1413 composite
->Defragment();
1415 if (node
->GetNext())
1417 wxRichTextObject
* nextChild
= node
->GetNext()->GetData();
1418 if (child
->CanMerge(nextChild
) && child
->Merge(nextChild
))
1420 nextChild
->Dereference();
1421 m_children
.Erase(node
->GetNext());
1423 // Don't set node -- we'll see if we can merge again with the next
1427 node
= node
->GetNext();
1430 node
= node
->GetNext();
1433 node
= node
->GetNext();
1436 // Delete any remaining empty objects, but leave at least one empty object per composite object.
1437 if (GetChildCount() > 1)
1439 node
= m_children
.GetFirst();
1442 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
1443 wxRichTextObject
* child
= node
->GetData();
1444 if (range
== wxRICHTEXT_ALL
|| !child
->GetRange().IsOutside(range
))
1446 if (child
->IsEmpty())
1448 child
->Dereference();
1449 m_children
.Erase(node
);
1454 node
= node
->GetNext();
1461 /// Dump to output stream for debugging
1462 void wxRichTextCompositeObject::Dump(wxTextOutputStream
& stream
)
1464 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1467 wxRichTextObject
* child
= node
->GetData();
1468 child
->Dump(stream
);
1469 node
= node
->GetNext();
1473 /// Get/set the object size for the given range. Returns false if the range
1474 /// is invalid for this object.
1475 bool wxRichTextCompositeObject::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
1477 if (!range
.IsWithin(GetRange()))
1482 wxArrayInt childExtents
;
1489 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1492 wxRichTextObject
* child
= node
->GetData();
1493 if (!child
->GetRange().IsOutside(range
))
1495 // Floating objects have a zero size within the paragraph.
1496 if (child
->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode())
1501 if (partialExtents
->GetCount() > 0)
1502 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
1506 partialExtents
->Add(0 /* zero size */ + lastSize
);
1513 wxRichTextRange rangeToUse
= range
;
1514 rangeToUse
.LimitTo(child
->GetRange());
1515 if (child
->IsTopLevel())
1516 rangeToUse
= child
->GetOwnRange();
1518 int childDescent
= 0;
1520 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we're already cached the size,
1521 // but it's only going to be used after caching has taken place.
1522 if ((flags
& wxRICHTEXT_HEIGHT_ONLY
) && child
->GetCachedSize().y
!= 0)
1524 childDescent
= child
->GetDescent();
1525 childSize
= child
->GetCachedSize();
1527 sz
.y
= wxMax(sz
.y
, childSize
.y
);
1528 sz
.x
+= childSize
.x
;
1529 descent
= wxMax(descent
, childDescent
);
1531 else if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, context
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
), p
))
1533 sz
.y
= wxMax(sz
.y
, childSize
.y
);
1534 sz
.x
+= childSize
.x
;
1535 descent
= wxMax(descent
, childDescent
);
1537 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange() || child
->IsTopLevel()))
1539 child
->SetCachedSize(childSize
);
1540 child
->SetDescent(childDescent
);
1546 if (partialExtents
->GetCount() > 0)
1547 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
1552 for (i
= 0; i
< childExtents
.GetCount(); i
++)
1554 partialExtents
->Add(childExtents
[i
] + lastSize
);
1564 node
= node
->GetNext();
1570 // Invalidate the buffer. With no argument, invalidates whole buffer.
1571 void wxRichTextCompositeObject::Invalidate(const wxRichTextRange
& invalidRange
)
1573 wxRichTextObject::Invalidate(invalidRange
);
1575 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1578 wxRichTextObject
* child
= node
->GetData();
1579 if (invalidRange
!= wxRICHTEXT_ALL
&& invalidRange
!= wxRICHTEXT_NONE
&& child
->GetRange().IsOutside(invalidRange
))
1583 else if (child
->IsTopLevel())
1585 if (wxRichTextBuffer::GetFloatingLayoutMode() && child
->IsFloating() && GetBuffer()->GetFloatCollector() && GetBuffer()->GetFloatCollector()->HasFloat(child
))
1587 // Don't invalidate subhierarchy if we've already been laid out
1591 if (invalidRange
== wxRICHTEXT_NONE
)
1592 child
->Invalidate(wxRICHTEXT_NONE
);
1594 child
->Invalidate(wxRICHTEXT_ALL
); // All children must be invalidated if within parent range
1598 child
->Invalidate(invalidRange
);
1599 node
= node
->GetNext();
1603 // Move the object recursively, by adding the offset from old to new
1604 void wxRichTextCompositeObject::Move(const wxPoint
& pt
)
1606 wxPoint oldPos
= GetPosition();
1608 wxPoint offset
= pt
- oldPos
;
1610 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1613 wxRichTextObject
* child
= node
->GetData();
1614 wxPoint childPos
= child
->GetPosition() + offset
;
1615 child
->Move(childPos
);
1616 node
= node
->GetNext();
1622 * wxRichTextParagraphLayoutBox
1623 * This box knows how to lay out paragraphs.
1626 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraphLayoutBox
, wxRichTextCompositeObject
)
1628 wxRichTextParagraphLayoutBox::wxRichTextParagraphLayoutBox(wxRichTextObject
* parent
):
1629 wxRichTextCompositeObject(parent
)
1634 wxRichTextParagraphLayoutBox::~wxRichTextParagraphLayoutBox()
1636 if (m_floatCollector
)
1638 delete m_floatCollector
;
1639 m_floatCollector
= NULL
;
1643 /// Initialize the object.
1644 void wxRichTextParagraphLayoutBox::Init()
1648 // For now, assume is the only box and has no initial size.
1649 m_range
= wxRichTextRange(0, -1);
1650 m_ownRange
= wxRichTextRange(0, -1);
1652 m_invalidRange
= wxRICHTEXT_ALL
;
1654 m_partialParagraph
= false;
1655 m_floatCollector
= NULL
;
1658 void wxRichTextParagraphLayoutBox::Clear()
1662 if (m_floatCollector
)
1663 delete m_floatCollector
;
1664 m_floatCollector
= NULL
;
1665 m_partialParagraph
= false;
1669 void wxRichTextParagraphLayoutBox::Copy(const wxRichTextParagraphLayoutBox
& obj
)
1673 wxRichTextCompositeObject::Copy(obj
);
1675 m_partialParagraph
= obj
.m_partialParagraph
;
1676 m_defaultAttributes
= obj
.m_defaultAttributes
;
1679 // Gather information about floating objects; only gather floats for those paragraphs that
1680 // will not be formatted again due to optimization, after which floats will be gathered per-paragraph
1682 bool wxRichTextParagraphLayoutBox::UpdateFloatingObjects(const wxRect
& availableRect
, wxRichTextObject
* untilObj
)
1684 if (m_floatCollector
!= NULL
)
1685 delete m_floatCollector
;
1686 m_floatCollector
= new wxRichTextFloatCollector(availableRect
);
1687 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1688 // Only gather floats up to the point we'll start formatting paragraphs.
1689 while (untilObj
&& node
&& node
->GetData() != untilObj
)
1691 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
1692 wxASSERT (child
!= NULL
);
1694 m_floatCollector
->CollectFloat(child
);
1695 node
= node
->GetNext();
1701 // Returns the style sheet associated with the overall buffer.
1702 wxRichTextStyleSheet
* wxRichTextParagraphLayoutBox::GetStyleSheet() const
1704 return GetBuffer() ? GetBuffer()->GetStyleSheet() : (wxRichTextStyleSheet
*) NULL
;
1707 // Get the number of floating objects at this level
1708 int wxRichTextParagraphLayoutBox::GetFloatingObjectCount() const
1710 if (m_floatCollector
)
1711 return m_floatCollector
->GetFloatingObjectCount();
1716 // Get a list of floating objects
1717 bool wxRichTextParagraphLayoutBox::GetFloatingObjects(wxRichTextObjectList
& objects
) const
1719 if (m_floatCollector
)
1721 return m_floatCollector
->GetFloatingObjects(objects
);
1728 void wxRichTextParagraphLayoutBox::UpdateRanges()
1732 start
= GetRange().GetStart();
1734 CalculateRange(start
, end
);
1738 int wxRichTextParagraphLayoutBox::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
1741 return wxRICHTEXT_HITTEST_NONE
;
1743 int ret
= wxRICHTEXT_HITTEST_NONE
;
1744 if (wxRichTextBuffer::GetFloatingLayoutMode() && m_floatCollector
&& (flags
& wxRICHTEXT_HITTEST_NO_FLOATING_OBJECTS
) == 0)
1745 ret
= m_floatCollector
->HitTest(dc
, context
, pt
, textPosition
, obj
, flags
);
1747 if (ret
== wxRICHTEXT_HITTEST_NONE
)
1748 return wxRichTextCompositeObject::HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
1756 /// Draw the floating objects
1757 void wxRichTextParagraphLayoutBox::DrawFloats(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
1759 if (wxRichTextBuffer::GetFloatingLayoutMode() && m_floatCollector
)
1760 m_floatCollector
->Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
1763 void wxRichTextParagraphLayoutBox::MoveAnchoredObjectToParagraph(wxRichTextParagraph
* from
, wxRichTextParagraph
* to
, wxRichTextObject
* obj
)
1768 from
->RemoveChild(obj
);
1769 to
->AppendChild(obj
);
1773 bool wxRichTextParagraphLayoutBox::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
1778 wxRect
thisRect(GetPosition(), GetCachedSize());
1780 wxRichTextAttr
attr(GetAttributes());
1781 context
.ApplyVirtualAttributes(attr
, this);
1784 if (selection
.IsValid() && GetParentContainer() != this && selection
.WithinSelection(GetRange().GetStart(), GetParentContainer()))
1785 flags
|= wxRICHTEXT_DRAW_SELECTED
;
1787 // Don't draw guidelines if at top level
1788 int theseFlags
= flags
;
1790 theseFlags
&= ~wxRICHTEXT_DRAW_GUIDELINES
;
1791 DrawBoxAttributes(dc
, GetBuffer(), attr
, thisRect
, theseFlags
);
1793 if (wxRichTextBuffer::GetFloatingLayoutMode())
1794 DrawFloats(dc
, context
, range
, selection
, rect
, descent
, style
);
1796 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1799 wxRichTextObject
* child
= node
->GetData();
1801 if (child
&& !child
->GetRange().IsOutside(range
))
1803 wxRect
childRect(child
->GetPosition(), child
->GetCachedSize());
1804 wxRichTextRange childRange
= range
;
1805 if (child
->IsTopLevel())
1807 childRange
= child
->GetOwnRange();
1810 if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) == 0) && childRect
.GetTop() > rect
.GetBottom())
1815 else if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) == 0) && childRect
.GetBottom() < rect
.GetTop())
1820 child
->Draw(dc
, context
, childRange
, selection
, rect
, descent
, style
);
1823 node
= node
->GetNext();
1828 /// Lay the item out
1829 bool wxRichTextParagraphLayoutBox::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
)
1831 SetPosition(rect
.GetPosition());
1836 wxRect availableSpace
;
1837 bool formatRect
= (style
& wxRICHTEXT_LAYOUT_SPECIFIED_RECT
) == wxRICHTEXT_LAYOUT_SPECIFIED_RECT
;
1839 wxRichTextAttr
attr(GetAttributes());
1840 context
.ApplyVirtualAttributes(attr
, this);
1842 // If only laying out a specific area, the passed rect has a different meaning:
1843 // the visible part of the buffer. This is used in wxRichTextCtrl::OnSize,
1844 // so that during a size, only the visible part will be relaid out, or
1845 // it would take too long causing flicker. As an approximation, we assume that
1846 // everything up to the start of the visible area is laid out correctly.
1849 wxRect
rect2(0, 0, rect
.width
, rect
.height
);
1850 availableSpace
= GetAvailableContentArea(dc
, context
, rect2
);
1852 // Invalidate the part of the buffer from the first visible line
1853 // to the end. If other parts of the buffer are currently invalid,
1854 // then they too will be taken into account if they are above
1855 // the visible point.
1857 wxRichTextLine
* line
= GetLineAtYPosition(rect
.y
);
1859 startPos
= line
->GetAbsoluteRange().GetStart();
1861 Invalidate(wxRichTextRange(startPos
, GetOwnRange().GetEnd()));
1865 availableSpace
= GetAvailableContentArea(dc
, context
, rect
);
1868 // Fix the width if we're at the top level
1870 attr
.GetTextBoxAttr().GetWidth().SetValue(rect
.GetWidth(), wxTEXT_ATTR_UNITS_PIXELS
);
1872 int leftMargin
, rightMargin
, topMargin
, bottomMargin
;
1873 wxRichTextObject::GetTotalMargin(dc
, GetBuffer(), attr
, leftMargin
, rightMargin
,
1874 topMargin
, bottomMargin
);
1879 // The maximum paragraph maximum width, so we can set the overall maximum width for this object
1880 int maxMaxWidth
= 0;
1882 // The maximum paragraph minimum width, so we can set the overall minimum width for this object
1883 int maxMinWidth
= 0;
1885 // If we have vertical alignment, we must recalculate everything.
1886 bool hasVerticalAlignment
= (attr
.GetTextBoxAttr().HasVerticalAlignment() &&
1887 (attr
.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP
));
1889 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1891 bool layoutAll
= true;
1893 // Get invalid range, rounding to paragraph start/end.
1894 wxRichTextRange invalidRange
= GetInvalidRange(true);
1896 if (invalidRange
== wxRICHTEXT_NONE
&& !formatRect
)
1899 if (invalidRange
== wxRICHTEXT_ALL
|| hasVerticalAlignment
)
1901 else // If we know what range is affected, start laying out from that point on.
1902 if (invalidRange
.GetStart() >= GetOwnRange().GetStart())
1904 wxRichTextParagraph
* firstParagraph
= GetParagraphAtPosition(invalidRange
.GetStart());
1907 wxRichTextObjectList::compatibility_iterator firstNode
= m_children
.Find(firstParagraph
);
1908 wxRichTextObjectList::compatibility_iterator previousNode
;
1910 previousNode
= firstNode
->GetPrevious();
1915 wxRichTextParagraph
* previousParagraph
= wxDynamicCast(previousNode
->GetData(), wxRichTextParagraph
);
1916 availableSpace
.y
= previousParagraph
->GetPosition().y
+ previousParagraph
->GetCachedSize().y
;
1919 // Now we're going to start iterating from the first affected paragraph.
1927 // Gather information about only those floating objects that will not be formatted,
1928 // after which floats will be gathered per-paragraph during layout.
1929 if (wxRichTextBuffer::GetFloatingLayoutMode())
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 (wxRichTextBuffer::GetFloatingLayoutMode() && 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
);
2053 // TODO: Make sure the layout box's position reflects
2054 // the position of the children, but without
2055 // breaking layout of a box within a paragraph.
2058 // TODO: (also in para layout) should set the
2059 // object's size to an absolute one if specified,
2060 // but if not specified, calculate it from content.
2062 // We need to add back the margins etc.
2064 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2065 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxWidth
, maxHeight
));
2066 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2067 SetCachedSize(marginRect
.GetSize());
2070 // The maximum size is the greatest of all maximum widths for all paragraphs.
2072 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2073 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxMaxWidth
, maxHeight
)); // Actually max height is a lie, we can't know it
2074 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2075 SetMaxSize(marginRect
.GetSize());
2078 // The minimum size is the greatest of all minimum widths for all paragraphs.
2080 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2081 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxMinWidth
, maxHeight
)); // Actually max height is a lie, we can't know it
2082 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2083 SetMinSize(marginRect
.GetSize());
2086 if (attr
.GetTextBoxAttr().HasVerticalAlignment() &&
2087 (attr
.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP
))
2090 int leftOverSpace
= availableSpace
.height
- topMargin
- bottomMargin
- maxHeight
;
2091 if (leftOverSpace
> 0)
2093 if (attr
.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_CENTRE
)
2095 yOffset
= (leftOverSpace
/2);
2097 else if (attr
.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_BOTTOM
)
2099 yOffset
= leftOverSpace
;
2103 // Move all the children to vertically align the content
2104 // This doesn't take into account floating objects, unfortunately.
2107 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2110 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2112 child
->Move(wxPoint(child
->GetPosition().x
, child
->GetPosition().y
+ yOffset
));
2114 node
= node
->GetNext();
2119 m_invalidRange
= wxRICHTEXT_NONE
;
2124 /// Get/set the size for the given range.
2125 bool wxRichTextParagraphLayoutBox::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* WXUNUSED(partialExtents
)) const
2129 wxRichTextObjectList::compatibility_iterator startPara
= wxRichTextObjectList::compatibility_iterator();
2130 wxRichTextObjectList::compatibility_iterator endPara
= wxRichTextObjectList::compatibility_iterator();
2132 // First find the first paragraph whose starting position is within the range.
2133 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2136 // child is a paragraph
2137 wxRichTextObject
* child
= node
->GetData();
2138 const wxRichTextRange
& r
= child
->GetRange();
2140 if (r
.GetStart() <= range
.GetStart() && r
.GetEnd() >= range
.GetStart())
2146 node
= node
->GetNext();
2149 // Next find the last paragraph containing part of the range
2150 node
= m_children
.GetFirst();
2153 // child is a paragraph
2154 wxRichTextObject
* child
= node
->GetData();
2155 const wxRichTextRange
& r
= child
->GetRange();
2157 if (r
.GetStart() <= range
.GetEnd() && r
.GetEnd() >= range
.GetEnd())
2163 node
= node
->GetNext();
2166 if (!startPara
|| !endPara
)
2169 // Now we can add up the sizes
2170 for (node
= startPara
; node
; node
= node
->GetNext())
2172 // child is a paragraph
2173 wxRichTextObject
* child
= node
->GetData();
2174 const wxRichTextRange
& childRange
= child
->GetRange();
2175 wxRichTextRange rangeToFind
= range
;
2176 rangeToFind
.LimitTo(childRange
);
2178 if (child
->IsTopLevel())
2179 rangeToFind
= child
->GetOwnRange();
2183 int childDescent
= 0;
2184 child
->GetRangeSize(rangeToFind
, childSize
, childDescent
, dc
, context
, flags
, position
);
2186 descent
= wxMax(childDescent
, descent
);
2188 sz
.x
= wxMax(sz
.x
, childSize
.x
);
2189 sz
.y
+= childSize
.y
;
2191 if (node
== endPara
)
2200 /// Get the paragraph at the given position
2201 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphAtPosition(long pos
, bool caretPosition
) const
2206 // First find the first paragraph whose starting position is within the range.
2207 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2210 // child is a paragraph
2211 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2212 // wxASSERT (child != NULL);
2216 // Return first child in buffer if position is -1
2220 if (child
->GetRange().Contains(pos
))
2224 node
= node
->GetNext();
2229 /// Get the line at the given position
2230 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineAtPosition(long pos
, bool caretPosition
) const
2235 // First find the first paragraph whose starting position is within the range.
2236 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2239 wxRichTextObject
* obj
= (wxRichTextObject
*) node
->GetData();
2240 if (obj
->GetRange().Contains(pos
))
2242 // child is a paragraph
2243 wxRichTextParagraph
* child
= wxDynamicCast(obj
, wxRichTextParagraph
);
2244 // wxASSERT (child != NULL);
2248 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2251 wxRichTextLine
* line
= node2
->GetData();
2253 wxRichTextRange range
= line
->GetAbsoluteRange();
2255 if (range
.Contains(pos
) ||
2257 // If the position is end-of-paragraph, then return the last line of
2258 // of the paragraph.
2259 ((range
.GetEnd() == child
->GetRange().GetEnd()-1) && (pos
== child
->GetRange().GetEnd())))
2262 node2
= node2
->GetNext();
2267 node
= node
->GetNext();
2270 int lineCount
= GetLineCount();
2272 return GetLineForVisibleLineNumber(lineCount
-1);
2277 /// Get the line at the given y pixel position, or the last line.
2278 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineAtYPosition(int y
) const
2280 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2283 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2284 // wxASSERT (child != NULL);
2288 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2291 wxRichTextLine
* line
= node2
->GetData();
2293 wxRect
rect(line
->GetRect());
2295 if (y
<= rect
.GetBottom())
2298 node2
= node2
->GetNext();
2302 node
= node
->GetNext();
2306 int lineCount
= GetLineCount();
2308 return GetLineForVisibleLineNumber(lineCount
-1);
2313 /// Get the number of visible lines
2314 int wxRichTextParagraphLayoutBox::GetLineCount() const
2318 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2321 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2322 // wxASSERT (child != NULL);
2325 count
+= child
->GetLines().GetCount();
2327 node
= node
->GetNext();
2333 /// Get the paragraph for a given line
2334 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphForLine(wxRichTextLine
* line
) const
2336 return GetParagraphAtPosition(line
->GetAbsoluteRange().GetStart());
2339 /// Get the line size at the given position
2340 wxSize
wxRichTextParagraphLayoutBox::GetLineSizeAtPosition(long pos
, bool caretPosition
) const
2342 wxRichTextLine
* line
= GetLineAtPosition(pos
, caretPosition
);
2345 return line
->GetSize();
2348 return wxSize(0, 0);
2352 /// Convenience function to add a paragraph of text
2353 wxRichTextRange
wxRichTextParagraphLayoutBox::AddParagraph(const wxString
& text
, wxRichTextAttr
* paraStyle
)
2355 // Don't use the base style, just the default style, and the base style will
2356 // be combined at display time.
2357 // Divide into paragraph and character styles.
2359 wxRichTextAttr defaultCharStyle
;
2360 wxRichTextAttr defaultParaStyle
;
2362 // If the default style is a named paragraph style, don't apply any character formatting
2363 // to the initial text string.
2364 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2366 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2368 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2371 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2373 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2374 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2376 wxRichTextParagraph
* para
= new wxRichTextParagraph(text
, this, pStyle
, cStyle
);
2377 para
->GetAttributes().GetTextBoxAttr().Reset();
2383 return para
->GetRange();
2386 /// Adds multiple paragraphs, based on newlines.
2387 wxRichTextRange
wxRichTextParagraphLayoutBox::AddParagraphs(const wxString
& text
, wxRichTextAttr
* paraStyle
)
2389 // Don't use the base style, just the default style, and the base style will
2390 // be combined at display time.
2391 // Divide into paragraph and character styles.
2393 wxRichTextAttr defaultCharStyle
;
2394 wxRichTextAttr defaultParaStyle
;
2396 // If the default style is a named paragraph style, don't apply any character formatting
2397 // to the initial text string.
2398 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2400 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2402 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2405 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2407 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2408 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2410 wxRichTextParagraph
* firstPara
= NULL
;
2411 wxRichTextParagraph
* lastPara
= NULL
;
2413 wxRichTextRange
range(-1, -1);
2416 size_t len
= text
.length();
2418 wxRichTextParagraph
* para
= new wxRichTextParagraph(wxEmptyString
, this, pStyle
, cStyle
);
2419 para
->GetAttributes().GetTextBoxAttr().Reset();
2428 wxChar ch
= text
[i
];
2429 if (ch
== wxT('\n') || ch
== wxT('\r'))
2433 wxRichTextPlainText
* plainText
= (wxRichTextPlainText
*) para
->GetChildren().GetFirst()->GetData();
2434 plainText
->SetText(line
);
2436 para
= new wxRichTextParagraph(wxEmptyString
, this, pStyle
, cStyle
);
2437 para
->GetAttributes().GetTextBoxAttr().Reset();
2442 line
= wxEmptyString
;
2453 wxRichTextPlainText
* plainText
= (wxRichTextPlainText
*) para
->GetChildren().GetFirst()->GetData();
2454 plainText
->SetText(line
);
2459 return wxRichTextRange(firstPara
->GetRange().GetStart(), lastPara
->GetRange().GetEnd());
2462 /// Convenience function to add an image
2463 wxRichTextRange
wxRichTextParagraphLayoutBox::AddImage(const wxImage
& image
, wxRichTextAttr
* paraStyle
)
2465 // Don't use the base style, just the default style, and the base style will
2466 // be combined at display time.
2467 // Divide into paragraph and character styles.
2469 wxRichTextAttr defaultCharStyle
;
2470 wxRichTextAttr defaultParaStyle
;
2472 // If the default style is a named paragraph style, don't apply any character formatting
2473 // to the initial text string.
2474 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2476 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2478 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2481 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2483 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2484 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2486 wxRichTextParagraph
* para
= new wxRichTextParagraph(this, pStyle
);
2487 para
->GetAttributes().GetTextBoxAttr().Reset();
2489 para
->AppendChild(new wxRichTextImage(image
, this, cStyle
));
2493 return para
->GetRange();
2497 /// Insert fragment into this box at the given position. If partialParagraph is true,
2498 /// it is assumed that the last (or only) paragraph is just a piece of data with no paragraph
2501 bool wxRichTextParagraphLayoutBox::InsertFragment(long position
, wxRichTextParagraphLayoutBox
& fragment
)
2503 // First, find the first paragraph whose starting position is within the range.
2504 wxRichTextParagraph
* para
= GetParagraphAtPosition(position
);
2507 wxRichTextAttr originalAttr
= para
->GetAttributes();
2509 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(para
);
2511 // Now split at this position, returning the object to insert the new
2512 // ones in front of.
2513 wxRichTextObject
* nextObject
= para
->SplitAt(position
);
2515 // Special case: partial paragraph, just one paragraph. Might be a small amount of
2516 // text, for example, so let's optimize.
2518 if (fragment
.GetPartialParagraph() && fragment
.GetChildren().GetCount() == 1)
2520 // Add the first para to this para...
2521 wxRichTextObjectList::compatibility_iterator firstParaNode
= fragment
.GetChildren().GetFirst();
2525 // Iterate through the fragment paragraph inserting the content into this paragraph.
2526 wxRichTextParagraph
* firstPara
= wxDynamicCast(firstParaNode
->GetData(), wxRichTextParagraph
);
2527 wxASSERT (firstPara
!= NULL
);
2529 wxRichTextObjectList::compatibility_iterator objectNode
= firstPara
->GetChildren().GetFirst();
2532 wxRichTextObject
* newObj
= objectNode
->GetData()->Clone();
2537 para
->AppendChild(newObj
);
2541 // Insert before nextObject
2542 para
->InsertChild(newObj
, nextObject
);
2545 objectNode
= objectNode
->GetNext();
2552 // Procedure for inserting a fragment consisting of a number of
2555 // 1. Remove and save the content that's after the insertion point, for adding
2556 // back once we've added the fragment.
2557 // 2. Add the content from the first fragment paragraph to the current
2559 // 3. Add remaining fragment paragraphs after the current paragraph.
2560 // 4. Add back the saved content from the first paragraph. If partialParagraph
2561 // is true, add it to the last paragraph added and not a new one.
2563 // 1. Remove and save objects after split point.
2564 wxList savedObjects
;
2566 para
->MoveToList(nextObject
, savedObjects
);
2568 // 2. Add the content from the 1st fragment paragraph.
2569 wxRichTextObjectList::compatibility_iterator firstParaNode
= fragment
.GetChildren().GetFirst();
2573 wxRichTextParagraph
* firstPara
= wxDynamicCast(firstParaNode
->GetData(), wxRichTextParagraph
);
2574 wxASSERT(firstPara
!= NULL
);
2576 if (!(fragment
.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
))
2577 para
->SetAttributes(firstPara
->GetAttributes());
2579 // Save empty paragraph attributes for appending later
2580 // These are character attributes deliberately set for a new paragraph. Without this,
2581 // we couldn't pass default attributes when appending a new paragraph.
2582 wxRichTextAttr emptyParagraphAttributes
;
2584 wxRichTextObjectList::compatibility_iterator objectNode
= firstPara
->GetChildren().GetFirst();
2586 if (objectNode
&& firstPara
->GetChildren().GetCount() == 1 && objectNode
->GetData()->IsEmpty())
2587 emptyParagraphAttributes
= objectNode
->GetData()->GetAttributes();
2591 wxRichTextObject
* newObj
= objectNode
->GetData()->Clone();
2594 para
->AppendChild(newObj
);
2596 objectNode
= objectNode
->GetNext();
2599 // 3. Add remaining fragment paragraphs after the current paragraph.
2600 wxRichTextObjectList::compatibility_iterator nextParagraphNode
= node
->GetNext();
2601 wxRichTextObject
* nextParagraph
= NULL
;
2602 if (nextParagraphNode
)
2603 nextParagraph
= nextParagraphNode
->GetData();
2605 wxRichTextObjectList::compatibility_iterator i
= fragment
.GetChildren().GetFirst()->GetNext();
2606 wxRichTextParagraph
* finalPara
= para
;
2608 bool needExtraPara
= (!i
|| !fragment
.GetPartialParagraph());
2610 // If there was only one paragraph, we need to insert a new one.
2613 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2614 wxASSERT( para
!= NULL
);
2616 finalPara
= (wxRichTextParagraph
*) para
->Clone();
2619 InsertChild(finalPara
, nextParagraph
);
2621 AppendChild(finalPara
);
2626 // If there was only one paragraph, or we have full paragraphs in our fragment,
2627 // we need to insert a new one.
2630 finalPara
= new wxRichTextParagraph
;
2633 InsertChild(finalPara
, nextParagraph
);
2635 AppendChild(finalPara
);
2638 // 4. Add back the remaining content.
2642 finalPara
->MoveFromList(savedObjects
);
2644 // Ensure there's at least one object
2645 if (finalPara
->GetChildCount() == 0)
2647 wxRichTextPlainText
* text
= new wxRichTextPlainText(wxEmptyString
);
2648 text
->SetAttributes(emptyParagraphAttributes
);
2650 finalPara
->AppendChild(text
);
2654 if ((fragment
.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
) && firstPara
)
2655 finalPara
->SetAttributes(firstPara
->GetAttributes());
2656 else if (finalPara
&& finalPara
!= para
)
2657 finalPara
->SetAttributes(originalAttr
);
2665 wxRichTextObjectList::compatibility_iterator i
= fragment
.GetChildren().GetFirst();
2668 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2669 wxASSERT( para
!= NULL
);
2671 AppendChild(para
->Clone());
2680 /// Make a copy of the fragment corresponding to the given range, putting it in 'fragment'.
2681 /// If there was an incomplete paragraph at the end, partialParagraph is set to true.
2682 bool wxRichTextParagraphLayoutBox::CopyFragment(const wxRichTextRange
& range
, wxRichTextParagraphLayoutBox
& fragment
)
2684 wxRichTextObjectList::compatibility_iterator i
= GetChildren().GetFirst();
2687 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2688 wxASSERT( para
!= NULL
);
2690 if (!para
->GetRange().IsOutside(range
))
2692 fragment
.AppendChild(para
->Clone());
2697 // Now top and tail the first and last paragraphs in our new fragment (which might be the same).
2698 if (!fragment
.IsEmpty())
2700 wxRichTextParagraph
* firstPara
= wxDynamicCast(fragment
.GetChildren().GetFirst()->GetData(), wxRichTextParagraph
);
2701 wxASSERT( firstPara
!= NULL
);
2703 wxRichTextParagraph
* lastPara
= wxDynamicCast(fragment
.GetChildren().GetLast()->GetData(), wxRichTextParagraph
);
2704 wxASSERT( lastPara
!= NULL
);
2706 if (!firstPara
|| !lastPara
)
2709 bool isFragment
= (range
.GetEnd() < lastPara
->GetRange().GetEnd());
2711 long firstPos
= firstPara
->GetRange().GetStart();
2713 // Adjust for renumbering from zero
2714 wxRichTextRange
topTailRange(range
.GetStart() - firstPos
, range
.GetEnd() - firstPos
);
2717 fragment
.CalculateRange(0, end
);
2719 // Chop off the start of the paragraph
2720 if (topTailRange
.GetStart() > 0)
2722 wxRichTextRange
r(0, topTailRange
.GetStart()-1);
2723 firstPara
->DeleteRange(r
);
2725 // Make sure the numbering is correct
2726 fragment
.CalculateRange(0, end
);
2728 // Now, we've deleted some positions, so adjust the range
2730 topTailRange
.SetStart(range
.GetLength());
2731 topTailRange
.SetEnd(fragment
.GetOwnRange().GetEnd());
2735 topTailRange
.SetStart(range
.GetLength());
2736 topTailRange
.SetEnd(fragment
.GetOwnRange().GetEnd());
2739 if (topTailRange
.GetStart() < lastPara
->GetRange().GetEnd())
2741 lastPara
->DeleteRange(topTailRange
);
2743 // Make sure the numbering is correct
2745 fragment
.CalculateRange(0, end
);
2747 // We only have part of a paragraph at the end
2748 fragment
.SetPartialParagraph(true);
2752 // We have a partial paragraph (don't save last new paragraph marker)
2753 // or complete paragraph
2754 fragment
.SetPartialParagraph(isFragment
);
2761 /// Given a position, get the number of the visible line (potentially many to a paragraph),
2762 /// starting from zero at the start of the buffer.
2763 long wxRichTextParagraphLayoutBox::GetVisibleLineNumber(long pos
, bool caretPosition
, bool startOfLine
) const
2770 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2773 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2774 // wxASSERT( child != NULL );
2778 if (child
->GetRange().Contains(pos
))
2780 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2783 wxRichTextLine
* line
= node2
->GetData();
2784 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
2786 if (lineRange
.Contains(pos
) || pos
== lineRange
.GetStart())
2788 // If the caret is displayed at the end of the previous wrapped line,
2789 // we want to return the line it's _displayed_ at (not the actual line
2790 // containing the position).
2791 if (lineRange
.GetStart() == pos
&& !startOfLine
&& child
->GetRange().GetStart() != pos
)
2792 return lineCount
- 1;
2799 node2
= node2
->GetNext();
2801 // If we didn't find it in the lines, it must be
2802 // the last position of the paragraph. So return the last line.
2806 lineCount
+= child
->GetLines().GetCount();
2809 node
= node
->GetNext();
2816 /// Given a line number, get the corresponding wxRichTextLine object.
2817 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineForVisibleLineNumber(long lineNumber
) const
2821 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2824 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2825 // wxASSERT(child != NULL);
2829 if (lineNumber
< (int) (child
->GetLines().GetCount() + lineCount
))
2831 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2834 wxRichTextLine
* line
= node2
->GetData();
2836 if (lineCount
== lineNumber
)
2841 node2
= node2
->GetNext();
2845 lineCount
+= child
->GetLines().GetCount();
2848 node
= node
->GetNext();
2855 /// Delete range from layout.
2856 bool wxRichTextParagraphLayoutBox::DeleteRange(const wxRichTextRange
& range
)
2858 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2860 wxRichTextParagraph
* firstPara
= NULL
;
2863 wxRichTextParagraph
* obj
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2864 // wxASSERT (obj != NULL);
2866 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
2870 // Delete the range in each paragraph
2872 if (!obj
->GetRange().IsOutside(range
))
2874 // Deletes the content of this object within the given range
2875 obj
->DeleteRange(range
);
2877 wxRichTextRange thisRange
= obj
->GetRange();
2878 wxRichTextAttr thisAttr
= obj
->GetAttributes();
2880 // If the whole paragraph is within the range to delete,
2881 // delete the whole thing.
2882 if (range
.GetStart() <= thisRange
.GetStart() && range
.GetEnd() >= thisRange
.GetEnd())
2884 // Delete the whole object
2885 RemoveChild(obj
, true);
2888 else if (!firstPara
)
2891 // If the range includes the paragraph end, we need to join this
2892 // and the next paragraph.
2893 if (range
.GetEnd() <= thisRange
.GetEnd())
2895 // We need to move the objects from the next paragraph
2896 // to this paragraph
2898 wxRichTextParagraph
* nextParagraph
= NULL
;
2899 if ((range
.GetEnd() < thisRange
.GetEnd()) && obj
)
2900 nextParagraph
= obj
;
2903 // We're ending at the end of the paragraph, so merge the _next_ paragraph.
2905 nextParagraph
= wxDynamicCast(next
->GetData(), wxRichTextParagraph
);
2908 bool applyFinalParagraphStyle
= firstPara
&& nextParagraph
&& nextParagraph
!= firstPara
;
2910 wxRichTextAttr nextParaAttr
;
2911 if (applyFinalParagraphStyle
)
2913 // Special case when deleting the end of a paragraph - use _this_ paragraph's style,
2914 // not the next one.
2915 if (range
.GetStart() == range
.GetEnd() && range
.GetStart() == thisRange
.GetEnd())
2916 nextParaAttr
= thisAttr
;
2918 nextParaAttr
= nextParagraph
->GetAttributes();
2921 if (firstPara
&& nextParagraph
&& firstPara
!= nextParagraph
)
2923 // Move the objects to the previous para
2924 wxRichTextObjectList::compatibility_iterator node1
= nextParagraph
->GetChildren().GetFirst();
2928 wxRichTextObject
* obj1
= node1
->GetData();
2930 firstPara
->AppendChild(obj1
);
2932 wxRichTextObjectList::compatibility_iterator next1
= node1
->GetNext();
2933 nextParagraph
->GetChildren().Erase(node1
);
2938 // Delete the paragraph
2939 RemoveChild(nextParagraph
, true);
2942 // Avoid empty paragraphs
2943 if (firstPara
&& firstPara
->GetChildren().GetCount() == 0)
2945 wxRichTextPlainText
* text
= new wxRichTextPlainText(wxEmptyString
);
2946 firstPara
->AppendChild(text
);
2949 if (applyFinalParagraphStyle
)
2950 firstPara
->SetAttributes(nextParaAttr
);
2963 /// Get any text in this object for the given range
2964 wxString
wxRichTextParagraphLayoutBox::GetTextForRange(const wxRichTextRange
& range
) const
2968 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2971 wxRichTextObject
* child
= node
->GetData();
2972 if (!child
->GetRange().IsOutside(range
))
2974 wxRichTextRange childRange
= range
;
2975 childRange
.LimitTo(child
->GetRange());
2977 wxString childText
= child
->GetTextForRange(childRange
);
2981 if ((childRange
.GetEnd() == child
->GetRange().GetEnd()) && node
->GetNext())
2986 node
= node
->GetNext();
2992 /// Get all the text
2993 wxString
wxRichTextParagraphLayoutBox::GetText() const
2995 return GetTextForRange(GetOwnRange());
2998 /// Get the paragraph by number
2999 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphAtLine(long paragraphNumber
) const
3001 if ((size_t) paragraphNumber
>= GetChildCount())
3004 return (wxRichTextParagraph
*) GetChild((size_t) paragraphNumber
);
3007 /// Get the length of the paragraph
3008 int wxRichTextParagraphLayoutBox::GetParagraphLength(long paragraphNumber
) const
3010 wxRichTextParagraph
* para
= GetParagraphAtLine(paragraphNumber
);
3012 return para
->GetRange().GetLength() - 1; // don't include newline
3017 /// Get the text of the paragraph
3018 wxString
wxRichTextParagraphLayoutBox::GetParagraphText(long paragraphNumber
) const
3020 wxRichTextParagraph
* para
= GetParagraphAtLine(paragraphNumber
);
3022 return para
->GetTextForRange(para
->GetRange());
3024 return wxEmptyString
;
3027 /// Convert zero-based line column and paragraph number to a position.
3028 long wxRichTextParagraphLayoutBox::XYToPosition(long x
, long y
) const
3030 wxRichTextParagraph
* para
= GetParagraphAtLine(y
);
3033 return para
->GetRange().GetStart() + x
;
3039 /// Convert zero-based position to line column and paragraph number
3040 bool wxRichTextParagraphLayoutBox::PositionToXY(long pos
, long* x
, long* y
) const
3042 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
);
3046 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3049 wxRichTextObject
* child
= node
->GetData();
3053 node
= node
->GetNext();
3057 *x
= pos
- para
->GetRange().GetStart();
3065 /// Get the leaf object in a paragraph at this position.
3066 /// Given a line number, get the corresponding wxRichTextLine object.
3067 wxRichTextObject
* wxRichTextParagraphLayoutBox::GetLeafObjectAtPosition(long position
) const
3069 wxRichTextParagraph
* para
= GetParagraphAtPosition(position
);
3072 wxRichTextObjectList::compatibility_iterator node
= para
->GetChildren().GetFirst();
3076 wxRichTextObject
* child
= node
->GetData();
3077 if (child
->GetRange().Contains(position
))
3080 node
= node
->GetNext();
3082 if (position
== para
->GetRange().GetEnd() && para
->GetChildCount() > 0)
3083 return para
->GetChildren().GetLast()->GetData();
3088 /// Set character or paragraph text attributes: apply character styles only to immediate text nodes
3089 bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange
& range
, const wxRichTextAttr
& style
, int flags
)
3091 bool characterStyle
= false;
3092 bool paragraphStyle
= false;
3094 if (style
.IsCharacterStyle())
3095 characterStyle
= true;
3096 if (style
.IsParagraphStyle())
3097 paragraphStyle
= true;
3099 wxRichTextBuffer
* buffer
= GetBuffer();
3101 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
3102 bool applyMinimal
= ((flags
& wxRICHTEXT_SETSTYLE_OPTIMIZE
) != 0);
3103 bool parasOnly
= ((flags
& wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY
) != 0);
3104 bool charactersOnly
= ((flags
& wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY
) != 0);
3105 bool resetExistingStyle
= ((flags
& wxRICHTEXT_SETSTYLE_RESET
) != 0);
3106 bool removeStyle
= ((flags
& wxRICHTEXT_SETSTYLE_REMOVE
) != 0);
3108 // Apply paragraph style first, if any
3109 wxRichTextAttr
wholeStyle(style
);
3111 if (!removeStyle
&& wholeStyle
.HasParagraphStyleName() && buffer
->GetStyleSheet())
3113 wxRichTextParagraphStyleDefinition
* def
= buffer
->GetStyleSheet()->FindParagraphStyle(wholeStyle
.GetParagraphStyleName());
3115 wxRichTextApplyStyle(wholeStyle
, def
->GetStyleMergedWithBase(buffer
->GetStyleSheet()));
3118 // Limit the attributes to be set to the content to only character attributes.
3119 wxRichTextAttr
characterAttributes(wholeStyle
);
3120 characterAttributes
.SetFlags(characterAttributes
.GetFlags() & (wxTEXT_ATTR_CHARACTER
));
3122 if (!removeStyle
&& characterAttributes
.HasCharacterStyleName() && buffer
->GetStyleSheet())
3124 wxRichTextCharacterStyleDefinition
* def
= buffer
->GetStyleSheet()->FindCharacterStyle(characterAttributes
.GetCharacterStyleName());
3126 wxRichTextApplyStyle(characterAttributes
, def
->GetStyleMergedWithBase(buffer
->GetStyleSheet()));
3129 // If we are associated with a control, make undoable; otherwise, apply immediately
3132 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3134 wxRichTextAction
* action
= NULL
;
3136 if (haveControl
&& withUndo
)
3138 action
= new wxRichTextAction(NULL
, _("Change Style"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
3139 action
->SetRange(range
);
3140 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3143 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3146 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3147 // wxASSERT (para != NULL);
3149 if (para
&& para
->GetChildCount() > 0)
3151 // Stop searching if we're beyond the range of interest
3152 if (para
->GetRange().GetStart() > range
.GetEnd())
3155 if (!para
->GetRange().IsOutside(range
))
3157 // We'll be using a copy of the paragraph to make style changes,
3158 // not updating the buffer directly.
3159 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3161 if (haveControl
&& withUndo
)
3163 newPara
= new wxRichTextParagraph(*para
);
3164 action
->GetNewParagraphs().AppendChild(newPara
);
3166 // Also store the old ones for Undo
3167 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3172 // If we're specifying paragraphs only, then we really mean character formatting
3173 // to be included in the paragraph style
3174 if ((paragraphStyle
|| parasOnly
) && !charactersOnly
)
3178 // Removes the given style from the paragraph
3179 wxRichTextRemoveStyle(newPara
->GetAttributes(), style
);
3181 else if (resetExistingStyle
)
3182 newPara
->GetAttributes() = wholeStyle
;
3187 // Only apply attributes that will make a difference to the combined
3188 // style as seen on the display
3189 wxRichTextAttr
combinedAttr(para
->GetCombinedAttributes(true));
3190 wxRichTextApplyStyle(newPara
->GetAttributes(), wholeStyle
, & combinedAttr
);
3193 wxRichTextApplyStyle(newPara
->GetAttributes(), wholeStyle
);
3197 // When applying paragraph styles dynamically, don't change the text objects' attributes
3198 // since they will computed as needed. Only apply the character styling if it's _only_
3199 // character styling. This policy is subject to change and might be put under user control.
3201 // Hm. we might well be applying a mix of paragraph and character styles, in which
3202 // case we _do_ want to apply character styles regardless of what para styles are set.
3203 // But if we're applying a paragraph style, which has some character attributes, but
3204 // we only want the paragraphs to hold this character style, then we _don't_ want to
3205 // apply the character style. So we need to be able to choose.
3207 if (!parasOnly
&& (characterStyle
|charactersOnly
) && range
.GetStart() != newPara
->GetRange().GetEnd())
3209 wxRichTextRange
childRange(range
);
3210 childRange
.LimitTo(newPara
->GetRange());
3212 // Find the starting position and if necessary split it so
3213 // we can start applying a different style.
3214 // TODO: check that the style actually changes or is different
3215 // from style outside of range
3216 wxRichTextObject
* firstObject
wxDUMMY_INITIALIZE(NULL
);
3217 wxRichTextObject
* lastObject
wxDUMMY_INITIALIZE(NULL
);
3219 if (childRange
.GetStart() == newPara
->GetRange().GetStart())
3220 firstObject
= newPara
->GetChildren().GetFirst()->GetData();
3222 firstObject
= newPara
->SplitAt(range
.GetStart());
3224 // Increment by 1 because we're apply the style one _after_ the split point
3225 long splitPoint
= childRange
.GetEnd();
3226 if (splitPoint
!= newPara
->GetRange().GetEnd())
3230 if (splitPoint
== newPara
->GetRange().GetEnd())
3231 lastObject
= newPara
->GetChildren().GetLast()->GetData();
3233 // lastObject is set as a side-effect of splitting. It's
3234 // returned as the object before the new object.
3235 (void) newPara
->SplitAt(splitPoint
, & lastObject
);
3237 wxASSERT(firstObject
!= NULL
);
3238 wxASSERT(lastObject
!= NULL
);
3240 if (!firstObject
|| !lastObject
)
3243 wxRichTextObjectList::compatibility_iterator firstNode
= newPara
->GetChildren().Find(firstObject
);
3244 wxRichTextObjectList::compatibility_iterator lastNode
= newPara
->GetChildren().Find(lastObject
);
3246 wxASSERT(firstNode
);
3249 wxRichTextObjectList::compatibility_iterator node2
= firstNode
;
3253 wxRichTextObject
* child
= node2
->GetData();
3257 // Removes the given style from the paragraph
3258 wxRichTextRemoveStyle(child
->GetAttributes(), style
);
3260 else if (resetExistingStyle
)
3261 child
->GetAttributes() = characterAttributes
;
3266 // Only apply attributes that will make a difference to the combined
3267 // style as seen on the display
3268 wxRichTextAttr
combinedAttr(newPara
->GetCombinedAttributes(child
->GetAttributes(), true));
3269 wxRichTextApplyStyle(child
->GetAttributes(), characterAttributes
, & combinedAttr
);
3272 wxRichTextApplyStyle(child
->GetAttributes(), characterAttributes
);
3275 if (node2
== lastNode
)
3278 node2
= node2
->GetNext();
3284 node
= node
->GetNext();
3287 // Do action, or delay it until end of batch.
3288 if (haveControl
&& withUndo
)
3289 buffer
->SubmitAction(action
);
3294 // Just change the attributes for this single object.
3295 void wxRichTextParagraphLayoutBox::SetStyle(wxRichTextObject
* obj
, const wxRichTextAttr
& textAttr
, int flags
)
3297 wxRichTextBuffer
* buffer
= GetBuffer();
3298 bool withUndo
= flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
;
3299 bool resetExistingStyle
= ((flags
& wxRICHTEXT_SETSTYLE_RESET
) != 0);
3300 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3302 wxRichTextAction
*action
= NULL
;
3303 wxRichTextAttr newAttr
= obj
->GetAttributes();
3304 if (resetExistingStyle
)
3307 newAttr
.Apply(textAttr
);
3309 if (haveControl
&& withUndo
)
3311 action
= new wxRichTextAction(NULL
, _("Change Object Style"), wxRICHTEXT_CHANGE_ATTRIBUTES
, buffer
, obj
->GetContainer(), buffer
->GetRichTextCtrl());
3312 action
->SetRange(obj
->GetRange().FromInternal());
3313 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3314 action
->MakeObject(obj
);
3316 action
->GetAttributes() = newAttr
;
3319 obj
->GetAttributes() = newAttr
;
3321 if (haveControl
&& withUndo
)
3322 buffer
->SubmitAction(action
);
3325 /// Get the text attributes for this position.
3326 bool wxRichTextParagraphLayoutBox::GetStyle(long position
, wxRichTextAttr
& style
)
3328 return DoGetStyle(position
, style
, true);
3331 bool wxRichTextParagraphLayoutBox::GetUncombinedStyle(long position
, wxRichTextAttr
& style
)
3333 return DoGetStyle(position
, style
, false);
3336 /// Implementation helper for GetStyle. If combineStyles is true, combine base, paragraph and
3337 /// context attributes.
3338 bool wxRichTextParagraphLayoutBox::DoGetStyle(long position
, wxRichTextAttr
& style
, bool combineStyles
)
3340 wxRichTextObject
* obj
wxDUMMY_INITIALIZE(NULL
);
3342 if (style
.IsParagraphStyle())
3344 obj
= GetParagraphAtPosition(position
);
3349 // Start with the base style
3350 style
= GetAttributes();
3351 style
.GetTextBoxAttr().Reset();
3353 // Apply the paragraph style
3354 wxRichTextApplyStyle(style
, obj
->GetAttributes());
3357 style
= obj
->GetAttributes();
3364 obj
= GetLeafObjectAtPosition(position
);
3369 wxRichTextParagraph
* para
= wxDynamicCast(obj
->GetParent(), wxRichTextParagraph
);
3370 style
= para
? para
->GetCombinedAttributes(obj
->GetAttributes()) : obj
->GetAttributes();
3373 style
= obj
->GetAttributes();
3381 static bool wxHasStyle(long flags
, long style
)
3383 return (flags
& style
) != 0;
3386 /// Combines 'style' with 'currentStyle' for the purpose of summarising the attributes of a range of
3388 bool wxRichTextParagraphLayoutBox::CollectStyle(wxRichTextAttr
& currentStyle
, const wxRichTextAttr
& style
, wxRichTextAttr
& clashingAttr
, wxRichTextAttr
& absentAttr
)
3390 currentStyle
.CollectCommonAttributes(style
, clashingAttr
, absentAttr
);
3395 /// Get the combined style for a range - if any attribute is different within the range,
3396 /// that attribute is not present within the flags.
3397 /// *** Note that this is not recursive, and so assumes that content inside a paragraph is not itself
3399 bool wxRichTextParagraphLayoutBox::GetStyleForRange(const wxRichTextRange
& range
, wxRichTextAttr
& style
)
3401 style
= wxRichTextAttr();
3403 wxRichTextAttr clashingAttrPara
, clashingAttrChar
;
3404 wxRichTextAttr absentAttrPara
, absentAttrChar
;
3406 wxRichTextObjectList::compatibility_iterator node
= GetChildren().GetFirst();
3409 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3410 if (para
&& !(para
->GetRange().GetStart() > range
.GetEnd() || para
->GetRange().GetEnd() < range
.GetStart()))
3412 if (para
->GetChildren().GetCount() == 0)
3414 wxRichTextAttr paraStyle
= para
->GetCombinedAttributes(true /* use box attributes */);
3416 CollectStyle(style
, paraStyle
, clashingAttrPara
, absentAttrPara
);
3420 wxRichTextRange
paraRange(para
->GetRange());
3421 paraRange
.LimitTo(range
);
3423 // First collect paragraph attributes only
3424 wxRichTextAttr paraStyle
= para
->GetCombinedAttributes();
3425 paraStyle
.SetFlags(paraStyle
.GetFlags() & wxTEXT_ATTR_PARAGRAPH
);
3426 CollectStyle(style
, paraStyle
, clashingAttrPara
, absentAttrPara
);
3428 wxRichTextObjectList::compatibility_iterator childNode
= para
->GetChildren().GetFirst();
3432 wxRichTextObject
* child
= childNode
->GetData();
3433 if (!(child
->GetRange().GetStart() > range
.GetEnd() || child
->GetRange().GetEnd() < range
.GetStart()))
3435 wxRichTextAttr childStyle
= para
->GetCombinedAttributes(child
->GetAttributes(), true /* include box attributes */);
3437 // Now collect character attributes only
3438 childStyle
.SetFlags(childStyle
.GetFlags() & wxTEXT_ATTR_CHARACTER
);
3440 CollectStyle(style
, childStyle
, clashingAttrChar
, absentAttrChar
);
3443 childNode
= childNode
->GetNext();
3447 node
= node
->GetNext();
3452 /// Set default style
3453 bool wxRichTextParagraphLayoutBox::SetDefaultStyle(const wxRichTextAttr
& style
)
3455 m_defaultAttributes
= style
;
3459 /// Test if this whole range has character attributes of the specified kind. If any
3460 /// of the attributes are different within the range, the test fails. You
3461 /// can use this to implement, for example, bold button updating. style must have
3462 /// flags indicating which attributes are of interest.
3463 bool wxRichTextParagraphLayoutBox::HasCharacterAttributes(const wxRichTextRange
& range
, const wxRichTextAttr
& style
) const
3466 int matchingCount
= 0;
3468 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3471 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3472 // wxASSERT (para != NULL);
3476 // Stop searching if we're beyond the range of interest
3477 if (para
->GetRange().GetStart() > range
.GetEnd())
3478 return foundCount
== matchingCount
&& foundCount
!= 0;
3480 if (!para
->GetRange().IsOutside(range
))
3482 wxRichTextObjectList::compatibility_iterator node2
= para
->GetChildren().GetFirst();
3486 wxRichTextObject
* child
= node2
->GetData();
3487 // Allow for empty string if no buffer
3488 wxRichTextRange childRange
= child
->GetRange();
3489 if (childRange
.GetLength() == 0 && GetRange().GetLength() == 1)
3490 childRange
.SetEnd(childRange
.GetEnd()+1);
3492 if (!childRange
.IsOutside(range
) && wxDynamicCast(child
, wxRichTextPlainText
))
3495 wxRichTextAttr textAttr
= para
->GetCombinedAttributes(child
->GetAttributes());
3497 if (textAttr
.EqPartial(style
, false /* strong test - attributes must be valid in both objects */))
3501 node2
= node2
->GetNext();
3506 node
= node
->GetNext();
3509 return foundCount
== matchingCount
&& foundCount
!= 0;
3512 /// Test if this whole range has paragraph attributes of the specified kind. If any
3513 /// of the attributes are different within the range, the test fails. You
3514 /// can use this to implement, for example, centering button updating. style must have
3515 /// flags indicating which attributes are of interest.
3516 bool wxRichTextParagraphLayoutBox::HasParagraphAttributes(const wxRichTextRange
& range
, const wxRichTextAttr
& style
) const
3519 int matchingCount
= 0;
3521 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3524 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3525 // wxASSERT (para != NULL);
3529 // Stop searching if we're beyond the range of interest
3530 if (para
->GetRange().GetStart() > range
.GetEnd())
3531 return foundCount
== matchingCount
&& foundCount
!= 0;
3533 if (!para
->GetRange().IsOutside(range
))
3535 wxRichTextAttr textAttr
= GetAttributes();
3536 // Apply the paragraph style
3537 wxRichTextApplyStyle(textAttr
, para
->GetAttributes());
3540 if (textAttr
.EqPartial(style
, false /* strong test */))
3545 node
= node
->GetNext();
3547 return foundCount
== matchingCount
&& foundCount
!= 0;
3550 void wxRichTextParagraphLayoutBox::PrepareContent(wxRichTextParagraphLayoutBox
& container
)
3552 wxRichTextBuffer
* buffer
= GetBuffer();
3553 if (buffer
&& buffer
->GetRichTextCtrl())
3554 buffer
->GetRichTextCtrl()->PrepareContent(container
);
3557 /// Set character or paragraph properties
3558 bool wxRichTextParagraphLayoutBox::SetProperties(const wxRichTextRange
& range
, const wxRichTextProperties
& properties
, int flags
)
3560 wxRichTextBuffer
* buffer
= GetBuffer();
3562 bool withUndo
= ((flags
& wxRICHTEXT_SETPROPERTIES_WITH_UNDO
) != 0);
3563 bool parasOnly
= ((flags
& wxRICHTEXT_SETPROPERTIES_PARAGRAPHS_ONLY
) != 0);
3564 bool charactersOnly
= ((flags
& wxRICHTEXT_SETPROPERTIES_CHARACTERS_ONLY
) != 0);
3565 bool resetExistingProperties
= ((flags
& wxRICHTEXT_SETPROPERTIES_RESET
) != 0);
3566 bool removeProperties
= ((flags
& wxRICHTEXT_SETPROPERTIES_REMOVE
) != 0);
3568 // If we are associated with a control, make undoable; otherwise, apply immediately
3571 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3573 wxRichTextAction
* action
= NULL
;
3575 if (haveControl
&& withUndo
)
3577 action
= new wxRichTextAction(NULL
, _("Change Properties"), wxRICHTEXT_CHANGE_PROPERTIES
, buffer
, this, buffer
->GetRichTextCtrl());
3578 action
->SetRange(range
);
3579 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3582 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3585 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3586 // wxASSERT (para != NULL);
3588 if (para
&& para
->GetChildCount() > 0)
3590 // Stop searching if we're beyond the range of interest
3591 if (para
->GetRange().GetStart() > range
.GetEnd())
3594 if (!para
->GetRange().IsOutside(range
))
3596 // We'll be using a copy of the paragraph to make style changes,
3597 // not updating the buffer directly.
3598 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3600 if (haveControl
&& withUndo
)
3602 newPara
= new wxRichTextParagraph(*para
);
3603 action
->GetNewParagraphs().AppendChild(newPara
);
3605 // Also store the old ones for Undo
3606 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3613 if (removeProperties
)
3615 // Removes the given style from the paragraph
3617 newPara
->GetProperties().RemoveProperties(properties
);
3619 else if (resetExistingProperties
)
3620 newPara
->GetProperties() = properties
;
3622 newPara
->GetProperties().MergeProperties(properties
);
3625 // When applying paragraph styles dynamically, don't change the text objects' attributes
3626 // since they will computed as needed. Only apply the character styling if it's _only_
3627 // character styling. This policy is subject to change and might be put under user control.
3629 // Hm. we might well be applying a mix of paragraph and character styles, in which
3630 // case we _do_ want to apply character styles regardless of what para styles are set.
3631 // But if we're applying a paragraph style, which has some character attributes, but
3632 // we only want the paragraphs to hold this character style, then we _don't_ want to
3633 // apply the character style. So we need to be able to choose.
3635 if (!parasOnly
&& charactersOnly
&& range
.GetStart() != newPara
->GetRange().GetEnd())
3637 wxRichTextRange
childRange(range
);
3638 childRange
.LimitTo(newPara
->GetRange());
3640 // Find the starting position and if necessary split it so
3641 // we can start applying different properties.
3642 // TODO: check that the properties actually change or are different
3643 // from properties outside of range
3644 wxRichTextObject
* firstObject
wxDUMMY_INITIALIZE(NULL
);
3645 wxRichTextObject
* lastObject
wxDUMMY_INITIALIZE(NULL
);
3647 if (childRange
.GetStart() == newPara
->GetRange().GetStart())
3648 firstObject
= newPara
->GetChildren().GetFirst()->GetData();
3650 firstObject
= newPara
->SplitAt(range
.GetStart());
3652 // Increment by 1 because we're apply the style one _after_ the split point
3653 long splitPoint
= childRange
.GetEnd();
3654 if (splitPoint
!= newPara
->GetRange().GetEnd())
3658 if (splitPoint
== newPara
->GetRange().GetEnd())
3659 lastObject
= newPara
->GetChildren().GetLast()->GetData();
3661 // lastObject is set as a side-effect of splitting. It's
3662 // returned as the object before the new object.
3663 (void) newPara
->SplitAt(splitPoint
, & lastObject
);
3665 wxASSERT(firstObject
!= NULL
);
3666 wxASSERT(lastObject
!= NULL
);
3668 if (!firstObject
|| !lastObject
)
3671 wxRichTextObjectList::compatibility_iterator firstNode
= newPara
->GetChildren().Find(firstObject
);
3672 wxRichTextObjectList::compatibility_iterator lastNode
= newPara
->GetChildren().Find(lastObject
);
3674 wxASSERT(firstNode
);
3677 wxRichTextObjectList::compatibility_iterator node2
= firstNode
;
3681 wxRichTextObject
* child
= node2
->GetData();
3683 if (removeProperties
)
3685 // Removes the given properties from the paragraph
3686 child
->GetProperties().RemoveProperties(properties
);
3688 else if (resetExistingProperties
)
3689 child
->GetProperties() = properties
;
3692 child
->GetProperties().MergeProperties(properties
);
3695 if (node2
== lastNode
)
3698 node2
= node2
->GetNext();
3704 node
= node
->GetNext();
3707 // Do action, or delay it until end of batch.
3708 if (haveControl
&& withUndo
)
3709 buffer
->SubmitAction(action
);
3714 void wxRichTextParagraphLayoutBox::Reset()
3718 wxRichTextBuffer
* buffer
= GetBuffer();
3719 if (buffer
&& buffer
->GetRichTextCtrl())
3721 wxRichTextEvent
event(wxEVT_COMMAND_RICHTEXT_BUFFER_RESET
, buffer
->GetRichTextCtrl()->GetId());
3722 event
.SetEventObject(buffer
->GetRichTextCtrl());
3723 event
.SetContainer(this);
3725 buffer
->SendEvent(event
, true);
3728 AddParagraph(wxEmptyString
);
3730 PrepareContent(*this);
3732 InvalidateHierarchy(wxRICHTEXT_ALL
);
3735 /// Invalidate the buffer. With no argument, invalidates whole buffer.
3736 void wxRichTextParagraphLayoutBox::Invalidate(const wxRichTextRange
& invalidRange
)
3738 wxRichTextCompositeObject::Invalidate(invalidRange
);
3740 DoInvalidate(invalidRange
);
3743 // Do the (in)validation for this object only
3744 void wxRichTextParagraphLayoutBox::DoInvalidate(const wxRichTextRange
& invalidRange
)
3746 if (invalidRange
== wxRICHTEXT_ALL
)
3748 m_invalidRange
= wxRICHTEXT_ALL
;
3750 // Already invalidating everything
3751 else if (m_invalidRange
== wxRICHTEXT_ALL
)
3756 if ((invalidRange
.GetStart() < m_invalidRange
.GetStart()) || m_invalidRange
.GetStart() == -1)
3757 m_invalidRange
.SetStart(invalidRange
.GetStart());
3758 if (invalidRange
.GetEnd() > m_invalidRange
.GetEnd())
3759 m_invalidRange
.SetEnd(invalidRange
.GetEnd());
3763 // Do the (in)validation both up and down the hierarchy
3764 void wxRichTextParagraphLayoutBox::InvalidateHierarchy(const wxRichTextRange
& invalidRange
)
3766 Invalidate(invalidRange
);
3768 if (invalidRange
!= wxRICHTEXT_NONE
)
3770 // Now go up the hierarchy
3771 wxRichTextObject
* thisObj
= this;
3772 wxRichTextObject
* p
= GetParent();
3775 wxRichTextParagraphLayoutBox
* l
= wxDynamicCast(p
, wxRichTextParagraphLayoutBox
);
3777 l
->DoInvalidate(thisObj
->GetRange());
3785 /// Get invalid range, rounding to entire paragraphs if argument is true.
3786 wxRichTextRange
wxRichTextParagraphLayoutBox::GetInvalidRange(bool wholeParagraphs
) const
3788 if (m_invalidRange
== wxRICHTEXT_ALL
|| m_invalidRange
== wxRICHTEXT_NONE
)
3789 return m_invalidRange
;
3791 wxRichTextRange range
= m_invalidRange
;
3793 if (wholeParagraphs
)
3795 wxRichTextParagraph
* para1
= GetParagraphAtPosition(range
.GetStart());
3797 range
.SetStart(para1
->GetRange().GetStart());
3798 // floating layout make all child should be relayout
3799 range
.SetEnd(GetOwnRange().GetEnd());
3804 /// Apply the style sheet to the buffer, for example if the styles have changed.
3805 bool wxRichTextParagraphLayoutBox::ApplyStyleSheet(wxRichTextStyleSheet
* styleSheet
)
3807 wxASSERT(styleSheet
!= NULL
);
3813 wxRichTextAttr
attr(GetBasicStyle());
3814 if (GetBasicStyle().HasParagraphStyleName())
3816 wxRichTextParagraphStyleDefinition
* paraDef
= styleSheet
->FindParagraphStyle(GetBasicStyle().GetParagraphStyleName());
3819 attr
.Apply(paraDef
->GetStyleMergedWithBase(styleSheet
));
3820 SetBasicStyle(attr
);
3825 if (GetBasicStyle().HasCharacterStyleName())
3827 wxRichTextCharacterStyleDefinition
* charDef
= styleSheet
->FindCharacterStyle(GetBasicStyle().GetCharacterStyleName());
3830 attr
.Apply(charDef
->GetStyleMergedWithBase(styleSheet
));
3831 SetBasicStyle(attr
);
3836 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3839 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3840 // wxASSERT (para != NULL);
3844 // Combine paragraph and list styles. If there is a list style in the original attributes,
3845 // the current indentation overrides anything else and is used to find the item indentation.
3846 // Also, for applying paragraph styles, consider having 2 modes: (1) we merge with what we have,
3847 // thereby taking into account all user changes, (2) reset the style completely (except for indentation/list
3848 // exception as above).
3849 // Problem: when changing from one list style to another, there's a danger that the level info will get lost.
3850 // So when changing a list style interactively, could retrieve level based on current style, then
3851 // set appropriate indent and apply new style.
3855 if (para
->GetAttributes().HasOutlineLevel())
3856 outline
= para
->GetAttributes().GetOutlineLevel();
3857 if (para
->GetAttributes().HasBulletNumber())
3858 num
= para
->GetAttributes().GetBulletNumber();
3860 if (!para
->GetAttributes().GetParagraphStyleName().IsEmpty() && !para
->GetAttributes().GetListStyleName().IsEmpty())
3862 int currentIndent
= para
->GetAttributes().GetLeftIndent();
3864 wxRichTextParagraphStyleDefinition
* paraDef
= styleSheet
->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
3865 wxRichTextListStyleDefinition
* listDef
= styleSheet
->FindListStyle(para
->GetAttributes().GetListStyleName());
3866 if (paraDef
&& !listDef
)
3868 para
->GetAttributes() = paraDef
->GetStyleMergedWithBase(styleSheet
);
3871 else if (listDef
&& !paraDef
)
3873 // Set overall style defined for the list style definition
3874 para
->GetAttributes() = listDef
->GetStyleMergedWithBase(styleSheet
);
3876 // Apply the style for this level
3877 wxRichTextApplyStyle(para
->GetAttributes(), * listDef
->GetLevelAttributes(listDef
->FindLevelForIndent(currentIndent
)));
3880 else if (listDef
&& paraDef
)
3882 // Combines overall list style, style for level, and paragraph style
3883 para
->GetAttributes() = listDef
->CombineWithParagraphStyle(currentIndent
, paraDef
->GetStyleMergedWithBase(styleSheet
));
3887 else if (para
->GetAttributes().GetParagraphStyleName().IsEmpty() && !para
->GetAttributes().GetListStyleName().IsEmpty())
3889 int currentIndent
= para
->GetAttributes().GetLeftIndent();
3891 wxRichTextListStyleDefinition
* listDef
= styleSheet
->FindListStyle(para
->GetAttributes().GetListStyleName());
3893 // Overall list definition style
3894 para
->GetAttributes() = listDef
->GetStyleMergedWithBase(styleSheet
);
3896 // Style for this level
3897 wxRichTextApplyStyle(para
->GetAttributes(), * listDef
->GetLevelAttributes(listDef
->FindLevelForIndent(currentIndent
)));
3901 else if (!para
->GetAttributes().GetParagraphStyleName().IsEmpty() && para
->GetAttributes().GetListStyleName().IsEmpty())
3903 wxRichTextParagraphStyleDefinition
* def
= styleSheet
->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
3906 para
->GetAttributes() = def
->GetStyleMergedWithBase(styleSheet
);
3912 para
->GetAttributes().SetOutlineLevel(outline
);
3914 para
->GetAttributes().SetBulletNumber(num
);
3917 node
= node
->GetNext();
3919 return foundCount
!= 0;
3923 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
3925 wxRichTextBuffer
* buffer
= GetBuffer();
3926 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
3928 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
3929 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
3930 bool specifyLevel
= ((flags
& wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL
) != 0);
3931 bool renumber
= ((flags
& wxRICHTEXT_SETSTYLE_RENUMBER
) != 0);
3933 // Current number, if numbering
3936 wxASSERT (!specifyLevel
|| (specifyLevel
&& (specifiedLevel
>= 0)));
3938 // If we are associated with a control, make undoable; otherwise, apply immediately
3941 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3943 wxRichTextAction
* action
= NULL
;
3945 if (haveControl
&& withUndo
)
3947 action
= new wxRichTextAction(NULL
, _("Change List Style"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
3948 action
->SetRange(range
);
3949 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3952 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3955 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3956 // wxASSERT (para != NULL);
3958 if (para
&& para
->GetChildCount() > 0)
3960 // Stop searching if we're beyond the range of interest
3961 if (para
->GetRange().GetStart() > range
.GetEnd())
3964 if (!para
->GetRange().IsOutside(range
))
3966 // We'll be using a copy of the paragraph to make style changes,
3967 // not updating the buffer directly.
3968 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3970 if (haveControl
&& withUndo
)
3972 newPara
= new wxRichTextParagraph(*para
);
3973 action
->GetNewParagraphs().AppendChild(newPara
);
3975 // Also store the old ones for Undo
3976 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3983 int thisIndent
= newPara
->GetAttributes().GetLeftIndent();
3984 int thisLevel
= specifyLevel
? specifiedLevel
: def
->FindLevelForIndent(thisIndent
);
3986 // How is numbering going to work?
3987 // If we are renumbering, or numbering for the first time, we need to keep
3988 // track of the number for each level. But we might be simply applying a different
3990 // In Word, applying a style to several paragraphs, even if at different levels,
3991 // reverts the level back to the same one. So we could do the same here.
3992 // Renumbering will need to be done when we promote/demote a paragraph.
3994 // Apply the overall list style, and item style for this level
3995 wxRichTextAttr
listStyle(def
->GetCombinedStyleForLevel(thisLevel
, styleSheet
));
3996 wxRichTextApplyStyle(newPara
->GetAttributes(), listStyle
);
3998 // Now we need to do numbering
3999 // Preserve the existing list item continuation bullet style, if any
4000 if (para
->GetAttributes().HasBulletStyle() && (para
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
))
4001 newPara
->GetAttributes().SetBulletStyle(newPara
->GetAttributes().GetBulletStyle()|wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
);
4006 newPara
->GetAttributes().SetBulletNumber(n
);
4012 else if (!newPara
->GetAttributes().GetListStyleName().IsEmpty())
4014 // if def is NULL, remove list style, applying any associated paragraph style
4015 // to restore the attributes
4017 newPara
->GetAttributes().SetListStyleName(wxEmptyString
);
4018 newPara
->GetAttributes().SetLeftIndent(0, 0);
4019 newPara
->GetAttributes().SetBulletText(wxEmptyString
);
4020 newPara
->GetAttributes().SetBulletStyle(0);
4022 // Eliminate the main list-related attributes
4023 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
);
4025 if (styleSheet
&& !newPara
->GetAttributes().GetParagraphStyleName().IsEmpty())
4027 wxRichTextParagraphStyleDefinition
* def
= styleSheet
->FindParagraphStyle(newPara
->GetAttributes().GetParagraphStyleName());
4030 newPara
->GetAttributes() = def
->GetStyleMergedWithBase(styleSheet
);
4037 node
= node
->GetNext();
4040 // Do action, or delay it until end of batch.
4041 if (haveControl
&& withUndo
)
4042 buffer
->SubmitAction(action
);
4047 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
)
4049 wxRichTextBuffer
* buffer
= GetBuffer();
4050 if (buffer
&& buffer
->GetStyleSheet())
4052 wxRichTextListStyleDefinition
* def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4054 return SetListStyle(range
, def
, flags
, startFrom
, specifiedLevel
);
4059 /// Clear list for given range
4060 bool wxRichTextParagraphLayoutBox::ClearListStyle(const wxRichTextRange
& range
, int flags
)
4062 return SetListStyle(range
, NULL
, flags
);
4065 /// Number/renumber any list elements in the given range
4066 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
4068 return DoNumberList(range
, range
, 0, def
, flags
, startFrom
, specifiedLevel
);
4071 /// Number/renumber any list elements in the given range. Also do promotion or demotion of items, if specified
4072 bool wxRichTextParagraphLayoutBox::DoNumberList(const wxRichTextRange
& range
, const wxRichTextRange
& promotionRange
, int promoteBy
,
4073 wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
4075 wxRichTextBuffer
* buffer
= GetBuffer();
4076 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
4078 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
4079 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
4081 bool specifyLevel
= ((flags
& wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL
) != 0);
4084 bool renumber
= ((flags
& wxRICHTEXT_SETSTYLE_RENUMBER
) != 0);
4086 // Max number of levels
4087 const int maxLevels
= 10;
4089 // The level we're looking at now
4090 int currentLevel
= -1;
4092 // The item number for each level
4093 int levels
[maxLevels
];
4096 // Reset all numbering
4097 for (i
= 0; i
< maxLevels
; i
++)
4099 if (startFrom
!= -1)
4100 levels
[i
] = startFrom
-1;
4101 else if (renumber
) // start again
4104 levels
[i
] = -1; // start from the number we found, if any
4108 wxASSERT(!specifyLevel
|| (specifyLevel
&& (specifiedLevel
>= 0)));
4111 // If we are associated with a control, make undoable; otherwise, apply immediately
4114 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
4116 wxRichTextAction
* action
= NULL
;
4118 if (haveControl
&& withUndo
)
4120 action
= new wxRichTextAction(NULL
, _("Renumber List"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
4121 action
->SetRange(range
);
4122 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
4125 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
4128 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
4129 // wxASSERT (para != NULL);
4131 if (para
&& para
->GetChildCount() > 0)
4133 // Stop searching if we're beyond the range of interest
4134 if (para
->GetRange().GetStart() > range
.GetEnd())
4137 if (!para
->GetRange().IsOutside(range
))
4139 // We'll be using a copy of the paragraph to make style changes,
4140 // not updating the buffer directly.
4141 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
4143 if (haveControl
&& withUndo
)
4145 newPara
= new wxRichTextParagraph(*para
);
4146 action
->GetNewParagraphs().AppendChild(newPara
);
4148 // Also store the old ones for Undo
4149 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
4154 wxRichTextListStyleDefinition
* defToUse
= def
;
4157 if (styleSheet
&& !newPara
->GetAttributes().GetListStyleName().IsEmpty())
4158 defToUse
= styleSheet
->FindListStyle(newPara
->GetAttributes().GetListStyleName());
4163 int thisIndent
= newPara
->GetAttributes().GetLeftIndent();
4164 int thisLevel
= defToUse
->FindLevelForIndent(thisIndent
);
4166 // If we've specified a level to apply to all, change the level.
4167 if (specifiedLevel
!= -1)
4168 thisLevel
= specifiedLevel
;
4170 // Do promotion if specified
4171 if ((promoteBy
!= 0) && !para
->GetRange().IsOutside(promotionRange
))
4173 thisLevel
= thisLevel
- promoteBy
;
4180 // Apply the overall list style, and item style for this level
4181 wxRichTextAttr
listStyle(defToUse
->GetCombinedStyleForLevel(thisLevel
, styleSheet
));
4182 wxRichTextApplyStyle(newPara
->GetAttributes(), listStyle
);
4184 // Preserve the existing list item continuation bullet style, if any
4185 if (para
->GetAttributes().HasBulletStyle() && (para
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
))
4186 newPara
->GetAttributes().SetBulletStyle(newPara
->GetAttributes().GetBulletStyle()|wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
);
4188 // OK, we've (re)applied the style, now let's get the numbering right.
4190 if (currentLevel
== -1)
4191 currentLevel
= thisLevel
;
4193 // Same level as before, do nothing except increment level's number afterwards
4194 if (currentLevel
== thisLevel
)
4197 // A deeper level: start renumbering all levels after current level
4198 else if (thisLevel
> currentLevel
)
4200 for (i
= currentLevel
+1; i
<= thisLevel
; i
++)
4204 currentLevel
= thisLevel
;
4206 else if (thisLevel
< currentLevel
)
4208 currentLevel
= thisLevel
;
4211 // Use the current numbering if -1 and we have a bullet number already
4212 if (levels
[currentLevel
] == -1)
4214 if (newPara
->GetAttributes().HasBulletNumber())
4215 levels
[currentLevel
] = newPara
->GetAttributes().GetBulletNumber();
4217 levels
[currentLevel
] = 1;
4221 if (!(para
->GetAttributes().HasBulletStyle() && (para
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
)))
4222 levels
[currentLevel
] ++;
4225 newPara
->GetAttributes().SetBulletNumber(levels
[currentLevel
]);
4227 // Create the bullet text if an outline list
4228 if (listStyle
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
)
4231 for (i
= 0; i
<= currentLevel
; i
++)
4233 if (!text
.IsEmpty())
4235 text
+= wxString::Format(wxT("%d"), levels
[i
]);
4237 newPara
->GetAttributes().SetBulletText(text
);
4243 node
= node
->GetNext();
4246 // Do action, or delay it until end of batch.
4247 if (haveControl
&& withUndo
)
4248 buffer
->SubmitAction(action
);
4253 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
)
4255 wxRichTextBuffer
* buffer
= GetBuffer();
4256 if (buffer
->GetStyleSheet())
4258 wxRichTextListStyleDefinition
* def
= NULL
;
4259 if (!defName
.IsEmpty())
4260 def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4261 return NumberList(range
, def
, flags
, startFrom
, specifiedLevel
);
4266 /// Promote the list items within the given range. promoteBy can be a positive or negative number, e.g. 1 or -1
4267 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy
, const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int specifiedLevel
)
4270 // One strategy is to first work out the range within which renumbering must occur. Then could pass these two ranges
4271 // to NumberList with a flag indicating promotion is required within one of the ranges.
4272 // Find first and last paragraphs in range. Then for first, calculate new indentation and look back until we find
4273 // a paragraph that either has no list style, or has one that is different or whose indentation is less.
4274 // We start renumbering from the para after that different para we found. We specify that the numbering of that
4275 // list position will start from 1.
4276 // Similarly, we look after the last para in the promote range for an indentation that is less (or no list style).
4277 // We can end the renumbering at this point.
4279 // For now, only renumber within the promotion range.
4281 return DoNumberList(range
, range
, promoteBy
, def
, flags
, 1, specifiedLevel
);
4284 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy
, const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int specifiedLevel
)
4286 wxRichTextBuffer
* buffer
= GetBuffer();
4287 if (buffer
->GetStyleSheet())
4289 wxRichTextListStyleDefinition
* def
= NULL
;
4290 if (!defName
.IsEmpty())
4291 def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4292 return PromoteList(promoteBy
, range
, def
, flags
, specifiedLevel
);
4297 /// Fills in the attributes for numbering a paragraph after previousParagraph. It also finds the
4298 /// position of the paragraph that it had to start looking from.
4299 bool wxRichTextParagraphLayoutBox::FindNextParagraphNumber(wxRichTextParagraph
* previousParagraph
, wxRichTextAttr
& attr
) const
4301 // TODO: add GetNextChild/GetPreviousChild to composite
4302 // Search for a paragraph that isn't a continuation paragraph (no bullet)
4303 while (previousParagraph
&& previousParagraph
->GetAttributes().HasBulletStyle() && previousParagraph
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
)
4305 wxRichTextObjectList::compatibility_iterator node
= ((wxRichTextCompositeObject
*) previousParagraph
->GetParent())->GetChildren().Find(previousParagraph
);
4308 node
= node
->GetPrevious();
4310 previousParagraph
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
4312 previousParagraph
= NULL
;
4315 previousParagraph
= NULL
;
4318 if (!previousParagraph
|| !previousParagraph
->GetAttributes().HasFlag(wxTEXT_ATTR_BULLET_STYLE
) || previousParagraph
->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
)
4321 wxRichTextBuffer
* buffer
= GetBuffer();
4322 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
4323 if (styleSheet
&& !previousParagraph
->GetAttributes().GetListStyleName().IsEmpty())
4325 wxRichTextListStyleDefinition
* def
= styleSheet
->FindListStyle(previousParagraph
->GetAttributes().GetListStyleName());
4328 // int thisIndent = previousParagraph->GetAttributes().GetLeftIndent();
4329 // int thisLevel = def->FindLevelForIndent(thisIndent);
4331 bool isOutline
= (previousParagraph
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
) != 0;
4333 attr
.SetFlags(previousParagraph
->GetAttributes().GetFlags() & (wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_BULLET_NUMBER
|wxTEXT_ATTR_BULLET_TEXT
|wxTEXT_ATTR_BULLET_NAME
));
4334 if (previousParagraph
->GetAttributes().HasBulletName())
4335 attr
.SetBulletName(previousParagraph
->GetAttributes().GetBulletName());
4336 attr
.SetBulletStyle(previousParagraph
->GetAttributes().GetBulletStyle());
4337 attr
.SetListStyleName(previousParagraph
->GetAttributes().GetListStyleName());
4339 int nextNumber
= previousParagraph
->GetAttributes().GetBulletNumber() + 1;
4340 attr
.SetBulletNumber(nextNumber
);
4344 wxString text
= previousParagraph
->GetAttributes().GetBulletText();
4345 if (!text
.IsEmpty())
4347 int pos
= text
.Find(wxT('.'), true);
4348 if (pos
!= wxNOT_FOUND
)
4350 text
= text
.Mid(0, text
.Length() - pos
- 1);
4353 text
= wxEmptyString
;
4354 if (!text
.IsEmpty())
4356 text
+= wxString::Format(wxT("%d"), nextNumber
);
4357 attr
.SetBulletText(text
);
4371 * wxRichTextParagraph
4372 * This object represents a single paragraph (or in a straight text editor, a line).
4375 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraph
, wxRichTextCompositeObject
)
4377 wxArrayInt
wxRichTextParagraph::sm_defaultTabs
;
4379 wxRichTextParagraph::wxRichTextParagraph(wxRichTextObject
* parent
, wxRichTextAttr
* style
):
4380 wxRichTextCompositeObject(parent
)
4383 SetAttributes(*style
);
4386 wxRichTextParagraph::wxRichTextParagraph(const wxString
& text
, wxRichTextObject
* parent
, wxRichTextAttr
* paraStyle
, wxRichTextAttr
* charStyle
):
4387 wxRichTextCompositeObject(parent
)
4390 SetAttributes(*paraStyle
);
4392 AppendChild(new wxRichTextPlainText(text
, this, charStyle
));
4395 wxRichTextParagraph::~wxRichTextParagraph()
4401 bool wxRichTextParagraph::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int WXUNUSED(descent
), int style
)
4406 // Currently we don't merge these attributes with the parent, but we
4407 // should consider whether we should (e.g. if we set a border colour
4408 // for all paragraphs). But generally box attributes are likely to be
4409 // different for different objects.
4410 wxRect paraRect
= GetRect();
4411 wxRichTextAttr attr
= GetCombinedAttributes();
4412 context
.ApplyVirtualAttributes(attr
, this);
4414 DrawBoxAttributes(dc
, GetBuffer(), attr
, paraRect
);
4416 // Draw the bullet, if any
4417 if ((attr
.GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
) == 0 && (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
) == 0)
4419 if (attr
.GetLeftSubIndent() != 0)
4421 int spaceBeforePara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingBefore());
4422 int leftIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftIndent());
4424 wxRichTextAttr
bulletAttr(attr
);
4426 // Combine with the font of the first piece of content, if one is specified
4427 if (GetChildren().GetCount() > 0)
4429 wxRichTextObject
* firstObj
= (wxRichTextObject
*) GetChildren().GetFirst()->GetData();
4430 if (!firstObj
->IsFloatable() && firstObj
->GetAttributes().HasFont())
4432 wxRichTextApplyStyle(bulletAttr
, firstObj
->GetAttributes());
4436 // Get line height from first line, if any
4437 wxRichTextLine
* line
= m_cachedLines
.GetFirst() ? (wxRichTextLine
* ) m_cachedLines
.GetFirst()->GetData() : NULL
;
4440 int lineHeight
wxDUMMY_INITIALIZE(0);
4443 lineHeight
= line
->GetSize().y
;
4444 linePos
= line
->GetPosition() + GetPosition();
4449 if (bulletAttr
.HasFont() && GetBuffer())
4450 font
= GetBuffer()->GetFontTable().FindFont(bulletAttr
);
4452 font
= (*wxNORMAL_FONT
);
4454 wxCheckSetFont(dc
, font
);
4456 lineHeight
= dc
.GetCharHeight();
4457 linePos
= GetPosition();
4458 linePos
.y
+= spaceBeforePara
;
4461 wxRect
bulletRect(GetPosition().x
+ leftIndent
, linePos
.y
, linePos
.x
- (GetPosition().x
+ leftIndent
), lineHeight
);
4463 if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
)
4465 if (wxRichTextBuffer::GetRenderer())
4466 wxRichTextBuffer::GetRenderer()->DrawBitmapBullet(this, dc
, bulletAttr
, bulletRect
);
4468 else if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_STANDARD
)
4470 if (wxRichTextBuffer::GetRenderer())
4471 wxRichTextBuffer::GetRenderer()->DrawStandardBullet(this, dc
, bulletAttr
, bulletRect
);
4475 wxString bulletText
= GetBulletText();
4477 if (!bulletText
.empty() && wxRichTextBuffer::GetRenderer())
4478 wxRichTextBuffer::GetRenderer()->DrawTextBullet(this, dc
, bulletAttr
, bulletRect
, bulletText
);
4483 // Draw the range for each line, one object at a time.
4485 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
4488 wxRichTextLine
* line
= node
->GetData();
4489 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
4491 // Lines are specified relative to the paragraph
4493 wxPoint linePosition
= line
->GetPosition() + GetPosition();
4495 // Don't draw if off the screen
4496 if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) != 0) || ((linePosition
.y
+ line
->GetSize().y
) >= rect
.y
&& linePosition
.y
<= rect
.y
+ rect
.height
))
4498 wxPoint objectPosition
= linePosition
;
4499 int maxDescent
= line
->GetDescent();
4501 // Loop through objects until we get to the one within range
4502 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
4507 wxRichTextObject
* child
= node2
->GetData();
4509 if ((!child
->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && child
->GetRange().GetLength() > 0 && !child
->GetRange().IsOutside(lineRange
) && !lineRange
.IsOutside(range
))
4511 // Draw this part of the line at the correct position
4512 wxRichTextRange
objectRange(child
->GetRange());
4513 objectRange
.LimitTo(lineRange
);
4516 if (child
->IsTopLevel())
4518 objectSize
= child
->GetCachedSize();
4519 objectRange
= child
->GetOwnRange();
4523 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING && wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4524 if (i
< (int) line
->GetObjectSizes().GetCount())
4526 objectSize
.x
= line
->GetObjectSizes()[(size_t) i
];
4532 child
->GetRangeSize(objectRange
, objectSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, objectPosition
);
4536 // Use the child object's width, but the whole line's height
4537 wxRect
childRect(objectPosition
, wxSize(objectSize
.x
, line
->GetSize().y
));
4538 child
->Draw(dc
, context
, objectRange
, selection
, childRect
, maxDescent
, style
);
4540 objectPosition
.x
+= objectSize
.x
;
4543 else if (child
->GetRange().GetStart() > lineRange
.GetEnd())
4544 // Can break out of inner loop now since we've passed this line's range
4547 node2
= node2
->GetNext();
4551 node
= node
->GetNext();
4557 // Get the range width using partial extents calculated for the whole paragraph.
4558 static int wxRichTextGetRangeWidth(const wxRichTextParagraph
& para
, const wxRichTextRange
& range
, const wxArrayInt
& partialExtents
)
4560 wxASSERT(partialExtents
.GetCount() >= (size_t) range
.GetLength());
4562 if (partialExtents
.GetCount() < (size_t) range
.GetLength())
4565 int leftMostPos
= 0;
4566 if (range
.GetStart() - para
.GetRange().GetStart() > 0)
4567 leftMostPos
= partialExtents
[range
.GetStart() - para
.GetRange().GetStart() - 1];
4569 int rightMostPos
= partialExtents
[range
.GetEnd() - para
.GetRange().GetStart()];
4571 int w
= rightMostPos
- leftMostPos
;
4576 /// Lay the item out
4577 bool wxRichTextParagraph::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
)
4579 // Deal with floating objects firstly before the normal layout
4580 wxRichTextBuffer
* buffer
= GetBuffer();
4583 wxRichTextFloatCollector
* collector
= GetContainer()->GetFloatCollector();
4585 if (wxRichTextBuffer::GetFloatingLayoutMode())
4587 wxASSERT(collector
!= NULL
);
4589 LayoutFloat(dc
, context
, rect
, parentRect
, style
, collector
);
4592 wxRichTextAttr attr
= GetCombinedAttributes();
4593 context
.ApplyVirtualAttributes(attr
, this);
4597 // Increase the size of the paragraph due to spacing
4598 int spaceBeforePara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingBefore());
4599 int spaceAfterPara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingAfter());
4600 int leftIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftIndent());
4601 int leftSubIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftSubIndent());
4602 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
4604 int lineSpacing
= 0;
4606 // Let's assume line spacing of 10 is normal, 15 is 1.5, 20 is 2, etc.
4607 if (attr
.HasLineSpacing() && attr
.GetLineSpacing() > 0 && attr
.GetFont().IsOk())
4609 wxCheckSetFont(dc
, attr
.GetFont());
4610 lineSpacing
= (int) (double(dc
.GetCharHeight()) * (double(attr
.GetLineSpacing())/10.0 - 1.0));
4613 // Start position for each line relative to the paragraph
4614 int startPositionFirstLine
= leftIndent
;
4615 int startPositionSubsequentLines
= leftIndent
+ leftSubIndent
;
4617 // If we have a bullet in this paragraph, the start position for the first line's text
4618 // is actually leftIndent + leftSubIndent.
4619 if (attr
.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE
)
4620 startPositionFirstLine
= startPositionSubsequentLines
;
4622 long lastEndPos
= GetRange().GetStart()-1;
4623 long lastCompletedEndPos
= lastEndPos
;
4625 int currentWidth
= 0;
4626 SetPosition(rect
.GetPosition());
4628 wxPoint
currentPosition(0, spaceBeforePara
); // We will calculate lines relative to paragraph
4631 int maxHeight
= currentPosition
.y
;
4636 int lineDescent
= 0;
4638 wxRichTextObjectList::compatibility_iterator node
;
4640 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4642 wxArrayInt partialExtents
;
4645 int paraDescent
= 0;
4647 // This calculates the partial text extents
4648 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, rect
.GetPosition(), & partialExtents
);
4650 node
= m_children
.GetFirst();
4653 wxRichTextObject
* child
= node
->GetData();
4655 //child->SetCachedSize(wxDefaultSize);
4656 child
->Layout(dc
, context
, rect
, style
);
4658 node
= node
->GetNext();
4664 // We may need to go back to a previous child, in which case create the new line,
4665 // find the child corresponding to the start position of the string, and
4668 wxRect availableRect
;
4670 node
= m_children
.GetFirst();
4673 wxRichTextObject
* child
= node
->GetData();
4675 // If floating, ignore. We already laid out floats.
4676 // Also ignore if empty object, except if we haven't got any
4678 if ((child
->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode())
4679 || !child
->IsShown() || (child
->GetRange().GetLength() == 0 && maxHeight
> spaceBeforePara
)
4682 node
= node
->GetNext();
4686 // If this is e.g. a composite text box, it will need to be laid out itself.
4687 // But if just a text fragment or image, for example, this will
4688 // do nothing. NB: won't we need to set the position after layout?
4689 // since for example if position is dependent on vertical line size, we
4690 // can't tell the position until the size is determined. So possibly introduce
4691 // another layout phase.
4693 // We may only be looking at part of a child, if we searched back for wrapping
4694 // and found a suitable point some way into the child. So get the size for the fragment
4697 long nextBreakPos
= GetFirstLineBreakPosition(lastEndPos
+1);
4698 long lastPosToUse
= child
->GetRange().GetEnd();
4699 bool lineBreakInThisObject
= (nextBreakPos
> -1 && nextBreakPos
<= child
->GetRange().GetEnd());
4701 if (lineBreakInThisObject
)
4702 lastPosToUse
= nextBreakPos
;
4705 int childDescent
= 0;
4707 int startOffset
= (lineCount
== 0 ? startPositionFirstLine
: startPositionSubsequentLines
);
4708 availableRect
= wxRect(rect
.x
+ startOffset
, rect
.y
+ currentPosition
.y
,
4709 rect
.width
- startOffset
- rightIndent
, rect
.height
);
4711 if (child
->IsTopLevel())
4713 wxSize oldSize
= child
->GetCachedSize();
4715 child
->Invalidate(wxRICHTEXT_ALL
);
4716 child
->SetPosition(wxPoint(0, 0));
4718 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4719 // lays out the object again using the minimum size
4720 // The position will be determined by its location in its line,
4721 // and not by the child's actual position.
4722 child
->LayoutToBestSize(dc
, context
, buffer
,
4723 attr
, child
->GetAttributes(), availableRect
, parentRect
, style
);
4725 if (oldSize
!= child
->GetCachedSize())
4727 partialExtents
.Clear();
4729 // Recalculate the partial text extents since the child object changed size
4730 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), & partialExtents
);
4734 // Problem: we need to layout composites here for which we need the available width,
4735 // but we can't get the available width without using the float collector which
4736 // needs to know the object height.
4738 if ((nextBreakPos
== -1) && (lastEndPos
== child
->GetRange().GetStart() - 1)) // i.e. we want to get the whole thing
4740 childSize
= child
->GetCachedSize();
4741 childDescent
= child
->GetDescent();
4745 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4746 // Get height only, then the width using the partial extents
4747 GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
);
4748 childSize
.x
= wxRichTextGetRangeWidth(*this, wxRichTextRange(lastEndPos
+1, lastPosToUse
), partialExtents
);
4750 GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, rect
.GetPosition());
4755 int loopIterations
= 0;
4757 // If there are nested objects that need to lay themselves out, we have to do this in a
4758 // loop because the height of the object may well depend on the available width.
4759 // And because of floating object positioning, the available width depends on the
4760 // height of the object and whether it will clash with the floating objects.
4761 // So, we see whether the available width changes due to the presence of floating images.
4762 // If it does, then we'll use the new restricted width to find the object height again.
4763 // If this causes another restriction in the available width, we'll try again, until
4764 // either we lose patience or the available width settles down.
4769 wxRect oldAvailableRect
= availableRect
;
4771 // Available width depends on the floating objects and the line height.
4772 // Note: the floating objects may be placed vertically along the two sides of
4773 // buffer, so we may have different available line widths with different
4774 // [startY, endY]. So, we can't determine how wide the available
4775 // space is until we know the exact line height.
4776 if (childDescent
== 0)
4778 lineHeight
= wxMax(lineHeight
, childSize
.y
);
4779 lineDescent
= maxDescent
;
4780 lineAscent
= maxAscent
;
4784 lineDescent
= wxMax(childDescent
, maxDescent
);
4785 lineAscent
= wxMax(childSize
.y
-childDescent
, maxAscent
);
4787 lineHeight
= wxMax(lineHeight
, (lineDescent
+ lineAscent
));
4789 if (wxRichTextBuffer::GetFloatingLayoutMode() && collector
)
4791 wxRect floatAvailableRect
= collector
->GetAvailableRect(rect
.y
+ currentPosition
.y
, rect
.y
+ currentPosition
.y
+ lineHeight
);
4793 // Adjust availableRect to the space that is available when taking floating objects into account.
4795 if (floatAvailableRect
.x
+ startOffset
> availableRect
.x
)
4797 int newX
= floatAvailableRect
.x
+ startOffset
;
4798 int newW
= availableRect
.width
- (newX
- availableRect
.x
);
4799 availableRect
.x
= newX
;
4800 availableRect
.width
= newW
;
4803 if (floatAvailableRect
.width
< availableRect
.width
)
4804 availableRect
.width
= floatAvailableRect
.width
;
4807 currentPosition
.x
= availableRect
.x
- rect
.x
;
4809 if (child
->IsTopLevel() && loopIterations
<= 20)
4811 if (availableRect
!= oldAvailableRect
)
4813 wxSize oldSize
= child
->GetCachedSize();
4815 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4816 // lays out the object again using the minimum size
4817 child
->Invalidate(wxRICHTEXT_ALL
);
4818 child
->LayoutToBestSize(dc
, context
, buffer
,
4819 attr
, child
->GetAttributes(), availableRect
, parentRect
, style
);
4820 childSize
= child
->GetCachedSize();
4821 childDescent
= child
->GetDescent();
4823 if (oldSize
!= child
->GetCachedSize())
4825 partialExtents
.Clear();
4827 // Recalculate the partial text extents since the child object changed size
4828 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), & partialExtents
);
4831 // Go around the loop finding the available rect for the given floating objects
4841 if (child
->IsTopLevel())
4843 // We can move it to the correct position at this point
4844 // TODO: probably need to add margin
4845 child
->Move(GetPosition() + wxPoint(currentWidth
+ (wxMax(leftIndent
, leftIndent
+ leftSubIndent
)), currentPosition
.y
));
4849 // 1) There was a line break BEFORE the natural break
4850 // 2) There was a line break AFTER the natural break
4851 // 3) It's the last line
4852 // 4) The child still fits (carry on) - 'else' clause
4854 if ((lineBreakInThisObject
&& (childSize
.x
+ currentWidth
<= availableRect
.width
))
4856 (childSize
.x
+ currentWidth
> availableRect
.width
)
4858 ((childSize
.x
+ currentWidth
<= availableRect
.width
) && !node
->GetNext())
4862 long wrapPosition
= 0;
4863 if ((childSize
.x
+ currentWidth
<= availableRect
.width
) && !node
->GetNext() && !lineBreakInThisObject
)
4864 wrapPosition
= child
->GetRange().GetEnd();
4867 // Find a place to wrap. This may walk back to previous children,
4868 // for example if a word spans several objects.
4869 // Note: one object must contains only one wxTextAtrr, so the line height will not
4870 // change inside one object. Thus, we can pass the remain line width to the
4871 // FindWrapPosition function.
4872 if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos
+1, child
->GetRange().GetEnd()), dc
, context
, availableRect
.width
, wrapPosition
, & partialExtents
))
4874 // If the function failed, just cut it off at the end of this child.
4875 wrapPosition
= child
->GetRange().GetEnd();
4878 // FindWrapPosition can still return a value that will put us in an endless wrapping loop
4879 if (wrapPosition
<= lastCompletedEndPos
)
4880 wrapPosition
= wxMax(lastCompletedEndPos
+1,child
->GetRange().GetEnd());
4882 // Line end position shouldn't be the same as the end, or greater.
4883 if (wrapPosition
>= GetRange().GetEnd())
4884 wrapPosition
= wxMax(0, GetRange().GetEnd()-1);
4886 // wxLogDebug(wxT("Split at %ld"), wrapPosition);
4888 // Let's find the actual size of the current line now
4890 wxRichTextRange
actualRange(lastCompletedEndPos
+1, wrapPosition
);
4894 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4895 if (!child
->IsEmpty())
4897 // Get height only, then the width using the partial extents
4898 GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
);
4899 actualSize
.x
= wxRichTextGetRangeWidth(*this, actualRange
, partialExtents
);
4903 GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
4905 currentWidth
= actualSize
.x
;
4907 // The descent for the whole line at this point, is the correct max descent
4908 maxDescent
= childDescent
;
4910 maxAscent
= actualSize
.y
-childDescent
;
4912 // lineHeight is given by the height for the whole line, since it will
4913 // take into account ascend/descend.
4914 lineHeight
= actualSize
.y
;
4916 if (lineHeight
== 0 && buffer
)
4918 wxFont
font(buffer
->GetFontTable().FindFont(attr
));
4919 wxCheckSetFont(dc
, font
);
4920 lineHeight
= dc
.GetCharHeight();
4923 if (maxDescent
== 0)
4926 dc
.GetTextExtent(wxT("X"), & w
, &h
, & maxDescent
);
4930 wxRichTextLine
* line
= AllocateLine(lineCount
);
4932 // Set relative range so we won't have to change line ranges when paragraphs are moved
4933 line
->SetRange(wxRichTextRange(actualRange
.GetStart() - GetRange().GetStart(), actualRange
.GetEnd() - GetRange().GetStart()));
4934 line
->SetPosition(currentPosition
);
4935 line
->SetSize(wxSize(currentWidth
, lineHeight
));
4936 line
->SetDescent(maxDescent
);
4938 maxHeight
= currentPosition
.y
+ lineHeight
;
4940 // Now move down a line. TODO: add margins, spacing
4941 currentPosition
.y
+= lineHeight
;
4942 currentPosition
.y
+= lineSpacing
;
4945 maxWidth
= wxMax(maxWidth
, currentWidth
+startOffset
);
4950 // TODO: account for zero-length objects
4951 // wxASSERT(wrapPosition > lastCompletedEndPos);
4953 lastEndPos
= wrapPosition
;
4954 lastCompletedEndPos
= lastEndPos
;
4958 if (wrapPosition
< GetRange().GetEnd()-1)
4960 // May need to set the node back to a previous one, due to searching back in wrapping
4961 wxRichTextObject
* childAfterWrapPosition
= FindObjectAtPosition(wrapPosition
+1);
4962 if (childAfterWrapPosition
)
4963 node
= m_children
.Find(childAfterWrapPosition
);
4965 node
= node
->GetNext();
4968 node
= node
->GetNext();
4970 // Apply paragraph styles such as alignment to the wrapped line
4971 ApplyParagraphStyle(line
, attr
, availableRect
, dc
);
4975 // We still fit, so don't add a line, and keep going
4976 currentWidth
+= childSize
.x
;
4978 if (childDescent
== 0)
4980 // An object with a zero descend value wants to take up the whole
4981 // height regardless of baseline
4982 lineHeight
= wxMax(lineHeight
, childSize
.y
);
4986 maxDescent
= wxMax(childDescent
, maxDescent
);
4987 maxAscent
= wxMax(childSize
.y
-childDescent
, maxAscent
);
4990 lineHeight
= wxMax(lineHeight
, (maxDescent
+ maxAscent
));
4992 maxWidth
= wxMax(maxWidth
, currentWidth
+startOffset
);
4993 lastEndPos
= child
->GetRange().GetEnd();
4995 node
= node
->GetNext();
4999 //wxASSERT(!(lastCompletedEndPos != -1 && lastCompletedEndPos < GetRange().GetEnd()-1));
5001 // Remove remaining unused line objects, if any
5002 ClearUnusedLines(lineCount
);
5004 // We need to add back the margins etc.
5006 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
5007 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxWidth
, currentPosition
.y
+ spaceAfterPara
));
5008 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
5009 SetCachedSize(marginRect
.GetSize());
5012 // The maximum size is the length of the paragraph stretched out into a line.
5013 // So if there were a single word, or an image, or a fixed-size text box, the object could be shrunk around
5014 // this size. TODO: take into account line breaks.
5016 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
5017 contentRect
= wxRect(wxPoint(0, 0), wxSize(paraSize
.x
+ wxMax(leftIndent
, leftIndent
+ leftSubIndent
) + rightIndent
, currentPosition
.y
+ spaceAfterPara
));
5018 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
5019 SetMaxSize(marginRect
.GetSize());
5022 // Find the greatest minimum size. Currently we only look at non-text objects,
5023 // which isn't ideal but it would be slow to find the maximum word width to
5024 // use as the minimum.
5027 node
= m_children
.GetFirst();
5030 wxRichTextObject
* child
= node
->GetData();
5032 // If floating, ignore. We already laid out floats.
5033 // Also ignore if empty object, except if we haven't got any
5035 if ((!child
->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && child
->GetRange().GetLength() != 0 && !wxDynamicCast(child
, wxRichTextPlainText
))
5037 if (child
->GetCachedSize().x
> minWidth
)
5038 minWidth
= child
->GetMinSize().x
;
5040 node
= node
->GetNext();
5043 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
5044 contentRect
= wxRect(wxPoint(0, 0), wxSize(minWidth
, currentPosition
.y
+ spaceAfterPara
));
5045 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
5046 SetMinSize(marginRect
.GetSize());
5049 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5050 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
5051 // Use the text extents to calculate the size of each fragment in each line
5052 wxRichTextLineList::compatibility_iterator lineNode
= m_cachedLines
.GetFirst();
5055 wxRichTextLine
* line
= lineNode
->GetData();
5056 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5058 // Loop through objects until we get to the one within range
5059 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
5063 wxRichTextObject
* child
= node2
->GetData();
5065 if (child
->GetRange().GetLength() > 0 && !child
->GetRange().IsOutside(lineRange
))
5067 wxRichTextRange rangeToUse
= lineRange
;
5068 rangeToUse
.LimitTo(child
->GetRange());
5070 // Find the size of the child from the text extents, and store in an array
5071 // for drawing later
5073 if (rangeToUse
.GetStart() > GetRange().GetStart())
5074 left
= partialExtents
[(rangeToUse
.GetStart()-1) - GetRange().GetStart()];
5075 int right
= partialExtents
[rangeToUse
.GetEnd() - GetRange().GetStart()];
5076 int sz
= right
- left
;
5077 line
->GetObjectSizes().Add(sz
);
5079 else if (child
->GetRange().GetStart() > lineRange
.GetEnd())
5080 // Can break out of inner loop now since we've passed this line's range
5083 node2
= node2
->GetNext();
5086 lineNode
= lineNode
->GetNext();
5094 /// Apply paragraph styles, such as centering, to wrapped lines
5095 /// TODO: take into account box attributes, possibly
5096 void wxRichTextParagraph::ApplyParagraphStyle(wxRichTextLine
* line
, const wxRichTextAttr
& attr
, const wxRect
& rect
, wxDC
& dc
)
5098 if (!attr
.HasAlignment())
5101 wxPoint pos
= line
->GetPosition();
5102 wxPoint originalPos
= pos
;
5103 wxSize size
= line
->GetSize();
5105 // centering, right-justification
5106 if (attr
.HasAlignment() && attr
.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE
)
5108 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
5109 pos
.x
= (rect
.GetWidth() - rightIndent
- size
.x
)/2 + pos
.x
;
5110 line
->SetPosition(pos
);
5112 else if (attr
.HasAlignment() && attr
.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT
)
5114 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
5115 pos
.x
= pos
.x
+ rect
.GetWidth() - size
.x
- rightIndent
;
5116 line
->SetPosition(pos
);
5119 if (pos
!= originalPos
)
5121 wxPoint inc
= pos
- originalPos
;
5123 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5127 wxRichTextObject
* child
= node
->GetData();
5128 if (child
->IsTopLevel() && !child
->GetRange().IsOutside(line
->GetAbsoluteRange()))
5129 child
->Move(child
->GetPosition() + inc
);
5131 node
= node
->GetNext();
5136 /// Insert text at the given position
5137 bool wxRichTextParagraph::InsertText(long pos
, const wxString
& text
)
5139 wxRichTextObject
* childToUse
= NULL
;
5140 wxRichTextObjectList::compatibility_iterator nodeToUse
= wxRichTextObjectList::compatibility_iterator();
5142 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5145 wxRichTextObject
* child
= node
->GetData();
5146 if (child
->GetRange().Contains(pos
) && child
->GetRange().GetLength() > 0)
5153 node
= node
->GetNext();
5158 wxRichTextPlainText
* textObject
= wxDynamicCast(childToUse
, wxRichTextPlainText
);
5161 int posInString
= pos
- textObject
->GetRange().GetStart();
5163 wxString newText
= textObject
->GetText().Mid(0, posInString
) +
5164 text
+ textObject
->GetText().Mid(posInString
);
5165 textObject
->SetText(newText
);
5167 int textLength
= text
.length();
5169 textObject
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart(),
5170 textObject
->GetRange().GetEnd() + textLength
));
5172 // Increment the end range of subsequent fragments in this paragraph.
5173 // We'll set the paragraph range itself at a higher level.
5175 wxRichTextObjectList::compatibility_iterator node
= nodeToUse
->GetNext();
5178 wxRichTextObject
* child
= node
->GetData();
5179 child
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart() + textLength
,
5180 textObject
->GetRange().GetEnd() + textLength
));
5182 node
= node
->GetNext();
5189 // TODO: if not a text object, insert at closest position, e.g. in front of it
5195 // Don't pass parent initially to suppress auto-setting of parent range.
5196 // We'll do that at a higher level.
5197 wxRichTextPlainText
* textObject
= new wxRichTextPlainText(text
, this);
5199 AppendChild(textObject
);
5206 void wxRichTextParagraph::Copy(const wxRichTextParagraph
& obj
)
5208 wxRichTextCompositeObject::Copy(obj
);
5211 /// Clear the cached lines
5212 void wxRichTextParagraph::ClearLines()
5214 WX_CLEAR_LIST(wxRichTextLineList
, m_cachedLines
);
5217 /// Get/set the object size for the given range. Returns false if the range
5218 /// is invalid for this object.
5219 bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
5221 if (!range
.IsWithin(GetRange()))
5224 if (flags
& wxRICHTEXT_UNFORMATTED
)
5226 // Just use unformatted data, assume no line breaks
5229 wxArrayInt childExtents
;
5238 int maxLineHeight
= 0;
5240 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5243 wxRichTextObject
* child
= node
->GetData();
5244 if (!child
->GetRange().IsOutside(range
))
5246 // Floating objects have a zero size within the paragraph.
5247 if (child
->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode())
5252 if (partialExtents
->GetCount() > 0)
5253 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5257 partialExtents
->Add(0 /* zero size */ + lastSize
);
5264 wxRichTextRange rangeToUse
= range
;
5265 rangeToUse
.LimitTo(child
->GetRange());
5266 int childDescent
= 0;
5268 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we've already cached the size,
5269 // but it's only going to be used after caching has taken place.
5270 if ((flags
& wxRICHTEXT_HEIGHT_ONLY
) && child
->GetCachedSize().y
!= 0)
5272 childDescent
= child
->GetDescent();
5273 childSize
= child
->GetCachedSize();
5275 if (childDescent
== 0)
5277 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5281 maxDescent
= wxMax(maxDescent
, childDescent
);
5282 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5285 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5287 sz
.y
= wxMax(sz
.y
, maxLineHeight
);
5288 sz
.x
+= childSize
.x
;
5289 descent
= maxDescent
;
5291 else if (child
->IsTopLevel())
5293 childDescent
= child
->GetDescent();
5294 childSize
= child
->GetCachedSize();
5296 if (childDescent
== 0)
5298 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5302 maxDescent
= wxMax(maxDescent
, childDescent
);
5303 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5306 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5308 sz
.y
= wxMax(sz
.y
, maxLineHeight
);
5309 sz
.x
+= childSize
.x
;
5310 descent
= maxDescent
;
5312 // FIXME: this won't change the original values.
5313 // Should we be calling GetRangeSize above instead of using cached values?
5315 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange()))
5317 child
->SetCachedSize(childSize
);
5318 child
->SetDescent(childDescent
);
5325 if (partialExtents
->GetCount() > 0)
5326 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5330 partialExtents
->Add(childSize
.x
+ lastSize
);
5333 else if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, context
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
), p
))
5335 if (childDescent
== 0)
5337 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5341 maxDescent
= wxMax(maxDescent
, childDescent
);
5342 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5345 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5347 sz
.y
= wxMax(sz
.y
, maxLineHeight
);
5348 sz
.x
+= childSize
.x
;
5349 descent
= maxDescent
;
5351 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange()))
5353 child
->SetCachedSize(childSize
);
5354 child
->SetDescent(childDescent
);
5360 if (partialExtents
->GetCount() > 0)
5361 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5366 for (i
= 0; i
< childExtents
.GetCount(); i
++)
5368 partialExtents
->Add(childExtents
[i
] + lastSize
);
5378 node
= node
->GetNext();
5384 // Use formatted data, with line breaks
5387 // We're going to loop through each line, and then for each line,
5388 // call GetRangeSize for the fragment that comprises that line.
5389 // Only we have to do that multiple times within the line, because
5390 // the line may be broken into pieces. For now ignore line break commands
5391 // (so we can assume that getting the unformatted size for a fragment
5392 // within a line is the actual size)
5394 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5397 wxRichTextLine
* line
= node
->GetData();
5398 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5399 if (!lineRange
.IsOutside(range
))
5403 int maxLineHeight
= 0;
5404 int maxLineWidth
= 0;
5406 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
5409 wxRichTextObject
* child
= node2
->GetData();
5411 if ((!child
->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && !child
->GetRange().IsOutside(lineRange
))
5413 wxRichTextRange rangeToUse
= lineRange
;
5414 rangeToUse
.LimitTo(child
->GetRange());
5415 if (child
->IsTopLevel())
5416 rangeToUse
= child
->GetOwnRange();
5419 int childDescent
= 0;
5420 if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, context
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
)))
5422 if (childDescent
== 0)
5424 // Assume that if descent is zero, this child can occupy the full line height
5425 // and does not need space for the line's maximum descent. So we influence
5426 // the overall max line height only.
5427 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5431 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5432 maxDescent
= wxMax(maxAscent
, childDescent
);
5434 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5435 maxLineWidth
+= childSize
.x
;
5439 node2
= node2
->GetNext();
5442 descent
= wxMax(descent
, maxDescent
);
5444 // Increase size by a line (TODO: paragraph spacing)
5445 sz
.y
+= maxLineHeight
;
5446 sz
.x
= wxMax(sz
.x
, maxLineWidth
);
5448 node
= node
->GetNext();
5455 /// Finds the absolute position and row height for the given character position
5456 bool wxRichTextParagraph::FindPosition(wxDC
& dc
, wxRichTextDrawingContext
& context
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
5460 wxRichTextLine
* line
= ((wxRichTextParagraphLayoutBox
*)GetParent())->GetLineAtPosition(0);
5462 *height
= line
->GetSize().y
;
5464 *height
= dc
.GetCharHeight();
5466 // -1 means 'the start of the buffer'.
5469 pt
= pt
+ line
->GetPosition();
5474 // The final position in a paragraph is taken to mean the position
5475 // at the start of the next paragraph.
5476 if (index
== GetRange().GetEnd())
5478 wxRichTextParagraphLayoutBox
* parent
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
5479 wxASSERT( parent
!= NULL
);
5481 // Find the height at the next paragraph, if any
5482 wxRichTextLine
* line
= parent
->GetLineAtPosition(index
+ 1);
5485 *height
= line
->GetSize().y
;
5486 pt
= line
->GetAbsolutePosition();
5490 *height
= dc
.GetCharHeight();
5491 int indent
= ConvertTenthsMMToPixels(dc
, m_attributes
.GetLeftIndent());
5492 pt
= wxPoint(indent
, GetCachedSize().y
);
5498 if (index
< GetRange().GetStart() || index
> GetRange().GetEnd())
5501 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5504 wxRichTextLine
* line
= node
->GetData();
5505 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5506 if (index
>= lineRange
.GetStart() && index
<= lineRange
.GetEnd())
5508 // If this is the last point in the line, and we're forcing the
5509 // returned value to be the start of the next line, do the required
5511 if (index
== lineRange
.GetEnd() && forceLineStart
)
5513 if (node
->GetNext())
5515 wxRichTextLine
* nextLine
= node
->GetNext()->GetData();
5516 *height
= nextLine
->GetSize().y
;
5517 pt
= nextLine
->GetAbsolutePosition();
5522 pt
.y
= line
->GetPosition().y
+ GetPosition().y
;
5524 wxRichTextRange
r(lineRange
.GetStart(), index
);
5528 // We find the size of the line up to this point,
5529 // then we can add this size to the line start position and
5530 // paragraph start position to find the actual position.
5532 if (GetRangeSize(r
, rangeSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, line
->GetPosition()+ GetPosition()))
5534 pt
.x
= line
->GetPosition().x
+ GetPosition().x
+ rangeSize
.x
;
5535 *height
= line
->GetSize().y
;
5542 node
= node
->GetNext();
5548 /// Hit-testing: returns a flag indicating hit test details, plus
5549 /// information about position
5550 int wxRichTextParagraph::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
5553 return wxRICHTEXT_HITTEST_NONE
;
5555 // If we're in the top-level container, then we can return
5556 // a suitable hit test code even if the point is outside the container area,
5557 // so that we can position the caret sensibly even if we don't
5558 // click on valid content. If we're not at the top-level, and the point
5559 // is not within this paragraph object, then we don't want to stop more
5560 // precise hit-testing from working prematurely, so return immediately.
5561 // NEW STRATEGY: use the parent boundary to test whether we're in the
5562 // right region, not the paragraph, since the paragraph may be positioned
5563 // some way in from where the user clicks.
5566 wxRichTextObject
* tempObj
, *tempContextObj
;
5567 if (GetParent() && GetParent()->wxRichTextObject::HitTest(dc
, context
, pt
, tmpPos
, & tempObj
, & tempContextObj
, flags
) == wxRICHTEXT_HITTEST_NONE
)
5568 return wxRICHTEXT_HITTEST_NONE
;
5571 wxRichTextObjectList::compatibility_iterator objNode
= m_children
.GetFirst();
5574 wxRichTextObject
* child
= objNode
->GetData();
5575 // Don't recurse if we have wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS,
5576 // and also, if this seems composite but actually is marked as atomic,
5578 if (child
->IsTopLevel() && ((flags
& wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS
) == 0) &&
5579 (! (((flags
& wxRICHTEXT_HITTEST_HONOUR_ATOMIC
) != 0) && child
->IsAtomic())))
5582 int hitTest
= child
->HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
);
5583 if (hitTest
!= wxRICHTEXT_HITTEST_NONE
)
5588 objNode
= objNode
->GetNext();
5591 wxPoint paraPos
= GetPosition();
5593 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5596 wxRichTextLine
* line
= node
->GetData();
5597 wxPoint linePos
= paraPos
+ line
->GetPosition();
5598 wxSize lineSize
= line
->GetSize();
5599 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5601 if (pt
.y
<= linePos
.y
+ lineSize
.y
)
5603 if (pt
.x
< linePos
.x
)
5605 textPosition
= lineRange
.GetStart();
5606 *obj
= FindObjectAtPosition(textPosition
);
5607 *contextObj
= GetContainer();
5608 return wxRICHTEXT_HITTEST_BEFORE
|wxRICHTEXT_HITTEST_OUTSIDE
;
5610 else if (pt
.x
>= (linePos
.x
+ lineSize
.x
))
5612 textPosition
= lineRange
.GetEnd();
5613 *obj
= FindObjectAtPosition(textPosition
);
5614 *contextObj
= GetContainer();
5615 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
;
5619 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5620 wxArrayInt partialExtents
;
5625 // This calculates the partial text extents
5626 GetRangeSize(lineRange
, paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, linePos
, & partialExtents
);
5628 int lastX
= linePos
.x
;
5630 for (i
= 0; i
< partialExtents
.GetCount(); i
++)
5632 int nextX
= partialExtents
[i
] + linePos
.x
;
5634 if (pt
.x
>= lastX
&& pt
.x
<= nextX
)
5636 textPosition
= i
+ lineRange
.GetStart(); // minus 1?
5638 *obj
= FindObjectAtPosition(textPosition
);
5639 *contextObj
= GetContainer();
5641 // So now we know it's between i-1 and i.
5642 // Let's see if we can be more precise about
5643 // which side of the position it's on.
5645 int midPoint
= (nextX
+ lastX
)/2;
5646 if (pt
.x
>= midPoint
)
5647 return wxRICHTEXT_HITTEST_AFTER
;
5649 return wxRICHTEXT_HITTEST_BEFORE
;
5656 int lastX
= linePos
.x
;
5657 for (i
= lineRange
.GetStart(); i
<= lineRange
.GetEnd(); i
++)
5662 wxRichTextRange
rangeToUse(lineRange
.GetStart(), i
);
5664 GetRangeSize(rangeToUse
, childSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, linePos
);
5666 int nextX
= childSize
.x
+ linePos
.x
;
5668 if (pt
.x
>= lastX
&& pt
.x
<= nextX
)
5672 *obj
= FindObjectAtPosition(textPosition
);
5673 *contextObj
= GetContainer();
5675 // So now we know it's between i-1 and i.
5676 // Let's see if we can be more precise about
5677 // which side of the position it's on.
5679 int midPoint
= (nextX
+ lastX
)/2;
5680 if (pt
.x
>= midPoint
)
5681 return wxRICHTEXT_HITTEST_AFTER
;
5683 return wxRICHTEXT_HITTEST_BEFORE
;
5694 node
= node
->GetNext();
5697 return wxRICHTEXT_HITTEST_NONE
;
5700 /// Split an object at this position if necessary, and return
5701 /// the previous object, or NULL if inserting at beginning.
5702 wxRichTextObject
* wxRichTextParagraph::SplitAt(long pos
, wxRichTextObject
** previousObject
)
5704 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5707 wxRichTextObject
* child
= node
->GetData();
5709 if (pos
== child
->GetRange().GetStart())
5713 if (node
->GetPrevious())
5714 *previousObject
= node
->GetPrevious()->GetData();
5716 *previousObject
= NULL
;
5722 if (child
->GetRange().Contains(pos
))
5724 // This should create a new object, transferring part of
5725 // the content to the old object and the rest to the new object.
5726 wxRichTextObject
* newObject
= child
->DoSplit(pos
);
5728 // If we couldn't split this object, just insert in front of it.
5731 // Maybe this is an empty string, try the next one
5736 // Insert the new object after 'child'
5737 if (node
->GetNext())
5738 m_children
.Insert(node
->GetNext(), newObject
);
5740 m_children
.Append(newObject
);
5741 newObject
->SetParent(this);
5744 *previousObject
= child
;
5750 node
= node
->GetNext();
5753 *previousObject
= NULL
;
5757 /// Move content to a list from obj on
5758 void wxRichTextParagraph::MoveToList(wxRichTextObject
* obj
, wxList
& list
)
5760 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(obj
);
5763 wxRichTextObject
* child
= node
->GetData();
5766 wxRichTextObjectList::compatibility_iterator oldNode
= node
;
5768 node
= node
->GetNext();
5770 m_children
.DeleteNode(oldNode
);
5774 /// Add content back from list
5775 void wxRichTextParagraph::MoveFromList(wxList
& list
)
5777 for (wxList::compatibility_iterator node
= list
.GetFirst(); node
; node
= node
->GetNext())
5779 AppendChild((wxRichTextObject
*) node
->GetData());
5784 void wxRichTextParagraph::CalculateRange(long start
, long& end
)
5786 wxRichTextCompositeObject::CalculateRange(start
, end
);
5788 // Add one for end of paragraph
5791 m_range
.SetRange(start
, end
);
5794 /// Find the object at the given position
5795 wxRichTextObject
* wxRichTextParagraph::FindObjectAtPosition(long position
)
5797 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5800 wxRichTextObject
* obj
= node
->GetData();
5801 if (obj
->GetRange().Contains(position
) ||
5802 obj
->GetRange().GetStart() == position
||
5803 obj
->GetRange().GetEnd() == position
)
5806 node
= node
->GetNext();
5811 /// Get the plain text searching from the start or end of the range.
5812 /// The resulting string may be shorter than the range given.
5813 bool wxRichTextParagraph::GetContiguousPlainText(wxString
& text
, const wxRichTextRange
& range
, bool fromStart
)
5815 text
= wxEmptyString
;
5819 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5822 wxRichTextObject
* obj
= node
->GetData();
5823 if (!obj
->GetRange().IsOutside(range
))
5825 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5828 text
+= textObj
->GetTextForRange(range
);
5836 node
= node
->GetNext();
5841 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetLast();
5844 wxRichTextObject
* obj
= node
->GetData();
5845 if (!obj
->GetRange().IsOutside(range
))
5847 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5850 text
= textObj
->GetTextForRange(range
) + text
;
5854 text
= wxT(" ") + text
;
5858 node
= node
->GetPrevious();
5865 /// Find a suitable wrap position.
5866 bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange
& range
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int availableSpace
, long& wrapPosition
, wxArrayInt
* partialExtents
)
5868 if (range
.GetLength() <= 0)
5871 // Find the first position where the line exceeds the available space.
5873 long breakPosition
= range
.GetEnd();
5875 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5876 if (partialExtents
&& partialExtents
->GetCount() >= (size_t) (GetRange().GetLength()-1)) // the final position in a paragraph is the newline
5880 if (range
.GetStart() > GetRange().GetStart())
5881 widthBefore
= (*partialExtents
)[range
.GetStart() - GetRange().GetStart() - 1];
5886 for (i
= (size_t) range
.GetStart(); i
<= (size_t) range
.GetEnd(); i
++)
5888 int widthFromStartOfThisRange
= (*partialExtents
)[i
- GetRange().GetStart()] - widthBefore
;
5890 if (widthFromStartOfThisRange
> availableSpace
)
5892 breakPosition
= i
-1;
5900 // Binary chop for speed
5901 long minPos
= range
.GetStart();
5902 long maxPos
= range
.GetEnd();
5905 if (minPos
== maxPos
)
5908 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
5910 if (sz
.x
> availableSpace
)
5911 breakPosition
= minPos
- 1;
5914 else if ((maxPos
- minPos
) == 1)
5917 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
5919 if (sz
.x
> availableSpace
)
5920 breakPosition
= minPos
- 1;
5923 GetRangeSize(wxRichTextRange(range
.GetStart(), maxPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
5924 if (sz
.x
> availableSpace
)
5925 breakPosition
= maxPos
-1;
5931 long nextPos
= minPos
+ ((maxPos
- minPos
) / 2);
5934 GetRangeSize(wxRichTextRange(range
.GetStart(), nextPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
5936 if (sz
.x
> availableSpace
)
5948 // Now we know the last position on the line.
5949 // Let's try to find a word break.
5952 if (GetContiguousPlainText(plainText
, wxRichTextRange(range
.GetStart(), breakPosition
), false))
5954 int newLinePos
= plainText
.Find(wxRichTextLineBreakChar
);
5955 if (newLinePos
!= wxNOT_FOUND
)
5957 breakPosition
= wxMax(0, range
.GetStart() + newLinePos
);
5961 int spacePos
= plainText
.Find(wxT(' '), true);
5962 int tabPos
= plainText
.Find(wxT('\t'), true);
5963 int pos
= wxMax(spacePos
, tabPos
);
5964 if (pos
!= wxNOT_FOUND
)
5966 int positionsFromEndOfString
= plainText
.length() - pos
- 1;
5967 breakPosition
= breakPosition
- positionsFromEndOfString
;
5972 wrapPosition
= breakPosition
;
5977 /// Get the bullet text for this paragraph.
5978 wxString
wxRichTextParagraph::GetBulletText()
5980 if (GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
||
5981 (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
))
5982 return wxEmptyString
;
5984 int number
= GetAttributes().GetBulletNumber();
5987 if ((GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ARABIC
) || (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
))
5989 text
.Printf(wxT("%d"), number
);
5991 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_UPPER
)
5993 // TODO: Unicode, and also check if number > 26
5994 text
.Printf(wxT("%c"), (wxChar
) (number
+64));
5996 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_LOWER
)
5998 // TODO: Unicode, and also check if number > 26
5999 text
.Printf(wxT("%c"), (wxChar
) (number
+96));
6001 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER
)
6003 text
= wxRichTextDecimalToRoman(number
);
6005 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER
)
6007 text
= wxRichTextDecimalToRoman(number
);
6010 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
)
6012 text
= GetAttributes().GetBulletText();
6015 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
)
6017 // The outline style relies on the text being computed statically,
6018 // since it depends on other levels points (e.g. 1.2.1.1). So normally the bullet text
6019 // should be stored in the attributes; if not, just use the number for this
6020 // level, as previously computed.
6021 if (!GetAttributes().GetBulletText().IsEmpty())
6022 text
= GetAttributes().GetBulletText();
6025 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PARENTHESES
)
6027 text
= wxT("(") + text
+ wxT(")");
6029 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_RIGHT_PARENTHESIS
)
6031 text
= text
+ wxT(")");
6034 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PERIOD
)
6042 /// Allocate or reuse a line object
6043 wxRichTextLine
* wxRichTextParagraph::AllocateLine(int pos
)
6045 if (pos
< (int) m_cachedLines
.GetCount())
6047 wxRichTextLine
* line
= m_cachedLines
.Item(pos
)->GetData();
6053 wxRichTextLine
* line
= new wxRichTextLine(this);
6054 m_cachedLines
.Append(line
);
6059 /// Clear remaining unused line objects, if any
6060 bool wxRichTextParagraph::ClearUnusedLines(int lineCount
)
6062 int cachedLineCount
= m_cachedLines
.GetCount();
6063 if ((int) cachedLineCount
> lineCount
)
6065 for (int i
= 0; i
< (int) (cachedLineCount
- lineCount
); i
++)
6067 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetLast();
6068 wxRichTextLine
* line
= node
->GetData();
6069 m_cachedLines
.Erase(node
);
6076 /// Get combined attributes of the base style, paragraph style and character style. We use this to dynamically
6077 /// retrieve the actual style.
6078 wxRichTextAttr
wxRichTextParagraph::GetCombinedAttributes(const wxRichTextAttr
& contentStyle
, bool includingBoxAttr
) const
6080 wxRichTextAttr attr
;
6081 wxRichTextParagraphLayoutBox
* buf
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
6084 attr
= buf
->GetBasicStyle();
6085 if (!includingBoxAttr
)
6087 attr
.GetTextBoxAttr().Reset();
6088 // The background colour will be painted by the container, and we don't
6089 // want to unnecessarily overwrite the background when we're drawing text
6090 // because this may erase the guideline (which appears just under the text
6091 // if there's no padding).
6092 attr
.SetFlags(attr
.GetFlags() & ~wxTEXT_ATTR_BACKGROUND_COLOUR
);
6094 wxRichTextApplyStyle(attr
, GetAttributes());
6097 attr
= GetAttributes();
6099 wxRichTextApplyStyle(attr
, contentStyle
);
6103 /// Get combined attributes of the base style and paragraph style.
6104 wxRichTextAttr
wxRichTextParagraph::GetCombinedAttributes(bool includingBoxAttr
) const
6106 wxRichTextAttr attr
;
6107 wxRichTextParagraphLayoutBox
* buf
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
6110 attr
= buf
->GetBasicStyle();
6111 if (!includingBoxAttr
)
6112 attr
.GetTextBoxAttr().Reset();
6113 wxRichTextApplyStyle(attr
, GetAttributes());
6116 attr
= GetAttributes();
6121 // Create default tabstop array
6122 void wxRichTextParagraph::InitDefaultTabs()
6124 // create a default tab list at 10 mm each.
6125 for (int i
= 0; i
< 20; ++i
)
6127 sm_defaultTabs
.Add(i
*100);
6131 // Clear default tabstop array
6132 void wxRichTextParagraph::ClearDefaultTabs()
6134 sm_defaultTabs
.Clear();
6137 void wxRichTextParagraph::LayoutFloat(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
, wxRichTextFloatCollector
* floatCollector
)
6139 wxRichTextObjectList::compatibility_iterator node
= GetChildren().GetFirst();
6142 wxRichTextObject
* anchored
= node
->GetData();
6143 if (anchored
&& anchored
->IsFloating() && !floatCollector
->HasFloat(anchored
))
6146 wxRichTextAttr
parentAttr(GetAttributes());
6147 context
.ApplyVirtualAttributes(parentAttr
, this);
6150 wxRect availableSpace
= GetParent()->GetAvailableContentArea(dc
, context
, rect
);
6152 anchored
->LayoutToBestSize(dc
, context
, GetBuffer(),
6153 parentAttr
, anchored
->GetAttributes(),
6154 parentRect
, availableSpace
,
6156 wxSize size
= anchored
->GetCachedSize();
6160 anchored
->GetRangeSize(anchored
->GetRange(), size
, descent
, dc
, context
, style
);
6164 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().IsValid())
6166 offsetY
= anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetValue();
6167 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
6169 offsetY
= ConvertTenthsMMToPixels(dc
, offsetY
);
6173 int pos
= floatCollector
->GetFitPosition(anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode(), rect
.y
+ offsetY
, size
.y
);
6175 /* Update the offset */
6176 int newOffsetY
= pos
- rect
.y
;
6177 if (newOffsetY
!= offsetY
)
6179 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
6180 newOffsetY
= ConvertPixelsToTenthsMM(dc
, newOffsetY
);
6181 anchored
->GetAttributes().GetTextBoxAttr().GetTop().SetValue(newOffsetY
);
6184 if (anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT
)
6186 else if (anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT
)
6187 x
= rect
.x
+ rect
.width
- size
.x
;
6189 //anchored->SetPosition(wxPoint(x, pos));
6190 anchored
->Move(wxPoint(x
, pos
)); // should move children
6191 anchored
->SetCachedSize(size
);
6192 floatCollector
->CollectFloat(this, anchored
);
6195 node
= node
->GetNext();
6199 // Get the first position from pos that has a line break character.
6200 long wxRichTextParagraph::GetFirstLineBreakPosition(long pos
)
6202 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
6205 wxRichTextObject
* obj
= node
->GetData();
6206 if (pos
>= obj
->GetRange().GetStart() && pos
<= obj
->GetRange().GetEnd())
6208 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
6211 long breakPos
= textObj
->GetFirstLineBreakPosition(pos
);
6216 node
= node
->GetNext();
6223 * This object represents a line in a paragraph, and stores
6224 * offsets from the start of the paragraph representing the
6225 * start and end positions of the line.
6228 wxRichTextLine::wxRichTextLine(wxRichTextParagraph
* parent
)
6234 void wxRichTextLine::Init(wxRichTextParagraph
* parent
)
6237 m_range
.SetRange(-1, -1);
6238 m_pos
= wxPoint(0, 0);
6239 m_size
= wxSize(0, 0);
6241 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6242 m_objectSizes
.Clear();
6247 void wxRichTextLine::Copy(const wxRichTextLine
& obj
)
6249 m_range
= obj
.m_range
;
6250 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6251 m_objectSizes
= obj
.m_objectSizes
;
6255 /// Get the absolute object position
6256 wxPoint
wxRichTextLine::GetAbsolutePosition() const
6258 return m_parent
->GetPosition() + m_pos
;
6261 /// Get the absolute range
6262 wxRichTextRange
wxRichTextLine::GetAbsoluteRange() const
6264 wxRichTextRange
range(m_range
.GetStart() + m_parent
->GetRange().GetStart(), 0);
6265 range
.SetEnd(range
.GetStart() + m_range
.GetLength()-1);
6270 * wxRichTextPlainText
6271 * This object represents a single piece of text.
6274 IMPLEMENT_DYNAMIC_CLASS(wxRichTextPlainText
, wxRichTextObject
)
6276 wxRichTextPlainText::wxRichTextPlainText(const wxString
& text
, wxRichTextObject
* parent
, wxRichTextAttr
* style
):
6277 wxRichTextObject(parent
)
6280 SetAttributes(*style
);
6285 #define USE_KERNING_FIX 1
6287 // If insufficient tabs are defined, this is the tab width used
6288 #define WIDTH_FOR_DEFAULT_TABS 50
6291 bool wxRichTextPlainText::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int WXUNUSED(style
))
6293 wxRichTextParagraph
* para
= wxDynamicCast(GetParent(), wxRichTextParagraph
);
6294 wxASSERT (para
!= NULL
);
6296 wxRichTextAttr
textAttr(para
? para
->GetCombinedAttributes(GetAttributes(), false /* no box attributes */) : GetAttributes());
6297 context
.ApplyVirtualAttributes(textAttr
, this);
6299 // Let's make the assumption for now that for content in a paragraph, including
6300 // text, we never have a discontinuous selection. So we only deal with a
6302 wxRichTextRange selectionRange
;
6303 if (selection
.IsValid())
6305 wxRichTextRangeArray selectionRanges
= selection
.GetSelectionForObject(this);
6306 if (selectionRanges
.GetCount() > 0)
6307 selectionRange
= selectionRanges
[0];
6309 selectionRange
= wxRICHTEXT_NO_SELECTION
;
6312 selectionRange
= wxRICHTEXT_NO_SELECTION
;
6314 int offset
= GetRange().GetStart();
6316 // Replace line break characters with spaces
6317 wxString str
= m_text
;
6318 wxString toRemove
= wxRichTextLineBreakChar
;
6319 str
.Replace(toRemove
, wxT(" "));
6320 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS
|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS
)))
6323 long len
= range
.GetLength();
6324 wxString stringChunk
= str
.Mid(range
.GetStart() - offset
, (size_t) len
);
6326 // Test for the optimized situations where all is selected, or none
6329 wxFont
textFont(GetBuffer()->GetFontTable().FindFont(textAttr
));
6330 wxCheckSetFont(dc
, textFont
);
6331 int charHeight
= dc
.GetCharHeight();
6334 if ( textFont
.IsOk() )
6336 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SMALL_CAPITALS
))
6338 textFont
.SetPointSize((int) (textFont
.GetPointSize()*0.75));
6339 wxCheckSetFont(dc
, textFont
);
6340 charHeight
= dc
.GetCharHeight();
6343 if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
) )
6345 if (textFont
.IsUsingSizeInPixels())
6347 double size
= static_cast<double>(textFont
.GetPixelSize().y
) / wxSCRIPT_MUL_FACTOR
;
6348 textFont
.SetPixelSize(wxSize(0, static_cast<int>(size
)));
6354 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6355 textFont
.SetPointSize(static_cast<int>(size
));
6359 wxCheckSetFont(dc
, textFont
);
6361 else if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) )
6363 if (textFont
.IsUsingSizeInPixels())
6365 double size
= static_cast<double>(textFont
.GetPixelSize().y
) / wxSCRIPT_MUL_FACTOR
;
6366 textFont
.SetPixelSize(wxSize(0, static_cast<int>(size
)));
6368 int sub_height
= static_cast<int>(static_cast<double>(charHeight
) / wxSCRIPT_MUL_FACTOR
);
6369 y
= rect
.y
+ (rect
.height
- sub_height
+ (descent
- m_descent
));
6373 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6374 textFont
.SetPointSize(static_cast<int>(size
));
6376 int sub_height
= static_cast<int>(static_cast<double>(charHeight
) / wxSCRIPT_MUL_FACTOR
);
6377 y
= rect
.y
+ (rect
.height
- sub_height
+ (descent
- m_descent
));
6379 wxCheckSetFont(dc
, textFont
);
6384 y
= rect
.y
+ (rect
.height
- charHeight
- (descent
- m_descent
));
6390 y
= rect
.y
+ (rect
.height
- charHeight
- (descent
- m_descent
));
6393 // TODO: new selection code
6395 // (a) All selected.
6396 if (selectionRange
.GetStart() <= range
.GetStart() && selectionRange
.GetEnd() >= range
.GetEnd())
6398 DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, true);
6400 // (b) None selected.
6401 else if (selectionRange
.GetEnd() < range
.GetStart() || selectionRange
.GetStart() > range
.GetEnd())
6403 // Draw all unselected
6404 DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, false);
6408 // (c) Part selected, part not
6409 // Let's draw unselected chunk, selected chunk, then unselected chunk.
6411 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6413 // 1. Initial unselected chunk, if any, up until start of selection.
6414 if (selectionRange
.GetStart() > range
.GetStart() && selectionRange
.GetStart() <= range
.GetEnd())
6416 int r1
= range
.GetStart();
6417 int s1
= selectionRange
.GetStart()-1;
6418 int fragmentLen
= s1
- r1
+ 1;
6419 if (fragmentLen
< 0)
6421 wxLogDebug(wxT("Mid(%d, %d"), (int)(r1
- offset
), (int)fragmentLen
);
6423 wxString stringFragment
= str
.Mid(r1
- offset
, fragmentLen
);
6425 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false);
6428 if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
)
6430 // Compensate for kerning difference
6431 wxString
stringFragment2(str
.Mid(r1
- offset
, fragmentLen
+1));
6432 wxString
stringFragment3(str
.Mid(r1
- offset
+ fragmentLen
, 1));
6434 wxCoord w1
, h1
, w2
, h2
, w3
, h3
;
6435 dc
.GetTextExtent(stringFragment
, & w1
, & h1
);
6436 dc
.GetTextExtent(stringFragment2
, & w2
, & h2
);
6437 dc
.GetTextExtent(stringFragment3
, & w3
, & h3
);
6439 int kerningDiff
= (w1
+ w3
) - w2
;
6440 x
= x
- kerningDiff
;
6445 // 2. Selected chunk, if any.
6446 if (selectionRange
.GetEnd() >= range
.GetStart())
6448 int s1
= wxMax(selectionRange
.GetStart(), range
.GetStart());
6449 int s2
= wxMin(selectionRange
.GetEnd(), range
.GetEnd());
6451 int fragmentLen
= s2
- s1
+ 1;
6452 if (fragmentLen
< 0)
6454 wxLogDebug(wxT("Mid(%d, %d"), (int)(s1
- offset
), (int)fragmentLen
);
6456 wxString stringFragment
= str
.Mid(s1
- offset
, fragmentLen
);
6458 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, true);
6461 if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
)
6463 // Compensate for kerning difference
6464 wxString
stringFragment2(str
.Mid(s1
- offset
, fragmentLen
+1));
6465 wxString
stringFragment3(str
.Mid(s1
- offset
+ fragmentLen
, 1));
6467 wxCoord w1
, h1
, w2
, h2
, w3
, h3
;
6468 dc
.GetTextExtent(stringFragment
, & w1
, & h1
);
6469 dc
.GetTextExtent(stringFragment2
, & w2
, & h2
);
6470 dc
.GetTextExtent(stringFragment3
, & w3
, & h3
);
6472 int kerningDiff
= (w1
+ w3
) - w2
;
6473 x
= x
- kerningDiff
;
6478 // 3. Remaining unselected chunk, if any
6479 if (selectionRange
.GetEnd() < range
.GetEnd())
6481 int s2
= wxMin(selectionRange
.GetEnd()+1, range
.GetEnd());
6482 int r2
= range
.GetEnd();
6484 int fragmentLen
= r2
- s2
+ 1;
6485 if (fragmentLen
< 0)
6487 wxLogDebug(wxT("Mid(%d, %d"), (int)(s2
- offset
), (int)fragmentLen
);
6489 wxString stringFragment
= str
.Mid(s2
- offset
, fragmentLen
);
6491 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false);
6498 bool wxRichTextPlainText::DrawTabbedString(wxDC
& dc
, const wxRichTextAttr
& attr
, const wxRect
& rect
,wxString
& str
, wxCoord
& x
, wxCoord
& y
, bool selected
)
6500 bool hasTabs
= (str
.Find(wxT('\t')) != wxNOT_FOUND
);
6502 wxArrayInt tabArray
;
6506 if (attr
.GetTabs().IsEmpty())
6507 tabArray
= wxRichTextParagraph::GetDefaultTabs();
6509 tabArray
= attr
.GetTabs();
6510 tabCount
= tabArray
.GetCount();
6512 for (int i
= 0; i
< tabCount
; ++i
)
6514 int pos
= tabArray
[i
];
6515 pos
= ConvertTenthsMMToPixels(dc
, pos
);
6522 int nextTabPos
= -1;
6528 wxColour
highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
));
6529 wxColour
highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
));
6531 wxCheckSetBrush(dc
, wxBrush(highlightColour
));
6532 wxCheckSetPen(dc
, wxPen(highlightColour
));
6533 dc
.SetTextForeground(highlightTextColour
);
6534 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6538 dc
.SetTextForeground(attr
.GetTextColour());
6540 if (attr
.HasFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
) && attr
.GetBackgroundColour().IsOk())
6542 dc
.SetBackgroundMode(wxBRUSHSTYLE_SOLID
);
6543 dc
.SetTextBackground(attr
.GetBackgroundColour());
6546 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6549 wxCoord x_orig
= GetParent()->GetPosition().x
;
6552 // the string has a tab
6553 // break up the string at the Tab
6554 wxString stringChunk
= str
.BeforeFirst(wxT('\t'));
6555 str
= str
.AfterFirst(wxT('\t'));
6556 dc
.GetTextExtent(stringChunk
, & w
, & h
);
6558 bool not_found
= true;
6559 for (int i
= 0; i
< tabCount
&& not_found
; ++i
)
6561 nextTabPos
= tabArray
.Item(i
) + x_orig
;
6563 // Find the next tab position.
6564 // Even if we're at the end of the tab array, we must still draw the chunk.
6566 if (nextTabPos
> tabPos
|| (i
== (tabCount
- 1)))
6568 if (nextTabPos
<= tabPos
)
6570 int defaultTabWidth
= ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
);
6571 nextTabPos
= tabPos
+ defaultTabWidth
;
6578 wxRect
selRect(x
, rect
.y
, w
, rect
.GetHeight());
6579 dc
.DrawRectangle(selRect
);
6581 dc
.DrawText(stringChunk
, x
, y
);
6583 if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
))
6585 wxPen oldPen
= dc
.GetPen();
6586 wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1));
6587 dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5));
6588 wxCheckSetPen(dc
, oldPen
);
6594 hasTabs
= (str
.Find(wxT('\t')) != wxNOT_FOUND
);
6599 dc
.GetTextExtent(str
, & w
, & h
);
6602 wxRect
selRect(x
, rect
.y
, w
, rect
.GetHeight());
6603 dc
.DrawRectangle(selRect
);
6605 dc
.DrawText(str
, x
, y
);
6607 if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
))
6609 wxPen oldPen
= dc
.GetPen();
6610 wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1));
6611 dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5));
6612 wxCheckSetPen(dc
, oldPen
);
6621 /// Lay the item out
6622 bool wxRichTextPlainText::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& WXUNUSED(rect
), const wxRect
& WXUNUSED(parentRect
), int WXUNUSED(style
))
6624 // Only lay out if we haven't already cached the size
6626 GetRangeSize(GetRange(), m_size
, m_descent
, dc
, context
, 0, wxPoint(0, 0));
6628 // Eventually we want to have a reasonable estimate of minimum size.
6629 m_minSize
= wxSize(0, 0);
6634 void wxRichTextPlainText::Copy(const wxRichTextPlainText
& obj
)
6636 wxRichTextObject::Copy(obj
);
6638 m_text
= obj
.m_text
;
6641 /// Get/set the object size for the given range. Returns false if the range
6642 /// is invalid for this object.
6643 bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int WXUNUSED(flags
), wxPoint position
, wxArrayInt
* partialExtents
) const
6645 if (!range
.IsWithin(GetRange()))
6648 wxRichTextParagraph
* para
= wxDynamicCast(GetParent(), wxRichTextParagraph
);
6649 wxASSERT (para
!= NULL
);
6651 int relativeX
= position
.x
- GetParent()->GetPosition().x
;
6653 wxRichTextAttr
textAttr(para
? para
->GetCombinedAttributes(GetAttributes()) : GetAttributes());
6654 context
.ApplyVirtualAttributes(textAttr
, (wxRichTextObject
*) this);
6656 // Always assume unformatted text, since at this level we have no knowledge
6657 // of line breaks - and we don't need it, since we'll calculate size within
6658 // formatted text by doing it in chunks according to the line ranges
6660 bool bScript(false);
6661 wxFont
font(GetBuffer()->GetFontTable().FindFont(textAttr
));
6664 if ( textAttr
.HasTextEffects() && ( (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
)
6665 || (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) ) )
6667 wxFont textFont
= font
;
6668 if (textFont
.IsUsingSizeInPixels())
6670 double size
= static_cast<double>(textFont
.GetPixelSize().y
) / wxSCRIPT_MUL_FACTOR
;
6671 textFont
.SetPixelSize(wxSize(0, static_cast<int>(size
)));
6675 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6676 textFont
.SetPointSize(static_cast<int>(size
));
6678 wxCheckSetFont(dc
, textFont
);
6681 else if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SMALL_CAPITALS
))
6683 wxFont textFont
= font
;
6684 textFont
.SetPointSize((int) (textFont
.GetPointSize()*0.75));
6685 wxCheckSetFont(dc
, textFont
);
6690 wxCheckSetFont(dc
, font
);
6694 bool haveDescent
= false;
6695 int startPos
= range
.GetStart() - GetRange().GetStart();
6696 long len
= range
.GetLength();
6698 wxString
str(m_text
);
6699 wxString toReplace
= wxRichTextLineBreakChar
;
6700 str
.Replace(toReplace
, wxT(" "));
6702 wxString stringChunk
= str
.Mid(startPos
, (size_t) len
);
6704 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS
|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS
)))
6705 stringChunk
.MakeUpper();
6709 if (stringChunk
.Find(wxT('\t')) != wxNOT_FOUND
)
6711 // the string has a tab
6712 wxArrayInt tabArray
;
6713 if (textAttr
.GetTabs().IsEmpty())
6714 tabArray
= wxRichTextParagraph::GetDefaultTabs();
6716 tabArray
= textAttr
.GetTabs();
6718 int tabCount
= tabArray
.GetCount();
6720 for (int i
= 0; i
< tabCount
; ++i
)
6722 int pos
= tabArray
[i
];
6723 pos
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, pos
);
6727 int nextTabPos
= -1;
6729 while (stringChunk
.Find(wxT('\t')) >= 0)
6731 int absoluteWidth
= 0;
6733 // the string has a tab
6734 // break up the string at the Tab
6735 wxString stringFragment
= stringChunk
.BeforeFirst(wxT('\t'));
6736 stringChunk
= stringChunk
.AfterFirst(wxT('\t'));
6741 if (partialExtents
->GetCount() > 0)
6742 oldWidth
= (*partialExtents
)[partialExtents
->GetCount()-1];
6746 // Add these partial extents
6748 dc
.GetPartialTextExtents(stringFragment
, p
);
6750 for (j
= 0; j
< p
.GetCount(); j
++)
6751 partialExtents
->Add(oldWidth
+ p
[j
]);
6753 if (partialExtents
->GetCount() > 0)
6754 absoluteWidth
= (*partialExtents
)[(*partialExtents
).GetCount()-1] + relativeX
;
6756 absoluteWidth
= relativeX
;
6760 dc
.GetTextExtent(stringFragment
, & w
, & h
);
6762 absoluteWidth
= width
+ relativeX
;
6766 bool notFound
= true;
6767 for (int i
= 0; i
< tabCount
&& notFound
; ++i
)
6769 nextTabPos
= tabArray
.Item(i
);
6771 // Find the next tab position.
6772 // Even if we're at the end of the tab array, we must still process the chunk.
6774 if (nextTabPos
> absoluteWidth
|| (i
== (tabCount
- 1)))
6776 if (nextTabPos
<= absoluteWidth
)
6778 int defaultTabWidth
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
);
6779 nextTabPos
= absoluteWidth
+ defaultTabWidth
;
6783 width
= nextTabPos
- relativeX
;
6786 partialExtents
->Add(width
);
6792 if (!stringChunk
.IsEmpty())
6797 if (partialExtents
->GetCount() > 0)
6798 oldWidth
= (*partialExtents
)[partialExtents
->GetCount()-1];
6802 // Add these partial extents
6804 dc
.GetPartialTextExtents(stringChunk
, p
);
6806 for (j
= 0; j
< p
.GetCount(); j
++)
6807 partialExtents
->Add(oldWidth
+ p
[j
]);
6811 dc
.GetTextExtent(stringChunk
, & w
, & h
, & descent
);
6819 int charHeight
= dc
.GetCharHeight();
6820 if ((*partialExtents
).GetCount() > 0)
6821 w
= (*partialExtents
)[partialExtents
->GetCount()-1];
6824 size
= wxSize(w
, charHeight
);
6828 size
= wxSize(width
, dc
.GetCharHeight());
6832 dc
.GetTextExtent(wxT("X"), & w
, & h
, & descent
);
6840 /// Do a split, returning an object containing the second part, and setting
6841 /// the first part in 'this'.
6842 wxRichTextObject
* wxRichTextPlainText::DoSplit(long pos
)
6844 long index
= pos
- GetRange().GetStart();
6846 if (index
< 0 || index
>= (int) m_text
.length())
6849 wxString firstPart
= m_text
.Mid(0, index
);
6850 wxString secondPart
= m_text
.Mid(index
);
6854 wxRichTextPlainText
* newObject
= new wxRichTextPlainText(secondPart
);
6855 newObject
->SetAttributes(GetAttributes());
6856 newObject
->SetProperties(GetProperties());
6858 newObject
->SetRange(wxRichTextRange(pos
, GetRange().GetEnd()));
6859 GetRange().SetEnd(pos
-1);
6865 void wxRichTextPlainText::CalculateRange(long start
, long& end
)
6867 end
= start
+ m_text
.length() - 1;
6868 m_range
.SetRange(start
, end
);
6872 bool wxRichTextPlainText::DeleteRange(const wxRichTextRange
& range
)
6874 wxRichTextRange r
= range
;
6876 r
.LimitTo(GetRange());
6878 if (r
.GetStart() == GetRange().GetStart() && r
.GetEnd() == GetRange().GetEnd())
6884 long startIndex
= r
.GetStart() - GetRange().GetStart();
6885 long len
= r
.GetLength();
6887 m_text
= m_text
.Mid(0, startIndex
) + m_text
.Mid(startIndex
+len
);
6891 /// Get text for the given range.
6892 wxString
wxRichTextPlainText::GetTextForRange(const wxRichTextRange
& range
) const
6894 wxRichTextRange r
= range
;
6896 r
.LimitTo(GetRange());
6898 long startIndex
= r
.GetStart() - GetRange().GetStart();
6899 long len
= r
.GetLength();
6901 return m_text
.Mid(startIndex
, len
);
6904 /// Returns true if this object can merge itself with the given one.
6905 bool wxRichTextPlainText::CanMerge(wxRichTextObject
* object
) const
6907 return object
->GetClassInfo() == wxCLASSINFO(wxRichTextPlainText
) &&
6908 (m_text
.empty() || (wxTextAttrEq(GetAttributes(), object
->GetAttributes()) && m_properties
== object
->GetProperties()));
6911 /// Returns true if this object merged itself with the given one.
6912 /// The calling code will then delete the given object.
6913 bool wxRichTextPlainText::Merge(wxRichTextObject
* object
)
6915 wxRichTextPlainText
* textObject
= wxDynamicCast(object
, wxRichTextPlainText
);
6916 wxASSERT( textObject
!= NULL
);
6920 m_text
+= textObject
->GetText();
6921 wxRichTextApplyStyle(m_attributes
, textObject
->GetAttributes());
6928 /// Dump to output stream for debugging
6929 void wxRichTextPlainText::Dump(wxTextOutputStream
& stream
)
6931 wxRichTextObject::Dump(stream
);
6932 stream
<< m_text
<< wxT("\n");
6935 /// Get the first position from pos that has a line break character.
6936 long wxRichTextPlainText::GetFirstLineBreakPosition(long pos
)
6939 int len
= m_text
.length();
6940 int startPos
= pos
- m_range
.GetStart();
6941 for (i
= startPos
; i
< len
; i
++)
6943 wxChar ch
= m_text
[i
];
6944 if (ch
== wxRichTextLineBreakChar
)
6946 return i
+ m_range
.GetStart();
6954 * This is a kind of box, used to represent the whole buffer
6957 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBuffer
, wxRichTextParagraphLayoutBox
)
6959 wxList
wxRichTextBuffer::sm_handlers
;
6960 wxList
wxRichTextBuffer::sm_drawingHandlers
;
6961 wxRichTextFieldTypeHashMap
wxRichTextBuffer::sm_fieldTypes
;
6962 wxRichTextRenderer
* wxRichTextBuffer::sm_renderer
= NULL
;
6963 int wxRichTextBuffer::sm_bulletRightMargin
= 20;
6964 float wxRichTextBuffer::sm_bulletProportion
= (float) 0.3;
6965 bool wxRichTextBuffer::sm_floatingLayoutMode
= true;
6968 void wxRichTextBuffer::Init()
6970 m_commandProcessor
= new wxCommandProcessor
;
6971 m_styleSheet
= NULL
;
6973 m_batchedCommandDepth
= 0;
6974 m_batchedCommand
= NULL
;
6978 m_dimensionScale
= 1.0;
6984 wxRichTextBuffer::~wxRichTextBuffer()
6986 delete m_commandProcessor
;
6987 delete m_batchedCommand
;
6990 ClearEventHandlers();
6993 void wxRichTextBuffer::ResetAndClearCommands()
6997 GetCommandProcessor()->ClearCommands();
7000 Invalidate(wxRICHTEXT_ALL
);
7003 void wxRichTextBuffer::Copy(const wxRichTextBuffer
& obj
)
7005 wxRichTextParagraphLayoutBox::Copy(obj
);
7007 m_styleSheet
= obj
.m_styleSheet
;
7008 m_modified
= obj
.m_modified
;
7009 m_batchedCommandDepth
= 0;
7010 if (m_batchedCommand
)
7011 delete m_batchedCommand
;
7012 m_batchedCommand
= NULL
;
7013 m_suppressUndo
= obj
.m_suppressUndo
;
7014 m_invalidRange
= obj
.m_invalidRange
;
7015 m_dimensionScale
= obj
.m_dimensionScale
;
7016 m_fontScale
= obj
.m_fontScale
;
7019 /// Push style sheet to top of stack
7020 bool wxRichTextBuffer::PushStyleSheet(wxRichTextStyleSheet
* styleSheet
)
7023 styleSheet
->InsertSheet(m_styleSheet
);
7025 SetStyleSheet(styleSheet
);
7030 /// Pop style sheet from top of stack
7031 wxRichTextStyleSheet
* wxRichTextBuffer::PopStyleSheet()
7035 wxRichTextStyleSheet
* oldSheet
= m_styleSheet
;
7036 m_styleSheet
= oldSheet
->GetNextSheet();
7045 /// Submit command to insert paragraphs
7046 bool wxRichTextBuffer::InsertParagraphsWithUndo(long pos
, const wxRichTextParagraphLayoutBox
& paragraphs
, wxRichTextCtrl
* ctrl
, int flags
)
7048 return ctrl
->GetFocusObject()->InsertParagraphsWithUndo(this, pos
, paragraphs
, ctrl
, flags
);
7051 /// Submit command to insert paragraphs
7052 bool wxRichTextParagraphLayoutBox::InsertParagraphsWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxRichTextParagraphLayoutBox
& paragraphs
, wxRichTextCtrl
* ctrl
, int WXUNUSED(flags
))
7054 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7056 action
->GetNewParagraphs() = paragraphs
;
7058 action
->SetPosition(pos
);
7060 wxRichTextRange range
= wxRichTextRange(pos
, pos
+ paragraphs
.GetOwnRange().GetEnd() - 1);
7061 if (!paragraphs
.GetPartialParagraph())
7062 range
.SetEnd(range
.GetEnd()+1);
7064 // Set the range we'll need to delete in Undo
7065 action
->SetRange(range
);
7067 buffer
->SubmitAction(action
);
7072 /// Submit command to insert the given text
7073 bool wxRichTextBuffer::InsertTextWithUndo(long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
, int flags
)
7075 return ctrl
->GetFocusObject()->InsertTextWithUndo(this, pos
, text
, ctrl
, flags
);
7078 /// Submit command to insert the given text
7079 bool wxRichTextParagraphLayoutBox::InsertTextWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
, int flags
)
7081 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7083 wxRichTextAttr
* p
= NULL
;
7084 wxRichTextAttr paraAttr
;
7085 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7087 // Get appropriate paragraph style
7088 paraAttr
= GetStyleForNewParagraph(buffer
, pos
, false, false);
7089 if (!paraAttr
.IsDefault())
7093 action
->GetNewParagraphs().AddParagraphs(text
, p
);
7095 int length
= action
->GetNewParagraphs().GetOwnRange().GetLength();
7097 if (!text
.empty() && text
.Last() != wxT('\n'))
7099 // Don't count the newline when undoing
7101 action
->GetNewParagraphs().SetPartialParagraph(true);
7103 else if (!text
.empty() && text
.Last() == wxT('\n'))
7106 action
->SetPosition(pos
);
7108 // Set the range we'll need to delete in Undo
7109 action
->SetRange(wxRichTextRange(pos
, pos
+ length
- 1));
7111 buffer
->SubmitAction(action
);
7116 /// Submit command to insert the given text
7117 bool wxRichTextBuffer::InsertNewlineWithUndo(long pos
, wxRichTextCtrl
* ctrl
, int flags
)
7119 return ctrl
->GetFocusObject()->InsertNewlineWithUndo(this, pos
, ctrl
, flags
);
7122 /// Submit command to insert the given text
7123 bool wxRichTextParagraphLayoutBox::InsertNewlineWithUndo(wxRichTextBuffer
* buffer
, long pos
, wxRichTextCtrl
* ctrl
, int flags
)
7125 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7127 wxRichTextAttr
* p
= NULL
;
7128 wxRichTextAttr paraAttr
;
7129 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7131 paraAttr
= GetStyleForNewParagraph(buffer
, pos
, false, true /* look for next paragraph style */);
7132 if (!paraAttr
.IsDefault())
7136 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7137 // Don't include box attributes such as margins
7138 attr
.GetTextBoxAttr().Reset();
7140 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(wxEmptyString
, this, & attr
);
7141 action
->GetNewParagraphs().AppendChild(newPara
);
7142 action
->GetNewParagraphs().UpdateRanges();
7143 action
->GetNewParagraphs().SetPartialParagraph(false);
7144 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
, false);
7148 newPara
->SetAttributes(*p
);
7150 if (flags
& wxRICHTEXT_INSERT_INTERACTIVE
)
7152 if (para
&& para
->GetRange().GetEnd() == pos
)
7155 // Now see if we need to number the paragraph.
7156 if (newPara
->GetAttributes().HasBulletNumber())
7158 wxRichTextAttr numberingAttr
;
7159 if (FindNextParagraphNumber(para
, numberingAttr
))
7160 wxRichTextApplyStyle(newPara
->GetAttributes(), (const wxRichTextAttr
&) numberingAttr
);
7164 action
->SetPosition(pos
);
7166 // Use the default character style
7167 if (!buffer
->GetDefaultStyle().IsDefault() && newPara
->GetChildren().GetFirst())
7169 // Check whether the default style merely reflects the paragraph/basic style,
7170 // in which case don't apply it.
7171 wxRichTextAttr
defaultStyle(buffer
->GetDefaultStyle());
7172 defaultStyle
.GetTextBoxAttr().Reset();
7173 wxRichTextAttr toApply
;
7176 wxRichTextAttr combinedAttr
= para
->GetCombinedAttributes(true /* include box attributes */);
7177 wxRichTextAttr newAttr
;
7178 // This filters out attributes that are accounted for by the current
7179 // paragraph/basic style
7180 wxRichTextApplyStyle(toApply
, defaultStyle
, & combinedAttr
);
7183 toApply
= defaultStyle
;
7185 if (!toApply
.IsDefault())
7186 newPara
->GetChildren().GetFirst()->GetData()->SetAttributes(toApply
);
7189 // Set the range we'll need to delete in Undo
7190 action
->SetRange(wxRichTextRange(pos1
, pos1
));
7192 buffer
->SubmitAction(action
);
7197 /// Submit command to insert the given image
7198 bool wxRichTextBuffer::InsertImageWithUndo(long pos
, const wxRichTextImageBlock
& imageBlock
, wxRichTextCtrl
* ctrl
, int flags
,
7199 const wxRichTextAttr
& textAttr
)
7201 return ctrl
->GetFocusObject()->InsertImageWithUndo(this, pos
, imageBlock
, ctrl
, flags
, textAttr
);
7204 /// Submit command to insert the given image
7205 bool wxRichTextParagraphLayoutBox::InsertImageWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxRichTextImageBlock
& imageBlock
,
7206 wxRichTextCtrl
* ctrl
, int flags
,
7207 const wxRichTextAttr
& textAttr
)
7209 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7211 wxRichTextAttr
* p
= NULL
;
7212 wxRichTextAttr paraAttr
;
7213 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7215 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
7216 if (!paraAttr
.IsDefault())
7220 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7222 // Don't include box attributes such as margins
7223 attr
.GetTextBoxAttr().Reset();
7225 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7227 newPara
->SetAttributes(*p
);
7229 wxRichTextImage
* imageObject
= new wxRichTextImage(imageBlock
, newPara
);
7230 newPara
->AppendChild(imageObject
);
7231 imageObject
->SetAttributes(textAttr
);
7232 action
->GetNewParagraphs().AppendChild(newPara
);
7233 action
->GetNewParagraphs().UpdateRanges();
7235 action
->GetNewParagraphs().SetPartialParagraph(true);
7237 action
->SetPosition(pos
);
7239 // Set the range we'll need to delete in Undo
7240 action
->SetRange(wxRichTextRange(pos
, pos
));
7242 buffer
->SubmitAction(action
);
7247 // Insert an object with no change of it
7248 wxRichTextObject
* wxRichTextBuffer::InsertObjectWithUndo(long pos
, wxRichTextObject
*object
, wxRichTextCtrl
* ctrl
, int flags
)
7250 return ctrl
->GetFocusObject()->InsertObjectWithUndo(this, pos
, object
, ctrl
, flags
);
7253 // Insert an object with no change of it
7254 wxRichTextObject
* wxRichTextParagraphLayoutBox::InsertObjectWithUndo(wxRichTextBuffer
* buffer
, long pos
, wxRichTextObject
*object
, wxRichTextCtrl
* ctrl
, int flags
)
7256 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Object"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7258 wxRichTextAttr
* p
= NULL
;
7259 wxRichTextAttr paraAttr
;
7260 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7262 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
7263 if (!paraAttr
.IsDefault())
7267 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7269 // Don't include box attributes such as margins
7270 attr
.GetTextBoxAttr().Reset();
7272 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7274 newPara
->SetAttributes(*p
);
7276 newPara
->AppendChild(object
);
7277 action
->GetNewParagraphs().AppendChild(newPara
);
7278 action
->GetNewParagraphs().UpdateRanges();
7280 action
->GetNewParagraphs().SetPartialParagraph(true);
7282 action
->SetPosition(pos
);
7284 // Set the range we'll need to delete in Undo
7285 action
->SetRange(wxRichTextRange(pos
, pos
));
7287 buffer
->SubmitAction(action
);
7289 wxRichTextObject
* obj
= GetLeafObjectAtPosition(pos
);
7293 wxRichTextField
* wxRichTextParagraphLayoutBox::InsertFieldWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxString
& fieldType
,
7294 const wxRichTextProperties
& properties
,
7295 wxRichTextCtrl
* ctrl
, int flags
,
7296 const wxRichTextAttr
& textAttr
)
7298 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Field"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7300 wxRichTextAttr
* p
= NULL
;
7301 wxRichTextAttr paraAttr
;
7302 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7304 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
7305 if (!paraAttr
.IsDefault())
7309 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7311 // Don't include box attributes such as margins
7312 attr
.GetTextBoxAttr().Reset();
7314 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7316 newPara
->SetAttributes(*p
);
7318 wxRichTextField
* fieldObject
= new wxRichTextField();
7319 fieldObject
->wxRichTextObject::SetProperties(properties
);
7320 fieldObject
->SetFieldType(fieldType
);
7321 fieldObject
->SetAttributes(textAttr
);
7322 newPara
->AppendChild(fieldObject
);
7323 action
->GetNewParagraphs().AppendChild(newPara
);
7324 action
->GetNewParagraphs().UpdateRanges();
7325 action
->GetNewParagraphs().SetPartialParagraph(true);
7326 action
->SetPosition(pos
);
7328 // Set the range we'll need to delete in Undo
7329 action
->SetRange(wxRichTextRange(pos
, pos
));
7331 buffer
->SubmitAction(action
);
7333 wxRichTextField
* obj
= wxDynamicCast(GetLeafObjectAtPosition(pos
), wxRichTextField
);
7337 /// Get the style that is appropriate for a new paragraph at this position.
7338 /// If the previous paragraph has a paragraph style name, look up the next-paragraph
7340 wxRichTextAttr
wxRichTextParagraphLayoutBox::GetStyleForNewParagraph(wxRichTextBuffer
* buffer
, long pos
, bool caretPosition
, bool lookUpNewParaStyle
) const
7342 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
, caretPosition
);
7345 wxRichTextAttr attr
;
7346 bool foundAttributes
= false;
7348 // Look for a matching paragraph style
7349 if (lookUpNewParaStyle
&& !para
->GetAttributes().GetParagraphStyleName().IsEmpty() && buffer
->GetStyleSheet())
7351 wxRichTextParagraphStyleDefinition
* paraDef
= buffer
->GetStyleSheet()->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
7354 // If we're not at the end of the paragraph, then we apply THIS style, and not the designated next style.
7355 if (para
->GetRange().GetEnd() == pos
&& !paraDef
->GetNextStyle().IsEmpty())
7357 wxRichTextParagraphStyleDefinition
* nextParaDef
= buffer
->GetStyleSheet()->FindParagraphStyle(paraDef
->GetNextStyle());
7360 foundAttributes
= true;
7361 attr
= nextParaDef
->GetStyleMergedWithBase(buffer
->GetStyleSheet());
7365 // If we didn't find the 'next style', use this style instead.
7366 if (!foundAttributes
)
7368 foundAttributes
= true;
7369 attr
= paraDef
->GetStyleMergedWithBase(buffer
->GetStyleSheet());
7374 // Also apply list style if present
7375 if (lookUpNewParaStyle
&& !para
->GetAttributes().GetListStyleName().IsEmpty() && buffer
->GetStyleSheet())
7377 wxRichTextListStyleDefinition
* listDef
= buffer
->GetStyleSheet()->FindListStyle(para
->GetAttributes().GetListStyleName());
7380 int thisIndent
= para
->GetAttributes().GetLeftIndent();
7381 int thisLevel
= para
->GetAttributes().HasOutlineLevel() ? para
->GetAttributes().GetOutlineLevel() : listDef
->FindLevelForIndent(thisIndent
);
7383 // Apply the overall list style, and item style for this level
7384 wxRichTextAttr
listStyle(listDef
->GetCombinedStyleForLevel(thisLevel
, buffer
->GetStyleSheet()));
7385 wxRichTextApplyStyle(attr
, listStyle
);
7386 attr
.SetOutlineLevel(thisLevel
);
7387 if (para
->GetAttributes().HasBulletNumber())
7388 attr
.SetBulletNumber(para
->GetAttributes().GetBulletNumber());
7392 if (!foundAttributes
)
7394 attr
= para
->GetAttributes();
7395 int flags
= attr
.GetFlags();
7397 // Eliminate character styles
7398 flags
&= ( (~ wxTEXT_ATTR_FONT
) |
7399 (~ wxTEXT_ATTR_TEXT_COLOUR
) |
7400 (~ wxTEXT_ATTR_BACKGROUND_COLOUR
) );
7401 attr
.SetFlags(flags
);
7407 return wxRichTextAttr();
7410 /// Submit command to delete this range
7411 bool wxRichTextBuffer::DeleteRangeWithUndo(const wxRichTextRange
& range
, wxRichTextCtrl
* ctrl
)
7413 return ctrl
->GetFocusObject()->DeleteRangeWithUndo(range
, ctrl
, this);
7416 /// Submit command to delete this range
7417 bool wxRichTextParagraphLayoutBox::DeleteRangeWithUndo(const wxRichTextRange
& range
, wxRichTextCtrl
* ctrl
, wxRichTextBuffer
* buffer
)
7419 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Delete"), wxRICHTEXT_DELETE
, buffer
, this, ctrl
);
7421 action
->SetPosition(ctrl
->GetCaretPosition());
7423 // Set the range to delete
7424 action
->SetRange(range
);
7426 // Copy the fragment that we'll need to restore in Undo
7427 CopyFragment(range
, action
->GetOldParagraphs());
7429 // See if we're deleting a paragraph marker, in which case we need to
7430 // make a note not to copy the attributes from the 2nd paragraph to the 1st.
7431 if (range
.GetStart() == range
.GetEnd())
7433 wxRichTextParagraph
* para
= GetParagraphAtPosition(range
.GetStart());
7434 if (para
&& para
->GetRange().GetEnd() == range
.GetEnd())
7436 wxRichTextParagraph
* nextPara
= GetParagraphAtPosition(range
.GetStart()+1);
7437 if (nextPara
&& nextPara
!= para
)
7439 action
->GetOldParagraphs().GetChildren().GetFirst()->GetData()->SetAttributes(nextPara
->GetAttributes());
7440 action
->GetOldParagraphs().GetAttributes().SetFlags(action
->GetOldParagraphs().GetAttributes().GetFlags() | wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
);
7445 buffer
->SubmitAction(action
);
7450 /// Collapse undo/redo commands
7451 bool wxRichTextBuffer::BeginBatchUndo(const wxString
& cmdName
)
7453 if (m_batchedCommandDepth
== 0)
7455 wxASSERT(m_batchedCommand
== NULL
);
7456 if (m_batchedCommand
)
7458 GetCommandProcessor()->Store(m_batchedCommand
);
7460 m_batchedCommand
= new wxRichTextCommand(cmdName
);
7463 m_batchedCommandDepth
++;
7468 /// Collapse undo/redo commands
7469 bool wxRichTextBuffer::EndBatchUndo()
7471 m_batchedCommandDepth
--;
7473 wxASSERT(m_batchedCommandDepth
>= 0);
7474 wxASSERT(m_batchedCommand
!= NULL
);
7476 if (m_batchedCommandDepth
== 0)
7478 GetCommandProcessor()->Store(m_batchedCommand
);
7479 m_batchedCommand
= NULL
;
7485 /// Submit immediately, or delay according to whether collapsing is on
7486 bool wxRichTextBuffer::SubmitAction(wxRichTextAction
* action
)
7488 if (action
&& !action
->GetNewParagraphs().IsEmpty())
7489 PrepareContent(action
->GetNewParagraphs());
7491 if (BatchingUndo() && m_batchedCommand
&& !SuppressingUndo())
7493 wxRichTextCommand
* cmd
= new wxRichTextCommand(action
->GetName());
7494 cmd
->AddAction(action
);
7496 cmd
->GetActions().Clear();
7499 m_batchedCommand
->AddAction(action
);
7503 wxRichTextCommand
* cmd
= new wxRichTextCommand(action
->GetName());
7504 cmd
->AddAction(action
);
7506 // Only store it if we're not suppressing undo.
7507 return GetCommandProcessor()->Submit(cmd
, !SuppressingUndo());
7513 /// Begin suppressing undo/redo commands.
7514 bool wxRichTextBuffer::BeginSuppressUndo()
7521 /// End suppressing undo/redo commands.
7522 bool wxRichTextBuffer::EndSuppressUndo()
7529 /// Begin using a style
7530 bool wxRichTextBuffer::BeginStyle(const wxRichTextAttr
& style
)
7532 wxRichTextAttr
newStyle(GetDefaultStyle());
7533 newStyle
.GetTextBoxAttr().Reset();
7535 // Save the old default style
7536 m_attributeStack
.Append((wxObject
*) new wxRichTextAttr(newStyle
));
7538 wxRichTextApplyStyle(newStyle
, style
);
7539 newStyle
.SetFlags(style
.GetFlags()|newStyle
.GetFlags());
7541 SetDefaultStyle(newStyle
);
7547 bool wxRichTextBuffer::EndStyle()
7549 if (!m_attributeStack
.GetFirst())
7551 wxLogDebug(_("Too many EndStyle calls!"));
7555 wxList::compatibility_iterator node
= m_attributeStack
.GetLast();
7556 wxRichTextAttr
* attr
= (wxRichTextAttr
*)node
->GetData();
7557 m_attributeStack
.Erase(node
);
7559 SetDefaultStyle(*attr
);
7566 bool wxRichTextBuffer::EndAllStyles()
7568 while (m_attributeStack
.GetCount() != 0)
7573 /// Clear the style stack
7574 void wxRichTextBuffer::ClearStyleStack()
7576 for (wxList::compatibility_iterator node
= m_attributeStack
.GetFirst(); node
; node
= node
->GetNext())
7577 delete (wxRichTextAttr
*) node
->GetData();
7578 m_attributeStack
.Clear();
7581 /// Begin using bold
7582 bool wxRichTextBuffer::BeginBold()
7584 wxRichTextAttr attr
;
7585 attr
.SetFontWeight(wxFONTWEIGHT_BOLD
);
7587 return BeginStyle(attr
);
7590 /// Begin using italic
7591 bool wxRichTextBuffer::BeginItalic()
7593 wxRichTextAttr attr
;
7594 attr
.SetFontStyle(wxFONTSTYLE_ITALIC
);
7596 return BeginStyle(attr
);
7599 /// Begin using underline
7600 bool wxRichTextBuffer::BeginUnderline()
7602 wxRichTextAttr attr
;
7603 attr
.SetFontUnderlined(true);
7605 return BeginStyle(attr
);
7608 /// Begin using point size
7609 bool wxRichTextBuffer::BeginFontSize(int pointSize
)
7611 wxRichTextAttr attr
;
7612 attr
.SetFontSize(pointSize
);
7614 return BeginStyle(attr
);
7617 /// Begin using this font
7618 bool wxRichTextBuffer::BeginFont(const wxFont
& font
)
7620 wxRichTextAttr attr
;
7623 return BeginStyle(attr
);
7626 /// Begin using this colour
7627 bool wxRichTextBuffer::BeginTextColour(const wxColour
& colour
)
7629 wxRichTextAttr attr
;
7630 attr
.SetFlags(wxTEXT_ATTR_TEXT_COLOUR
);
7631 attr
.SetTextColour(colour
);
7633 return BeginStyle(attr
);
7636 /// Begin using alignment
7637 bool wxRichTextBuffer::BeginAlignment(wxTextAttrAlignment alignment
)
7639 wxRichTextAttr attr
;
7640 attr
.SetFlags(wxTEXT_ATTR_ALIGNMENT
);
7641 attr
.SetAlignment(alignment
);
7643 return BeginStyle(attr
);
7646 /// Begin left indent
7647 bool wxRichTextBuffer::BeginLeftIndent(int leftIndent
, int leftSubIndent
)
7649 wxRichTextAttr attr
;
7650 attr
.SetFlags(wxTEXT_ATTR_LEFT_INDENT
);
7651 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7653 return BeginStyle(attr
);
7656 /// Begin right indent
7657 bool wxRichTextBuffer::BeginRightIndent(int rightIndent
)
7659 wxRichTextAttr attr
;
7660 attr
.SetFlags(wxTEXT_ATTR_RIGHT_INDENT
);
7661 attr
.SetRightIndent(rightIndent
);
7663 return BeginStyle(attr
);
7666 /// Begin paragraph spacing
7667 bool wxRichTextBuffer::BeginParagraphSpacing(int before
, int after
)
7671 flags
|= wxTEXT_ATTR_PARA_SPACING_BEFORE
;
7673 flags
|= wxTEXT_ATTR_PARA_SPACING_AFTER
;
7675 wxRichTextAttr attr
;
7676 attr
.SetFlags(flags
);
7677 attr
.SetParagraphSpacingBefore(before
);
7678 attr
.SetParagraphSpacingAfter(after
);
7680 return BeginStyle(attr
);
7683 /// Begin line spacing
7684 bool wxRichTextBuffer::BeginLineSpacing(int lineSpacing
)
7686 wxRichTextAttr attr
;
7687 attr
.SetFlags(wxTEXT_ATTR_LINE_SPACING
);
7688 attr
.SetLineSpacing(lineSpacing
);
7690 return BeginStyle(attr
);
7693 /// Begin numbered bullet
7694 bool wxRichTextBuffer::BeginNumberedBullet(int bulletNumber
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7696 wxRichTextAttr attr
;
7697 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7698 attr
.SetBulletStyle(bulletStyle
);
7699 attr
.SetBulletNumber(bulletNumber
);
7700 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7702 return BeginStyle(attr
);
7705 /// Begin symbol bullet
7706 bool wxRichTextBuffer::BeginSymbolBullet(const wxString
& symbol
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7708 wxRichTextAttr attr
;
7709 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7710 attr
.SetBulletStyle(bulletStyle
);
7711 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7712 attr
.SetBulletText(symbol
);
7714 return BeginStyle(attr
);
7717 /// Begin standard bullet
7718 bool wxRichTextBuffer::BeginStandardBullet(const wxString
& bulletName
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7720 wxRichTextAttr attr
;
7721 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7722 attr
.SetBulletStyle(bulletStyle
);
7723 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7724 attr
.SetBulletName(bulletName
);
7726 return BeginStyle(attr
);
7729 /// Begin named character style
7730 bool wxRichTextBuffer::BeginCharacterStyle(const wxString
& characterStyle
)
7732 if (GetStyleSheet())
7734 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->FindCharacterStyle(characterStyle
);
7737 wxRichTextAttr attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7738 return BeginStyle(attr
);
7744 /// Begin named paragraph style
7745 bool wxRichTextBuffer::BeginParagraphStyle(const wxString
& paragraphStyle
)
7747 if (GetStyleSheet())
7749 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(paragraphStyle
);
7752 wxRichTextAttr attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7753 return BeginStyle(attr
);
7759 /// Begin named list style
7760 bool wxRichTextBuffer::BeginListStyle(const wxString
& listStyle
, int level
, int number
)
7762 if (GetStyleSheet())
7764 wxRichTextListStyleDefinition
* def
= GetStyleSheet()->FindListStyle(listStyle
);
7767 wxRichTextAttr
attr(def
->GetCombinedStyleForLevel(level
));
7769 attr
.SetBulletNumber(number
);
7771 return BeginStyle(attr
);
7778 bool wxRichTextBuffer::BeginURL(const wxString
& url
, const wxString
& characterStyle
)
7780 wxRichTextAttr attr
;
7782 if (!characterStyle
.IsEmpty() && GetStyleSheet())
7784 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->FindCharacterStyle(characterStyle
);
7787 attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7792 return BeginStyle(attr
);
7795 /// Adds a handler to the end
7796 void wxRichTextBuffer::AddHandler(wxRichTextFileHandler
*handler
)
7798 sm_handlers
.Append(handler
);
7801 /// Inserts a handler at the front
7802 void wxRichTextBuffer::InsertHandler(wxRichTextFileHandler
*handler
)
7804 sm_handlers
.Insert( handler
);
7807 /// Removes a handler
7808 bool wxRichTextBuffer::RemoveHandler(const wxString
& name
)
7810 wxRichTextFileHandler
*handler
= FindHandler(name
);
7813 sm_handlers
.DeleteObject(handler
);
7821 /// Finds a handler by filename or, if supplied, type
7822 wxRichTextFileHandler
*wxRichTextBuffer::FindHandlerFilenameOrType(const wxString
& filename
,
7823 wxRichTextFileType imageType
)
7825 if (imageType
!= wxRICHTEXT_TYPE_ANY
)
7826 return FindHandler(imageType
);
7827 else if (!filename
.IsEmpty())
7829 wxString path
, file
, ext
;
7830 wxFileName::SplitPath(filename
, & path
, & file
, & ext
);
7831 return FindHandler(ext
, imageType
);
7838 /// Finds a handler by name
7839 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(const wxString
& name
)
7841 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7844 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7845 if (handler
->GetName().Lower() == name
.Lower()) return handler
;
7847 node
= node
->GetNext();
7852 /// Finds a handler by extension and type
7853 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(const wxString
& extension
, wxRichTextFileType type
)
7855 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7858 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7859 if ( handler
->GetExtension().Lower() == extension
.Lower() &&
7860 (type
== wxRICHTEXT_TYPE_ANY
|| handler
->GetType() == type
) )
7862 node
= node
->GetNext();
7867 /// Finds a handler by type
7868 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(wxRichTextFileType type
)
7870 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7873 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7874 if (handler
->GetType() == type
) return handler
;
7875 node
= node
->GetNext();
7880 void wxRichTextBuffer::InitStandardHandlers()
7882 if (!FindHandler(wxRICHTEXT_TYPE_TEXT
))
7883 AddHandler(new wxRichTextPlainTextHandler
);
7886 void wxRichTextBuffer::CleanUpHandlers()
7888 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7891 wxRichTextFileHandler
* handler
= (wxRichTextFileHandler
*)node
->GetData();
7892 wxList::compatibility_iterator next
= node
->GetNext();
7897 sm_handlers
.Clear();
7900 wxString
wxRichTextBuffer::GetExtWildcard(bool combine
, bool save
, wxArrayInt
* types
)
7907 wxList::compatibility_iterator node
= GetHandlers().GetFirst();
7911 wxRichTextFileHandler
* handler
= (wxRichTextFileHandler
*) node
->GetData();
7912 if (handler
->IsVisible() && ((save
&& handler
->CanSave()) || (!save
&& handler
->CanLoad())))
7917 wildcard
+= wxT(";");
7918 wildcard
+= wxT("*.") + handler
->GetExtension();
7923 wildcard
+= wxT("|");
7924 wildcard
+= handler
->GetName();
7925 wildcard
+= wxT(" ");
7926 wildcard
+= _("files");
7927 wildcard
+= wxT(" (*.");
7928 wildcard
+= handler
->GetExtension();
7929 wildcard
+= wxT(")|*.");
7930 wildcard
+= handler
->GetExtension();
7932 types
->Add(handler
->GetType());
7937 node
= node
->GetNext();
7941 wildcard
= wxT("(") + wildcard
+ wxT(")|") + wildcard
;
7946 bool wxRichTextBuffer::LoadFile(const wxString
& filename
, wxRichTextFileType type
)
7948 wxRichTextFileHandler
* handler
= FindHandlerFilenameOrType(filename
, type
);
7951 SetDefaultStyle(wxRichTextAttr());
7952 handler
->SetFlags(GetHandlerFlags());
7953 bool success
= handler
->LoadFile(this, filename
);
7954 Invalidate(wxRICHTEXT_ALL
);
7962 bool wxRichTextBuffer::SaveFile(const wxString
& filename
, wxRichTextFileType type
)
7964 wxRichTextFileHandler
* handler
= FindHandlerFilenameOrType(filename
, type
);
7967 handler
->SetFlags(GetHandlerFlags());
7968 return handler
->SaveFile(this, filename
);
7974 /// Load from a stream
7975 bool wxRichTextBuffer::LoadFile(wxInputStream
& stream
, wxRichTextFileType type
)
7977 wxRichTextFileHandler
* handler
= FindHandler(type
);
7980 SetDefaultStyle(wxRichTextAttr());
7981 handler
->SetFlags(GetHandlerFlags());
7982 bool success
= handler
->LoadFile(this, stream
);
7983 Invalidate(wxRICHTEXT_ALL
);
7990 /// Save to a stream
7991 bool wxRichTextBuffer::SaveFile(wxOutputStream
& stream
, wxRichTextFileType type
)
7993 wxRichTextFileHandler
* handler
= FindHandler(type
);
7996 handler
->SetFlags(GetHandlerFlags());
7997 return handler
->SaveFile(this, stream
);
8003 /// Copy the range to the clipboard
8004 bool wxRichTextBuffer::CopyToClipboard(const wxRichTextRange
& range
)
8006 bool success
= false;
8007 wxRichTextParagraphLayoutBox
* container
= this;
8008 if (GetRichTextCtrl())
8009 container
= GetRichTextCtrl()->GetFocusObject();
8011 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
8013 if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open())
8015 wxTheClipboard
->Clear();
8017 // Add composite object
8019 wxDataObjectComposite
* compositeObject
= new wxDataObjectComposite();
8022 wxString text
= container
->GetTextForRange(range
);
8025 text
= wxTextFile::Translate(text
, wxTextFileType_Dos
);
8028 compositeObject
->Add(new wxTextDataObject(text
), false /* not preferred */);
8031 // Add rich text buffer data object. This needs the XML handler to be present.
8033 if (FindHandler(wxRICHTEXT_TYPE_XML
))
8035 wxRichTextBuffer
* richTextBuf
= new wxRichTextBuffer
;
8036 container
->CopyFragment(range
, *richTextBuf
);
8038 compositeObject
->Add(new wxRichTextBufferDataObject(richTextBuf
), true /* preferred */);
8041 if (wxTheClipboard
->SetData(compositeObject
))
8044 wxTheClipboard
->Close();
8053 /// Paste the clipboard content to the buffer
8054 bool wxRichTextBuffer::PasteFromClipboard(long position
)
8056 bool success
= false;
8057 wxRichTextParagraphLayoutBox
* container
= this;
8058 if (GetRichTextCtrl())
8059 container
= GetRichTextCtrl()->GetFocusObject();
8061 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
8062 if (CanPasteFromClipboard())
8064 if (wxTheClipboard
->Open())
8066 if (wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())))
8068 wxRichTextBufferDataObject data
;
8069 wxTheClipboard
->GetData(data
);
8070 wxRichTextBuffer
* richTextBuffer
= data
.GetRichTextBuffer();
8073 container
->InsertParagraphsWithUndo(this, position
+1, *richTextBuffer
, GetRichTextCtrl(), 0);
8074 if (GetRichTextCtrl())
8075 GetRichTextCtrl()->ShowPosition(position
+ richTextBuffer
->GetOwnRange().GetEnd());
8076 delete richTextBuffer
;
8079 else if (wxTheClipboard
->IsSupported(wxDF_TEXT
)
8081 || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
)
8085 wxTextDataObject data
;
8086 wxTheClipboard
->GetData(data
);
8087 wxString
text(data
.GetText());
8090 text2
.Alloc(text
.Length()+1);
8092 for (i
= 0; i
< text
.Length(); i
++)
8094 wxChar ch
= text
[i
];
8095 if (ch
!= wxT('\r'))
8099 wxString text2
= text
;
8101 container
->InsertTextWithUndo(this, position
+1, text2
, GetRichTextCtrl(), wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
);
8103 if (GetRichTextCtrl())
8104 GetRichTextCtrl()->ShowPosition(position
+ text2
.Length());
8108 else if (wxTheClipboard
->IsSupported(wxDF_BITMAP
))
8110 wxBitmapDataObject data
;
8111 wxTheClipboard
->GetData(data
);
8112 wxBitmap
bitmap(data
.GetBitmap());
8113 wxImage
image(bitmap
.ConvertToImage());
8115 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, this, container
, GetRichTextCtrl(), false);
8117 action
->GetNewParagraphs().AddImage(image
);
8119 if (action
->GetNewParagraphs().GetChildCount() == 1)
8120 action
->GetNewParagraphs().SetPartialParagraph(true);
8122 action
->SetPosition(position
+1);
8124 // Set the range we'll need to delete in Undo
8125 action
->SetRange(wxRichTextRange(position
+1, position
+1));
8127 SubmitAction(action
);
8131 wxTheClipboard
->Close();
8135 wxUnusedVar(position
);
8140 /// Can we paste from the clipboard?
8141 bool wxRichTextBuffer::CanPasteFromClipboard() const
8143 bool canPaste
= false;
8144 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
8145 if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open())
8147 if (wxTheClipboard
->IsSupported(wxDF_TEXT
)
8149 || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
)
8151 || wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())) ||
8152 wxTheClipboard
->IsSupported(wxDF_BITMAP
))
8156 wxTheClipboard
->Close();
8162 /// Dumps contents of buffer for debugging purposes
8163 void wxRichTextBuffer::Dump()
8167 wxStringOutputStream
stream(& text
);
8168 wxTextOutputStream
textStream(stream
);
8175 /// Add an event handler
8176 bool wxRichTextBuffer::AddEventHandler(wxEvtHandler
* handler
)
8178 m_eventHandlers
.Append(handler
);
8182 /// Remove an event handler
8183 bool wxRichTextBuffer::RemoveEventHandler(wxEvtHandler
* handler
, bool deleteHandler
)
8185 wxList::compatibility_iterator node
= m_eventHandlers
.Find(handler
);
8188 m_eventHandlers
.Erase(node
);
8198 /// Clear event handlers
8199 void wxRichTextBuffer::ClearEventHandlers()
8201 m_eventHandlers
.Clear();
8204 /// Send event to event handlers. If sendToAll is true, will send to all event handlers,
8205 /// otherwise will stop at the first successful one.
8206 bool wxRichTextBuffer::SendEvent(wxEvent
& event
, bool sendToAll
)
8208 bool success
= false;
8209 for (wxList::compatibility_iterator node
= m_eventHandlers
.GetFirst(); node
; node
= node
->GetNext())
8211 wxEvtHandler
* handler
= (wxEvtHandler
*) node
->GetData();
8212 if (handler
->ProcessEvent(event
))
8222 /// Set style sheet and notify of the change
8223 bool wxRichTextBuffer::SetStyleSheetAndNotify(wxRichTextStyleSheet
* sheet
)
8225 wxRichTextStyleSheet
* oldSheet
= GetStyleSheet();
8227 wxWindowID winid
= wxID_ANY
;
8228 if (GetRichTextCtrl())
8229 winid
= GetRichTextCtrl()->GetId();
8231 wxRichTextEvent
event(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACING
, winid
);
8232 event
.SetEventObject(GetRichTextCtrl());
8233 event
.SetContainer(GetRichTextCtrl()->GetFocusObject());
8234 event
.SetOldStyleSheet(oldSheet
);
8235 event
.SetNewStyleSheet(sheet
);
8238 if (SendEvent(event
) && !event
.IsAllowed())
8240 if (sheet
!= oldSheet
)
8246 if (oldSheet
&& oldSheet
!= sheet
)
8249 SetStyleSheet(sheet
);
8251 event
.SetEventType(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACED
);
8252 event
.SetOldStyleSheet(NULL
);
8255 return SendEvent(event
);
8258 /// Set renderer, deleting old one
8259 void wxRichTextBuffer::SetRenderer(wxRichTextRenderer
* renderer
)
8263 sm_renderer
= renderer
;
8266 /// Hit-testing: returns a flag indicating hit test details, plus
8267 /// information about position
8268 int wxRichTextBuffer::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
8270 int ret
= wxRichTextParagraphLayoutBox::HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
8271 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
8277 textPosition
= m_ownRange
.GetEnd()-1;
8280 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
;
8284 void wxRichTextBuffer::SetFontScale(double fontScale
)
8286 m_fontScale
= fontScale
;
8287 m_fontTable
.SetFontScale(fontScale
);
8290 void wxRichTextBuffer::SetDimensionScale(double dimScale
)
8292 m_dimensionScale
= dimScale
;
8295 bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxRichTextAttr
& bulletAttr
, const wxRect
& rect
)
8297 if (bulletAttr
.GetTextColour().IsOk())
8299 wxCheckSetPen(dc
, wxPen(bulletAttr
.GetTextColour()));
8300 wxCheckSetBrush(dc
, wxBrush(bulletAttr
.GetTextColour()));
8304 wxCheckSetPen(dc
, *wxBLACK_PEN
);
8305 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
8309 if (bulletAttr
.HasFont())
8311 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(bulletAttr
);
8314 font
= (*wxNORMAL_FONT
);
8316 wxCheckSetFont(dc
, font
);
8318 int charHeight
= dc
.GetCharHeight();
8320 int bulletWidth
= (int) (((float) charHeight
) * wxRichTextBuffer::GetBulletProportion());
8321 int bulletHeight
= bulletWidth
;
8325 // Calculate the top position of the character (as opposed to the whole line height)
8326 int y
= rect
.y
+ (rect
.height
- charHeight
);
8328 // Calculate where the bullet should be positioned
8329 y
= y
+ (charHeight
+1)/2 - (bulletHeight
+1)/2;
8331 // The margin between a bullet and text.
8332 int margin
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer::GetBulletRightMargin());
8334 if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
)
8335 x
= rect
.x
+ rect
.width
- bulletWidth
- margin
;
8336 else if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
)
8337 x
= x
+ (rect
.width
)/2 - bulletWidth
/2;
8339 if (bulletAttr
.GetBulletName() == wxT("standard/square"))
8341 dc
.DrawRectangle(x
, y
, bulletWidth
, bulletHeight
);
8343 else if (bulletAttr
.GetBulletName() == wxT("standard/diamond"))
8346 pts
[0].x
= x
; pts
[0].y
= y
+ bulletHeight
/2;
8347 pts
[1].x
= x
+ bulletWidth
/2; pts
[1].y
= y
;
8348 pts
[2].x
= x
+ bulletWidth
; pts
[2].y
= y
+ bulletHeight
/2;
8349 pts
[3].x
= x
+ bulletWidth
/2; pts
[3].y
= y
+ bulletHeight
;
8351 dc
.DrawPolygon(4, pts
);
8353 else if (bulletAttr
.GetBulletName() == wxT("standard/triangle"))
8356 pts
[0].x
= x
; pts
[0].y
= y
;
8357 pts
[1].x
= x
+ bulletWidth
; pts
[1].y
= y
+ bulletHeight
/2;
8358 pts
[2].x
= x
; pts
[2].y
= y
+ bulletHeight
;
8360 dc
.DrawPolygon(3, pts
);
8362 else if (bulletAttr
.GetBulletName() == wxT("standard/circle-outline"))
8364 wxCheckSetBrush(dc
, *wxTRANSPARENT_BRUSH
);
8365 dc
.DrawEllipse(x
, y
, bulletWidth
, bulletHeight
);
8367 else // "standard/circle", and catch-all
8369 dc
.DrawEllipse(x
, y
, bulletWidth
, bulletHeight
);
8375 bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxRichTextAttr
& attr
, const wxRect
& rect
, const wxString
& text
)
8380 if ((attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
) && !attr
.GetBulletFont().IsEmpty() && attr
.HasFont())
8382 wxRichTextAttr fontAttr
;
8383 if (attr
.HasFontPixelSize())
8384 fontAttr
.SetFontPixelSize(attr
.GetFontSize());
8386 fontAttr
.SetFontPointSize(attr
.GetFontSize());
8387 fontAttr
.SetFontStyle(attr
.GetFontStyle());
8388 fontAttr
.SetFontWeight(attr
.GetFontWeight());
8389 fontAttr
.SetFontUnderlined(attr
.GetFontUnderlined());
8390 fontAttr
.SetFontFaceName(attr
.GetBulletFont());
8391 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(fontAttr
);
8393 else if (attr
.HasFont())
8394 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(attr
);
8396 font
= (*wxNORMAL_FONT
);
8398 wxCheckSetFont(dc
, font
);
8400 if (attr
.GetTextColour().IsOk())
8401 dc
.SetTextForeground(attr
.GetTextColour());
8403 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
8405 int charHeight
= dc
.GetCharHeight();
8407 dc
.GetTextExtent(text
, & tw
, & th
);
8411 // Calculate the top position of the character (as opposed to the whole line height)
8412 int y
= rect
.y
+ (rect
.height
- charHeight
);
8414 // The margin between a bullet and text.
8415 int margin
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer::GetBulletRightMargin());
8417 if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
)
8418 x
= (rect
.x
+ rect
.width
) - tw
- margin
;
8419 else if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
)
8420 x
= x
+ (rect
.width
)/2 - tw
/2;
8422 dc
.DrawText(text
, x
, y
);
8430 bool wxRichTextStdRenderer::DrawBitmapBullet(wxRichTextParagraph
* WXUNUSED(paragraph
), wxDC
& WXUNUSED(dc
), const wxRichTextAttr
& WXUNUSED(attr
), const wxRect
& WXUNUSED(rect
))
8432 // Currently unimplemented. The intention is to store bitmaps by name in a media store associated
8433 // with the buffer. The store will allow retrieval from memory, disk or other means.
8437 /// Enumerate the standard bullet names currently supported
8438 bool wxRichTextStdRenderer::EnumerateStandardBulletNames(wxArrayString
& bulletNames
)
8440 bulletNames
.Add(wxTRANSLATE("standard/circle"));
8441 bulletNames
.Add(wxTRANSLATE("standard/circle-outline"));
8442 bulletNames
.Add(wxTRANSLATE("standard/square"));
8443 bulletNames
.Add(wxTRANSLATE("standard/diamond"));
8444 bulletNames
.Add(wxTRANSLATE("standard/triangle"));
8453 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBox
, wxRichTextParagraphLayoutBox
)
8455 wxRichTextBox::wxRichTextBox(wxRichTextObject
* parent
):
8456 wxRichTextParagraphLayoutBox(parent
)
8461 bool wxRichTextBox::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8466 // TODO: if the active object in the control, draw an indication.
8467 // We need to add the concept of active object, and not just focus object,
8468 // so we can apply commands (properties, delete, ...) to objects such as text boxes and images.
8469 // Ultimately we would like to be able to interactively resize an active object
8470 // using drag handles.
8471 return wxRichTextParagraphLayoutBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8475 void wxRichTextBox::Copy(const wxRichTextBox
& obj
)
8477 wxRichTextParagraphLayoutBox::Copy(obj
);
8480 // Edit properties via a GUI
8481 bool wxRichTextBox::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8483 wxRichTextObjectPropertiesDialog
boxDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Box Properties"));
8484 boxDlg
.SetAttributes(GetAttributes());
8486 if (boxDlg
.ShowModal() == wxID_OK
)
8488 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
8489 // indeterminate in the object.
8490 boxDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
8501 IMPLEMENT_DYNAMIC_CLASS(wxRichTextField
, wxRichTextParagraphLayoutBox
)
8503 wxRichTextField::wxRichTextField(const wxString
& fieldType
, wxRichTextObject
* parent
):
8504 wxRichTextParagraphLayoutBox(parent
)
8506 SetFieldType(fieldType
);
8510 bool wxRichTextField::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8515 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8516 if (fieldType
&& fieldType
->Draw(this, dc
, context
, range
, selection
, rect
, descent
, style
))
8519 // Fallback; but don't draw guidelines.
8520 style
&= ~wxRICHTEXT_DRAW_GUIDELINES
;
8521 return wxRichTextParagraphLayoutBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8524 bool wxRichTextField::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
)
8526 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8527 if (fieldType
&& fieldType
->Layout(this, dc
, context
, rect
, parentRect
, style
))
8531 return wxRichTextParagraphLayoutBox::Layout(dc
, context
, rect
, parentRect
, style
);
8534 bool wxRichTextField::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
8536 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8538 return fieldType
->GetRangeSize((wxRichTextField
*) this, range
, size
, descent
, dc
, context
, flags
, position
, partialExtents
);
8540 return wxRichTextParagraphLayoutBox::GetRangeSize(range
, size
, descent
, dc
, context
, flags
, position
, partialExtents
);
8544 void wxRichTextField::CalculateRange(long start
, long& end
)
8547 wxRichTextParagraphLayoutBox::CalculateRange(start
, end
);
8549 wxRichTextObject::CalculateRange(start
, end
);
8553 void wxRichTextField::Copy(const wxRichTextField
& obj
)
8555 wxRichTextParagraphLayoutBox::Copy(obj
);
8557 UpdateField(GetBuffer());
8560 // Edit properties via a GUI
8561 bool wxRichTextField::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8563 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8565 return fieldType
->EditProperties(this, parent
, buffer
);
8570 bool wxRichTextField::CanEditProperties() const
8572 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8574 return fieldType
->CanEditProperties((wxRichTextField
*) this);
8579 wxString
wxRichTextField::GetPropertiesMenuLabel() const
8581 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8583 return fieldType
->GetPropertiesMenuLabel((wxRichTextField
*) this);
8585 return wxEmptyString
;
8588 bool wxRichTextField::UpdateField(wxRichTextBuffer
* buffer
)
8590 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8592 return fieldType
->UpdateField(buffer
, (wxRichTextField
*) this);
8597 bool wxRichTextField::IsTopLevel() const
8599 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8601 return fieldType
->IsTopLevel((wxRichTextField
*) this);
8606 IMPLEMENT_CLASS(wxRichTextFieldType
, wxObject
)
8608 IMPLEMENT_CLASS(wxRichTextFieldTypeStandard
, wxRichTextFieldType
)
8610 wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString
& name
, const wxString
& label
, int displayStyle
)
8616 SetDisplayStyle(displayStyle
);
8619 wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString
& name
, const wxBitmap
& bitmap
, int displayStyle
)
8625 SetDisplayStyle(displayStyle
);
8628 void wxRichTextFieldTypeStandard::Init()
8630 m_displayStyle
= wxRICHTEXT_FIELD_STYLE_RECTANGLE
;
8631 m_font
= wxFont(6, wxFONTFAMILY_SWISS
, wxFONTSTYLE_NORMAL
, wxFONTWEIGHT_NORMAL
);
8632 m_textColour
= *wxWHITE
;
8633 m_borderColour
= *wxBLACK
;
8634 m_backgroundColour
= *wxBLACK
;
8635 m_verticalPadding
= 1;
8636 m_horizontalPadding
= 3;
8637 m_horizontalMargin
= 2;
8638 m_verticalMargin
= 0;
8641 void wxRichTextFieldTypeStandard::Copy(const wxRichTextFieldTypeStandard
& field
)
8643 wxRichTextFieldType::Copy(field
);
8645 m_label
= field
.m_label
;
8646 m_displayStyle
= field
.m_displayStyle
;
8647 m_font
= field
.m_font
;
8648 m_textColour
= field
.m_textColour
;
8649 m_borderColour
= field
.m_borderColour
;
8650 m_backgroundColour
= field
.m_backgroundColour
;
8651 m_verticalPadding
= field
.m_verticalPadding
;
8652 m_horizontalPadding
= field
.m_horizontalPadding
;
8653 m_horizontalMargin
= field
.m_horizontalMargin
;
8654 m_bitmap
= field
.m_bitmap
;
8657 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
))
8659 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_COMPOSITE
)
8660 return false; // USe default composite drawing
8661 else // if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_RECTANGLE || m_displayStyle == wxRICHTEXT_FIELD_STYLE_NOBORDER)
8665 wxPen
borderPen(m_borderColour
, 1, wxSOLID
);
8666 wxBrush
backgroundBrush(m_backgroundColour
);
8667 wxColour
textColour(m_textColour
);
8669 if (selection
.WithinSelection(obj
->GetRange().GetStart(), obj
))
8671 wxColour
highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
));
8672 wxColour
highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
));
8674 borderPen
= wxPen(highlightTextColour
, 1, wxSOLID
);
8675 backgroundBrush
= wxBrush(highlightColour
);
8677 wxCheckSetBrush(dc
, backgroundBrush
);
8678 wxCheckSetPen(dc
, wxPen(highlightColour
, 1, wxSOLID
));
8679 dc
.DrawRectangle(rect
);
8682 if (m_displayStyle
!= wxRICHTEXT_FIELD_STYLE_NO_BORDER
)
8685 // objectRect is the area where the content is drawn, after margins around it have been taken into account
8686 wxRect objectRect
= wxRect(wxPoint(rect
.x
+ m_horizontalMargin
, rect
.y
+ wxMax(0, rect
.height
- descent
- obj
->GetCachedSize().y
)),
8687 wxSize(obj
->GetCachedSize().x
- 2*m_horizontalMargin
- borderSize
, obj
->GetCachedSize().y
));
8689 // clientArea is where the text is actually written
8690 wxRect clientArea
= objectRect
;
8692 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_RECTANGLE
)
8694 dc
.SetPen(borderPen
);
8695 dc
.SetBrush(backgroundBrush
);
8696 dc
.DrawRoundedRectangle(objectRect
, 4.0);
8698 else if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_START_TAG
)
8700 int arrowLength
= objectRect
.height
/2;
8701 clientArea
.width
-= (arrowLength
- m_horizontalPadding
);
8704 pts
[0].x
= objectRect
.x
; pts
[0].y
= objectRect
.y
;
8705 pts
[1].x
= objectRect
.x
+ objectRect
.width
- arrowLength
; pts
[1].y
= objectRect
.y
;
8706 pts
[2].x
= objectRect
.x
+ objectRect
.width
; pts
[2].y
= objectRect
.y
+ (objectRect
.height
/2);
8707 pts
[3].x
= objectRect
.x
+ objectRect
.width
- arrowLength
; pts
[3].y
= objectRect
.y
+ objectRect
.height
;
8708 pts
[4].x
= objectRect
.x
; pts
[4].y
= objectRect
.y
+ objectRect
.height
;
8709 dc
.SetPen(borderPen
);
8710 dc
.SetBrush(backgroundBrush
);
8711 dc
.DrawPolygon(5, pts
);
8713 else if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_END_TAG
)
8715 int arrowLength
= objectRect
.height
/2;
8716 clientArea
.width
-= (arrowLength
- m_horizontalPadding
);
8717 clientArea
.x
+= (arrowLength
- m_horizontalPadding
);
8720 pts
[0].x
= objectRect
.x
+ objectRect
.width
; pts
[0].y
= objectRect
.y
;
8721 pts
[1].x
= objectRect
.x
+ arrowLength
; pts
[1].y
= objectRect
.y
;
8722 pts
[2].x
= objectRect
.x
; pts
[2].y
= objectRect
.y
+ (objectRect
.height
/2);
8723 pts
[3].x
= objectRect
.x
+ arrowLength
; pts
[3].y
= objectRect
.y
+ objectRect
.height
;
8724 pts
[4].x
= objectRect
.x
+ objectRect
.width
; pts
[4].y
= objectRect
.y
+ objectRect
.height
;
8725 dc
.SetPen(borderPen
);
8726 dc
.SetBrush(backgroundBrush
);
8727 dc
.DrawPolygon(5, pts
);
8730 if (m_bitmap
.IsOk())
8732 int x
= clientArea
.x
+ (clientArea
.width
- m_bitmap
.GetWidth())/2;
8733 int y
= clientArea
.y
+ m_verticalPadding
;
8734 dc
.DrawBitmap(m_bitmap
, x
, y
, true);
8736 if (selection
.WithinSelection(obj
->GetRange().GetStart(), obj
))
8738 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
8739 wxCheckSetPen(dc
, *wxBLACK_PEN
);
8740 dc
.SetLogicalFunction(wxINVERT
);
8741 dc
.DrawRectangle(wxRect(x
, y
, m_bitmap
.GetWidth(), m_bitmap
.GetHeight()));
8742 dc
.SetLogicalFunction(wxCOPY
);
8747 wxString
label(m_label
);
8748 if (label
.IsEmpty())
8750 int w
, h
, maxDescent
;
8752 dc
.GetTextExtent(m_label
, & w
, &h
, & maxDescent
);
8753 dc
.SetTextForeground(textColour
);
8755 int x
= clientArea
.x
+ (clientArea
.width
- w
)/2;
8756 int y
= clientArea
.y
+ (clientArea
.height
- (h
- maxDescent
))/2;
8757 dc
.DrawText(m_label
, x
, y
);
8764 bool wxRichTextFieldTypeStandard::Layout(wxRichTextField
* obj
, wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& WXUNUSED(rect
), const wxRect
& WXUNUSED(parentRect
), int style
)
8766 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_COMPOSITE
)
8767 return false; // USe default composite layout
8769 wxSize size
= GetSize(obj
, dc
, context
, style
);
8770 obj
->SetCachedSize(size
);
8771 obj
->SetMinSize(size
);
8772 obj
->SetMaxSize(size
);
8776 bool wxRichTextFieldTypeStandard::GetRangeSize(wxRichTextField
* obj
, const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
8778 if (IsTopLevel(obj
))
8779 return obj
->wxRichTextParagraphLayoutBox::GetRangeSize(range
, size
, descent
, dc
, context
, flags
, position
);
8782 wxSize sz
= GetSize(obj
, dc
, context
, 0);
8786 if (partialExtents
->GetCount() > 0)
8787 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
8790 partialExtents
->Add(lastSize
+ sz
.x
);
8797 wxSize
wxRichTextFieldTypeStandard::GetSize(wxRichTextField
* WXUNUSED(obj
), wxDC
& dc
, wxRichTextDrawingContext
& WXUNUSED(context
), int WXUNUSED(style
)) const
8800 int w
= 0, h
= 0, maxDescent
= 0;
8803 if (m_bitmap
.IsOk())
8805 w
= m_bitmap
.GetWidth();
8806 h
= m_bitmap
.GetHeight();
8808 sz
= wxSize(w
+ m_horizontalMargin
*2, h
+ m_verticalMargin
*2);
8812 wxString
label(m_label
);
8813 if (label
.IsEmpty())
8816 dc
.GetTextExtent(label
, & w
, &h
, & maxDescent
);
8818 sz
= wxSize(w
+ m_horizontalPadding
*2 + m_horizontalMargin
*2, h
+ m_verticalPadding
*2 + m_verticalMargin
*2);
8821 if (m_displayStyle
!= wxRICHTEXT_FIELD_STYLE_NO_BORDER
)
8823 sz
.x
+= borderSize
*2;
8824 sz
.y
+= borderSize
*2;
8827 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_START_TAG
|| m_displayStyle
== wxRICHTEXT_FIELD_STYLE_END_TAG
)
8829 // Add space for the arrow
8830 sz
.x
+= (sz
.y
/2 - m_horizontalPadding
);
8836 IMPLEMENT_DYNAMIC_CLASS(wxRichTextCell
, wxRichTextBox
)
8838 wxRichTextCell::wxRichTextCell(wxRichTextObject
* parent
):
8839 wxRichTextBox(parent
)
8844 bool wxRichTextCell::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8846 return wxRichTextBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8850 void wxRichTextCell::Copy(const wxRichTextCell
& obj
)
8852 wxRichTextBox::Copy(obj
);
8855 // Edit properties via a GUI
8856 bool wxRichTextCell::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8858 // We need to gather common attributes for all selected cells.
8860 wxRichTextTable
* table
= wxDynamicCast(GetParent(), wxRichTextTable
);
8861 bool multipleCells
= false;
8862 wxRichTextAttr attr
;
8864 if (table
&& buffer
&& buffer
->GetRichTextCtrl() && buffer
->GetRichTextCtrl()->GetSelection().IsValid() &&
8865 buffer
->GetRichTextCtrl()->GetSelection().GetContainer() == GetParent())
8867 wxRichTextAttr clashingAttr
, absentAttr
;
8868 const wxRichTextSelection
& sel
= buffer
->GetRichTextCtrl()->GetSelection();
8870 int selectedCellCount
= 0;
8871 for (i
= 0; i
< sel
.GetCount(); i
++)
8873 const wxRichTextRange
& range
= sel
[i
];
8874 wxRichTextCell
* cell
= table
->GetCell(range
.GetStart());
8877 wxRichTextAttr cellStyle
= cell
->GetAttributes();
8879 CollectStyle(attr
, cellStyle
, clashingAttr
, absentAttr
);
8881 selectedCellCount
++;
8884 multipleCells
= selectedCellCount
> 1;
8888 attr
= GetAttributes();
8893 caption
= _("Multiple Cell Properties");
8895 caption
= _("Cell Properties");
8897 wxRichTextObjectPropertiesDialog
cellDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, caption
);
8898 cellDlg
.SetAttributes(attr
);
8900 wxRichTextSizePage
* sizePage
= wxDynamicCast(cellDlg
.FindPage(wxCLASSINFO(wxRichTextSizePage
)), wxRichTextSizePage
);
8903 // We don't want position and floating controls for a cell.
8904 sizePage
->ShowPositionControls(false);
8905 sizePage
->ShowFloatingControls(false);
8908 if (cellDlg
.ShowModal() == wxID_OK
)
8912 const wxRichTextSelection
& sel
= buffer
->GetRichTextCtrl()->GetSelection();
8913 // Apply the style; we interpret indeterminate attributes as 'don't touch this attribute'
8914 // since it may represent clashing attributes across multiple objects.
8915 table
->SetCellStyle(sel
, attr
);
8918 // For a single object, indeterminate attributes set by the user should be reflected in the
8919 // actual object style, so pass the wxRICHTEXT_SETSTYLE_RESET flag to assign
8920 // the style directly instead of applying (which ignores indeterminate attributes,
8921 // leaving them as they were).
8922 cellDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
8929 WX_DEFINE_OBJARRAY(wxRichTextObjectPtrArrayArray
)
8931 IMPLEMENT_DYNAMIC_CLASS(wxRichTextTable
, wxRichTextBox
)
8933 wxRichTextTable::wxRichTextTable(wxRichTextObject
* parent
): wxRichTextBox(parent
)
8939 // Draws the object.
8940 bool wxRichTextTable::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8942 return wxRichTextBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8945 WX_DECLARE_OBJARRAY(wxRect
, wxRichTextRectArray
);
8946 WX_DEFINE_OBJARRAY(wxRichTextRectArray
);
8948 // Lays the object out. rect is the space available for layout. Often it will
8949 // be the specified overall space for this object, if trying to constrain
8950 // layout to a particular size, or it could be the total space available in the
8951 // parent. rect is the overall size, so we must subtract margins and padding.
8952 // to get the actual available space.
8953 bool wxRichTextTable::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& WXUNUSED(parentRect
), int style
)
8955 SetPosition(rect
.GetPosition());
8957 // TODO: the meaty bit. Calculate sizes of all cells and rows. Try to use
8958 // minimum size if within alloted size, then divide up remaining size
8959 // between rows/cols.
8962 wxRichTextBuffer
* buffer
= GetBuffer();
8963 if (buffer
) scale
= buffer
->GetScale();
8965 wxRect availableSpace
= GetAvailableContentArea(dc
, context
, rect
);
8966 wxTextAttrDimensionConverter
converter(dc
, scale
, availableSpace
.GetSize());
8968 wxRichTextAttr
attr(GetAttributes());
8969 context
.ApplyVirtualAttributes(attr
, this);
8971 // If we have no fixed table size, and assuming we're not pushed for
8972 // space, then we don't have to try to stretch the table to fit the contents.
8973 bool stretchToFitTableWidth
= false;
8975 int tableWidth
= rect
.width
;
8976 if (attr
.GetTextBoxAttr().GetWidth().IsValid())
8978 tableWidth
= converter
.GetPixels(attr
.GetTextBoxAttr().GetWidth());
8980 // Fixed table width, so we do want to stretch columns out if necessary.
8981 stretchToFitTableWidth
= true;
8983 // Shouldn't be able to exceed the size passed to this function
8984 tableWidth
= wxMin(rect
.width
, tableWidth
);
8987 // Get internal padding
8988 int paddingLeft
= 0, paddingTop
= 0;
8989 if (attr
.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
8990 paddingLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetLeft());
8991 if (attr
.GetTextBoxAttr().GetPadding().GetTop().IsValid())
8992 paddingTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetTop());
8994 // Assume that left and top padding are also used for inter-cell padding.
8995 int paddingX
= paddingLeft
;
8996 int paddingY
= paddingTop
;
8998 int totalLeftMargin
= 0, totalRightMargin
= 0, totalTopMargin
= 0, totalBottomMargin
= 0;
8999 GetTotalMargin(dc
, buffer
, attr
, totalLeftMargin
, totalRightMargin
, totalTopMargin
, totalBottomMargin
);
9001 // Internal table width - the area for content
9002 int internalTableWidth
= tableWidth
- totalLeftMargin
- totalRightMargin
;
9004 int rowCount
= m_cells
.GetCount();
9005 if (m_colCount
== 0 || rowCount
== 0)
9007 wxRect
overallRect(rect
.x
, rect
.y
, totalLeftMargin
+ totalRightMargin
, totalTopMargin
+ totalBottomMargin
);
9008 SetCachedSize(overallRect
.GetSize());
9010 // Zero content size
9011 SetMinSize(overallRect
.GetSize());
9012 SetMaxSize(GetMinSize());
9016 // The final calculated widths
9017 wxArrayInt colWidths
;
9018 colWidths
.Add(0, m_colCount
);
9020 wxArrayInt absoluteColWidths
;
9021 absoluteColWidths
.Add(0, m_colCount
);
9023 wxArrayInt percentageColWidths
;
9024 percentageColWidths
.Add(0, m_colCount
);
9025 // wxArrayInt percentageColWidthsSpanning(m_colCount);
9026 // These are only relevant when the first column contains spanning information.
9027 // wxArrayInt columnSpans(m_colCount); // Each contains 1 for non-spanning cell, > 1 for spanning cell.
9028 wxArrayInt maxColWidths
;
9029 maxColWidths
.Add(0, m_colCount
);
9030 wxArrayInt minColWidths
;
9031 minColWidths
.Add(0, m_colCount
);
9033 wxSize
tableSize(tableWidth
, 0);
9037 for (i
= 0; i
< m_colCount
; i
++)
9039 absoluteColWidths
[i
] = 0;
9040 // absoluteColWidthsSpanning[i] = 0;
9041 percentageColWidths
[i
] = -1;
9042 // percentageColWidthsSpanning[i] = -1;
9044 maxColWidths
[i
] = 0;
9045 minColWidths
[i
] = 0;
9046 // columnSpans[i] = 1;
9049 // (0) Determine which cells are visible according to spans
9051 // __________________
9056 // |------------------|
9057 // |__________________| 4
9059 // To calculate cell visibility:
9060 // First find all spanning cells. Build an array of span records with start x, y and end x, y.
9061 // Then for each cell, test whether we're within one of those cells, and unless we're at the start of
9062 // that cell, hide the cell.
9064 // We can also use this array to match the size of spanning cells to the grid. Or just do
9065 // this when we iterate through all cells.
9067 // 0.1: add spanning cells to an array
9068 wxRichTextRectArray rectArray
;
9069 for (j
= 0; j
< m_rowCount
; j
++)
9071 for (i
= 0; i
< m_colCount
; i
++)
9073 wxRichTextBox
* cell
= GetCell(j
, i
);
9074 int colSpan
= 1, rowSpan
= 1;
9075 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9076 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9077 if (cell
->GetProperties().HasProperty(wxT("rowspan")))
9078 rowSpan
= cell
->GetProperties().GetPropertyLong(wxT("rowspan"));
9079 if (colSpan
> 1 || rowSpan
> 1)
9081 rectArray
.Add(wxRect(i
, j
, colSpan
, rowSpan
));
9085 // 0.2: find which cells are subsumed by a spanning cell
9086 for (j
= 0; j
< m_rowCount
; j
++)
9088 for (i
= 0; i
< m_colCount
; i
++)
9090 wxRichTextBox
* cell
= GetCell(j
, i
);
9091 if (rectArray
.GetCount() == 0)
9097 int colSpan
= 1, rowSpan
= 1;
9098 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9099 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9100 if (cell
->GetProperties().HasProperty(wxT("rowspan")))
9101 rowSpan
= cell
->GetProperties().GetPropertyLong(wxT("rowspan"));
9102 if (colSpan
> 1 || rowSpan
> 1)
9104 // Assume all spanning cells are shown
9110 for (k
= 0; k
< (int) rectArray
.GetCount(); k
++)
9112 if (rectArray
[k
].Contains(wxPoint(i
, j
)))
9124 // TODO: find the first spanned cell in each row that spans the most columns and doesn't
9125 // overlap with a spanned cell starting at a previous column position.
9126 // This means we need to keep an array of rects so we can check. However
9127 // it does also mean that some spans simply may not be taken into account
9128 // where there are different spans happening on different rows. In these cases,
9129 // they will simply be as wide as their constituent columns.
9131 // (1) Do an initial layout for all cells to get minimum and maximum size, and get
9132 // the absolute or percentage width of each column.
9134 for (j
= 0; j
< m_rowCount
; j
++)
9136 // First get the overall margins so we can calculate percentage widths based on
9137 // the available content space for all cells on the row
9139 int overallRowContentMargin
= 0;
9140 int visibleCellCount
= 0;
9142 for (i
= 0; i
< m_colCount
; i
++)
9144 wxRichTextBox
* cell
= GetCell(j
, i
);
9145 if (cell
->IsShown())
9147 int cellTotalLeftMargin
= 0, cellTotalRightMargin
= 0, cellTotalTopMargin
= 0, cellTotalBottomMargin
= 0;
9148 GetTotalMargin(dc
, buffer
, cell
->GetAttributes(), cellTotalLeftMargin
, cellTotalRightMargin
, cellTotalTopMargin
, cellTotalBottomMargin
);
9150 overallRowContentMargin
+= (cellTotalLeftMargin
+ cellTotalRightMargin
);
9151 visibleCellCount
++;
9155 // Add in inter-cell padding
9156 overallRowContentMargin
+= ((visibleCellCount
-1) * paddingX
);
9158 int rowContentWidth
= internalTableWidth
- overallRowContentMargin
;
9159 wxSize
rowTableSize(rowContentWidth
, 0);
9160 wxTextAttrDimensionConverter
converter(dc
, scale
, rowTableSize
);
9162 for (i
= 0; i
< m_colCount
; i
++)
9164 wxRichTextBox
* cell
= GetCell(j
, i
);
9165 if (cell
->IsShown())
9168 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9169 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9171 // Lay out cell to find min/max widths
9172 cell
->Invalidate(wxRICHTEXT_ALL
);
9173 cell
->Layout(dc
, context
, availableSpace
, availableSpace
, style
);
9177 int absoluteCellWidth
= -1;
9178 int percentageCellWidth
= -1;
9180 // I think we need to calculate percentages from the internal table size,
9181 // minus the padding between cells which we'll need to calculate from the
9182 // (number of VISIBLE cells - 1)*paddingX. Then percentages that add up to 100%
9183 // will add up to 100%. In CSS, the width specifies the cell's content rect width,
9184 // so if we want to conform to that we'll need to add in the overall cell margins.
9185 // However, this will make it difficult to specify percentages that add up to
9186 // 100% and still fit within the table width.
9187 // Let's say two cells have 50% width. They have 10 pixels of overall margin each.
9188 // The table content rect is 500 pixels and the inter-cell padding is 20 pixels.
9189 // If we're using internal content size for the width, we would calculate the
9190 // the overall cell width for n cells as:
9191 // (500 - 20*(n-1) - overallCellMargin1 - overallCellMargin2 - ...) * percentage / 100
9192 // + thisOverallCellMargin
9193 // = 500 - 20 - 10 - 10) * 0.5 + 10 = 240 pixels overall cell width.
9194 // Adding this back, we get 240 + 240 + 20 = 500 pixels.
9196 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9198 int w
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetWidth());
9199 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
9201 percentageCellWidth
= w
;
9205 absoluteCellWidth
= w
;
9207 // Override absolute width with minimum width if necessary
9208 if (cell
->GetMinSize().x
> 0 && absoluteCellWidth
!=1 && cell
->GetMinSize().x
> absoluteCellWidth
)
9209 absoluteCellWidth
= cell
->GetMinSize().x
;
9212 if (absoluteCellWidth
!= -1)
9214 if (absoluteCellWidth
> absoluteColWidths
[i
])
9215 absoluteColWidths
[i
] = absoluteCellWidth
;
9218 if (percentageCellWidth
!= -1)
9220 if (percentageCellWidth
> percentageColWidths
[i
])
9221 percentageColWidths
[i
] = percentageCellWidth
;
9224 if (colSpan
== 1 && cell
->GetMinSize().x
&& cell
->GetMinSize().x
> minColWidths
[i
])
9225 minColWidths
[i
] = cell
->GetMinSize().x
;
9226 if (colSpan
== 1 && cell
->GetMaxSize().x
&& cell
->GetMaxSize().x
> maxColWidths
[i
])
9227 maxColWidths
[i
] = cell
->GetMaxSize().x
;
9233 // (2) Allocate initial column widths from minimum widths, absolute values and proportions
9234 // TODO: simply merge this into (1).
9235 for (i
= 0; i
< m_colCount
; i
++)
9237 if (absoluteColWidths
[i
] > 0)
9239 colWidths
[i
] = absoluteColWidths
[i
];
9241 else if (percentageColWidths
[i
] > 0)
9243 colWidths
[i
] = percentageColWidths
[i
];
9245 // This is rubbish - we calculated the absolute widths from percentages, so
9246 // we can't do it again here.
9247 //colWidths[i] = (int) (double(percentageColWidths[i]) * double(tableWidth) / 100.0 + 0.5);
9251 // (3) Process absolute or proportional widths of spanning columns,
9252 // now that we know what our fixed column widths are going to be.
9253 // Spanned cells will try to adjust columns so the span will fit.
9254 // Even existing fixed column widths can be expanded if necessary.
9255 // Actually, currently fixed columns widths aren't adjusted; instead,
9256 // the algorithm favours earlier rows and adjusts unspecified column widths
9257 // the first time only. After that, we can't know whether the column has been
9258 // specified explicitly or not. (We could make a note if necessary.)
9259 for (j
= 0; j
< m_rowCount
; j
++)
9261 // First get the overall margins so we can calculate percentage widths based on
9262 // the available content space for all cells on the row
9264 int overallRowContentMargin
= 0;
9265 int visibleCellCount
= 0;
9267 for (i
= 0; i
< m_colCount
; i
++)
9269 wxRichTextBox
* cell
= GetCell(j
, i
);
9270 if (cell
->IsShown())
9272 int cellTotalLeftMargin
= 0, cellTotalRightMargin
= 0, cellTotalTopMargin
= 0, cellTotalBottomMargin
= 0;
9273 GetTotalMargin(dc
, buffer
, cell
->GetAttributes(), cellTotalLeftMargin
, cellTotalRightMargin
, cellTotalTopMargin
, cellTotalBottomMargin
);
9275 overallRowContentMargin
+= (cellTotalLeftMargin
+ cellTotalRightMargin
);
9276 visibleCellCount
++;
9280 // Add in inter-cell padding
9281 overallRowContentMargin
+= ((visibleCellCount
-1) * paddingX
);
9283 int rowContentWidth
= internalTableWidth
- overallRowContentMargin
;
9284 wxSize
rowTableSize(rowContentWidth
, 0);
9285 wxTextAttrDimensionConverter
converter(dc
, scale
, rowTableSize
);
9287 for (i
= 0; i
< m_colCount
; i
++)
9289 wxRichTextBox
* cell
= GetCell(j
, i
);
9290 if (cell
->IsShown())
9293 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9294 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9298 int spans
= wxMin(colSpan
, m_colCount
- i
);
9302 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9304 cellWidth
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetWidth());
9305 // Override absolute width with minimum width if necessary
9306 if (cell
->GetMinSize().x
> 0 && cellWidth
!=1 && cell
->GetMinSize().x
> cellWidth
)
9307 cellWidth
= cell
->GetMinSize().x
;
9311 // Do we want to do this? It's the only chance we get to
9312 // use the cell's min/max sizes, so we need to work out
9313 // how we're going to balance the unspecified spanning cell
9314 // width with the possibility more-constrained constituent cell widths.
9315 // Say there's a tiny bitmap giving it a max width of 10 pixels. We
9316 // don't want to constraint all the spanned columns to fit into this cell.
9317 // OK, let's say that if any of the constituent columns don't fit,
9318 // then we simply stop constraining the columns; instead, we'll just fit the spanning
9319 // cells to the columns later.
9320 cellWidth
= cell
->GetMinSize().x
;
9321 if (cell
->GetMaxSize().x
> cellWidth
)
9322 cellWidth
= cell
->GetMaxSize().x
;
9325 // Subtract the padding between cells
9326 int spanningWidth
= cellWidth
;
9327 spanningWidth
-= paddingX
* (spans
-1);
9329 if (spanningWidth
> 0)
9331 // Now share the spanning width between columns within that span
9332 // TODO: take into account min widths of columns within the span
9333 int spanningWidthLeft
= spanningWidth
;
9334 int stretchColCount
= 0;
9335 for (k
= i
; k
< (i
+spans
); k
++)
9337 if (colWidths
[k
] > 0) // absolute or proportional width has been specified
9338 spanningWidthLeft
-= colWidths
[k
];
9342 // Now divide what's left between the remaining columns
9344 if (stretchColCount
> 0)
9345 colShare
= spanningWidthLeft
/ stretchColCount
;
9346 int colShareRemainder
= spanningWidthLeft
- (colShare
* stretchColCount
);
9348 // If fixed-width columns are currently too big, then we'll later
9349 // stretch the spanned cell to fit.
9351 if (spanningWidthLeft
> 0)
9353 for (k
= i
; k
< (i
+spans
); k
++)
9355 if (colWidths
[k
] <= 0) // absolute or proportional width has not been specified
9357 int newWidth
= colShare
;
9358 if (k
== (i
+spans
-1))
9359 newWidth
+= colShareRemainder
; // ensure all pixels are filled
9360 colWidths
[k
] = newWidth
;
9371 // (4) Next, share any remaining space out between columns that have not yet been calculated.
9372 // TODO: take into account min widths of columns within the span
9373 int tableWidthMinusPadding
= internalTableWidth
- (m_colCount
-1)*paddingX
;
9374 int widthLeft
= tableWidthMinusPadding
;
9375 int stretchColCount
= 0;
9376 for (i
= 0; i
< m_colCount
; i
++)
9378 // TODO: we need to take into account min widths.
9379 // Subtract min width from width left, then
9380 // add the colShare to the min width
9381 if (colWidths
[i
] > 0) // absolute or proportional width has been specified
9382 widthLeft
-= colWidths
[i
];
9385 if (minColWidths
[i
] > 0)
9386 widthLeft
-= minColWidths
[i
];
9392 // Now divide what's left between the remaining columns
9394 if (stretchColCount
> 0)
9395 colShare
= widthLeft
/ stretchColCount
;
9396 int colShareRemainder
= widthLeft
- (colShare
* stretchColCount
);
9398 // Check we don't have enough space, in which case shrink all columns, overriding
9399 // any absolute/proportional widths
9400 // TODO: actually we would like to divide up the shrinkage according to size.
9401 // How do we calculate the proportions that will achieve this?
9402 // Could first choose an arbitrary value for stretching cells, and then calculate
9403 // factors to multiply each width by.
9404 // TODO: want to record this fact and pass to an iteration that tries e.g. min widths
9405 if (widthLeft
< 0 || (stretchToFitTableWidth
&& (stretchColCount
== 0)))
9407 colShare
= tableWidthMinusPadding
/ m_colCount
;
9408 colShareRemainder
= tableWidthMinusPadding
- (colShare
* m_colCount
);
9409 for (i
= 0; i
< m_colCount
; i
++)
9412 minColWidths
[i
] = 0;
9416 // We have to adjust the columns if either we need to shrink the
9417 // table to fit the parent/table width, or we explicitly set the
9418 // table width and need to stretch out the table.
9419 if (widthLeft
< 0 || stretchToFitTableWidth
)
9421 for (i
= 0; i
< m_colCount
; i
++)
9423 if (colWidths
[i
] <= 0) // absolute or proportional width has not been specified
9425 if (minColWidths
[i
] > 0)
9426 colWidths
[i
] = minColWidths
[i
] + colShare
;
9428 colWidths
[i
] = colShare
;
9429 if (i
== (m_colCount
-1))
9430 colWidths
[i
] += colShareRemainder
; // ensure all pixels are filled
9435 // TODO: if spanned cells have no specified or max width, make them the
9436 // as big as the columns they span. Do this for all spanned cells in all
9437 // rows, of course. Size any spanned cells left over at the end - even if they
9438 // have width > 0, make sure they're limited to the appropriate column edge.
9442 Sort out confusion between content width
9443 and overall width later. For now, assume we specify overall width.
9445 So, now we've laid out the table to fit into the given space
9446 and have used specified widths and minimum widths.
9448 Now we need to consider how we will try to take maximum width into account.
9452 // (??) TODO: take max width into account
9454 // (6) Lay out all cells again with the current values
9457 int y
= availableSpace
.y
;
9458 for (j
= 0; j
< m_rowCount
; j
++)
9460 int x
= availableSpace
.x
; // TODO: take into account centering etc.
9461 int maxCellHeight
= 0;
9462 int maxSpecifiedCellHeight
= 0;
9464 wxArrayInt actualWidths
;
9465 actualWidths
.Add(0, m_colCount
);
9467 wxTextAttrDimensionConverter
converter(dc
, scale
);
9468 for (i
= 0; i
< m_colCount
; i
++)
9470 wxRichTextCell
* cell
= GetCell(j
, i
);
9471 if (cell
->IsShown())
9473 // Get max specified cell height
9474 // Don't handle percentages for height
9475 if (cell
->GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && cell
->GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() != wxTEXT_ATTR_UNITS_PERCENTAGE
)
9477 int h
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetHeight());
9478 if (h
> maxSpecifiedCellHeight
)
9479 maxSpecifiedCellHeight
= h
;
9482 if (colWidths
[i
] > 0) // absolute or proportional width has been specified
9485 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9486 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9488 wxRect availableCellSpace
;
9490 // TODO: take into acount spans
9493 // Calculate the size of this spanning cell from its constituent columns
9495 int spans
= wxMin(colSpan
, m_colCount
- i
);
9496 for (k
= i
; k
< spans
; k
++)
9502 availableCellSpace
= wxRect(x
, y
, xx
, -1);
9505 availableCellSpace
= wxRect(x
, y
, colWidths
[i
], -1);
9507 // Store actual width so we can force cell to be the appropriate width on the final loop
9508 actualWidths
[i
] = availableCellSpace
.GetWidth();
9511 cell
->Invalidate(wxRICHTEXT_ALL
);
9512 cell
->Layout(dc
, context
, availableCellSpace
, availableSpace
, style
);
9514 // TODO: use GetCachedSize().x to compute 'natural' size
9516 x
+= (availableCellSpace
.GetWidth() + paddingX
);
9517 if (cell
->GetCachedSize().y
> maxCellHeight
)
9518 maxCellHeight
= cell
->GetCachedSize().y
;
9523 maxCellHeight
= wxMax(maxCellHeight
, maxSpecifiedCellHeight
);
9525 for (i
= 0; i
< m_colCount
; i
++)
9527 wxRichTextCell
* cell
= GetCell(j
, i
);
9528 if (cell
->IsShown())
9530 wxRect availableCellSpace
= wxRect(cell
->GetPosition(), wxSize(actualWidths
[i
], maxCellHeight
));
9531 // Lay out cell with new height
9532 cell
->Invalidate(wxRICHTEXT_ALL
);
9533 cell
->Layout(dc
, context
, availableCellSpace
, availableSpace
, style
);
9535 // Make sure the cell size really is the appropriate size,
9536 // not the calculated box size
9537 cell
->SetCachedSize(wxSize(actualWidths
[i
], maxCellHeight
));
9539 maxRight
= wxMax(maxRight
, cell
->GetPosition().x
+ cell
->GetCachedSize().x
);
9544 if (j
< (m_rowCount
-1))
9548 // We need to add back the margins etc.
9550 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
9551 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxRight
- availableSpace
.x
, y
- availableSpace
.y
));
9552 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
9553 SetCachedSize(marginRect
.GetSize());
9556 // TODO: calculate max size
9558 SetMaxSize(GetCachedSize());
9561 // TODO: calculate min size
9563 SetMinSize(GetCachedSize());
9566 // TODO: currently we use either a fixed table width or the parent's size.
9567 // We also want to be able to calculate the table width from its content,
9568 // whether using fixed column widths or cell content min/max width.
9569 // Probably need a boolean flag to say whether we need to stretch cells
9570 // to fit the table width, or to simply use min/max cell widths. The
9571 // trouble with this is that if cell widths are not specified, they
9572 // will be tiny; we could use arbitrary defaults but this seems unsatisfactory.
9573 // Anyway, ignoring that problem, we probably need to factor layout into a function
9574 // that can can calculate the maximum unconstrained layout in case table size is
9575 // not specified. Then LayoutToBestSize() can choose to use either parent size to
9576 // constrain Layout(), or the previously-calculated max size to constraint layout.
9581 // Finds the absolute position and row height for the given character position
9582 bool wxRichTextTable::FindPosition(wxDC
& dc
, wxRichTextDrawingContext
& context
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
9584 wxRichTextCell
* child
= GetCell(index
+1);
9587 // Find the position at the start of the child cell, since the table doesn't
9588 // have any caret position of its own.
9589 return child
->FindPosition(dc
, context
, -1, pt
, height
, forceLineStart
);
9595 // Get the cell at the given character position (in the range of the table).
9596 wxRichTextCell
* wxRichTextTable::GetCell(long pos
) const
9598 int row
= 0, col
= 0;
9599 if (GetCellRowColumnPosition(pos
, row
, col
))
9601 return GetCell(row
, col
);
9607 // Get the row/column for a given character position
9608 bool wxRichTextTable::GetCellRowColumnPosition(long pos
, int& row
, int& col
) const
9610 if (m_colCount
== 0 || m_rowCount
== 0)
9613 row
= (int) (pos
/ m_colCount
);
9614 col
= pos
- (row
* m_colCount
);
9616 wxASSERT(row
< m_rowCount
&& col
< m_colCount
);
9618 if (row
< m_rowCount
&& col
< m_colCount
)
9624 // Calculate range, taking row/cell ordering into account instead of relying
9625 // on list ordering.
9626 void wxRichTextTable::CalculateRange(long start
, long& end
)
9628 long current
= start
;
9629 long lastEnd
= current
;
9638 for (i
= 0; i
< m_rowCount
; i
++)
9640 for (j
= 0; j
< m_colCount
; j
++)
9642 wxRichTextCell
* child
= GetCell(i
, j
);
9647 child
->CalculateRange(current
, childEnd
);
9650 current
= childEnd
+ 1;
9655 // A top-level object always has a range of size 1,
9656 // because its children don't count at this level.
9658 m_range
.SetRange(start
, start
);
9660 // An object with no children has zero length
9661 if (m_children
.GetCount() == 0)
9663 m_ownRange
.SetRange(0, lastEnd
);
9666 // Gets the range size.
9667 bool wxRichTextTable::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
9669 return wxRichTextBox::GetRangeSize(range
, size
, descent
, dc
, context
, flags
, position
, partialExtents
);
9672 // Deletes content in the given range.
9673 bool wxRichTextTable::DeleteRange(const wxRichTextRange
& WXUNUSED(range
))
9675 // TODO: implement deletion of cells
9679 // Gets any text in this object for the given range.
9680 wxString
wxRichTextTable::GetTextForRange(const wxRichTextRange
& range
) const
9682 return wxRichTextBox::GetTextForRange(range
);
9685 // Copies this object.
9686 void wxRichTextTable::Copy(const wxRichTextTable
& obj
)
9688 wxRichTextBox::Copy(obj
);
9692 m_rowCount
= obj
.m_rowCount
;
9693 m_colCount
= obj
.m_colCount
;
9695 m_cells
.Add(wxRichTextObjectPtrArray(), m_rowCount
);
9698 for (i
= 0; i
< m_rowCount
; i
++)
9700 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
9701 for (j
= 0; j
< m_colCount
; j
++)
9703 wxRichTextCell
* cell
= wxDynamicCast(obj
.GetCell(i
, j
)->Clone(), wxRichTextCell
);
9711 void wxRichTextTable::ClearTable()
9717 bool wxRichTextTable::CreateTable(int rows
, int cols
)
9724 m_cells
.Add(wxRichTextObjectPtrArray(), rows
);
9727 for (i
= 0; i
< rows
; i
++)
9729 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
9730 for (j
= 0; j
< cols
; j
++)
9732 wxRichTextCell
* cell
= new wxRichTextCell
;
9734 cell
->AddParagraph(wxEmptyString
);
9743 wxRichTextCell
* wxRichTextTable::GetCell(int row
, int col
) const
9745 wxASSERT(row
< m_rowCount
);
9746 wxASSERT(col
< m_colCount
);
9748 if (row
< m_rowCount
&& col
< m_colCount
)
9750 wxRichTextObjectPtrArray
& colArray
= m_cells
[row
];
9751 wxRichTextObject
* obj
= colArray
[col
];
9752 return wxDynamicCast(obj
, wxRichTextCell
);
9758 // Returns a selection object specifying the selections between start and end character positions.
9759 // For example, a table would deduce what cells (of range length 1) are selected when dragging across the table.
9760 wxRichTextSelection
wxRichTextTable::GetSelection(long start
, long end
) const
9762 wxRichTextSelection selection
;
9763 selection
.SetContainer((wxRichTextTable
*) this);
9772 wxASSERT( start
>= 0 && end
< (m_colCount
* m_rowCount
));
9774 if (end
>= (m_colCount
* m_rowCount
))
9777 // We need to find the rectangle of cells that is described by the rectangle
9778 // with start, end as the diagonal. Make sure we don't add cells that are
9779 // not currenty visible because they are overlapped by spanning cells.
9781 --------------------------
9782 | 0 | 1 | 2 | 3 | 4 |
9783 --------------------------
9784 | 5 | 6 | 7 | 8 | 9 |
9785 --------------------------
9786 | 10 | 11 | 12 | 13 | 14 |
9787 --------------------------
9788 | 15 | 16 | 17 | 18 | 19 |
9789 --------------------------
9791 Let's say we select 6 -> 18.
9793 Left and right edge cols of rectangle are 1 and 3 inclusive. Find least/greatest to find
9794 which is left and which is right.
9796 Top and bottom edge rows are 1 and 3 inclusive. Again, find least/greatest to find top and bottom.
9798 Now go through rows from 1 to 3 and only add cells that are (a) within above column range
9804 int leftCol
= start
- m_colCount
* int(start
/m_colCount
);
9805 int rightCol
= end
- m_colCount
* int(end
/m_colCount
);
9807 int topRow
= int(start
/m_colCount
);
9808 int bottomRow
= int(end
/m_colCount
);
9810 if (leftCol
> rightCol
)
9817 if (topRow
> bottomRow
)
9819 int tmp
= bottomRow
;
9825 for (i
= topRow
; i
<= bottomRow
; i
++)
9827 for (j
= leftCol
; j
<= rightCol
; j
++)
9829 wxRichTextCell
* cell
= GetCell(i
, j
);
9830 if (cell
&& cell
->IsShown())
9831 selection
.Add(cell
->GetRange());
9838 // Sets the attributes for the cells specified by the selection.
9839 bool wxRichTextTable::SetCellStyle(const wxRichTextSelection
& selection
, const wxRichTextAttr
& style
, int flags
)
9841 if (selection
.GetContainer() != this)
9844 wxRichTextBuffer
* buffer
= GetBuffer();
9845 bool haveControl
= (buffer
&& buffer
->GetRichTextCtrl() != NULL
);
9846 bool withUndo
= haveControl
&& ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
9849 buffer
->BeginBatchUndo(_("Set Cell Style"));
9851 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
9854 wxRichTextCell
* cell
= wxDynamicCast(node
->GetData(), wxRichTextCell
);
9855 if (cell
&& selection
.WithinSelection(cell
->GetRange().GetStart()))
9856 SetStyle(cell
, style
, flags
);
9857 node
= node
->GetNext();
9860 // Do action, or delay it until end of batch.
9862 buffer
->EndBatchUndo();
9867 bool wxRichTextTable::DeleteRows(int startRow
, int noRows
)
9869 wxASSERT((startRow
+ noRows
) < m_rowCount
);
9870 if ((startRow
+ noRows
) >= m_rowCount
)
9874 for (i
= startRow
; i
< (startRow
+noRows
); i
++)
9876 wxRichTextObjectPtrArray
& colArray
= m_cells
[startRow
];
9877 for (j
= 0; j
< (int) colArray
.GetCount(); j
++)
9879 wxRichTextObject
* cell
= colArray
[j
];
9880 RemoveChild(cell
, true);
9883 // Keep deleting at the same position, since we move all
9885 m_cells
.RemoveAt(startRow
);
9888 m_rowCount
= m_rowCount
- noRows
;
9893 bool wxRichTextTable::DeleteColumns(int startCol
, int noCols
)
9895 wxASSERT((startCol
+ noCols
) < m_colCount
);
9896 if ((startCol
+ noCols
) >= m_colCount
)
9899 bool deleteRows
= (noCols
== m_colCount
);
9902 for (i
= 0; i
< m_rowCount
; i
++)
9904 wxRichTextObjectPtrArray
& colArray
= m_cells
[deleteRows
? 0 : i
];
9905 for (j
= startCol
; j
< (startCol
+noCols
); j
++)
9907 wxRichTextObject
* cell
= colArray
[j
];
9908 RemoveChild(cell
, true);
9912 m_cells
.RemoveAt(0);
9917 m_colCount
= m_colCount
- noCols
;
9922 bool wxRichTextTable::AddRows(int startRow
, int noRows
, const wxRichTextAttr
& attr
)
9924 wxASSERT(startRow
<= m_rowCount
);
9925 if (startRow
> m_rowCount
)
9929 for (i
= 0; i
< noRows
; i
++)
9932 if (startRow
== m_rowCount
)
9934 m_cells
.Add(wxRichTextObjectPtrArray());
9935 idx
= m_cells
.GetCount() - 1;
9939 m_cells
.Insert(wxRichTextObjectPtrArray(), startRow
+i
);
9943 wxRichTextObjectPtrArray
& colArray
= m_cells
[idx
];
9944 for (j
= 0; j
< m_colCount
; j
++)
9946 wxRichTextCell
* cell
= new wxRichTextCell
;
9947 cell
->GetAttributes() = attr
;
9954 m_rowCount
= m_rowCount
+ noRows
;
9958 bool wxRichTextTable::AddColumns(int startCol
, int noCols
, const wxRichTextAttr
& attr
)
9960 wxASSERT(startCol
<= m_colCount
);
9961 if (startCol
> m_colCount
)
9965 for (i
= 0; i
< m_rowCount
; i
++)
9967 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
9968 for (j
= 0; j
< noCols
; j
++)
9970 wxRichTextCell
* cell
= new wxRichTextCell
;
9971 cell
->GetAttributes() = attr
;
9975 if (startCol
== m_colCount
)
9978 colArray
.Insert(cell
, startCol
+j
);
9982 m_colCount
= m_colCount
+ noCols
;
9987 // Edit properties via a GUI
9988 bool wxRichTextTable::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
9990 wxRichTextObjectPropertiesDialog
boxDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Table Properties"));
9991 boxDlg
.SetAttributes(GetAttributes());
9993 if (boxDlg
.ShowModal() == wxID_OK
)
9995 boxDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
10003 * Module to initialise and clean up handlers
10006 class wxRichTextModule
: public wxModule
10008 DECLARE_DYNAMIC_CLASS(wxRichTextModule
)
10010 wxRichTextModule() {}
10013 wxRichTextBuffer::SetRenderer(new wxRichTextStdRenderer
);
10014 wxRichTextBuffer::InitStandardHandlers();
10015 wxRichTextParagraph::InitDefaultTabs();
10017 wxRichTextXMLHandler::RegisterNodeName(wxT("text"), wxT("wxRichTextPlainText"));
10018 wxRichTextXMLHandler::RegisterNodeName(wxT("symbol"), wxT("wxRichTextPlainText"));
10019 wxRichTextXMLHandler::RegisterNodeName(wxT("image"), wxT("wxRichTextImage"));
10020 wxRichTextXMLHandler::RegisterNodeName(wxT("paragraph"), wxT("wxRichTextParagraph"));
10021 wxRichTextXMLHandler::RegisterNodeName(wxT("paragraphlayout"), wxT("wxRichTextParagraphLayoutBox"));
10022 wxRichTextXMLHandler::RegisterNodeName(wxT("textbox"), wxT("wxRichTextBox"));
10023 wxRichTextXMLHandler::RegisterNodeName(wxT("cell"), wxT("wxRichTextCell"));
10024 wxRichTextXMLHandler::RegisterNodeName(wxT("table"), wxT("wxRichTextTable"));
10025 wxRichTextXMLHandler::RegisterNodeName(wxT("field"), wxT("wxRichTextField"));
10031 wxRichTextBuffer::CleanUpHandlers();
10032 wxRichTextBuffer::CleanUpDrawingHandlers();
10033 wxRichTextBuffer::CleanUpFieldTypes();
10034 wxRichTextXMLHandler::ClearNodeToClassMap();
10035 wxRichTextDecimalToRoman(-1);
10036 wxRichTextParagraph::ClearDefaultTabs();
10037 wxRichTextCtrl::ClearAvailableFontNames();
10038 wxRichTextBuffer::SetRenderer(NULL
);
10042 IMPLEMENT_DYNAMIC_CLASS(wxRichTextModule
, wxModule
)
10045 // If the richtext lib is dynamically loaded after the app has already started
10046 // (such as from wxPython) then the built-in module system will not init this
10047 // module. Provide this function to do it manually.
10048 void wxRichTextModuleInit()
10050 wxModule
* module = new wxRichTextModule
;
10052 wxModule::RegisterModule(module);
10057 * Commands for undo/redo
10061 wxRichTextCommand::wxRichTextCommand(const wxString
& name
, wxRichTextCommandId id
, wxRichTextBuffer
* buffer
,
10062 wxRichTextParagraphLayoutBox
* container
, wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
): wxCommand(true, name
)
10064 /* wxRichTextAction* action = */ new wxRichTextAction(this, name
, id
, buffer
, container
, ctrl
, ignoreFirstTime
);
10067 wxRichTextCommand::wxRichTextCommand(const wxString
& name
): wxCommand(true, name
)
10071 wxRichTextCommand::~wxRichTextCommand()
10076 void wxRichTextCommand::AddAction(wxRichTextAction
* action
)
10078 if (!m_actions
.Member(action
))
10079 m_actions
.Append(action
);
10082 bool wxRichTextCommand::Do()
10084 for (wxList::compatibility_iterator node
= m_actions
.GetFirst(); node
; node
= node
->GetNext())
10086 wxRichTextAction
* action
= (wxRichTextAction
*) node
->GetData();
10093 bool wxRichTextCommand::Undo()
10095 for (wxList::compatibility_iterator node
= m_actions
.GetLast(); node
; node
= node
->GetPrevious())
10097 wxRichTextAction
* action
= (wxRichTextAction
*) node
->GetData();
10104 void wxRichTextCommand::ClearActions()
10106 WX_CLEAR_LIST(wxList
, m_actions
);
10110 * Individual action
10114 wxRichTextAction::wxRichTextAction(wxRichTextCommand
* cmd
, const wxString
& name
, wxRichTextCommandId id
,
10115 wxRichTextBuffer
* buffer
, wxRichTextParagraphLayoutBox
* container
,
10116 wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
)
10120 m_containerAddress
.Create(buffer
, container
);
10121 m_ignoreThis
= ignoreFirstTime
;
10126 m_newParagraphs
.SetDefaultStyle(buffer
->GetDefaultStyle());
10127 m_newParagraphs
.SetBasicStyle(buffer
->GetBasicStyle());
10129 cmd
->AddAction(this);
10132 wxRichTextAction::~wxRichTextAction()
10138 // Returns the container that this action refers to, using the container address and top-level buffer.
10139 wxRichTextParagraphLayoutBox
* wxRichTextAction::GetContainer() const
10141 wxRichTextParagraphLayoutBox
* container
= wxDynamicCast(GetContainerAddress().GetObject(m_buffer
), wxRichTextParagraphLayoutBox
);
10146 void wxRichTextAction::CalculateRefreshOptimizations(wxArrayInt
& optimizationLineCharPositions
, wxArrayInt
& optimizationLineYPositions
)
10148 // Store a list of line start character and y positions so we can figure out which area
10149 // we need to refresh
10151 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10152 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10153 wxASSERT(container
!= NULL
);
10157 // NOTE: we're assuming that the buffer is laid out correctly at this point.
10158 // If we had several actions, which only invalidate and leave layout until the
10159 // paint handler is called, then this might not be true. So we may need to switch
10160 // optimisation on only when we're simply adding text and not simultaneously
10161 // deleting a selection, for example. Or, we make sure the buffer is laid out correctly
10162 // first, but of course this means we'll be doing it twice.
10163 if (!m_buffer
->IsDirty() && m_ctrl
) // can only do optimisation if the buffer is already laid out correctly
10165 wxSize clientSize
= m_ctrl
->GetUnscaledSize(m_ctrl
->GetClientSize());
10166 wxPoint firstVisiblePt
= m_ctrl
->GetUnscaledPoint(m_ctrl
->GetFirstVisiblePoint());
10167 int lastY
= firstVisiblePt
.y
+ clientSize
.y
;
10169 wxRichTextParagraph
* para
= container
->GetParagraphAtPosition(GetRange().GetStart());
10170 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(para
);
10173 wxRichTextParagraph
* child
= (wxRichTextParagraph
*) node
->GetData();
10174 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
10177 wxRichTextLine
* line
= node2
->GetData();
10178 wxPoint pt
= line
->GetAbsolutePosition();
10179 wxRichTextRange range
= line
->GetAbsoluteRange();
10183 node2
= wxRichTextLineList::compatibility_iterator();
10184 node
= wxRichTextObjectList::compatibility_iterator();
10186 else if (range
.GetStart() > GetPosition() && pt
.y
>= firstVisiblePt
.y
)
10188 optimizationLineCharPositions
.Add(range
.GetStart());
10189 optimizationLineYPositions
.Add(pt
.y
);
10193 node2
= node2
->GetNext();
10197 node
= node
->GetNext();
10203 bool wxRichTextAction::Do()
10205 m_buffer
->Modify(true);
10207 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10208 wxASSERT(container
!= NULL
);
10214 case wxRICHTEXT_INSERT
:
10216 // Store a list of line start character and y positions so we can figure out which area
10217 // we need to refresh
10218 wxArrayInt optimizationLineCharPositions
;
10219 wxArrayInt optimizationLineYPositions
;
10221 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10222 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
10225 container
->InsertFragment(GetRange().GetStart(), m_newParagraphs
);
10226 container
->UpdateRanges();
10228 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10229 // Layout() would stop prematurely at the top level.
10230 container
->InvalidateHierarchy(wxRichTextRange(wxMax(0, GetRange().GetStart()-1), GetRange().GetEnd()));
10232 long newCaretPosition
= GetPosition() + m_newParagraphs
.GetOwnRange().GetLength();
10234 // Character position to caret position
10235 newCaretPosition
--;
10237 // Don't take into account the last newline
10238 if (m_newParagraphs
.GetPartialParagraph())
10239 newCaretPosition
--;
10241 if (m_newParagraphs
.GetChildren().GetCount() > 1)
10243 wxRichTextObject
* p
= (wxRichTextObject
*) m_newParagraphs
.GetChildren().GetLast()->GetData();
10244 if (p
->GetRange().GetLength() == 1)
10245 newCaretPosition
--;
10248 newCaretPosition
= wxMin(newCaretPosition
, (container
->GetOwnRange().GetEnd()-1));
10250 UpdateAppearance(newCaretPosition
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */);
10252 wxRichTextEvent
cmdEvent(
10253 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED
,
10254 m_ctrl
? m_ctrl
->GetId() : -1);
10255 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10256 cmdEvent
.SetRange(GetRange());
10257 cmdEvent
.SetPosition(GetRange().GetStart());
10258 cmdEvent
.SetContainer(container
);
10260 m_buffer
->SendEvent(cmdEvent
);
10264 case wxRICHTEXT_DELETE
:
10266 wxArrayInt optimizationLineCharPositions
;
10267 wxArrayInt optimizationLineYPositions
;
10269 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10270 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
10273 container
->DeleteRange(GetRange());
10274 container
->UpdateRanges();
10275 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10276 // Layout() would stop prematurely at the top level.
10277 container
->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
10279 long caretPos
= GetRange().GetStart()-1;
10280 if (caretPos
>= container
->GetOwnRange().GetEnd())
10283 UpdateAppearance(caretPos
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */);
10285 wxRichTextEvent
cmdEvent(
10286 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED
,
10287 m_ctrl
? m_ctrl
->GetId() : -1);
10288 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10289 cmdEvent
.SetRange(GetRange());
10290 cmdEvent
.SetPosition(GetRange().GetStart());
10291 cmdEvent
.SetContainer(container
);
10293 m_buffer
->SendEvent(cmdEvent
);
10297 case wxRICHTEXT_CHANGE_STYLE
:
10298 case wxRICHTEXT_CHANGE_PROPERTIES
:
10300 ApplyParagraphs(GetNewParagraphs());
10302 // Invalidate the whole buffer if there were floating objects
10303 if (wxRichTextBuffer::GetFloatingLayoutMode() && container
->GetFloatingObjectCount() > 0)
10304 m_buffer
->InvalidateHierarchy(wxRICHTEXT_ALL
);
10307 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10308 // Layout() would stop prematurely at the top level.
10309 container
->InvalidateHierarchy(GetRange());
10312 UpdateAppearance(GetPosition());
10314 wxRichTextEvent
cmdEvent(
10315 m_cmdId
== wxRICHTEXT_CHANGE_STYLE
? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
: wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED
,
10316 m_ctrl
? m_ctrl
->GetId() : -1);
10317 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10318 cmdEvent
.SetRange(GetRange());
10319 cmdEvent
.SetPosition(GetRange().GetStart());
10320 cmdEvent
.SetContainer(container
);
10322 m_buffer
->SendEvent(cmdEvent
);
10326 case wxRICHTEXT_CHANGE_ATTRIBUTES
:
10328 wxRichTextObject
* obj
= m_objectAddress
.GetObject(m_buffer
); // container->GetChildAtPosition(GetRange().GetStart());
10331 wxRichTextAttr oldAttr
= obj
->GetAttributes();
10332 obj
->GetAttributes() = m_attributes
;
10333 m_attributes
= oldAttr
;
10336 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10337 // Layout() would stop prematurely at the top level.
10338 // Invalidate the whole buffer if there were floating objects
10339 if (wxRichTextBuffer::GetFloatingLayoutMode() && container
->GetFloatingObjectCount() > 0)
10340 m_buffer
->InvalidateHierarchy(wxRICHTEXT_ALL
);
10342 container
->InvalidateHierarchy(GetRange());
10344 UpdateAppearance(GetPosition());
10346 wxRichTextEvent
cmdEvent(
10347 wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
,
10348 m_ctrl
? m_ctrl
->GetId() : -1);
10349 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10350 cmdEvent
.SetRange(GetRange());
10351 cmdEvent
.SetPosition(GetRange().GetStart());
10352 cmdEvent
.SetContainer(container
);
10354 m_buffer
->SendEvent(cmdEvent
);
10358 case wxRICHTEXT_CHANGE_OBJECT
:
10360 wxRichTextObject
* obj
= m_objectAddress
.GetObject(m_buffer
);
10361 // wxRichTextObject* obj = container->GetChildAtPosition(GetRange().GetStart());
10362 if (obj
&& m_object
)
10364 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(obj
);
10367 wxRichTextObject
* obj
= node
->GetData();
10368 node
->SetData(m_object
);
10373 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10374 // Layout() would stop prematurely at the top level.
10375 // Invalidate the whole buffer if there were floating objects
10376 if (wxRichTextBuffer::GetFloatingLayoutMode() && container
->GetFloatingObjectCount() > 0)
10377 m_buffer
->InvalidateHierarchy(wxRICHTEXT_ALL
);
10379 container
->InvalidateHierarchy(GetRange());
10381 UpdateAppearance(GetPosition());
10383 // TODO: send new kind of modification event
10394 bool wxRichTextAction::Undo()
10396 m_buffer
->Modify(true);
10398 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10399 wxASSERT(container
!= NULL
);
10405 case wxRICHTEXT_INSERT
:
10407 wxArrayInt optimizationLineCharPositions
;
10408 wxArrayInt optimizationLineYPositions
;
10410 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10411 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
10414 container
->DeleteRange(GetRange());
10415 container
->UpdateRanges();
10417 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10418 // Layout() would stop prematurely at the top level.
10419 container
->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
10421 long newCaretPosition
= GetPosition() - 1;
10423 UpdateAppearance(newCaretPosition
, true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */);
10425 wxRichTextEvent
cmdEvent(
10426 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED
,
10427 m_ctrl
? m_ctrl
->GetId() : -1);
10428 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10429 cmdEvent
.SetRange(GetRange());
10430 cmdEvent
.SetPosition(GetRange().GetStart());
10431 cmdEvent
.SetContainer(container
);
10433 m_buffer
->SendEvent(cmdEvent
);
10437 case wxRICHTEXT_DELETE
:
10439 wxArrayInt optimizationLineCharPositions
;
10440 wxArrayInt optimizationLineYPositions
;
10442 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10443 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
10446 container
->InsertFragment(GetRange().GetStart(), m_oldParagraphs
);
10447 container
->UpdateRanges();
10449 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10450 // Layout() would stop prematurely at the top level.
10451 container
->InvalidateHierarchy(GetRange());
10453 UpdateAppearance(GetPosition(), true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */);
10455 wxRichTextEvent
cmdEvent(
10456 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED
,
10457 m_ctrl
? m_ctrl
->GetId() : -1);
10458 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10459 cmdEvent
.SetRange(GetRange());
10460 cmdEvent
.SetPosition(GetRange().GetStart());
10461 cmdEvent
.SetContainer(container
);
10463 m_buffer
->SendEvent(cmdEvent
);
10467 case wxRICHTEXT_CHANGE_STYLE
:
10468 case wxRICHTEXT_CHANGE_PROPERTIES
:
10470 ApplyParagraphs(GetOldParagraphs());
10471 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10472 // Layout() would stop prematurely at the top level.
10473 container
->InvalidateHierarchy(GetRange());
10475 UpdateAppearance(GetPosition());
10477 wxRichTextEvent
cmdEvent(
10478 m_cmdId
== wxRICHTEXT_CHANGE_STYLE
? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
: wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED
,
10479 m_ctrl
? m_ctrl
->GetId() : -1);
10480 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10481 cmdEvent
.SetRange(GetRange());
10482 cmdEvent
.SetPosition(GetRange().GetStart());
10483 cmdEvent
.SetContainer(container
);
10485 m_buffer
->SendEvent(cmdEvent
);
10489 case wxRICHTEXT_CHANGE_ATTRIBUTES
:
10490 case wxRICHTEXT_CHANGE_OBJECT
:
10501 /// Update the control appearance
10502 void wxRichTextAction::UpdateAppearance(long caretPosition
, bool sendUpdateEvent
, wxArrayInt
* optimizationLineCharPositions
, wxArrayInt
* optimizationLineYPositions
, bool isDoCmd
)
10504 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10505 wxASSERT(container
!= NULL
);
10511 m_ctrl
->SetFocusObject(container
);
10512 m_ctrl
->SetCaretPosition(caretPosition
);
10514 if (!m_ctrl
->IsFrozen())
10516 wxRect containerRect
= container
->GetRect();
10518 m_ctrl
->LayoutContent();
10520 // Refresh everything if there were floating objects or the container changed size
10521 // (we can't yet optimize in these cases, since more complex interaction with other content occurs)
10522 if ((wxRichTextBuffer::GetFloatingLayoutMode() && container
->GetFloatingObjectCount() > 0) || (container
->GetParent() && containerRect
!= container
->GetRect()))
10524 m_ctrl
->Refresh(false);
10528 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10529 // Find refresh rectangle if we are in a position to optimise refresh
10530 if ((m_cmdId
== wxRICHTEXT_INSERT
|| m_cmdId
== wxRICHTEXT_DELETE
) && optimizationLineCharPositions
)
10534 wxSize clientSize
= m_ctrl
->GetUnscaledSize(m_ctrl
->GetClientSize());
10535 wxPoint firstVisiblePt
= m_ctrl
->GetUnscaledPoint(m_ctrl
->GetFirstVisiblePoint());
10537 // Start/end positions
10539 int lastY
= firstVisiblePt
.y
+ clientSize
.y
;
10541 bool foundEnd
= false;
10543 // position offset - how many characters were inserted
10544 int positionOffset
= GetRange().GetLength();
10546 // Determine whether this is Do or Undo, and adjust positionOffset accordingly
10547 if ((m_cmdId
== wxRICHTEXT_DELETE
&& isDoCmd
) || (m_cmdId
== wxRICHTEXT_INSERT
&& !isDoCmd
))
10548 positionOffset
= - positionOffset
;
10550 // find the first line which is being drawn at the same position as it was
10551 // before. Since we're talking about a simple insertion, we can assume
10552 // that the rest of the window does not need to be redrawn.
10554 wxRichTextParagraph
* para
= container
->GetParagraphAtPosition(GetPosition());
10555 // Since we support floating layout, we should redraw the whole para instead of just
10556 // the first line touching the invalid range.
10559 firstY
= para
->GetPosition().y
;
10562 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(para
);
10565 wxRichTextParagraph
* child
= (wxRichTextParagraph
*) node
->GetData();
10566 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
10569 wxRichTextLine
* line
= node2
->GetData();
10570 wxPoint pt
= line
->GetAbsolutePosition();
10571 wxRichTextRange range
= line
->GetAbsoluteRange();
10573 // we want to find the first line that is in the same position
10574 // as before. This will mean we're at the end of the changed text.
10576 if (pt
.y
> lastY
) // going past the end of the window, no more info
10578 node2
= wxRichTextLineList::compatibility_iterator();
10579 node
= wxRichTextObjectList::compatibility_iterator();
10581 // Detect last line in the buffer
10582 else if (!node2
->GetNext() && para
->GetRange().Contains(container
->GetOwnRange().GetEnd()))
10584 // If deleting text, make sure we refresh below as well as above
10585 if (positionOffset
>= 0)
10588 lastY
= pt
.y
+ line
->GetSize().y
;
10591 node2
= wxRichTextLineList::compatibility_iterator();
10592 node
= wxRichTextObjectList::compatibility_iterator();
10598 // search for this line being at the same position as before
10599 for (i
= 0; i
< optimizationLineCharPositions
->GetCount(); i
++)
10601 if (((*optimizationLineCharPositions
)[i
] + positionOffset
== range
.GetStart()) &&
10602 ((*optimizationLineYPositions
)[i
] == pt
.y
))
10604 // Stop, we're now the same as we were
10609 node2
= wxRichTextLineList::compatibility_iterator();
10610 node
= wxRichTextObjectList::compatibility_iterator();
10618 node2
= node2
->GetNext();
10622 node
= node
->GetNext();
10625 firstY
= wxMax(firstVisiblePt
.y
, firstY
);
10627 lastY
= firstVisiblePt
.y
+ clientSize
.y
;
10629 // Convert to device coordinates
10630 wxRect
rect(m_ctrl
->GetPhysicalPoint(m_ctrl
->GetScaledPoint(wxPoint(firstVisiblePt
.x
, firstY
))), m_ctrl
->GetScaledSize(wxSize(clientSize
.x
, lastY
- firstY
)));
10631 m_ctrl
->RefreshRect(rect
);
10635 m_ctrl
->Refresh(false);
10637 m_ctrl
->PositionCaret();
10639 // This causes styles to persist when doing programmatic
10640 // content creation except when Freeze/Thaw is used, so
10641 // disable this and check for the consequences.
10642 // m_ctrl->SetDefaultStyleToCursorStyle();
10644 if (sendUpdateEvent
)
10645 wxTextCtrl::SendTextUpdatedEvent(m_ctrl
);
10650 /// Replace the buffer paragraphs with the new ones.
10651 void wxRichTextAction::ApplyParagraphs(const wxRichTextParagraphLayoutBox
& fragment
)
10653 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10654 wxASSERT(container
!= NULL
);
10658 wxRichTextObjectList::compatibility_iterator node
= fragment
.GetChildren().GetFirst();
10661 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
10662 wxASSERT (para
!= NULL
);
10664 // We'll replace the existing paragraph by finding the paragraph at this position,
10665 // delete its node data, and setting a copy as the new node data.
10666 // TODO: make more efficient by simply swapping old and new paragraph objects.
10668 wxRichTextParagraph
* existingPara
= container
->GetParagraphAtPosition(para
->GetRange().GetStart());
10671 wxRichTextObjectList::compatibility_iterator bufferParaNode
= container
->GetChildren().Find(existingPara
);
10672 if (bufferParaNode
)
10674 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(*para
);
10675 newPara
->SetParent(container
);
10677 bufferParaNode
->SetData(newPara
);
10679 delete existingPara
;
10683 node
= node
->GetNext();
10690 * This stores beginning and end positions for a range of data.
10693 WX_DEFINE_OBJARRAY(wxRichTextRangeArray
);
10695 /// Limit this range to be within 'range'
10696 bool wxRichTextRange::LimitTo(const wxRichTextRange
& range
)
10698 if (m_start
< range
.m_start
)
10699 m_start
= range
.m_start
;
10701 if (m_end
> range
.m_end
)
10702 m_end
= range
.m_end
;
10708 * wxRichTextImage implementation
10709 * This object represents an image.
10712 IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage
, wxRichTextObject
)
10714 wxRichTextImage::wxRichTextImage(const wxImage
& image
, wxRichTextObject
* parent
, wxRichTextAttr
* charStyle
):
10715 wxRichTextObject(parent
)
10718 m_imageBlock
.MakeImageBlockDefaultQuality(image
, wxBITMAP_TYPE_PNG
);
10720 SetAttributes(*charStyle
);
10723 wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock
& imageBlock
, wxRichTextObject
* parent
, wxRichTextAttr
* charStyle
):
10724 wxRichTextObject(parent
)
10727 m_imageBlock
= imageBlock
;
10729 SetAttributes(*charStyle
);
10732 void wxRichTextImage::Init()
10734 m_originalImageSize
= wxSize(-1, -1);
10737 /// Create a cached image at the required size
10738 bool wxRichTextImage::LoadImageCache(wxDC
& dc
, bool resetCache
)
10740 if (!m_imageBlock
.IsOk())
10743 // If we have an original image size, use that to compute the cached bitmap size
10744 // instead of loading the image each time. This way we can avoid loading
10745 // the image so long as the new cached bitmap size hasn't changed.
10748 if (resetCache
|| m_originalImageSize
.GetWidth() <= 0 || m_originalImageSize
.GetHeight() <= 0)
10750 m_imageCache
= wxNullBitmap
;
10752 m_imageBlock
.Load(image
);
10756 m_originalImageSize
= wxSize(image
.GetWidth(), image
.GetHeight());
10759 int width
= m_originalImageSize
.GetWidth();
10760 int height
= m_originalImageSize
.GetHeight();
10762 int parentWidth
= 0;
10763 int parentHeight
= 0;
10766 int maxHeight
= -1;
10768 wxRichTextBuffer
* buffer
= GetBuffer();
10772 if (buffer
->GetRichTextCtrl())
10774 // Subtract borders
10775 sz
= buffer
->GetRichTextCtrl()->GetClientSize();
10777 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
10778 marginRect
= wxRect(0, 0, sz
.x
, sz
.y
);
10779 buffer
->GetBoxRects(dc
, buffer
, buffer
->GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
10781 sz
= contentRect
.GetSize();
10783 // Start with a maximum width of the control size, even if not specified by the content,
10784 // to minimize the amount of picture overlapping the right-hand side
10788 sz
= buffer
->GetCachedSize();
10789 parentWidth
= sz
.GetWidth();
10790 parentHeight
= sz
.GetHeight();
10793 if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetWidth().GetValue() > 0)
10795 if (parentWidth
> 0 && GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
10796 width
= (int) ((GetAttributes().GetTextBoxAttr().GetWidth().GetValue() * parentWidth
)/100.0);
10797 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
10798 width
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetWidth().GetValue());
10799 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
10800 width
= GetAttributes().GetTextBoxAttr().GetWidth().GetValue();
10803 // Limit to max width
10805 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() > 0)
10809 if (parentWidth
> 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
10810 mw
= (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() * parentWidth
)/100.0);
10811 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
10812 mw
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue());
10813 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
10814 mw
= GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue();
10816 // If we already have a smaller max width due to the constraints of the control size,
10817 // don't use the larger max width.
10818 if (mw
!= -1 && ((maxWidth
== -1) || (mw
< maxWidth
)))
10822 if (maxWidth
> 0 && width
> maxWidth
)
10825 // Preserve the aspect ratio
10826 if (width
!= m_originalImageSize
.GetWidth())
10827 height
= (int) (float(m_originalImageSize
.GetHeight()) * (float(width
)/float(m_originalImageSize
.GetWidth())));
10829 if (GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetHeight().GetValue() > 0)
10831 if (parentHeight
> 0 && GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
10832 height
= (int) ((GetAttributes().GetTextBoxAttr().GetHeight().GetValue() * parentHeight
)/100.0);
10833 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
10834 height
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetHeight().GetValue());
10835 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
10836 height
= GetAttributes().GetTextBoxAttr().GetHeight().GetValue();
10838 // Preserve the aspect ratio
10839 if (height
!= m_originalImageSize
.GetHeight())
10840 width
= (int) (float(m_originalImageSize
.GetWidth()) * (float(height
)/float(m_originalImageSize
.GetHeight())));
10843 // Limit to max height
10845 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() > 0)
10847 if (parentHeight
> 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
10848 maxHeight
= (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() * parentHeight
)/100.0);
10849 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
10850 maxHeight
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue());
10851 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
10852 maxHeight
= GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue();
10855 if (maxHeight
> 0 && height
> maxHeight
)
10857 height
= maxHeight
;
10859 // Preserve the aspect ratio
10860 if (height
!= m_originalImageSize
.GetHeight())
10861 width
= (int) (float(m_originalImageSize
.GetWidth()) * (float(height
)/float(m_originalImageSize
.GetHeight())));
10864 if (m_imageCache
.IsOk() && m_imageCache
.GetWidth() == width
&& m_imageCache
.GetHeight() == height
)
10866 // Do nothing, we didn't need to change the image cache
10872 m_imageBlock
.Load(image
);
10877 if (image
.GetWidth() == width
&& image
.GetHeight() == height
)
10878 m_imageCache
= wxBitmap(image
);
10881 // If the original width and height is small, e.g. 400 or below,
10882 // scale up and then down to improve image quality. This can make
10883 // a big difference, with not much performance hit.
10884 int upscaleThreshold
= 400;
10886 if (image
.GetWidth() <= upscaleThreshold
|| image
.GetHeight() <= upscaleThreshold
)
10888 img
= image
.Scale(image
.GetWidth()*2, image
.GetHeight()*2);
10889 img
.Rescale(width
, height
, wxIMAGE_QUALITY_HIGH
);
10892 img
= image
.Scale(width
, height
, wxIMAGE_QUALITY_HIGH
);
10893 m_imageCache
= wxBitmap(img
);
10897 return m_imageCache
.IsOk();
10901 bool wxRichTextImage::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& WXUNUSED(range
), const wxRichTextSelection
& selection
, const wxRect
& rect
, int WXUNUSED(descent
), int WXUNUSED(style
))
10906 // Don't need cached size AFAIK
10907 // wxSize size = GetCachedSize();
10908 if (!LoadImageCache(dc
))
10911 wxRichTextAttr
attr(GetAttributes());
10912 context
.ApplyVirtualAttributes(attr
, this);
10914 DrawBoxAttributes(dc
, GetBuffer(), attr
, wxRect(rect
.GetPosition(), GetCachedSize()));
10916 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
10917 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
10918 marginRect
= rect
; // outer rectangle, will calculate contentRect
10919 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
10921 dc
.DrawBitmap(m_imageCache
, contentRect
.x
, contentRect
.y
, true);
10923 if (selection
.WithinSelection(GetRange().GetStart(), this))
10925 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
10926 wxCheckSetPen(dc
, *wxBLACK_PEN
);
10927 dc
.SetLogicalFunction(wxINVERT
);
10928 dc
.DrawRectangle(contentRect
);
10929 dc
.SetLogicalFunction(wxCOPY
);
10935 /// Lay the item out
10936 bool wxRichTextImage::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& WXUNUSED(parentRect
), int WXUNUSED(style
))
10938 if (!LoadImageCache(dc
))
10941 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
10942 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
10943 contentRect
= wxRect(wxPoint(0,0), imageSize
);
10945 wxRichTextAttr
attr(GetAttributes());
10946 context
.ApplyVirtualAttributes(attr
, this);
10948 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
10950 wxSize overallSize
= marginRect
.GetSize();
10952 SetCachedSize(overallSize
);
10953 SetMaxSize(overallSize
);
10954 SetMinSize(overallSize
);
10955 SetPosition(rect
.GetPosition());
10960 /// Get/set the object size for the given range. Returns false if the range
10961 /// is invalid for this object.
10962 bool wxRichTextImage::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& WXUNUSED(descent
), wxDC
& dc
, wxRichTextDrawingContext
& context
, int WXUNUSED(flags
), wxPoint
WXUNUSED(position
), wxArrayInt
* partialExtents
) const
10964 if (!range
.IsWithin(GetRange()))
10967 if (!((wxRichTextImage
*)this)->LoadImageCache(dc
))
10969 size
.x
= 0; size
.y
= 0;
10970 if (partialExtents
)
10971 partialExtents
->Add(0);
10975 wxRichTextAttr
attr(GetAttributes());
10976 context
.ApplyVirtualAttributes(attr
, (wxRichTextObject
*) this);
10978 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
10979 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
10980 contentRect
= wxRect(wxPoint(0,0), imageSize
);
10981 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
10983 wxSize overallSize
= marginRect
.GetSize();
10985 if (partialExtents
)
10986 partialExtents
->Add(overallSize
.x
);
10988 size
= overallSize
;
10993 // Get the 'natural' size for an object. For an image, it would be the
10995 wxTextAttrSize
wxRichTextImage::GetNaturalSize() const
10997 wxTextAttrSize size
;
10998 if (GetImageCache().IsOk())
11000 size
.SetWidth(GetImageCache().GetWidth(), wxTEXT_ATTR_UNITS_PIXELS
);
11001 size
.SetHeight(GetImageCache().GetHeight(), wxTEXT_ATTR_UNITS_PIXELS
);
11008 void wxRichTextImage::Copy(const wxRichTextImage
& obj
)
11010 wxRichTextObject::Copy(obj
);
11012 m_imageBlock
= obj
.m_imageBlock
;
11013 m_originalImageSize
= obj
.m_originalImageSize
;
11016 /// Edit properties via a GUI
11017 bool wxRichTextImage::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
11019 wxRichTextObjectPropertiesDialog
imageDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Picture Properties"));
11020 imageDlg
.SetAttributes(GetAttributes());
11022 if (imageDlg
.ShowModal() == wxID_OK
)
11024 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
11025 // indeterminate in the object.
11026 imageDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
11038 /// Compare two attribute objects
11039 bool wxTextAttrEq(const wxRichTextAttr
& attr1
, const wxRichTextAttr
& attr2
)
11041 return (attr1
== attr2
);
11045 bool wxRichTextTabsEq(const wxArrayInt
& tabs1
, const wxArrayInt
& tabs2
)
11047 if (tabs1
.GetCount() != tabs2
.GetCount())
11051 for (i
= 0; i
< tabs1
.GetCount(); i
++)
11053 if (tabs1
[i
] != tabs2
[i
])
11059 bool wxRichTextApplyStyle(wxRichTextAttr
& destStyle
, const wxRichTextAttr
& style
, wxRichTextAttr
* compareWith
)
11061 return destStyle
.Apply(style
, compareWith
);
11064 // Remove attributes
11065 bool wxRichTextRemoveStyle(wxRichTextAttr
& destStyle
, const wxRichTextAttr
& style
)
11067 return destStyle
.RemoveStyle(style
);
11070 /// Combine two bitlists, specifying the bits of interest with separate flags.
11071 bool wxRichTextCombineBitlists(int& valueA
, int valueB
, int& flagsA
, int flagsB
)
11073 return wxRichTextAttr::CombineBitlists(valueA
, valueB
, flagsA
, flagsB
);
11076 /// Compare two bitlists
11077 bool wxRichTextBitlistsEqPartial(int valueA
, int valueB
, int flags
)
11079 return wxRichTextAttr::BitlistsEqPartial(valueA
, valueB
, flags
);
11082 /// Split into paragraph and character styles
11083 bool wxRichTextSplitParaCharStyles(const wxRichTextAttr
& style
, wxRichTextAttr
& parStyle
, wxRichTextAttr
& charStyle
)
11085 return wxRichTextAttr::SplitParaCharStyles(style
, parStyle
, charStyle
);
11088 /// Convert a decimal to Roman numerals
11089 wxString
wxRichTextDecimalToRoman(long n
)
11091 static wxArrayInt decimalNumbers
;
11092 static wxArrayString romanNumbers
;
11097 decimalNumbers
.Clear();
11098 romanNumbers
.Clear();
11099 return wxEmptyString
;
11102 if (decimalNumbers
.GetCount() == 0)
11104 #define wxRichTextAddDecRom(n, r) decimalNumbers.Add(n); romanNumbers.Add(r);
11106 wxRichTextAddDecRom(1000, wxT("M"));
11107 wxRichTextAddDecRom(900, wxT("CM"));
11108 wxRichTextAddDecRom(500, wxT("D"));
11109 wxRichTextAddDecRom(400, wxT("CD"));
11110 wxRichTextAddDecRom(100, wxT("C"));
11111 wxRichTextAddDecRom(90, wxT("XC"));
11112 wxRichTextAddDecRom(50, wxT("L"));
11113 wxRichTextAddDecRom(40, wxT("XL"));
11114 wxRichTextAddDecRom(10, wxT("X"));
11115 wxRichTextAddDecRom(9, wxT("IX"));
11116 wxRichTextAddDecRom(5, wxT("V"));
11117 wxRichTextAddDecRom(4, wxT("IV"));
11118 wxRichTextAddDecRom(1, wxT("I"));
11124 while (n
> 0 && i
< 13)
11126 if (n
>= decimalNumbers
[i
])
11128 n
-= decimalNumbers
[i
];
11129 roman
+= romanNumbers
[i
];
11136 if (roman
.IsEmpty())
11142 * wxRichTextFileHandler
11143 * Base class for file handlers
11146 IMPLEMENT_CLASS(wxRichTextFileHandler
, wxObject
)
11148 #if wxUSE_FFILE && wxUSE_STREAMS
11149 bool wxRichTextFileHandler::LoadFile(wxRichTextBuffer
*buffer
, const wxString
& filename
)
11151 wxFFileInputStream
stream(filename
);
11153 return LoadFile(buffer
, stream
);
11158 bool wxRichTextFileHandler::SaveFile(wxRichTextBuffer
*buffer
, const wxString
& filename
)
11160 wxFFileOutputStream
stream(filename
);
11162 return SaveFile(buffer
, stream
);
11166 #endif // wxUSE_FFILE && wxUSE_STREAMS
11168 /// Can we handle this filename (if using files)? By default, checks the extension.
11169 bool wxRichTextFileHandler::CanHandle(const wxString
& filename
) const
11171 wxString path
, file
, ext
;
11172 wxFileName::SplitPath(filename
, & path
, & file
, & ext
);
11174 return (ext
.Lower() == GetExtension());
11178 * wxRichTextTextHandler
11179 * Plain text handler
11182 IMPLEMENT_CLASS(wxRichTextPlainTextHandler
, wxRichTextFileHandler
)
11185 bool wxRichTextPlainTextHandler::DoLoadFile(wxRichTextBuffer
*buffer
, wxInputStream
& stream
)
11187 if (!stream
.IsOk())
11193 while (!stream
.Eof())
11195 int ch
= stream
.GetC();
11199 if (ch
== 10 && lastCh
!= 13)
11202 if (ch
> 0 && ch
!= 10)
11209 buffer
->ResetAndClearCommands();
11211 buffer
->AddParagraphs(str
);
11212 buffer
->UpdateRanges();
11217 bool wxRichTextPlainTextHandler::DoSaveFile(wxRichTextBuffer
*buffer
, wxOutputStream
& stream
)
11219 if (!stream
.IsOk())
11222 wxString text
= buffer
->GetText();
11224 wxString newLine
= wxRichTextLineBreakChar
;
11225 text
.Replace(newLine
, wxT("\n"));
11227 wxCharBuffer buf
= text
.ToAscii();
11229 stream
.Write((const char*) buf
, text
.length());
11232 #endif // wxUSE_STREAMS
11235 * Stores information about an image, in binary in-memory form
11238 wxRichTextImageBlock::wxRichTextImageBlock()
11243 wxRichTextImageBlock::wxRichTextImageBlock(const wxRichTextImageBlock
& block
):wxObject()
11249 wxRichTextImageBlock::~wxRichTextImageBlock()
11254 void wxRichTextImageBlock::Init()
11258 m_imageType
= wxBITMAP_TYPE_INVALID
;
11261 void wxRichTextImageBlock::Clear()
11265 m_imageType
= wxBITMAP_TYPE_INVALID
;
11269 // Load the original image into a memory block.
11270 // If the image is not a JPEG, we must convert it into a JPEG
11271 // to conserve space.
11272 // If it's not a JPEG we can make use of 'image', already scaled, so we don't have to
11273 // load the image a 2nd time.
11275 bool wxRichTextImageBlock::MakeImageBlock(const wxString
& filename
, wxBitmapType imageType
,
11276 wxImage
& image
, bool convertToJPEG
)
11278 m_imageType
= imageType
;
11280 wxString
filenameToRead(filename
);
11281 bool removeFile
= false;
11283 if (imageType
== wxBITMAP_TYPE_INVALID
)
11284 return false; // Could not determine image type
11286 if ((imageType
!= wxBITMAP_TYPE_JPEG
) && convertToJPEG
)
11288 wxString tempFile
=
11289 wxFileName::CreateTempFileName(_("image"));
11291 wxASSERT(!tempFile
.IsEmpty());
11293 image
.SaveFile(tempFile
, wxBITMAP_TYPE_JPEG
);
11294 filenameToRead
= tempFile
;
11297 m_imageType
= wxBITMAP_TYPE_JPEG
;
11300 if (!file
.Open(filenameToRead
))
11303 m_dataSize
= (size_t) file
.Length();
11308 m_data
= ReadBlock(filenameToRead
, m_dataSize
);
11311 wxRemoveFile(filenameToRead
);
11313 return (m_data
!= NULL
);
11316 // Make an image block from the wxImage in the given
11318 bool wxRichTextImageBlock::MakeImageBlock(wxImage
& image
, wxBitmapType imageType
, int quality
)
11320 image
.SetOption(wxT("quality"), quality
);
11322 if (imageType
== wxBITMAP_TYPE_INVALID
)
11323 return false; // Could not determine image type
11325 return DoMakeImageBlock(image
, imageType
);
11328 // Uses a const wxImage for efficiency, but can't set quality (only relevant for JPEG)
11329 bool wxRichTextImageBlock::MakeImageBlockDefaultQuality(const wxImage
& image
, wxBitmapType imageType
)
11331 if (imageType
== wxBITMAP_TYPE_INVALID
)
11332 return false; // Could not determine image type
11334 return DoMakeImageBlock(image
, imageType
);
11337 // Makes the image block
11338 bool wxRichTextImageBlock::DoMakeImageBlock(const wxImage
& image
, wxBitmapType imageType
)
11340 wxMemoryOutputStream memStream
;
11341 if (!image
.SaveFile(memStream
, imageType
))
11346 unsigned char* block
= new unsigned char[memStream
.GetSize()];
11354 m_imageType
= imageType
;
11355 m_dataSize
= memStream
.GetSize();
11357 memStream
.CopyTo(m_data
, m_dataSize
);
11359 return (m_data
!= NULL
);
11363 bool wxRichTextImageBlock::Write(const wxString
& filename
)
11365 return WriteBlock(filename
, m_data
, m_dataSize
);
11368 void wxRichTextImageBlock::Copy(const wxRichTextImageBlock
& block
)
11370 m_imageType
= block
.m_imageType
;
11372 m_dataSize
= block
.m_dataSize
;
11373 if (m_dataSize
== 0)
11376 m_data
= new unsigned char[m_dataSize
];
11378 for (i
= 0; i
< m_dataSize
; i
++)
11379 m_data
[i
] = block
.m_data
[i
];
11383 void wxRichTextImageBlock::operator=(const wxRichTextImageBlock
& block
)
11388 // Load a wxImage from the block
11389 bool wxRichTextImageBlock::Load(wxImage
& image
)
11394 // Read in the image.
11396 wxMemoryInputStream
mstream(m_data
, m_dataSize
);
11397 bool success
= image
.LoadFile(mstream
, GetImageType());
11399 wxString tempFile
= wxFileName::CreateTempFileName(_("image"));
11400 wxASSERT(!tempFile
.IsEmpty());
11402 if (!WriteBlock(tempFile
, m_data
, m_dataSize
))
11406 success
= image
.LoadFile(tempFile
, GetImageType());
11407 wxRemoveFile(tempFile
);
11413 // Write data in hex to a stream
11414 bool wxRichTextImageBlock::WriteHex(wxOutputStream
& stream
)
11416 if (m_dataSize
== 0)
11419 int bufSize
= 100000;
11420 if (int(2*m_dataSize
) < bufSize
)
11421 bufSize
= 2*m_dataSize
;
11422 char* buf
= new char[bufSize
+1];
11424 int left
= m_dataSize
;
11429 if (left
*2 > bufSize
)
11431 n
= bufSize
; left
-= (bufSize
/2);
11435 n
= left
*2; left
= 0;
11439 for (i
= 0; i
< (n
/2); i
++)
11441 wxDecToHex(m_data
[j
], b
, b
+1);
11446 stream
.Write((const char*) buf
, n
);
11452 // Read data in hex from a stream
11453 bool wxRichTextImageBlock::ReadHex(wxInputStream
& stream
, int length
, wxBitmapType imageType
)
11455 int dataSize
= length
/2;
11460 // create a null terminated temporary string:
11464 m_data
= new unsigned char[dataSize
];
11466 for (i
= 0; i
< dataSize
; i
++)
11468 str
[0] = (char)stream
.GetC();
11469 str
[1] = (char)stream
.GetC();
11471 m_data
[i
] = (unsigned char)wxHexToDec(str
);
11474 m_dataSize
= dataSize
;
11475 m_imageType
= imageType
;
11480 // Allocate and read from stream as a block of memory
11481 unsigned char* wxRichTextImageBlock::ReadBlock(wxInputStream
& stream
, size_t size
)
11483 unsigned char* block
= new unsigned char[size
];
11487 stream
.Read(block
, size
);
11492 unsigned char* wxRichTextImageBlock::ReadBlock(const wxString
& filename
, size_t size
)
11494 wxFileInputStream
stream(filename
);
11495 if (!stream
.IsOk())
11498 return ReadBlock(stream
, size
);
11501 // Write memory block to stream
11502 bool wxRichTextImageBlock::WriteBlock(wxOutputStream
& stream
, unsigned char* block
, size_t size
)
11504 stream
.Write((void*) block
, size
);
11505 return stream
.IsOk();
11509 // Write memory block to file
11510 bool wxRichTextImageBlock::WriteBlock(const wxString
& filename
, unsigned char* block
, size_t size
)
11512 wxFileOutputStream
outStream(filename
);
11513 if (!outStream
.IsOk())
11516 return WriteBlock(outStream
, block
, size
);
11519 // Gets the extension for the block's type
11520 wxString
wxRichTextImageBlock::GetExtension() const
11522 wxImageHandler
* handler
= wxImage::FindHandler(GetImageType());
11524 return handler
->GetExtension();
11526 return wxEmptyString
;
11532 * The data object for a wxRichTextBuffer
11535 const wxChar
*wxRichTextBufferDataObject::ms_richTextBufferFormatId
= wxT("wxShape");
11537 wxRichTextBufferDataObject::wxRichTextBufferDataObject(wxRichTextBuffer
* richTextBuffer
)
11539 m_richTextBuffer
= richTextBuffer
;
11541 // this string should uniquely identify our format, but is otherwise
11543 m_formatRichTextBuffer
.SetId(GetRichTextBufferFormatId());
11545 SetFormat(m_formatRichTextBuffer
);
11548 wxRichTextBufferDataObject::~wxRichTextBufferDataObject()
11550 delete m_richTextBuffer
;
11553 // after a call to this function, the richTextBuffer is owned by the caller and it
11554 // is responsible for deleting it!
11555 wxRichTextBuffer
* wxRichTextBufferDataObject::GetRichTextBuffer()
11557 wxRichTextBuffer
* richTextBuffer
= m_richTextBuffer
;
11558 m_richTextBuffer
= NULL
;
11560 return richTextBuffer
;
11563 wxDataFormat
wxRichTextBufferDataObject::GetPreferredFormat(Direction
WXUNUSED(dir
)) const
11565 return m_formatRichTextBuffer
;
11568 size_t wxRichTextBufferDataObject::GetDataSize() const
11570 if (!m_richTextBuffer
)
11576 wxStringOutputStream
stream(& bufXML
);
11577 if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
))
11579 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
11585 wxCharBuffer buffer
= bufXML
.mb_str(wxConvUTF8
);
11586 return strlen(buffer
) + 1;
11588 return bufXML
.Length()+1;
11592 bool wxRichTextBufferDataObject::GetDataHere(void *pBuf
) const
11594 if (!pBuf
|| !m_richTextBuffer
)
11600 wxStringOutputStream
stream(& bufXML
);
11601 if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
))
11603 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
11609 wxCharBuffer buffer
= bufXML
.mb_str(wxConvUTF8
);
11610 size_t len
= strlen(buffer
);
11611 memcpy((char*) pBuf
, (const char*) buffer
, len
);
11612 ((char*) pBuf
)[len
] = 0;
11614 size_t len
= bufXML
.Length();
11615 memcpy((char*) pBuf
, (const char*) bufXML
.c_str(), len
);
11616 ((char*) pBuf
)[len
] = 0;
11622 bool wxRichTextBufferDataObject::SetData(size_t WXUNUSED(len
), const void *buf
)
11624 wxDELETE(m_richTextBuffer
);
11626 wxString
bufXML((const char*) buf
, wxConvUTF8
);
11628 m_richTextBuffer
= new wxRichTextBuffer
;
11630 wxStringInputStream
stream(bufXML
);
11631 if (!m_richTextBuffer
->LoadFile(stream
, wxRICHTEXT_TYPE_XML
))
11633 wxLogError(wxT("Could not read the buffer from an XML stream.\nYou may have forgotten to add the XML file handler."));
11635 wxDELETE(m_richTextBuffer
);
11647 * wxRichTextFontTable
11648 * Manages quick access to a pool of fonts for rendering rich text
11651 WX_DECLARE_STRING_HASH_MAP_WITH_DECL(wxFont
, wxRichTextFontTableHashMap
, class WXDLLIMPEXP_RICHTEXT
);
11653 class wxRichTextFontTableData
: public wxObjectRefData
11656 wxRichTextFontTableData() {}
11658 wxFont
FindFont(const wxRichTextAttr
& fontSpec
, double fontScale
);
11660 wxRichTextFontTableHashMap m_hashMap
;
11663 wxFont
wxRichTextFontTableData::FindFont(const wxRichTextAttr
& fontSpec
, double fontScale
)
11665 wxString
facename(fontSpec
.GetFontFaceName());
11667 int fontSize
= fontSpec
.GetFontSize();
11668 if (fontScale
!= 1.0)
11669 fontSize
= (int) ((double(fontSize
) * fontScale
) + 0.5);
11672 if (fontSpec
.HasFontPixelSize() && !fontSpec
.HasFontPointSize())
11676 wxString spec
= wxString::Format(wxT("%d-%s-%d-%d-%d-%d-%s-%d"),
11677 fontSize
, units
.c_str(), fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), (int) fontSpec
.GetFontUnderlined(), (int) fontSpec
.GetFontStrikethrough(),
11678 facename
.c_str(), (int) fontSpec
.GetFontEncoding());
11680 wxRichTextFontTableHashMap::iterator entry
= m_hashMap
.find(spec
);
11681 if ( entry
== m_hashMap
.end() )
11683 if (fontSpec
.HasFontPixelSize() && !fontSpec
.HasFontPointSize())
11685 wxFont
font(wxSize(0, fontSize
), wxFONTFAMILY_DEFAULT
, fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), fontSpec
.GetFontUnderlined(), facename
);
11686 if (fontSpec
.HasFontStrikethrough() && fontSpec
.GetFontStrikethrough())
11687 font
.SetStrikethrough(true);
11688 m_hashMap
[spec
] = font
;
11693 wxFont
font(fontSize
, wxFONTFAMILY_DEFAULT
, fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), fontSpec
.GetFontUnderlined(), facename
.c_str());
11694 if (fontSpec
.HasFontStrikethrough() && fontSpec
.GetFontStrikethrough())
11695 font
.SetStrikethrough(true);
11697 m_hashMap
[spec
] = font
;
11703 return entry
->second
;
11707 IMPLEMENT_DYNAMIC_CLASS(wxRichTextFontTable
, wxObject
)
11709 wxRichTextFontTable::wxRichTextFontTable()
11711 m_refData
= new wxRichTextFontTableData
;
11715 wxRichTextFontTable::wxRichTextFontTable(const wxRichTextFontTable
& table
)
11721 wxRichTextFontTable::~wxRichTextFontTable()
11726 bool wxRichTextFontTable::operator == (const wxRichTextFontTable
& table
) const
11728 return (m_refData
== table
.m_refData
);
11731 void wxRichTextFontTable::operator= (const wxRichTextFontTable
& table
)
11734 m_fontScale
= table
.m_fontScale
;
11737 wxFont
wxRichTextFontTable::FindFont(const wxRichTextAttr
& fontSpec
)
11739 wxRichTextFontTableData
* data
= (wxRichTextFontTableData
*) m_refData
;
11741 return data
->FindFont(fontSpec
, m_fontScale
);
11746 void wxRichTextFontTable::Clear()
11748 wxRichTextFontTableData
* data
= (wxRichTextFontTableData
*) m_refData
;
11750 data
->m_hashMap
.clear();
11753 void wxRichTextFontTable::SetFontScale(double fontScale
)
11755 if (fontScale
!= m_fontScale
)
11757 m_fontScale
= fontScale
;
11762 void wxTextBoxAttr::Reset()
11765 m_floatMode
= wxTEXT_BOX_ATTR_FLOAT_NONE
;
11766 m_clearMode
= wxTEXT_BOX_ATTR_CLEAR_NONE
;
11767 m_collapseMode
= wxTEXT_BOX_ATTR_COLLAPSE_NONE
;
11768 m_verticalAlignment
= wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_NONE
;
11769 m_boxStyleName
= wxEmptyString
;
11773 m_position
.Reset();
11784 bool wxTextBoxAttr::operator== (const wxTextBoxAttr
& attr
) const
11787 m_flags
== attr
.m_flags
&&
11788 m_floatMode
== attr
.m_floatMode
&&
11789 m_clearMode
== attr
.m_clearMode
&&
11790 m_collapseMode
== attr
.m_collapseMode
&&
11791 m_verticalAlignment
== attr
.m_verticalAlignment
&&
11793 m_margins
== attr
.m_margins
&&
11794 m_padding
== attr
.m_padding
&&
11795 m_position
== attr
.m_position
&&
11797 m_size
== attr
.m_size
&&
11798 m_minSize
== attr
.m_minSize
&&
11799 m_maxSize
== attr
.m_maxSize
&&
11801 m_border
== attr
.m_border
&&
11802 m_outline
== attr
.m_outline
&&
11804 m_boxStyleName
== attr
.m_boxStyleName
11808 // Partial equality test
11809 bool wxTextBoxAttr::EqPartial(const wxTextBoxAttr
& attr
, bool weakTest
) const
11812 ((!HasFloatMode() && attr
.HasFloatMode()) ||
11813 (!HasClearMode() && attr
.HasClearMode()) ||
11814 (!HasCollapseBorders() && attr
.HasCollapseBorders()) ||
11815 (!HasVerticalAlignment() && attr
.HasVerticalAlignment()) ||
11816 (!HasBoxStyleName() && attr
.HasBoxStyleName())))
11820 if (attr
.HasFloatMode() && HasFloatMode() && (GetFloatMode() != attr
.GetFloatMode()))
11823 if (attr
.HasClearMode() && HasClearMode() && (GetClearMode() != attr
.GetClearMode()))
11826 if (attr
.HasCollapseBorders() && HasCollapseBorders() && (attr
.GetCollapseBorders() != GetCollapseBorders()))
11829 if (attr
.HasVerticalAlignment() && HasVerticalAlignment() && (attr
.GetVerticalAlignment() != GetVerticalAlignment()))
11832 if (attr
.HasBoxStyleName() && HasBoxStyleName() && (attr
.GetBoxStyleName() != GetBoxStyleName()))
11837 if (!m_position
.EqPartial(attr
.m_position
, weakTest
))
11842 if (!m_size
.EqPartial(attr
.m_size
, weakTest
))
11844 if (!m_minSize
.EqPartial(attr
.m_minSize
, weakTest
))
11846 if (!m_maxSize
.EqPartial(attr
.m_maxSize
, weakTest
))
11851 if (!m_margins
.EqPartial(attr
.m_margins
, weakTest
))
11856 if (!m_padding
.EqPartial(attr
.m_padding
, weakTest
))
11861 if (!GetBorder().EqPartial(attr
.GetBorder(), weakTest
))
11866 if (!GetOutline().EqPartial(attr
.GetOutline(), weakTest
))
11872 // Merges the given attributes. If compareWith
11873 // is non-NULL, then it will be used to mask out those attributes that are the same in style
11874 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
11875 bool wxTextBoxAttr::Apply(const wxTextBoxAttr
& attr
, const wxTextBoxAttr
* compareWith
)
11877 if (attr
.HasFloatMode())
11879 if (!(compareWith
&& compareWith
->HasFloatMode() && compareWith
->GetFloatMode() == attr
.GetFloatMode()))
11880 SetFloatMode(attr
.GetFloatMode());
11883 if (attr
.HasClearMode())
11885 if (!(compareWith
&& compareWith
->HasClearMode() && compareWith
->GetClearMode() == attr
.GetClearMode()))
11886 SetClearMode(attr
.GetClearMode());
11889 if (attr
.HasCollapseBorders())
11891 if (!(compareWith
&& compareWith
->HasCollapseBorders() && compareWith
->GetCollapseBorders() == attr
.GetCollapseBorders()))
11892 SetCollapseBorders(attr
.GetCollapseBorders());
11895 if (attr
.HasVerticalAlignment())
11897 if (!(compareWith
&& compareWith
->HasVerticalAlignment() && compareWith
->GetVerticalAlignment() == attr
.GetVerticalAlignment()))
11898 SetVerticalAlignment(attr
.GetVerticalAlignment());
11901 if (attr
.HasBoxStyleName())
11903 if (!(compareWith
&& compareWith
->HasBoxStyleName() && compareWith
->GetBoxStyleName() == attr
.GetBoxStyleName()))
11904 SetBoxStyleName(attr
.GetBoxStyleName());
11907 m_margins
.Apply(attr
.m_margins
, compareWith
? (& attr
.m_margins
) : (const wxTextAttrDimensions
*) NULL
);
11908 m_padding
.Apply(attr
.m_padding
, compareWith
? (& attr
.m_padding
) : (const wxTextAttrDimensions
*) NULL
);
11909 m_position
.Apply(attr
.m_position
, compareWith
? (& attr
.m_position
) : (const wxTextAttrDimensions
*) NULL
);
11911 m_size
.Apply(attr
.m_size
, compareWith
? (& attr
.m_size
) : (const wxTextAttrSize
*) NULL
);
11912 m_minSize
.Apply(attr
.m_minSize
, compareWith
? (& attr
.m_minSize
) : (const wxTextAttrSize
*) NULL
);
11913 m_maxSize
.Apply(attr
.m_maxSize
, compareWith
? (& attr
.m_maxSize
) : (const wxTextAttrSize
*) NULL
);
11915 m_border
.Apply(attr
.m_border
, compareWith
? (& attr
.m_border
) : (const wxTextAttrBorders
*) NULL
);
11916 m_outline
.Apply(attr
.m_outline
, compareWith
? (& attr
.m_outline
) : (const wxTextAttrBorders
*) NULL
);
11921 // Remove specified attributes from this object
11922 bool wxTextBoxAttr::RemoveStyle(const wxTextBoxAttr
& attr
)
11924 if (attr
.HasFloatMode())
11925 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT
);
11927 if (attr
.HasClearMode())
11928 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR
);
11930 if (attr
.HasCollapseBorders())
11931 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
11933 if (attr
.HasVerticalAlignment())
11934 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
11936 if (attr
.HasBoxStyleName())
11938 SetBoxStyleName(wxEmptyString
);
11939 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
11942 m_margins
.RemoveStyle(attr
.m_margins
);
11943 m_padding
.RemoveStyle(attr
.m_padding
);
11944 m_position
.RemoveStyle(attr
.m_position
);
11946 m_size
.RemoveStyle(attr
.m_size
);
11947 m_minSize
.RemoveStyle(attr
.m_minSize
);
11948 m_maxSize
.RemoveStyle(attr
.m_maxSize
);
11950 m_border
.RemoveStyle(attr
.m_border
);
11951 m_outline
.RemoveStyle(attr
.m_outline
);
11956 // Collects the attributes that are common to a range of content, building up a note of
11957 // which attributes are absent in some objects and which clash in some objects.
11958 void wxTextBoxAttr::CollectCommonAttributes(const wxTextBoxAttr
& attr
, wxTextBoxAttr
& clashingAttr
, wxTextBoxAttr
& absentAttr
)
11960 if (attr
.HasFloatMode())
11962 if (!clashingAttr
.HasFloatMode() && !absentAttr
.HasFloatMode())
11964 if (HasFloatMode())
11966 if (GetFloatMode() != attr
.GetFloatMode())
11968 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_FLOAT
);
11969 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT
);
11973 SetFloatMode(attr
.GetFloatMode());
11977 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_FLOAT
);
11979 if (attr
.HasClearMode())
11981 if (!clashingAttr
.HasClearMode() && !absentAttr
.HasClearMode())
11983 if (HasClearMode())
11985 if (GetClearMode() != attr
.GetClearMode())
11987 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_CLEAR
);
11988 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR
);
11992 SetClearMode(attr
.GetClearMode());
11996 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_CLEAR
);
11998 if (attr
.HasCollapseBorders())
12000 if (!clashingAttr
.HasCollapseBorders() && !absentAttr
.HasCollapseBorders())
12002 if (HasCollapseBorders())
12004 if (GetCollapseBorders() != attr
.GetCollapseBorders())
12006 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
12007 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
12011 SetCollapseBorders(attr
.GetCollapseBorders());
12015 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
12017 if (attr
.HasVerticalAlignment())
12019 if (!clashingAttr
.HasVerticalAlignment() && !absentAttr
.HasVerticalAlignment())
12021 if (HasVerticalAlignment())
12023 if (GetVerticalAlignment() != attr
.GetVerticalAlignment())
12025 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
12026 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
12030 SetVerticalAlignment(attr
.GetVerticalAlignment());
12034 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
12036 if (attr
.HasBoxStyleName())
12038 if (!clashingAttr
.HasBoxStyleName() && !absentAttr
.HasBoxStyleName())
12040 if (HasBoxStyleName())
12042 if (GetBoxStyleName() != attr
.GetBoxStyleName())
12044 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
12045 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
12049 SetBoxStyleName(attr
.GetBoxStyleName());
12053 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
12055 m_margins
.CollectCommonAttributes(attr
.m_margins
, clashingAttr
.m_margins
, absentAttr
.m_margins
);
12056 m_padding
.CollectCommonAttributes(attr
.m_padding
, clashingAttr
.m_padding
, absentAttr
.m_padding
);
12057 m_position
.CollectCommonAttributes(attr
.m_position
, clashingAttr
.m_position
, absentAttr
.m_position
);
12059 m_size
.CollectCommonAttributes(attr
.m_size
, clashingAttr
.m_size
, absentAttr
.m_size
);
12060 m_minSize
.CollectCommonAttributes(attr
.m_minSize
, clashingAttr
.m_minSize
, absentAttr
.m_minSize
);
12061 m_maxSize
.CollectCommonAttributes(attr
.m_maxSize
, clashingAttr
.m_maxSize
, absentAttr
.m_maxSize
);
12063 m_border
.CollectCommonAttributes(attr
.m_border
, clashingAttr
.m_border
, absentAttr
.m_border
);
12064 m_outline
.CollectCommonAttributes(attr
.m_outline
, clashingAttr
.m_outline
, absentAttr
.m_outline
);
12067 bool wxTextBoxAttr::IsDefault() const
12069 return GetFlags() == 0 && !m_border
.IsValid() && !m_outline
.IsValid() &&
12070 !m_size
.IsValid() && !m_minSize
.IsValid() && !m_maxSize
.IsValid() &&
12071 !m_position
.IsValid() && !m_padding
.IsValid() && !m_margins
.IsValid();
12076 void wxRichTextAttr::Copy(const wxRichTextAttr
& attr
)
12078 wxTextAttr::Copy(attr
);
12080 m_textBoxAttr
= attr
.m_textBoxAttr
;
12083 bool wxRichTextAttr::operator==(const wxRichTextAttr
& attr
) const
12085 if (!(wxTextAttr::operator==(attr
)))
12088 return (m_textBoxAttr
== attr
.m_textBoxAttr
);
12091 // Partial equality test
12092 bool wxRichTextAttr::EqPartial(const wxRichTextAttr
& attr
, bool weakTest
) const
12094 if (!(wxTextAttr::EqPartial(attr
, weakTest
)))
12097 return m_textBoxAttr
.EqPartial(attr
.m_textBoxAttr
, weakTest
);
12100 // Merges the given attributes. If compareWith
12101 // is non-NULL, then it will be used to mask out those attributes that are the same in style
12102 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
12103 bool wxRichTextAttr::Apply(const wxRichTextAttr
& style
, const wxRichTextAttr
* compareWith
)
12105 wxTextAttr::Apply(style
, compareWith
);
12107 return m_textBoxAttr
.Apply(style
.m_textBoxAttr
, compareWith
? (& compareWith
->m_textBoxAttr
) : (const wxTextBoxAttr
*) NULL
);
12110 // Remove specified attributes from this object
12111 bool wxRichTextAttr::RemoveStyle(const wxRichTextAttr
& attr
)
12113 wxTextAttr::RemoveStyle(*this, attr
);
12115 return m_textBoxAttr
.RemoveStyle(attr
.m_textBoxAttr
);
12118 // Collects the attributes that are common to a range of content, building up a note of
12119 // which attributes are absent in some objects and which clash in some objects.
12120 void wxRichTextAttr::CollectCommonAttributes(const wxRichTextAttr
& attr
, wxRichTextAttr
& clashingAttr
, wxRichTextAttr
& absentAttr
)
12122 wxTextAttrCollectCommonAttributes(*this, attr
, clashingAttr
, absentAttr
);
12124 m_textBoxAttr
.CollectCommonAttributes(attr
.m_textBoxAttr
, clashingAttr
.m_textBoxAttr
, absentAttr
.m_textBoxAttr
);
12127 // Partial equality test
12128 bool wxTextAttrBorder::EqPartial(const wxTextAttrBorder
& border
, bool weakTest
) const
12131 ((!HasStyle() && border
.HasStyle()) ||
12132 (!HasColour() && border
.HasColour()) ||
12133 (!HasWidth() && border
.HasWidth())))
12138 if (border
.HasStyle() && HasStyle() && (border
.GetStyle() != GetStyle()))
12141 if (border
.HasColour() && HasColour() && (border
.GetColourLong() != GetColourLong()))
12144 if (border
.HasWidth() && HasWidth() && !(border
.GetWidth() == GetWidth()))
12150 // Apply border to 'this', but not if the same as compareWith
12151 bool wxTextAttrBorder::Apply(const wxTextAttrBorder
& border
, const wxTextAttrBorder
* compareWith
)
12153 if (border
.HasStyle())
12155 if (!(compareWith
&& (border
.GetStyle() == compareWith
->GetStyle())))
12156 SetStyle(border
.GetStyle());
12158 if (border
.HasColour())
12160 if (!(compareWith
&& (border
.GetColourLong() == compareWith
->GetColourLong())))
12161 SetColour(border
.GetColourLong());
12163 if (border
.HasWidth())
12165 if (!(compareWith
&& (border
.GetWidth() == compareWith
->GetWidth())))
12166 SetWidth(border
.GetWidth());
12172 // Remove specified attributes from this object
12173 bool wxTextAttrBorder::RemoveStyle(const wxTextAttrBorder
& attr
)
12175 if (attr
.HasStyle() && HasStyle())
12176 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_STYLE
);
12177 if (attr
.HasColour() && HasColour())
12178 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_COLOUR
);
12179 if (attr
.HasWidth() && HasWidth())
12180 m_borderWidth
.Reset();
12185 // Collects the attributes that are common to a range of content, building up a note of
12186 // which attributes are absent in some objects and which clash in some objects.
12187 void wxTextAttrBorder::CollectCommonAttributes(const wxTextAttrBorder
& attr
, wxTextAttrBorder
& clashingAttr
, wxTextAttrBorder
& absentAttr
)
12189 if (attr
.HasStyle())
12191 if (!clashingAttr
.HasStyle() && !absentAttr
.HasStyle())
12195 if (GetStyle() != attr
.GetStyle())
12197 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
12198 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
12202 SetStyle(attr
.GetStyle());
12206 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
12208 if (attr
.HasColour())
12210 if (!clashingAttr
.HasColour() && !absentAttr
.HasColour())
12214 if (GetColour() != attr
.GetColour())
12216 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
12217 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
12221 SetColour(attr
.GetColourLong());
12225 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
12227 m_borderWidth
.CollectCommonAttributes(attr
.m_borderWidth
, clashingAttr
.m_borderWidth
, absentAttr
.m_borderWidth
);
12230 // Partial equality test
12231 bool wxTextAttrBorders::EqPartial(const wxTextAttrBorders
& borders
, bool weakTest
) const
12233 return m_left
.EqPartial(borders
.m_left
, weakTest
) && m_right
.EqPartial(borders
.m_right
, weakTest
) &&
12234 m_top
.EqPartial(borders
.m_top
, weakTest
) && m_bottom
.EqPartial(borders
.m_bottom
, weakTest
);
12237 // Apply border to 'this', but not if the same as compareWith
12238 bool wxTextAttrBorders::Apply(const wxTextAttrBorders
& borders
, const wxTextAttrBorders
* compareWith
)
12240 m_left
.Apply(borders
.m_left
, compareWith
? (& compareWith
->m_left
) : (const wxTextAttrBorder
*) NULL
);
12241 m_right
.Apply(borders
.m_right
, compareWith
? (& compareWith
->m_right
) : (const wxTextAttrBorder
*) NULL
);
12242 m_top
.Apply(borders
.m_top
, compareWith
? (& compareWith
->m_top
) : (const wxTextAttrBorder
*) NULL
);
12243 m_bottom
.Apply(borders
.m_bottom
, compareWith
? (& compareWith
->m_bottom
) : (const wxTextAttrBorder
*) NULL
);
12247 // Remove specified attributes from this object
12248 bool wxTextAttrBorders::RemoveStyle(const wxTextAttrBorders
& attr
)
12250 m_left
.RemoveStyle(attr
.m_left
);
12251 m_right
.RemoveStyle(attr
.m_right
);
12252 m_top
.RemoveStyle(attr
.m_top
);
12253 m_bottom
.RemoveStyle(attr
.m_bottom
);
12257 // Collects the attributes that are common to a range of content, building up a note of
12258 // which attributes are absent in some objects and which clash in some objects.
12259 void wxTextAttrBorders::CollectCommonAttributes(const wxTextAttrBorders
& attr
, wxTextAttrBorders
& clashingAttr
, wxTextAttrBorders
& absentAttr
)
12261 m_left
.CollectCommonAttributes(attr
.m_left
, clashingAttr
.m_left
, absentAttr
.m_left
);
12262 m_right
.CollectCommonAttributes(attr
.m_right
, clashingAttr
.m_right
, absentAttr
.m_right
);
12263 m_top
.CollectCommonAttributes(attr
.m_top
, clashingAttr
.m_top
, absentAttr
.m_top
);
12264 m_bottom
.CollectCommonAttributes(attr
.m_bottom
, clashingAttr
.m_bottom
, absentAttr
.m_bottom
);
12267 // Set style of all borders
12268 void wxTextAttrBorders::SetStyle(int style
)
12270 m_left
.SetStyle(style
);
12271 m_right
.SetStyle(style
);
12272 m_top
.SetStyle(style
);
12273 m_bottom
.SetStyle(style
);
12276 // Set colour of all borders
12277 void wxTextAttrBorders::SetColour(unsigned long colour
)
12279 m_left
.SetColour(colour
);
12280 m_right
.SetColour(colour
);
12281 m_top
.SetColour(colour
);
12282 m_bottom
.SetColour(colour
);
12285 void wxTextAttrBorders::SetColour(const wxColour
& colour
)
12287 m_left
.SetColour(colour
);
12288 m_right
.SetColour(colour
);
12289 m_top
.SetColour(colour
);
12290 m_bottom
.SetColour(colour
);
12293 // Set width of all borders
12294 void wxTextAttrBorders::SetWidth(const wxTextAttrDimension
& width
)
12296 m_left
.SetWidth(width
);
12297 m_right
.SetWidth(width
);
12298 m_top
.SetWidth(width
);
12299 m_bottom
.SetWidth(width
);
12302 // Partial equality test
12303 bool wxTextAttrDimension::EqPartial(const wxTextAttrDimension
& dim
, bool weakTest
) const
12305 if (!weakTest
&& !IsValid() && dim
.IsValid())
12308 if (dim
.IsValid() && IsValid() && !((*this) == dim
))
12314 bool wxTextAttrDimension::Apply(const wxTextAttrDimension
& dim
, const wxTextAttrDimension
* compareWith
)
12318 if (!(compareWith
&& dim
== (*compareWith
)))
12325 // Collects the attributes that are common to a range of content, building up a note of
12326 // which attributes are absent in some objects and which clash in some objects.
12327 void wxTextAttrDimension::CollectCommonAttributes(const wxTextAttrDimension
& attr
, wxTextAttrDimension
& clashingAttr
, wxTextAttrDimension
& absentAttr
)
12329 if (attr
.IsValid())
12331 if (!clashingAttr
.IsValid() && !absentAttr
.IsValid())
12335 if (!((*this) == attr
))
12337 clashingAttr
.SetValid(true);
12346 absentAttr
.SetValid(true);
12349 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(wxDC
& dc
, double scale
, const wxSize
& parentSize
)
12351 m_ppi
= dc
.GetPPI().x
; m_scale
= scale
; m_parentSize
= parentSize
;
12354 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(int ppi
, double scale
, const wxSize
& parentSize
)
12356 m_ppi
= ppi
; m_scale
= scale
; m_parentSize
= parentSize
;
12359 int wxTextAttrDimensionConverter::ConvertTenthsMMToPixels(int units
) const
12361 return wxRichTextObject::ConvertTenthsMMToPixels(m_ppi
, units
, m_scale
);
12364 int wxTextAttrDimensionConverter::ConvertPixelsToTenthsMM(int pixels
) const
12366 return wxRichTextObject::ConvertPixelsToTenthsMM(m_ppi
, pixels
, m_scale
);
12369 int wxTextAttrDimensionConverter::GetPixels(const wxTextAttrDimension
& dim
, int direction
) const
12371 if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
12372 return ConvertTenthsMMToPixels(dim
.GetValue());
12373 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
12374 return dim
.GetValue();
12375 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
12377 wxASSERT(m_parentSize
!= wxDefaultSize
);
12378 if (direction
== wxHORIZONTAL
)
12379 return (int) (double(m_parentSize
.x
) * double(dim
.GetValue()) / 100.0);
12381 return (int) (double(m_parentSize
.y
) * double(dim
.GetValue()) / 100.0);
12390 int wxTextAttrDimensionConverter::GetTenthsMM(const wxTextAttrDimension
& dim
) const
12392 if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
12393 return dim
.GetValue();
12394 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
12395 return ConvertPixelsToTenthsMM(dim
.GetValue());
12403 // Partial equality test
12404 bool wxTextAttrDimensions::EqPartial(const wxTextAttrDimensions
& dims
, bool weakTest
) const
12406 if (!m_left
.EqPartial(dims
.m_left
, weakTest
))
12409 if (!m_right
.EqPartial(dims
.m_right
, weakTest
))
12412 if (!m_top
.EqPartial(dims
.m_top
, weakTest
))
12415 if (!m_bottom
.EqPartial(dims
.m_bottom
, weakTest
))
12421 // Apply border to 'this', but not if the same as compareWith
12422 bool wxTextAttrDimensions::Apply(const wxTextAttrDimensions
& dims
, const wxTextAttrDimensions
* compareWith
)
12424 m_left
.Apply(dims
.m_left
, compareWith
? (& compareWith
->m_left
) : (const wxTextAttrDimension
*) NULL
);
12425 m_right
.Apply(dims
.m_right
, compareWith
? (& compareWith
->m_right
): (const wxTextAttrDimension
*) NULL
);
12426 m_top
.Apply(dims
.m_top
, compareWith
? (& compareWith
->m_top
): (const wxTextAttrDimension
*) NULL
);
12427 m_bottom
.Apply(dims
.m_bottom
, compareWith
? (& compareWith
->m_bottom
): (const wxTextAttrDimension
*) NULL
);
12432 // Remove specified attributes from this object
12433 bool wxTextAttrDimensions::RemoveStyle(const wxTextAttrDimensions
& attr
)
12435 if (attr
.m_left
.IsValid())
12437 if (attr
.m_right
.IsValid())
12439 if (attr
.m_top
.IsValid())
12441 if (attr
.m_bottom
.IsValid())
12447 // Collects the attributes that are common to a range of content, building up a note of
12448 // which attributes are absent in some objects and which clash in some objects.
12449 void wxTextAttrDimensions::CollectCommonAttributes(const wxTextAttrDimensions
& attr
, wxTextAttrDimensions
& clashingAttr
, wxTextAttrDimensions
& absentAttr
)
12451 m_left
.CollectCommonAttributes(attr
.m_left
, clashingAttr
.m_left
, absentAttr
.m_left
);
12452 m_right
.CollectCommonAttributes(attr
.m_right
, clashingAttr
.m_right
, absentAttr
.m_right
);
12453 m_top
.CollectCommonAttributes(attr
.m_top
, clashingAttr
.m_top
, absentAttr
.m_top
);
12454 m_bottom
.CollectCommonAttributes(attr
.m_bottom
, clashingAttr
.m_bottom
, absentAttr
.m_bottom
);
12457 // Partial equality test
12458 bool wxTextAttrSize::EqPartial(const wxTextAttrSize
& size
, bool weakTest
) const
12460 if (!m_width
.EqPartial(size
.m_width
, weakTest
))
12463 if (!m_height
.EqPartial(size
.m_height
, weakTest
))
12469 // Apply border to 'this', but not if the same as compareWith
12470 bool wxTextAttrSize::Apply(const wxTextAttrSize
& size
, const wxTextAttrSize
* compareWith
)
12472 m_width
.Apply(size
.m_width
, compareWith
? (& compareWith
->m_width
) : (const wxTextAttrDimension
*) NULL
);
12473 m_height
.Apply(size
.m_height
, compareWith
? (& compareWith
->m_height
): (const wxTextAttrDimension
*) NULL
);
12478 // Remove specified attributes from this object
12479 bool wxTextAttrSize::RemoveStyle(const wxTextAttrSize
& attr
)
12481 if (attr
.m_width
.IsValid())
12483 if (attr
.m_height
.IsValid())
12489 // Collects the attributes that are common to a range of content, building up a note of
12490 // which attributes are absent in some objects and which clash in some objects.
12491 void wxTextAttrSize::CollectCommonAttributes(const wxTextAttrSize
& attr
, wxTextAttrSize
& clashingAttr
, wxTextAttrSize
& absentAttr
)
12493 m_width
.CollectCommonAttributes(attr
.m_width
, clashingAttr
.m_width
, absentAttr
.m_width
);
12494 m_height
.CollectCommonAttributes(attr
.m_height
, clashingAttr
.m_height
, absentAttr
.m_height
);
12497 // Collects the attributes that are common to a range of content, building up a note of
12498 // which attributes are absent in some objects and which clash in some objects.
12499 void wxTextAttrCollectCommonAttributes(wxTextAttr
& currentStyle
, const wxTextAttr
& attr
, wxTextAttr
& clashingAttr
, wxTextAttr
& absentAttr
)
12501 absentAttr
.SetFlags(absentAttr
.GetFlags() | (~attr
.GetFlags() & wxTEXT_ATTR_ALL
));
12502 absentAttr
.SetTextEffectFlags(absentAttr
.GetTextEffectFlags() | (~attr
.GetTextEffectFlags() & 0xFFFF));
12504 long forbiddenFlags
= clashingAttr
.GetFlags()|absentAttr
.GetFlags();
12506 // If different font size units are being used, this is a clash.
12507 if (((attr
.GetFlags() & wxTEXT_ATTR_FONT_SIZE
) | (currentStyle
.GetFlags() & wxTEXT_ATTR_FONT_SIZE
)) == wxTEXT_ATTR_FONT_SIZE
)
12509 currentStyle
.SetFontSize(0);
12510 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_SIZE
);
12511 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_SIZE
);
12515 if (attr
.HasFontPointSize() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_POINT_SIZE
))
12517 if (currentStyle
.HasFontPointSize())
12519 if (currentStyle
.GetFontSize() != attr
.GetFontSize())
12521 // Clash of attr - mark as such
12522 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE
);
12523 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE
);
12527 currentStyle
.SetFontSize(attr
.GetFontSize());
12529 else if (!attr
.HasFontPointSize() && currentStyle
.HasFontPointSize())
12531 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE
);
12532 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE
);
12535 if (attr
.HasFontPixelSize() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_PIXEL_SIZE
))
12537 if (currentStyle
.HasFontPixelSize())
12539 if (currentStyle
.GetFontSize() != attr
.GetFontSize())
12541 // Clash of attr - mark as such
12542 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE
);
12543 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE
);
12547 currentStyle
.SetFontPixelSize(attr
.GetFontSize());
12549 else if (!attr
.HasFontPixelSize() && currentStyle
.HasFontPixelSize())
12551 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE
);
12552 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE
);
12556 if (attr
.HasFontItalic() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_ITALIC
))
12558 if (currentStyle
.HasFontItalic())
12560 if (currentStyle
.GetFontStyle() != attr
.GetFontStyle())
12562 // Clash of attr - mark as such
12563 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_ITALIC
);
12564 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC
);
12568 currentStyle
.SetFontStyle(attr
.GetFontStyle());
12570 else if (!attr
.HasFontItalic() && currentStyle
.HasFontItalic())
12572 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_ITALIC
);
12573 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC
);
12576 if (attr
.HasFontFamily() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_FAMILY
))
12578 if (currentStyle
.HasFontFamily())
12580 if (currentStyle
.GetFontFamily() != attr
.GetFontFamily())
12582 // Clash of attr - mark as such
12583 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FAMILY
);
12584 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY
);
12588 currentStyle
.SetFontFamily(attr
.GetFontFamily());
12590 else if (!attr
.HasFontFamily() && currentStyle
.HasFontFamily())
12592 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FAMILY
);
12593 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY
);
12596 if (attr
.HasFontWeight() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_WEIGHT
))
12598 if (currentStyle
.HasFontWeight())
12600 if (currentStyle
.GetFontWeight() != attr
.GetFontWeight())
12602 // Clash of attr - mark as such
12603 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_WEIGHT
);
12604 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT
);
12608 currentStyle
.SetFontWeight(attr
.GetFontWeight());
12610 else if (!attr
.HasFontWeight() && currentStyle
.HasFontWeight())
12612 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_WEIGHT
);
12613 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT
);
12616 if (attr
.HasFontFaceName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_FACE
))
12618 if (currentStyle
.HasFontFaceName())
12620 wxString
faceName1(currentStyle
.GetFontFaceName());
12621 wxString
faceName2(attr
.GetFontFaceName());
12623 if (faceName1
!= faceName2
)
12625 // Clash of attr - mark as such
12626 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FACE
);
12627 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FACE
);
12631 currentStyle
.SetFontFaceName(attr
.GetFontFaceName());
12633 else if (!attr
.HasFontFaceName() && currentStyle
.HasFontFaceName())
12635 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FACE
);
12636 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FACE
);
12639 if (attr
.HasFontUnderlined() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_UNDERLINE
))
12641 if (currentStyle
.HasFontUnderlined())
12643 if (currentStyle
.GetFontUnderlined() != attr
.GetFontUnderlined())
12645 // Clash of attr - mark as such
12646 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
12647 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
12651 currentStyle
.SetFontUnderlined(attr
.GetFontUnderlined());
12653 else if (!attr
.HasFontUnderlined() && currentStyle
.HasFontUnderlined())
12655 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
12656 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
12659 if (attr
.HasFontStrikethrough() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_STRIKETHROUGH
))
12661 if (currentStyle
.HasFontStrikethrough())
12663 if (currentStyle
.GetFontStrikethrough() != attr
.GetFontStrikethrough())
12665 // Clash of attr - mark as such
12666 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH
);
12667 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH
);
12671 currentStyle
.SetFontStrikethrough(attr
.GetFontStrikethrough());
12673 else if (!attr
.HasFontStrikethrough() && currentStyle
.HasFontStrikethrough())
12675 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH
);
12676 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH
);
12679 if (attr
.HasTextColour() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_TEXT_COLOUR
))
12681 if (currentStyle
.HasTextColour())
12683 if (currentStyle
.GetTextColour() != attr
.GetTextColour())
12685 // Clash of attr - mark as such
12686 clashingAttr
.AddFlag(wxTEXT_ATTR_TEXT_COLOUR
);
12687 currentStyle
.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR
);
12691 currentStyle
.SetTextColour(attr
.GetTextColour());
12693 else if (!attr
.HasTextColour() && currentStyle
.HasTextColour())
12695 clashingAttr
.AddFlag(wxTEXT_ATTR_TEXT_COLOUR
);
12696 currentStyle
.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR
);
12699 if (attr
.HasBackgroundColour() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BACKGROUND_COLOUR
))
12701 if (currentStyle
.HasBackgroundColour())
12703 if (currentStyle
.GetBackgroundColour() != attr
.GetBackgroundColour())
12705 // Clash of attr - mark as such
12706 clashingAttr
.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
12707 currentStyle
.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
12711 currentStyle
.SetBackgroundColour(attr
.GetBackgroundColour());
12713 else if (!attr
.HasBackgroundColour() && currentStyle
.HasBackgroundColour())
12715 clashingAttr
.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
12716 currentStyle
.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
12719 if (attr
.HasAlignment() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_ALIGNMENT
))
12721 if (currentStyle
.HasAlignment())
12723 if (currentStyle
.GetAlignment() != attr
.GetAlignment())
12725 // Clash of attr - mark as such
12726 clashingAttr
.AddFlag(wxTEXT_ATTR_ALIGNMENT
);
12727 currentStyle
.RemoveFlag(wxTEXT_ATTR_ALIGNMENT
);
12731 currentStyle
.SetAlignment(attr
.GetAlignment());
12733 else if (!attr
.HasAlignment() && currentStyle
.HasAlignment())
12735 clashingAttr
.AddFlag(wxTEXT_ATTR_ALIGNMENT
);
12736 currentStyle
.RemoveFlag(wxTEXT_ATTR_ALIGNMENT
);
12739 if (attr
.HasTabs() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_TABS
))
12741 if (currentStyle
.HasTabs())
12743 if (!wxRichTextTabsEq(currentStyle
.GetTabs(), attr
.GetTabs()))
12745 // Clash of attr - mark as such
12746 clashingAttr
.AddFlag(wxTEXT_ATTR_TABS
);
12747 currentStyle
.RemoveFlag(wxTEXT_ATTR_TABS
);
12751 currentStyle
.SetTabs(attr
.GetTabs());
12753 else if (!attr
.HasTabs() && currentStyle
.HasTabs())
12755 clashingAttr
.AddFlag(wxTEXT_ATTR_TABS
);
12756 currentStyle
.RemoveFlag(wxTEXT_ATTR_TABS
);
12759 if (attr
.HasLeftIndent() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LEFT_INDENT
))
12761 if (currentStyle
.HasLeftIndent())
12763 if (currentStyle
.GetLeftIndent() != attr
.GetLeftIndent() || currentStyle
.GetLeftSubIndent() != attr
.GetLeftSubIndent())
12765 // Clash of attr - mark as such
12766 clashingAttr
.AddFlag(wxTEXT_ATTR_LEFT_INDENT
);
12767 currentStyle
.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT
);
12771 currentStyle
.SetLeftIndent(attr
.GetLeftIndent(), attr
.GetLeftSubIndent());
12773 else if (!attr
.HasLeftIndent() && currentStyle
.HasLeftIndent())
12775 clashingAttr
.AddFlag(wxTEXT_ATTR_LEFT_INDENT
);
12776 currentStyle
.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT
);
12779 if (attr
.HasRightIndent() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_RIGHT_INDENT
))
12781 if (currentStyle
.HasRightIndent())
12783 if (currentStyle
.GetRightIndent() != attr
.GetRightIndent())
12785 // Clash of attr - mark as such
12786 clashingAttr
.AddFlag(wxTEXT_ATTR_RIGHT_INDENT
);
12787 currentStyle
.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT
);
12791 currentStyle
.SetRightIndent(attr
.GetRightIndent());
12793 else if (!attr
.HasRightIndent() && currentStyle
.HasRightIndent())
12795 clashingAttr
.AddFlag(wxTEXT_ATTR_RIGHT_INDENT
);
12796 currentStyle
.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT
);
12799 if (attr
.HasParagraphSpacingAfter() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARA_SPACING_AFTER
))
12801 if (currentStyle
.HasParagraphSpacingAfter())
12803 if (currentStyle
.GetParagraphSpacingAfter() != attr
.GetParagraphSpacingAfter())
12805 // Clash of attr - mark as such
12806 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
12807 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
12811 currentStyle
.SetParagraphSpacingAfter(attr
.GetParagraphSpacingAfter());
12813 else if (!attr
.HasParagraphSpacingAfter() && currentStyle
.HasParagraphSpacingAfter())
12815 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
12816 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
12819 if (attr
.HasParagraphSpacingBefore() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARA_SPACING_BEFORE
))
12821 if (currentStyle
.HasParagraphSpacingBefore())
12823 if (currentStyle
.GetParagraphSpacingBefore() != attr
.GetParagraphSpacingBefore())
12825 // Clash of attr - mark as such
12826 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
12827 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
12831 currentStyle
.SetParagraphSpacingBefore(attr
.GetParagraphSpacingBefore());
12833 else if (!attr
.HasParagraphSpacingBefore() && currentStyle
.HasParagraphSpacingBefore())
12835 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
12836 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
12839 if (attr
.HasLineSpacing() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LINE_SPACING
))
12841 if (currentStyle
.HasLineSpacing())
12843 if (currentStyle
.GetLineSpacing() != attr
.GetLineSpacing())
12845 // Clash of attr - mark as such
12846 clashingAttr
.AddFlag(wxTEXT_ATTR_LINE_SPACING
);
12847 currentStyle
.RemoveFlag(wxTEXT_ATTR_LINE_SPACING
);
12851 currentStyle
.SetLineSpacing(attr
.GetLineSpacing());
12853 else if (!attr
.HasLineSpacing() && currentStyle
.HasLineSpacing())
12855 clashingAttr
.AddFlag(wxTEXT_ATTR_LINE_SPACING
);
12856 currentStyle
.RemoveFlag(wxTEXT_ATTR_LINE_SPACING
);
12859 if (attr
.HasCharacterStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_CHARACTER_STYLE_NAME
))
12861 if (currentStyle
.HasCharacterStyleName())
12863 if (currentStyle
.GetCharacterStyleName() != attr
.GetCharacterStyleName())
12865 // Clash of attr - mark as such
12866 clashingAttr
.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
12867 currentStyle
.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
12871 currentStyle
.SetCharacterStyleName(attr
.GetCharacterStyleName());
12873 else if (!attr
.HasCharacterStyleName() && currentStyle
.HasCharacterStyleName())
12875 clashingAttr
.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
12876 currentStyle
.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
12879 if (attr
.HasParagraphStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
))
12881 if (currentStyle
.HasParagraphStyleName())
12883 if (currentStyle
.GetParagraphStyleName() != attr
.GetParagraphStyleName())
12885 // Clash of attr - mark as such
12886 clashingAttr
.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
12887 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
12891 currentStyle
.SetParagraphStyleName(attr
.GetParagraphStyleName());
12893 else if (!attr
.HasParagraphStyleName() && currentStyle
.HasParagraphStyleName())
12895 clashingAttr
.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
12896 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
12899 if (attr
.HasListStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LIST_STYLE_NAME
))
12901 if (currentStyle
.HasListStyleName())
12903 if (currentStyle
.GetListStyleName() != attr
.GetListStyleName())
12905 // Clash of attr - mark as such
12906 clashingAttr
.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
12907 currentStyle
.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
12911 currentStyle
.SetListStyleName(attr
.GetListStyleName());
12913 else if (!attr
.HasListStyleName() && currentStyle
.HasListStyleName())
12915 clashingAttr
.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
12916 currentStyle
.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
12919 if (attr
.HasBulletStyle() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_STYLE
))
12921 if (currentStyle
.HasBulletStyle())
12923 if (currentStyle
.GetBulletStyle() != attr
.GetBulletStyle())
12925 // Clash of attr - mark as such
12926 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_STYLE
);
12927 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE
);
12931 currentStyle
.SetBulletStyle(attr
.GetBulletStyle());
12933 else if (!attr
.HasBulletStyle() && currentStyle
.HasBulletStyle())
12935 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_STYLE
);
12936 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE
);
12939 if (attr
.HasBulletNumber() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_NUMBER
))
12941 if (currentStyle
.HasBulletNumber())
12943 if (currentStyle
.GetBulletNumber() != attr
.GetBulletNumber())
12945 // Clash of attr - mark as such
12946 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NUMBER
);
12947 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER
);
12951 currentStyle
.SetBulletNumber(attr
.GetBulletNumber());
12953 else if (!attr
.HasBulletNumber() && currentStyle
.HasBulletNumber())
12955 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NUMBER
);
12956 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER
);
12959 if (attr
.HasBulletText() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_TEXT
))
12961 if (currentStyle
.HasBulletText())
12963 if (currentStyle
.GetBulletText() != attr
.GetBulletText())
12965 // Clash of attr - mark as such
12966 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_TEXT
);
12967 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT
);
12972 currentStyle
.SetBulletText(attr
.GetBulletText());
12973 currentStyle
.SetBulletFont(attr
.GetBulletFont());
12976 else if (!attr
.HasBulletText() && currentStyle
.HasBulletText())
12978 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_TEXT
);
12979 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT
);
12982 if (attr
.HasBulletName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_NAME
))
12984 if (currentStyle
.HasBulletName())
12986 if (currentStyle
.GetBulletName() != attr
.GetBulletName())
12988 // Clash of attr - mark as such
12989 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NAME
);
12990 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NAME
);
12995 currentStyle
.SetBulletName(attr
.GetBulletName());
12998 else if (!attr
.HasBulletName() && currentStyle
.HasBulletName())
13000 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NAME
);
13001 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NAME
);
13004 if (attr
.HasURL() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_URL
))
13006 if (currentStyle
.HasURL())
13008 if (currentStyle
.GetURL() != attr
.GetURL())
13010 // Clash of attr - mark as such
13011 clashingAttr
.AddFlag(wxTEXT_ATTR_URL
);
13012 currentStyle
.RemoveFlag(wxTEXT_ATTR_URL
);
13017 currentStyle
.SetURL(attr
.GetURL());
13020 else if (!attr
.HasURL() && currentStyle
.HasURL())
13022 clashingAttr
.AddFlag(wxTEXT_ATTR_URL
);
13023 currentStyle
.RemoveFlag(wxTEXT_ATTR_URL
);
13026 if (attr
.HasTextEffects() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_EFFECTS
))
13028 if (currentStyle
.HasTextEffects())
13030 // We need to find the bits in the new attr that are different:
13031 // just look at those bits that are specified by the new attr.
13033 // We need to remove the bits and flags that are not common between current attr
13034 // and new attr. In so doing we need to take account of the styles absent from one or more of the
13035 // previous styles.
13037 int currentRelevantTextEffects
= currentStyle
.GetTextEffects() & attr
.GetTextEffectFlags();
13038 int newRelevantTextEffects
= attr
.GetTextEffects() & attr
.GetTextEffectFlags();
13040 if (currentRelevantTextEffects
!= newRelevantTextEffects
)
13042 // Find the text effects that were different, using XOR
13043 int differentEffects
= currentRelevantTextEffects
^ newRelevantTextEffects
;
13045 // Clash of attr - mark as such
13046 clashingAttr
.SetTextEffectFlags(clashingAttr
.GetTextEffectFlags() | differentEffects
);
13047 currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~differentEffects
);
13052 currentStyle
.SetTextEffects(attr
.GetTextEffects());
13053 currentStyle
.SetTextEffectFlags(attr
.GetTextEffectFlags());
13056 // Mask out the flags and values that cannot be common because they were absent in one or more objecrs
13057 // that we've looked at so far
13058 currentStyle
.SetTextEffects(currentStyle
.GetTextEffects() & ~absentAttr
.GetTextEffectFlags());
13059 currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~absentAttr
.GetTextEffectFlags());
13061 if (currentStyle
.GetTextEffectFlags() == 0)
13062 currentStyle
.RemoveFlag(wxTEXT_ATTR_EFFECTS
);
13064 else if (!attr
.HasTextEffects() && currentStyle
.HasTextEffects())
13066 clashingAttr
.AddFlag(wxTEXT_ATTR_EFFECTS
);
13067 currentStyle
.RemoveFlag(wxTEXT_ATTR_EFFECTS
);
13070 if (attr
.HasOutlineLevel() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_OUTLINE_LEVEL
))
13072 if (currentStyle
.HasOutlineLevel())
13074 if (currentStyle
.GetOutlineLevel() != attr
.GetOutlineLevel())
13076 // Clash of attr - mark as such
13077 clashingAttr
.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
13078 currentStyle
.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
13082 currentStyle
.SetOutlineLevel(attr
.GetOutlineLevel());
13084 else if (!attr
.HasOutlineLevel() && currentStyle
.HasOutlineLevel())
13086 clashingAttr
.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
13087 currentStyle
.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
13091 WX_DEFINE_OBJARRAY(wxRichTextVariantArray
);
13093 IMPLEMENT_DYNAMIC_CLASS(wxRichTextProperties
, wxObject
)
13095 bool wxRichTextProperties::operator==(const wxRichTextProperties
& props
) const
13097 if (m_properties
.GetCount() != props
.GetCount())
13101 for (i
= 0; i
< m_properties
.GetCount(); i
++)
13103 const wxVariant
& var1
= m_properties
[i
];
13104 int idx
= props
.Find(var1
.GetName());
13107 const wxVariant
& var2
= props
.m_properties
[idx
];
13108 if (!(var1
== var2
))
13115 wxArrayString
wxRichTextProperties::GetPropertyNames() const
13119 for (i
= 0; i
< m_properties
.GetCount(); i
++)
13121 arr
.Add(m_properties
[i
].GetName());
13126 int wxRichTextProperties::Find(const wxString
& name
) const
13129 for (i
= 0; i
< m_properties
.GetCount(); i
++)
13131 if (m_properties
[i
].GetName() == name
)
13137 bool wxRichTextProperties::Remove(const wxString
& name
)
13139 int idx
= Find(name
);
13142 m_properties
.RemoveAt(idx
);
13149 wxVariant
* wxRichTextProperties::FindOrCreateProperty(const wxString
& name
)
13151 int idx
= Find(name
);
13152 if (idx
== wxNOT_FOUND
)
13153 SetProperty(name
, wxString());
13155 if (idx
!= wxNOT_FOUND
)
13157 return & (*this)[idx
];
13163 const wxVariant
& wxRichTextProperties::GetProperty(const wxString
& name
) const
13165 static const wxVariant nullVariant
;
13166 int idx
= Find(name
);
13168 return m_properties
[idx
];
13170 return nullVariant
;
13173 wxString
wxRichTextProperties::GetPropertyString(const wxString
& name
) const
13175 return GetProperty(name
).GetString();
13178 long wxRichTextProperties::GetPropertyLong(const wxString
& name
) const
13180 return GetProperty(name
).GetLong();
13183 bool wxRichTextProperties::GetPropertyBool(const wxString
& name
) const
13185 return GetProperty(name
).GetBool();
13188 double wxRichTextProperties::GetPropertyDouble(const wxString
& name
) const
13190 return GetProperty(name
).GetDouble();
13193 void wxRichTextProperties::SetProperty(const wxVariant
& variant
)
13195 wxASSERT(!variant
.GetName().IsEmpty());
13197 int idx
= Find(variant
.GetName());
13200 m_properties
.Add(variant
);
13202 m_properties
[idx
] = variant
;
13205 void wxRichTextProperties::SetProperty(const wxString
& name
, const wxVariant
& variant
)
13207 int idx
= Find(name
);
13208 wxVariant
var(variant
);
13212 m_properties
.Add(var
);
13214 m_properties
[idx
] = var
;
13217 void wxRichTextProperties::SetProperty(const wxString
& name
, const wxString
& value
)
13219 SetProperty(name
, wxVariant(value
, name
));
13222 void wxRichTextProperties::SetProperty(const wxString
& name
, long value
)
13224 SetProperty(name
, wxVariant(value
, name
));
13227 void wxRichTextProperties::SetProperty(const wxString
& name
, double value
)
13229 SetProperty(name
, wxVariant(value
, name
));
13232 void wxRichTextProperties::SetProperty(const wxString
& name
, bool value
)
13234 SetProperty(name
, wxVariant(value
, name
));
13237 void wxRichTextProperties::RemoveProperties(const wxRichTextProperties
& properties
)
13240 for (i
= 0; i
< properties
.GetCount(); i
++)
13242 wxString name
= properties
.GetProperties()[i
].GetName();
13243 if (HasProperty(name
))
13248 void wxRichTextProperties::MergeProperties(const wxRichTextProperties
& properties
)
13251 for (i
= 0; i
< properties
.GetCount(); i
++)
13253 SetProperty(properties
.GetProperties()[i
]);
13257 wxRichTextObject
* wxRichTextObjectAddress::GetObject(wxRichTextParagraphLayoutBox
* topLevelContainer
) const
13259 if (m_address
.GetCount() == 0)
13260 return topLevelContainer
;
13262 wxRichTextCompositeObject
* p
= topLevelContainer
;
13264 while (p
&& i
< m_address
.GetCount())
13266 int pos
= m_address
[i
];
13267 wxASSERT(pos
>= 0 && pos
< (int) p
->GetChildren().GetCount());
13268 if (pos
< 0 || pos
>= (int) p
->GetChildren().GetCount())
13271 wxRichTextObject
* p1
= p
->GetChild(pos
);
13272 if (i
== (m_address
.GetCount()-1))
13275 p
= wxDynamicCast(p1
, wxRichTextCompositeObject
);
13281 bool wxRichTextObjectAddress::Create(wxRichTextParagraphLayoutBox
* topLevelContainer
, wxRichTextObject
* obj
)
13285 if (topLevelContainer
== obj
)
13288 wxRichTextObject
* o
= obj
;
13291 wxRichTextCompositeObject
* p
= wxDynamicCast(o
->GetParent(), wxRichTextCompositeObject
);
13295 int pos
= p
->GetChildren().IndexOf(o
);
13299 m_address
.Insert(pos
, 0);
13301 if (p
== topLevelContainer
)
13310 bool wxRichTextSelection::operator==(const wxRichTextSelection
& sel
) const
13312 if (m_container
!= sel
.m_container
)
13314 if (m_ranges
.GetCount() != sel
.m_ranges
.GetCount())
13317 for (i
= 0; i
< m_ranges
.GetCount(); i
++)
13318 if (!(m_ranges
[i
] == sel
.m_ranges
[i
]))
13323 // Get the selections appropriate to the specified object, if any; returns wxRICHTEXT_NO_SELECTION if none
13324 // or none at the level of the object's container.
13325 wxRichTextRangeArray
wxRichTextSelection::GetSelectionForObject(wxRichTextObject
* obj
) const
13329 wxRichTextParagraphLayoutBox
* container
= obj
->GetParentContainer();
13331 if (container
== m_container
)
13334 container
= obj
->GetContainer();
13337 if (container
->GetParent())
13339 // If we found that our object's container is within the range of
13340 // a selection higher up, then assume the whole original object
13341 // is also selected.
13342 wxRichTextParagraphLayoutBox
* parentContainer
= container
->GetParentContainer();
13343 if (parentContainer
== m_container
)
13345 if (WithinSelection(container
->GetRange().GetStart(), m_ranges
))
13347 wxRichTextRangeArray ranges
;
13348 ranges
.Add(obj
->GetRange());
13353 container
= parentContainer
;
13362 return wxRichTextRangeArray();
13365 // Is the given position within the selection?
13366 bool wxRichTextSelection::WithinSelection(long pos
, wxRichTextObject
* obj
) const
13372 wxRichTextRangeArray selectionRanges
= GetSelectionForObject(obj
);
13373 return WithinSelection(pos
, selectionRanges
);
13377 // Is the given position within the selection range?
13378 bool wxRichTextSelection::WithinSelection(long pos
, const wxRichTextRangeArray
& ranges
)
13381 for (i
= 0; i
< ranges
.GetCount(); i
++)
13383 const wxRichTextRange
& range
= ranges
[i
];
13384 if (pos
>= range
.GetStart() && pos
<= range
.GetEnd())
13390 // Is the given range completely within the selection range?
13391 bool wxRichTextSelection::WithinSelection(const wxRichTextRange
& range
, const wxRichTextRangeArray
& ranges
)
13394 for (i
= 0; i
< ranges
.GetCount(); i
++)
13396 const wxRichTextRange
& eachRange
= ranges
[i
];
13397 if (range
.IsWithin(eachRange
))
13403 IMPLEMENT_CLASS(wxRichTextDrawingHandler
, wxObject
)
13404 IMPLEMENT_CLASS(wxRichTextDrawingContext
, wxObject
)
13406 bool wxRichTextDrawingContext::HasVirtualAttributes(wxRichTextObject
* obj
) const
13408 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
13411 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13412 if (handler
->HasVirtualAttributes(obj
))
13415 node
= node
->GetNext();
13420 wxRichTextAttr
wxRichTextDrawingContext::GetVirtualAttributes(wxRichTextObject
* obj
) const
13422 wxRichTextAttr attr
;
13423 // We apply all handlers, so we can may combine several different attributes
13424 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
13427 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13428 if (handler
->HasVirtualAttributes(obj
))
13430 bool success
= handler
->GetVirtualAttributes(attr
, obj
);
13432 wxUnusedVar(success
);
13435 node
= node
->GetNext();
13440 bool wxRichTextDrawingContext::ApplyVirtualAttributes(wxRichTextAttr
& attr
, wxRichTextObject
* obj
) const
13442 if (HasVirtualAttributes(obj
))
13444 wxRichTextAttr
a(GetVirtualAttributes(obj
));
13452 /// Adds a handler to the end
13453 void wxRichTextBuffer::AddDrawingHandler(wxRichTextDrawingHandler
*handler
)
13455 sm_drawingHandlers
.Append(handler
);
13458 /// Inserts a handler at the front
13459 void wxRichTextBuffer::InsertDrawingHandler(wxRichTextDrawingHandler
*handler
)
13461 sm_drawingHandlers
.Insert( handler
);
13464 /// Removes a handler
13465 bool wxRichTextBuffer::RemoveDrawingHandler(const wxString
& name
)
13467 wxRichTextDrawingHandler
*handler
= FindDrawingHandler(name
);
13470 sm_drawingHandlers
.DeleteObject(handler
);
13478 wxRichTextDrawingHandler
* wxRichTextBuffer::FindDrawingHandler(const wxString
& name
)
13480 wxList::compatibility_iterator node
= sm_drawingHandlers
.GetFirst();
13483 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13484 if (handler
->GetName().Lower() == name
.Lower()) return handler
;
13486 node
= node
->GetNext();
13491 void wxRichTextBuffer::CleanUpDrawingHandlers()
13493 wxList::compatibility_iterator node
= sm_drawingHandlers
.GetFirst();
13496 wxRichTextDrawingHandler
* handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13497 wxList::compatibility_iterator next
= node
->GetNext();
13502 sm_drawingHandlers
.Clear();
13505 void wxRichTextBuffer::AddFieldType(wxRichTextFieldType
*fieldType
)
13507 sm_fieldTypes
[fieldType
->GetName()] = fieldType
;
13510 bool wxRichTextBuffer::RemoveFieldType(const wxString
& name
)
13512 wxRichTextFieldTypeHashMap::iterator it
= sm_fieldTypes
.find(name
);
13513 if (it
== sm_fieldTypes
.end())
13517 wxRichTextFieldType
* fieldType
= it
->second
;
13518 sm_fieldTypes
.erase(it
);
13524 wxRichTextFieldType
*wxRichTextBuffer::FindFieldType(const wxString
& name
)
13526 wxRichTextFieldTypeHashMap::iterator it
= sm_fieldTypes
.find(name
);
13527 if (it
== sm_fieldTypes
.end())
13533 void wxRichTextBuffer::CleanUpFieldTypes()
13535 wxRichTextFieldTypeHashMap::iterator it
;
13536 for( it
= sm_fieldTypes
.begin(); it
!= sm_fieldTypes
.end(); ++it
)
13538 wxRichTextFieldType
* fieldType
= it
->second
;
13542 sm_fieldTypes
.clear();