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.
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 if (attr
.HasAlignment() && !GetContainer()->GetFloatCollector()->HasFloats()) // FIXME: aligning whole paragraph not compatible with floating objects
1081 // centering, right-justification
1082 if (attr
.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE
)
1084 availableChildRect
.x
= (originalAvailableRect
.GetWidth() - availableChildRect
.GetWidth())/2 + availableChildRect
.x
;
1086 else if (attr
.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT
)
1088 availableChildRect
.x
= availableChildRect
.x
+ originalAvailableRect
.GetWidth() - availableChildRect
.GetWidth();
1092 Layout(dc
, context
, availableChildRect
, availableContainerSpace
, style
);
1106 // Move the object recursively, by adding the offset from old to new
1107 void wxRichTextObject::Move(const wxPoint
& pt
)
1114 * wxRichTextCompositeObject
1115 * This is the base for drawable objects.
1118 IMPLEMENT_CLASS(wxRichTextCompositeObject
, wxRichTextObject
)
1120 wxRichTextCompositeObject::wxRichTextCompositeObject(wxRichTextObject
* parent
):
1121 wxRichTextObject(parent
)
1125 wxRichTextCompositeObject::~wxRichTextCompositeObject()
1130 /// Get the nth child
1131 wxRichTextObject
* wxRichTextCompositeObject::GetChild(size_t n
) const
1133 wxASSERT ( n
< m_children
.GetCount() );
1135 return m_children
.Item(n
)->GetData();
1138 /// Append a child, returning the position
1139 size_t wxRichTextCompositeObject::AppendChild(wxRichTextObject
* child
)
1141 m_children
.Append(child
);
1142 child
->SetParent(this);
1143 return m_children
.GetCount() - 1;
1146 /// Insert the child in front of the given object, or at the beginning
1147 bool wxRichTextCompositeObject::InsertChild(wxRichTextObject
* child
, wxRichTextObject
* inFrontOf
)
1151 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(inFrontOf
);
1152 m_children
.Insert(node
, child
);
1155 m_children
.Insert(child
);
1156 child
->SetParent(this);
1161 /// Delete the child
1162 bool wxRichTextCompositeObject::RemoveChild(wxRichTextObject
* child
, bool deleteChild
)
1164 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(child
);
1167 wxRichTextObject
* obj
= node
->GetData();
1168 m_children
.Erase(node
);
1177 /// Delete all children
1178 bool wxRichTextCompositeObject::DeleteChildren()
1180 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1183 wxRichTextObjectList::compatibility_iterator oldNode
= node
;
1185 wxRichTextObject
* child
= node
->GetData();
1186 child
->Dereference(); // Only delete if reference count is zero
1188 node
= node
->GetNext();
1189 m_children
.Erase(oldNode
);
1195 /// Get the child count
1196 size_t wxRichTextCompositeObject::GetChildCount() const
1198 return m_children
.GetCount();
1202 void wxRichTextCompositeObject::Copy(const wxRichTextCompositeObject
& obj
)
1204 wxRichTextObject::Copy(obj
);
1208 wxRichTextObjectList::compatibility_iterator node
= obj
.m_children
.GetFirst();
1211 wxRichTextObject
* child
= node
->GetData();
1212 wxRichTextObject
* newChild
= child
->Clone();
1213 newChild
->SetParent(this);
1214 m_children
.Append(newChild
);
1216 node
= node
->GetNext();
1220 /// Hit-testing: returns a flag indicating hit test details, plus
1221 /// information about position
1222 int wxRichTextCompositeObject::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
1225 return wxRICHTEXT_HITTEST_NONE
;
1227 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1230 wxRichTextObject
* child
= node
->GetData();
1232 if (child
->IsShown() && child
->IsTopLevel() && (flags
& wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS
))
1234 // Just check if we hit the overall object
1235 int ret
= child
->wxRichTextObject::HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
1236 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
1239 else if (child
->IsShown())
1241 int ret
= child
->HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
1242 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
1246 node
= node
->GetNext();
1249 return wxRICHTEXT_HITTEST_NONE
;
1252 /// Finds the absolute position and row height for the given character position
1253 bool wxRichTextCompositeObject::FindPosition(wxDC
& dc
, wxRichTextDrawingContext
& context
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
1255 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1258 wxRichTextObject
* child
= node
->GetData();
1260 // Don't recurse if the child is a top-level object,
1261 // such as a text box, because the character position will no longer
1262 // apply. By definition, a top-level object has its own range of
1263 // character positions.
1264 if (!child
->IsTopLevel() && child
->FindPosition(dc
, context
, index
, pt
, height
, forceLineStart
))
1267 node
= node
->GetNext();
1274 void wxRichTextCompositeObject::CalculateRange(long start
, long& end
)
1276 long current
= start
;
1277 long lastEnd
= current
;
1285 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1288 wxRichTextObject
* child
= node
->GetData();
1291 child
->CalculateRange(current
, childEnd
);
1294 current
= childEnd
+ 1;
1296 node
= node
->GetNext();
1301 // A top-level object always has a range of size 1,
1302 // because its children don't count at this level.
1304 m_range
.SetRange(start
, start
);
1306 // An object with no children has zero length
1307 if (m_children
.GetCount() == 0)
1309 m_ownRange
.SetRange(0, lastEnd
);
1315 // An object with no children has zero length
1316 if (m_children
.GetCount() == 0)
1319 m_range
.SetRange(start
, end
);
1323 /// Delete range from layout.
1324 bool wxRichTextCompositeObject::DeleteRange(const wxRichTextRange
& range
)
1326 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1330 wxRichTextObject
* obj
= (wxRichTextObject
*) node
->GetData();
1331 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
1333 // Delete the range in each paragraph
1335 // When a chunk has been deleted, internally the content does not
1336 // now match the ranges.
1337 // However, so long as deletion is not done on the same object twice this is OK.
1338 // If you may delete content from the same object twice, recalculate
1339 // the ranges inbetween DeleteRange calls by calling CalculateRanges, and
1340 // adjust the range you're deleting accordingly.
1342 if (!obj
->GetRange().IsOutside(range
))
1344 // No need to delete within a top-level object; just removing this object will do fine
1345 if (!obj
->IsTopLevel())
1346 obj
->DeleteRange(range
);
1348 // Delete an empty object, or paragraph within this range.
1349 if (obj
->IsEmpty() ||
1350 (range
.GetStart() <= obj
->GetRange().GetStart() && range
.GetEnd() >= obj
->GetRange().GetEnd()))
1352 // An empty paragraph has length 1, so won't be deleted unless the
1353 // whole range is deleted.
1354 RemoveChild(obj
, true);
1364 /// Get any text in this object for the given range
1365 wxString
wxRichTextCompositeObject::GetTextForRange(const wxRichTextRange
& range
) const
1368 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1371 wxRichTextObject
* child
= node
->GetData();
1372 wxRichTextRange childRange
= range
;
1373 if (!child
->GetRange().IsOutside(range
))
1375 childRange
.LimitTo(child
->GetRange());
1377 wxString childText
= child
->GetTextForRange(childRange
);
1381 node
= node
->GetNext();
1387 /// Get the child object at the given character position
1388 wxRichTextObject
* wxRichTextCompositeObject::GetChildAtPosition(long pos
) const
1390 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1393 wxRichTextObject
* child
= node
->GetData();
1394 if (child
->GetRange().GetStart() == pos
)
1396 node
= node
->GetNext();
1401 /// Recursively merge all pieces that can be merged.
1402 bool wxRichTextCompositeObject::Defragment(const wxRichTextRange
& range
)
1404 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1407 wxRichTextObject
* child
= node
->GetData();
1408 if (range
== wxRICHTEXT_ALL
|| !child
->GetRange().IsOutside(range
))
1410 wxRichTextCompositeObject
* composite
= wxDynamicCast(child
, wxRichTextCompositeObject
);
1412 composite
->Defragment();
1414 if (node
->GetNext())
1416 wxRichTextObject
* nextChild
= node
->GetNext()->GetData();
1417 if (child
->CanMerge(nextChild
) && child
->Merge(nextChild
))
1419 nextChild
->Dereference();
1420 m_children
.Erase(node
->GetNext());
1422 // Don't set node -- we'll see if we can merge again with the next
1426 node
= node
->GetNext();
1429 node
= node
->GetNext();
1432 node
= node
->GetNext();
1435 // Delete any remaining empty objects, but leave at least one empty object per composite object.
1436 if (GetChildCount() > 1)
1438 node
= m_children
.GetFirst();
1441 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
1442 wxRichTextObject
* child
= node
->GetData();
1443 if (range
== wxRICHTEXT_ALL
|| !child
->GetRange().IsOutside(range
))
1445 if (child
->IsEmpty())
1447 child
->Dereference();
1448 m_children
.Erase(node
);
1453 node
= node
->GetNext();
1460 /// Dump to output stream for debugging
1461 void wxRichTextCompositeObject::Dump(wxTextOutputStream
& stream
)
1463 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1466 wxRichTextObject
* child
= node
->GetData();
1467 child
->Dump(stream
);
1468 node
= node
->GetNext();
1472 /// Get/set the object size for the given range. Returns false if the range
1473 /// is invalid for this object.
1474 bool wxRichTextCompositeObject::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
1476 if (!range
.IsWithin(GetRange()))
1481 wxArrayInt childExtents
;
1488 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1491 wxRichTextObject
* child
= node
->GetData();
1492 if (!child
->GetRange().IsOutside(range
))
1494 // Floating objects have a zero size within the paragraph.
1495 if (child
->IsFloating())
1500 if (partialExtents
->GetCount() > 0)
1501 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
1505 partialExtents
->Add(0 /* zero size */ + lastSize
);
1512 wxRichTextRange rangeToUse
= range
;
1513 rangeToUse
.LimitTo(child
->GetRange());
1514 if (child
->IsTopLevel())
1515 rangeToUse
= child
->GetOwnRange();
1517 int childDescent
= 0;
1519 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we're already cached the size,
1520 // but it's only going to be used after caching has taken place.
1521 if ((flags
& wxRICHTEXT_HEIGHT_ONLY
) && child
->GetCachedSize().y
!= 0)
1523 childDescent
= child
->GetDescent();
1524 childSize
= child
->GetCachedSize();
1526 sz
.y
= wxMax(sz
.y
, childSize
.y
);
1527 sz
.x
+= childSize
.x
;
1528 descent
= wxMax(descent
, childDescent
);
1530 else if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, context
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
), p
))
1532 sz
.y
= wxMax(sz
.y
, childSize
.y
);
1533 sz
.x
+= childSize
.x
;
1534 descent
= wxMax(descent
, childDescent
);
1536 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange() || child
->IsTopLevel()))
1538 child
->SetCachedSize(childSize
);
1539 child
->SetDescent(childDescent
);
1545 if (partialExtents
->GetCount() > 0)
1546 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
1551 for (i
= 0; i
< childExtents
.GetCount(); i
++)
1553 partialExtents
->Add(childExtents
[i
] + lastSize
);
1563 node
= node
->GetNext();
1569 // Invalidate the buffer. With no argument, invalidates whole buffer.
1570 void wxRichTextCompositeObject::Invalidate(const wxRichTextRange
& invalidRange
)
1572 wxRichTextObject::Invalidate(invalidRange
);
1574 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1577 wxRichTextObject
* child
= node
->GetData();
1578 if (invalidRange
!= wxRICHTEXT_ALL
&& invalidRange
!= wxRICHTEXT_NONE
&& child
->GetRange().IsOutside(invalidRange
))
1582 else if (child
->IsTopLevel())
1584 if (invalidRange
== wxRICHTEXT_NONE
)
1585 child
->Invalidate(wxRICHTEXT_NONE
);
1587 child
->Invalidate(wxRICHTEXT_ALL
); // All children must be invalidated if within parent range
1590 child
->Invalidate(invalidRange
);
1591 node
= node
->GetNext();
1595 // Move the object recursively, by adding the offset from old to new
1596 void wxRichTextCompositeObject::Move(const wxPoint
& pt
)
1598 wxPoint oldPos
= GetPosition();
1600 wxPoint offset
= pt
- oldPos
;
1602 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1605 wxRichTextObject
* child
= node
->GetData();
1606 wxPoint childPos
= child
->GetPosition() + offset
;
1607 child
->Move(childPos
);
1608 node
= node
->GetNext();
1614 * wxRichTextParagraphLayoutBox
1615 * This box knows how to lay out paragraphs.
1618 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraphLayoutBox
, wxRichTextCompositeObject
)
1620 wxRichTextParagraphLayoutBox::wxRichTextParagraphLayoutBox(wxRichTextObject
* parent
):
1621 wxRichTextCompositeObject(parent
)
1626 wxRichTextParagraphLayoutBox::~wxRichTextParagraphLayoutBox()
1628 if (m_floatCollector
)
1630 delete m_floatCollector
;
1631 m_floatCollector
= NULL
;
1635 /// Initialize the object.
1636 void wxRichTextParagraphLayoutBox::Init()
1640 // For now, assume is the only box and has no initial size.
1641 m_range
= wxRichTextRange(0, -1);
1642 m_ownRange
= wxRichTextRange(0, -1);
1644 m_invalidRange
= wxRICHTEXT_ALL
;
1646 m_partialParagraph
= false;
1647 m_floatCollector
= NULL
;
1650 void wxRichTextParagraphLayoutBox::Clear()
1654 if (m_floatCollector
)
1655 delete m_floatCollector
;
1656 m_floatCollector
= NULL
;
1657 m_partialParagraph
= false;
1661 void wxRichTextParagraphLayoutBox::Copy(const wxRichTextParagraphLayoutBox
& obj
)
1665 wxRichTextCompositeObject::Copy(obj
);
1667 m_partialParagraph
= obj
.m_partialParagraph
;
1668 m_defaultAttributes
= obj
.m_defaultAttributes
;
1671 // Gather information about floating objects; only gather floats for those paragraphs that
1672 // will not be formatted again due to optimization, after which floats will be gathered per-paragraph
1674 bool wxRichTextParagraphLayoutBox::UpdateFloatingObjects(const wxRect
& availableRect
, wxRichTextObject
* untilObj
)
1676 if (m_floatCollector
!= NULL
)
1677 delete m_floatCollector
;
1678 m_floatCollector
= new wxRichTextFloatCollector(availableRect
);
1679 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1680 // Only gather floats up to the point we'll start formatting paragraphs.
1681 while (untilObj
&& node
&& node
->GetData() != untilObj
)
1683 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
1684 wxASSERT (child
!= NULL
);
1686 m_floatCollector
->CollectFloat(child
);
1687 node
= node
->GetNext();
1693 // Returns the style sheet associated with the overall buffer.
1694 wxRichTextStyleSheet
* wxRichTextParagraphLayoutBox::GetStyleSheet() const
1696 return GetBuffer() ? GetBuffer()->GetStyleSheet() : (wxRichTextStyleSheet
*) NULL
;
1699 // Get the number of floating objects at this level
1700 int wxRichTextParagraphLayoutBox::GetFloatingObjectCount() const
1702 if (m_floatCollector
)
1703 return m_floatCollector
->GetFloatingObjectCount();
1708 // Get a list of floating objects
1709 bool wxRichTextParagraphLayoutBox::GetFloatingObjects(wxRichTextObjectList
& objects
) const
1711 if (m_floatCollector
)
1713 return m_floatCollector
->GetFloatingObjects(objects
);
1720 void wxRichTextParagraphLayoutBox::UpdateRanges()
1724 start
= GetRange().GetStart();
1726 CalculateRange(start
, end
);
1730 int wxRichTextParagraphLayoutBox::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
1733 return wxRICHTEXT_HITTEST_NONE
;
1735 int ret
= wxRICHTEXT_HITTEST_NONE
;
1736 if (m_floatCollector
&& (flags
& wxRICHTEXT_HITTEST_NO_FLOATING_OBJECTS
) == 0)
1737 ret
= m_floatCollector
->HitTest(dc
, context
, pt
, textPosition
, obj
, flags
);
1739 if (ret
== wxRICHTEXT_HITTEST_NONE
)
1740 return wxRichTextCompositeObject::HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
1748 /// Draw the floating objects
1749 void wxRichTextParagraphLayoutBox::DrawFloats(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
1751 if (m_floatCollector
)
1752 m_floatCollector
->Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
1755 void wxRichTextParagraphLayoutBox::MoveAnchoredObjectToParagraph(wxRichTextParagraph
* from
, wxRichTextParagraph
* to
, wxRichTextObject
* obj
)
1760 from
->RemoveChild(obj
);
1761 to
->AppendChild(obj
);
1765 bool wxRichTextParagraphLayoutBox::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
1770 wxRect
thisRect(GetPosition(), GetCachedSize());
1772 wxRichTextAttr
attr(GetAttributes());
1773 context
.ApplyVirtualAttributes(attr
, this);
1776 if (selection
.IsValid() && GetParentContainer() != this && selection
.WithinSelection(GetRange().GetStart(), GetParentContainer()))
1777 flags
|= wxRICHTEXT_DRAW_SELECTED
;
1779 // Don't draw guidelines if at top level
1780 int theseFlags
= flags
;
1782 theseFlags
&= ~wxRICHTEXT_DRAW_GUIDELINES
;
1783 DrawBoxAttributes(dc
, GetBuffer(), attr
, thisRect
, theseFlags
);
1785 DrawFloats(dc
, context
, range
, selection
, rect
, descent
, style
);
1786 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1789 wxRichTextObject
* child
= node
->GetData();
1791 if (child
&& !child
->GetRange().IsOutside(range
))
1793 wxRect
childRect(child
->GetPosition(), child
->GetCachedSize());
1794 wxRichTextRange childRange
= range
;
1795 if (child
->IsTopLevel())
1797 childRange
= child
->GetOwnRange();
1800 if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) == 0) && childRect
.GetTop() > rect
.GetBottom())
1805 else if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) == 0) && childRect
.GetBottom() < rect
.GetTop())
1810 child
->Draw(dc
, context
, childRange
, selection
, rect
, descent
, style
);
1813 node
= node
->GetNext();
1818 /// Lay the item out
1819 bool wxRichTextParagraphLayoutBox::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
)
1821 SetPosition(rect
.GetPosition());
1826 wxRect availableSpace
;
1827 bool formatRect
= (style
& wxRICHTEXT_LAYOUT_SPECIFIED_RECT
) == wxRICHTEXT_LAYOUT_SPECIFIED_RECT
;
1829 wxRichTextAttr
attr(GetAttributes());
1830 context
.ApplyVirtualAttributes(attr
, this);
1832 // If only laying out a specific area, the passed rect has a different meaning:
1833 // the visible part of the buffer. This is used in wxRichTextCtrl::OnSize,
1834 // so that during a size, only the visible part will be relaid out, or
1835 // it would take too long causing flicker. As an approximation, we assume that
1836 // everything up to the start of the visible area is laid out correctly.
1839 wxRect
rect2(0, 0, rect
.width
, rect
.height
);
1840 availableSpace
= GetAvailableContentArea(dc
, context
, rect2
);
1842 // Invalidate the part of the buffer from the first visible line
1843 // to the end. If other parts of the buffer are currently invalid,
1844 // then they too will be taken into account if they are above
1845 // the visible point.
1847 wxRichTextLine
* line
= GetLineAtYPosition(rect
.y
);
1849 startPos
= line
->GetAbsoluteRange().GetStart();
1851 Invalidate(wxRichTextRange(startPos
, GetOwnRange().GetEnd()));
1855 availableSpace
= GetAvailableContentArea(dc
, context
, rect
);
1858 // Fix the width if we're at the top level
1860 attr
.GetTextBoxAttr().GetWidth().SetValue(rect
.GetWidth(), wxTEXT_ATTR_UNITS_PIXELS
);
1862 int leftMargin
, rightMargin
, topMargin
, bottomMargin
;
1863 wxRichTextObject::GetTotalMargin(dc
, GetBuffer(), attr
, leftMargin
, rightMargin
,
1864 topMargin
, bottomMargin
);
1869 // The maximum paragraph maximum width, so we can set the overall maximum width for this object
1870 int maxMaxWidth
= 0;
1872 // The maximum paragraph minimum width, so we can set the overall minimum width for this object
1873 int maxMinWidth
= 0;
1875 // If we have vertical alignment, we must recalculate everything.
1876 bool hasVerticalAlignment
= (attr
.GetTextBoxAttr().HasVerticalAlignment() &&
1877 (attr
.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP
));
1879 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1881 bool layoutAll
= true;
1883 // Get invalid range, rounding to paragraph start/end.
1884 wxRichTextRange invalidRange
= GetInvalidRange(true);
1886 if (invalidRange
== wxRICHTEXT_NONE
&& !formatRect
)
1889 if (invalidRange
== wxRICHTEXT_ALL
|| hasVerticalAlignment
)
1891 else // If we know what range is affected, start laying out from that point on.
1892 if (invalidRange
.GetStart() >= GetOwnRange().GetStart())
1894 wxRichTextParagraph
* firstParagraph
= GetParagraphAtPosition(invalidRange
.GetStart());
1897 wxRichTextObjectList::compatibility_iterator firstNode
= m_children
.Find(firstParagraph
);
1898 wxRichTextObjectList::compatibility_iterator previousNode
;
1900 previousNode
= firstNode
->GetPrevious();
1905 wxRichTextParagraph
* previousParagraph
= wxDynamicCast(previousNode
->GetData(), wxRichTextParagraph
);
1906 availableSpace
.y
= previousParagraph
->GetPosition().y
+ previousParagraph
->GetCachedSize().y
;
1909 // Now we're going to start iterating from the first affected paragraph.
1917 // Gather information about only those floating objects that will not be formatted,
1918 // after which floats will be gathered per-paragraph during layout.
1919 UpdateFloatingObjects(availableSpace
, node
? node
->GetData() : (wxRichTextObject
*) NULL
);
1921 // A way to force speedy rest-of-buffer layout (the 'else' below)
1922 bool forceQuickLayout
= false;
1924 // First get the size of the paragraphs we won't be laying out
1925 wxRichTextObjectList::compatibility_iterator n
= m_children
.GetFirst();
1926 while (n
&& n
!= node
)
1928 wxRichTextParagraph
* child
= wxDynamicCast(n
->GetData(), wxRichTextParagraph
);
1931 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
1932 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
1933 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
1940 // Assume this box only contains paragraphs
1942 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
1943 // Unsure if this is needed
1944 // wxCHECK_MSG( child, false, wxT("Unknown object in layout") );
1946 if (child
&& child
->IsShown())
1948 // TODO: what if the child hasn't been laid out (e.g. involved in Undo) but still has 'old' lines
1949 if ( !forceQuickLayout
&&
1951 child
->GetLines().IsEmpty() ||
1952 !child
->GetRange().IsOutside(invalidRange
)) )
1954 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1955 // lays out the object again using the minimum size
1956 child
->LayoutToBestSize(dc
, context
, GetBuffer(),
1957 attr
, child
->GetAttributes(), availableSpace
, rect
, style
&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT
);
1959 // Layout must set the cached size
1960 availableSpace
.y
+= child
->GetCachedSize().y
;
1961 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
1962 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
1963 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
1965 // If we're just formatting the visible part of the buffer,
1966 // and we're now past the bottom of the window, and we don't have any
1967 // floating objects (since they may cause wrapping to change for the rest of the
1968 // the buffer), start quick layout.
1969 if (!hasVerticalAlignment
&& formatRect
&& child
->GetPosition().y
> rect
.GetBottom() && GetFloatingObjectCount() == 0)
1970 forceQuickLayout
= true;
1974 // We're outside the immediately affected range, so now let's just
1975 // move everything up or down. This assumes that all the children have previously
1976 // been laid out and have wrapped line lists associated with them.
1977 // TODO: check all paragraphs before the affected range.
1979 int inc
= availableSpace
.y
- child
->GetPosition().y
;
1983 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
1986 if (child
->GetLines().GetCount() == 0)
1988 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1989 // lays out the object again using the minimum size
1990 child
->LayoutToBestSize(dc
, context
, GetBuffer(),
1991 attr
, child
->GetAttributes(), availableSpace
, rect
, style
&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT
);
1993 //child->Layout(dc, availableChildRect, style);
1996 child
->Move(wxPoint(child
->GetPosition().x
, child
->GetPosition().y
+ inc
));
1998 availableSpace
.y
+= child
->GetCachedSize().y
;
1999 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
2000 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
2001 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
2004 node
= node
->GetNext();
2010 node
= node
->GetNext();
2013 node
= m_children
.GetLast();
2014 if (node
&& node
->GetData()->IsShown())
2016 wxRichTextObject
* child
= node
->GetData();
2017 maxHeight
= child
->GetPosition().y
- (GetPosition().y
+ topMargin
) + child
->GetCachedSize().y
;
2020 maxHeight
= 0; // topMargin + bottomMargin;
2022 // Check the bottom edge of any floating object
2023 if (GetFloatCollector() && GetFloatCollector()->HasFloats())
2025 int bottom
= GetFloatCollector()->GetLastRectBottom();
2026 if (bottom
> maxHeight
)
2030 if (attr
.GetTextBoxAttr().GetSize().GetWidth().IsValid())
2032 wxRect r
= AdjustAvailableSpace(dc
, GetBuffer(), wxRichTextAttr() /* not used */, attr
, parentRect
, parentRect
);
2033 int w
= r
.GetWidth();
2035 // Convert external to content rect
2036 w
= w
- leftMargin
- rightMargin
;
2037 maxWidth
= wxMax(maxWidth
, w
);
2038 maxMaxWidth
= wxMax(maxMaxWidth
, w
);
2042 // TODO: Make sure the layout box's position reflects
2043 // the position of the children, but without
2044 // breaking layout of a box within a paragraph.
2047 // TODO: (also in para layout) should set the
2048 // object's size to an absolute one if specified,
2049 // but if not specified, calculate it from content.
2051 // We need to add back the margins etc.
2053 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2054 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxWidth
, maxHeight
));
2055 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2056 SetCachedSize(marginRect
.GetSize());
2059 // The maximum size is the greatest of all maximum widths for all paragraphs.
2061 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2062 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxMaxWidth
, maxHeight
)); // Actually max height is a lie, we can't know it
2063 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2064 SetMaxSize(marginRect
.GetSize());
2067 // The minimum size is the greatest of all minimum widths for all paragraphs.
2069 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2070 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxMinWidth
, maxHeight
)); // Actually max height is a lie, we can't know it
2071 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2072 SetMinSize(marginRect
.GetSize());
2075 if (attr
.GetTextBoxAttr().HasVerticalAlignment() &&
2076 (attr
.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP
))
2079 int leftOverSpace
= availableSpace
.height
- topMargin
- bottomMargin
- maxHeight
;
2080 if (leftOverSpace
> 0)
2082 if (attr
.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_CENTRE
)
2084 yOffset
= (leftOverSpace
/2);
2086 else if (attr
.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_BOTTOM
)
2088 yOffset
= leftOverSpace
;
2092 // Move all the children to vertically align the content
2093 // This doesn't take into account floating objects, unfortunately.
2096 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2099 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2101 child
->Move(wxPoint(child
->GetPosition().x
, child
->GetPosition().y
+ yOffset
));
2103 node
= node
->GetNext();
2108 m_invalidRange
= wxRICHTEXT_NONE
;
2113 /// Get/set the size for the given range.
2114 bool wxRichTextParagraphLayoutBox::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* WXUNUSED(partialExtents
)) const
2118 wxRichTextObjectList::compatibility_iterator startPara
= wxRichTextObjectList::compatibility_iterator();
2119 wxRichTextObjectList::compatibility_iterator endPara
= wxRichTextObjectList::compatibility_iterator();
2121 // First find the first paragraph whose starting position is within the range.
2122 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2125 // child is a paragraph
2126 wxRichTextObject
* child
= node
->GetData();
2127 const wxRichTextRange
& r
= child
->GetRange();
2129 if (r
.GetStart() <= range
.GetStart() && r
.GetEnd() >= range
.GetStart())
2135 node
= node
->GetNext();
2138 // Next find the last paragraph containing part of the range
2139 node
= m_children
.GetFirst();
2142 // child is a paragraph
2143 wxRichTextObject
* child
= node
->GetData();
2144 const wxRichTextRange
& r
= child
->GetRange();
2146 if (r
.GetStart() <= range
.GetEnd() && r
.GetEnd() >= range
.GetEnd())
2152 node
= node
->GetNext();
2155 if (!startPara
|| !endPara
)
2158 // Now we can add up the sizes
2159 for (node
= startPara
; node
; node
= node
->GetNext())
2161 // child is a paragraph
2162 wxRichTextObject
* child
= node
->GetData();
2163 const wxRichTextRange
& childRange
= child
->GetRange();
2164 wxRichTextRange rangeToFind
= range
;
2165 rangeToFind
.LimitTo(childRange
);
2167 if (child
->IsTopLevel())
2168 rangeToFind
= child
->GetOwnRange();
2172 int childDescent
= 0;
2173 child
->GetRangeSize(rangeToFind
, childSize
, childDescent
, dc
, context
, flags
, position
);
2175 descent
= wxMax(childDescent
, descent
);
2177 sz
.x
= wxMax(sz
.x
, childSize
.x
);
2178 sz
.y
+= childSize
.y
;
2180 if (node
== endPara
)
2189 /// Get the paragraph at the given position
2190 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphAtPosition(long pos
, bool caretPosition
) const
2195 // First find the first paragraph whose starting position is within the range.
2196 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2199 // child is a paragraph
2200 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2201 // wxASSERT (child != NULL);
2205 // Return first child in buffer if position is -1
2209 if (child
->GetRange().Contains(pos
))
2213 node
= node
->GetNext();
2218 /// Get the line at the given position
2219 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineAtPosition(long pos
, bool caretPosition
) const
2224 // First find the first paragraph whose starting position is within the range.
2225 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2228 wxRichTextObject
* obj
= (wxRichTextObject
*) node
->GetData();
2229 if (obj
->GetRange().Contains(pos
))
2231 // child is a paragraph
2232 wxRichTextParagraph
* child
= wxDynamicCast(obj
, wxRichTextParagraph
);
2233 // wxASSERT (child != NULL);
2237 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2240 wxRichTextLine
* line
= node2
->GetData();
2242 wxRichTextRange range
= line
->GetAbsoluteRange();
2244 if (range
.Contains(pos
) ||
2246 // If the position is end-of-paragraph, then return the last line of
2247 // of the paragraph.
2248 ((range
.GetEnd() == child
->GetRange().GetEnd()-1) && (pos
== child
->GetRange().GetEnd())))
2251 node2
= node2
->GetNext();
2256 node
= node
->GetNext();
2259 int lineCount
= GetLineCount();
2261 return GetLineForVisibleLineNumber(lineCount
-1);
2266 /// Get the line at the given y pixel position, or the last line.
2267 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineAtYPosition(int y
) const
2269 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2272 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2273 // wxASSERT (child != NULL);
2277 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2280 wxRichTextLine
* line
= node2
->GetData();
2282 wxRect
rect(line
->GetRect());
2284 if (y
<= rect
.GetBottom())
2287 node2
= node2
->GetNext();
2291 node
= node
->GetNext();
2295 int lineCount
= GetLineCount();
2297 return GetLineForVisibleLineNumber(lineCount
-1);
2302 /// Get the number of visible lines
2303 int wxRichTextParagraphLayoutBox::GetLineCount() const
2307 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2310 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2311 // wxASSERT (child != NULL);
2314 count
+= child
->GetLines().GetCount();
2316 node
= node
->GetNext();
2322 /// Get the paragraph for a given line
2323 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphForLine(wxRichTextLine
* line
) const
2325 return GetParagraphAtPosition(line
->GetAbsoluteRange().GetStart());
2328 /// Get the line size at the given position
2329 wxSize
wxRichTextParagraphLayoutBox::GetLineSizeAtPosition(long pos
, bool caretPosition
) const
2331 wxRichTextLine
* line
= GetLineAtPosition(pos
, caretPosition
);
2334 return line
->GetSize();
2337 return wxSize(0, 0);
2341 /// Convenience function to add a paragraph of text
2342 wxRichTextRange
wxRichTextParagraphLayoutBox::AddParagraph(const wxString
& text
, wxRichTextAttr
* paraStyle
)
2344 // Don't use the base style, just the default style, and the base style will
2345 // be combined at display time.
2346 // Divide into paragraph and character styles.
2348 wxRichTextAttr defaultCharStyle
;
2349 wxRichTextAttr defaultParaStyle
;
2351 // If the default style is a named paragraph style, don't apply any character formatting
2352 // to the initial text string.
2353 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2355 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2357 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2360 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2362 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2363 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2365 wxRichTextParagraph
* para
= new wxRichTextParagraph(text
, this, pStyle
, cStyle
);
2366 para
->GetAttributes().GetTextBoxAttr().Reset();
2372 return para
->GetRange();
2375 /// Adds multiple paragraphs, based on newlines.
2376 wxRichTextRange
wxRichTextParagraphLayoutBox::AddParagraphs(const wxString
& text
, wxRichTextAttr
* paraStyle
)
2378 // Don't use the base style, just the default style, and the base style will
2379 // be combined at display time.
2380 // Divide into paragraph and character styles.
2382 wxRichTextAttr defaultCharStyle
;
2383 wxRichTextAttr defaultParaStyle
;
2385 // If the default style is a named paragraph style, don't apply any character formatting
2386 // to the initial text string.
2387 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2389 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2391 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2394 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2396 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2397 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2399 wxRichTextParagraph
* firstPara
= NULL
;
2400 wxRichTextParagraph
* lastPara
= NULL
;
2402 wxRichTextRange
range(-1, -1);
2405 size_t len
= text
.length();
2407 wxRichTextParagraph
* para
= new wxRichTextParagraph(wxEmptyString
, this, pStyle
, cStyle
);
2408 para
->GetAttributes().GetTextBoxAttr().Reset();
2417 wxChar ch
= text
[i
];
2418 if (ch
== wxT('\n') || ch
== wxT('\r'))
2422 wxRichTextPlainText
* plainText
= (wxRichTextPlainText
*) para
->GetChildren().GetFirst()->GetData();
2423 plainText
->SetText(line
);
2425 para
= new wxRichTextParagraph(wxEmptyString
, this, pStyle
, cStyle
);
2426 para
->GetAttributes().GetTextBoxAttr().Reset();
2431 line
= wxEmptyString
;
2442 wxRichTextPlainText
* plainText
= (wxRichTextPlainText
*) para
->GetChildren().GetFirst()->GetData();
2443 plainText
->SetText(line
);
2448 return wxRichTextRange(firstPara
->GetRange().GetStart(), lastPara
->GetRange().GetEnd());
2451 /// Convenience function to add an image
2452 wxRichTextRange
wxRichTextParagraphLayoutBox::AddImage(const wxImage
& image
, wxRichTextAttr
* paraStyle
)
2454 // Don't use the base style, just the default style, and the base style will
2455 // be combined at display time.
2456 // Divide into paragraph and character styles.
2458 wxRichTextAttr defaultCharStyle
;
2459 wxRichTextAttr defaultParaStyle
;
2461 // If the default style is a named paragraph style, don't apply any character formatting
2462 // to the initial text string.
2463 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2465 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2467 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2470 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2472 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2473 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2475 wxRichTextParagraph
* para
= new wxRichTextParagraph(this, pStyle
);
2476 para
->GetAttributes().GetTextBoxAttr().Reset();
2478 para
->AppendChild(new wxRichTextImage(image
, this, cStyle
));
2482 return para
->GetRange();
2486 /// Insert fragment into this box at the given position. If partialParagraph is true,
2487 /// it is assumed that the last (or only) paragraph is just a piece of data with no paragraph
2490 bool wxRichTextParagraphLayoutBox::InsertFragment(long position
, wxRichTextParagraphLayoutBox
& fragment
)
2492 // First, find the first paragraph whose starting position is within the range.
2493 wxRichTextParagraph
* para
= GetParagraphAtPosition(position
);
2496 wxRichTextAttr originalAttr
= para
->GetAttributes();
2498 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(para
);
2500 // Now split at this position, returning the object to insert the new
2501 // ones in front of.
2502 wxRichTextObject
* nextObject
= para
->SplitAt(position
);
2504 // Special case: partial paragraph, just one paragraph. Might be a small amount of
2505 // text, for example, so let's optimize.
2507 if (fragment
.GetPartialParagraph() && fragment
.GetChildren().GetCount() == 1)
2509 // Add the first para to this para...
2510 wxRichTextObjectList::compatibility_iterator firstParaNode
= fragment
.GetChildren().GetFirst();
2514 // Iterate through the fragment paragraph inserting the content into this paragraph.
2515 wxRichTextParagraph
* firstPara
= wxDynamicCast(firstParaNode
->GetData(), wxRichTextParagraph
);
2516 wxASSERT (firstPara
!= NULL
);
2518 wxRichTextObjectList::compatibility_iterator objectNode
= firstPara
->GetChildren().GetFirst();
2521 wxRichTextObject
* newObj
= objectNode
->GetData()->Clone();
2526 para
->AppendChild(newObj
);
2530 // Insert before nextObject
2531 para
->InsertChild(newObj
, nextObject
);
2534 objectNode
= objectNode
->GetNext();
2541 // Procedure for inserting a fragment consisting of a number of
2544 // 1. Remove and save the content that's after the insertion point, for adding
2545 // back once we've added the fragment.
2546 // 2. Add the content from the first fragment paragraph to the current
2548 // 3. Add remaining fragment paragraphs after the current paragraph.
2549 // 4. Add back the saved content from the first paragraph. If partialParagraph
2550 // is true, add it to the last paragraph added and not a new one.
2552 // 1. Remove and save objects after split point.
2553 wxList savedObjects
;
2555 para
->MoveToList(nextObject
, savedObjects
);
2557 // 2. Add the content from the 1st fragment paragraph.
2558 wxRichTextObjectList::compatibility_iterator firstParaNode
= fragment
.GetChildren().GetFirst();
2562 wxRichTextParagraph
* firstPara
= wxDynamicCast(firstParaNode
->GetData(), wxRichTextParagraph
);
2563 wxASSERT(firstPara
!= NULL
);
2565 if (!(fragment
.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
))
2566 para
->SetAttributes(firstPara
->GetAttributes());
2568 // Save empty paragraph attributes for appending later
2569 // These are character attributes deliberately set for a new paragraph. Without this,
2570 // we couldn't pass default attributes when appending a new paragraph.
2571 wxRichTextAttr emptyParagraphAttributes
;
2573 wxRichTextObjectList::compatibility_iterator objectNode
= firstPara
->GetChildren().GetFirst();
2575 if (objectNode
&& firstPara
->GetChildren().GetCount() == 1 && objectNode
->GetData()->IsEmpty())
2576 emptyParagraphAttributes
= objectNode
->GetData()->GetAttributes();
2580 wxRichTextObject
* newObj
= objectNode
->GetData()->Clone();
2583 para
->AppendChild(newObj
);
2585 objectNode
= objectNode
->GetNext();
2588 // 3. Add remaining fragment paragraphs after the current paragraph.
2589 wxRichTextObjectList::compatibility_iterator nextParagraphNode
= node
->GetNext();
2590 wxRichTextObject
* nextParagraph
= NULL
;
2591 if (nextParagraphNode
)
2592 nextParagraph
= nextParagraphNode
->GetData();
2594 wxRichTextObjectList::compatibility_iterator i
= fragment
.GetChildren().GetFirst()->GetNext();
2595 wxRichTextParagraph
* finalPara
= para
;
2597 bool needExtraPara
= (!i
|| !fragment
.GetPartialParagraph());
2599 // If there was only one paragraph, we need to insert a new one.
2602 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2603 wxASSERT( para
!= NULL
);
2605 finalPara
= (wxRichTextParagraph
*) para
->Clone();
2608 InsertChild(finalPara
, nextParagraph
);
2610 AppendChild(finalPara
);
2615 // If there was only one paragraph, or we have full paragraphs in our fragment,
2616 // we need to insert a new one.
2619 finalPara
= new wxRichTextParagraph
;
2622 InsertChild(finalPara
, nextParagraph
);
2624 AppendChild(finalPara
);
2627 // 4. Add back the remaining content.
2631 finalPara
->MoveFromList(savedObjects
);
2633 // Ensure there's at least one object
2634 if (finalPara
->GetChildCount() == 0)
2636 wxRichTextPlainText
* text
= new wxRichTextPlainText(wxEmptyString
);
2637 text
->SetAttributes(emptyParagraphAttributes
);
2639 finalPara
->AppendChild(text
);
2643 if ((fragment
.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
) && firstPara
)
2644 finalPara
->SetAttributes(firstPara
->GetAttributes());
2645 else if (finalPara
&& finalPara
!= para
)
2646 finalPara
->SetAttributes(originalAttr
);
2654 wxRichTextObjectList::compatibility_iterator i
= fragment
.GetChildren().GetFirst();
2657 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2658 wxASSERT( para
!= NULL
);
2660 AppendChild(para
->Clone());
2669 /// Make a copy of the fragment corresponding to the given range, putting it in 'fragment'.
2670 /// If there was an incomplete paragraph at the end, partialParagraph is set to true.
2671 bool wxRichTextParagraphLayoutBox::CopyFragment(const wxRichTextRange
& range
, wxRichTextParagraphLayoutBox
& fragment
)
2673 wxRichTextObjectList::compatibility_iterator i
= GetChildren().GetFirst();
2676 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2677 wxASSERT( para
!= NULL
);
2679 if (!para
->GetRange().IsOutside(range
))
2681 fragment
.AppendChild(para
->Clone());
2686 // Now top and tail the first and last paragraphs in our new fragment (which might be the same).
2687 if (!fragment
.IsEmpty())
2689 wxRichTextParagraph
* firstPara
= wxDynamicCast(fragment
.GetChildren().GetFirst()->GetData(), wxRichTextParagraph
);
2690 wxASSERT( firstPara
!= NULL
);
2692 wxRichTextParagraph
* lastPara
= wxDynamicCast(fragment
.GetChildren().GetLast()->GetData(), wxRichTextParagraph
);
2693 wxASSERT( lastPara
!= NULL
);
2695 if (!firstPara
|| !lastPara
)
2698 bool isFragment
= (range
.GetEnd() < lastPara
->GetRange().GetEnd());
2700 long firstPos
= firstPara
->GetRange().GetStart();
2702 // Adjust for renumbering from zero
2703 wxRichTextRange
topTailRange(range
.GetStart() - firstPos
, range
.GetEnd() - firstPos
);
2706 fragment
.CalculateRange(0, end
);
2708 // Chop off the start of the paragraph
2709 if (topTailRange
.GetStart() > 0)
2711 wxRichTextRange
r(0, topTailRange
.GetStart()-1);
2712 firstPara
->DeleteRange(r
);
2714 // Make sure the numbering is correct
2715 fragment
.CalculateRange(0, end
);
2717 // Now, we've deleted some positions, so adjust the range
2719 topTailRange
.SetStart(range
.GetLength());
2720 topTailRange
.SetEnd(fragment
.GetOwnRange().GetEnd());
2724 topTailRange
.SetStart(range
.GetLength());
2725 topTailRange
.SetEnd(fragment
.GetOwnRange().GetEnd());
2728 if (topTailRange
.GetStart() < lastPara
->GetRange().GetEnd())
2730 lastPara
->DeleteRange(topTailRange
);
2732 // Make sure the numbering is correct
2734 fragment
.CalculateRange(0, end
);
2736 // We only have part of a paragraph at the end
2737 fragment
.SetPartialParagraph(true);
2741 // We have a partial paragraph (don't save last new paragraph marker)
2742 // or complete paragraph
2743 fragment
.SetPartialParagraph(isFragment
);
2750 /// Given a position, get the number of the visible line (potentially many to a paragraph),
2751 /// starting from zero at the start of the buffer.
2752 long wxRichTextParagraphLayoutBox::GetVisibleLineNumber(long pos
, bool caretPosition
, bool startOfLine
) const
2759 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2762 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2763 // wxASSERT( child != NULL );
2767 if (child
->GetRange().Contains(pos
))
2769 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2772 wxRichTextLine
* line
= node2
->GetData();
2773 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
2775 if (lineRange
.Contains(pos
) || pos
== lineRange
.GetStart())
2777 // If the caret is displayed at the end of the previous wrapped line,
2778 // we want to return the line it's _displayed_ at (not the actual line
2779 // containing the position).
2780 if (lineRange
.GetStart() == pos
&& !startOfLine
&& child
->GetRange().GetStart() != pos
)
2781 return lineCount
- 1;
2788 node2
= node2
->GetNext();
2790 // If we didn't find it in the lines, it must be
2791 // the last position of the paragraph. So return the last line.
2795 lineCount
+= child
->GetLines().GetCount();
2798 node
= node
->GetNext();
2805 /// Given a line number, get the corresponding wxRichTextLine object.
2806 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineForVisibleLineNumber(long lineNumber
) const
2810 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2813 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2814 // wxASSERT(child != NULL);
2818 if (lineNumber
< (int) (child
->GetLines().GetCount() + lineCount
))
2820 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2823 wxRichTextLine
* line
= node2
->GetData();
2825 if (lineCount
== lineNumber
)
2830 node2
= node2
->GetNext();
2834 lineCount
+= child
->GetLines().GetCount();
2837 node
= node
->GetNext();
2844 /// Delete range from layout.
2845 bool wxRichTextParagraphLayoutBox::DeleteRange(const wxRichTextRange
& range
)
2847 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2849 wxRichTextParagraph
* firstPara
= NULL
;
2852 wxRichTextParagraph
* obj
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2853 // wxASSERT (obj != NULL);
2855 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
2859 // Delete the range in each paragraph
2861 if (!obj
->GetRange().IsOutside(range
))
2863 // Deletes the content of this object within the given range
2864 obj
->DeleteRange(range
);
2866 wxRichTextRange thisRange
= obj
->GetRange();
2867 wxRichTextAttr thisAttr
= obj
->GetAttributes();
2869 // If the whole paragraph is within the range to delete,
2870 // delete the whole thing.
2871 if (range
.GetStart() <= thisRange
.GetStart() && range
.GetEnd() >= thisRange
.GetEnd())
2873 // Delete the whole object
2874 RemoveChild(obj
, true);
2877 else if (!firstPara
)
2880 // If the range includes the paragraph end, we need to join this
2881 // and the next paragraph.
2882 if (range
.GetEnd() <= thisRange
.GetEnd())
2884 // We need to move the objects from the next paragraph
2885 // to this paragraph
2887 wxRichTextParagraph
* nextParagraph
= NULL
;
2888 if ((range
.GetEnd() < thisRange
.GetEnd()) && obj
)
2889 nextParagraph
= obj
;
2892 // We're ending at the end of the paragraph, so merge the _next_ paragraph.
2894 nextParagraph
= wxDynamicCast(next
->GetData(), wxRichTextParagraph
);
2897 bool applyFinalParagraphStyle
= firstPara
&& nextParagraph
&& nextParagraph
!= firstPara
;
2899 wxRichTextAttr nextParaAttr
;
2900 if (applyFinalParagraphStyle
)
2902 // Special case when deleting the end of a paragraph - use _this_ paragraph's style,
2903 // not the next one.
2904 if (range
.GetStart() == range
.GetEnd() && range
.GetStart() == thisRange
.GetEnd())
2905 nextParaAttr
= thisAttr
;
2907 nextParaAttr
= nextParagraph
->GetAttributes();
2910 if (firstPara
&& nextParagraph
&& firstPara
!= nextParagraph
)
2912 // Move the objects to the previous para
2913 wxRichTextObjectList::compatibility_iterator node1
= nextParagraph
->GetChildren().GetFirst();
2917 wxRichTextObject
* obj1
= node1
->GetData();
2919 firstPara
->AppendChild(obj1
);
2921 wxRichTextObjectList::compatibility_iterator next1
= node1
->GetNext();
2922 nextParagraph
->GetChildren().Erase(node1
);
2927 // Delete the paragraph
2928 RemoveChild(nextParagraph
, true);
2931 // Avoid empty paragraphs
2932 if (firstPara
&& firstPara
->GetChildren().GetCount() == 0)
2934 wxRichTextPlainText
* text
= new wxRichTextPlainText(wxEmptyString
);
2935 firstPara
->AppendChild(text
);
2938 if (applyFinalParagraphStyle
)
2939 firstPara
->SetAttributes(nextParaAttr
);
2952 /// Get any text in this object for the given range
2953 wxString
wxRichTextParagraphLayoutBox::GetTextForRange(const wxRichTextRange
& range
) const
2957 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2960 wxRichTextObject
* child
= node
->GetData();
2961 if (!child
->GetRange().IsOutside(range
))
2963 wxRichTextRange childRange
= range
;
2964 childRange
.LimitTo(child
->GetRange());
2966 wxString childText
= child
->GetTextForRange(childRange
);
2970 if ((childRange
.GetEnd() == child
->GetRange().GetEnd()) && node
->GetNext())
2975 node
= node
->GetNext();
2981 /// Get all the text
2982 wxString
wxRichTextParagraphLayoutBox::GetText() const
2984 return GetTextForRange(GetOwnRange());
2987 /// Get the paragraph by number
2988 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphAtLine(long paragraphNumber
) const
2990 if ((size_t) paragraphNumber
>= GetChildCount())
2993 return (wxRichTextParagraph
*) GetChild((size_t) paragraphNumber
);
2996 /// Get the length of the paragraph
2997 int wxRichTextParagraphLayoutBox::GetParagraphLength(long paragraphNumber
) const
2999 wxRichTextParagraph
* para
= GetParagraphAtLine(paragraphNumber
);
3001 return para
->GetRange().GetLength() - 1; // don't include newline
3006 /// Get the text of the paragraph
3007 wxString
wxRichTextParagraphLayoutBox::GetParagraphText(long paragraphNumber
) const
3009 wxRichTextParagraph
* para
= GetParagraphAtLine(paragraphNumber
);
3011 return para
->GetTextForRange(para
->GetRange());
3013 return wxEmptyString
;
3016 /// Convert zero-based line column and paragraph number to a position.
3017 long wxRichTextParagraphLayoutBox::XYToPosition(long x
, long y
) const
3019 wxRichTextParagraph
* para
= GetParagraphAtLine(y
);
3022 return para
->GetRange().GetStart() + x
;
3028 /// Convert zero-based position to line column and paragraph number
3029 bool wxRichTextParagraphLayoutBox::PositionToXY(long pos
, long* x
, long* y
) const
3031 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
);
3035 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3038 wxRichTextObject
* child
= node
->GetData();
3042 node
= node
->GetNext();
3046 *x
= pos
- para
->GetRange().GetStart();
3054 /// Get the leaf object in a paragraph at this position.
3055 /// Given a line number, get the corresponding wxRichTextLine object.
3056 wxRichTextObject
* wxRichTextParagraphLayoutBox::GetLeafObjectAtPosition(long position
) const
3058 wxRichTextParagraph
* para
= GetParagraphAtPosition(position
);
3061 wxRichTextObjectList::compatibility_iterator node
= para
->GetChildren().GetFirst();
3065 wxRichTextObject
* child
= node
->GetData();
3066 if (child
->GetRange().Contains(position
))
3069 node
= node
->GetNext();
3071 if (position
== para
->GetRange().GetEnd() && para
->GetChildCount() > 0)
3072 return para
->GetChildren().GetLast()->GetData();
3077 /// Set character or paragraph text attributes: apply character styles only to immediate text nodes
3078 bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange
& range
, const wxRichTextAttr
& style
, int flags
)
3080 bool characterStyle
= false;
3081 bool paragraphStyle
= false;
3083 if (style
.IsCharacterStyle())
3084 characterStyle
= true;
3085 if (style
.IsParagraphStyle())
3086 paragraphStyle
= true;
3088 wxRichTextBuffer
* buffer
= GetBuffer();
3090 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
3091 bool applyMinimal
= ((flags
& wxRICHTEXT_SETSTYLE_OPTIMIZE
) != 0);
3092 bool parasOnly
= ((flags
& wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY
) != 0);
3093 bool charactersOnly
= ((flags
& wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY
) != 0);
3094 bool resetExistingStyle
= ((flags
& wxRICHTEXT_SETSTYLE_RESET
) != 0);
3095 bool removeStyle
= ((flags
& wxRICHTEXT_SETSTYLE_REMOVE
) != 0);
3097 // Apply paragraph style first, if any
3098 wxRichTextAttr
wholeStyle(style
);
3100 if (!removeStyle
&& wholeStyle
.HasParagraphStyleName() && buffer
->GetStyleSheet())
3102 wxRichTextParagraphStyleDefinition
* def
= buffer
->GetStyleSheet()->FindParagraphStyle(wholeStyle
.GetParagraphStyleName());
3104 wxRichTextApplyStyle(wholeStyle
, def
->GetStyleMergedWithBase(buffer
->GetStyleSheet()));
3107 // Limit the attributes to be set to the content to only character attributes.
3108 wxRichTextAttr
characterAttributes(wholeStyle
);
3109 characterAttributes
.SetFlags(characterAttributes
.GetFlags() & (wxTEXT_ATTR_CHARACTER
));
3111 if (!removeStyle
&& characterAttributes
.HasCharacterStyleName() && buffer
->GetStyleSheet())
3113 wxRichTextCharacterStyleDefinition
* def
= buffer
->GetStyleSheet()->FindCharacterStyle(characterAttributes
.GetCharacterStyleName());
3115 wxRichTextApplyStyle(characterAttributes
, def
->GetStyleMergedWithBase(buffer
->GetStyleSheet()));
3118 // If we are associated with a control, make undoable; otherwise, apply immediately
3121 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3123 wxRichTextAction
* action
= NULL
;
3125 if (haveControl
&& withUndo
)
3127 action
= new wxRichTextAction(NULL
, _("Change Style"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
3128 action
->SetRange(range
);
3129 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3132 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3135 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3136 // wxASSERT (para != NULL);
3138 if (para
&& para
->GetChildCount() > 0)
3140 // Stop searching if we're beyond the range of interest
3141 if (para
->GetRange().GetStart() > range
.GetEnd())
3144 if (!para
->GetRange().IsOutside(range
))
3146 // We'll be using a copy of the paragraph to make style changes,
3147 // not updating the buffer directly.
3148 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3150 if (haveControl
&& withUndo
)
3152 newPara
= new wxRichTextParagraph(*para
);
3153 action
->GetNewParagraphs().AppendChild(newPara
);
3155 // Also store the old ones for Undo
3156 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3161 // If we're specifying paragraphs only, then we really mean character formatting
3162 // to be included in the paragraph style
3163 if ((paragraphStyle
|| parasOnly
) && !charactersOnly
)
3167 // Removes the given style from the paragraph
3168 wxRichTextRemoveStyle(newPara
->GetAttributes(), style
);
3170 else if (resetExistingStyle
)
3171 newPara
->GetAttributes() = wholeStyle
;
3176 // Only apply attributes that will make a difference to the combined
3177 // style as seen on the display
3178 wxRichTextAttr
combinedAttr(para
->GetCombinedAttributes(true));
3179 wxRichTextApplyStyle(newPara
->GetAttributes(), wholeStyle
, & combinedAttr
);
3182 wxRichTextApplyStyle(newPara
->GetAttributes(), wholeStyle
);
3186 // When applying paragraph styles dynamically, don't change the text objects' attributes
3187 // since they will computed as needed. Only apply the character styling if it's _only_
3188 // character styling. This policy is subject to change and might be put under user control.
3190 // Hm. we might well be applying a mix of paragraph and character styles, in which
3191 // case we _do_ want to apply character styles regardless of what para styles are set.
3192 // But if we're applying a paragraph style, which has some character attributes, but
3193 // we only want the paragraphs to hold this character style, then we _don't_ want to
3194 // apply the character style. So we need to be able to choose.
3196 if (!parasOnly
&& (characterStyle
|charactersOnly
) && range
.GetStart() != newPara
->GetRange().GetEnd())
3198 wxRichTextRange
childRange(range
);
3199 childRange
.LimitTo(newPara
->GetRange());
3201 // Find the starting position and if necessary split it so
3202 // we can start applying a different style.
3203 // TODO: check that the style actually changes or is different
3204 // from style outside of range
3205 wxRichTextObject
* firstObject
wxDUMMY_INITIALIZE(NULL
);
3206 wxRichTextObject
* lastObject
wxDUMMY_INITIALIZE(NULL
);
3208 if (childRange
.GetStart() == newPara
->GetRange().GetStart())
3209 firstObject
= newPara
->GetChildren().GetFirst()->GetData();
3211 firstObject
= newPara
->SplitAt(range
.GetStart());
3213 // Increment by 1 because we're apply the style one _after_ the split point
3214 long splitPoint
= childRange
.GetEnd();
3215 if (splitPoint
!= newPara
->GetRange().GetEnd())
3219 if (splitPoint
== newPara
->GetRange().GetEnd())
3220 lastObject
= newPara
->GetChildren().GetLast()->GetData();
3222 // lastObject is set as a side-effect of splitting. It's
3223 // returned as the object before the new object.
3224 (void) newPara
->SplitAt(splitPoint
, & lastObject
);
3226 wxASSERT(firstObject
!= NULL
);
3227 wxASSERT(lastObject
!= NULL
);
3229 if (!firstObject
|| !lastObject
)
3232 wxRichTextObjectList::compatibility_iterator firstNode
= newPara
->GetChildren().Find(firstObject
);
3233 wxRichTextObjectList::compatibility_iterator lastNode
= newPara
->GetChildren().Find(lastObject
);
3235 wxASSERT(firstNode
);
3238 wxRichTextObjectList::compatibility_iterator node2
= firstNode
;
3242 wxRichTextObject
* child
= node2
->GetData();
3246 // Removes the given style from the paragraph
3247 wxRichTextRemoveStyle(child
->GetAttributes(), style
);
3249 else if (resetExistingStyle
)
3250 child
->GetAttributes() = characterAttributes
;
3255 // Only apply attributes that will make a difference to the combined
3256 // style as seen on the display
3257 wxRichTextAttr
combinedAttr(newPara
->GetCombinedAttributes(child
->GetAttributes(), true));
3258 wxRichTextApplyStyle(child
->GetAttributes(), characterAttributes
, & combinedAttr
);
3261 wxRichTextApplyStyle(child
->GetAttributes(), characterAttributes
);
3264 if (node2
== lastNode
)
3267 node2
= node2
->GetNext();
3273 node
= node
->GetNext();
3276 // Do action, or delay it until end of batch.
3277 if (haveControl
&& withUndo
)
3278 buffer
->SubmitAction(action
);
3283 // Just change the attributes for this single object.
3284 void wxRichTextParagraphLayoutBox::SetStyle(wxRichTextObject
* obj
, const wxRichTextAttr
& textAttr
, int flags
)
3286 wxRichTextBuffer
* buffer
= GetBuffer();
3287 bool withUndo
= flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
;
3288 bool resetExistingStyle
= ((flags
& wxRICHTEXT_SETSTYLE_RESET
) != 0);
3289 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3291 wxRichTextAction
*action
= NULL
;
3292 wxRichTextAttr newAttr
= obj
->GetAttributes();
3293 if (resetExistingStyle
)
3296 newAttr
.Apply(textAttr
);
3298 if (haveControl
&& withUndo
)
3300 action
= new wxRichTextAction(NULL
, _("Change Object Style"), wxRICHTEXT_CHANGE_ATTRIBUTES
, buffer
, obj
->GetContainer(), buffer
->GetRichTextCtrl());
3301 action
->SetRange(obj
->GetRange().FromInternal());
3302 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3303 action
->MakeObject(obj
);
3305 action
->GetAttributes() = newAttr
;
3308 obj
->GetAttributes() = newAttr
;
3310 if (haveControl
&& withUndo
)
3311 buffer
->SubmitAction(action
);
3314 /// Get the text attributes for this position.
3315 bool wxRichTextParagraphLayoutBox::GetStyle(long position
, wxRichTextAttr
& style
)
3317 return DoGetStyle(position
, style
, true);
3320 bool wxRichTextParagraphLayoutBox::GetUncombinedStyle(long position
, wxRichTextAttr
& style
)
3322 return DoGetStyle(position
, style
, false);
3325 /// Implementation helper for GetStyle. If combineStyles is true, combine base, paragraph and
3326 /// context attributes.
3327 bool wxRichTextParagraphLayoutBox::DoGetStyle(long position
, wxRichTextAttr
& style
, bool combineStyles
)
3329 wxRichTextObject
* obj
wxDUMMY_INITIALIZE(NULL
);
3331 if (style
.IsParagraphStyle())
3333 obj
= GetParagraphAtPosition(position
);
3338 // Start with the base style
3339 style
= GetAttributes();
3340 style
.GetTextBoxAttr().Reset();
3342 // Apply the paragraph style
3343 wxRichTextApplyStyle(style
, obj
->GetAttributes());
3346 style
= obj
->GetAttributes();
3353 obj
= GetLeafObjectAtPosition(position
);
3358 wxRichTextParagraph
* para
= wxDynamicCast(obj
->GetParent(), wxRichTextParagraph
);
3359 style
= para
? para
->GetCombinedAttributes(obj
->GetAttributes()) : obj
->GetAttributes();
3362 style
= obj
->GetAttributes();
3370 static bool wxHasStyle(long flags
, long style
)
3372 return (flags
& style
) != 0;
3375 /// Combines 'style' with 'currentStyle' for the purpose of summarising the attributes of a range of
3377 bool wxRichTextParagraphLayoutBox::CollectStyle(wxRichTextAttr
& currentStyle
, const wxRichTextAttr
& style
, wxRichTextAttr
& clashingAttr
, wxRichTextAttr
& absentAttr
)
3379 currentStyle
.CollectCommonAttributes(style
, clashingAttr
, absentAttr
);
3384 /// Get the combined style for a range - if any attribute is different within the range,
3385 /// that attribute is not present within the flags.
3386 /// *** Note that this is not recursive, and so assumes that content inside a paragraph is not itself
3388 bool wxRichTextParagraphLayoutBox::GetStyleForRange(const wxRichTextRange
& range
, wxRichTextAttr
& style
)
3390 style
= wxRichTextAttr();
3392 wxRichTextAttr clashingAttr
;
3393 wxRichTextAttr absentAttrPara
, absentAttrChar
;
3395 wxRichTextObjectList::compatibility_iterator node
= GetChildren().GetFirst();
3398 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3399 if (para
&& !(para
->GetRange().GetStart() > range
.GetEnd() || para
->GetRange().GetEnd() < range
.GetStart()))
3401 if (para
->GetChildren().GetCount() == 0)
3403 wxRichTextAttr paraStyle
= para
->GetCombinedAttributes(true /* use box attributes */);
3405 CollectStyle(style
, paraStyle
, clashingAttr
, absentAttrPara
);
3409 wxRichTextRange
paraRange(para
->GetRange());
3410 paraRange
.LimitTo(range
);
3412 // First collect paragraph attributes only
3413 wxRichTextAttr paraStyle
= para
->GetCombinedAttributes();
3414 paraStyle
.SetFlags(paraStyle
.GetFlags() & wxTEXT_ATTR_PARAGRAPH
);
3415 CollectStyle(style
, paraStyle
, clashingAttr
, absentAttrPara
);
3417 wxRichTextObjectList::compatibility_iterator childNode
= para
->GetChildren().GetFirst();
3421 wxRichTextObject
* child
= childNode
->GetData();
3422 if (!(child
->GetRange().GetStart() > range
.GetEnd() || child
->GetRange().GetEnd() < range
.GetStart()))
3424 wxRichTextAttr childStyle
= para
->GetCombinedAttributes(child
->GetAttributes(), true /* include box attributes */);
3426 // Now collect character attributes only
3427 childStyle
.SetFlags(childStyle
.GetFlags() & wxTEXT_ATTR_CHARACTER
);
3429 CollectStyle(style
, childStyle
, clashingAttr
, absentAttrChar
);
3432 childNode
= childNode
->GetNext();
3436 node
= node
->GetNext();
3441 /// Set default style
3442 bool wxRichTextParagraphLayoutBox::SetDefaultStyle(const wxRichTextAttr
& style
)
3444 m_defaultAttributes
= style
;
3448 /// Test if this whole range has character attributes of the specified kind. If any
3449 /// of the attributes are different within the range, the test fails. You
3450 /// can use this to implement, for example, bold button updating. style must have
3451 /// flags indicating which attributes are of interest.
3452 bool wxRichTextParagraphLayoutBox::HasCharacterAttributes(const wxRichTextRange
& range
, const wxRichTextAttr
& style
) const
3455 int matchingCount
= 0;
3457 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3460 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3461 // wxASSERT (para != NULL);
3465 // Stop searching if we're beyond the range of interest
3466 if (para
->GetRange().GetStart() > range
.GetEnd())
3467 return foundCount
== matchingCount
&& foundCount
!= 0;
3469 if (!para
->GetRange().IsOutside(range
))
3471 wxRichTextObjectList::compatibility_iterator node2
= para
->GetChildren().GetFirst();
3475 wxRichTextObject
* child
= node2
->GetData();
3476 // Allow for empty string if no buffer
3477 wxRichTextRange childRange
= child
->GetRange();
3478 if (childRange
.GetLength() == 0 && GetRange().GetLength() == 1)
3479 childRange
.SetEnd(childRange
.GetEnd()+1);
3481 if (!childRange
.IsOutside(range
) && wxDynamicCast(child
, wxRichTextPlainText
))
3484 wxRichTextAttr textAttr
= para
->GetCombinedAttributes(child
->GetAttributes());
3486 if (textAttr
.EqPartial(style
, false /* strong test - attributes must be valid in both objects */))
3490 node2
= node2
->GetNext();
3495 node
= node
->GetNext();
3498 return foundCount
== matchingCount
&& foundCount
!= 0;
3501 /// Test if this whole range has paragraph attributes of the specified kind. If any
3502 /// of the attributes are different within the range, the test fails. You
3503 /// can use this to implement, for example, centering button updating. style must have
3504 /// flags indicating which attributes are of interest.
3505 bool wxRichTextParagraphLayoutBox::HasParagraphAttributes(const wxRichTextRange
& range
, const wxRichTextAttr
& style
) const
3508 int matchingCount
= 0;
3510 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3513 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3514 // wxASSERT (para != NULL);
3518 // Stop searching if we're beyond the range of interest
3519 if (para
->GetRange().GetStart() > range
.GetEnd())
3520 return foundCount
== matchingCount
&& foundCount
!= 0;
3522 if (!para
->GetRange().IsOutside(range
))
3524 wxRichTextAttr textAttr
= GetAttributes();
3525 // Apply the paragraph style
3526 wxRichTextApplyStyle(textAttr
, para
->GetAttributes());
3529 if (textAttr
.EqPartial(style
, false /* strong test */))
3534 node
= node
->GetNext();
3536 return foundCount
== matchingCount
&& foundCount
!= 0;
3539 void wxRichTextParagraphLayoutBox::PrepareContent(wxRichTextParagraphLayoutBox
& container
)
3541 wxRichTextBuffer
* buffer
= GetBuffer();
3542 if (buffer
&& buffer
->GetRichTextCtrl())
3543 buffer
->GetRichTextCtrl()->PrepareContent(container
);
3546 /// Set character or paragraph properties
3547 bool wxRichTextParagraphLayoutBox::SetProperties(const wxRichTextRange
& range
, const wxRichTextProperties
& properties
, int flags
)
3549 wxRichTextBuffer
* buffer
= GetBuffer();
3551 bool withUndo
= ((flags
& wxRICHTEXT_SETPROPERTIES_WITH_UNDO
) != 0);
3552 bool parasOnly
= ((flags
& wxRICHTEXT_SETPROPERTIES_PARAGRAPHS_ONLY
) != 0);
3553 bool charactersOnly
= ((flags
& wxRICHTEXT_SETPROPERTIES_CHARACTERS_ONLY
) != 0);
3554 bool resetExistingProperties
= ((flags
& wxRICHTEXT_SETPROPERTIES_RESET
) != 0);
3555 bool removeProperties
= ((flags
& wxRICHTEXT_SETPROPERTIES_REMOVE
) != 0);
3557 // If we are associated with a control, make undoable; otherwise, apply immediately
3560 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3562 wxRichTextAction
* action
= NULL
;
3564 if (haveControl
&& withUndo
)
3566 action
= new wxRichTextAction(NULL
, _("Change Properties"), wxRICHTEXT_CHANGE_PROPERTIES
, buffer
, this, buffer
->GetRichTextCtrl());
3567 action
->SetRange(range
);
3568 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3571 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3574 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3575 // wxASSERT (para != NULL);
3577 if (para
&& para
->GetChildCount() > 0)
3579 // Stop searching if we're beyond the range of interest
3580 if (para
->GetRange().GetStart() > range
.GetEnd())
3583 if (!para
->GetRange().IsOutside(range
))
3585 // We'll be using a copy of the paragraph to make style changes,
3586 // not updating the buffer directly.
3587 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3589 if (haveControl
&& withUndo
)
3591 newPara
= new wxRichTextParagraph(*para
);
3592 action
->GetNewParagraphs().AppendChild(newPara
);
3594 // Also store the old ones for Undo
3595 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3602 if (removeProperties
)
3604 // Removes the given style from the paragraph
3606 newPara
->GetProperties().RemoveProperties(properties
);
3608 else if (resetExistingProperties
)
3609 newPara
->GetProperties() = properties
;
3611 newPara
->GetProperties().MergeProperties(properties
);
3614 // When applying paragraph styles dynamically, don't change the text objects' attributes
3615 // since they will computed as needed. Only apply the character styling if it's _only_
3616 // character styling. This policy is subject to change and might be put under user control.
3618 // Hm. we might well be applying a mix of paragraph and character styles, in which
3619 // case we _do_ want to apply character styles regardless of what para styles are set.
3620 // But if we're applying a paragraph style, which has some character attributes, but
3621 // we only want the paragraphs to hold this character style, then we _don't_ want to
3622 // apply the character style. So we need to be able to choose.
3624 if (!parasOnly
&& charactersOnly
&& range
.GetStart() != newPara
->GetRange().GetEnd())
3626 wxRichTextRange
childRange(range
);
3627 childRange
.LimitTo(newPara
->GetRange());
3629 // Find the starting position and if necessary split it so
3630 // we can start applying different properties.
3631 // TODO: check that the properties actually change or are different
3632 // from properties outside of range
3633 wxRichTextObject
* firstObject
wxDUMMY_INITIALIZE(NULL
);
3634 wxRichTextObject
* lastObject
wxDUMMY_INITIALIZE(NULL
);
3636 if (childRange
.GetStart() == newPara
->GetRange().GetStart())
3637 firstObject
= newPara
->GetChildren().GetFirst()->GetData();
3639 firstObject
= newPara
->SplitAt(range
.GetStart());
3641 // Increment by 1 because we're apply the style one _after_ the split point
3642 long splitPoint
= childRange
.GetEnd();
3643 if (splitPoint
!= newPara
->GetRange().GetEnd())
3647 if (splitPoint
== newPara
->GetRange().GetEnd())
3648 lastObject
= newPara
->GetChildren().GetLast()->GetData();
3650 // lastObject is set as a side-effect of splitting. It's
3651 // returned as the object before the new object.
3652 (void) newPara
->SplitAt(splitPoint
, & lastObject
);
3654 wxASSERT(firstObject
!= NULL
);
3655 wxASSERT(lastObject
!= NULL
);
3657 if (!firstObject
|| !lastObject
)
3660 wxRichTextObjectList::compatibility_iterator firstNode
= newPara
->GetChildren().Find(firstObject
);
3661 wxRichTextObjectList::compatibility_iterator lastNode
= newPara
->GetChildren().Find(lastObject
);
3663 wxASSERT(firstNode
);
3666 wxRichTextObjectList::compatibility_iterator node2
= firstNode
;
3670 wxRichTextObject
* child
= node2
->GetData();
3672 if (removeProperties
)
3674 // Removes the given properties from the paragraph
3675 child
->GetProperties().RemoveProperties(properties
);
3677 else if (resetExistingProperties
)
3678 child
->GetProperties() = properties
;
3681 child
->GetProperties().MergeProperties(properties
);
3684 if (node2
== lastNode
)
3687 node2
= node2
->GetNext();
3693 node
= node
->GetNext();
3696 // Do action, or delay it until end of batch.
3697 if (haveControl
&& withUndo
)
3698 buffer
->SubmitAction(action
);
3703 void wxRichTextParagraphLayoutBox::Reset()
3707 wxRichTextBuffer
* buffer
= GetBuffer();
3708 if (buffer
&& buffer
->GetRichTextCtrl())
3710 wxRichTextEvent
event(wxEVT_COMMAND_RICHTEXT_BUFFER_RESET
, buffer
->GetRichTextCtrl()->GetId());
3711 event
.SetEventObject(buffer
->GetRichTextCtrl());
3712 event
.SetContainer(this);
3714 buffer
->SendEvent(event
, true);
3717 AddParagraph(wxEmptyString
);
3719 PrepareContent(*this);
3721 InvalidateHierarchy(wxRICHTEXT_ALL
);
3724 /// Invalidate the buffer. With no argument, invalidates whole buffer.
3725 void wxRichTextParagraphLayoutBox::Invalidate(const wxRichTextRange
& invalidRange
)
3727 wxRichTextCompositeObject::Invalidate(invalidRange
);
3729 DoInvalidate(invalidRange
);
3732 // Do the (in)validation for this object only
3733 void wxRichTextParagraphLayoutBox::DoInvalidate(const wxRichTextRange
& invalidRange
)
3735 if (invalidRange
== wxRICHTEXT_ALL
)
3737 m_invalidRange
= wxRICHTEXT_ALL
;
3739 // Already invalidating everything
3740 else if (m_invalidRange
== wxRICHTEXT_ALL
)
3745 if ((invalidRange
.GetStart() < m_invalidRange
.GetStart()) || m_invalidRange
.GetStart() == -1)
3746 m_invalidRange
.SetStart(invalidRange
.GetStart());
3747 if (invalidRange
.GetEnd() > m_invalidRange
.GetEnd())
3748 m_invalidRange
.SetEnd(invalidRange
.GetEnd());
3752 // Do the (in)validation both up and down the hierarchy
3753 void wxRichTextParagraphLayoutBox::InvalidateHierarchy(const wxRichTextRange
& invalidRange
)
3755 Invalidate(invalidRange
);
3757 if (invalidRange
!= wxRICHTEXT_NONE
)
3759 // Now go up the hierarchy
3760 wxRichTextObject
* thisObj
= this;
3761 wxRichTextObject
* p
= GetParent();
3764 wxRichTextParagraphLayoutBox
* l
= wxDynamicCast(p
, wxRichTextParagraphLayoutBox
);
3766 l
->DoInvalidate(thisObj
->GetRange());
3774 /// Get invalid range, rounding to entire paragraphs if argument is true.
3775 wxRichTextRange
wxRichTextParagraphLayoutBox::GetInvalidRange(bool wholeParagraphs
) const
3777 if (m_invalidRange
== wxRICHTEXT_ALL
|| m_invalidRange
== wxRICHTEXT_NONE
)
3778 return m_invalidRange
;
3780 wxRichTextRange range
= m_invalidRange
;
3782 if (wholeParagraphs
)
3784 wxRichTextParagraph
* para1
= GetParagraphAtPosition(range
.GetStart());
3786 range
.SetStart(para1
->GetRange().GetStart());
3787 // floating layout make all child should be relayout
3788 range
.SetEnd(GetOwnRange().GetEnd());
3793 /// Apply the style sheet to the buffer, for example if the styles have changed.
3794 bool wxRichTextParagraphLayoutBox::ApplyStyleSheet(wxRichTextStyleSheet
* styleSheet
)
3796 wxASSERT(styleSheet
!= NULL
);
3802 wxRichTextAttr
attr(GetBasicStyle());
3803 if (GetBasicStyle().HasParagraphStyleName())
3805 wxRichTextParagraphStyleDefinition
* paraDef
= styleSheet
->FindParagraphStyle(GetBasicStyle().GetParagraphStyleName());
3808 attr
.Apply(paraDef
->GetStyleMergedWithBase(styleSheet
));
3809 SetBasicStyle(attr
);
3814 if (GetBasicStyle().HasCharacterStyleName())
3816 wxRichTextCharacterStyleDefinition
* charDef
= styleSheet
->FindCharacterStyle(GetBasicStyle().GetCharacterStyleName());
3819 attr
.Apply(charDef
->GetStyleMergedWithBase(styleSheet
));
3820 SetBasicStyle(attr
);
3825 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3828 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3829 // wxASSERT (para != NULL);
3833 // Combine paragraph and list styles. If there is a list style in the original attributes,
3834 // the current indentation overrides anything else and is used to find the item indentation.
3835 // Also, for applying paragraph styles, consider having 2 modes: (1) we merge with what we have,
3836 // thereby taking into account all user changes, (2) reset the style completely (except for indentation/list
3837 // exception as above).
3838 // Problem: when changing from one list style to another, there's a danger that the level info will get lost.
3839 // So when changing a list style interactively, could retrieve level based on current style, then
3840 // set appropriate indent and apply new style.
3844 if (para
->GetAttributes().HasOutlineLevel())
3845 outline
= para
->GetAttributes().GetOutlineLevel();
3846 if (para
->GetAttributes().HasBulletNumber())
3847 num
= para
->GetAttributes().GetBulletNumber();
3849 if (!para
->GetAttributes().GetParagraphStyleName().IsEmpty() && !para
->GetAttributes().GetListStyleName().IsEmpty())
3851 int currentIndent
= para
->GetAttributes().GetLeftIndent();
3853 wxRichTextParagraphStyleDefinition
* paraDef
= styleSheet
->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
3854 wxRichTextListStyleDefinition
* listDef
= styleSheet
->FindListStyle(para
->GetAttributes().GetListStyleName());
3855 if (paraDef
&& !listDef
)
3857 para
->GetAttributes() = paraDef
->GetStyleMergedWithBase(styleSheet
);
3860 else if (listDef
&& !paraDef
)
3862 // Set overall style defined for the list style definition
3863 para
->GetAttributes() = listDef
->GetStyleMergedWithBase(styleSheet
);
3865 // Apply the style for this level
3866 wxRichTextApplyStyle(para
->GetAttributes(), * listDef
->GetLevelAttributes(listDef
->FindLevelForIndent(currentIndent
)));
3869 else if (listDef
&& paraDef
)
3871 // Combines overall list style, style for level, and paragraph style
3872 para
->GetAttributes() = listDef
->CombineWithParagraphStyle(currentIndent
, paraDef
->GetStyleMergedWithBase(styleSheet
));
3876 else if (para
->GetAttributes().GetParagraphStyleName().IsEmpty() && !para
->GetAttributes().GetListStyleName().IsEmpty())
3878 int currentIndent
= para
->GetAttributes().GetLeftIndent();
3880 wxRichTextListStyleDefinition
* listDef
= styleSheet
->FindListStyle(para
->GetAttributes().GetListStyleName());
3882 // Overall list definition style
3883 para
->GetAttributes() = listDef
->GetStyleMergedWithBase(styleSheet
);
3885 // Style for this level
3886 wxRichTextApplyStyle(para
->GetAttributes(), * listDef
->GetLevelAttributes(listDef
->FindLevelForIndent(currentIndent
)));
3890 else if (!para
->GetAttributes().GetParagraphStyleName().IsEmpty() && para
->GetAttributes().GetListStyleName().IsEmpty())
3892 wxRichTextParagraphStyleDefinition
* def
= styleSheet
->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
3895 para
->GetAttributes() = def
->GetStyleMergedWithBase(styleSheet
);
3901 para
->GetAttributes().SetOutlineLevel(outline
);
3903 para
->GetAttributes().SetBulletNumber(num
);
3906 node
= node
->GetNext();
3908 return foundCount
!= 0;
3912 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
3914 wxRichTextBuffer
* buffer
= GetBuffer();
3915 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
3917 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
3918 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
3919 bool specifyLevel
= ((flags
& wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL
) != 0);
3920 bool renumber
= ((flags
& wxRICHTEXT_SETSTYLE_RENUMBER
) != 0);
3922 // Current number, if numbering
3925 wxASSERT (!specifyLevel
|| (specifyLevel
&& (specifiedLevel
>= 0)));
3927 // If we are associated with a control, make undoable; otherwise, apply immediately
3930 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3932 wxRichTextAction
* action
= NULL
;
3934 if (haveControl
&& withUndo
)
3936 action
= new wxRichTextAction(NULL
, _("Change List Style"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
3937 action
->SetRange(range
);
3938 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3941 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3944 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3945 // wxASSERT (para != NULL);
3947 if (para
&& para
->GetChildCount() > 0)
3949 // Stop searching if we're beyond the range of interest
3950 if (para
->GetRange().GetStart() > range
.GetEnd())
3953 if (!para
->GetRange().IsOutside(range
))
3955 // We'll be using a copy of the paragraph to make style changes,
3956 // not updating the buffer directly.
3957 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3959 if (haveControl
&& withUndo
)
3961 newPara
= new wxRichTextParagraph(*para
);
3962 action
->GetNewParagraphs().AppendChild(newPara
);
3964 // Also store the old ones for Undo
3965 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3972 int thisIndent
= newPara
->GetAttributes().GetLeftIndent();
3973 int thisLevel
= specifyLevel
? specifiedLevel
: def
->FindLevelForIndent(thisIndent
);
3975 // How is numbering going to work?
3976 // If we are renumbering, or numbering for the first time, we need to keep
3977 // track of the number for each level. But we might be simply applying a different
3979 // In Word, applying a style to several paragraphs, even if at different levels,
3980 // reverts the level back to the same one. So we could do the same here.
3981 // Renumbering will need to be done when we promote/demote a paragraph.
3983 // Apply the overall list style, and item style for this level
3984 wxRichTextAttr
listStyle(def
->GetCombinedStyleForLevel(thisLevel
, styleSheet
));
3985 wxRichTextApplyStyle(newPara
->GetAttributes(), listStyle
);
3987 // Now we need to do numbering
3988 // Preserve the existing list item continuation bullet style, if any
3989 if (para
->GetAttributes().HasBulletStyle() && (para
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
))
3990 newPara
->GetAttributes().SetBulletStyle(newPara
->GetAttributes().GetBulletStyle()|wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
);
3993 // Now we need to do numbering
3996 newPara
->GetAttributes().SetBulletNumber(n
);
4002 else if (!newPara
->GetAttributes().GetListStyleName().IsEmpty())
4004 // if def is NULL, remove list style, applying any associated paragraph style
4005 // to restore the attributes
4007 newPara
->GetAttributes().SetListStyleName(wxEmptyString
);
4008 newPara
->GetAttributes().SetLeftIndent(0, 0);
4009 newPara
->GetAttributes().SetBulletText(wxEmptyString
);
4011 // Eliminate the main list-related attributes
4012 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
);
4014 if (styleSheet
&& !newPara
->GetAttributes().GetParagraphStyleName().IsEmpty())
4016 wxRichTextParagraphStyleDefinition
* def
= styleSheet
->FindParagraphStyle(newPara
->GetAttributes().GetParagraphStyleName());
4019 newPara
->GetAttributes() = def
->GetStyleMergedWithBase(styleSheet
);
4026 node
= node
->GetNext();
4029 // Do action, or delay it until end of batch.
4030 if (haveControl
&& withUndo
)
4031 buffer
->SubmitAction(action
);
4036 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
)
4038 wxRichTextBuffer
* buffer
= GetBuffer();
4039 if (buffer
&& buffer
->GetStyleSheet())
4041 wxRichTextListStyleDefinition
* def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4043 return SetListStyle(range
, def
, flags
, startFrom
, specifiedLevel
);
4048 /// Clear list for given range
4049 bool wxRichTextParagraphLayoutBox::ClearListStyle(const wxRichTextRange
& range
, int flags
)
4051 return SetListStyle(range
, NULL
, flags
);
4054 /// Number/renumber any list elements in the given range
4055 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
4057 return DoNumberList(range
, range
, 0, def
, flags
, startFrom
, specifiedLevel
);
4060 /// Number/renumber any list elements in the given range. Also do promotion or demotion of items, if specified
4061 bool wxRichTextParagraphLayoutBox::DoNumberList(const wxRichTextRange
& range
, const wxRichTextRange
& promotionRange
, int promoteBy
,
4062 wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
4064 wxRichTextBuffer
* buffer
= GetBuffer();
4065 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
4067 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
4068 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
4070 bool specifyLevel
= ((flags
& wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL
) != 0);
4073 bool renumber
= ((flags
& wxRICHTEXT_SETSTYLE_RENUMBER
) != 0);
4075 // Max number of levels
4076 const int maxLevels
= 10;
4078 // The level we're looking at now
4079 int currentLevel
= -1;
4081 // The item number for each level
4082 int levels
[maxLevels
];
4085 // Reset all numbering
4086 for (i
= 0; i
< maxLevels
; i
++)
4088 if (startFrom
!= -1)
4089 levels
[i
] = startFrom
-1;
4090 else if (renumber
) // start again
4093 levels
[i
] = -1; // start from the number we found, if any
4097 wxASSERT(!specifyLevel
|| (specifyLevel
&& (specifiedLevel
>= 0)));
4100 // If we are associated with a control, make undoable; otherwise, apply immediately
4103 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
4105 wxRichTextAction
* action
= NULL
;
4107 if (haveControl
&& withUndo
)
4109 action
= new wxRichTextAction(NULL
, _("Renumber List"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
4110 action
->SetRange(range
);
4111 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
4114 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
4117 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
4118 // wxASSERT (para != NULL);
4120 if (para
&& para
->GetChildCount() > 0)
4122 // Stop searching if we're beyond the range of interest
4123 if (para
->GetRange().GetStart() > range
.GetEnd())
4126 if (!para
->GetRange().IsOutside(range
))
4128 // We'll be using a copy of the paragraph to make style changes,
4129 // not updating the buffer directly.
4130 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
4132 if (haveControl
&& withUndo
)
4134 newPara
= new wxRichTextParagraph(*para
);
4135 action
->GetNewParagraphs().AppendChild(newPara
);
4137 // Also store the old ones for Undo
4138 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
4143 wxRichTextListStyleDefinition
* defToUse
= def
;
4146 if (styleSheet
&& !newPara
->GetAttributes().GetListStyleName().IsEmpty())
4147 defToUse
= styleSheet
->FindListStyle(newPara
->GetAttributes().GetListStyleName());
4152 int thisIndent
= newPara
->GetAttributes().GetLeftIndent();
4153 int thisLevel
= defToUse
->FindLevelForIndent(thisIndent
);
4155 // If we've specified a level to apply to all, change the level.
4156 if (specifiedLevel
!= -1)
4157 thisLevel
= specifiedLevel
;
4159 // Do promotion if specified
4160 if ((promoteBy
!= 0) && !para
->GetRange().IsOutside(promotionRange
))
4162 thisLevel
= thisLevel
- promoteBy
;
4169 // Apply the overall list style, and item style for this level
4170 wxRichTextAttr
listStyle(defToUse
->GetCombinedStyleForLevel(thisLevel
, styleSheet
));
4171 wxRichTextApplyStyle(newPara
->GetAttributes(), listStyle
);
4173 // Preserve the existing list item continuation bullet style, if any
4174 if (para
->GetAttributes().HasBulletStyle() && (para
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
))
4175 newPara
->GetAttributes().SetBulletStyle(newPara
->GetAttributes().GetBulletStyle()|wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
);
4177 // OK, we've (re)applied the style, now let's get the numbering right.
4179 if (currentLevel
== -1)
4180 currentLevel
= thisLevel
;
4182 // Same level as before, do nothing except increment level's number afterwards
4183 if (currentLevel
== thisLevel
)
4186 // A deeper level: start renumbering all levels after current level
4187 else if (thisLevel
> currentLevel
)
4189 for (i
= currentLevel
+1; i
<= thisLevel
; i
++)
4193 currentLevel
= thisLevel
;
4195 else if (thisLevel
< currentLevel
)
4197 currentLevel
= thisLevel
;
4200 // Use the current numbering if -1 and we have a bullet number already
4201 if (levels
[currentLevel
] == -1)
4203 if (newPara
->GetAttributes().HasBulletNumber())
4204 levels
[currentLevel
] = newPara
->GetAttributes().GetBulletNumber();
4206 levels
[currentLevel
] = 1;
4210 if (!(para
->GetAttributes().HasBulletStyle() && (para
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
)))
4211 levels
[currentLevel
] ++;
4214 newPara
->GetAttributes().SetBulletNumber(levels
[currentLevel
]);
4216 // Create the bullet text if an outline list
4217 if (listStyle
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
)
4220 for (i
= 0; i
<= currentLevel
; i
++)
4222 if (!text
.IsEmpty())
4224 text
+= wxString::Format(wxT("%d"), levels
[i
]);
4226 newPara
->GetAttributes().SetBulletText(text
);
4232 node
= node
->GetNext();
4235 // Do action, or delay it until end of batch.
4236 if (haveControl
&& withUndo
)
4237 buffer
->SubmitAction(action
);
4242 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
)
4244 wxRichTextBuffer
* buffer
= GetBuffer();
4245 if (buffer
->GetStyleSheet())
4247 wxRichTextListStyleDefinition
* def
= NULL
;
4248 if (!defName
.IsEmpty())
4249 def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4250 return NumberList(range
, def
, flags
, startFrom
, specifiedLevel
);
4255 /// Promote the list items within the given range. promoteBy can be a positive or negative number, e.g. 1 or -1
4256 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy
, const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int specifiedLevel
)
4259 // One strategy is to first work out the range within which renumbering must occur. Then could pass these two ranges
4260 // to NumberList with a flag indicating promotion is required within one of the ranges.
4261 // Find first and last paragraphs in range. Then for first, calculate new indentation and look back until we find
4262 // a paragraph that either has no list style, or has one that is different or whose indentation is less.
4263 // We start renumbering from the para after that different para we found. We specify that the numbering of that
4264 // list position will start from 1.
4265 // Similarly, we look after the last para in the promote range for an indentation that is less (or no list style).
4266 // We can end the renumbering at this point.
4268 // For now, only renumber within the promotion range.
4270 return DoNumberList(range
, range
, promoteBy
, def
, flags
, 1, specifiedLevel
);
4273 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy
, const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int specifiedLevel
)
4275 wxRichTextBuffer
* buffer
= GetBuffer();
4276 if (buffer
->GetStyleSheet())
4278 wxRichTextListStyleDefinition
* def
= NULL
;
4279 if (!defName
.IsEmpty())
4280 def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4281 return PromoteList(promoteBy
, range
, def
, flags
, specifiedLevel
);
4286 /// Fills in the attributes for numbering a paragraph after previousParagraph. It also finds the
4287 /// position of the paragraph that it had to start looking from.
4288 bool wxRichTextParagraphLayoutBox::FindNextParagraphNumber(wxRichTextParagraph
* previousParagraph
, wxRichTextAttr
& attr
) const
4290 // Search for a paragraph that isn't a continuation paragraph (no bullet)
4291 while (previousParagraph
&& previousParagraph
->GetAttributes().HasBulletStyle() && previousParagraph
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
)
4293 wxRichTextObjectList::compatibility_iterator node
= ((wxRichTextCompositeObject
*) previousParagraph
->GetParent())->GetChildren().Find(previousParagraph
);
4296 node
= node
->GetPrevious();
4298 previousParagraph
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
4300 previousParagraph
= NULL
;
4303 previousParagraph
= NULL
;
4306 if (!previousParagraph
->GetAttributes().HasFlag(wxTEXT_ATTR_BULLET_STYLE
) || previousParagraph
->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
)
4309 wxRichTextBuffer
* buffer
= GetBuffer();
4310 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
4311 if (styleSheet
&& !previousParagraph
->GetAttributes().GetListStyleName().IsEmpty())
4313 wxRichTextListStyleDefinition
* def
= styleSheet
->FindListStyle(previousParagraph
->GetAttributes().GetListStyleName());
4316 // int thisIndent = previousParagraph->GetAttributes().GetLeftIndent();
4317 // int thisLevel = def->FindLevelForIndent(thisIndent);
4319 bool isOutline
= (previousParagraph
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
) != 0;
4321 attr
.SetFlags(previousParagraph
->GetAttributes().GetFlags() & (wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_BULLET_NUMBER
|wxTEXT_ATTR_BULLET_TEXT
|wxTEXT_ATTR_BULLET_NAME
));
4322 if (previousParagraph
->GetAttributes().HasBulletName())
4323 attr
.SetBulletName(previousParagraph
->GetAttributes().GetBulletName());
4324 attr
.SetBulletStyle(previousParagraph
->GetAttributes().GetBulletStyle());
4325 attr
.SetListStyleName(previousParagraph
->GetAttributes().GetListStyleName());
4327 int nextNumber
= previousParagraph
->GetAttributes().GetBulletNumber() + 1;
4328 attr
.SetBulletNumber(nextNumber
);
4332 wxString text
= previousParagraph
->GetAttributes().GetBulletText();
4333 if (!text
.IsEmpty())
4335 int pos
= text
.Find(wxT('.'), true);
4336 if (pos
!= wxNOT_FOUND
)
4338 text
= text
.Mid(0, text
.Length() - pos
- 1);
4341 text
= wxEmptyString
;
4342 if (!text
.IsEmpty())
4344 text
+= wxString::Format(wxT("%d"), nextNumber
);
4345 attr
.SetBulletText(text
);
4359 * wxRichTextParagraph
4360 * This object represents a single paragraph (or in a straight text editor, a line).
4363 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraph
, wxRichTextCompositeObject
)
4365 wxArrayInt
wxRichTextParagraph::sm_defaultTabs
;
4367 wxRichTextParagraph::wxRichTextParagraph(wxRichTextObject
* parent
, wxRichTextAttr
* style
):
4368 wxRichTextCompositeObject(parent
)
4371 SetAttributes(*style
);
4374 wxRichTextParagraph::wxRichTextParagraph(const wxString
& text
, wxRichTextObject
* parent
, wxRichTextAttr
* paraStyle
, wxRichTextAttr
* charStyle
):
4375 wxRichTextCompositeObject(parent
)
4378 SetAttributes(*paraStyle
);
4380 AppendChild(new wxRichTextPlainText(text
, this, charStyle
));
4383 wxRichTextParagraph::~wxRichTextParagraph()
4389 bool wxRichTextParagraph::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int WXUNUSED(descent
), int style
)
4394 // Currently we don't merge these attributes with the parent, but we
4395 // should consider whether we should (e.g. if we set a border colour
4396 // for all paragraphs). But generally box attributes are likely to be
4397 // different for different objects.
4398 wxRect paraRect
= GetRect();
4399 wxRichTextAttr attr
= GetCombinedAttributes();
4400 context
.ApplyVirtualAttributes(attr
, this);
4402 DrawBoxAttributes(dc
, GetBuffer(), attr
, paraRect
);
4404 // Draw the bullet, if any
4405 if ((attr
.GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
) == 0 && (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION
) == 0)
4407 if (attr
.GetLeftSubIndent() != 0)
4409 int spaceBeforePara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingBefore());
4410 int leftIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftIndent());
4412 wxRichTextAttr
bulletAttr(attr
);
4414 // Combine with the font of the first piece of content, if one is specified
4415 if (GetChildren().GetCount() > 0)
4417 wxRichTextObject
* firstObj
= (wxRichTextObject
*) GetChildren().GetFirst()->GetData();
4418 if (!firstObj
->IsFloatable() && firstObj
->GetAttributes().HasFont())
4420 wxRichTextApplyStyle(bulletAttr
, firstObj
->GetAttributes());
4424 // Get line height from first line, if any
4425 wxRichTextLine
* line
= m_cachedLines
.GetFirst() ? (wxRichTextLine
* ) m_cachedLines
.GetFirst()->GetData() : NULL
;
4428 int lineHeight
wxDUMMY_INITIALIZE(0);
4431 lineHeight
= line
->GetSize().y
;
4432 linePos
= line
->GetPosition() + GetPosition();
4437 if (bulletAttr
.HasFont() && GetBuffer())
4438 font
= GetBuffer()->GetFontTable().FindFont(bulletAttr
);
4440 font
= (*wxNORMAL_FONT
);
4442 wxCheckSetFont(dc
, font
);
4444 lineHeight
= dc
.GetCharHeight();
4445 linePos
= GetPosition();
4446 linePos
.y
+= spaceBeforePara
;
4449 wxRect
bulletRect(GetPosition().x
+ leftIndent
, linePos
.y
, linePos
.x
- (GetPosition().x
+ leftIndent
), lineHeight
);
4451 if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
)
4453 if (wxRichTextBuffer::GetRenderer())
4454 wxRichTextBuffer::GetRenderer()->DrawBitmapBullet(this, dc
, bulletAttr
, bulletRect
);
4456 else if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_STANDARD
)
4458 if (wxRichTextBuffer::GetRenderer())
4459 wxRichTextBuffer::GetRenderer()->DrawStandardBullet(this, dc
, bulletAttr
, bulletRect
);
4463 wxString bulletText
= GetBulletText();
4465 if (!bulletText
.empty() && wxRichTextBuffer::GetRenderer())
4466 wxRichTextBuffer::GetRenderer()->DrawTextBullet(this, dc
, bulletAttr
, bulletRect
, bulletText
);
4471 // Draw the range for each line, one object at a time.
4473 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
4476 wxRichTextLine
* line
= node
->GetData();
4477 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
4479 // Lines are specified relative to the paragraph
4481 wxPoint linePosition
= line
->GetPosition() + GetPosition();
4483 // Don't draw if off the screen
4484 if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) != 0) || ((linePosition
.y
+ line
->GetSize().y
) >= rect
.y
&& linePosition
.y
<= rect
.y
+ rect
.height
))
4486 wxPoint objectPosition
= linePosition
;
4487 int maxDescent
= line
->GetDescent();
4489 // Loop through objects until we get to the one within range
4490 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
4495 wxRichTextObject
* child
= node2
->GetData();
4497 if (!child
->IsFloating() && child
->GetRange().GetLength() > 0 && !child
->GetRange().IsOutside(lineRange
) && !lineRange
.IsOutside(range
))
4499 // Draw this part of the line at the correct position
4500 wxRichTextRange
objectRange(child
->GetRange());
4501 objectRange
.LimitTo(lineRange
);
4504 if (child
->IsTopLevel())
4506 objectSize
= child
->GetCachedSize();
4507 objectRange
= child
->GetOwnRange();
4511 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING && wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4512 if (i
< (int) line
->GetObjectSizes().GetCount())
4514 objectSize
.x
= line
->GetObjectSizes()[(size_t) i
];
4520 child
->GetRangeSize(objectRange
, objectSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, objectPosition
);
4524 // Use the child object's width, but the whole line's height
4525 wxRect
childRect(objectPosition
, wxSize(objectSize
.x
, line
->GetSize().y
));
4526 child
->Draw(dc
, context
, objectRange
, selection
, childRect
, maxDescent
, style
);
4528 objectPosition
.x
+= objectSize
.x
;
4531 else if (child
->GetRange().GetStart() > lineRange
.GetEnd())
4532 // Can break out of inner loop now since we've passed this line's range
4535 node2
= node2
->GetNext();
4539 node
= node
->GetNext();
4545 // Get the range width using partial extents calculated for the whole paragraph.
4546 static int wxRichTextGetRangeWidth(const wxRichTextParagraph
& para
, const wxRichTextRange
& range
, const wxArrayInt
& partialExtents
)
4548 wxASSERT(partialExtents
.GetCount() >= (size_t) range
.GetLength());
4550 if (partialExtents
.GetCount() < (size_t) range
.GetLength())
4553 int leftMostPos
= 0;
4554 if (range
.GetStart() - para
.GetRange().GetStart() > 0)
4555 leftMostPos
= partialExtents
[range
.GetStart() - para
.GetRange().GetStart() - 1];
4557 int rightMostPos
= partialExtents
[range
.GetEnd() - para
.GetRange().GetStart()];
4559 int w
= rightMostPos
- leftMostPos
;
4564 /// Lay the item out
4565 bool wxRichTextParagraph::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
)
4567 // Deal with floating objects firstly before the normal layout
4568 wxRichTextBuffer
* buffer
= GetBuffer();
4570 wxRichTextFloatCollector
* collector
= GetContainer()->GetFloatCollector();
4571 wxASSERT(collector
);
4572 LayoutFloat(dc
, context
, rect
, style
, collector
);
4574 wxRichTextAttr attr
= GetCombinedAttributes();
4575 context
.ApplyVirtualAttributes(attr
, this);
4579 // Increase the size of the paragraph due to spacing
4580 int spaceBeforePara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingBefore());
4581 int spaceAfterPara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingAfter());
4582 int leftIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftIndent());
4583 int leftSubIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftSubIndent());
4584 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
4586 int lineSpacing
= 0;
4588 // Let's assume line spacing of 10 is normal, 15 is 1.5, 20 is 2, etc.
4589 if (attr
.HasLineSpacing() && attr
.GetLineSpacing() > 0 && attr
.GetFont().IsOk())
4591 wxCheckSetFont(dc
, attr
.GetFont());
4592 lineSpacing
= (int) (double(dc
.GetCharHeight()) * (double(attr
.GetLineSpacing())/10.0 - 1.0));
4595 // Start position for each line relative to the paragraph
4596 int startPositionFirstLine
= leftIndent
;
4597 int startPositionSubsequentLines
= leftIndent
+ leftSubIndent
;
4599 // If we have a bullet in this paragraph, the start position for the first line's text
4600 // is actually leftIndent + leftSubIndent.
4601 if (attr
.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE
)
4602 startPositionFirstLine
= startPositionSubsequentLines
;
4604 long lastEndPos
= GetRange().GetStart()-1;
4605 long lastCompletedEndPos
= lastEndPos
;
4607 int currentWidth
= 0;
4608 SetPosition(rect
.GetPosition());
4610 wxPoint
currentPosition(0, spaceBeforePara
); // We will calculate lines relative to paragraph
4613 int maxHeight
= currentPosition
.y
;
4618 int lineDescent
= 0;
4620 wxRichTextObjectList::compatibility_iterator node
;
4622 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4624 wxArrayInt partialExtents
;
4627 int paraDescent
= 0;
4629 // This calculates the partial text extents
4630 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, rect
.GetPosition(), & partialExtents
);
4632 node
= m_children
.GetFirst();
4635 wxRichTextObject
* child
= node
->GetData();
4637 //child->SetCachedSize(wxDefaultSize);
4638 child
->Layout(dc
, context
, rect
, style
);
4640 node
= node
->GetNext();
4646 // We may need to go back to a previous child, in which case create the new line,
4647 // find the child corresponding to the start position of the string, and
4650 wxRect availableRect
;
4652 node
= m_children
.GetFirst();
4655 wxRichTextObject
* child
= node
->GetData();
4657 // If floating, ignore. We already laid out floats.
4658 // Also ignore if empty object, except if we haven't got any
4660 if (child
->IsFloating() || !child
->IsShown() ||
4661 (child
->GetRange().GetLength() == 0 && maxHeight
> spaceBeforePara
)
4664 node
= node
->GetNext();
4668 // If this is e.g. a composite text box, it will need to be laid out itself.
4669 // But if just a text fragment or image, for example, this will
4670 // do nothing. NB: won't we need to set the position after layout?
4671 // since for example if position is dependent on vertical line size, we
4672 // can't tell the position until the size is determined. So possibly introduce
4673 // another layout phase.
4675 // We may only be looking at part of a child, if we searched back for wrapping
4676 // and found a suitable point some way into the child. So get the size for the fragment
4679 long nextBreakPos
= GetFirstLineBreakPosition(lastEndPos
+1);
4680 long lastPosToUse
= child
->GetRange().GetEnd();
4681 bool lineBreakInThisObject
= (nextBreakPos
> -1 && nextBreakPos
<= child
->GetRange().GetEnd());
4683 if (lineBreakInThisObject
)
4684 lastPosToUse
= nextBreakPos
;
4687 int childDescent
= 0;
4689 int startOffset
= (lineCount
== 0 ? startPositionFirstLine
: startPositionSubsequentLines
);
4690 availableRect
= wxRect(rect
.x
+ startOffset
, rect
.y
+ currentPosition
.y
,
4691 rect
.width
- startOffset
- rightIndent
, rect
.height
);
4693 if (child
->IsTopLevel())
4695 wxSize oldSize
= child
->GetCachedSize();
4697 child
->Invalidate(wxRICHTEXT_ALL
);
4698 child
->SetPosition(wxPoint(0, 0));
4700 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4701 // lays out the object again using the minimum size
4702 // The position will be determined by its location in its line,
4703 // and not by the child's actual position.
4704 child
->LayoutToBestSize(dc
, context
, buffer
,
4705 attr
, child
->GetAttributes(), availableRect
, parentRect
, style
);
4707 if (oldSize
!= child
->GetCachedSize())
4709 partialExtents
.Clear();
4711 // Recalculate the partial text extents since the child object changed size
4712 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), & partialExtents
);
4716 // Problem: we need to layout composites here for which we need the available width,
4717 // but we can't get the available width without using the float collector which
4718 // needs to know the object height.
4720 if ((nextBreakPos
== -1) && (lastEndPos
== child
->GetRange().GetStart() - 1)) // i.e. we want to get the whole thing
4722 childSize
= child
->GetCachedSize();
4723 childDescent
= child
->GetDescent();
4727 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4728 // Get height only, then the width using the partial extents
4729 GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
);
4730 childSize
.x
= wxRichTextGetRangeWidth(*this, wxRichTextRange(lastEndPos
+1, lastPosToUse
), partialExtents
);
4732 GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, rect
.GetPosition());
4737 int loopIterations
= 0;
4739 // If there are nested objects that need to lay themselves out, we have to do this in a
4740 // loop because the height of the object may well depend on the available width.
4741 // And because of floating object positioning, the available width depends on the
4742 // height of the object and whether it will clash with the floating objects.
4743 // So, we see whether the available width changes due to the presence of floating images.
4744 // If it does, then we'll use the new restricted width to find the object height again.
4745 // If this causes another restriction in the available width, we'll try again, until
4746 // either we lose patience or the available width settles down.
4751 wxRect oldAvailableRect
= availableRect
;
4753 // Available width depends on the floating objects and the line height.
4754 // Note: the floating objects may be placed vertically along the two sides of
4755 // buffer, so we may have different available line widths with different
4756 // [startY, endY]. So, we can't determine how wide the available
4757 // space is until we know the exact line height.
4758 if (childDescent
== 0)
4760 lineHeight
= wxMax(lineHeight
, childSize
.y
);
4761 lineDescent
= maxDescent
;
4762 lineAscent
= maxAscent
;
4766 lineDescent
= wxMax(childDescent
, maxDescent
);
4767 lineAscent
= wxMax(childSize
.y
-childDescent
, maxAscent
);
4769 lineHeight
= wxMax(lineHeight
, (lineDescent
+ lineAscent
));
4770 wxRect floatAvailableRect
= collector
->GetAvailableRect(rect
.y
+ currentPosition
.y
, rect
.y
+ currentPosition
.y
+ lineHeight
);
4772 // Adjust availableRect to the space that is available when taking floating objects into account.
4774 if (floatAvailableRect
.x
+ startOffset
> availableRect
.x
)
4776 int newX
= floatAvailableRect
.x
+ startOffset
;
4777 int newW
= availableRect
.width
- (newX
- availableRect
.x
);
4778 availableRect
.x
= newX
;
4779 availableRect
.width
= newW
;
4782 if (floatAvailableRect
.width
< availableRect
.width
)
4783 availableRect
.width
= floatAvailableRect
.width
;
4785 currentPosition
.x
= availableRect
.x
- rect
.x
;
4787 if (child
->IsTopLevel() && loopIterations
<= 20)
4789 if (availableRect
!= oldAvailableRect
)
4791 wxSize oldSize
= child
->GetCachedSize();
4793 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4794 // lays out the object again using the minimum size
4795 child
->Invalidate(wxRICHTEXT_ALL
);
4796 child
->LayoutToBestSize(dc
, context
, buffer
,
4797 attr
, child
->GetAttributes(), availableRect
, parentRect
, style
);
4798 childSize
= child
->GetCachedSize();
4799 childDescent
= child
->GetDescent();
4801 if (oldSize
!= child
->GetCachedSize())
4803 partialExtents
.Clear();
4805 // Recalculate the partial text extents since the child object changed size
4806 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), & partialExtents
);
4809 // Go around the loop finding the available rect for the given floating objects
4819 if (child
->IsTopLevel())
4821 // We can move it to the correct position at this point
4822 // TODO: probably need to add margin
4823 child
->Move(GetPosition() + wxPoint(currentWidth
+ (wxMax(leftIndent
, leftIndent
+ leftSubIndent
)), currentPosition
.y
));
4827 // 1) There was a line break BEFORE the natural break
4828 // 2) There was a line break AFTER the natural break
4829 // 3) It's the last line
4830 // 4) The child still fits (carry on) - 'else' clause
4832 if ((lineBreakInThisObject
&& (childSize
.x
+ currentWidth
<= availableRect
.width
))
4834 (childSize
.x
+ currentWidth
> availableRect
.width
)
4836 ((childSize
.x
+ currentWidth
<= availableRect
.width
) && !node
->GetNext())
4840 long wrapPosition
= 0;
4841 if ((childSize
.x
+ currentWidth
<= availableRect
.width
) && !node
->GetNext() && !lineBreakInThisObject
)
4842 wrapPosition
= child
->GetRange().GetEnd();
4845 // Find a place to wrap. This may walk back to previous children,
4846 // for example if a word spans several objects.
4847 // Note: one object must contains only one wxTextAtrr, so the line height will not
4848 // change inside one object. Thus, we can pass the remain line width to the
4849 // FindWrapPosition function.
4850 if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos
+1, child
->GetRange().GetEnd()), dc
, context
, availableRect
.width
, wrapPosition
, & partialExtents
))
4852 // If the function failed, just cut it off at the end of this child.
4853 wrapPosition
= child
->GetRange().GetEnd();
4856 // FindWrapPosition can still return a value that will put us in an endless wrapping loop
4857 if (wrapPosition
<= lastCompletedEndPos
)
4858 wrapPosition
= wxMax(lastCompletedEndPos
+1,child
->GetRange().GetEnd());
4860 // Line end position shouldn't be the same as the end, or greater.
4861 if (wrapPosition
>= GetRange().GetEnd())
4862 wrapPosition
= wxMax(0, GetRange().GetEnd()-1);
4864 // wxLogDebug(wxT("Split at %ld"), wrapPosition);
4866 // Let's find the actual size of the current line now
4868 wxRichTextRange
actualRange(lastCompletedEndPos
+1, wrapPosition
);
4872 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4873 if (!child
->IsEmpty())
4875 // Get height only, then the width using the partial extents
4876 GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
);
4877 actualSize
.x
= wxRichTextGetRangeWidth(*this, actualRange
, partialExtents
);
4881 GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
4883 currentWidth
= actualSize
.x
;
4885 // The descent for the whole line at this point, is the correct max descent
4886 maxDescent
= childDescent
;
4888 maxAscent
= actualSize
.y
-childDescent
;
4890 // lineHeight is given by the height for the whole line, since it will
4891 // take into account ascend/descend.
4892 lineHeight
= actualSize
.y
;
4894 if (lineHeight
== 0 && buffer
)
4896 wxFont
font(buffer
->GetFontTable().FindFont(attr
));
4897 wxCheckSetFont(dc
, font
);
4898 lineHeight
= dc
.GetCharHeight();
4901 if (maxDescent
== 0)
4904 dc
.GetTextExtent(wxT("X"), & w
, &h
, & maxDescent
);
4908 wxRichTextLine
* line
= AllocateLine(lineCount
);
4910 // Set relative range so we won't have to change line ranges when paragraphs are moved
4911 line
->SetRange(wxRichTextRange(actualRange
.GetStart() - GetRange().GetStart(), actualRange
.GetEnd() - GetRange().GetStart()));
4912 line
->SetPosition(currentPosition
);
4913 line
->SetSize(wxSize(currentWidth
, lineHeight
));
4914 line
->SetDescent(maxDescent
);
4916 maxHeight
= currentPosition
.y
+ lineHeight
;
4918 // Now move down a line. TODO: add margins, spacing
4919 currentPosition
.y
+= lineHeight
;
4920 currentPosition
.y
+= lineSpacing
;
4923 maxWidth
= wxMax(maxWidth
, currentWidth
+startOffset
);
4928 // TODO: account for zero-length objects
4929 // wxASSERT(wrapPosition > lastCompletedEndPos);
4931 lastEndPos
= wrapPosition
;
4932 lastCompletedEndPos
= lastEndPos
;
4936 if (wrapPosition
< GetRange().GetEnd()-1)
4938 // May need to set the node back to a previous one, due to searching back in wrapping
4939 wxRichTextObject
* childAfterWrapPosition
= FindObjectAtPosition(wrapPosition
+1);
4940 if (childAfterWrapPosition
)
4941 node
= m_children
.Find(childAfterWrapPosition
);
4943 node
= node
->GetNext();
4946 node
= node
->GetNext();
4948 // Apply paragraph styles such as alignment to the wrapped line
4949 ApplyParagraphStyle(line
, attr
, availableRect
, dc
);
4953 // We still fit, so don't add a line, and keep going
4954 currentWidth
+= childSize
.x
;
4956 if (childDescent
== 0)
4958 // An object with a zero descend value wants to take up the whole
4959 // height regardless of baseline
4960 lineHeight
= wxMax(lineHeight
, childSize
.y
);
4964 maxDescent
= wxMax(childDescent
, maxDescent
);
4965 maxAscent
= wxMax(childSize
.y
-childDescent
, maxAscent
);
4968 lineHeight
= wxMax(lineHeight
, (maxDescent
+ maxAscent
));
4970 maxWidth
= wxMax(maxWidth
, currentWidth
+startOffset
);
4971 lastEndPos
= child
->GetRange().GetEnd();
4973 node
= node
->GetNext();
4977 //wxASSERT(!(lastCompletedEndPos != -1 && lastCompletedEndPos < GetRange().GetEnd()-1));
4979 // Remove remaining unused line objects, if any
4980 ClearUnusedLines(lineCount
);
4982 // We need to add back the margins etc.
4984 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
4985 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxWidth
, currentPosition
.y
+ spaceAfterPara
));
4986 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
4987 SetCachedSize(marginRect
.GetSize());
4990 // The maximum size is the length of the paragraph stretched out into a line.
4991 // So if there were a single word, or an image, or a fixed-size text box, the object could be shrunk around
4992 // this size. TODO: take into account line breaks.
4994 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
4995 contentRect
= wxRect(wxPoint(0, 0), wxSize(paraSize
.x
+ wxMax(leftIndent
, leftIndent
+ leftSubIndent
) + rightIndent
, currentPosition
.y
+ spaceAfterPara
));
4996 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
4997 SetMaxSize(marginRect
.GetSize());
5000 // Find the greatest minimum size. Currently we only look at non-text objects,
5001 // which isn't ideal but it would be slow to find the maximum word width to
5002 // use as the minimum.
5005 node
= m_children
.GetFirst();
5008 wxRichTextObject
* child
= node
->GetData();
5010 // If floating, ignore. We already laid out floats.
5011 // Also ignore if empty object, except if we haven't got any
5013 if (!child
->IsFloating() && child
->GetRange().GetLength() != 0 && !wxDynamicCast(child
, wxRichTextPlainText
))
5015 if (child
->GetCachedSize().x
> minWidth
)
5016 minWidth
= child
->GetMinSize().x
;
5018 node
= node
->GetNext();
5021 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
5022 contentRect
= wxRect(wxPoint(0, 0), wxSize(minWidth
, currentPosition
.y
+ spaceAfterPara
));
5023 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
5024 SetMinSize(marginRect
.GetSize());
5027 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5028 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
5029 // Use the text extents to calculate the size of each fragment in each line
5030 wxRichTextLineList::compatibility_iterator lineNode
= m_cachedLines
.GetFirst();
5033 wxRichTextLine
* line
= lineNode
->GetData();
5034 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5036 // Loop through objects until we get to the one within range
5037 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
5041 wxRichTextObject
* child
= node2
->GetData();
5043 if (child
->GetRange().GetLength() > 0 && !child
->GetRange().IsOutside(lineRange
))
5045 wxRichTextRange rangeToUse
= lineRange
;
5046 rangeToUse
.LimitTo(child
->GetRange());
5048 // Find the size of the child from the text extents, and store in an array
5049 // for drawing later
5051 if (rangeToUse
.GetStart() > GetRange().GetStart())
5052 left
= partialExtents
[(rangeToUse
.GetStart()-1) - GetRange().GetStart()];
5053 int right
= partialExtents
[rangeToUse
.GetEnd() - GetRange().GetStart()];
5054 int sz
= right
- left
;
5055 line
->GetObjectSizes().Add(sz
);
5057 else if (child
->GetRange().GetStart() > lineRange
.GetEnd())
5058 // Can break out of inner loop now since we've passed this line's range
5061 node2
= node2
->GetNext();
5064 lineNode
= lineNode
->GetNext();
5072 /// Apply paragraph styles, such as centering, to wrapped lines
5073 /// TODO: take into account box attributes, possibly
5074 void wxRichTextParagraph::ApplyParagraphStyle(wxRichTextLine
* line
, const wxRichTextAttr
& attr
, const wxRect
& rect
, wxDC
& dc
)
5076 if (!attr
.HasAlignment())
5079 wxPoint pos
= line
->GetPosition();
5080 wxPoint originalPos
= pos
;
5081 wxSize size
= line
->GetSize();
5083 // centering, right-justification
5084 if (attr
.HasAlignment() && attr
.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE
)
5086 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
5087 pos
.x
= (rect
.GetWidth() - rightIndent
- size
.x
)/2 + pos
.x
;
5088 line
->SetPosition(pos
);
5090 else if (attr
.HasAlignment() && attr
.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT
)
5092 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
5093 pos
.x
= pos
.x
+ rect
.GetWidth() - size
.x
- rightIndent
;
5094 line
->SetPosition(pos
);
5097 if (pos
!= originalPos
)
5099 wxPoint inc
= pos
- originalPos
;
5101 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5105 wxRichTextObject
* child
= node
->GetData();
5106 if (child
->IsTopLevel() && !child
->GetRange().IsOutside(line
->GetAbsoluteRange()))
5107 child
->Move(child
->GetPosition() + inc
);
5109 node
= node
->GetNext();
5114 /// Insert text at the given position
5115 bool wxRichTextParagraph::InsertText(long pos
, const wxString
& text
)
5117 wxRichTextObject
* childToUse
= NULL
;
5118 wxRichTextObjectList::compatibility_iterator nodeToUse
= wxRichTextObjectList::compatibility_iterator();
5120 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5123 wxRichTextObject
* child
= node
->GetData();
5124 if (child
->GetRange().Contains(pos
) && child
->GetRange().GetLength() > 0)
5131 node
= node
->GetNext();
5136 wxRichTextPlainText
* textObject
= wxDynamicCast(childToUse
, wxRichTextPlainText
);
5139 int posInString
= pos
- textObject
->GetRange().GetStart();
5141 wxString newText
= textObject
->GetText().Mid(0, posInString
) +
5142 text
+ textObject
->GetText().Mid(posInString
);
5143 textObject
->SetText(newText
);
5145 int textLength
= text
.length();
5147 textObject
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart(),
5148 textObject
->GetRange().GetEnd() + textLength
));
5150 // Increment the end range of subsequent fragments in this paragraph.
5151 // We'll set the paragraph range itself at a higher level.
5153 wxRichTextObjectList::compatibility_iterator node
= nodeToUse
->GetNext();
5156 wxRichTextObject
* child
= node
->GetData();
5157 child
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart() + textLength
,
5158 textObject
->GetRange().GetEnd() + textLength
));
5160 node
= node
->GetNext();
5167 // TODO: if not a text object, insert at closest position, e.g. in front of it
5173 // Don't pass parent initially to suppress auto-setting of parent range.
5174 // We'll do that at a higher level.
5175 wxRichTextPlainText
* textObject
= new wxRichTextPlainText(text
, this);
5177 AppendChild(textObject
);
5184 void wxRichTextParagraph::Copy(const wxRichTextParagraph
& obj
)
5186 wxRichTextCompositeObject::Copy(obj
);
5189 /// Clear the cached lines
5190 void wxRichTextParagraph::ClearLines()
5192 WX_CLEAR_LIST(wxRichTextLineList
, m_cachedLines
);
5195 /// Get/set the object size for the given range. Returns false if the range
5196 /// is invalid for this object.
5197 bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
5199 if (!range
.IsWithin(GetRange()))
5202 if (flags
& wxRICHTEXT_UNFORMATTED
)
5204 // Just use unformatted data, assume no line breaks
5207 wxArrayInt childExtents
;
5216 int maxLineHeight
= 0;
5218 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5221 wxRichTextObject
* child
= node
->GetData();
5222 if (!child
->GetRange().IsOutside(range
))
5224 // Floating objects have a zero size within the paragraph.
5225 if (child
->IsFloating())
5230 if (partialExtents
->GetCount() > 0)
5231 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5235 partialExtents
->Add(0 /* zero size */ + lastSize
);
5242 wxRichTextRange rangeToUse
= range
;
5243 rangeToUse
.LimitTo(child
->GetRange());
5244 int childDescent
= 0;
5246 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we've already cached the size,
5247 // but it's only going to be used after caching has taken place.
5248 if ((flags
& wxRICHTEXT_HEIGHT_ONLY
) && child
->GetCachedSize().y
!= 0)
5250 childDescent
= child
->GetDescent();
5251 childSize
= child
->GetCachedSize();
5253 if (childDescent
== 0)
5255 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5259 maxDescent
= wxMax(maxDescent
, childDescent
);
5260 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5263 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5265 sz
.y
= wxMax(sz
.y
, maxLineHeight
);
5266 sz
.x
+= childSize
.x
;
5267 descent
= maxDescent
;
5269 else if (child
->IsTopLevel())
5271 childDescent
= child
->GetDescent();
5272 childSize
= child
->GetCachedSize();
5274 if (childDescent
== 0)
5276 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5280 maxDescent
= wxMax(maxDescent
, childDescent
);
5281 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5284 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5286 sz
.y
= wxMax(sz
.y
, maxLineHeight
);
5287 sz
.x
+= childSize
.x
;
5288 descent
= maxDescent
;
5290 // FIXME: this won't change the original values.
5291 // Should we be calling GetRangeSize above instead of using cached values?
5293 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange()))
5295 child
->SetCachedSize(childSize
);
5296 child
->SetDescent(childDescent
);
5303 if (partialExtents
->GetCount() > 0)
5304 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5308 partialExtents
->Add(childSize
.x
+ lastSize
);
5311 else if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, context
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
), p
))
5313 if (childDescent
== 0)
5315 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5319 maxDescent
= wxMax(maxDescent
, childDescent
);
5320 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5323 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5325 sz
.y
= wxMax(sz
.y
, maxLineHeight
);
5326 sz
.x
+= childSize
.x
;
5327 descent
= maxDescent
;
5329 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange()))
5331 child
->SetCachedSize(childSize
);
5332 child
->SetDescent(childDescent
);
5338 if (partialExtents
->GetCount() > 0)
5339 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5344 for (i
= 0; i
< childExtents
.GetCount(); i
++)
5346 partialExtents
->Add(childExtents
[i
] + lastSize
);
5356 node
= node
->GetNext();
5362 // Use formatted data, with line breaks
5365 // We're going to loop through each line, and then for each line,
5366 // call GetRangeSize for the fragment that comprises that line.
5367 // Only we have to do that multiple times within the line, because
5368 // the line may be broken into pieces. For now ignore line break commands
5369 // (so we can assume that getting the unformatted size for a fragment
5370 // within a line is the actual size)
5372 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5375 wxRichTextLine
* line
= node
->GetData();
5376 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5377 if (!lineRange
.IsOutside(range
))
5381 int maxLineHeight
= 0;
5382 int maxLineWidth
= 0;
5384 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
5387 wxRichTextObject
* child
= node2
->GetData();
5389 if (!child
->IsFloating() && !child
->GetRange().IsOutside(lineRange
))
5391 wxRichTextRange rangeToUse
= lineRange
;
5392 rangeToUse
.LimitTo(child
->GetRange());
5393 if (child
->IsTopLevel())
5394 rangeToUse
= child
->GetOwnRange();
5397 int childDescent
= 0;
5398 if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, context
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
)))
5400 if (childDescent
== 0)
5402 // Assume that if descent is zero, this child can occupy the full line height
5403 // and does not need space for the line's maximum descent. So we influence
5404 // the overall max line height only.
5405 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5409 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5410 maxDescent
= wxMax(maxAscent
, childDescent
);
5412 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5413 maxLineWidth
+= childSize
.x
;
5417 node2
= node2
->GetNext();
5420 descent
= wxMax(descent
, maxDescent
);
5422 // Increase size by a line (TODO: paragraph spacing)
5423 sz
.y
+= maxLineHeight
;
5424 sz
.x
= wxMax(sz
.x
, maxLineWidth
);
5426 node
= node
->GetNext();
5433 /// Finds the absolute position and row height for the given character position
5434 bool wxRichTextParagraph::FindPosition(wxDC
& dc
, wxRichTextDrawingContext
& context
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
5438 wxRichTextLine
* line
= ((wxRichTextParagraphLayoutBox
*)GetParent())->GetLineAtPosition(0);
5440 *height
= line
->GetSize().y
;
5442 *height
= dc
.GetCharHeight();
5444 // -1 means 'the start of the buffer'.
5447 pt
= pt
+ line
->GetPosition();
5452 // The final position in a paragraph is taken to mean the position
5453 // at the start of the next paragraph.
5454 if (index
== GetRange().GetEnd())
5456 wxRichTextParagraphLayoutBox
* parent
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
5457 wxASSERT( parent
!= NULL
);
5459 // Find the height at the next paragraph, if any
5460 wxRichTextLine
* line
= parent
->GetLineAtPosition(index
+ 1);
5463 *height
= line
->GetSize().y
;
5464 pt
= line
->GetAbsolutePosition();
5468 *height
= dc
.GetCharHeight();
5469 int indent
= ConvertTenthsMMToPixels(dc
, m_attributes
.GetLeftIndent());
5470 pt
= wxPoint(indent
, GetCachedSize().y
);
5476 if (index
< GetRange().GetStart() || index
> GetRange().GetEnd())
5479 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5482 wxRichTextLine
* line
= node
->GetData();
5483 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5484 if (index
>= lineRange
.GetStart() && index
<= lineRange
.GetEnd())
5486 // If this is the last point in the line, and we're forcing the
5487 // returned value to be the start of the next line, do the required
5489 if (index
== lineRange
.GetEnd() && forceLineStart
)
5491 if (node
->GetNext())
5493 wxRichTextLine
* nextLine
= node
->GetNext()->GetData();
5494 *height
= nextLine
->GetSize().y
;
5495 pt
= nextLine
->GetAbsolutePosition();
5500 pt
.y
= line
->GetPosition().y
+ GetPosition().y
;
5502 wxRichTextRange
r(lineRange
.GetStart(), index
);
5506 // We find the size of the line up to this point,
5507 // then we can add this size to the line start position and
5508 // paragraph start position to find the actual position.
5510 if (GetRangeSize(r
, rangeSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, line
->GetPosition()+ GetPosition()))
5512 pt
.x
= line
->GetPosition().x
+ GetPosition().x
+ rangeSize
.x
;
5513 *height
= line
->GetSize().y
;
5520 node
= node
->GetNext();
5526 /// Hit-testing: returns a flag indicating hit test details, plus
5527 /// information about position
5528 int wxRichTextParagraph::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
5531 return wxRICHTEXT_HITTEST_NONE
;
5533 // If we're in the top-level container, then we can return
5534 // a suitable hit test code even if the point is outside the container area,
5535 // so that we can position the caret sensibly even if we don't
5536 // click on valid content. If we're not at the top-level, and the point
5537 // is not within this paragraph object, then we don't want to stop more
5538 // precise hit-testing from working prematurely, so return immediately.
5539 // NEW STRATEGY: use the parent boundary to test whether we're in the
5540 // right region, not the paragraph, since the paragraph may be positioned
5541 // some way in from where the user clicks.
5544 wxRichTextObject
* tempObj
, *tempContextObj
;
5545 if (GetParent() && GetParent()->wxRichTextObject::HitTest(dc
, context
, pt
, tmpPos
, & tempObj
, & tempContextObj
, flags
) == wxRICHTEXT_HITTEST_NONE
)
5546 return wxRICHTEXT_HITTEST_NONE
;
5549 wxRichTextObjectList::compatibility_iterator objNode
= m_children
.GetFirst();
5552 wxRichTextObject
* child
= objNode
->GetData();
5553 // Don't recurse if we have wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS,
5554 // and also, if this seems composite but actually is marked as atomic,
5556 if (child
->IsTopLevel() && ((flags
& wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS
) == 0) &&
5557 (! (((flags
& wxRICHTEXT_HITTEST_HONOUR_ATOMIC
) != 0) && child
->IsAtomic())))
5560 int hitTest
= child
->HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
);
5561 if (hitTest
!= wxRICHTEXT_HITTEST_NONE
)
5566 objNode
= objNode
->GetNext();
5569 wxPoint paraPos
= GetPosition();
5571 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5574 wxRichTextLine
* line
= node
->GetData();
5575 wxPoint linePos
= paraPos
+ line
->GetPosition();
5576 wxSize lineSize
= line
->GetSize();
5577 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5579 if (pt
.y
<= linePos
.y
+ lineSize
.y
)
5581 if (pt
.x
< linePos
.x
)
5583 textPosition
= lineRange
.GetStart();
5584 *obj
= FindObjectAtPosition(textPosition
);
5585 *contextObj
= GetContainer();
5586 return wxRICHTEXT_HITTEST_BEFORE
|wxRICHTEXT_HITTEST_OUTSIDE
;
5588 else if (pt
.x
>= (linePos
.x
+ lineSize
.x
))
5590 textPosition
= lineRange
.GetEnd();
5591 *obj
= FindObjectAtPosition(textPosition
);
5592 *contextObj
= GetContainer();
5593 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
;
5597 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5598 wxArrayInt partialExtents
;
5603 // This calculates the partial text extents
5604 GetRangeSize(lineRange
, paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, linePos
, & partialExtents
);
5606 int lastX
= linePos
.x
;
5608 for (i
= 0; i
< partialExtents
.GetCount(); i
++)
5610 int nextX
= partialExtents
[i
] + linePos
.x
;
5612 if (pt
.x
>= lastX
&& pt
.x
<= nextX
)
5614 textPosition
= i
+ lineRange
.GetStart(); // minus 1?
5616 *obj
= FindObjectAtPosition(textPosition
);
5617 *contextObj
= GetContainer();
5619 // So now we know it's between i-1 and i.
5620 // Let's see if we can be more precise about
5621 // which side of the position it's on.
5623 int midPoint
= (nextX
+ lastX
)/2;
5624 if (pt
.x
>= midPoint
)
5625 return wxRICHTEXT_HITTEST_AFTER
;
5627 return wxRICHTEXT_HITTEST_BEFORE
;
5634 int lastX
= linePos
.x
;
5635 for (i
= lineRange
.GetStart(); i
<= lineRange
.GetEnd(); i
++)
5640 wxRichTextRange
rangeToUse(lineRange
.GetStart(), i
);
5642 GetRangeSize(rangeToUse
, childSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, linePos
);
5644 int nextX
= childSize
.x
+ linePos
.x
;
5646 if (pt
.x
>= lastX
&& pt
.x
<= nextX
)
5650 *obj
= FindObjectAtPosition(textPosition
);
5651 *contextObj
= GetContainer();
5653 // So now we know it's between i-1 and i.
5654 // Let's see if we can be more precise about
5655 // which side of the position it's on.
5657 int midPoint
= (nextX
+ lastX
)/2;
5658 if (pt
.x
>= midPoint
)
5659 return wxRICHTEXT_HITTEST_AFTER
;
5661 return wxRICHTEXT_HITTEST_BEFORE
;
5672 node
= node
->GetNext();
5675 return wxRICHTEXT_HITTEST_NONE
;
5678 /// Split an object at this position if necessary, and return
5679 /// the previous object, or NULL if inserting at beginning.
5680 wxRichTextObject
* wxRichTextParagraph::SplitAt(long pos
, wxRichTextObject
** previousObject
)
5682 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5685 wxRichTextObject
* child
= node
->GetData();
5687 if (pos
== child
->GetRange().GetStart())
5691 if (node
->GetPrevious())
5692 *previousObject
= node
->GetPrevious()->GetData();
5694 *previousObject
= NULL
;
5700 if (child
->GetRange().Contains(pos
))
5702 // This should create a new object, transferring part of
5703 // the content to the old object and the rest to the new object.
5704 wxRichTextObject
* newObject
= child
->DoSplit(pos
);
5706 // If we couldn't split this object, just insert in front of it.
5709 // Maybe this is an empty string, try the next one
5714 // Insert the new object after 'child'
5715 if (node
->GetNext())
5716 m_children
.Insert(node
->GetNext(), newObject
);
5718 m_children
.Append(newObject
);
5719 newObject
->SetParent(this);
5722 *previousObject
= child
;
5728 node
= node
->GetNext();
5731 *previousObject
= NULL
;
5735 /// Move content to a list from obj on
5736 void wxRichTextParagraph::MoveToList(wxRichTextObject
* obj
, wxList
& list
)
5738 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(obj
);
5741 wxRichTextObject
* child
= node
->GetData();
5744 wxRichTextObjectList::compatibility_iterator oldNode
= node
;
5746 node
= node
->GetNext();
5748 m_children
.DeleteNode(oldNode
);
5752 /// Add content back from list
5753 void wxRichTextParagraph::MoveFromList(wxList
& list
)
5755 for (wxList::compatibility_iterator node
= list
.GetFirst(); node
; node
= node
->GetNext())
5757 AppendChild((wxRichTextObject
*) node
->GetData());
5762 void wxRichTextParagraph::CalculateRange(long start
, long& end
)
5764 wxRichTextCompositeObject::CalculateRange(start
, end
);
5766 // Add one for end of paragraph
5769 m_range
.SetRange(start
, end
);
5772 /// Find the object at the given position
5773 wxRichTextObject
* wxRichTextParagraph::FindObjectAtPosition(long position
)
5775 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5778 wxRichTextObject
* obj
= node
->GetData();
5779 if (obj
->GetRange().Contains(position
) ||
5780 obj
->GetRange().GetStart() == position
||
5781 obj
->GetRange().GetEnd() == position
)
5784 node
= node
->GetNext();
5789 /// Get the plain text searching from the start or end of the range.
5790 /// The resulting string may be shorter than the range given.
5791 bool wxRichTextParagraph::GetContiguousPlainText(wxString
& text
, const wxRichTextRange
& range
, bool fromStart
)
5793 text
= wxEmptyString
;
5797 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5800 wxRichTextObject
* obj
= node
->GetData();
5801 if (!obj
->GetRange().IsOutside(range
))
5803 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5806 text
+= textObj
->GetTextForRange(range
);
5814 node
= node
->GetNext();
5819 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetLast();
5822 wxRichTextObject
* obj
= node
->GetData();
5823 if (!obj
->GetRange().IsOutside(range
))
5825 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5828 text
= textObj
->GetTextForRange(range
) + text
;
5832 text
= wxT(" ") + text
;
5836 node
= node
->GetPrevious();
5843 /// Find a suitable wrap position.
5844 bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange
& range
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int availableSpace
, long& wrapPosition
, wxArrayInt
* partialExtents
)
5846 if (range
.GetLength() <= 0)
5849 // Find the first position where the line exceeds the available space.
5851 long breakPosition
= range
.GetEnd();
5853 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5854 if (partialExtents
&& partialExtents
->GetCount() >= (size_t) (GetRange().GetLength()-1)) // the final position in a paragraph is the newline
5858 if (range
.GetStart() > GetRange().GetStart())
5859 widthBefore
= (*partialExtents
)[range
.GetStart() - GetRange().GetStart() - 1];
5864 for (i
= (size_t) range
.GetStart(); i
<= (size_t) range
.GetEnd(); i
++)
5866 int widthFromStartOfThisRange
= (*partialExtents
)[i
- GetRange().GetStart()] - widthBefore
;
5868 if (widthFromStartOfThisRange
> availableSpace
)
5870 breakPosition
= i
-1;
5878 // Binary chop for speed
5879 long minPos
= range
.GetStart();
5880 long maxPos
= range
.GetEnd();
5883 if (minPos
== maxPos
)
5886 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
5888 if (sz
.x
> availableSpace
)
5889 breakPosition
= minPos
- 1;
5892 else if ((maxPos
- minPos
) == 1)
5895 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
5897 if (sz
.x
> availableSpace
)
5898 breakPosition
= minPos
- 1;
5901 GetRangeSize(wxRichTextRange(range
.GetStart(), maxPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
5902 if (sz
.x
> availableSpace
)
5903 breakPosition
= maxPos
-1;
5909 long nextPos
= minPos
+ ((maxPos
- minPos
) / 2);
5912 GetRangeSize(wxRichTextRange(range
.GetStart(), nextPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
5914 if (sz
.x
> availableSpace
)
5926 // Now we know the last position on the line.
5927 // Let's try to find a word break.
5930 if (GetContiguousPlainText(plainText
, wxRichTextRange(range
.GetStart(), breakPosition
), false))
5932 int newLinePos
= plainText
.Find(wxRichTextLineBreakChar
);
5933 if (newLinePos
!= wxNOT_FOUND
)
5935 breakPosition
= wxMax(0, range
.GetStart() + newLinePos
);
5939 int spacePos
= plainText
.Find(wxT(' '), true);
5940 int tabPos
= plainText
.Find(wxT('\t'), true);
5941 int pos
= wxMax(spacePos
, tabPos
);
5942 if (pos
!= wxNOT_FOUND
)
5944 int positionsFromEndOfString
= plainText
.length() - pos
- 1;
5945 breakPosition
= breakPosition
- positionsFromEndOfString
;
5950 wrapPosition
= breakPosition
;
5955 /// Get the bullet text for this paragraph.
5956 wxString
wxRichTextParagraph::GetBulletText()
5958 if (GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
||
5959 (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
))
5960 return wxEmptyString
;
5962 int number
= GetAttributes().GetBulletNumber();
5965 if ((GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ARABIC
) || (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
))
5967 text
.Printf(wxT("%d"), number
);
5969 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_UPPER
)
5971 // TODO: Unicode, and also check if number > 26
5972 text
.Printf(wxT("%c"), (wxChar
) (number
+64));
5974 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_LOWER
)
5976 // TODO: Unicode, and also check if number > 26
5977 text
.Printf(wxT("%c"), (wxChar
) (number
+96));
5979 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER
)
5981 text
= wxRichTextDecimalToRoman(number
);
5983 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER
)
5985 text
= wxRichTextDecimalToRoman(number
);
5988 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
)
5990 text
= GetAttributes().GetBulletText();
5993 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
)
5995 // The outline style relies on the text being computed statically,
5996 // since it depends on other levels points (e.g. 1.2.1.1). So normally the bullet text
5997 // should be stored in the attributes; if not, just use the number for this
5998 // level, as previously computed.
5999 if (!GetAttributes().GetBulletText().IsEmpty())
6000 text
= GetAttributes().GetBulletText();
6003 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PARENTHESES
)
6005 text
= wxT("(") + text
+ wxT(")");
6007 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_RIGHT_PARENTHESIS
)
6009 text
= text
+ wxT(")");
6012 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PERIOD
)
6020 /// Allocate or reuse a line object
6021 wxRichTextLine
* wxRichTextParagraph::AllocateLine(int pos
)
6023 if (pos
< (int) m_cachedLines
.GetCount())
6025 wxRichTextLine
* line
= m_cachedLines
.Item(pos
)->GetData();
6031 wxRichTextLine
* line
= new wxRichTextLine(this);
6032 m_cachedLines
.Append(line
);
6037 /// Clear remaining unused line objects, if any
6038 bool wxRichTextParagraph::ClearUnusedLines(int lineCount
)
6040 int cachedLineCount
= m_cachedLines
.GetCount();
6041 if ((int) cachedLineCount
> lineCount
)
6043 for (int i
= 0; i
< (int) (cachedLineCount
- lineCount
); i
++)
6045 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetLast();
6046 wxRichTextLine
* line
= node
->GetData();
6047 m_cachedLines
.Erase(node
);
6054 /// Get combined attributes of the base style, paragraph style and character style. We use this to dynamically
6055 /// retrieve the actual style.
6056 wxRichTextAttr
wxRichTextParagraph::GetCombinedAttributes(const wxRichTextAttr
& contentStyle
, bool includingBoxAttr
) const
6058 wxRichTextAttr attr
;
6059 wxRichTextParagraphLayoutBox
* buf
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
6062 attr
= buf
->GetBasicStyle();
6063 if (!includingBoxAttr
)
6065 attr
.GetTextBoxAttr().Reset();
6066 // The background colour will be painted by the container, and we don't
6067 // want to unnecessarily overwrite the background when we're drawing text
6068 // because this may erase the guideline (which appears just under the text
6069 // if there's no padding).
6070 attr
.SetFlags(attr
.GetFlags() & ~wxTEXT_ATTR_BACKGROUND_COLOUR
);
6072 wxRichTextApplyStyle(attr
, GetAttributes());
6075 attr
= GetAttributes();
6077 wxRichTextApplyStyle(attr
, contentStyle
);
6081 /// Get combined attributes of the base style and paragraph style.
6082 wxRichTextAttr
wxRichTextParagraph::GetCombinedAttributes(bool includingBoxAttr
) const
6084 wxRichTextAttr attr
;
6085 wxRichTextParagraphLayoutBox
* buf
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
6088 attr
= buf
->GetBasicStyle();
6089 if (!includingBoxAttr
)
6090 attr
.GetTextBoxAttr().Reset();
6091 wxRichTextApplyStyle(attr
, GetAttributes());
6094 attr
= GetAttributes();
6099 // Create default tabstop array
6100 void wxRichTextParagraph::InitDefaultTabs()
6102 // create a default tab list at 10 mm each.
6103 for (int i
= 0; i
< 20; ++i
)
6105 sm_defaultTabs
.Add(i
*100);
6109 // Clear default tabstop array
6110 void wxRichTextParagraph::ClearDefaultTabs()
6112 sm_defaultTabs
.Clear();
6115 void wxRichTextParagraph::LayoutFloat(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, int style
, wxRichTextFloatCollector
* floatCollector
)
6117 wxRichTextObjectList::compatibility_iterator node
= GetChildren().GetFirst();
6120 wxRichTextObject
* anchored
= node
->GetData();
6121 if (anchored
&& anchored
->IsFloating() && !floatCollector
->HasFloat(anchored
))
6125 anchored
->GetRangeSize(anchored
->GetRange(), size
, descent
, dc
, context
, style
);
6128 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().IsValid())
6130 offsetY
= anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetValue();
6131 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
6133 offsetY
= ConvertTenthsMMToPixels(dc
, offsetY
);
6137 int pos
= floatCollector
->GetFitPosition(anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode(), rect
.y
+ offsetY
, size
.y
);
6139 /* Update the offset */
6140 int newOffsetY
= pos
- rect
.y
;
6141 if (newOffsetY
!= offsetY
)
6143 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
6144 newOffsetY
= ConvertPixelsToTenthsMM(dc
, newOffsetY
);
6145 anchored
->GetAttributes().GetTextBoxAttr().GetTop().SetValue(newOffsetY
);
6148 if (anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT
)
6150 else if (anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT
)
6151 x
= rect
.x
+ rect
.width
- size
.x
;
6153 anchored
->SetPosition(wxPoint(x
, pos
));
6154 anchored
->SetCachedSize(size
);
6155 floatCollector
->CollectFloat(this, anchored
);
6158 node
= node
->GetNext();
6162 // Get the first position from pos that has a line break character.
6163 long wxRichTextParagraph::GetFirstLineBreakPosition(long pos
)
6165 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
6168 wxRichTextObject
* obj
= node
->GetData();
6169 if (pos
>= obj
->GetRange().GetStart() && pos
<= obj
->GetRange().GetEnd())
6171 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
6174 long breakPos
= textObj
->GetFirstLineBreakPosition(pos
);
6179 node
= node
->GetNext();
6186 * This object represents a line in a paragraph, and stores
6187 * offsets from the start of the paragraph representing the
6188 * start and end positions of the line.
6191 wxRichTextLine::wxRichTextLine(wxRichTextParagraph
* parent
)
6197 void wxRichTextLine::Init(wxRichTextParagraph
* parent
)
6200 m_range
.SetRange(-1, -1);
6201 m_pos
= wxPoint(0, 0);
6202 m_size
= wxSize(0, 0);
6204 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6205 m_objectSizes
.Clear();
6210 void wxRichTextLine::Copy(const wxRichTextLine
& obj
)
6212 m_range
= obj
.m_range
;
6213 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6214 m_objectSizes
= obj
.m_objectSizes
;
6218 /// Get the absolute object position
6219 wxPoint
wxRichTextLine::GetAbsolutePosition() const
6221 return m_parent
->GetPosition() + m_pos
;
6224 /// Get the absolute range
6225 wxRichTextRange
wxRichTextLine::GetAbsoluteRange() const
6227 wxRichTextRange
range(m_range
.GetStart() + m_parent
->GetRange().GetStart(), 0);
6228 range
.SetEnd(range
.GetStart() + m_range
.GetLength()-1);
6233 * wxRichTextPlainText
6234 * This object represents a single piece of text.
6237 IMPLEMENT_DYNAMIC_CLASS(wxRichTextPlainText
, wxRichTextObject
)
6239 wxRichTextPlainText::wxRichTextPlainText(const wxString
& text
, wxRichTextObject
* parent
, wxRichTextAttr
* style
):
6240 wxRichTextObject(parent
)
6243 SetAttributes(*style
);
6248 #define USE_KERNING_FIX 1
6250 // If insufficient tabs are defined, this is the tab width used
6251 #define WIDTH_FOR_DEFAULT_TABS 50
6254 bool wxRichTextPlainText::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int WXUNUSED(style
))
6256 wxRichTextParagraph
* para
= wxDynamicCast(GetParent(), wxRichTextParagraph
);
6257 wxASSERT (para
!= NULL
);
6259 wxRichTextAttr
textAttr(para
? para
->GetCombinedAttributes(GetAttributes(), false /* no box attributes */) : GetAttributes());
6260 context
.ApplyVirtualAttributes(textAttr
, this);
6262 // Let's make the assumption for now that for content in a paragraph, including
6263 // text, we never have a discontinuous selection. So we only deal with a
6265 wxRichTextRange selectionRange
;
6266 if (selection
.IsValid())
6268 wxRichTextRangeArray selectionRanges
= selection
.GetSelectionForObject(this);
6269 if (selectionRanges
.GetCount() > 0)
6270 selectionRange
= selectionRanges
[0];
6272 selectionRange
= wxRICHTEXT_NO_SELECTION
;
6275 selectionRange
= wxRICHTEXT_NO_SELECTION
;
6277 int offset
= GetRange().GetStart();
6279 // Replace line break characters with spaces
6280 wxString str
= m_text
;
6281 wxString toRemove
= wxRichTextLineBreakChar
;
6282 str
.Replace(toRemove
, wxT(" "));
6283 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS
))
6286 long len
= range
.GetLength();
6287 wxString stringChunk
= str
.Mid(range
.GetStart() - offset
, (size_t) len
);
6289 // Test for the optimized situations where all is selected, or none
6292 wxFont
textFont(GetBuffer()->GetFontTable().FindFont(textAttr
));
6293 wxCheckSetFont(dc
, textFont
);
6294 int charHeight
= dc
.GetCharHeight();
6297 if ( textFont
.IsOk() )
6299 if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
) )
6301 if (textFont
.IsUsingSizeInPixels())
6303 double size
= static_cast<double>(textFont
.GetPixelSize().y
) / wxSCRIPT_MUL_FACTOR
;
6304 textFont
.SetPixelSize(wxSize(0, static_cast<int>(size
)));
6310 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6311 textFont
.SetPointSize(static_cast<int>(size
));
6315 wxCheckSetFont(dc
, textFont
);
6317 else if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) )
6319 if (textFont
.IsUsingSizeInPixels())
6321 double size
= static_cast<double>(textFont
.GetPixelSize().y
) / wxSCRIPT_MUL_FACTOR
;
6322 textFont
.SetPixelSize(wxSize(0, static_cast<int>(size
)));
6324 int sub_height
= static_cast<int>(static_cast<double>(charHeight
) / wxSCRIPT_MUL_FACTOR
);
6325 y
= rect
.y
+ (rect
.height
- sub_height
+ (descent
- m_descent
));
6329 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6330 textFont
.SetPointSize(static_cast<int>(size
));
6332 int sub_height
= static_cast<int>(static_cast<double>(charHeight
) / wxSCRIPT_MUL_FACTOR
);
6333 y
= rect
.y
+ (rect
.height
- sub_height
+ (descent
- m_descent
));
6335 wxCheckSetFont(dc
, textFont
);
6340 y
= rect
.y
+ (rect
.height
- charHeight
- (descent
- m_descent
));
6346 y
= rect
.y
+ (rect
.height
- charHeight
- (descent
- m_descent
));
6349 // TODO: new selection code
6351 // (a) All selected.
6352 if (selectionRange
.GetStart() <= range
.GetStart() && selectionRange
.GetEnd() >= range
.GetEnd())
6354 DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, true);
6356 // (b) None selected.
6357 else if (selectionRange
.GetEnd() < range
.GetStart() || selectionRange
.GetStart() > range
.GetEnd())
6359 // Draw all unselected
6360 DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, false);
6364 // (c) Part selected, part not
6365 // Let's draw unselected chunk, selected chunk, then unselected chunk.
6367 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6369 // 1. Initial unselected chunk, if any, up until start of selection.
6370 if (selectionRange
.GetStart() > range
.GetStart() && selectionRange
.GetStart() <= range
.GetEnd())
6372 int r1
= range
.GetStart();
6373 int s1
= selectionRange
.GetStart()-1;
6374 int fragmentLen
= s1
- r1
+ 1;
6375 if (fragmentLen
< 0)
6377 wxLogDebug(wxT("Mid(%d, %d"), (int)(r1
- offset
), (int)fragmentLen
);
6379 wxString stringFragment
= str
.Mid(r1
- offset
, fragmentLen
);
6381 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false);
6384 if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
)
6386 // Compensate for kerning difference
6387 wxString
stringFragment2(str
.Mid(r1
- offset
, fragmentLen
+1));
6388 wxString
stringFragment3(str
.Mid(r1
- offset
+ fragmentLen
, 1));
6390 wxCoord w1
, h1
, w2
, h2
, w3
, h3
;
6391 dc
.GetTextExtent(stringFragment
, & w1
, & h1
);
6392 dc
.GetTextExtent(stringFragment2
, & w2
, & h2
);
6393 dc
.GetTextExtent(stringFragment3
, & w3
, & h3
);
6395 int kerningDiff
= (w1
+ w3
) - w2
;
6396 x
= x
- kerningDiff
;
6401 // 2. Selected chunk, if any.
6402 if (selectionRange
.GetEnd() >= range
.GetStart())
6404 int s1
= wxMax(selectionRange
.GetStart(), range
.GetStart());
6405 int s2
= wxMin(selectionRange
.GetEnd(), range
.GetEnd());
6407 int fragmentLen
= s2
- s1
+ 1;
6408 if (fragmentLen
< 0)
6410 wxLogDebug(wxT("Mid(%d, %d"), (int)(s1
- offset
), (int)fragmentLen
);
6412 wxString stringFragment
= str
.Mid(s1
- offset
, fragmentLen
);
6414 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, true);
6417 if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
)
6419 // Compensate for kerning difference
6420 wxString
stringFragment2(str
.Mid(s1
- offset
, fragmentLen
+1));
6421 wxString
stringFragment3(str
.Mid(s1
- offset
+ fragmentLen
, 1));
6423 wxCoord w1
, h1
, w2
, h2
, w3
, h3
;
6424 dc
.GetTextExtent(stringFragment
, & w1
, & h1
);
6425 dc
.GetTextExtent(stringFragment2
, & w2
, & h2
);
6426 dc
.GetTextExtent(stringFragment3
, & w3
, & h3
);
6428 int kerningDiff
= (w1
+ w3
) - w2
;
6429 x
= x
- kerningDiff
;
6434 // 3. Remaining unselected chunk, if any
6435 if (selectionRange
.GetEnd() < range
.GetEnd())
6437 int s2
= wxMin(selectionRange
.GetEnd()+1, range
.GetEnd());
6438 int r2
= range
.GetEnd();
6440 int fragmentLen
= r2
- s2
+ 1;
6441 if (fragmentLen
< 0)
6443 wxLogDebug(wxT("Mid(%d, %d"), (int)(s2
- offset
), (int)fragmentLen
);
6445 wxString stringFragment
= str
.Mid(s2
- offset
, fragmentLen
);
6447 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false);
6454 bool wxRichTextPlainText::DrawTabbedString(wxDC
& dc
, const wxRichTextAttr
& attr
, const wxRect
& rect
,wxString
& str
, wxCoord
& x
, wxCoord
& y
, bool selected
)
6456 bool hasTabs
= (str
.Find(wxT('\t')) != wxNOT_FOUND
);
6458 wxArrayInt tabArray
;
6462 if (attr
.GetTabs().IsEmpty())
6463 tabArray
= wxRichTextParagraph::GetDefaultTabs();
6465 tabArray
= attr
.GetTabs();
6466 tabCount
= tabArray
.GetCount();
6468 for (int i
= 0; i
< tabCount
; ++i
)
6470 int pos
= tabArray
[i
];
6471 pos
= ConvertTenthsMMToPixels(dc
, pos
);
6478 int nextTabPos
= -1;
6484 wxColour
highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
));
6485 wxColour
highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
));
6487 wxCheckSetBrush(dc
, wxBrush(highlightColour
));
6488 wxCheckSetPen(dc
, wxPen(highlightColour
));
6489 dc
.SetTextForeground(highlightTextColour
);
6490 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6494 dc
.SetTextForeground(attr
.GetTextColour());
6496 if (attr
.HasFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
) && attr
.GetBackgroundColour().IsOk())
6498 dc
.SetBackgroundMode(wxBRUSHSTYLE_SOLID
);
6499 dc
.SetTextBackground(attr
.GetBackgroundColour());
6502 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6505 wxCoord x_orig
= GetParent()->GetPosition().x
;
6508 // the string has a tab
6509 // break up the string at the Tab
6510 wxString stringChunk
= str
.BeforeFirst(wxT('\t'));
6511 str
= str
.AfterFirst(wxT('\t'));
6512 dc
.GetTextExtent(stringChunk
, & w
, & h
);
6514 bool not_found
= true;
6515 for (int i
= 0; i
< tabCount
&& not_found
; ++i
)
6517 nextTabPos
= tabArray
.Item(i
) + x_orig
;
6519 // Find the next tab position.
6520 // Even if we're at the end of the tab array, we must still draw the chunk.
6522 if (nextTabPos
> tabPos
|| (i
== (tabCount
- 1)))
6524 if (nextTabPos
<= tabPos
)
6526 int defaultTabWidth
= ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
);
6527 nextTabPos
= tabPos
+ defaultTabWidth
;
6534 wxRect
selRect(x
, rect
.y
, w
, rect
.GetHeight());
6535 dc
.DrawRectangle(selRect
);
6537 dc
.DrawText(stringChunk
, x
, y
);
6539 if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
))
6541 wxPen oldPen
= dc
.GetPen();
6542 wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1));
6543 dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5));
6544 wxCheckSetPen(dc
, oldPen
);
6550 hasTabs
= (str
.Find(wxT('\t')) != wxNOT_FOUND
);
6555 dc
.GetTextExtent(str
, & w
, & h
);
6558 wxRect
selRect(x
, rect
.y
, w
, rect
.GetHeight());
6559 dc
.DrawRectangle(selRect
);
6561 dc
.DrawText(str
, x
, y
);
6563 if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
))
6565 wxPen oldPen
= dc
.GetPen();
6566 wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1));
6567 dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5));
6568 wxCheckSetPen(dc
, oldPen
);
6577 /// Lay the item out
6578 bool wxRichTextPlainText::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& WXUNUSED(rect
), const wxRect
& WXUNUSED(parentRect
), int WXUNUSED(style
))
6580 // Only lay out if we haven't already cached the size
6582 GetRangeSize(GetRange(), m_size
, m_descent
, dc
, context
, 0, wxPoint(0, 0));
6584 // Eventually we want to have a reasonable estimate of minimum size.
6585 m_minSize
= wxSize(0, 0);
6590 void wxRichTextPlainText::Copy(const wxRichTextPlainText
& obj
)
6592 wxRichTextObject::Copy(obj
);
6594 m_text
= obj
.m_text
;
6597 /// Get/set the object size for the given range. Returns false if the range
6598 /// is invalid for this object.
6599 bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int WXUNUSED(flags
), wxPoint position
, wxArrayInt
* partialExtents
) const
6601 if (!range
.IsWithin(GetRange()))
6604 wxRichTextParagraph
* para
= wxDynamicCast(GetParent(), wxRichTextParagraph
);
6605 wxASSERT (para
!= NULL
);
6607 int relativeX
= position
.x
- GetParent()->GetPosition().x
;
6609 wxRichTextAttr
textAttr(para
? para
->GetCombinedAttributes(GetAttributes()) : GetAttributes());
6610 context
.ApplyVirtualAttributes(textAttr
, (wxRichTextObject
*) this);
6612 // Always assume unformatted text, since at this level we have no knowledge
6613 // of line breaks - and we don't need it, since we'll calculate size within
6614 // formatted text by doing it in chunks according to the line ranges
6616 bool bScript(false);
6617 wxFont
font(GetBuffer()->GetFontTable().FindFont(textAttr
));
6620 if ( textAttr
.HasTextEffects() && ( (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
)
6621 || (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) ) )
6623 wxFont textFont
= font
;
6624 if (textFont
.IsUsingSizeInPixels())
6626 double size
= static_cast<double>(textFont
.GetPixelSize().y
) / wxSCRIPT_MUL_FACTOR
;
6627 textFont
.SetPixelSize(wxSize(0, static_cast<int>(size
)));
6631 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6632 textFont
.SetPointSize(static_cast<int>(size
));
6634 wxCheckSetFont(dc
, textFont
);
6639 wxCheckSetFont(dc
, font
);
6643 bool haveDescent
= false;
6644 int startPos
= range
.GetStart() - GetRange().GetStart();
6645 long len
= range
.GetLength();
6647 wxString
str(m_text
);
6648 wxString toReplace
= wxRichTextLineBreakChar
;
6649 str
.Replace(toReplace
, wxT(" "));
6651 wxString stringChunk
= str
.Mid(startPos
, (size_t) len
);
6653 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS
))
6654 stringChunk
.MakeUpper();
6658 if (stringChunk
.Find(wxT('\t')) != wxNOT_FOUND
)
6660 // the string has a tab
6661 wxArrayInt tabArray
;
6662 if (textAttr
.GetTabs().IsEmpty())
6663 tabArray
= wxRichTextParagraph::GetDefaultTabs();
6665 tabArray
= textAttr
.GetTabs();
6667 int tabCount
= tabArray
.GetCount();
6669 for (int i
= 0; i
< tabCount
; ++i
)
6671 int pos
= tabArray
[i
];
6672 pos
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, pos
);
6676 int nextTabPos
= -1;
6678 while (stringChunk
.Find(wxT('\t')) >= 0)
6680 int absoluteWidth
= 0;
6682 // the string has a tab
6683 // break up the string at the Tab
6684 wxString stringFragment
= stringChunk
.BeforeFirst(wxT('\t'));
6685 stringChunk
= stringChunk
.AfterFirst(wxT('\t'));
6690 if (partialExtents
->GetCount() > 0)
6691 oldWidth
= (*partialExtents
)[partialExtents
->GetCount()-1];
6695 // Add these partial extents
6697 dc
.GetPartialTextExtents(stringFragment
, p
);
6699 for (j
= 0; j
< p
.GetCount(); j
++)
6700 partialExtents
->Add(oldWidth
+ p
[j
]);
6702 if (partialExtents
->GetCount() > 0)
6703 absoluteWidth
= (*partialExtents
)[(*partialExtents
).GetCount()-1] + relativeX
;
6705 absoluteWidth
= relativeX
;
6709 dc
.GetTextExtent(stringFragment
, & w
, & h
);
6711 absoluteWidth
= width
+ relativeX
;
6715 bool notFound
= true;
6716 for (int i
= 0; i
< tabCount
&& notFound
; ++i
)
6718 nextTabPos
= tabArray
.Item(i
);
6720 // Find the next tab position.
6721 // Even if we're at the end of the tab array, we must still process the chunk.
6723 if (nextTabPos
> absoluteWidth
|| (i
== (tabCount
- 1)))
6725 if (nextTabPos
<= absoluteWidth
)
6727 int defaultTabWidth
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
);
6728 nextTabPos
= absoluteWidth
+ defaultTabWidth
;
6732 width
= nextTabPos
- relativeX
;
6735 partialExtents
->Add(width
);
6741 if (!stringChunk
.IsEmpty())
6746 if (partialExtents
->GetCount() > 0)
6747 oldWidth
= (*partialExtents
)[partialExtents
->GetCount()-1];
6751 // Add these partial extents
6753 dc
.GetPartialTextExtents(stringChunk
, p
);
6755 for (j
= 0; j
< p
.GetCount(); j
++)
6756 partialExtents
->Add(oldWidth
+ p
[j
]);
6760 dc
.GetTextExtent(stringChunk
, & w
, & h
, & descent
);
6768 int charHeight
= dc
.GetCharHeight();
6769 if ((*partialExtents
).GetCount() > 0)
6770 w
= (*partialExtents
)[partialExtents
->GetCount()-1];
6773 size
= wxSize(w
, charHeight
);
6777 size
= wxSize(width
, dc
.GetCharHeight());
6781 dc
.GetTextExtent(wxT("X"), & w
, & h
, & descent
);
6789 /// Do a split, returning an object containing the second part, and setting
6790 /// the first part in 'this'.
6791 wxRichTextObject
* wxRichTextPlainText::DoSplit(long pos
)
6793 long index
= pos
- GetRange().GetStart();
6795 if (index
< 0 || index
>= (int) m_text
.length())
6798 wxString firstPart
= m_text
.Mid(0, index
);
6799 wxString secondPart
= m_text
.Mid(index
);
6803 wxRichTextPlainText
* newObject
= new wxRichTextPlainText(secondPart
);
6804 newObject
->SetAttributes(GetAttributes());
6805 newObject
->SetProperties(GetProperties());
6807 newObject
->SetRange(wxRichTextRange(pos
, GetRange().GetEnd()));
6808 GetRange().SetEnd(pos
-1);
6814 void wxRichTextPlainText::CalculateRange(long start
, long& end
)
6816 end
= start
+ m_text
.length() - 1;
6817 m_range
.SetRange(start
, end
);
6821 bool wxRichTextPlainText::DeleteRange(const wxRichTextRange
& range
)
6823 wxRichTextRange r
= range
;
6825 r
.LimitTo(GetRange());
6827 if (r
.GetStart() == GetRange().GetStart() && r
.GetEnd() == GetRange().GetEnd())
6833 long startIndex
= r
.GetStart() - GetRange().GetStart();
6834 long len
= r
.GetLength();
6836 m_text
= m_text
.Mid(0, startIndex
) + m_text
.Mid(startIndex
+len
);
6840 /// Get text for the given range.
6841 wxString
wxRichTextPlainText::GetTextForRange(const wxRichTextRange
& range
) const
6843 wxRichTextRange r
= range
;
6845 r
.LimitTo(GetRange());
6847 long startIndex
= r
.GetStart() - GetRange().GetStart();
6848 long len
= r
.GetLength();
6850 return m_text
.Mid(startIndex
, len
);
6853 /// Returns true if this object can merge itself with the given one.
6854 bool wxRichTextPlainText::CanMerge(wxRichTextObject
* object
) const
6856 return object
->GetClassInfo() == wxCLASSINFO(wxRichTextPlainText
) &&
6857 (m_text
.empty() || (wxTextAttrEq(GetAttributes(), object
->GetAttributes()) && m_properties
== object
->GetProperties()));
6860 /// Returns true if this object merged itself with the given one.
6861 /// The calling code will then delete the given object.
6862 bool wxRichTextPlainText::Merge(wxRichTextObject
* object
)
6864 wxRichTextPlainText
* textObject
= wxDynamicCast(object
, wxRichTextPlainText
);
6865 wxASSERT( textObject
!= NULL
);
6869 m_text
+= textObject
->GetText();
6870 wxRichTextApplyStyle(m_attributes
, textObject
->GetAttributes());
6877 /// Dump to output stream for debugging
6878 void wxRichTextPlainText::Dump(wxTextOutputStream
& stream
)
6880 wxRichTextObject::Dump(stream
);
6881 stream
<< m_text
<< wxT("\n");
6884 /// Get the first position from pos that has a line break character.
6885 long wxRichTextPlainText::GetFirstLineBreakPosition(long pos
)
6888 int len
= m_text
.length();
6889 int startPos
= pos
- m_range
.GetStart();
6890 for (i
= startPos
; i
< len
; i
++)
6892 wxChar ch
= m_text
[i
];
6893 if (ch
== wxRichTextLineBreakChar
)
6895 return i
+ m_range
.GetStart();
6903 * This is a kind of box, used to represent the whole buffer
6906 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBuffer
, wxRichTextParagraphLayoutBox
)
6908 wxList
wxRichTextBuffer::sm_handlers
;
6909 wxList
wxRichTextBuffer::sm_drawingHandlers
;
6910 wxRichTextFieldTypeHashMap
wxRichTextBuffer::sm_fieldTypes
;
6911 wxRichTextRenderer
* wxRichTextBuffer::sm_renderer
= NULL
;
6912 int wxRichTextBuffer::sm_bulletRightMargin
= 20;
6913 float wxRichTextBuffer::sm_bulletProportion
= (float) 0.3;
6916 void wxRichTextBuffer::Init()
6918 m_commandProcessor
= new wxCommandProcessor
;
6919 m_styleSheet
= NULL
;
6921 m_batchedCommandDepth
= 0;
6922 m_batchedCommand
= NULL
;
6926 m_dimensionScale
= 1.0;
6932 wxRichTextBuffer::~wxRichTextBuffer()
6934 delete m_commandProcessor
;
6935 delete m_batchedCommand
;
6938 ClearEventHandlers();
6941 void wxRichTextBuffer::ResetAndClearCommands()
6945 GetCommandProcessor()->ClearCommands();
6948 Invalidate(wxRICHTEXT_ALL
);
6951 void wxRichTextBuffer::Copy(const wxRichTextBuffer
& obj
)
6953 wxRichTextParagraphLayoutBox::Copy(obj
);
6955 m_styleSheet
= obj
.m_styleSheet
;
6956 m_modified
= obj
.m_modified
;
6957 m_batchedCommandDepth
= 0;
6958 if (m_batchedCommand
)
6959 delete m_batchedCommand
;
6960 m_batchedCommand
= NULL
;
6961 m_suppressUndo
= obj
.m_suppressUndo
;
6962 m_invalidRange
= obj
.m_invalidRange
;
6963 m_dimensionScale
= obj
.m_dimensionScale
;
6964 m_fontScale
= obj
.m_fontScale
;
6967 /// Push style sheet to top of stack
6968 bool wxRichTextBuffer::PushStyleSheet(wxRichTextStyleSheet
* styleSheet
)
6971 styleSheet
->InsertSheet(m_styleSheet
);
6973 SetStyleSheet(styleSheet
);
6978 /// Pop style sheet from top of stack
6979 wxRichTextStyleSheet
* wxRichTextBuffer::PopStyleSheet()
6983 wxRichTextStyleSheet
* oldSheet
= m_styleSheet
;
6984 m_styleSheet
= oldSheet
->GetNextSheet();
6993 /// Submit command to insert paragraphs
6994 bool wxRichTextBuffer::InsertParagraphsWithUndo(long pos
, const wxRichTextParagraphLayoutBox
& paragraphs
, wxRichTextCtrl
* ctrl
, int flags
)
6996 return ctrl
->GetFocusObject()->InsertParagraphsWithUndo(this, pos
, paragraphs
, ctrl
, flags
);
6999 /// Submit command to insert paragraphs
7000 bool wxRichTextParagraphLayoutBox::InsertParagraphsWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxRichTextParagraphLayoutBox
& paragraphs
, wxRichTextCtrl
* ctrl
, int WXUNUSED(flags
))
7002 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7004 action
->GetNewParagraphs() = paragraphs
;
7006 action
->SetPosition(pos
);
7008 wxRichTextRange range
= wxRichTextRange(pos
, pos
+ paragraphs
.GetOwnRange().GetEnd() - 1);
7009 if (!paragraphs
.GetPartialParagraph())
7010 range
.SetEnd(range
.GetEnd()+1);
7012 // Set the range we'll need to delete in Undo
7013 action
->SetRange(range
);
7015 buffer
->SubmitAction(action
);
7020 /// Submit command to insert the given text
7021 bool wxRichTextBuffer::InsertTextWithUndo(long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
, int flags
)
7023 return ctrl
->GetFocusObject()->InsertTextWithUndo(this, pos
, text
, ctrl
, flags
);
7026 /// Submit command to insert the given text
7027 bool wxRichTextParagraphLayoutBox::InsertTextWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
, int flags
)
7029 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7031 wxRichTextAttr
* p
= NULL
;
7032 wxRichTextAttr paraAttr
;
7033 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7035 // Get appropriate paragraph style
7036 paraAttr
= GetStyleForNewParagraph(buffer
, pos
, false, false);
7037 if (!paraAttr
.IsDefault())
7041 action
->GetNewParagraphs().AddParagraphs(text
, p
);
7043 int length
= action
->GetNewParagraphs().GetOwnRange().GetLength();
7045 if (!text
.empty() && text
.Last() != wxT('\n'))
7047 // Don't count the newline when undoing
7049 action
->GetNewParagraphs().SetPartialParagraph(true);
7051 else if (!text
.empty() && text
.Last() == wxT('\n'))
7054 action
->SetPosition(pos
);
7056 // Set the range we'll need to delete in Undo
7057 action
->SetRange(wxRichTextRange(pos
, pos
+ length
- 1));
7059 buffer
->SubmitAction(action
);
7064 /// Submit command to insert the given text
7065 bool wxRichTextBuffer::InsertNewlineWithUndo(long pos
, wxRichTextCtrl
* ctrl
, int flags
)
7067 return ctrl
->GetFocusObject()->InsertNewlineWithUndo(this, pos
, ctrl
, flags
);
7070 /// Submit command to insert the given text
7071 bool wxRichTextParagraphLayoutBox::InsertNewlineWithUndo(wxRichTextBuffer
* buffer
, long pos
, wxRichTextCtrl
* ctrl
, int flags
)
7073 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7075 wxRichTextAttr
* p
= NULL
;
7076 wxRichTextAttr paraAttr
;
7077 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7079 paraAttr
= GetStyleForNewParagraph(buffer
, pos
, false, true /* look for next paragraph style */);
7080 if (!paraAttr
.IsDefault())
7084 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7085 // Don't include box attributes such as margins
7086 attr
.GetTextBoxAttr().Reset();
7088 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(wxEmptyString
, this, & attr
);
7089 action
->GetNewParagraphs().AppendChild(newPara
);
7090 action
->GetNewParagraphs().UpdateRanges();
7091 action
->GetNewParagraphs().SetPartialParagraph(false);
7092 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
, false);
7096 newPara
->SetAttributes(*p
);
7098 if (flags
& wxRICHTEXT_INSERT_INTERACTIVE
)
7100 if (para
&& para
->GetRange().GetEnd() == pos
)
7103 // Now see if we need to number the paragraph.
7104 if (newPara
->GetAttributes().HasBulletNumber())
7106 wxRichTextAttr numberingAttr
;
7107 if (FindNextParagraphNumber(para
, numberingAttr
))
7108 wxRichTextApplyStyle(newPara
->GetAttributes(), (const wxRichTextAttr
&) numberingAttr
);
7112 action
->SetPosition(pos
);
7114 // Use the default character style
7115 if (!buffer
->GetDefaultStyle().IsDefault() && newPara
->GetChildren().GetFirst())
7117 // Check whether the default style merely reflects the paragraph/basic style,
7118 // in which case don't apply it.
7119 wxRichTextAttr
defaultStyle(buffer
->GetDefaultStyle());
7120 defaultStyle
.GetTextBoxAttr().Reset();
7121 wxRichTextAttr toApply
;
7124 wxRichTextAttr combinedAttr
= para
->GetCombinedAttributes(true /* include box attributes */);
7125 wxRichTextAttr newAttr
;
7126 // This filters out attributes that are accounted for by the current
7127 // paragraph/basic style
7128 wxRichTextApplyStyle(toApply
, defaultStyle
, & combinedAttr
);
7131 toApply
= defaultStyle
;
7133 if (!toApply
.IsDefault())
7134 newPara
->GetChildren().GetFirst()->GetData()->SetAttributes(toApply
);
7137 // Set the range we'll need to delete in Undo
7138 action
->SetRange(wxRichTextRange(pos1
, pos1
));
7140 buffer
->SubmitAction(action
);
7145 /// Submit command to insert the given image
7146 bool wxRichTextBuffer::InsertImageWithUndo(long pos
, const wxRichTextImageBlock
& imageBlock
, wxRichTextCtrl
* ctrl
, int flags
,
7147 const wxRichTextAttr
& textAttr
)
7149 return ctrl
->GetFocusObject()->InsertImageWithUndo(this, pos
, imageBlock
, ctrl
, flags
, textAttr
);
7152 /// Submit command to insert the given image
7153 bool wxRichTextParagraphLayoutBox::InsertImageWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxRichTextImageBlock
& imageBlock
,
7154 wxRichTextCtrl
* ctrl
, int flags
,
7155 const wxRichTextAttr
& textAttr
)
7157 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7159 wxRichTextAttr
* p
= NULL
;
7160 wxRichTextAttr paraAttr
;
7161 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7163 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
7164 if (!paraAttr
.IsDefault())
7168 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7170 // Don't include box attributes such as margins
7171 attr
.GetTextBoxAttr().Reset();
7173 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7175 newPara
->SetAttributes(*p
);
7177 wxRichTextImage
* imageObject
= new wxRichTextImage(imageBlock
, newPara
);
7178 newPara
->AppendChild(imageObject
);
7179 imageObject
->SetAttributes(textAttr
);
7180 action
->GetNewParagraphs().AppendChild(newPara
);
7181 action
->GetNewParagraphs().UpdateRanges();
7183 action
->GetNewParagraphs().SetPartialParagraph(true);
7185 action
->SetPosition(pos
);
7187 // Set the range we'll need to delete in Undo
7188 action
->SetRange(wxRichTextRange(pos
, pos
));
7190 buffer
->SubmitAction(action
);
7195 // Insert an object with no change of it
7196 wxRichTextObject
* wxRichTextBuffer::InsertObjectWithUndo(long pos
, wxRichTextObject
*object
, wxRichTextCtrl
* ctrl
, int flags
)
7198 return ctrl
->GetFocusObject()->InsertObjectWithUndo(this, pos
, object
, ctrl
, flags
);
7201 // Insert an object with no change of it
7202 wxRichTextObject
* wxRichTextParagraphLayoutBox::InsertObjectWithUndo(wxRichTextBuffer
* buffer
, long pos
, wxRichTextObject
*object
, wxRichTextCtrl
* ctrl
, int flags
)
7204 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Object"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7206 wxRichTextAttr
* p
= NULL
;
7207 wxRichTextAttr paraAttr
;
7208 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7210 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
7211 if (!paraAttr
.IsDefault())
7215 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7217 // Don't include box attributes such as margins
7218 attr
.GetTextBoxAttr().Reset();
7220 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7222 newPara
->SetAttributes(*p
);
7224 newPara
->AppendChild(object
);
7225 action
->GetNewParagraphs().AppendChild(newPara
);
7226 action
->GetNewParagraphs().UpdateRanges();
7228 action
->GetNewParagraphs().SetPartialParagraph(true);
7230 action
->SetPosition(pos
);
7232 // Set the range we'll need to delete in Undo
7233 action
->SetRange(wxRichTextRange(pos
, pos
));
7235 buffer
->SubmitAction(action
);
7237 wxRichTextObject
* obj
= GetLeafObjectAtPosition(pos
);
7241 wxRichTextField
* wxRichTextParagraphLayoutBox::InsertFieldWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxString
& fieldType
,
7242 const wxRichTextProperties
& properties
,
7243 wxRichTextCtrl
* ctrl
, int flags
,
7244 const wxRichTextAttr
& textAttr
)
7246 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Field"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7248 wxRichTextAttr
* p
= NULL
;
7249 wxRichTextAttr paraAttr
;
7250 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7252 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
7253 if (!paraAttr
.IsDefault())
7257 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7259 // Don't include box attributes such as margins
7260 attr
.GetTextBoxAttr().Reset();
7262 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7264 newPara
->SetAttributes(*p
);
7266 wxRichTextField
* fieldObject
= new wxRichTextField();
7267 fieldObject
->wxRichTextObject::SetProperties(properties
);
7268 fieldObject
->SetFieldType(fieldType
);
7269 fieldObject
->SetAttributes(textAttr
);
7270 newPara
->AppendChild(fieldObject
);
7271 action
->GetNewParagraphs().AppendChild(newPara
);
7272 action
->GetNewParagraphs().UpdateRanges();
7273 action
->GetNewParagraphs().SetPartialParagraph(true);
7274 action
->SetPosition(pos
);
7276 // Set the range we'll need to delete in Undo
7277 action
->SetRange(wxRichTextRange(pos
, pos
));
7279 buffer
->SubmitAction(action
);
7281 wxRichTextField
* obj
= wxDynamicCast(GetLeafObjectAtPosition(pos
), wxRichTextField
);
7285 /// Get the style that is appropriate for a new paragraph at this position.
7286 /// If the previous paragraph has a paragraph style name, look up the next-paragraph
7288 wxRichTextAttr
wxRichTextParagraphLayoutBox::GetStyleForNewParagraph(wxRichTextBuffer
* buffer
, long pos
, bool caretPosition
, bool lookUpNewParaStyle
) const
7290 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
, caretPosition
);
7293 wxRichTextAttr attr
;
7294 bool foundAttributes
= false;
7296 // Look for a matching paragraph style
7297 if (lookUpNewParaStyle
&& !para
->GetAttributes().GetParagraphStyleName().IsEmpty() && buffer
->GetStyleSheet())
7299 wxRichTextParagraphStyleDefinition
* paraDef
= buffer
->GetStyleSheet()->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
7302 // If we're not at the end of the paragraph, then we apply THIS style, and not the designated next style.
7303 if (para
->GetRange().GetEnd() == pos
&& !paraDef
->GetNextStyle().IsEmpty())
7305 wxRichTextParagraphStyleDefinition
* nextParaDef
= buffer
->GetStyleSheet()->FindParagraphStyle(paraDef
->GetNextStyle());
7308 foundAttributes
= true;
7309 attr
= nextParaDef
->GetStyleMergedWithBase(buffer
->GetStyleSheet());
7313 // If we didn't find the 'next style', use this style instead.
7314 if (!foundAttributes
)
7316 foundAttributes
= true;
7317 attr
= paraDef
->GetStyleMergedWithBase(buffer
->GetStyleSheet());
7322 // Also apply list style if present
7323 if (lookUpNewParaStyle
&& !para
->GetAttributes().GetListStyleName().IsEmpty() && buffer
->GetStyleSheet())
7325 wxRichTextListStyleDefinition
* listDef
= buffer
->GetStyleSheet()->FindListStyle(para
->GetAttributes().GetListStyleName());
7328 int thisIndent
= para
->GetAttributes().GetLeftIndent();
7329 int thisLevel
= para
->GetAttributes().HasOutlineLevel() ? para
->GetAttributes().GetOutlineLevel() : listDef
->FindLevelForIndent(thisIndent
);
7331 // Apply the overall list style, and item style for this level
7332 wxRichTextAttr
listStyle(listDef
->GetCombinedStyleForLevel(thisLevel
, buffer
->GetStyleSheet()));
7333 wxRichTextApplyStyle(attr
, listStyle
);
7334 attr
.SetOutlineLevel(thisLevel
);
7335 if (para
->GetAttributes().HasBulletNumber())
7336 attr
.SetBulletNumber(para
->GetAttributes().GetBulletNumber());
7340 if (!foundAttributes
)
7342 attr
= para
->GetAttributes();
7343 int flags
= attr
.GetFlags();
7345 // Eliminate character styles
7346 flags
&= ( (~ wxTEXT_ATTR_FONT
) |
7347 (~ wxTEXT_ATTR_TEXT_COLOUR
) |
7348 (~ wxTEXT_ATTR_BACKGROUND_COLOUR
) );
7349 attr
.SetFlags(flags
);
7355 return wxRichTextAttr();
7358 /// Submit command to delete this range
7359 bool wxRichTextBuffer::DeleteRangeWithUndo(const wxRichTextRange
& range
, wxRichTextCtrl
* ctrl
)
7361 return ctrl
->GetFocusObject()->DeleteRangeWithUndo(range
, ctrl
, this);
7364 /// Submit command to delete this range
7365 bool wxRichTextParagraphLayoutBox::DeleteRangeWithUndo(const wxRichTextRange
& range
, wxRichTextCtrl
* ctrl
, wxRichTextBuffer
* buffer
)
7367 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Delete"), wxRICHTEXT_DELETE
, buffer
, this, ctrl
);
7369 action
->SetPosition(ctrl
->GetCaretPosition());
7371 // Set the range to delete
7372 action
->SetRange(range
);
7374 // Copy the fragment that we'll need to restore in Undo
7375 CopyFragment(range
, action
->GetOldParagraphs());
7377 // See if we're deleting a paragraph marker, in which case we need to
7378 // make a note not to copy the attributes from the 2nd paragraph to the 1st.
7379 if (range
.GetStart() == range
.GetEnd())
7381 wxRichTextParagraph
* para
= GetParagraphAtPosition(range
.GetStart());
7382 if (para
&& para
->GetRange().GetEnd() == range
.GetEnd())
7384 wxRichTextParagraph
* nextPara
= GetParagraphAtPosition(range
.GetStart()+1);
7385 if (nextPara
&& nextPara
!= para
)
7387 action
->GetOldParagraphs().GetChildren().GetFirst()->GetData()->SetAttributes(nextPara
->GetAttributes());
7388 action
->GetOldParagraphs().GetAttributes().SetFlags(action
->GetOldParagraphs().GetAttributes().GetFlags() | wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
);
7393 buffer
->SubmitAction(action
);
7398 /// Collapse undo/redo commands
7399 bool wxRichTextBuffer::BeginBatchUndo(const wxString
& cmdName
)
7401 if (m_batchedCommandDepth
== 0)
7403 wxASSERT(m_batchedCommand
== NULL
);
7404 if (m_batchedCommand
)
7406 GetCommandProcessor()->Store(m_batchedCommand
);
7408 m_batchedCommand
= new wxRichTextCommand(cmdName
);
7411 m_batchedCommandDepth
++;
7416 /// Collapse undo/redo commands
7417 bool wxRichTextBuffer::EndBatchUndo()
7419 m_batchedCommandDepth
--;
7421 wxASSERT(m_batchedCommandDepth
>= 0);
7422 wxASSERT(m_batchedCommand
!= NULL
);
7424 if (m_batchedCommandDepth
== 0)
7426 GetCommandProcessor()->Store(m_batchedCommand
);
7427 m_batchedCommand
= NULL
;
7433 /// Submit immediately, or delay according to whether collapsing is on
7434 bool wxRichTextBuffer::SubmitAction(wxRichTextAction
* action
)
7436 if (action
&& !action
->GetNewParagraphs().IsEmpty())
7437 PrepareContent(action
->GetNewParagraphs());
7439 if (BatchingUndo() && m_batchedCommand
&& !SuppressingUndo())
7441 wxRichTextCommand
* cmd
= new wxRichTextCommand(action
->GetName());
7442 cmd
->AddAction(action
);
7444 cmd
->GetActions().Clear();
7447 m_batchedCommand
->AddAction(action
);
7451 wxRichTextCommand
* cmd
= new wxRichTextCommand(action
->GetName());
7452 cmd
->AddAction(action
);
7454 // Only store it if we're not suppressing undo.
7455 return GetCommandProcessor()->Submit(cmd
, !SuppressingUndo());
7461 /// Begin suppressing undo/redo commands.
7462 bool wxRichTextBuffer::BeginSuppressUndo()
7469 /// End suppressing undo/redo commands.
7470 bool wxRichTextBuffer::EndSuppressUndo()
7477 /// Begin using a style
7478 bool wxRichTextBuffer::BeginStyle(const wxRichTextAttr
& style
)
7480 wxRichTextAttr
newStyle(GetDefaultStyle());
7481 newStyle
.GetTextBoxAttr().Reset();
7483 // Save the old default style
7484 m_attributeStack
.Append((wxObject
*) new wxRichTextAttr(newStyle
));
7486 wxRichTextApplyStyle(newStyle
, style
);
7487 newStyle
.SetFlags(style
.GetFlags()|newStyle
.GetFlags());
7489 SetDefaultStyle(newStyle
);
7495 bool wxRichTextBuffer::EndStyle()
7497 if (!m_attributeStack
.GetFirst())
7499 wxLogDebug(_("Too many EndStyle calls!"));
7503 wxList::compatibility_iterator node
= m_attributeStack
.GetLast();
7504 wxRichTextAttr
* attr
= (wxRichTextAttr
*)node
->GetData();
7505 m_attributeStack
.Erase(node
);
7507 SetDefaultStyle(*attr
);
7514 bool wxRichTextBuffer::EndAllStyles()
7516 while (m_attributeStack
.GetCount() != 0)
7521 /// Clear the style stack
7522 void wxRichTextBuffer::ClearStyleStack()
7524 for (wxList::compatibility_iterator node
= m_attributeStack
.GetFirst(); node
; node
= node
->GetNext())
7525 delete (wxRichTextAttr
*) node
->GetData();
7526 m_attributeStack
.Clear();
7529 /// Begin using bold
7530 bool wxRichTextBuffer::BeginBold()
7532 wxRichTextAttr attr
;
7533 attr
.SetFontWeight(wxFONTWEIGHT_BOLD
);
7535 return BeginStyle(attr
);
7538 /// Begin using italic
7539 bool wxRichTextBuffer::BeginItalic()
7541 wxRichTextAttr attr
;
7542 attr
.SetFontStyle(wxFONTSTYLE_ITALIC
);
7544 return BeginStyle(attr
);
7547 /// Begin using underline
7548 bool wxRichTextBuffer::BeginUnderline()
7550 wxRichTextAttr attr
;
7551 attr
.SetFontUnderlined(true);
7553 return BeginStyle(attr
);
7556 /// Begin using point size
7557 bool wxRichTextBuffer::BeginFontSize(int pointSize
)
7559 wxRichTextAttr attr
;
7560 attr
.SetFontSize(pointSize
);
7562 return BeginStyle(attr
);
7565 /// Begin using this font
7566 bool wxRichTextBuffer::BeginFont(const wxFont
& font
)
7568 wxRichTextAttr attr
;
7571 return BeginStyle(attr
);
7574 /// Begin using this colour
7575 bool wxRichTextBuffer::BeginTextColour(const wxColour
& colour
)
7577 wxRichTextAttr attr
;
7578 attr
.SetFlags(wxTEXT_ATTR_TEXT_COLOUR
);
7579 attr
.SetTextColour(colour
);
7581 return BeginStyle(attr
);
7584 /// Begin using alignment
7585 bool wxRichTextBuffer::BeginAlignment(wxTextAttrAlignment alignment
)
7587 wxRichTextAttr attr
;
7588 attr
.SetFlags(wxTEXT_ATTR_ALIGNMENT
);
7589 attr
.SetAlignment(alignment
);
7591 return BeginStyle(attr
);
7594 /// Begin left indent
7595 bool wxRichTextBuffer::BeginLeftIndent(int leftIndent
, int leftSubIndent
)
7597 wxRichTextAttr attr
;
7598 attr
.SetFlags(wxTEXT_ATTR_LEFT_INDENT
);
7599 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7601 return BeginStyle(attr
);
7604 /// Begin right indent
7605 bool wxRichTextBuffer::BeginRightIndent(int rightIndent
)
7607 wxRichTextAttr attr
;
7608 attr
.SetFlags(wxTEXT_ATTR_RIGHT_INDENT
);
7609 attr
.SetRightIndent(rightIndent
);
7611 return BeginStyle(attr
);
7614 /// Begin paragraph spacing
7615 bool wxRichTextBuffer::BeginParagraphSpacing(int before
, int after
)
7619 flags
|= wxTEXT_ATTR_PARA_SPACING_BEFORE
;
7621 flags
|= wxTEXT_ATTR_PARA_SPACING_AFTER
;
7623 wxRichTextAttr attr
;
7624 attr
.SetFlags(flags
);
7625 attr
.SetParagraphSpacingBefore(before
);
7626 attr
.SetParagraphSpacingAfter(after
);
7628 return BeginStyle(attr
);
7631 /// Begin line spacing
7632 bool wxRichTextBuffer::BeginLineSpacing(int lineSpacing
)
7634 wxRichTextAttr attr
;
7635 attr
.SetFlags(wxTEXT_ATTR_LINE_SPACING
);
7636 attr
.SetLineSpacing(lineSpacing
);
7638 return BeginStyle(attr
);
7641 /// Begin numbered bullet
7642 bool wxRichTextBuffer::BeginNumberedBullet(int bulletNumber
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7644 wxRichTextAttr attr
;
7645 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7646 attr
.SetBulletStyle(bulletStyle
);
7647 attr
.SetBulletNumber(bulletNumber
);
7648 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7650 return BeginStyle(attr
);
7653 /// Begin symbol bullet
7654 bool wxRichTextBuffer::BeginSymbolBullet(const wxString
& symbol
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7656 wxRichTextAttr attr
;
7657 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7658 attr
.SetBulletStyle(bulletStyle
);
7659 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7660 attr
.SetBulletText(symbol
);
7662 return BeginStyle(attr
);
7665 /// Begin standard bullet
7666 bool wxRichTextBuffer::BeginStandardBullet(const wxString
& bulletName
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7668 wxRichTextAttr attr
;
7669 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7670 attr
.SetBulletStyle(bulletStyle
);
7671 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7672 attr
.SetBulletName(bulletName
);
7674 return BeginStyle(attr
);
7677 /// Begin named character style
7678 bool wxRichTextBuffer::BeginCharacterStyle(const wxString
& characterStyle
)
7680 if (GetStyleSheet())
7682 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->FindCharacterStyle(characterStyle
);
7685 wxRichTextAttr attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7686 return BeginStyle(attr
);
7692 /// Begin named paragraph style
7693 bool wxRichTextBuffer::BeginParagraphStyle(const wxString
& paragraphStyle
)
7695 if (GetStyleSheet())
7697 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(paragraphStyle
);
7700 wxRichTextAttr attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7701 return BeginStyle(attr
);
7707 /// Begin named list style
7708 bool wxRichTextBuffer::BeginListStyle(const wxString
& listStyle
, int level
, int number
)
7710 if (GetStyleSheet())
7712 wxRichTextListStyleDefinition
* def
= GetStyleSheet()->FindListStyle(listStyle
);
7715 wxRichTextAttr
attr(def
->GetCombinedStyleForLevel(level
));
7717 attr
.SetBulletNumber(number
);
7719 return BeginStyle(attr
);
7726 bool wxRichTextBuffer::BeginURL(const wxString
& url
, const wxString
& characterStyle
)
7728 wxRichTextAttr attr
;
7730 if (!characterStyle
.IsEmpty() && GetStyleSheet())
7732 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->FindCharacterStyle(characterStyle
);
7735 attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7740 return BeginStyle(attr
);
7743 /// Adds a handler to the end
7744 void wxRichTextBuffer::AddHandler(wxRichTextFileHandler
*handler
)
7746 sm_handlers
.Append(handler
);
7749 /// Inserts a handler at the front
7750 void wxRichTextBuffer::InsertHandler(wxRichTextFileHandler
*handler
)
7752 sm_handlers
.Insert( handler
);
7755 /// Removes a handler
7756 bool wxRichTextBuffer::RemoveHandler(const wxString
& name
)
7758 wxRichTextFileHandler
*handler
= FindHandler(name
);
7761 sm_handlers
.DeleteObject(handler
);
7769 /// Finds a handler by filename or, if supplied, type
7770 wxRichTextFileHandler
*wxRichTextBuffer::FindHandlerFilenameOrType(const wxString
& filename
,
7771 wxRichTextFileType imageType
)
7773 if (imageType
!= wxRICHTEXT_TYPE_ANY
)
7774 return FindHandler(imageType
);
7775 else if (!filename
.IsEmpty())
7777 wxString path
, file
, ext
;
7778 wxFileName::SplitPath(filename
, & path
, & file
, & ext
);
7779 return FindHandler(ext
, imageType
);
7786 /// Finds a handler by name
7787 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(const wxString
& name
)
7789 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7792 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7793 if (handler
->GetName().Lower() == name
.Lower()) return handler
;
7795 node
= node
->GetNext();
7800 /// Finds a handler by extension and type
7801 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(const wxString
& extension
, wxRichTextFileType type
)
7803 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7806 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7807 if ( handler
->GetExtension().Lower() == extension
.Lower() &&
7808 (type
== wxRICHTEXT_TYPE_ANY
|| handler
->GetType() == type
) )
7810 node
= node
->GetNext();
7815 /// Finds a handler by type
7816 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(wxRichTextFileType type
)
7818 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7821 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7822 if (handler
->GetType() == type
) return handler
;
7823 node
= node
->GetNext();
7828 void wxRichTextBuffer::InitStandardHandlers()
7830 if (!FindHandler(wxRICHTEXT_TYPE_TEXT
))
7831 AddHandler(new wxRichTextPlainTextHandler
);
7834 void wxRichTextBuffer::CleanUpHandlers()
7836 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7839 wxRichTextFileHandler
* handler
= (wxRichTextFileHandler
*)node
->GetData();
7840 wxList::compatibility_iterator next
= node
->GetNext();
7845 sm_handlers
.Clear();
7848 wxString
wxRichTextBuffer::GetExtWildcard(bool combine
, bool save
, wxArrayInt
* types
)
7855 wxList::compatibility_iterator node
= GetHandlers().GetFirst();
7859 wxRichTextFileHandler
* handler
= (wxRichTextFileHandler
*) node
->GetData();
7860 if (handler
->IsVisible() && ((save
&& handler
->CanSave()) || (!save
&& handler
->CanLoad())))
7865 wildcard
+= wxT(";");
7866 wildcard
+= wxT("*.") + handler
->GetExtension();
7871 wildcard
+= wxT("|");
7872 wildcard
+= handler
->GetName();
7873 wildcard
+= wxT(" ");
7874 wildcard
+= _("files");
7875 wildcard
+= wxT(" (*.");
7876 wildcard
+= handler
->GetExtension();
7877 wildcard
+= wxT(")|*.");
7878 wildcard
+= handler
->GetExtension();
7880 types
->Add(handler
->GetType());
7885 node
= node
->GetNext();
7889 wildcard
= wxT("(") + wildcard
+ wxT(")|") + wildcard
;
7894 bool wxRichTextBuffer::LoadFile(const wxString
& filename
, wxRichTextFileType type
)
7896 wxRichTextFileHandler
* handler
= FindHandlerFilenameOrType(filename
, type
);
7899 SetDefaultStyle(wxRichTextAttr());
7900 handler
->SetFlags(GetHandlerFlags());
7901 bool success
= handler
->LoadFile(this, filename
);
7902 Invalidate(wxRICHTEXT_ALL
);
7910 bool wxRichTextBuffer::SaveFile(const wxString
& filename
, wxRichTextFileType type
)
7912 wxRichTextFileHandler
* handler
= FindHandlerFilenameOrType(filename
, type
);
7915 handler
->SetFlags(GetHandlerFlags());
7916 return handler
->SaveFile(this, filename
);
7922 /// Load from a stream
7923 bool wxRichTextBuffer::LoadFile(wxInputStream
& stream
, wxRichTextFileType type
)
7925 wxRichTextFileHandler
* handler
= FindHandler(type
);
7928 SetDefaultStyle(wxRichTextAttr());
7929 handler
->SetFlags(GetHandlerFlags());
7930 bool success
= handler
->LoadFile(this, stream
);
7931 Invalidate(wxRICHTEXT_ALL
);
7938 /// Save to a stream
7939 bool wxRichTextBuffer::SaveFile(wxOutputStream
& stream
, wxRichTextFileType type
)
7941 wxRichTextFileHandler
* handler
= FindHandler(type
);
7944 handler
->SetFlags(GetHandlerFlags());
7945 return handler
->SaveFile(this, stream
);
7951 /// Copy the range to the clipboard
7952 bool wxRichTextBuffer::CopyToClipboard(const wxRichTextRange
& range
)
7954 bool success
= false;
7955 wxRichTextParagraphLayoutBox
* container
= this;
7956 if (GetRichTextCtrl())
7957 container
= GetRichTextCtrl()->GetFocusObject();
7959 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
7961 if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open())
7963 wxTheClipboard
->Clear();
7965 // Add composite object
7967 wxDataObjectComposite
* compositeObject
= new wxDataObjectComposite();
7970 wxString text
= container
->GetTextForRange(range
);
7973 text
= wxTextFile::Translate(text
, wxTextFileType_Dos
);
7976 compositeObject
->Add(new wxTextDataObject(text
), false /* not preferred */);
7979 // Add rich text buffer data object. This needs the XML handler to be present.
7981 if (FindHandler(wxRICHTEXT_TYPE_XML
))
7983 wxRichTextBuffer
* richTextBuf
= new wxRichTextBuffer
;
7984 container
->CopyFragment(range
, *richTextBuf
);
7986 compositeObject
->Add(new wxRichTextBufferDataObject(richTextBuf
), true /* preferred */);
7989 if (wxTheClipboard
->SetData(compositeObject
))
7992 wxTheClipboard
->Close();
8001 /// Paste the clipboard content to the buffer
8002 bool wxRichTextBuffer::PasteFromClipboard(long position
)
8004 bool success
= false;
8005 wxRichTextParagraphLayoutBox
* container
= this;
8006 if (GetRichTextCtrl())
8007 container
= GetRichTextCtrl()->GetFocusObject();
8009 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
8010 if (CanPasteFromClipboard())
8012 if (wxTheClipboard
->Open())
8014 if (wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())))
8016 wxRichTextBufferDataObject data
;
8017 wxTheClipboard
->GetData(data
);
8018 wxRichTextBuffer
* richTextBuffer
= data
.GetRichTextBuffer();
8021 container
->InsertParagraphsWithUndo(this, position
+1, *richTextBuffer
, GetRichTextCtrl(), 0);
8022 if (GetRichTextCtrl())
8023 GetRichTextCtrl()->ShowPosition(position
+ richTextBuffer
->GetOwnRange().GetEnd());
8024 delete richTextBuffer
;
8027 else if (wxTheClipboard
->IsSupported(wxDF_TEXT
)
8029 || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
)
8033 wxTextDataObject data
;
8034 wxTheClipboard
->GetData(data
);
8035 wxString
text(data
.GetText());
8038 text2
.Alloc(text
.Length()+1);
8040 for (i
= 0; i
< text
.Length(); i
++)
8042 wxChar ch
= text
[i
];
8043 if (ch
!= wxT('\r'))
8047 wxString text2
= text
;
8049 container
->InsertTextWithUndo(this, position
+1, text2
, GetRichTextCtrl(), wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
);
8051 if (GetRichTextCtrl())
8052 GetRichTextCtrl()->ShowPosition(position
+ text2
.Length());
8056 else if (wxTheClipboard
->IsSupported(wxDF_BITMAP
))
8058 wxBitmapDataObject data
;
8059 wxTheClipboard
->GetData(data
);
8060 wxBitmap
bitmap(data
.GetBitmap());
8061 wxImage
image(bitmap
.ConvertToImage());
8063 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, this, container
, GetRichTextCtrl(), false);
8065 action
->GetNewParagraphs().AddImage(image
);
8067 if (action
->GetNewParagraphs().GetChildCount() == 1)
8068 action
->GetNewParagraphs().SetPartialParagraph(true);
8070 action
->SetPosition(position
+1);
8072 // Set the range we'll need to delete in Undo
8073 action
->SetRange(wxRichTextRange(position
+1, position
+1));
8075 SubmitAction(action
);
8079 wxTheClipboard
->Close();
8083 wxUnusedVar(position
);
8088 /// Can we paste from the clipboard?
8089 bool wxRichTextBuffer::CanPasteFromClipboard() const
8091 bool canPaste
= false;
8092 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
8093 if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open())
8095 if (wxTheClipboard
->IsSupported(wxDF_TEXT
)
8097 || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
)
8099 || wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())) ||
8100 wxTheClipboard
->IsSupported(wxDF_BITMAP
))
8104 wxTheClipboard
->Close();
8110 /// Dumps contents of buffer for debugging purposes
8111 void wxRichTextBuffer::Dump()
8115 wxStringOutputStream
stream(& text
);
8116 wxTextOutputStream
textStream(stream
);
8123 /// Add an event handler
8124 bool wxRichTextBuffer::AddEventHandler(wxEvtHandler
* handler
)
8126 m_eventHandlers
.Append(handler
);
8130 /// Remove an event handler
8131 bool wxRichTextBuffer::RemoveEventHandler(wxEvtHandler
* handler
, bool deleteHandler
)
8133 wxList::compatibility_iterator node
= m_eventHandlers
.Find(handler
);
8136 m_eventHandlers
.Erase(node
);
8146 /// Clear event handlers
8147 void wxRichTextBuffer::ClearEventHandlers()
8149 m_eventHandlers
.Clear();
8152 /// Send event to event handlers. If sendToAll is true, will send to all event handlers,
8153 /// otherwise will stop at the first successful one.
8154 bool wxRichTextBuffer::SendEvent(wxEvent
& event
, bool sendToAll
)
8156 bool success
= false;
8157 for (wxList::compatibility_iterator node
= m_eventHandlers
.GetFirst(); node
; node
= node
->GetNext())
8159 wxEvtHandler
* handler
= (wxEvtHandler
*) node
->GetData();
8160 if (handler
->ProcessEvent(event
))
8170 /// Set style sheet and notify of the change
8171 bool wxRichTextBuffer::SetStyleSheetAndNotify(wxRichTextStyleSheet
* sheet
)
8173 wxRichTextStyleSheet
* oldSheet
= GetStyleSheet();
8175 wxWindowID winid
= wxID_ANY
;
8176 if (GetRichTextCtrl())
8177 winid
= GetRichTextCtrl()->GetId();
8179 wxRichTextEvent
event(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACING
, winid
);
8180 event
.SetEventObject(GetRichTextCtrl());
8181 event
.SetContainer(GetRichTextCtrl()->GetFocusObject());
8182 event
.SetOldStyleSheet(oldSheet
);
8183 event
.SetNewStyleSheet(sheet
);
8186 if (SendEvent(event
) && !event
.IsAllowed())
8188 if (sheet
!= oldSheet
)
8194 if (oldSheet
&& oldSheet
!= sheet
)
8197 SetStyleSheet(sheet
);
8199 event
.SetEventType(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACED
);
8200 event
.SetOldStyleSheet(NULL
);
8203 return SendEvent(event
);
8206 /// Set renderer, deleting old one
8207 void wxRichTextBuffer::SetRenderer(wxRichTextRenderer
* renderer
)
8211 sm_renderer
= renderer
;
8214 /// Hit-testing: returns a flag indicating hit test details, plus
8215 /// information about position
8216 int wxRichTextBuffer::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
8218 int ret
= wxRichTextParagraphLayoutBox::HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
8219 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
8225 textPosition
= m_ownRange
.GetEnd()-1;
8228 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
;
8232 void wxRichTextBuffer::SetFontScale(double fontScale
)
8234 m_fontScale
= fontScale
;
8235 m_fontTable
.SetFontScale(fontScale
);
8238 void wxRichTextBuffer::SetDimensionScale(double dimScale
)
8240 m_dimensionScale
= dimScale
;
8243 bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxRichTextAttr
& bulletAttr
, const wxRect
& rect
)
8245 if (bulletAttr
.GetTextColour().IsOk())
8247 wxCheckSetPen(dc
, wxPen(bulletAttr
.GetTextColour()));
8248 wxCheckSetBrush(dc
, wxBrush(bulletAttr
.GetTextColour()));
8252 wxCheckSetPen(dc
, *wxBLACK_PEN
);
8253 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
8257 if (bulletAttr
.HasFont())
8259 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(bulletAttr
);
8262 font
= (*wxNORMAL_FONT
);
8264 wxCheckSetFont(dc
, font
);
8266 int charHeight
= dc
.GetCharHeight();
8268 int bulletWidth
= (int) (((float) charHeight
) * wxRichTextBuffer::GetBulletProportion());
8269 int bulletHeight
= bulletWidth
;
8273 // Calculate the top position of the character (as opposed to the whole line height)
8274 int y
= rect
.y
+ (rect
.height
- charHeight
);
8276 // Calculate where the bullet should be positioned
8277 y
= y
+ (charHeight
+1)/2 - (bulletHeight
+1)/2;
8279 // The margin between a bullet and text.
8280 int margin
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer::GetBulletRightMargin());
8282 if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
)
8283 x
= rect
.x
+ rect
.width
- bulletWidth
- margin
;
8284 else if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
)
8285 x
= x
+ (rect
.width
)/2 - bulletWidth
/2;
8287 if (bulletAttr
.GetBulletName() == wxT("standard/square"))
8289 dc
.DrawRectangle(x
, y
, bulletWidth
, bulletHeight
);
8291 else if (bulletAttr
.GetBulletName() == wxT("standard/diamond"))
8294 pts
[0].x
= x
; pts
[0].y
= y
+ bulletHeight
/2;
8295 pts
[1].x
= x
+ bulletWidth
/2; pts
[1].y
= y
;
8296 pts
[2].x
= x
+ bulletWidth
; pts
[2].y
= y
+ bulletHeight
/2;
8297 pts
[3].x
= x
+ bulletWidth
/2; pts
[3].y
= y
+ bulletHeight
;
8299 dc
.DrawPolygon(4, pts
);
8301 else if (bulletAttr
.GetBulletName() == wxT("standard/triangle"))
8304 pts
[0].x
= x
; pts
[0].y
= y
;
8305 pts
[1].x
= x
+ bulletWidth
; pts
[1].y
= y
+ bulletHeight
/2;
8306 pts
[2].x
= x
; pts
[2].y
= y
+ bulletHeight
;
8308 dc
.DrawPolygon(3, pts
);
8310 else if (bulletAttr
.GetBulletName() == wxT("standard/circle-outline"))
8312 wxCheckSetBrush(dc
, *wxTRANSPARENT_BRUSH
);
8313 dc
.DrawEllipse(x
, y
, bulletWidth
, bulletHeight
);
8315 else // "standard/circle", and catch-all
8317 dc
.DrawEllipse(x
, y
, bulletWidth
, bulletHeight
);
8323 bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxRichTextAttr
& attr
, const wxRect
& rect
, const wxString
& text
)
8328 if ((attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
) && !attr
.GetBulletFont().IsEmpty() && attr
.HasFont())
8330 wxRichTextAttr fontAttr
;
8331 if (attr
.HasFontPixelSize())
8332 fontAttr
.SetFontPixelSize(attr
.GetFontSize());
8334 fontAttr
.SetFontPointSize(attr
.GetFontSize());
8335 fontAttr
.SetFontStyle(attr
.GetFontStyle());
8336 fontAttr
.SetFontWeight(attr
.GetFontWeight());
8337 fontAttr
.SetFontUnderlined(attr
.GetFontUnderlined());
8338 fontAttr
.SetFontFaceName(attr
.GetBulletFont());
8339 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(fontAttr
);
8341 else if (attr
.HasFont())
8342 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(attr
);
8344 font
= (*wxNORMAL_FONT
);
8346 wxCheckSetFont(dc
, font
);
8348 if (attr
.GetTextColour().IsOk())
8349 dc
.SetTextForeground(attr
.GetTextColour());
8351 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
8353 int charHeight
= dc
.GetCharHeight();
8355 dc
.GetTextExtent(text
, & tw
, & th
);
8359 // Calculate the top position of the character (as opposed to the whole line height)
8360 int y
= rect
.y
+ (rect
.height
- charHeight
);
8362 // The margin between a bullet and text.
8363 int margin
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer::GetBulletRightMargin());
8365 if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
)
8366 x
= (rect
.x
+ rect
.width
) - tw
- margin
;
8367 else if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
)
8368 x
= x
+ (rect
.width
)/2 - tw
/2;
8370 dc
.DrawText(text
, x
, y
);
8378 bool wxRichTextStdRenderer::DrawBitmapBullet(wxRichTextParagraph
* WXUNUSED(paragraph
), wxDC
& WXUNUSED(dc
), const wxRichTextAttr
& WXUNUSED(attr
), const wxRect
& WXUNUSED(rect
))
8380 // Currently unimplemented. The intention is to store bitmaps by name in a media store associated
8381 // with the buffer. The store will allow retrieval from memory, disk or other means.
8385 /// Enumerate the standard bullet names currently supported
8386 bool wxRichTextStdRenderer::EnumerateStandardBulletNames(wxArrayString
& bulletNames
)
8388 bulletNames
.Add(wxTRANSLATE("standard/circle"));
8389 bulletNames
.Add(wxTRANSLATE("standard/circle-outline"));
8390 bulletNames
.Add(wxTRANSLATE("standard/square"));
8391 bulletNames
.Add(wxTRANSLATE("standard/diamond"));
8392 bulletNames
.Add(wxTRANSLATE("standard/triangle"));
8401 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBox
, wxRichTextParagraphLayoutBox
)
8403 wxRichTextBox::wxRichTextBox(wxRichTextObject
* parent
):
8404 wxRichTextParagraphLayoutBox(parent
)
8409 bool wxRichTextBox::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8414 // TODO: if the active object in the control, draw an indication.
8415 // We need to add the concept of active object, and not just focus object,
8416 // so we can apply commands (properties, delete, ...) to objects such as text boxes and images.
8417 // Ultimately we would like to be able to interactively resize an active object
8418 // using drag handles.
8419 return wxRichTextParagraphLayoutBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8423 void wxRichTextBox::Copy(const wxRichTextBox
& obj
)
8425 wxRichTextParagraphLayoutBox::Copy(obj
);
8428 // Edit properties via a GUI
8429 bool wxRichTextBox::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8431 wxRichTextObjectPropertiesDialog
boxDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Box Properties"));
8432 boxDlg
.SetAttributes(GetAttributes());
8434 if (boxDlg
.ShowModal() == wxID_OK
)
8436 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
8437 // indeterminate in the object.
8438 boxDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
8449 IMPLEMENT_DYNAMIC_CLASS(wxRichTextField
, wxRichTextParagraphLayoutBox
)
8451 wxRichTextField::wxRichTextField(const wxString
& fieldType
, wxRichTextObject
* parent
):
8452 wxRichTextParagraphLayoutBox(parent
)
8454 SetFieldType(fieldType
);
8458 bool wxRichTextField::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8463 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8464 if (fieldType
&& fieldType
->Draw(this, dc
, context
, range
, selection
, rect
, descent
, style
))
8467 // Fallback; but don't draw guidelines.
8468 style
&= ~wxRICHTEXT_DRAW_GUIDELINES
;
8469 return wxRichTextParagraphLayoutBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8472 bool wxRichTextField::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
)
8474 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8475 if (fieldType
&& fieldType
->Layout(this, dc
, context
, rect
, parentRect
, style
))
8479 return wxRichTextParagraphLayoutBox::Layout(dc
, context
, rect
, parentRect
, style
);
8482 bool wxRichTextField::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
8484 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8486 return fieldType
->GetRangeSize((wxRichTextField
*) this, range
, size
, descent
, dc
, context
, flags
, position
, partialExtents
);
8488 return wxRichTextParagraphLayoutBox::GetRangeSize(range
, size
, descent
, dc
, context
, flags
, position
, partialExtents
);
8492 void wxRichTextField::CalculateRange(long start
, long& end
)
8495 wxRichTextParagraphLayoutBox::CalculateRange(start
, end
);
8497 wxRichTextObject::CalculateRange(start
, end
);
8501 void wxRichTextField::Copy(const wxRichTextField
& obj
)
8503 wxRichTextParagraphLayoutBox::Copy(obj
);
8505 UpdateField(GetBuffer());
8508 // Edit properties via a GUI
8509 bool wxRichTextField::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8511 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8513 return fieldType
->EditProperties(this, parent
, buffer
);
8518 bool wxRichTextField::CanEditProperties() const
8520 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8522 return fieldType
->CanEditProperties((wxRichTextField
*) this);
8527 wxString
wxRichTextField::GetPropertiesMenuLabel() const
8529 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8531 return fieldType
->GetPropertiesMenuLabel((wxRichTextField
*) this);
8533 return wxEmptyString
;
8536 bool wxRichTextField::UpdateField(wxRichTextBuffer
* buffer
)
8538 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8540 return fieldType
->UpdateField(buffer
, (wxRichTextField
*) this);
8545 bool wxRichTextField::IsTopLevel() const
8547 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8549 return fieldType
->IsTopLevel((wxRichTextField
*) this);
8554 IMPLEMENT_CLASS(wxRichTextFieldType
, wxObject
)
8556 IMPLEMENT_CLASS(wxRichTextFieldTypeStandard
, wxRichTextFieldType
)
8558 wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString
& name
, const wxString
& label
, int displayStyle
)
8564 SetDisplayStyle(displayStyle
);
8567 wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString
& name
, const wxBitmap
& bitmap
, int displayStyle
)
8573 SetDisplayStyle(displayStyle
);
8576 void wxRichTextFieldTypeStandard::Init()
8578 m_displayStyle
= wxRICHTEXT_FIELD_STYLE_RECTANGLE
;
8579 m_font
= wxFont(6, wxFONTFAMILY_SWISS
, wxFONTSTYLE_NORMAL
, wxFONTWEIGHT_NORMAL
);
8580 m_textColour
= *wxWHITE
;
8581 m_borderColour
= *wxBLACK
;
8582 m_backgroundColour
= *wxBLACK
;
8583 m_verticalPadding
= 1;
8584 m_horizontalPadding
= 3;
8585 m_horizontalMargin
= 2;
8586 m_verticalMargin
= 0;
8589 void wxRichTextFieldTypeStandard::Copy(const wxRichTextFieldTypeStandard
& field
)
8591 wxRichTextFieldType::Copy(field
);
8593 m_label
= field
.m_label
;
8594 m_displayStyle
= field
.m_displayStyle
;
8595 m_font
= field
.m_font
;
8596 m_textColour
= field
.m_textColour
;
8597 m_borderColour
= field
.m_borderColour
;
8598 m_backgroundColour
= field
.m_backgroundColour
;
8599 m_verticalPadding
= field
.m_verticalPadding
;
8600 m_horizontalPadding
= field
.m_horizontalPadding
;
8601 m_horizontalMargin
= field
.m_horizontalMargin
;
8602 m_bitmap
= field
.m_bitmap
;
8605 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
))
8607 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_COMPOSITE
)
8608 return false; // USe default composite drawing
8609 else // if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_RECTANGLE || m_displayStyle == wxRICHTEXT_FIELD_STYLE_NOBORDER)
8613 wxPen
borderPen(m_borderColour
, 1, wxSOLID
);
8614 wxBrush
backgroundBrush(m_backgroundColour
);
8615 wxColour
textColour(m_textColour
);
8617 if (selection
.WithinSelection(obj
->GetRange().GetStart(), obj
))
8619 wxColour
highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
));
8620 wxColour
highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
));
8622 borderPen
= wxPen(highlightTextColour
, 1, wxSOLID
);
8623 backgroundBrush
= wxBrush(highlightColour
);
8625 wxCheckSetBrush(dc
, backgroundBrush
);
8626 wxCheckSetPen(dc
, wxPen(highlightColour
, 1, wxSOLID
));
8627 dc
.DrawRectangle(rect
);
8630 if (m_displayStyle
!= wxRICHTEXT_FIELD_STYLE_NO_BORDER
)
8633 // objectRect is the area where the content is drawn, after margins around it have been taken into account
8634 wxRect objectRect
= wxRect(wxPoint(rect
.x
+ m_horizontalMargin
, rect
.y
+ wxMax(0, rect
.height
- descent
- obj
->GetCachedSize().y
)),
8635 wxSize(obj
->GetCachedSize().x
- 2*m_horizontalMargin
- borderSize
, obj
->GetCachedSize().y
));
8637 // clientArea is where the text is actually written
8638 wxRect clientArea
= objectRect
;
8640 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_RECTANGLE
)
8642 dc
.SetPen(borderPen
);
8643 dc
.SetBrush(backgroundBrush
);
8644 dc
.DrawRoundedRectangle(objectRect
, 4.0);
8646 else if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_START_TAG
)
8648 int arrowLength
= objectRect
.height
/2;
8649 clientArea
.width
-= (arrowLength
- m_horizontalPadding
);
8652 pts
[0].x
= objectRect
.x
; pts
[0].y
= objectRect
.y
;
8653 pts
[1].x
= objectRect
.x
+ objectRect
.width
- arrowLength
; pts
[1].y
= objectRect
.y
;
8654 pts
[2].x
= objectRect
.x
+ objectRect
.width
; pts
[2].y
= objectRect
.y
+ (objectRect
.height
/2);
8655 pts
[3].x
= objectRect
.x
+ objectRect
.width
- arrowLength
; pts
[3].y
= objectRect
.y
+ objectRect
.height
;
8656 pts
[4].x
= objectRect
.x
; pts
[4].y
= objectRect
.y
+ objectRect
.height
;
8657 dc
.SetPen(borderPen
);
8658 dc
.SetBrush(backgroundBrush
);
8659 dc
.DrawPolygon(5, pts
);
8661 else if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_END_TAG
)
8663 int arrowLength
= objectRect
.height
/2;
8664 clientArea
.width
-= (arrowLength
- m_horizontalPadding
);
8665 clientArea
.x
+= (arrowLength
- m_horizontalPadding
);
8668 pts
[0].x
= objectRect
.x
+ objectRect
.width
; pts
[0].y
= objectRect
.y
;
8669 pts
[1].x
= objectRect
.x
+ arrowLength
; pts
[1].y
= objectRect
.y
;
8670 pts
[2].x
= objectRect
.x
; pts
[2].y
= objectRect
.y
+ (objectRect
.height
/2);
8671 pts
[3].x
= objectRect
.x
+ arrowLength
; pts
[3].y
= objectRect
.y
+ objectRect
.height
;
8672 pts
[4].x
= objectRect
.x
+ objectRect
.width
; pts
[4].y
= objectRect
.y
+ objectRect
.height
;
8673 dc
.SetPen(borderPen
);
8674 dc
.SetBrush(backgroundBrush
);
8675 dc
.DrawPolygon(5, pts
);
8678 if (m_bitmap
.IsOk())
8680 int x
= clientArea
.x
+ (clientArea
.width
- m_bitmap
.GetWidth())/2;
8681 int y
= clientArea
.y
+ m_verticalPadding
;
8682 dc
.DrawBitmap(m_bitmap
, x
, y
, true);
8684 if (selection
.WithinSelection(obj
->GetRange().GetStart(), obj
))
8686 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
8687 wxCheckSetPen(dc
, *wxBLACK_PEN
);
8688 dc
.SetLogicalFunction(wxINVERT
);
8689 dc
.DrawRectangle(wxRect(x
, y
, m_bitmap
.GetWidth(), m_bitmap
.GetHeight()));
8690 dc
.SetLogicalFunction(wxCOPY
);
8695 wxString
label(m_label
);
8696 if (label
.IsEmpty())
8698 int w
, h
, maxDescent
;
8700 dc
.GetTextExtent(m_label
, & w
, &h
, & maxDescent
);
8701 dc
.SetTextForeground(textColour
);
8703 int x
= clientArea
.x
+ (clientArea
.width
- w
)/2;
8704 int y
= clientArea
.y
+ (clientArea
.height
- (h
- maxDescent
))/2;
8705 dc
.DrawText(m_label
, x
, y
);
8712 bool wxRichTextFieldTypeStandard::Layout(wxRichTextField
* obj
, wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& WXUNUSED(rect
), const wxRect
& WXUNUSED(parentRect
), int style
)
8714 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_COMPOSITE
)
8715 return false; // USe default composite layout
8717 wxSize size
= GetSize(obj
, dc
, context
, style
);
8718 obj
->SetCachedSize(size
);
8719 obj
->SetMinSize(size
);
8720 obj
->SetMaxSize(size
);
8724 bool wxRichTextFieldTypeStandard::GetRangeSize(wxRichTextField
* obj
, const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
8726 if (IsTopLevel(obj
))
8727 return obj
->wxRichTextParagraphLayoutBox::GetRangeSize(range
, size
, descent
, dc
, context
, flags
, position
);
8730 wxSize sz
= GetSize(obj
, dc
, context
, 0);
8734 if (partialExtents
->GetCount() > 0)
8735 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
8738 partialExtents
->Add(lastSize
+ sz
.x
);
8745 wxSize
wxRichTextFieldTypeStandard::GetSize(wxRichTextField
* WXUNUSED(obj
), wxDC
& dc
, wxRichTextDrawingContext
& WXUNUSED(context
), int WXUNUSED(style
)) const
8748 int w
= 0, h
= 0, maxDescent
= 0;
8751 if (m_bitmap
.IsOk())
8753 w
= m_bitmap
.GetWidth();
8754 h
= m_bitmap
.GetHeight();
8756 sz
= wxSize(w
+ m_horizontalMargin
*2, h
+ m_verticalMargin
*2);
8760 wxString
label(m_label
);
8761 if (label
.IsEmpty())
8764 dc
.GetTextExtent(label
, & w
, &h
, & maxDescent
);
8766 sz
= wxSize(w
+ m_horizontalPadding
*2 + m_horizontalMargin
*2, h
+ m_verticalPadding
*2 + m_verticalMargin
*2);
8769 if (m_displayStyle
!= wxRICHTEXT_FIELD_STYLE_NO_BORDER
)
8771 sz
.x
+= borderSize
*2;
8772 sz
.y
+= borderSize
*2;
8775 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_START_TAG
|| m_displayStyle
== wxRICHTEXT_FIELD_STYLE_END_TAG
)
8777 // Add space for the arrow
8778 sz
.x
+= (sz
.y
/2 - m_horizontalPadding
);
8784 IMPLEMENT_DYNAMIC_CLASS(wxRichTextCell
, wxRichTextBox
)
8786 wxRichTextCell::wxRichTextCell(wxRichTextObject
* parent
):
8787 wxRichTextBox(parent
)
8792 bool wxRichTextCell::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8794 return wxRichTextBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8798 void wxRichTextCell::Copy(const wxRichTextCell
& obj
)
8800 wxRichTextBox::Copy(obj
);
8803 // Edit properties via a GUI
8804 bool wxRichTextCell::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8806 // We need to gather common attributes for all selected cells.
8808 wxRichTextTable
* table
= wxDynamicCast(GetParent(), wxRichTextTable
);
8809 bool multipleCells
= false;
8810 wxRichTextAttr attr
;
8812 if (table
&& buffer
&& buffer
->GetRichTextCtrl() && buffer
->GetRichTextCtrl()->GetSelection().IsValid() &&
8813 buffer
->GetRichTextCtrl()->GetSelection().GetContainer() == GetParent())
8815 wxRichTextAttr clashingAttr
, absentAttr
;
8816 const wxRichTextSelection
& sel
= buffer
->GetRichTextCtrl()->GetSelection();
8818 int selectedCellCount
= 0;
8819 for (i
= 0; i
< sel
.GetCount(); i
++)
8821 const wxRichTextRange
& range
= sel
[i
];
8822 wxRichTextCell
* cell
= table
->GetCell(range
.GetStart());
8825 wxRichTextAttr cellStyle
= cell
->GetAttributes();
8827 CollectStyle(attr
, cellStyle
, clashingAttr
, absentAttr
);
8829 selectedCellCount
++;
8832 multipleCells
= selectedCellCount
> 1;
8836 attr
= GetAttributes();
8841 caption
= _("Multiple Cell Properties");
8843 caption
= _("Cell Properties");
8845 wxRichTextObjectPropertiesDialog
cellDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, caption
);
8846 cellDlg
.SetAttributes(attr
);
8848 wxRichTextSizePage
* sizePage
= wxDynamicCast(cellDlg
.FindPage(wxCLASSINFO(wxRichTextSizePage
)), wxRichTextSizePage
);
8851 // We don't want position and floating controls for a cell.
8852 sizePage
->ShowPositionControls(false);
8853 sizePage
->ShowFloatingControls(false);
8856 if (cellDlg
.ShowModal() == wxID_OK
)
8860 const wxRichTextSelection
& sel
= buffer
->GetRichTextCtrl()->GetSelection();
8861 // Apply the style; we interpret indeterminate attributes as 'don't touch this attribute'
8862 // since it may represent clashing attributes across multiple objects.
8863 table
->SetCellStyle(sel
, attr
);
8866 // For a single object, indeterminate attributes set by the user should be reflected in the
8867 // actual object style, so pass the wxRICHTEXT_SETSTYLE_RESET flag to assign
8868 // the style directly instead of applying (which ignores indeterminate attributes,
8869 // leaving them as they were).
8870 cellDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
8877 WX_DEFINE_OBJARRAY(wxRichTextObjectPtrArrayArray
)
8879 IMPLEMENT_DYNAMIC_CLASS(wxRichTextTable
, wxRichTextBox
)
8881 wxRichTextTable::wxRichTextTable(wxRichTextObject
* parent
): wxRichTextBox(parent
)
8887 // Draws the object.
8888 bool wxRichTextTable::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8890 return wxRichTextBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8893 WX_DECLARE_OBJARRAY(wxRect
, wxRichTextRectArray
);
8894 WX_DEFINE_OBJARRAY(wxRichTextRectArray
);
8896 // Lays the object out. rect is the space available for layout. Often it will
8897 // be the specified overall space for this object, if trying to constrain
8898 // layout to a particular size, or it could be the total space available in the
8899 // parent. rect is the overall size, so we must subtract margins and padding.
8900 // to get the actual available space.
8901 bool wxRichTextTable::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& WXUNUSED(parentRect
), int style
)
8903 SetPosition(rect
.GetPosition());
8905 // TODO: the meaty bit. Calculate sizes of all cells and rows. Try to use
8906 // minimum size if within alloted size, then divide up remaining size
8907 // between rows/cols.
8910 wxRichTextBuffer
* buffer
= GetBuffer();
8911 if (buffer
) scale
= buffer
->GetScale();
8913 wxRect availableSpace
= GetAvailableContentArea(dc
, context
, rect
);
8914 wxTextAttrDimensionConverter
converter(dc
, scale
, availableSpace
.GetSize());
8916 wxRichTextAttr
attr(GetAttributes());
8917 context
.ApplyVirtualAttributes(attr
, this);
8919 // If we have no fixed table size, and assuming we're not pushed for
8920 // space, then we don't have to try to stretch the table to fit the contents.
8921 bool stretchToFitTableWidth
= false;
8923 int tableWidth
= rect
.width
;
8924 if (attr
.GetTextBoxAttr().GetWidth().IsValid())
8926 tableWidth
= converter
.GetPixels(attr
.GetTextBoxAttr().GetWidth());
8928 // Fixed table width, so we do want to stretch columns out if necessary.
8929 stretchToFitTableWidth
= true;
8931 // Shouldn't be able to exceed the size passed to this function
8932 tableWidth
= wxMin(rect
.width
, tableWidth
);
8935 // Get internal padding
8936 int paddingLeft
= 0, paddingTop
= 0;
8937 if (attr
.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
8938 paddingLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetLeft());
8939 if (attr
.GetTextBoxAttr().GetPadding().GetTop().IsValid())
8940 paddingTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetTop());
8942 // Assume that left and top padding are also used for inter-cell padding.
8943 int paddingX
= paddingLeft
;
8944 int paddingY
= paddingTop
;
8946 int totalLeftMargin
= 0, totalRightMargin
= 0, totalTopMargin
= 0, totalBottomMargin
= 0;
8947 GetTotalMargin(dc
, buffer
, attr
, totalLeftMargin
, totalRightMargin
, totalTopMargin
, totalBottomMargin
);
8949 // Internal table width - the area for content
8950 int internalTableWidth
= tableWidth
- totalLeftMargin
- totalRightMargin
;
8952 int rowCount
= m_cells
.GetCount();
8953 if (m_colCount
== 0 || rowCount
== 0)
8955 wxRect
overallRect(rect
.x
, rect
.y
, totalLeftMargin
+ totalRightMargin
, totalTopMargin
+ totalBottomMargin
);
8956 SetCachedSize(overallRect
.GetSize());
8958 // Zero content size
8959 SetMinSize(overallRect
.GetSize());
8960 SetMaxSize(GetMinSize());
8964 // The final calculated widths
8965 wxArrayInt colWidths
;
8966 colWidths
.Add(0, m_colCount
);
8968 wxArrayInt absoluteColWidths
;
8969 absoluteColWidths
.Add(0, m_colCount
);
8971 wxArrayInt percentageColWidths
;
8972 percentageColWidths
.Add(0, m_colCount
);
8973 // wxArrayInt percentageColWidthsSpanning(m_colCount);
8974 // These are only relevant when the first column contains spanning information.
8975 // wxArrayInt columnSpans(m_colCount); // Each contains 1 for non-spanning cell, > 1 for spanning cell.
8976 wxArrayInt maxColWidths
;
8977 maxColWidths
.Add(0, m_colCount
);
8978 wxArrayInt minColWidths
;
8979 minColWidths
.Add(0, m_colCount
);
8981 wxSize
tableSize(tableWidth
, 0);
8985 for (i
= 0; i
< m_colCount
; i
++)
8987 absoluteColWidths
[i
] = 0;
8988 // absoluteColWidthsSpanning[i] = 0;
8989 percentageColWidths
[i
] = -1;
8990 // percentageColWidthsSpanning[i] = -1;
8992 maxColWidths
[i
] = 0;
8993 minColWidths
[i
] = 0;
8994 // columnSpans[i] = 1;
8997 // (0) Determine which cells are visible according to spans
8999 // __________________
9004 // |------------------|
9005 // |__________________| 4
9007 // To calculate cell visibility:
9008 // First find all spanning cells. Build an array of span records with start x, y and end x, y.
9009 // Then for each cell, test whether we're within one of those cells, and unless we're at the start of
9010 // that cell, hide the cell.
9012 // We can also use this array to match the size of spanning cells to the grid. Or just do
9013 // this when we iterate through all cells.
9015 // 0.1: add spanning cells to an array
9016 wxRichTextRectArray rectArray
;
9017 for (j
= 0; j
< m_rowCount
; j
++)
9019 for (i
= 0; i
< m_colCount
; i
++)
9021 wxRichTextBox
* cell
= GetCell(j
, i
);
9022 int colSpan
= 1, rowSpan
= 1;
9023 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9024 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9025 if (cell
->GetProperties().HasProperty(wxT("rowspan")))
9026 rowSpan
= cell
->GetProperties().GetPropertyLong(wxT("rowspan"));
9027 if (colSpan
> 1 || rowSpan
> 1)
9029 rectArray
.Add(wxRect(i
, j
, colSpan
, rowSpan
));
9033 // 0.2: find which cells are subsumed by a spanning cell
9034 for (j
= 0; j
< m_rowCount
; j
++)
9036 for (i
= 0; i
< m_colCount
; i
++)
9038 wxRichTextBox
* cell
= GetCell(j
, i
);
9039 if (rectArray
.GetCount() == 0)
9045 int colSpan
= 1, rowSpan
= 1;
9046 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9047 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9048 if (cell
->GetProperties().HasProperty(wxT("rowspan")))
9049 rowSpan
= cell
->GetProperties().GetPropertyLong(wxT("rowspan"));
9050 if (colSpan
> 1 || rowSpan
> 1)
9052 // Assume all spanning cells are shown
9058 for (k
= 0; k
< (int) rectArray
.GetCount(); k
++)
9060 if (rectArray
[k
].Contains(wxPoint(i
, j
)))
9072 // TODO: find the first spanned cell in each row that spans the most columns and doesn't
9073 // overlap with a spanned cell starting at a previous column position.
9074 // This means we need to keep an array of rects so we can check. However
9075 // it does also mean that some spans simply may not be taken into account
9076 // where there are different spans happening on different rows. In these cases,
9077 // they will simply be as wide as their constituent columns.
9079 // (1) Do an initial layout for all cells to get minimum and maximum size, and get
9080 // the absolute or percentage width of each column.
9082 for (j
= 0; j
< m_rowCount
; j
++)
9084 // First get the overall margins so we can calculate percentage widths based on
9085 // the available content space for all cells on the row
9087 int overallRowContentMargin
= 0;
9088 int visibleCellCount
= 0;
9090 for (i
= 0; i
< m_colCount
; i
++)
9092 wxRichTextBox
* cell
= GetCell(j
, i
);
9093 if (cell
->IsShown())
9095 int cellTotalLeftMargin
= 0, cellTotalRightMargin
= 0, cellTotalTopMargin
= 0, cellTotalBottomMargin
= 0;
9096 GetTotalMargin(dc
, buffer
, cell
->GetAttributes(), cellTotalLeftMargin
, cellTotalRightMargin
, cellTotalTopMargin
, cellTotalBottomMargin
);
9098 overallRowContentMargin
+= (cellTotalLeftMargin
+ cellTotalRightMargin
);
9099 visibleCellCount
++;
9103 // Add in inter-cell padding
9104 overallRowContentMargin
+= ((visibleCellCount
-1) * paddingX
);
9106 int rowContentWidth
= internalTableWidth
- overallRowContentMargin
;
9107 wxSize
rowTableSize(rowContentWidth
, 0);
9108 wxTextAttrDimensionConverter
converter(dc
, scale
, rowTableSize
);
9110 for (i
= 0; i
< m_colCount
; i
++)
9112 wxRichTextBox
* cell
= GetCell(j
, i
);
9113 if (cell
->IsShown())
9116 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9117 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9119 // Lay out cell to find min/max widths
9120 cell
->Invalidate(wxRICHTEXT_ALL
);
9121 cell
->Layout(dc
, context
, availableSpace
, availableSpace
, style
);
9125 int absoluteCellWidth
= -1;
9126 int percentageCellWidth
= -1;
9128 // I think we need to calculate percentages from the internal table size,
9129 // minus the padding between cells which we'll need to calculate from the
9130 // (number of VISIBLE cells - 1)*paddingX. Then percentages that add up to 100%
9131 // will add up to 100%. In CSS, the width specifies the cell's content rect width,
9132 // so if we want to conform to that we'll need to add in the overall cell margins.
9133 // However, this will make it difficult to specify percentages that add up to
9134 // 100% and still fit within the table width.
9135 // Let's say two cells have 50% width. They have 10 pixels of overall margin each.
9136 // The table content rect is 500 pixels and the inter-cell padding is 20 pixels.
9137 // If we're using internal content size for the width, we would calculate the
9138 // the overall cell width for n cells as:
9139 // (500 - 20*(n-1) - overallCellMargin1 - overallCellMargin2 - ...) * percentage / 100
9140 // + thisOverallCellMargin
9141 // = 500 - 20 - 10 - 10) * 0.5 + 10 = 240 pixels overall cell width.
9142 // Adding this back, we get 240 + 240 + 20 = 500 pixels.
9144 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9146 int w
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetWidth());
9147 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
9149 percentageCellWidth
= w
;
9153 absoluteCellWidth
= w
;
9155 // Override absolute width with minimum width if necessary
9156 if (cell
->GetMinSize().x
> 0 && absoluteCellWidth
!=1 && cell
->GetMinSize().x
> absoluteCellWidth
)
9157 absoluteCellWidth
= cell
->GetMinSize().x
;
9160 if (absoluteCellWidth
!= -1)
9162 if (absoluteCellWidth
> absoluteColWidths
[i
])
9163 absoluteColWidths
[i
] = absoluteCellWidth
;
9166 if (percentageCellWidth
!= -1)
9168 if (percentageCellWidth
> percentageColWidths
[i
])
9169 percentageColWidths
[i
] = percentageCellWidth
;
9172 if (colSpan
== 1 && cell
->GetMinSize().x
&& cell
->GetMinSize().x
> minColWidths
[i
])
9173 minColWidths
[i
] = cell
->GetMinSize().x
;
9174 if (colSpan
== 1 && cell
->GetMaxSize().x
&& cell
->GetMaxSize().x
> maxColWidths
[i
])
9175 maxColWidths
[i
] = cell
->GetMaxSize().x
;
9181 // (2) Allocate initial column widths from minimum widths, absolute values and proportions
9182 // TODO: simply merge this into (1).
9183 for (i
= 0; i
< m_colCount
; i
++)
9185 if (absoluteColWidths
[i
] > 0)
9187 colWidths
[i
] = absoluteColWidths
[i
];
9189 else if (percentageColWidths
[i
] > 0)
9191 colWidths
[i
] = percentageColWidths
[i
];
9193 // This is rubbish - we calculated the absolute widths from percentages, so
9194 // we can't do it again here.
9195 //colWidths[i] = (int) (double(percentageColWidths[i]) * double(tableWidth) / 100.0 + 0.5);
9199 // (3) Process absolute or proportional widths of spanning columns,
9200 // now that we know what our fixed column widths are going to be.
9201 // Spanned cells will try to adjust columns so the span will fit.
9202 // Even existing fixed column widths can be expanded if necessary.
9203 // Actually, currently fixed columns widths aren't adjusted; instead,
9204 // the algorithm favours earlier rows and adjusts unspecified column widths
9205 // the first time only. After that, we can't know whether the column has been
9206 // specified explicitly or not. (We could make a note if necessary.)
9207 for (j
= 0; j
< m_rowCount
; j
++)
9209 // First get the overall margins so we can calculate percentage widths based on
9210 // the available content space for all cells on the row
9212 int overallRowContentMargin
= 0;
9213 int visibleCellCount
= 0;
9215 for (i
= 0; i
< m_colCount
; i
++)
9217 wxRichTextBox
* cell
= GetCell(j
, i
);
9218 if (cell
->IsShown())
9220 int cellTotalLeftMargin
= 0, cellTotalRightMargin
= 0, cellTotalTopMargin
= 0, cellTotalBottomMargin
= 0;
9221 GetTotalMargin(dc
, buffer
, cell
->GetAttributes(), cellTotalLeftMargin
, cellTotalRightMargin
, cellTotalTopMargin
, cellTotalBottomMargin
);
9223 overallRowContentMargin
+= (cellTotalLeftMargin
+ cellTotalRightMargin
);
9224 visibleCellCount
++;
9228 // Add in inter-cell padding
9229 overallRowContentMargin
+= ((visibleCellCount
-1) * paddingX
);
9231 int rowContentWidth
= internalTableWidth
- overallRowContentMargin
;
9232 wxSize
rowTableSize(rowContentWidth
, 0);
9233 wxTextAttrDimensionConverter
converter(dc
, scale
, rowTableSize
);
9235 for (i
= 0; i
< m_colCount
; i
++)
9237 wxRichTextBox
* cell
= GetCell(j
, i
);
9238 if (cell
->IsShown())
9241 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9242 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9246 int spans
= wxMin(colSpan
, m_colCount
- i
);
9250 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9252 cellWidth
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetWidth());
9253 // Override absolute width with minimum width if necessary
9254 if (cell
->GetMinSize().x
> 0 && cellWidth
!=1 && cell
->GetMinSize().x
> cellWidth
)
9255 cellWidth
= cell
->GetMinSize().x
;
9259 // Do we want to do this? It's the only chance we get to
9260 // use the cell's min/max sizes, so we need to work out
9261 // how we're going to balance the unspecified spanning cell
9262 // width with the possibility more-constrained constituent cell widths.
9263 // Say there's a tiny bitmap giving it a max width of 10 pixels. We
9264 // don't want to constraint all the spanned columns to fit into this cell.
9265 // OK, let's say that if any of the constituent columns don't fit,
9266 // then we simply stop constraining the columns; instead, we'll just fit the spanning
9267 // cells to the columns later.
9268 cellWidth
= cell
->GetMinSize().x
;
9269 if (cell
->GetMaxSize().x
> cellWidth
)
9270 cellWidth
= cell
->GetMaxSize().x
;
9273 // Subtract the padding between cells
9274 int spanningWidth
= cellWidth
;
9275 spanningWidth
-= paddingX
* (spans
-1);
9277 if (spanningWidth
> 0)
9279 // Now share the spanning width between columns within that span
9280 // TODO: take into account min widths of columns within the span
9281 int spanningWidthLeft
= spanningWidth
;
9282 int stretchColCount
= 0;
9283 for (k
= i
; k
< (i
+spans
); k
++)
9285 if (colWidths
[k
] > 0) // absolute or proportional width has been specified
9286 spanningWidthLeft
-= colWidths
[k
];
9290 // Now divide what's left between the remaining columns
9292 if (stretchColCount
> 0)
9293 colShare
= spanningWidthLeft
/ stretchColCount
;
9294 int colShareRemainder
= spanningWidthLeft
- (colShare
* stretchColCount
);
9296 // If fixed-width columns are currently too big, then we'll later
9297 // stretch the spanned cell to fit.
9299 if (spanningWidthLeft
> 0)
9301 for (k
= i
; k
< (i
+spans
); k
++)
9303 if (colWidths
[k
] <= 0) // absolute or proportional width has not been specified
9305 int newWidth
= colShare
;
9306 if (k
== (i
+spans
-1))
9307 newWidth
+= colShareRemainder
; // ensure all pixels are filled
9308 colWidths
[k
] = newWidth
;
9319 // (4) Next, share any remaining space out between columns that have not yet been calculated.
9320 // TODO: take into account min widths of columns within the span
9321 int tableWidthMinusPadding
= internalTableWidth
- (m_colCount
-1)*paddingX
;
9322 int widthLeft
= tableWidthMinusPadding
;
9323 int stretchColCount
= 0;
9324 for (i
= 0; i
< m_colCount
; i
++)
9326 // TODO: we need to take into account min widths.
9327 // Subtract min width from width left, then
9328 // add the colShare to the min width
9329 if (colWidths
[i
] > 0) // absolute or proportional width has been specified
9330 widthLeft
-= colWidths
[i
];
9333 if (minColWidths
[i
] > 0)
9334 widthLeft
-= minColWidths
[i
];
9340 // Now divide what's left between the remaining columns
9342 if (stretchColCount
> 0)
9343 colShare
= widthLeft
/ stretchColCount
;
9344 int colShareRemainder
= widthLeft
- (colShare
* stretchColCount
);
9346 // Check we don't have enough space, in which case shrink all columns, overriding
9347 // any absolute/proportional widths
9348 // TODO: actually we would like to divide up the shrinkage according to size.
9349 // How do we calculate the proportions that will achieve this?
9350 // Could first choose an arbitrary value for stretching cells, and then calculate
9351 // factors to multiply each width by.
9352 // TODO: want to record this fact and pass to an iteration that tries e.g. min widths
9353 if (widthLeft
< 0 || (stretchToFitTableWidth
&& (stretchColCount
== 0)))
9355 colShare
= tableWidthMinusPadding
/ m_colCount
;
9356 colShareRemainder
= tableWidthMinusPadding
- (colShare
* m_colCount
);
9357 for (i
= 0; i
< m_colCount
; i
++)
9360 minColWidths
[i
] = 0;
9364 // We have to adjust the columns if either we need to shrink the
9365 // table to fit the parent/table width, or we explicitly set the
9366 // table width and need to stretch out the table.
9367 if (widthLeft
< 0 || stretchToFitTableWidth
)
9369 for (i
= 0; i
< m_colCount
; i
++)
9371 if (colWidths
[i
] <= 0) // absolute or proportional width has not been specified
9373 if (minColWidths
[i
] > 0)
9374 colWidths
[i
] = minColWidths
[i
] + colShare
;
9376 colWidths
[i
] = colShare
;
9377 if (i
== (m_colCount
-1))
9378 colWidths
[i
] += colShareRemainder
; // ensure all pixels are filled
9383 // TODO: if spanned cells have no specified or max width, make them the
9384 // as big as the columns they span. Do this for all spanned cells in all
9385 // rows, of course. Size any spanned cells left over at the end - even if they
9386 // have width > 0, make sure they're limited to the appropriate column edge.
9390 Sort out confusion between content width
9391 and overall width later. For now, assume we specify overall width.
9393 So, now we've laid out the table to fit into the given space
9394 and have used specified widths and minimum widths.
9396 Now we need to consider how we will try to take maximum width into account.
9400 // (??) TODO: take max width into account
9402 // (6) Lay out all cells again with the current values
9405 int y
= availableSpace
.y
;
9406 for (j
= 0; j
< m_rowCount
; j
++)
9408 int x
= availableSpace
.x
; // TODO: take into account centering etc.
9409 int maxCellHeight
= 0;
9410 int maxSpecifiedCellHeight
= 0;
9412 wxArrayInt actualWidths
;
9413 actualWidths
.Add(0, m_colCount
);
9415 wxTextAttrDimensionConverter
converter(dc
, scale
);
9416 for (i
= 0; i
< m_colCount
; i
++)
9418 wxRichTextCell
* cell
= GetCell(j
, i
);
9419 if (cell
->IsShown())
9421 // Get max specified cell height
9422 // Don't handle percentages for height
9423 if (cell
->GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && cell
->GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() != wxTEXT_ATTR_UNITS_PERCENTAGE
)
9425 int h
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetHeight());
9426 if (h
> maxSpecifiedCellHeight
)
9427 maxSpecifiedCellHeight
= h
;
9430 if (colWidths
[i
] > 0) // absolute or proportional width has been specified
9433 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9434 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9436 wxRect availableCellSpace
;
9438 // TODO: take into acount spans
9441 // Calculate the size of this spanning cell from its constituent columns
9443 int spans
= wxMin(colSpan
, m_colCount
- i
);
9444 for (k
= i
; k
< spans
; k
++)
9450 availableCellSpace
= wxRect(x
, y
, xx
, -1);
9453 availableCellSpace
= wxRect(x
, y
, colWidths
[i
], -1);
9455 // Store actual width so we can force cell to be the appropriate width on the final loop
9456 actualWidths
[i
] = availableCellSpace
.GetWidth();
9459 cell
->Invalidate(wxRICHTEXT_ALL
);
9460 cell
->Layout(dc
, context
, availableCellSpace
, availableSpace
, style
);
9462 // TODO: use GetCachedSize().x to compute 'natural' size
9464 x
+= (availableCellSpace
.GetWidth() + paddingX
);
9465 if (cell
->GetCachedSize().y
> maxCellHeight
)
9466 maxCellHeight
= cell
->GetCachedSize().y
;
9471 maxCellHeight
= wxMax(maxCellHeight
, maxSpecifiedCellHeight
);
9473 for (i
= 0; i
< m_colCount
; i
++)
9475 wxRichTextCell
* cell
= GetCell(j
, i
);
9476 if (cell
->IsShown())
9478 wxRect availableCellSpace
= wxRect(cell
->GetPosition(), wxSize(actualWidths
[i
], maxCellHeight
));
9479 // Lay out cell with new height
9480 cell
->Invalidate(wxRICHTEXT_ALL
);
9481 cell
->Layout(dc
, context
, availableCellSpace
, availableSpace
, style
);
9483 // Make sure the cell size really is the appropriate size,
9484 // not the calculated box size
9485 cell
->SetCachedSize(wxSize(actualWidths
[i
], maxCellHeight
));
9487 maxRight
= wxMax(maxRight
, cell
->GetPosition().x
+ cell
->GetCachedSize().x
);
9492 if (j
< (m_rowCount
-1))
9496 // We need to add back the margins etc.
9498 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
9499 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxRight
- availableSpace
.x
, y
- availableSpace
.y
));
9500 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
9501 SetCachedSize(marginRect
.GetSize());
9504 // TODO: calculate max size
9506 SetMaxSize(GetCachedSize());
9509 // TODO: calculate min size
9511 SetMinSize(GetCachedSize());
9514 // TODO: currently we use either a fixed table width or the parent's size.
9515 // We also want to be able to calculate the table width from its content,
9516 // whether using fixed column widths or cell content min/max width.
9517 // Probably need a boolean flag to say whether we need to stretch cells
9518 // to fit the table width, or to simply use min/max cell widths. The
9519 // trouble with this is that if cell widths are not specified, they
9520 // will be tiny; we could use arbitrary defaults but this seems unsatisfactory.
9521 // Anyway, ignoring that problem, we probably need to factor layout into a function
9522 // that can can calculate the maximum unconstrained layout in case table size is
9523 // not specified. Then LayoutToBestSize() can choose to use either parent size to
9524 // constrain Layout(), or the previously-calculated max size to constraint layout.
9529 // Finds the absolute position and row height for the given character position
9530 bool wxRichTextTable::FindPosition(wxDC
& dc
, wxRichTextDrawingContext
& context
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
9532 wxRichTextCell
* child
= GetCell(index
+1);
9535 // Find the position at the start of the child cell, since the table doesn't
9536 // have any caret position of its own.
9537 return child
->FindPosition(dc
, context
, -1, pt
, height
, forceLineStart
);
9543 // Get the cell at the given character position (in the range of the table).
9544 wxRichTextCell
* wxRichTextTable::GetCell(long pos
) const
9546 int row
= 0, col
= 0;
9547 if (GetCellRowColumnPosition(pos
, row
, col
))
9549 return GetCell(row
, col
);
9555 // Get the row/column for a given character position
9556 bool wxRichTextTable::GetCellRowColumnPosition(long pos
, int& row
, int& col
) const
9558 if (m_colCount
== 0 || m_rowCount
== 0)
9561 row
= (int) (pos
/ m_colCount
);
9562 col
= pos
- (row
* m_colCount
);
9564 wxASSERT(row
< m_rowCount
&& col
< m_colCount
);
9566 if (row
< m_rowCount
&& col
< m_colCount
)
9572 // Calculate range, taking row/cell ordering into account instead of relying
9573 // on list ordering.
9574 void wxRichTextTable::CalculateRange(long start
, long& end
)
9576 long current
= start
;
9577 long lastEnd
= current
;
9586 for (i
= 0; i
< m_rowCount
; i
++)
9588 for (j
= 0; j
< m_colCount
; j
++)
9590 wxRichTextCell
* child
= GetCell(i
, j
);
9595 child
->CalculateRange(current
, childEnd
);
9598 current
= childEnd
+ 1;
9603 // A top-level object always has a range of size 1,
9604 // because its children don't count at this level.
9606 m_range
.SetRange(start
, start
);
9608 // An object with no children has zero length
9609 if (m_children
.GetCount() == 0)
9611 m_ownRange
.SetRange(0, lastEnd
);
9614 // Gets the range size.
9615 bool wxRichTextTable::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
9617 return wxRichTextBox::GetRangeSize(range
, size
, descent
, dc
, context
, flags
, position
, partialExtents
);
9620 // Deletes content in the given range.
9621 bool wxRichTextTable::DeleteRange(const wxRichTextRange
& WXUNUSED(range
))
9623 // TODO: implement deletion of cells
9627 // Gets any text in this object for the given range.
9628 wxString
wxRichTextTable::GetTextForRange(const wxRichTextRange
& range
) const
9630 return wxRichTextBox::GetTextForRange(range
);
9633 // Copies this object.
9634 void wxRichTextTable::Copy(const wxRichTextTable
& obj
)
9636 wxRichTextBox::Copy(obj
);
9640 m_rowCount
= obj
.m_rowCount
;
9641 m_colCount
= obj
.m_colCount
;
9643 m_cells
.Add(wxRichTextObjectPtrArray(), m_rowCount
);
9646 for (i
= 0; i
< m_rowCount
; i
++)
9648 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
9649 for (j
= 0; j
< m_colCount
; j
++)
9651 wxRichTextCell
* cell
= wxDynamicCast(obj
.GetCell(i
, j
)->Clone(), wxRichTextCell
);
9659 void wxRichTextTable::ClearTable()
9665 bool wxRichTextTable::CreateTable(int rows
, int cols
)
9672 m_cells
.Add(wxRichTextObjectPtrArray(), rows
);
9675 for (i
= 0; i
< rows
; i
++)
9677 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
9678 for (j
= 0; j
< cols
; j
++)
9680 wxRichTextCell
* cell
= new wxRichTextCell
;
9682 cell
->AddParagraph(wxEmptyString
);
9691 wxRichTextCell
* wxRichTextTable::GetCell(int row
, int col
) const
9693 wxASSERT(row
< m_rowCount
);
9694 wxASSERT(col
< m_colCount
);
9696 if (row
< m_rowCount
&& col
< m_colCount
)
9698 wxRichTextObjectPtrArray
& colArray
= m_cells
[row
];
9699 wxRichTextObject
* obj
= colArray
[col
];
9700 return wxDynamicCast(obj
, wxRichTextCell
);
9706 // Returns a selection object specifying the selections between start and end character positions.
9707 // For example, a table would deduce what cells (of range length 1) are selected when dragging across the table.
9708 wxRichTextSelection
wxRichTextTable::GetSelection(long start
, long end
) const
9710 wxRichTextSelection selection
;
9711 selection
.SetContainer((wxRichTextTable
*) this);
9720 wxASSERT( start
>= 0 && end
< (m_colCount
* m_rowCount
));
9722 if (end
>= (m_colCount
* m_rowCount
))
9725 // We need to find the rectangle of cells that is described by the rectangle
9726 // with start, end as the diagonal. Make sure we don't add cells that are
9727 // not currenty visible because they are overlapped by spanning cells.
9729 --------------------------
9730 | 0 | 1 | 2 | 3 | 4 |
9731 --------------------------
9732 | 5 | 6 | 7 | 8 | 9 |
9733 --------------------------
9734 | 10 | 11 | 12 | 13 | 14 |
9735 --------------------------
9736 | 15 | 16 | 17 | 18 | 19 |
9737 --------------------------
9739 Let's say we select 6 -> 18.
9741 Left and right edge cols of rectangle are 1 and 3 inclusive. Find least/greatest to find
9742 which is left and which is right.
9744 Top and bottom edge rows are 1 and 3 inclusive. Again, find least/greatest to find top and bottom.
9746 Now go through rows from 1 to 3 and only add cells that are (a) within above column range
9752 int leftCol
= start
- m_colCount
* int(start
/m_colCount
);
9753 int rightCol
= end
- m_colCount
* int(end
/m_colCount
);
9755 int topRow
= int(start
/m_colCount
);
9756 int bottomRow
= int(end
/m_colCount
);
9758 if (leftCol
> rightCol
)
9765 if (topRow
> bottomRow
)
9767 int tmp
= bottomRow
;
9773 for (i
= topRow
; i
<= bottomRow
; i
++)
9775 for (j
= leftCol
; j
<= rightCol
; j
++)
9777 wxRichTextCell
* cell
= GetCell(i
, j
);
9778 if (cell
&& cell
->IsShown())
9779 selection
.Add(cell
->GetRange());
9786 // Sets the attributes for the cells specified by the selection.
9787 bool wxRichTextTable::SetCellStyle(const wxRichTextSelection
& selection
, const wxRichTextAttr
& style
, int flags
)
9789 if (selection
.GetContainer() != this)
9792 wxRichTextBuffer
* buffer
= GetBuffer();
9793 bool haveControl
= (buffer
&& buffer
->GetRichTextCtrl() != NULL
);
9794 bool withUndo
= haveControl
&& ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
9797 buffer
->BeginBatchUndo(_("Set Cell Style"));
9799 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
9802 wxRichTextCell
* cell
= wxDynamicCast(node
->GetData(), wxRichTextCell
);
9803 if (cell
&& selection
.WithinSelection(cell
->GetRange().GetStart()))
9804 SetStyle(cell
, style
, flags
);
9805 node
= node
->GetNext();
9808 // Do action, or delay it until end of batch.
9810 buffer
->EndBatchUndo();
9815 bool wxRichTextTable::DeleteRows(int startRow
, int noRows
)
9817 wxASSERT((startRow
+ noRows
) < m_rowCount
);
9818 if ((startRow
+ noRows
) >= m_rowCount
)
9822 for (i
= startRow
; i
< (startRow
+noRows
); i
++)
9824 wxRichTextObjectPtrArray
& colArray
= m_cells
[startRow
];
9825 for (j
= 0; j
< (int) colArray
.GetCount(); j
++)
9827 wxRichTextObject
* cell
= colArray
[j
];
9828 RemoveChild(cell
, true);
9831 // Keep deleting at the same position, since we move all
9833 m_cells
.RemoveAt(startRow
);
9836 m_rowCount
= m_rowCount
- noRows
;
9841 bool wxRichTextTable::DeleteColumns(int startCol
, int noCols
)
9843 wxASSERT((startCol
+ noCols
) < m_colCount
);
9844 if ((startCol
+ noCols
) >= m_colCount
)
9847 bool deleteRows
= (noCols
== m_colCount
);
9850 for (i
= 0; i
< m_rowCount
; i
++)
9852 wxRichTextObjectPtrArray
& colArray
= m_cells
[deleteRows
? 0 : i
];
9853 for (j
= startCol
; j
< (startCol
+noCols
); j
++)
9855 wxRichTextObject
* cell
= colArray
[j
];
9856 RemoveChild(cell
, true);
9860 m_cells
.RemoveAt(0);
9865 m_colCount
= m_colCount
- noCols
;
9870 bool wxRichTextTable::AddRows(int startRow
, int noRows
, const wxRichTextAttr
& attr
)
9872 wxASSERT(startRow
<= m_rowCount
);
9873 if (startRow
> m_rowCount
)
9877 for (i
= 0; i
< noRows
; i
++)
9880 if (startRow
== m_rowCount
)
9882 m_cells
.Add(wxRichTextObjectPtrArray());
9883 idx
= m_cells
.GetCount() - 1;
9887 m_cells
.Insert(wxRichTextObjectPtrArray(), startRow
+i
);
9891 wxRichTextObjectPtrArray
& colArray
= m_cells
[idx
];
9892 for (j
= 0; j
< m_colCount
; j
++)
9894 wxRichTextCell
* cell
= new wxRichTextCell
;
9895 cell
->GetAttributes() = attr
;
9902 m_rowCount
= m_rowCount
+ noRows
;
9906 bool wxRichTextTable::AddColumns(int startCol
, int noCols
, const wxRichTextAttr
& attr
)
9908 wxASSERT(startCol
<= m_colCount
);
9909 if (startCol
> m_colCount
)
9913 for (i
= 0; i
< m_rowCount
; i
++)
9915 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
9916 for (j
= 0; j
< noCols
; j
++)
9918 wxRichTextCell
* cell
= new wxRichTextCell
;
9919 cell
->GetAttributes() = attr
;
9923 if (startCol
== m_colCount
)
9926 colArray
.Insert(cell
, startCol
+j
);
9930 m_colCount
= m_colCount
+ noCols
;
9935 // Edit properties via a GUI
9936 bool wxRichTextTable::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
9938 wxRichTextObjectPropertiesDialog
boxDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Table Properties"));
9939 boxDlg
.SetAttributes(GetAttributes());
9941 if (boxDlg
.ShowModal() == wxID_OK
)
9943 boxDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
9951 * Module to initialise and clean up handlers
9954 class wxRichTextModule
: public wxModule
9956 DECLARE_DYNAMIC_CLASS(wxRichTextModule
)
9958 wxRichTextModule() {}
9961 wxRichTextBuffer::SetRenderer(new wxRichTextStdRenderer
);
9962 wxRichTextBuffer::InitStandardHandlers();
9963 wxRichTextParagraph::InitDefaultTabs();
9965 wxRichTextXMLHandler::RegisterNodeName(wxT("text"), wxT("wxRichTextPlainText"));
9966 wxRichTextXMLHandler::RegisterNodeName(wxT("symbol"), wxT("wxRichTextPlainText"));
9967 wxRichTextXMLHandler::RegisterNodeName(wxT("image"), wxT("wxRichTextImage"));
9968 wxRichTextXMLHandler::RegisterNodeName(wxT("paragraph"), wxT("wxRichTextParagraph"));
9969 wxRichTextXMLHandler::RegisterNodeName(wxT("paragraphlayout"), wxT("wxRichTextParagraphLayoutBox"));
9970 wxRichTextXMLHandler::RegisterNodeName(wxT("textbox"), wxT("wxRichTextBox"));
9971 wxRichTextXMLHandler::RegisterNodeName(wxT("cell"), wxT("wxRichTextCell"));
9972 wxRichTextXMLHandler::RegisterNodeName(wxT("table"), wxT("wxRichTextTable"));
9973 wxRichTextXMLHandler::RegisterNodeName(wxT("field"), wxT("wxRichTextField"));
9979 wxRichTextBuffer::CleanUpHandlers();
9980 wxRichTextBuffer::CleanUpDrawingHandlers();
9981 wxRichTextBuffer::CleanUpFieldTypes();
9982 wxRichTextXMLHandler::ClearNodeToClassMap();
9983 wxRichTextDecimalToRoman(-1);
9984 wxRichTextParagraph::ClearDefaultTabs();
9985 wxRichTextCtrl::ClearAvailableFontNames();
9986 wxRichTextBuffer::SetRenderer(NULL
);
9990 IMPLEMENT_DYNAMIC_CLASS(wxRichTextModule
, wxModule
)
9993 // If the richtext lib is dynamically loaded after the app has already started
9994 // (such as from wxPython) then the built-in module system will not init this
9995 // module. Provide this function to do it manually.
9996 void wxRichTextModuleInit()
9998 wxModule
* module = new wxRichTextModule
;
10000 wxModule::RegisterModule(module);
10005 * Commands for undo/redo
10009 wxRichTextCommand::wxRichTextCommand(const wxString
& name
, wxRichTextCommandId id
, wxRichTextBuffer
* buffer
,
10010 wxRichTextParagraphLayoutBox
* container
, wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
): wxCommand(true, name
)
10012 /* wxRichTextAction* action = */ new wxRichTextAction(this, name
, id
, buffer
, container
, ctrl
, ignoreFirstTime
);
10015 wxRichTextCommand::wxRichTextCommand(const wxString
& name
): wxCommand(true, name
)
10019 wxRichTextCommand::~wxRichTextCommand()
10024 void wxRichTextCommand::AddAction(wxRichTextAction
* action
)
10026 if (!m_actions
.Member(action
))
10027 m_actions
.Append(action
);
10030 bool wxRichTextCommand::Do()
10032 for (wxList::compatibility_iterator node
= m_actions
.GetFirst(); node
; node
= node
->GetNext())
10034 wxRichTextAction
* action
= (wxRichTextAction
*) node
->GetData();
10041 bool wxRichTextCommand::Undo()
10043 for (wxList::compatibility_iterator node
= m_actions
.GetLast(); node
; node
= node
->GetPrevious())
10045 wxRichTextAction
* action
= (wxRichTextAction
*) node
->GetData();
10052 void wxRichTextCommand::ClearActions()
10054 WX_CLEAR_LIST(wxList
, m_actions
);
10058 * Individual action
10062 wxRichTextAction::wxRichTextAction(wxRichTextCommand
* cmd
, const wxString
& name
, wxRichTextCommandId id
,
10063 wxRichTextBuffer
* buffer
, wxRichTextParagraphLayoutBox
* container
,
10064 wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
)
10068 m_containerAddress
.Create(buffer
, container
);
10069 m_ignoreThis
= ignoreFirstTime
;
10074 m_newParagraphs
.SetDefaultStyle(buffer
->GetDefaultStyle());
10075 m_newParagraphs
.SetBasicStyle(buffer
->GetBasicStyle());
10077 cmd
->AddAction(this);
10080 wxRichTextAction::~wxRichTextAction()
10086 // Returns the container that this action refers to, using the container address and top-level buffer.
10087 wxRichTextParagraphLayoutBox
* wxRichTextAction::GetContainer() const
10089 wxRichTextParagraphLayoutBox
* container
= wxDynamicCast(GetContainerAddress().GetObject(m_buffer
), wxRichTextParagraphLayoutBox
);
10094 void wxRichTextAction::CalculateRefreshOptimizations(wxArrayInt
& optimizationLineCharPositions
, wxArrayInt
& optimizationLineYPositions
)
10096 // Store a list of line start character and y positions so we can figure out which area
10097 // we need to refresh
10099 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10100 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10101 wxASSERT(container
!= NULL
);
10105 // NOTE: we're assuming that the buffer is laid out correctly at this point.
10106 // If we had several actions, which only invalidate and leave layout until the
10107 // paint handler is called, then this might not be true. So we may need to switch
10108 // optimisation on only when we're simply adding text and not simultaneously
10109 // deleting a selection, for example. Or, we make sure the buffer is laid out correctly
10110 // first, but of course this means we'll be doing it twice.
10111 if (!m_buffer
->IsDirty() && m_ctrl
) // can only do optimisation if the buffer is already laid out correctly
10113 wxSize clientSize
= m_ctrl
->GetUnscaledSize(m_ctrl
->GetClientSize());
10114 wxPoint firstVisiblePt
= m_ctrl
->GetUnscaledPoint(m_ctrl
->GetFirstVisiblePoint());
10115 int lastY
= firstVisiblePt
.y
+ clientSize
.y
;
10117 wxRichTextParagraph
* para
= container
->GetParagraphAtPosition(GetRange().GetStart());
10118 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(para
);
10121 wxRichTextParagraph
* child
= (wxRichTextParagraph
*) node
->GetData();
10122 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
10125 wxRichTextLine
* line
= node2
->GetData();
10126 wxPoint pt
= line
->GetAbsolutePosition();
10127 wxRichTextRange range
= line
->GetAbsoluteRange();
10131 node2
= wxRichTextLineList::compatibility_iterator();
10132 node
= wxRichTextObjectList::compatibility_iterator();
10134 else if (range
.GetStart() > GetPosition() && pt
.y
>= firstVisiblePt
.y
)
10136 optimizationLineCharPositions
.Add(range
.GetStart());
10137 optimizationLineYPositions
.Add(pt
.y
);
10141 node2
= node2
->GetNext();
10145 node
= node
->GetNext();
10151 bool wxRichTextAction::Do()
10153 m_buffer
->Modify(true);
10155 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10156 wxASSERT(container
!= NULL
);
10162 case wxRICHTEXT_INSERT
:
10164 // Store a list of line start character and y positions so we can figure out which area
10165 // we need to refresh
10166 wxArrayInt optimizationLineCharPositions
;
10167 wxArrayInt optimizationLineYPositions
;
10169 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10170 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
10173 container
->InsertFragment(GetRange().GetStart(), m_newParagraphs
);
10174 container
->UpdateRanges();
10176 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10177 // Layout() would stop prematurely at the top level.
10178 container
->InvalidateHierarchy(wxRichTextRange(wxMax(0, GetRange().GetStart()-1), GetRange().GetEnd()));
10180 long newCaretPosition
= GetPosition() + m_newParagraphs
.GetOwnRange().GetLength();
10182 // Character position to caret position
10183 newCaretPosition
--;
10185 // Don't take into account the last newline
10186 if (m_newParagraphs
.GetPartialParagraph())
10187 newCaretPosition
--;
10189 if (m_newParagraphs
.GetChildren().GetCount() > 1)
10191 wxRichTextObject
* p
= (wxRichTextObject
*) m_newParagraphs
.GetChildren().GetLast()->GetData();
10192 if (p
->GetRange().GetLength() == 1)
10193 newCaretPosition
--;
10196 newCaretPosition
= wxMin(newCaretPosition
, (container
->GetOwnRange().GetEnd()-1));
10198 UpdateAppearance(newCaretPosition
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */);
10200 wxRichTextEvent
cmdEvent(
10201 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED
,
10202 m_ctrl
? m_ctrl
->GetId() : -1);
10203 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10204 cmdEvent
.SetRange(GetRange());
10205 cmdEvent
.SetPosition(GetRange().GetStart());
10206 cmdEvent
.SetContainer(container
);
10208 m_buffer
->SendEvent(cmdEvent
);
10212 case wxRICHTEXT_DELETE
:
10214 wxArrayInt optimizationLineCharPositions
;
10215 wxArrayInt optimizationLineYPositions
;
10217 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10218 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
10221 container
->DeleteRange(GetRange());
10222 container
->UpdateRanges();
10223 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10224 // Layout() would stop prematurely at the top level.
10225 container
->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
10227 long caretPos
= GetRange().GetStart()-1;
10228 if (caretPos
>= container
->GetOwnRange().GetEnd())
10231 UpdateAppearance(caretPos
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */);
10233 wxRichTextEvent
cmdEvent(
10234 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED
,
10235 m_ctrl
? m_ctrl
->GetId() : -1);
10236 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10237 cmdEvent
.SetRange(GetRange());
10238 cmdEvent
.SetPosition(GetRange().GetStart());
10239 cmdEvent
.SetContainer(container
);
10241 m_buffer
->SendEvent(cmdEvent
);
10245 case wxRICHTEXT_CHANGE_STYLE
:
10246 case wxRICHTEXT_CHANGE_PROPERTIES
:
10248 ApplyParagraphs(GetNewParagraphs());
10250 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10251 // Layout() would stop prematurely at the top level.
10252 container
->InvalidateHierarchy(GetRange());
10254 UpdateAppearance(GetPosition());
10256 wxRichTextEvent
cmdEvent(
10257 m_cmdId
== wxRICHTEXT_CHANGE_STYLE
? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
: wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED
,
10258 m_ctrl
? m_ctrl
->GetId() : -1);
10259 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10260 cmdEvent
.SetRange(GetRange());
10261 cmdEvent
.SetPosition(GetRange().GetStart());
10262 cmdEvent
.SetContainer(container
);
10264 m_buffer
->SendEvent(cmdEvent
);
10268 case wxRICHTEXT_CHANGE_ATTRIBUTES
:
10270 wxRichTextObject
* obj
= m_objectAddress
.GetObject(m_buffer
); // container->GetChildAtPosition(GetRange().GetStart());
10273 wxRichTextAttr oldAttr
= obj
->GetAttributes();
10274 obj
->GetAttributes() = m_attributes
;
10275 m_attributes
= oldAttr
;
10278 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10279 // Layout() would stop prematurely at the top level.
10280 container
->InvalidateHierarchy(GetRange());
10282 UpdateAppearance(GetPosition());
10284 wxRichTextEvent
cmdEvent(
10285 wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
,
10286 m_ctrl
? m_ctrl
->GetId() : -1);
10287 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10288 cmdEvent
.SetRange(GetRange());
10289 cmdEvent
.SetPosition(GetRange().GetStart());
10290 cmdEvent
.SetContainer(container
);
10292 m_buffer
->SendEvent(cmdEvent
);
10296 case wxRICHTEXT_CHANGE_OBJECT
:
10298 wxRichTextObject
* obj
= m_objectAddress
.GetObject(m_buffer
);
10299 // wxRichTextObject* obj = container->GetChildAtPosition(GetRange().GetStart());
10300 if (obj
&& m_object
)
10302 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(obj
);
10305 wxRichTextObject
* obj
= node
->GetData();
10306 node
->SetData(m_object
);
10311 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10312 // Layout() would stop prematurely at the top level.
10313 container
->InvalidateHierarchy(GetRange());
10315 UpdateAppearance(GetPosition());
10317 // TODO: send new kind of modification event
10328 bool wxRichTextAction::Undo()
10330 m_buffer
->Modify(true);
10332 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10333 wxASSERT(container
!= NULL
);
10339 case wxRICHTEXT_INSERT
:
10341 wxArrayInt optimizationLineCharPositions
;
10342 wxArrayInt optimizationLineYPositions
;
10344 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10345 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
10348 container
->DeleteRange(GetRange());
10349 container
->UpdateRanges();
10351 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10352 // Layout() would stop prematurely at the top level.
10353 container
->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
10355 long newCaretPosition
= GetPosition() - 1;
10357 UpdateAppearance(newCaretPosition
, true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */);
10359 wxRichTextEvent
cmdEvent(
10360 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED
,
10361 m_ctrl
? m_ctrl
->GetId() : -1);
10362 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10363 cmdEvent
.SetRange(GetRange());
10364 cmdEvent
.SetPosition(GetRange().GetStart());
10365 cmdEvent
.SetContainer(container
);
10367 m_buffer
->SendEvent(cmdEvent
);
10371 case wxRICHTEXT_DELETE
:
10373 wxArrayInt optimizationLineCharPositions
;
10374 wxArrayInt optimizationLineYPositions
;
10376 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10377 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
10380 container
->InsertFragment(GetRange().GetStart(), m_oldParagraphs
);
10381 container
->UpdateRanges();
10383 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10384 // Layout() would stop prematurely at the top level.
10385 container
->InvalidateHierarchy(GetRange());
10387 UpdateAppearance(GetPosition(), true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */);
10389 wxRichTextEvent
cmdEvent(
10390 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED
,
10391 m_ctrl
? m_ctrl
->GetId() : -1);
10392 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10393 cmdEvent
.SetRange(GetRange());
10394 cmdEvent
.SetPosition(GetRange().GetStart());
10395 cmdEvent
.SetContainer(container
);
10397 m_buffer
->SendEvent(cmdEvent
);
10401 case wxRICHTEXT_CHANGE_STYLE
:
10402 case wxRICHTEXT_CHANGE_PROPERTIES
:
10404 ApplyParagraphs(GetOldParagraphs());
10405 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10406 // Layout() would stop prematurely at the top level.
10407 container
->InvalidateHierarchy(GetRange());
10409 UpdateAppearance(GetPosition());
10411 wxRichTextEvent
cmdEvent(
10412 m_cmdId
== wxRICHTEXT_CHANGE_STYLE
? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
: wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED
,
10413 m_ctrl
? m_ctrl
->GetId() : -1);
10414 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10415 cmdEvent
.SetRange(GetRange());
10416 cmdEvent
.SetPosition(GetRange().GetStart());
10417 cmdEvent
.SetContainer(container
);
10419 m_buffer
->SendEvent(cmdEvent
);
10423 case wxRICHTEXT_CHANGE_ATTRIBUTES
:
10424 case wxRICHTEXT_CHANGE_OBJECT
:
10435 /// Update the control appearance
10436 void wxRichTextAction::UpdateAppearance(long caretPosition
, bool sendUpdateEvent
, wxArrayInt
* optimizationLineCharPositions
, wxArrayInt
* optimizationLineYPositions
, bool isDoCmd
)
10438 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10439 wxASSERT(container
!= NULL
);
10445 m_ctrl
->SetFocusObject(container
);
10446 m_ctrl
->SetCaretPosition(caretPosition
);
10448 if (!m_ctrl
->IsFrozen())
10450 wxRect containerRect
= container
->GetRect();
10452 m_ctrl
->LayoutContent();
10454 // Refresh everything if there were floating objects or the container changed size
10455 // (we can't yet optimize in these cases, since more complex interaction with other content occurs)
10456 if (container
->GetFloatingObjectCount() > 0 || (container
->GetParent() && containerRect
!= container
->GetRect()))
10458 m_ctrl
->Refresh(false);
10462 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10463 // Find refresh rectangle if we are in a position to optimise refresh
10464 if ((m_cmdId
== wxRICHTEXT_INSERT
|| m_cmdId
== wxRICHTEXT_DELETE
) && optimizationLineCharPositions
)
10468 wxSize clientSize
= m_ctrl
->GetUnscaledSize(m_ctrl
->GetClientSize());
10469 wxPoint firstVisiblePt
= m_ctrl
->GetUnscaledPoint(m_ctrl
->GetFirstVisiblePoint());
10471 // Start/end positions
10473 int lastY
= firstVisiblePt
.y
+ clientSize
.y
;
10475 bool foundEnd
= false;
10477 // position offset - how many characters were inserted
10478 int positionOffset
= GetRange().GetLength();
10480 // Determine whether this is Do or Undo, and adjust positionOffset accordingly
10481 if ((m_cmdId
== wxRICHTEXT_DELETE
&& isDoCmd
) || (m_cmdId
== wxRICHTEXT_INSERT
&& !isDoCmd
))
10482 positionOffset
= - positionOffset
;
10484 // find the first line which is being drawn at the same position as it was
10485 // before. Since we're talking about a simple insertion, we can assume
10486 // that the rest of the window does not need to be redrawn.
10488 wxRichTextParagraph
* para
= container
->GetParagraphAtPosition(GetPosition());
10489 // Since we support floating layout, we should redraw the whole para instead of just
10490 // the first line touching the invalid range.
10493 firstY
= para
->GetPosition().y
;
10496 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(para
);
10499 wxRichTextParagraph
* child
= (wxRichTextParagraph
*) node
->GetData();
10500 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
10503 wxRichTextLine
* line
= node2
->GetData();
10504 wxPoint pt
= line
->GetAbsolutePosition();
10505 wxRichTextRange range
= line
->GetAbsoluteRange();
10507 // we want to find the first line that is in the same position
10508 // as before. This will mean we're at the end of the changed text.
10510 if (pt
.y
> lastY
) // going past the end of the window, no more info
10512 node2
= wxRichTextLineList::compatibility_iterator();
10513 node
= wxRichTextObjectList::compatibility_iterator();
10515 // Detect last line in the buffer
10516 else if (!node2
->GetNext() && para
->GetRange().Contains(container
->GetOwnRange().GetEnd()))
10518 // If deleting text, make sure we refresh below as well as above
10519 if (positionOffset
>= 0)
10522 lastY
= pt
.y
+ line
->GetSize().y
;
10525 node2
= wxRichTextLineList::compatibility_iterator();
10526 node
= wxRichTextObjectList::compatibility_iterator();
10532 // search for this line being at the same position as before
10533 for (i
= 0; i
< optimizationLineCharPositions
->GetCount(); i
++)
10535 if (((*optimizationLineCharPositions
)[i
] + positionOffset
== range
.GetStart()) &&
10536 ((*optimizationLineYPositions
)[i
] == pt
.y
))
10538 // Stop, we're now the same as we were
10543 node2
= wxRichTextLineList::compatibility_iterator();
10544 node
= wxRichTextObjectList::compatibility_iterator();
10552 node2
= node2
->GetNext();
10556 node
= node
->GetNext();
10559 firstY
= wxMax(firstVisiblePt
.y
, firstY
);
10561 lastY
= firstVisiblePt
.y
+ clientSize
.y
;
10563 // Convert to device coordinates
10564 wxRect
rect(m_ctrl
->GetPhysicalPoint(m_ctrl
->GetScaledPoint(wxPoint(firstVisiblePt
.x
, firstY
))), m_ctrl
->GetScaledSize(wxSize(clientSize
.x
, lastY
- firstY
)));
10565 m_ctrl
->RefreshRect(rect
);
10569 m_ctrl
->Refresh(false);
10571 m_ctrl
->PositionCaret();
10573 // This causes styles to persist when doing programmatic
10574 // content creation except when Freeze/Thaw is used, so
10575 // disable this and check for the consequences.
10576 // m_ctrl->SetDefaultStyleToCursorStyle();
10578 if (sendUpdateEvent
)
10579 wxTextCtrl::SendTextUpdatedEvent(m_ctrl
);
10584 /// Replace the buffer paragraphs with the new ones.
10585 void wxRichTextAction::ApplyParagraphs(const wxRichTextParagraphLayoutBox
& fragment
)
10587 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10588 wxASSERT(container
!= NULL
);
10592 wxRichTextObjectList::compatibility_iterator node
= fragment
.GetChildren().GetFirst();
10595 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
10596 wxASSERT (para
!= NULL
);
10598 // We'll replace the existing paragraph by finding the paragraph at this position,
10599 // delete its node data, and setting a copy as the new node data.
10600 // TODO: make more efficient by simply swapping old and new paragraph objects.
10602 wxRichTextParagraph
* existingPara
= container
->GetParagraphAtPosition(para
->GetRange().GetStart());
10605 wxRichTextObjectList::compatibility_iterator bufferParaNode
= container
->GetChildren().Find(existingPara
);
10606 if (bufferParaNode
)
10608 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(*para
);
10609 newPara
->SetParent(container
);
10611 bufferParaNode
->SetData(newPara
);
10613 delete existingPara
;
10617 node
= node
->GetNext();
10624 * This stores beginning and end positions for a range of data.
10627 WX_DEFINE_OBJARRAY(wxRichTextRangeArray
);
10629 /// Limit this range to be within 'range'
10630 bool wxRichTextRange::LimitTo(const wxRichTextRange
& range
)
10632 if (m_start
< range
.m_start
)
10633 m_start
= range
.m_start
;
10635 if (m_end
> range
.m_end
)
10636 m_end
= range
.m_end
;
10642 * wxRichTextImage implementation
10643 * This object represents an image.
10646 IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage
, wxRichTextObject
)
10648 wxRichTextImage::wxRichTextImage(const wxImage
& image
, wxRichTextObject
* parent
, wxRichTextAttr
* charStyle
):
10649 wxRichTextObject(parent
)
10652 m_imageBlock
.MakeImageBlockDefaultQuality(image
, wxBITMAP_TYPE_PNG
);
10654 SetAttributes(*charStyle
);
10657 wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock
& imageBlock
, wxRichTextObject
* parent
, wxRichTextAttr
* charStyle
):
10658 wxRichTextObject(parent
)
10661 m_imageBlock
= imageBlock
;
10663 SetAttributes(*charStyle
);
10666 void wxRichTextImage::Init()
10668 m_originalImageSize
= wxSize(-1, -1);
10671 /// Create a cached image at the required size
10672 bool wxRichTextImage::LoadImageCache(wxDC
& dc
, bool resetCache
)
10674 if (!m_imageBlock
.IsOk())
10677 // If we have an original image size, use that to compute the cached bitmap size
10678 // instead of loading the image each time. This way we can avoid loading
10679 // the image so long as the new cached bitmap size hasn't changed.
10682 if (resetCache
|| m_originalImageSize
== wxSize(-1, -1))
10684 m_imageCache
= wxNullBitmap
;
10686 m_imageBlock
.Load(image
);
10690 m_originalImageSize
= wxSize(image
.GetWidth(), image
.GetHeight());
10693 int width
= m_originalImageSize
.GetWidth();
10694 int height
= m_originalImageSize
.GetHeight();
10696 int parentWidth
= 0;
10697 int parentHeight
= 0;
10700 int maxHeight
= -1;
10702 wxRichTextBuffer
* buffer
= GetBuffer();
10706 if (buffer
->GetRichTextCtrl())
10708 // Subtract borders
10709 sz
= buffer
->GetRichTextCtrl()->GetClientSize();
10711 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
10712 marginRect
= wxRect(0, 0, sz
.x
, sz
.y
);
10713 buffer
->GetBoxRects(dc
, buffer
, buffer
->GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
10715 sz
= contentRect
.GetSize();
10717 // Start with a maximum width of the control size, even if not specified by the content,
10718 // to minimize the amount of picture overlapping the right-hand side
10722 sz
= buffer
->GetCachedSize();
10723 parentWidth
= sz
.GetWidth();
10724 parentHeight
= sz
.GetHeight();
10727 if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetWidth().GetValue() > 0)
10729 if (parentWidth
> 0 && GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
10730 width
= (int) ((GetAttributes().GetTextBoxAttr().GetWidth().GetValue() * parentWidth
)/100.0);
10731 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
10732 width
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetWidth().GetValue());
10733 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
10734 width
= GetAttributes().GetTextBoxAttr().GetWidth().GetValue();
10737 // Limit to max width
10739 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() > 0)
10743 if (parentWidth
> 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
10744 mw
= (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() * parentWidth
)/100.0);
10745 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
10746 mw
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue());
10747 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
10748 mw
= GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue();
10750 // If we already have a smaller max width due to the constraints of the control size,
10751 // don't use the larger max width.
10752 if (mw
!= -1 && ((maxWidth
== -1) || (mw
< maxWidth
)))
10756 if (maxWidth
> 0 && width
> maxWidth
)
10759 // Preserve the aspect ratio
10760 if (width
!= m_originalImageSize
.GetWidth())
10761 height
= (int) (float(m_originalImageSize
.GetHeight()) * (float(width
)/float(m_originalImageSize
.GetWidth())));
10763 if (GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetHeight().GetValue() > 0)
10765 if (parentHeight
> 0 && GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
10766 height
= (int) ((GetAttributes().GetTextBoxAttr().GetHeight().GetValue() * parentHeight
)/100.0);
10767 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
10768 height
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetHeight().GetValue());
10769 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
10770 height
= GetAttributes().GetTextBoxAttr().GetHeight().GetValue();
10772 // Preserve the aspect ratio
10773 if (height
!= m_originalImageSize
.GetHeight())
10774 width
= (int) (float(m_originalImageSize
.GetWidth()) * (float(height
)/float(m_originalImageSize
.GetHeight())));
10777 // Limit to max height
10779 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() > 0)
10781 if (parentHeight
> 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
10782 maxHeight
= (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() * parentHeight
)/100.0);
10783 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
10784 maxHeight
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue());
10785 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
10786 maxHeight
= GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue();
10789 if (maxHeight
> 0 && height
> maxHeight
)
10791 height
= maxHeight
;
10793 // Preserve the aspect ratio
10794 if (height
!= m_originalImageSize
.GetHeight())
10795 width
= (int) (float(m_originalImageSize
.GetWidth()) * (float(height
)/float(m_originalImageSize
.GetHeight())));
10798 if (m_imageCache
.IsOk() && m_imageCache
.GetWidth() == width
&& m_imageCache
.GetHeight() == height
)
10800 // Do nothing, we didn't need to change the image cache
10806 m_imageBlock
.Load(image
);
10811 if (image
.GetWidth() == width
&& image
.GetHeight() == height
)
10812 m_imageCache
= wxBitmap(image
);
10815 // If the original width and height is small, e.g. 400 or below,
10816 // scale up and then down to improve image quality. This can make
10817 // a big difference, with not much performance hit.
10818 int upscaleThreshold
= 400;
10820 if (image
.GetWidth() <= upscaleThreshold
|| image
.GetHeight() <= upscaleThreshold
)
10822 img
= image
.Scale(image
.GetWidth()*2, image
.GetHeight()*2);
10823 img
.Rescale(width
, height
, wxIMAGE_QUALITY_HIGH
);
10826 img
= image
.Scale(width
, height
, wxIMAGE_QUALITY_HIGH
);
10827 m_imageCache
= wxBitmap(img
);
10831 return m_imageCache
.IsOk();
10835 bool wxRichTextImage::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& WXUNUSED(range
), const wxRichTextSelection
& selection
, const wxRect
& rect
, int WXUNUSED(descent
), int WXUNUSED(style
))
10840 // Don't need cached size AFAIK
10841 // wxSize size = GetCachedSize();
10842 if (!LoadImageCache(dc
))
10845 wxRichTextAttr
attr(GetAttributes());
10846 context
.ApplyVirtualAttributes(attr
, this);
10848 DrawBoxAttributes(dc
, GetBuffer(), attr
, wxRect(rect
.GetPosition(), GetCachedSize()));
10850 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
10851 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
10852 marginRect
= rect
; // outer rectangle, will calculate contentRect
10853 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
10855 dc
.DrawBitmap(m_imageCache
, contentRect
.x
, contentRect
.y
, true);
10857 if (selection
.WithinSelection(GetRange().GetStart(), this))
10859 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
10860 wxCheckSetPen(dc
, *wxBLACK_PEN
);
10861 dc
.SetLogicalFunction(wxINVERT
);
10862 dc
.DrawRectangle(contentRect
);
10863 dc
.SetLogicalFunction(wxCOPY
);
10869 /// Lay the item out
10870 bool wxRichTextImage::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& WXUNUSED(parentRect
), int WXUNUSED(style
))
10872 if (!LoadImageCache(dc
))
10875 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
10876 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
10877 contentRect
= wxRect(wxPoint(0,0), imageSize
);
10879 wxRichTextAttr
attr(GetAttributes());
10880 context
.ApplyVirtualAttributes(attr
, this);
10882 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
10884 wxSize overallSize
= marginRect
.GetSize();
10886 SetCachedSize(overallSize
);
10887 SetMaxSize(overallSize
);
10888 SetMinSize(overallSize
);
10889 SetPosition(rect
.GetPosition());
10894 /// Get/set the object size for the given range. Returns false if the range
10895 /// is invalid for this object.
10896 bool wxRichTextImage::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& WXUNUSED(descent
), wxDC
& dc
, wxRichTextDrawingContext
& context
, int WXUNUSED(flags
), wxPoint
WXUNUSED(position
), wxArrayInt
* partialExtents
) const
10898 if (!range
.IsWithin(GetRange()))
10901 if (!((wxRichTextImage
*)this)->LoadImageCache(dc
))
10903 size
.x
= 0; size
.y
= 0;
10904 if (partialExtents
)
10905 partialExtents
->Add(0);
10909 wxRichTextAttr
attr(GetAttributes());
10910 context
.ApplyVirtualAttributes(attr
, (wxRichTextObject
*) this);
10912 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
10913 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
10914 contentRect
= wxRect(wxPoint(0,0), imageSize
);
10915 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
10917 wxSize overallSize
= marginRect
.GetSize();
10919 if (partialExtents
)
10920 partialExtents
->Add(overallSize
.x
);
10922 size
= overallSize
;
10927 // Get the 'natural' size for an object. For an image, it would be the
10929 wxTextAttrSize
wxRichTextImage::GetNaturalSize() const
10931 wxTextAttrSize size
;
10932 if (GetImageCache().IsOk())
10934 size
.SetWidth(GetImageCache().GetWidth(), wxTEXT_ATTR_UNITS_PIXELS
);
10935 size
.SetHeight(GetImageCache().GetHeight(), wxTEXT_ATTR_UNITS_PIXELS
);
10942 void wxRichTextImage::Copy(const wxRichTextImage
& obj
)
10944 wxRichTextObject::Copy(obj
);
10946 m_imageBlock
= obj
.m_imageBlock
;
10947 m_originalImageSize
= obj
.m_originalImageSize
;
10950 /// Edit properties via a GUI
10951 bool wxRichTextImage::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
10953 wxRichTextObjectPropertiesDialog
imageDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Picture Properties"));
10954 imageDlg
.SetAttributes(GetAttributes());
10956 if (imageDlg
.ShowModal() == wxID_OK
)
10958 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
10959 // indeterminate in the object.
10960 imageDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
10972 /// Compare two attribute objects
10973 bool wxTextAttrEq(const wxRichTextAttr
& attr1
, const wxRichTextAttr
& attr2
)
10975 return (attr1
== attr2
);
10979 bool wxRichTextTabsEq(const wxArrayInt
& tabs1
, const wxArrayInt
& tabs2
)
10981 if (tabs1
.GetCount() != tabs2
.GetCount())
10985 for (i
= 0; i
< tabs1
.GetCount(); i
++)
10987 if (tabs1
[i
] != tabs2
[i
])
10993 bool wxRichTextApplyStyle(wxRichTextAttr
& destStyle
, const wxRichTextAttr
& style
, wxRichTextAttr
* compareWith
)
10995 return destStyle
.Apply(style
, compareWith
);
10998 // Remove attributes
10999 bool wxRichTextRemoveStyle(wxRichTextAttr
& destStyle
, const wxRichTextAttr
& style
)
11001 return destStyle
.RemoveStyle(style
);
11004 /// Combine two bitlists, specifying the bits of interest with separate flags.
11005 bool wxRichTextCombineBitlists(int& valueA
, int valueB
, int& flagsA
, int flagsB
)
11007 return wxRichTextAttr::CombineBitlists(valueA
, valueB
, flagsA
, flagsB
);
11010 /// Compare two bitlists
11011 bool wxRichTextBitlistsEqPartial(int valueA
, int valueB
, int flags
)
11013 return wxRichTextAttr::BitlistsEqPartial(valueA
, valueB
, flags
);
11016 /// Split into paragraph and character styles
11017 bool wxRichTextSplitParaCharStyles(const wxRichTextAttr
& style
, wxRichTextAttr
& parStyle
, wxRichTextAttr
& charStyle
)
11019 return wxRichTextAttr::SplitParaCharStyles(style
, parStyle
, charStyle
);
11022 /// Convert a decimal to Roman numerals
11023 wxString
wxRichTextDecimalToRoman(long n
)
11025 static wxArrayInt decimalNumbers
;
11026 static wxArrayString romanNumbers
;
11031 decimalNumbers
.Clear();
11032 romanNumbers
.Clear();
11033 return wxEmptyString
;
11036 if (decimalNumbers
.GetCount() == 0)
11038 #define wxRichTextAddDecRom(n, r) decimalNumbers.Add(n); romanNumbers.Add(r);
11040 wxRichTextAddDecRom(1000, wxT("M"));
11041 wxRichTextAddDecRom(900, wxT("CM"));
11042 wxRichTextAddDecRom(500, wxT("D"));
11043 wxRichTextAddDecRom(400, wxT("CD"));
11044 wxRichTextAddDecRom(100, wxT("C"));
11045 wxRichTextAddDecRom(90, wxT("XC"));
11046 wxRichTextAddDecRom(50, wxT("L"));
11047 wxRichTextAddDecRom(40, wxT("XL"));
11048 wxRichTextAddDecRom(10, wxT("X"));
11049 wxRichTextAddDecRom(9, wxT("IX"));
11050 wxRichTextAddDecRom(5, wxT("V"));
11051 wxRichTextAddDecRom(4, wxT("IV"));
11052 wxRichTextAddDecRom(1, wxT("I"));
11058 while (n
> 0 && i
< 13)
11060 if (n
>= decimalNumbers
[i
])
11062 n
-= decimalNumbers
[i
];
11063 roman
+= romanNumbers
[i
];
11070 if (roman
.IsEmpty())
11076 * wxRichTextFileHandler
11077 * Base class for file handlers
11080 IMPLEMENT_CLASS(wxRichTextFileHandler
, wxObject
)
11082 #if wxUSE_FFILE && wxUSE_STREAMS
11083 bool wxRichTextFileHandler::LoadFile(wxRichTextBuffer
*buffer
, const wxString
& filename
)
11085 wxFFileInputStream
stream(filename
);
11087 return LoadFile(buffer
, stream
);
11092 bool wxRichTextFileHandler::SaveFile(wxRichTextBuffer
*buffer
, const wxString
& filename
)
11094 wxFFileOutputStream
stream(filename
);
11096 return SaveFile(buffer
, stream
);
11100 #endif // wxUSE_FFILE && wxUSE_STREAMS
11102 /// Can we handle this filename (if using files)? By default, checks the extension.
11103 bool wxRichTextFileHandler::CanHandle(const wxString
& filename
) const
11105 wxString path
, file
, ext
;
11106 wxFileName::SplitPath(filename
, & path
, & file
, & ext
);
11108 return (ext
.Lower() == GetExtension());
11112 * wxRichTextTextHandler
11113 * Plain text handler
11116 IMPLEMENT_CLASS(wxRichTextPlainTextHandler
, wxRichTextFileHandler
)
11119 bool wxRichTextPlainTextHandler::DoLoadFile(wxRichTextBuffer
*buffer
, wxInputStream
& stream
)
11121 if (!stream
.IsOk())
11127 while (!stream
.Eof())
11129 int ch
= stream
.GetC();
11133 if (ch
== 10 && lastCh
!= 13)
11136 if (ch
> 0 && ch
!= 10)
11143 buffer
->ResetAndClearCommands();
11145 buffer
->AddParagraphs(str
);
11146 buffer
->UpdateRanges();
11151 bool wxRichTextPlainTextHandler::DoSaveFile(wxRichTextBuffer
*buffer
, wxOutputStream
& stream
)
11153 if (!stream
.IsOk())
11156 wxString text
= buffer
->GetText();
11158 wxString newLine
= wxRichTextLineBreakChar
;
11159 text
.Replace(newLine
, wxT("\n"));
11161 wxCharBuffer buf
= text
.ToAscii();
11163 stream
.Write((const char*) buf
, text
.length());
11166 #endif // wxUSE_STREAMS
11169 * Stores information about an image, in binary in-memory form
11172 wxRichTextImageBlock::wxRichTextImageBlock()
11177 wxRichTextImageBlock::wxRichTextImageBlock(const wxRichTextImageBlock
& block
):wxObject()
11183 wxRichTextImageBlock::~wxRichTextImageBlock()
11188 void wxRichTextImageBlock::Init()
11192 m_imageType
= wxBITMAP_TYPE_INVALID
;
11195 void wxRichTextImageBlock::Clear()
11199 m_imageType
= wxBITMAP_TYPE_INVALID
;
11203 // Load the original image into a memory block.
11204 // If the image is not a JPEG, we must convert it into a JPEG
11205 // to conserve space.
11206 // If it's not a JPEG we can make use of 'image', already scaled, so we don't have to
11207 // load the image a 2nd time.
11209 bool wxRichTextImageBlock::MakeImageBlock(const wxString
& filename
, wxBitmapType imageType
,
11210 wxImage
& image
, bool convertToJPEG
)
11212 m_imageType
= imageType
;
11214 wxString
filenameToRead(filename
);
11215 bool removeFile
= false;
11217 if (imageType
== wxBITMAP_TYPE_INVALID
)
11218 return false; // Could not determine image type
11220 if ((imageType
!= wxBITMAP_TYPE_JPEG
) && convertToJPEG
)
11222 wxString tempFile
=
11223 wxFileName::CreateTempFileName(_("image"));
11225 wxASSERT(!tempFile
.IsEmpty());
11227 image
.SaveFile(tempFile
, wxBITMAP_TYPE_JPEG
);
11228 filenameToRead
= tempFile
;
11231 m_imageType
= wxBITMAP_TYPE_JPEG
;
11234 if (!file
.Open(filenameToRead
))
11237 m_dataSize
= (size_t) file
.Length();
11242 m_data
= ReadBlock(filenameToRead
, m_dataSize
);
11245 wxRemoveFile(filenameToRead
);
11247 return (m_data
!= NULL
);
11250 // Make an image block from the wxImage in the given
11252 bool wxRichTextImageBlock::MakeImageBlock(wxImage
& image
, wxBitmapType imageType
, int quality
)
11254 image
.SetOption(wxT("quality"), quality
);
11256 if (imageType
== wxBITMAP_TYPE_INVALID
)
11257 return false; // Could not determine image type
11259 return DoMakeImageBlock(image
, imageType
);
11262 // Uses a const wxImage for efficiency, but can't set quality (only relevant for JPEG)
11263 bool wxRichTextImageBlock::MakeImageBlockDefaultQuality(const wxImage
& image
, wxBitmapType imageType
)
11265 if (imageType
== wxBITMAP_TYPE_INVALID
)
11266 return false; // Could not determine image type
11268 return DoMakeImageBlock(image
, imageType
);
11271 // Makes the image block
11272 bool wxRichTextImageBlock::DoMakeImageBlock(const wxImage
& image
, wxBitmapType imageType
)
11274 wxMemoryOutputStream memStream
;
11275 if (!image
.SaveFile(memStream
, imageType
))
11280 unsigned char* block
= new unsigned char[memStream
.GetSize()];
11288 m_imageType
= imageType
;
11289 m_dataSize
= memStream
.GetSize();
11291 memStream
.CopyTo(m_data
, m_dataSize
);
11293 return (m_data
!= NULL
);
11297 bool wxRichTextImageBlock::Write(const wxString
& filename
)
11299 return WriteBlock(filename
, m_data
, m_dataSize
);
11302 void wxRichTextImageBlock::Copy(const wxRichTextImageBlock
& block
)
11304 m_imageType
= block
.m_imageType
;
11306 m_dataSize
= block
.m_dataSize
;
11307 if (m_dataSize
== 0)
11310 m_data
= new unsigned char[m_dataSize
];
11312 for (i
= 0; i
< m_dataSize
; i
++)
11313 m_data
[i
] = block
.m_data
[i
];
11317 void wxRichTextImageBlock::operator=(const wxRichTextImageBlock
& block
)
11322 // Load a wxImage from the block
11323 bool wxRichTextImageBlock::Load(wxImage
& image
)
11328 // Read in the image.
11330 wxMemoryInputStream
mstream(m_data
, m_dataSize
);
11331 bool success
= image
.LoadFile(mstream
, GetImageType());
11333 wxString tempFile
= wxFileName::CreateTempFileName(_("image"));
11334 wxASSERT(!tempFile
.IsEmpty());
11336 if (!WriteBlock(tempFile
, m_data
, m_dataSize
))
11340 success
= image
.LoadFile(tempFile
, GetImageType());
11341 wxRemoveFile(tempFile
);
11347 // Write data in hex to a stream
11348 bool wxRichTextImageBlock::WriteHex(wxOutputStream
& stream
)
11350 if (m_dataSize
== 0)
11353 int bufSize
= 100000;
11354 if (int(2*m_dataSize
) < bufSize
)
11355 bufSize
= 2*m_dataSize
;
11356 char* buf
= new char[bufSize
+1];
11358 int left
= m_dataSize
;
11363 if (left
*2 > bufSize
)
11365 n
= bufSize
; left
-= (bufSize
/2);
11369 n
= left
*2; left
= 0;
11373 for (i
= 0; i
< (n
/2); i
++)
11375 wxDecToHex(m_data
[j
], b
, b
+1);
11380 stream
.Write((const char*) buf
, n
);
11386 // Read data in hex from a stream
11387 bool wxRichTextImageBlock::ReadHex(wxInputStream
& stream
, int length
, wxBitmapType imageType
)
11389 int dataSize
= length
/2;
11394 // create a null terminated temporary string:
11398 m_data
= new unsigned char[dataSize
];
11400 for (i
= 0; i
< dataSize
; i
++)
11402 str
[0] = (char)stream
.GetC();
11403 str
[1] = (char)stream
.GetC();
11405 m_data
[i
] = (unsigned char)wxHexToDec(str
);
11408 m_dataSize
= dataSize
;
11409 m_imageType
= imageType
;
11414 // Allocate and read from stream as a block of memory
11415 unsigned char* wxRichTextImageBlock::ReadBlock(wxInputStream
& stream
, size_t size
)
11417 unsigned char* block
= new unsigned char[size
];
11421 stream
.Read(block
, size
);
11426 unsigned char* wxRichTextImageBlock::ReadBlock(const wxString
& filename
, size_t size
)
11428 wxFileInputStream
stream(filename
);
11429 if (!stream
.IsOk())
11432 return ReadBlock(stream
, size
);
11435 // Write memory block to stream
11436 bool wxRichTextImageBlock::WriteBlock(wxOutputStream
& stream
, unsigned char* block
, size_t size
)
11438 stream
.Write((void*) block
, size
);
11439 return stream
.IsOk();
11443 // Write memory block to file
11444 bool wxRichTextImageBlock::WriteBlock(const wxString
& filename
, unsigned char* block
, size_t size
)
11446 wxFileOutputStream
outStream(filename
);
11447 if (!outStream
.IsOk())
11450 return WriteBlock(outStream
, block
, size
);
11453 // Gets the extension for the block's type
11454 wxString
wxRichTextImageBlock::GetExtension() const
11456 wxImageHandler
* handler
= wxImage::FindHandler(GetImageType());
11458 return handler
->GetExtension();
11460 return wxEmptyString
;
11466 * The data object for a wxRichTextBuffer
11469 const wxChar
*wxRichTextBufferDataObject::ms_richTextBufferFormatId
= wxT("wxShape");
11471 wxRichTextBufferDataObject::wxRichTextBufferDataObject(wxRichTextBuffer
* richTextBuffer
)
11473 m_richTextBuffer
= richTextBuffer
;
11475 // this string should uniquely identify our format, but is otherwise
11477 m_formatRichTextBuffer
.SetId(GetRichTextBufferFormatId());
11479 SetFormat(m_formatRichTextBuffer
);
11482 wxRichTextBufferDataObject::~wxRichTextBufferDataObject()
11484 delete m_richTextBuffer
;
11487 // after a call to this function, the richTextBuffer is owned by the caller and it
11488 // is responsible for deleting it!
11489 wxRichTextBuffer
* wxRichTextBufferDataObject::GetRichTextBuffer()
11491 wxRichTextBuffer
* richTextBuffer
= m_richTextBuffer
;
11492 m_richTextBuffer
= NULL
;
11494 return richTextBuffer
;
11497 wxDataFormat
wxRichTextBufferDataObject::GetPreferredFormat(Direction
WXUNUSED(dir
)) const
11499 return m_formatRichTextBuffer
;
11502 size_t wxRichTextBufferDataObject::GetDataSize() const
11504 if (!m_richTextBuffer
)
11510 wxStringOutputStream
stream(& bufXML
);
11511 if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
))
11513 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
11519 wxCharBuffer buffer
= bufXML
.mb_str(wxConvUTF8
);
11520 return strlen(buffer
) + 1;
11522 return bufXML
.Length()+1;
11526 bool wxRichTextBufferDataObject::GetDataHere(void *pBuf
) const
11528 if (!pBuf
|| !m_richTextBuffer
)
11534 wxStringOutputStream
stream(& bufXML
);
11535 if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
))
11537 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
11543 wxCharBuffer buffer
= bufXML
.mb_str(wxConvUTF8
);
11544 size_t len
= strlen(buffer
);
11545 memcpy((char*) pBuf
, (const char*) buffer
, len
);
11546 ((char*) pBuf
)[len
] = 0;
11548 size_t len
= bufXML
.Length();
11549 memcpy((char*) pBuf
, (const char*) bufXML
.c_str(), len
);
11550 ((char*) pBuf
)[len
] = 0;
11556 bool wxRichTextBufferDataObject::SetData(size_t WXUNUSED(len
), const void *buf
)
11558 wxDELETE(m_richTextBuffer
);
11560 wxString
bufXML((const char*) buf
, wxConvUTF8
);
11562 m_richTextBuffer
= new wxRichTextBuffer
;
11564 wxStringInputStream
stream(bufXML
);
11565 if (!m_richTextBuffer
->LoadFile(stream
, wxRICHTEXT_TYPE_XML
))
11567 wxLogError(wxT("Could not read the buffer from an XML stream.\nYou may have forgotten to add the XML file handler."));
11569 wxDELETE(m_richTextBuffer
);
11581 * wxRichTextFontTable
11582 * Manages quick access to a pool of fonts for rendering rich text
11585 WX_DECLARE_STRING_HASH_MAP_WITH_DECL(wxFont
, wxRichTextFontTableHashMap
, class WXDLLIMPEXP_RICHTEXT
);
11587 class wxRichTextFontTableData
: public wxObjectRefData
11590 wxRichTextFontTableData() {}
11592 wxFont
FindFont(const wxRichTextAttr
& fontSpec
, double fontScale
);
11594 wxRichTextFontTableHashMap m_hashMap
;
11597 wxFont
wxRichTextFontTableData::FindFont(const wxRichTextAttr
& fontSpec
, double fontScale
)
11599 wxString
facename(fontSpec
.GetFontFaceName());
11601 int fontSize
= fontSpec
.GetFontSize();
11602 if (fontScale
!= 1.0)
11603 fontSize
= (int) ((double(fontSize
) * fontScale
) + 0.5);
11606 if (fontSpec
.HasFontPixelSize() && !fontSpec
.HasFontPointSize())
11610 wxString spec
= wxString::Format(wxT("%d-%s-%d-%d-%d-%d-%s-%d"),
11611 fontSize
, units
.c_str(), fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), (int) fontSpec
.GetFontUnderlined(), (int) fontSpec
.GetFontStrikethrough(),
11612 facename
.c_str(), (int) fontSpec
.GetFontEncoding());
11614 wxRichTextFontTableHashMap::iterator entry
= m_hashMap
.find(spec
);
11615 if ( entry
== m_hashMap
.end() )
11617 if (fontSpec
.HasFontPixelSize() && !fontSpec
.HasFontPointSize())
11619 wxFont
font(wxSize(0, fontSize
), wxFONTFAMILY_DEFAULT
, fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), fontSpec
.GetFontUnderlined(), facename
);
11620 if (fontSpec
.HasFontStrikethrough() && fontSpec
.GetFontStrikethrough())
11621 font
.SetStrikethrough(true);
11622 m_hashMap
[spec
] = font
;
11627 wxFont
font(fontSize
, wxFONTFAMILY_DEFAULT
, fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), fontSpec
.GetFontUnderlined(), facename
.c_str());
11628 if (fontSpec
.HasFontStrikethrough() && fontSpec
.GetFontStrikethrough())
11629 font
.SetStrikethrough(true);
11631 m_hashMap
[spec
] = font
;
11637 return entry
->second
;
11641 IMPLEMENT_DYNAMIC_CLASS(wxRichTextFontTable
, wxObject
)
11643 wxRichTextFontTable::wxRichTextFontTable()
11645 m_refData
= new wxRichTextFontTableData
;
11649 wxRichTextFontTable::wxRichTextFontTable(const wxRichTextFontTable
& table
)
11655 wxRichTextFontTable::~wxRichTextFontTable()
11660 bool wxRichTextFontTable::operator == (const wxRichTextFontTable
& table
) const
11662 return (m_refData
== table
.m_refData
);
11665 void wxRichTextFontTable::operator= (const wxRichTextFontTable
& table
)
11668 m_fontScale
= table
.m_fontScale
;
11671 wxFont
wxRichTextFontTable::FindFont(const wxRichTextAttr
& fontSpec
)
11673 wxRichTextFontTableData
* data
= (wxRichTextFontTableData
*) m_refData
;
11675 return data
->FindFont(fontSpec
, m_fontScale
);
11680 void wxRichTextFontTable::Clear()
11682 wxRichTextFontTableData
* data
= (wxRichTextFontTableData
*) m_refData
;
11684 data
->m_hashMap
.clear();
11687 void wxRichTextFontTable::SetFontScale(double fontScale
)
11689 if (fontScale
!= m_fontScale
)
11691 m_fontScale
= fontScale
;
11696 void wxTextBoxAttr::Reset()
11699 m_floatMode
= wxTEXT_BOX_ATTR_FLOAT_NONE
;
11700 m_clearMode
= wxTEXT_BOX_ATTR_CLEAR_NONE
;
11701 m_collapseMode
= wxTEXT_BOX_ATTR_COLLAPSE_NONE
;
11702 m_verticalAlignment
= wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_NONE
;
11703 m_boxStyleName
= wxEmptyString
;
11707 m_position
.Reset();
11718 bool wxTextBoxAttr::operator== (const wxTextBoxAttr
& attr
) const
11721 m_flags
== attr
.m_flags
&&
11722 m_floatMode
== attr
.m_floatMode
&&
11723 m_clearMode
== attr
.m_clearMode
&&
11724 m_collapseMode
== attr
.m_collapseMode
&&
11725 m_verticalAlignment
== attr
.m_verticalAlignment
&&
11727 m_margins
== attr
.m_margins
&&
11728 m_padding
== attr
.m_padding
&&
11729 m_position
== attr
.m_position
&&
11731 m_size
== attr
.m_size
&&
11732 m_minSize
== attr
.m_minSize
&&
11733 m_maxSize
== attr
.m_maxSize
&&
11735 m_border
== attr
.m_border
&&
11736 m_outline
== attr
.m_outline
&&
11738 m_boxStyleName
== attr
.m_boxStyleName
11742 // Partial equality test
11743 bool wxTextBoxAttr::EqPartial(const wxTextBoxAttr
& attr
, bool weakTest
) const
11746 ((!HasFloatMode() && attr
.HasFloatMode()) ||
11747 (!HasClearMode() && attr
.HasClearMode()) ||
11748 (!HasCollapseBorders() && attr
.HasCollapseBorders()) ||
11749 (!HasVerticalAlignment() && attr
.HasVerticalAlignment()) ||
11750 (!HasBoxStyleName() && attr
.HasBoxStyleName())))
11754 if (attr
.HasFloatMode() && HasFloatMode() && (GetFloatMode() != attr
.GetFloatMode()))
11757 if (attr
.HasClearMode() && HasClearMode() && (GetClearMode() != attr
.GetClearMode()))
11760 if (attr
.HasCollapseBorders() && HasCollapseBorders() && (attr
.GetCollapseBorders() != GetCollapseBorders()))
11763 if (attr
.HasVerticalAlignment() && HasVerticalAlignment() && (attr
.GetVerticalAlignment() != GetVerticalAlignment()))
11766 if (attr
.HasBoxStyleName() && HasBoxStyleName() && (attr
.GetBoxStyleName() != GetBoxStyleName()))
11771 if (!m_position
.EqPartial(attr
.m_position
, weakTest
))
11776 if (!m_size
.EqPartial(attr
.m_size
, weakTest
))
11778 if (!m_minSize
.EqPartial(attr
.m_minSize
, weakTest
))
11780 if (!m_maxSize
.EqPartial(attr
.m_maxSize
, weakTest
))
11785 if (!m_margins
.EqPartial(attr
.m_margins
, weakTest
))
11790 if (!m_padding
.EqPartial(attr
.m_padding
, weakTest
))
11795 if (!GetBorder().EqPartial(attr
.GetBorder(), weakTest
))
11800 if (!GetOutline().EqPartial(attr
.GetOutline(), weakTest
))
11806 // Merges the given attributes. If compareWith
11807 // is non-NULL, then it will be used to mask out those attributes that are the same in style
11808 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
11809 bool wxTextBoxAttr::Apply(const wxTextBoxAttr
& attr
, const wxTextBoxAttr
* compareWith
)
11811 if (attr
.HasFloatMode())
11813 if (!(compareWith
&& compareWith
->HasFloatMode() && compareWith
->GetFloatMode() == attr
.GetFloatMode()))
11814 SetFloatMode(attr
.GetFloatMode());
11817 if (attr
.HasClearMode())
11819 if (!(compareWith
&& compareWith
->HasClearMode() && compareWith
->GetClearMode() == attr
.GetClearMode()))
11820 SetClearMode(attr
.GetClearMode());
11823 if (attr
.HasCollapseBorders())
11825 if (!(compareWith
&& compareWith
->HasCollapseBorders() && compareWith
->GetCollapseBorders() == attr
.GetCollapseBorders()))
11826 SetCollapseBorders(attr
.GetCollapseBorders());
11829 if (attr
.HasVerticalAlignment())
11831 if (!(compareWith
&& compareWith
->HasVerticalAlignment() && compareWith
->GetVerticalAlignment() == attr
.GetVerticalAlignment()))
11832 SetVerticalAlignment(attr
.GetVerticalAlignment());
11835 if (attr
.HasBoxStyleName())
11837 if (!(compareWith
&& compareWith
->HasBoxStyleName() && compareWith
->GetBoxStyleName() == attr
.GetBoxStyleName()))
11838 SetBoxStyleName(attr
.GetBoxStyleName());
11841 m_margins
.Apply(attr
.m_margins
, compareWith
? (& attr
.m_margins
) : (const wxTextAttrDimensions
*) NULL
);
11842 m_padding
.Apply(attr
.m_padding
, compareWith
? (& attr
.m_padding
) : (const wxTextAttrDimensions
*) NULL
);
11843 m_position
.Apply(attr
.m_position
, compareWith
? (& attr
.m_position
) : (const wxTextAttrDimensions
*) NULL
);
11845 m_size
.Apply(attr
.m_size
, compareWith
? (& attr
.m_size
) : (const wxTextAttrSize
*) NULL
);
11846 m_minSize
.Apply(attr
.m_minSize
, compareWith
? (& attr
.m_minSize
) : (const wxTextAttrSize
*) NULL
);
11847 m_maxSize
.Apply(attr
.m_maxSize
, compareWith
? (& attr
.m_maxSize
) : (const wxTextAttrSize
*) NULL
);
11849 m_border
.Apply(attr
.m_border
, compareWith
? (& attr
.m_border
) : (const wxTextAttrBorders
*) NULL
);
11850 m_outline
.Apply(attr
.m_outline
, compareWith
? (& attr
.m_outline
) : (const wxTextAttrBorders
*) NULL
);
11855 // Remove specified attributes from this object
11856 bool wxTextBoxAttr::RemoveStyle(const wxTextBoxAttr
& attr
)
11858 if (attr
.HasFloatMode())
11859 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT
);
11861 if (attr
.HasClearMode())
11862 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR
);
11864 if (attr
.HasCollapseBorders())
11865 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
11867 if (attr
.HasVerticalAlignment())
11868 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
11870 if (attr
.HasBoxStyleName())
11872 SetBoxStyleName(wxEmptyString
);
11873 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
11876 m_margins
.RemoveStyle(attr
.m_margins
);
11877 m_padding
.RemoveStyle(attr
.m_padding
);
11878 m_position
.RemoveStyle(attr
.m_position
);
11880 m_size
.RemoveStyle(attr
.m_size
);
11881 m_minSize
.RemoveStyle(attr
.m_minSize
);
11882 m_maxSize
.RemoveStyle(attr
.m_maxSize
);
11884 m_border
.RemoveStyle(attr
.m_border
);
11885 m_outline
.RemoveStyle(attr
.m_outline
);
11890 // Collects the attributes that are common to a range of content, building up a note of
11891 // which attributes are absent in some objects and which clash in some objects.
11892 void wxTextBoxAttr::CollectCommonAttributes(const wxTextBoxAttr
& attr
, wxTextBoxAttr
& clashingAttr
, wxTextBoxAttr
& absentAttr
)
11894 if (attr
.HasFloatMode())
11896 if (!clashingAttr
.HasFloatMode() && !absentAttr
.HasFloatMode())
11898 if (HasFloatMode())
11900 if (GetFloatMode() != attr
.GetFloatMode())
11902 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_FLOAT
);
11903 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT
);
11907 SetFloatMode(attr
.GetFloatMode());
11911 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_FLOAT
);
11913 if (attr
.HasClearMode())
11915 if (!clashingAttr
.HasClearMode() && !absentAttr
.HasClearMode())
11917 if (HasClearMode())
11919 if (GetClearMode() != attr
.GetClearMode())
11921 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_CLEAR
);
11922 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR
);
11926 SetClearMode(attr
.GetClearMode());
11930 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_CLEAR
);
11932 if (attr
.HasCollapseBorders())
11934 if (!clashingAttr
.HasCollapseBorders() && !absentAttr
.HasCollapseBorders())
11936 if (HasCollapseBorders())
11938 if (GetCollapseBorders() != attr
.GetCollapseBorders())
11940 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
11941 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
11945 SetCollapseBorders(attr
.GetCollapseBorders());
11949 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
11951 if (attr
.HasVerticalAlignment())
11953 if (!clashingAttr
.HasVerticalAlignment() && !absentAttr
.HasVerticalAlignment())
11955 if (HasVerticalAlignment())
11957 if (GetVerticalAlignment() != attr
.GetVerticalAlignment())
11959 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
11960 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
11964 SetVerticalAlignment(attr
.GetVerticalAlignment());
11968 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
11970 if (attr
.HasBoxStyleName())
11972 if (!clashingAttr
.HasBoxStyleName() && !absentAttr
.HasBoxStyleName())
11974 if (HasBoxStyleName())
11976 if (GetBoxStyleName() != attr
.GetBoxStyleName())
11978 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
11979 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
11983 SetBoxStyleName(attr
.GetBoxStyleName());
11987 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
11989 m_margins
.CollectCommonAttributes(attr
.m_margins
, clashingAttr
.m_margins
, absentAttr
.m_margins
);
11990 m_padding
.CollectCommonAttributes(attr
.m_padding
, clashingAttr
.m_padding
, absentAttr
.m_padding
);
11991 m_position
.CollectCommonAttributes(attr
.m_position
, clashingAttr
.m_position
, absentAttr
.m_position
);
11993 m_size
.CollectCommonAttributes(attr
.m_size
, clashingAttr
.m_size
, absentAttr
.m_size
);
11994 m_minSize
.CollectCommonAttributes(attr
.m_minSize
, clashingAttr
.m_minSize
, absentAttr
.m_minSize
);
11995 m_maxSize
.CollectCommonAttributes(attr
.m_maxSize
, clashingAttr
.m_maxSize
, absentAttr
.m_maxSize
);
11997 m_border
.CollectCommonAttributes(attr
.m_border
, clashingAttr
.m_border
, absentAttr
.m_border
);
11998 m_outline
.CollectCommonAttributes(attr
.m_outline
, clashingAttr
.m_outline
, absentAttr
.m_outline
);
12001 bool wxTextBoxAttr::IsDefault() const
12003 return GetFlags() == 0 && !m_border
.IsValid() && !m_outline
.IsValid() &&
12004 !m_size
.IsValid() && !m_minSize
.IsValid() && !m_maxSize
.IsValid() &&
12005 !m_position
.IsValid() && !m_padding
.IsValid() && !m_margins
.IsValid();
12010 void wxRichTextAttr::Copy(const wxRichTextAttr
& attr
)
12012 wxTextAttr::Copy(attr
);
12014 m_textBoxAttr
= attr
.m_textBoxAttr
;
12017 bool wxRichTextAttr::operator==(const wxRichTextAttr
& attr
) const
12019 if (!(wxTextAttr::operator==(attr
)))
12022 return (m_textBoxAttr
== attr
.m_textBoxAttr
);
12025 // Partial equality test
12026 bool wxRichTextAttr::EqPartial(const wxRichTextAttr
& attr
, bool weakTest
) const
12028 if (!(wxTextAttr::EqPartial(attr
, weakTest
)))
12031 return m_textBoxAttr
.EqPartial(attr
.m_textBoxAttr
, weakTest
);
12034 // Merges the given attributes. If compareWith
12035 // is non-NULL, then it will be used to mask out those attributes that are the same in style
12036 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
12037 bool wxRichTextAttr::Apply(const wxRichTextAttr
& style
, const wxRichTextAttr
* compareWith
)
12039 wxTextAttr::Apply(style
, compareWith
);
12041 return m_textBoxAttr
.Apply(style
.m_textBoxAttr
, compareWith
? (& compareWith
->m_textBoxAttr
) : (const wxTextBoxAttr
*) NULL
);
12044 // Remove specified attributes from this object
12045 bool wxRichTextAttr::RemoveStyle(const wxRichTextAttr
& attr
)
12047 wxTextAttr::RemoveStyle(*this, attr
);
12049 return m_textBoxAttr
.RemoveStyle(attr
.m_textBoxAttr
);
12052 // Collects the attributes that are common to a range of content, building up a note of
12053 // which attributes are absent in some objects and which clash in some objects.
12054 void wxRichTextAttr::CollectCommonAttributes(const wxRichTextAttr
& attr
, wxRichTextAttr
& clashingAttr
, wxRichTextAttr
& absentAttr
)
12056 wxTextAttrCollectCommonAttributes(*this, attr
, clashingAttr
, absentAttr
);
12058 m_textBoxAttr
.CollectCommonAttributes(attr
.m_textBoxAttr
, clashingAttr
.m_textBoxAttr
, absentAttr
.m_textBoxAttr
);
12061 // Partial equality test
12062 bool wxTextAttrBorder::EqPartial(const wxTextAttrBorder
& border
, bool weakTest
) const
12065 ((!HasStyle() && border
.HasStyle()) ||
12066 (!HasColour() && border
.HasColour()) ||
12067 (!HasWidth() && border
.HasWidth())))
12072 if (border
.HasStyle() && HasStyle() && (border
.GetStyle() != GetStyle()))
12075 if (border
.HasColour() && HasColour() && (border
.GetColourLong() != GetColourLong()))
12078 if (border
.HasWidth() && HasWidth() && !(border
.GetWidth() == GetWidth()))
12084 // Apply border to 'this', but not if the same as compareWith
12085 bool wxTextAttrBorder::Apply(const wxTextAttrBorder
& border
, const wxTextAttrBorder
* compareWith
)
12087 if (border
.HasStyle())
12089 if (!(compareWith
&& (border
.GetStyle() == compareWith
->GetStyle())))
12090 SetStyle(border
.GetStyle());
12092 if (border
.HasColour())
12094 if (!(compareWith
&& (border
.GetColourLong() == compareWith
->GetColourLong())))
12095 SetColour(border
.GetColourLong());
12097 if (border
.HasWidth())
12099 if (!(compareWith
&& (border
.GetWidth() == compareWith
->GetWidth())))
12100 SetWidth(border
.GetWidth());
12106 // Remove specified attributes from this object
12107 bool wxTextAttrBorder::RemoveStyle(const wxTextAttrBorder
& attr
)
12109 if (attr
.HasStyle() && HasStyle())
12110 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_STYLE
);
12111 if (attr
.HasColour() && HasColour())
12112 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_COLOUR
);
12113 if (attr
.HasWidth() && HasWidth())
12114 m_borderWidth
.Reset();
12119 // Collects the attributes that are common to a range of content, building up a note of
12120 // which attributes are absent in some objects and which clash in some objects.
12121 void wxTextAttrBorder::CollectCommonAttributes(const wxTextAttrBorder
& attr
, wxTextAttrBorder
& clashingAttr
, wxTextAttrBorder
& absentAttr
)
12123 if (attr
.HasStyle())
12125 if (!clashingAttr
.HasStyle() && !absentAttr
.HasStyle())
12129 if (GetStyle() != attr
.GetStyle())
12131 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
12132 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
12136 SetStyle(attr
.GetStyle());
12140 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
12142 if (attr
.HasColour())
12144 if (!clashingAttr
.HasColour() && !absentAttr
.HasColour())
12148 if (GetColour() != attr
.GetColour())
12150 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
12151 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
12155 SetColour(attr
.GetColourLong());
12159 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
12161 m_borderWidth
.CollectCommonAttributes(attr
.m_borderWidth
, clashingAttr
.m_borderWidth
, absentAttr
.m_borderWidth
);
12164 // Partial equality test
12165 bool wxTextAttrBorders::EqPartial(const wxTextAttrBorders
& borders
, bool weakTest
) const
12167 return m_left
.EqPartial(borders
.m_left
, weakTest
) && m_right
.EqPartial(borders
.m_right
, weakTest
) &&
12168 m_top
.EqPartial(borders
.m_top
, weakTest
) && m_bottom
.EqPartial(borders
.m_bottom
, weakTest
);
12171 // Apply border to 'this', but not if the same as compareWith
12172 bool wxTextAttrBorders::Apply(const wxTextAttrBorders
& borders
, const wxTextAttrBorders
* compareWith
)
12174 m_left
.Apply(borders
.m_left
, compareWith
? (& compareWith
->m_left
) : (const wxTextAttrBorder
*) NULL
);
12175 m_right
.Apply(borders
.m_right
, compareWith
? (& compareWith
->m_right
) : (const wxTextAttrBorder
*) NULL
);
12176 m_top
.Apply(borders
.m_top
, compareWith
? (& compareWith
->m_top
) : (const wxTextAttrBorder
*) NULL
);
12177 m_bottom
.Apply(borders
.m_bottom
, compareWith
? (& compareWith
->m_bottom
) : (const wxTextAttrBorder
*) NULL
);
12181 // Remove specified attributes from this object
12182 bool wxTextAttrBorders::RemoveStyle(const wxTextAttrBorders
& attr
)
12184 m_left
.RemoveStyle(attr
.m_left
);
12185 m_right
.RemoveStyle(attr
.m_right
);
12186 m_top
.RemoveStyle(attr
.m_top
);
12187 m_bottom
.RemoveStyle(attr
.m_bottom
);
12191 // Collects the attributes that are common to a range of content, building up a note of
12192 // which attributes are absent in some objects and which clash in some objects.
12193 void wxTextAttrBorders::CollectCommonAttributes(const wxTextAttrBorders
& attr
, wxTextAttrBorders
& clashingAttr
, wxTextAttrBorders
& absentAttr
)
12195 m_left
.CollectCommonAttributes(attr
.m_left
, clashingAttr
.m_left
, absentAttr
.m_left
);
12196 m_right
.CollectCommonAttributes(attr
.m_right
, clashingAttr
.m_right
, absentAttr
.m_right
);
12197 m_top
.CollectCommonAttributes(attr
.m_top
, clashingAttr
.m_top
, absentAttr
.m_top
);
12198 m_bottom
.CollectCommonAttributes(attr
.m_bottom
, clashingAttr
.m_bottom
, absentAttr
.m_bottom
);
12201 // Set style of all borders
12202 void wxTextAttrBorders::SetStyle(int style
)
12204 m_left
.SetStyle(style
);
12205 m_right
.SetStyle(style
);
12206 m_top
.SetStyle(style
);
12207 m_bottom
.SetStyle(style
);
12210 // Set colour of all borders
12211 void wxTextAttrBorders::SetColour(unsigned long colour
)
12213 m_left
.SetColour(colour
);
12214 m_right
.SetColour(colour
);
12215 m_top
.SetColour(colour
);
12216 m_bottom
.SetColour(colour
);
12219 void wxTextAttrBorders::SetColour(const wxColour
& colour
)
12221 m_left
.SetColour(colour
);
12222 m_right
.SetColour(colour
);
12223 m_top
.SetColour(colour
);
12224 m_bottom
.SetColour(colour
);
12227 // Set width of all borders
12228 void wxTextAttrBorders::SetWidth(const wxTextAttrDimension
& width
)
12230 m_left
.SetWidth(width
);
12231 m_right
.SetWidth(width
);
12232 m_top
.SetWidth(width
);
12233 m_bottom
.SetWidth(width
);
12236 // Partial equality test
12237 bool wxTextAttrDimension::EqPartial(const wxTextAttrDimension
& dim
, bool weakTest
) const
12239 if (!weakTest
&& !IsValid() && dim
.IsValid())
12242 if (dim
.IsValid() && IsValid() && !((*this) == dim
))
12248 bool wxTextAttrDimension::Apply(const wxTextAttrDimension
& dim
, const wxTextAttrDimension
* compareWith
)
12252 if (!(compareWith
&& dim
== (*compareWith
)))
12259 // Collects the attributes that are common to a range of content, building up a note of
12260 // which attributes are absent in some objects and which clash in some objects.
12261 void wxTextAttrDimension::CollectCommonAttributes(const wxTextAttrDimension
& attr
, wxTextAttrDimension
& clashingAttr
, wxTextAttrDimension
& absentAttr
)
12263 if (attr
.IsValid())
12265 if (!clashingAttr
.IsValid() && !absentAttr
.IsValid())
12269 if (!((*this) == attr
))
12271 clashingAttr
.SetValid(true);
12280 absentAttr
.SetValid(true);
12283 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(wxDC
& dc
, double scale
, const wxSize
& parentSize
)
12285 m_ppi
= dc
.GetPPI().x
; m_scale
= scale
; m_parentSize
= parentSize
;
12288 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(int ppi
, double scale
, const wxSize
& parentSize
)
12290 m_ppi
= ppi
; m_scale
= scale
; m_parentSize
= parentSize
;
12293 int wxTextAttrDimensionConverter::ConvertTenthsMMToPixels(int units
) const
12295 return wxRichTextObject::ConvertTenthsMMToPixels(m_ppi
, units
, m_scale
);
12298 int wxTextAttrDimensionConverter::ConvertPixelsToTenthsMM(int pixels
) const
12300 return wxRichTextObject::ConvertPixelsToTenthsMM(m_ppi
, pixels
, m_scale
);
12303 int wxTextAttrDimensionConverter::GetPixels(const wxTextAttrDimension
& dim
, int direction
) const
12305 if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
12306 return ConvertTenthsMMToPixels(dim
.GetValue());
12307 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
12308 return dim
.GetValue();
12309 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
12311 wxASSERT(m_parentSize
!= wxDefaultSize
);
12312 if (direction
== wxHORIZONTAL
)
12313 return (int) (double(m_parentSize
.x
) * double(dim
.GetValue()) / 100.0);
12315 return (int) (double(m_parentSize
.y
) * double(dim
.GetValue()) / 100.0);
12324 int wxTextAttrDimensionConverter::GetTenthsMM(const wxTextAttrDimension
& dim
) const
12326 if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
12327 return dim
.GetValue();
12328 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
12329 return ConvertPixelsToTenthsMM(dim
.GetValue());
12337 // Partial equality test
12338 bool wxTextAttrDimensions::EqPartial(const wxTextAttrDimensions
& dims
, bool weakTest
) const
12340 if (!m_left
.EqPartial(dims
.m_left
, weakTest
))
12343 if (!m_right
.EqPartial(dims
.m_right
, weakTest
))
12346 if (!m_top
.EqPartial(dims
.m_top
, weakTest
))
12349 if (!m_bottom
.EqPartial(dims
.m_bottom
, weakTest
))
12355 // Apply border to 'this', but not if the same as compareWith
12356 bool wxTextAttrDimensions::Apply(const wxTextAttrDimensions
& dims
, const wxTextAttrDimensions
* compareWith
)
12358 m_left
.Apply(dims
.m_left
, compareWith
? (& compareWith
->m_left
) : (const wxTextAttrDimension
*) NULL
);
12359 m_right
.Apply(dims
.m_right
, compareWith
? (& compareWith
->m_right
): (const wxTextAttrDimension
*) NULL
);
12360 m_top
.Apply(dims
.m_top
, compareWith
? (& compareWith
->m_top
): (const wxTextAttrDimension
*) NULL
);
12361 m_bottom
.Apply(dims
.m_bottom
, compareWith
? (& compareWith
->m_bottom
): (const wxTextAttrDimension
*) NULL
);
12366 // Remove specified attributes from this object
12367 bool wxTextAttrDimensions::RemoveStyle(const wxTextAttrDimensions
& attr
)
12369 if (attr
.m_left
.IsValid())
12371 if (attr
.m_right
.IsValid())
12373 if (attr
.m_top
.IsValid())
12375 if (attr
.m_bottom
.IsValid())
12381 // Collects the attributes that are common to a range of content, building up a note of
12382 // which attributes are absent in some objects and which clash in some objects.
12383 void wxTextAttrDimensions::CollectCommonAttributes(const wxTextAttrDimensions
& attr
, wxTextAttrDimensions
& clashingAttr
, wxTextAttrDimensions
& absentAttr
)
12385 m_left
.CollectCommonAttributes(attr
.m_left
, clashingAttr
.m_left
, absentAttr
.m_left
);
12386 m_right
.CollectCommonAttributes(attr
.m_right
, clashingAttr
.m_right
, absentAttr
.m_right
);
12387 m_top
.CollectCommonAttributes(attr
.m_top
, clashingAttr
.m_top
, absentAttr
.m_top
);
12388 m_bottom
.CollectCommonAttributes(attr
.m_bottom
, clashingAttr
.m_bottom
, absentAttr
.m_bottom
);
12391 // Partial equality test
12392 bool wxTextAttrSize::EqPartial(const wxTextAttrSize
& size
, bool weakTest
) const
12394 if (!m_width
.EqPartial(size
.m_width
, weakTest
))
12397 if (!m_height
.EqPartial(size
.m_height
, weakTest
))
12403 // Apply border to 'this', but not if the same as compareWith
12404 bool wxTextAttrSize::Apply(const wxTextAttrSize
& size
, const wxTextAttrSize
* compareWith
)
12406 m_width
.Apply(size
.m_width
, compareWith
? (& compareWith
->m_width
) : (const wxTextAttrDimension
*) NULL
);
12407 m_height
.Apply(size
.m_height
, compareWith
? (& compareWith
->m_height
): (const wxTextAttrDimension
*) NULL
);
12412 // Remove specified attributes from this object
12413 bool wxTextAttrSize::RemoveStyle(const wxTextAttrSize
& attr
)
12415 if (attr
.m_width
.IsValid())
12417 if (attr
.m_height
.IsValid())
12423 // Collects the attributes that are common to a range of content, building up a note of
12424 // which attributes are absent in some objects and which clash in some objects.
12425 void wxTextAttrSize::CollectCommonAttributes(const wxTextAttrSize
& attr
, wxTextAttrSize
& clashingAttr
, wxTextAttrSize
& absentAttr
)
12427 m_width
.CollectCommonAttributes(attr
.m_width
, clashingAttr
.m_width
, absentAttr
.m_width
);
12428 m_height
.CollectCommonAttributes(attr
.m_height
, clashingAttr
.m_height
, absentAttr
.m_height
);
12431 // Collects the attributes that are common to a range of content, building up a note of
12432 // which attributes are absent in some objects and which clash in some objects.
12433 void wxTextAttrCollectCommonAttributes(wxTextAttr
& currentStyle
, const wxTextAttr
& attr
, wxTextAttr
& clashingAttr
, wxTextAttr
& absentAttr
)
12435 absentAttr
.SetFlags(absentAttr
.GetFlags() | (~attr
.GetFlags() & wxTEXT_ATTR_ALL
));
12436 absentAttr
.SetTextEffectFlags(absentAttr
.GetTextEffectFlags() | (~attr
.GetTextEffectFlags() & 0xFFFF));
12438 long forbiddenFlags
= clashingAttr
.GetFlags()|absentAttr
.GetFlags();
12440 if (attr
.HasFont())
12442 // If different font size units are being used, this is a clash.
12443 if (((attr
.GetFlags() & wxTEXT_ATTR_FONT_SIZE
) | (currentStyle
.GetFlags() & wxTEXT_ATTR_FONT_SIZE
)) == wxTEXT_ATTR_FONT_SIZE
)
12445 currentStyle
.SetFontSize(0);
12446 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_SIZE
);
12447 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_SIZE
);
12451 if (attr
.HasFontPointSize() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_POINT_SIZE
))
12453 if (currentStyle
.HasFontPointSize())
12455 if (currentStyle
.GetFontSize() != attr
.GetFontSize())
12457 // Clash of attr - mark as such
12458 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE
);
12459 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE
);
12463 currentStyle
.SetFontSize(attr
.GetFontSize());
12466 if (attr
.HasFontPixelSize() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_PIXEL_SIZE
))
12468 if (currentStyle
.HasFontPixelSize())
12470 if (currentStyle
.GetFontSize() != attr
.GetFontSize())
12472 // Clash of attr - mark as such
12473 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE
);
12474 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE
);
12478 currentStyle
.SetFontPixelSize(attr
.GetFontSize());
12482 if (attr
.HasFontItalic() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_ITALIC
))
12484 if (currentStyle
.HasFontItalic())
12486 if (currentStyle
.GetFontStyle() != attr
.GetFontStyle())
12488 // Clash of attr - mark as such
12489 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_ITALIC
);
12490 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC
);
12494 currentStyle
.SetFontStyle(attr
.GetFontStyle());
12497 if (attr
.HasFontFamily() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_FAMILY
))
12499 if (currentStyle
.HasFontFamily())
12501 if (currentStyle
.GetFontFamily() != attr
.GetFontFamily())
12503 // Clash of attr - mark as such
12504 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FAMILY
);
12505 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY
);
12509 currentStyle
.SetFontFamily(attr
.GetFontFamily());
12512 if (attr
.HasFontWeight() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_WEIGHT
))
12514 if (currentStyle
.HasFontWeight())
12516 if (currentStyle
.GetFontWeight() != attr
.GetFontWeight())
12518 // Clash of attr - mark as such
12519 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_WEIGHT
);
12520 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT
);
12524 currentStyle
.SetFontWeight(attr
.GetFontWeight());
12527 if (attr
.HasFontFaceName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_FACE
))
12529 if (currentStyle
.HasFontFaceName())
12531 wxString
faceName1(currentStyle
.GetFontFaceName());
12532 wxString
faceName2(attr
.GetFontFaceName());
12534 if (faceName1
!= faceName2
)
12536 // Clash of attr - mark as such
12537 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FACE
);
12538 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FACE
);
12542 currentStyle
.SetFontFaceName(attr
.GetFontFaceName());
12545 if (attr
.HasFontUnderlined() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_UNDERLINE
))
12547 if (currentStyle
.HasFontUnderlined())
12549 if (currentStyle
.GetFontUnderlined() != attr
.GetFontUnderlined())
12551 // Clash of attr - mark as such
12552 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
12553 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
12557 currentStyle
.SetFontUnderlined(attr
.GetFontUnderlined());
12560 if (attr
.HasFontStrikethrough() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_STRIKETHROUGH
))
12562 if (currentStyle
.HasFontStrikethrough())
12564 if (currentStyle
.GetFontStrikethrough() != attr
.GetFontStrikethrough())
12566 // Clash of attr - mark as such
12567 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH
);
12568 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH
);
12572 currentStyle
.SetFontStrikethrough(attr
.GetFontStrikethrough());
12576 if (attr
.HasTextColour() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_TEXT_COLOUR
))
12578 if (currentStyle
.HasTextColour())
12580 if (currentStyle
.GetTextColour() != attr
.GetTextColour())
12582 // Clash of attr - mark as such
12583 clashingAttr
.AddFlag(wxTEXT_ATTR_TEXT_COLOUR
);
12584 currentStyle
.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR
);
12588 currentStyle
.SetTextColour(attr
.GetTextColour());
12591 if (attr
.HasBackgroundColour() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BACKGROUND_COLOUR
))
12593 if (currentStyle
.HasBackgroundColour())
12595 if (currentStyle
.GetBackgroundColour() != attr
.GetBackgroundColour())
12597 // Clash of attr - mark as such
12598 clashingAttr
.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
12599 currentStyle
.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
12603 currentStyle
.SetBackgroundColour(attr
.GetBackgroundColour());
12606 if (attr
.HasAlignment() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_ALIGNMENT
))
12608 if (currentStyle
.HasAlignment())
12610 if (currentStyle
.GetAlignment() != attr
.GetAlignment())
12612 // Clash of attr - mark as such
12613 clashingAttr
.AddFlag(wxTEXT_ATTR_ALIGNMENT
);
12614 currentStyle
.RemoveFlag(wxTEXT_ATTR_ALIGNMENT
);
12618 currentStyle
.SetAlignment(attr
.GetAlignment());
12621 if (attr
.HasTabs() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_TABS
))
12623 if (currentStyle
.HasTabs())
12625 if (!wxRichTextTabsEq(currentStyle
.GetTabs(), attr
.GetTabs()))
12627 // Clash of attr - mark as such
12628 clashingAttr
.AddFlag(wxTEXT_ATTR_TABS
);
12629 currentStyle
.RemoveFlag(wxTEXT_ATTR_TABS
);
12633 currentStyle
.SetTabs(attr
.GetTabs());
12636 if (attr
.HasLeftIndent() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LEFT_INDENT
))
12638 if (currentStyle
.HasLeftIndent())
12640 if (currentStyle
.GetLeftIndent() != attr
.GetLeftIndent() || currentStyle
.GetLeftSubIndent() != attr
.GetLeftSubIndent())
12642 // Clash of attr - mark as such
12643 clashingAttr
.AddFlag(wxTEXT_ATTR_LEFT_INDENT
);
12644 currentStyle
.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT
);
12648 currentStyle
.SetLeftIndent(attr
.GetLeftIndent(), attr
.GetLeftSubIndent());
12651 if (attr
.HasRightIndent() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_RIGHT_INDENT
))
12653 if (currentStyle
.HasRightIndent())
12655 if (currentStyle
.GetRightIndent() != attr
.GetRightIndent())
12657 // Clash of attr - mark as such
12658 clashingAttr
.AddFlag(wxTEXT_ATTR_RIGHT_INDENT
);
12659 currentStyle
.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT
);
12663 currentStyle
.SetRightIndent(attr
.GetRightIndent());
12666 if (attr
.HasParagraphSpacingAfter() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARA_SPACING_AFTER
))
12668 if (currentStyle
.HasParagraphSpacingAfter())
12670 if (currentStyle
.GetParagraphSpacingAfter() != attr
.GetParagraphSpacingAfter())
12672 // Clash of attr - mark as such
12673 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
12674 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
12678 currentStyle
.SetParagraphSpacingAfter(attr
.GetParagraphSpacingAfter());
12681 if (attr
.HasParagraphSpacingBefore() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARA_SPACING_BEFORE
))
12683 if (currentStyle
.HasParagraphSpacingBefore())
12685 if (currentStyle
.GetParagraphSpacingBefore() != attr
.GetParagraphSpacingBefore())
12687 // Clash of attr - mark as such
12688 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
12689 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
12693 currentStyle
.SetParagraphSpacingBefore(attr
.GetParagraphSpacingBefore());
12696 if (attr
.HasLineSpacing() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LINE_SPACING
))
12698 if (currentStyle
.HasLineSpacing())
12700 if (currentStyle
.GetLineSpacing() != attr
.GetLineSpacing())
12702 // Clash of attr - mark as such
12703 clashingAttr
.AddFlag(wxTEXT_ATTR_LINE_SPACING
);
12704 currentStyle
.RemoveFlag(wxTEXT_ATTR_LINE_SPACING
);
12708 currentStyle
.SetLineSpacing(attr
.GetLineSpacing());
12711 if (attr
.HasCharacterStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_CHARACTER_STYLE_NAME
))
12713 if (currentStyle
.HasCharacterStyleName())
12715 if (currentStyle
.GetCharacterStyleName() != attr
.GetCharacterStyleName())
12717 // Clash of attr - mark as such
12718 clashingAttr
.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
12719 currentStyle
.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
12723 currentStyle
.SetCharacterStyleName(attr
.GetCharacterStyleName());
12726 if (attr
.HasParagraphStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
))
12728 if (currentStyle
.HasParagraphStyleName())
12730 if (currentStyle
.GetParagraphStyleName() != attr
.GetParagraphStyleName())
12732 // Clash of attr - mark as such
12733 clashingAttr
.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
12734 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
12738 currentStyle
.SetParagraphStyleName(attr
.GetParagraphStyleName());
12741 if (attr
.HasListStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LIST_STYLE_NAME
))
12743 if (currentStyle
.HasListStyleName())
12745 if (currentStyle
.GetListStyleName() != attr
.GetListStyleName())
12747 // Clash of attr - mark as such
12748 clashingAttr
.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
12749 currentStyle
.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
12753 currentStyle
.SetListStyleName(attr
.GetListStyleName());
12756 if (attr
.HasBulletStyle() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_STYLE
))
12758 if (currentStyle
.HasBulletStyle())
12760 if (currentStyle
.GetBulletStyle() != attr
.GetBulletStyle())
12762 // Clash of attr - mark as such
12763 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_STYLE
);
12764 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE
);
12768 currentStyle
.SetBulletStyle(attr
.GetBulletStyle());
12771 if (attr
.HasBulletNumber() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_NUMBER
))
12773 if (currentStyle
.HasBulletNumber())
12775 if (currentStyle
.GetBulletNumber() != attr
.GetBulletNumber())
12777 // Clash of attr - mark as such
12778 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NUMBER
);
12779 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER
);
12783 currentStyle
.SetBulletNumber(attr
.GetBulletNumber());
12786 if (attr
.HasBulletText() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_TEXT
))
12788 if (currentStyle
.HasBulletText())
12790 if (currentStyle
.GetBulletText() != attr
.GetBulletText())
12792 // Clash of attr - mark as such
12793 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_TEXT
);
12794 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT
);
12799 currentStyle
.SetBulletText(attr
.GetBulletText());
12800 currentStyle
.SetBulletFont(attr
.GetBulletFont());
12804 if (attr
.HasBulletName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_NAME
))
12806 if (currentStyle
.HasBulletName())
12808 if (currentStyle
.GetBulletName() != attr
.GetBulletName())
12810 // Clash of attr - mark as such
12811 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NAME
);
12812 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NAME
);
12817 currentStyle
.SetBulletName(attr
.GetBulletName());
12821 if (attr
.HasURL() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_URL
))
12823 if (currentStyle
.HasURL())
12825 if (currentStyle
.GetURL() != attr
.GetURL())
12827 // Clash of attr - mark as such
12828 clashingAttr
.AddFlag(wxTEXT_ATTR_URL
);
12829 currentStyle
.RemoveFlag(wxTEXT_ATTR_URL
);
12834 currentStyle
.SetURL(attr
.GetURL());
12838 if (attr
.HasTextEffects() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_EFFECTS
))
12840 if (currentStyle
.HasTextEffects())
12842 // We need to find the bits in the new attr that are different:
12843 // just look at those bits that are specified by the new attr.
12845 // We need to remove the bits and flags that are not common between current attr
12846 // and new attr. In so doing we need to take account of the styles absent from one or more of the
12847 // previous styles.
12849 int currentRelevantTextEffects
= currentStyle
.GetTextEffects() & attr
.GetTextEffectFlags();
12850 int newRelevantTextEffects
= attr
.GetTextEffects() & attr
.GetTextEffectFlags();
12852 if (currentRelevantTextEffects
!= newRelevantTextEffects
)
12854 // Find the text effects that were different, using XOR
12855 int differentEffects
= currentRelevantTextEffects
^ newRelevantTextEffects
;
12857 // Clash of attr - mark as such
12858 clashingAttr
.SetTextEffectFlags(clashingAttr
.GetTextEffectFlags() | differentEffects
);
12859 currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~differentEffects
);
12864 currentStyle
.SetTextEffects(attr
.GetTextEffects());
12865 currentStyle
.SetTextEffectFlags(attr
.GetTextEffectFlags());
12868 // Mask out the flags and values that cannot be common because they were absent in one or more objecrs
12869 // that we've looked at so far
12870 currentStyle
.SetTextEffects(currentStyle
.GetTextEffects() & ~absentAttr
.GetTextEffectFlags());
12871 currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~absentAttr
.GetTextEffectFlags());
12873 if (currentStyle
.GetTextEffectFlags() == 0)
12874 currentStyle
.RemoveFlag(wxTEXT_ATTR_EFFECTS
);
12877 if (attr
.HasOutlineLevel() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_OUTLINE_LEVEL
))
12879 if (currentStyle
.HasOutlineLevel())
12881 if (currentStyle
.GetOutlineLevel() != attr
.GetOutlineLevel())
12883 // Clash of attr - mark as such
12884 clashingAttr
.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
12885 currentStyle
.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
12889 currentStyle
.SetOutlineLevel(attr
.GetOutlineLevel());
12893 WX_DEFINE_OBJARRAY(wxRichTextVariantArray
);
12895 IMPLEMENT_DYNAMIC_CLASS(wxRichTextProperties
, wxObject
)
12897 bool wxRichTextProperties::operator==(const wxRichTextProperties
& props
) const
12899 if (m_properties
.GetCount() != props
.GetCount())
12903 for (i
= 0; i
< m_properties
.GetCount(); i
++)
12905 const wxVariant
& var1
= m_properties
[i
];
12906 int idx
= props
.Find(var1
.GetName());
12909 const wxVariant
& var2
= props
.m_properties
[idx
];
12910 if (!(var1
== var2
))
12917 wxArrayString
wxRichTextProperties::GetPropertyNames() const
12921 for (i
= 0; i
< m_properties
.GetCount(); i
++)
12923 arr
.Add(m_properties
[i
].GetName());
12928 int wxRichTextProperties::Find(const wxString
& name
) const
12931 for (i
= 0; i
< m_properties
.GetCount(); i
++)
12933 if (m_properties
[i
].GetName() == name
)
12939 bool wxRichTextProperties::Remove(const wxString
& name
)
12941 int idx
= Find(name
);
12944 m_properties
.RemoveAt(idx
);
12951 wxVariant
* wxRichTextProperties::FindOrCreateProperty(const wxString
& name
)
12953 int idx
= Find(name
);
12954 if (idx
== wxNOT_FOUND
)
12955 SetProperty(name
, wxString());
12957 if (idx
!= wxNOT_FOUND
)
12959 return & (*this)[idx
];
12965 const wxVariant
& wxRichTextProperties::GetProperty(const wxString
& name
) const
12967 static const wxVariant nullVariant
;
12968 int idx
= Find(name
);
12970 return m_properties
[idx
];
12972 return nullVariant
;
12975 wxString
wxRichTextProperties::GetPropertyString(const wxString
& name
) const
12977 return GetProperty(name
).GetString();
12980 long wxRichTextProperties::GetPropertyLong(const wxString
& name
) const
12982 return GetProperty(name
).GetLong();
12985 bool wxRichTextProperties::GetPropertyBool(const wxString
& name
) const
12987 return GetProperty(name
).GetBool();
12990 double wxRichTextProperties::GetPropertyDouble(const wxString
& name
) const
12992 return GetProperty(name
).GetDouble();
12995 void wxRichTextProperties::SetProperty(const wxVariant
& variant
)
12997 wxASSERT(!variant
.GetName().IsEmpty());
12999 int idx
= Find(variant
.GetName());
13002 m_properties
.Add(variant
);
13004 m_properties
[idx
] = variant
;
13007 void wxRichTextProperties::SetProperty(const wxString
& name
, const wxVariant
& variant
)
13009 int idx
= Find(name
);
13010 wxVariant
var(variant
);
13014 m_properties
.Add(var
);
13016 m_properties
[idx
] = var
;
13019 void wxRichTextProperties::SetProperty(const wxString
& name
, const wxString
& value
)
13021 SetProperty(name
, wxVariant(value
, name
));
13024 void wxRichTextProperties::SetProperty(const wxString
& name
, long value
)
13026 SetProperty(name
, wxVariant(value
, name
));
13029 void wxRichTextProperties::SetProperty(const wxString
& name
, double value
)
13031 SetProperty(name
, wxVariant(value
, name
));
13034 void wxRichTextProperties::SetProperty(const wxString
& name
, bool value
)
13036 SetProperty(name
, wxVariant(value
, name
));
13039 void wxRichTextProperties::RemoveProperties(const wxRichTextProperties
& properties
)
13042 for (i
= 0; i
< properties
.GetCount(); i
++)
13044 wxString name
= properties
.GetProperties()[i
].GetName();
13045 if (HasProperty(name
))
13050 void wxRichTextProperties::MergeProperties(const wxRichTextProperties
& properties
)
13053 for (i
= 0; i
< properties
.GetCount(); i
++)
13055 SetProperty(properties
.GetProperties()[i
]);
13059 wxRichTextObject
* wxRichTextObjectAddress::GetObject(wxRichTextParagraphLayoutBox
* topLevelContainer
) const
13061 if (m_address
.GetCount() == 0)
13062 return topLevelContainer
;
13064 wxRichTextCompositeObject
* p
= topLevelContainer
;
13066 while (p
&& i
< m_address
.GetCount())
13068 int pos
= m_address
[i
];
13069 wxASSERT(pos
>= 0 && pos
< (int) p
->GetChildren().GetCount());
13070 if (pos
< 0 || pos
>= (int) p
->GetChildren().GetCount())
13073 wxRichTextObject
* p1
= p
->GetChild(pos
);
13074 if (i
== (m_address
.GetCount()-1))
13077 p
= wxDynamicCast(p1
, wxRichTextCompositeObject
);
13083 bool wxRichTextObjectAddress::Create(wxRichTextParagraphLayoutBox
* topLevelContainer
, wxRichTextObject
* obj
)
13087 if (topLevelContainer
== obj
)
13090 wxRichTextObject
* o
= obj
;
13093 wxRichTextCompositeObject
* p
= wxDynamicCast(o
->GetParent(), wxRichTextCompositeObject
);
13097 int pos
= p
->GetChildren().IndexOf(o
);
13101 m_address
.Insert(pos
, 0);
13103 if (p
== topLevelContainer
)
13112 bool wxRichTextSelection::operator==(const wxRichTextSelection
& sel
) const
13114 if (m_container
!= sel
.m_container
)
13116 if (m_ranges
.GetCount() != sel
.m_ranges
.GetCount())
13119 for (i
= 0; i
< m_ranges
.GetCount(); i
++)
13120 if (!(m_ranges
[i
] == sel
.m_ranges
[i
]))
13125 // Get the selections appropriate to the specified object, if any; returns wxRICHTEXT_NO_SELECTION if none
13126 // or none at the level of the object's container.
13127 wxRichTextRangeArray
wxRichTextSelection::GetSelectionForObject(wxRichTextObject
* obj
) const
13131 wxRichTextParagraphLayoutBox
* container
= obj
->GetParentContainer();
13133 if (container
== m_container
)
13136 container
= obj
->GetContainer();
13139 if (container
->GetParent())
13141 // If we found that our object's container is within the range of
13142 // a selection higher up, then assume the whole original object
13143 // is also selected.
13144 wxRichTextParagraphLayoutBox
* parentContainer
= container
->GetParentContainer();
13145 if (parentContainer
== m_container
)
13147 if (WithinSelection(container
->GetRange().GetStart(), m_ranges
))
13149 wxRichTextRangeArray ranges
;
13150 ranges
.Add(obj
->GetRange());
13155 container
= parentContainer
;
13164 return wxRichTextRangeArray();
13167 // Is the given position within the selection?
13168 bool wxRichTextSelection::WithinSelection(long pos
, wxRichTextObject
* obj
) const
13174 wxRichTextRangeArray selectionRanges
= GetSelectionForObject(obj
);
13175 return WithinSelection(pos
, selectionRanges
);
13179 // Is the given position within the selection range?
13180 bool wxRichTextSelection::WithinSelection(long pos
, const wxRichTextRangeArray
& ranges
)
13183 for (i
= 0; i
< ranges
.GetCount(); i
++)
13185 const wxRichTextRange
& range
= ranges
[i
];
13186 if (pos
>= range
.GetStart() && pos
<= range
.GetEnd())
13192 // Is the given range completely within the selection range?
13193 bool wxRichTextSelection::WithinSelection(const wxRichTextRange
& range
, const wxRichTextRangeArray
& ranges
)
13196 for (i
= 0; i
< ranges
.GetCount(); i
++)
13198 const wxRichTextRange
& eachRange
= ranges
[i
];
13199 if (range
.IsWithin(eachRange
))
13205 IMPLEMENT_CLASS(wxRichTextDrawingHandler
, wxObject
)
13206 IMPLEMENT_CLASS(wxRichTextDrawingContext
, wxObject
)
13208 bool wxRichTextDrawingContext::HasVirtualAttributes(wxRichTextObject
* obj
) const
13210 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
13213 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13214 if (handler
->HasVirtualAttributes(obj
))
13217 node
= node
->GetNext();
13222 wxRichTextAttr
wxRichTextDrawingContext::GetVirtualAttributes(wxRichTextObject
* obj
) const
13224 wxRichTextAttr attr
;
13225 // We apply all handlers, so we can may combine several different attributes
13226 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
13229 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13230 if (handler
->HasVirtualAttributes(obj
))
13232 bool success
= handler
->GetVirtualAttributes(attr
, obj
);
13234 wxUnusedVar(success
);
13237 node
= node
->GetNext();
13242 bool wxRichTextDrawingContext::ApplyVirtualAttributes(wxRichTextAttr
& attr
, wxRichTextObject
* obj
) const
13244 if (HasVirtualAttributes(obj
))
13246 wxRichTextAttr
a(GetVirtualAttributes(obj
));
13254 /// Adds a handler to the end
13255 void wxRichTextBuffer::AddDrawingHandler(wxRichTextDrawingHandler
*handler
)
13257 sm_drawingHandlers
.Append(handler
);
13260 /// Inserts a handler at the front
13261 void wxRichTextBuffer::InsertDrawingHandler(wxRichTextDrawingHandler
*handler
)
13263 sm_drawingHandlers
.Insert( handler
);
13266 /// Removes a handler
13267 bool wxRichTextBuffer::RemoveDrawingHandler(const wxString
& name
)
13269 wxRichTextDrawingHandler
*handler
= FindDrawingHandler(name
);
13272 sm_drawingHandlers
.DeleteObject(handler
);
13280 wxRichTextDrawingHandler
* wxRichTextBuffer::FindDrawingHandler(const wxString
& name
)
13282 wxList::compatibility_iterator node
= sm_drawingHandlers
.GetFirst();
13285 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13286 if (handler
->GetName().Lower() == name
.Lower()) return handler
;
13288 node
= node
->GetNext();
13293 void wxRichTextBuffer::CleanUpDrawingHandlers()
13295 wxList::compatibility_iterator node
= sm_drawingHandlers
.GetFirst();
13298 wxRichTextDrawingHandler
* handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13299 wxList::compatibility_iterator next
= node
->GetNext();
13304 sm_drawingHandlers
.Clear();
13307 void wxRichTextBuffer::AddFieldType(wxRichTextFieldType
*fieldType
)
13309 sm_fieldTypes
[fieldType
->GetName()] = fieldType
;
13312 bool wxRichTextBuffer::RemoveFieldType(const wxString
& name
)
13314 wxRichTextFieldTypeHashMap::iterator it
= sm_fieldTypes
.find(name
);
13315 if (it
== sm_fieldTypes
.end())
13319 wxRichTextFieldType
* fieldType
= it
->second
;
13320 sm_fieldTypes
.erase(it
);
13326 wxRichTextFieldType
*wxRichTextBuffer::FindFieldType(const wxString
& name
)
13328 wxRichTextFieldTypeHashMap::iterator it
= sm_fieldTypes
.find(name
);
13329 if (it
== sm_fieldTypes
.end())
13335 void wxRichTextBuffer::CleanUpFieldTypes()
13337 wxRichTextFieldTypeHashMap::iterator it
;
13338 for( it
= sm_fieldTypes
.begin(); it
!= sm_fieldTypes
.end(); ++it
)
13340 wxRichTextFieldType
* fieldType
= it
->second
;
13344 sm_fieldTypes
.clear();