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
3990 newPara
->GetAttributes().SetBulletNumber(n
);
3995 else if (!newPara
->GetAttributes().GetListStyleName().IsEmpty())
3997 // if def is NULL, remove list style, applying any associated paragraph style
3998 // to restore the attributes
4000 newPara
->GetAttributes().SetListStyleName(wxEmptyString
);
4001 newPara
->GetAttributes().SetLeftIndent(0, 0);
4002 newPara
->GetAttributes().SetBulletText(wxEmptyString
);
4004 // Eliminate the main list-related attributes
4005 newPara
->GetAttributes().SetFlags(newPara
->GetAttributes().GetFlags() & ~wxTEXT_ATTR_LEFT_INDENT
& ~wxTEXT_ATTR_BULLET_STYLE
& ~wxTEXT_ATTR_BULLET_NUMBER
& ~wxTEXT_ATTR_BULLET_TEXT
& wxTEXT_ATTR_LIST_STYLE_NAME
);
4007 if (styleSheet
&& !newPara
->GetAttributes().GetParagraphStyleName().IsEmpty())
4009 wxRichTextParagraphStyleDefinition
* def
= styleSheet
->FindParagraphStyle(newPara
->GetAttributes().GetParagraphStyleName());
4012 newPara
->GetAttributes() = def
->GetStyleMergedWithBase(styleSheet
);
4019 node
= node
->GetNext();
4022 // Do action, or delay it until end of batch.
4023 if (haveControl
&& withUndo
)
4024 buffer
->SubmitAction(action
);
4029 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
)
4031 wxRichTextBuffer
* buffer
= GetBuffer();
4032 if (buffer
&& buffer
->GetStyleSheet())
4034 wxRichTextListStyleDefinition
* def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4036 return SetListStyle(range
, def
, flags
, startFrom
, specifiedLevel
);
4041 /// Clear list for given range
4042 bool wxRichTextParagraphLayoutBox::ClearListStyle(const wxRichTextRange
& range
, int flags
)
4044 return SetListStyle(range
, NULL
, flags
);
4047 /// Number/renumber any list elements in the given range
4048 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
4050 return DoNumberList(range
, range
, 0, def
, flags
, startFrom
, specifiedLevel
);
4053 /// Number/renumber any list elements in the given range. Also do promotion or demotion of items, if specified
4054 bool wxRichTextParagraphLayoutBox::DoNumberList(const wxRichTextRange
& range
, const wxRichTextRange
& promotionRange
, int promoteBy
,
4055 wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
4057 wxRichTextBuffer
* buffer
= GetBuffer();
4058 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
4060 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
4061 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
4063 bool specifyLevel
= ((flags
& wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL
) != 0);
4066 bool renumber
= ((flags
& wxRICHTEXT_SETSTYLE_RENUMBER
) != 0);
4068 // Max number of levels
4069 const int maxLevels
= 10;
4071 // The level we're looking at now
4072 int currentLevel
= -1;
4074 // The item number for each level
4075 int levels
[maxLevels
];
4078 // Reset all numbering
4079 for (i
= 0; i
< maxLevels
; i
++)
4081 if (startFrom
!= -1)
4082 levels
[i
] = startFrom
-1;
4083 else if (renumber
) // start again
4086 levels
[i
] = -1; // start from the number we found, if any
4090 wxASSERT(!specifyLevel
|| (specifyLevel
&& (specifiedLevel
>= 0)));
4093 // If we are associated with a control, make undoable; otherwise, apply immediately
4096 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
4098 wxRichTextAction
* action
= NULL
;
4100 if (haveControl
&& withUndo
)
4102 action
= new wxRichTextAction(NULL
, _("Renumber List"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
4103 action
->SetRange(range
);
4104 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
4107 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
4110 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
4111 // wxASSERT (para != NULL);
4113 if (para
&& para
->GetChildCount() > 0)
4115 // Stop searching if we're beyond the range of interest
4116 if (para
->GetRange().GetStart() > range
.GetEnd())
4119 if (!para
->GetRange().IsOutside(range
))
4121 // We'll be using a copy of the paragraph to make style changes,
4122 // not updating the buffer directly.
4123 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
4125 if (haveControl
&& withUndo
)
4127 newPara
= new wxRichTextParagraph(*para
);
4128 action
->GetNewParagraphs().AppendChild(newPara
);
4130 // Also store the old ones for Undo
4131 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
4136 wxRichTextListStyleDefinition
* defToUse
= def
;
4139 if (styleSheet
&& !newPara
->GetAttributes().GetListStyleName().IsEmpty())
4140 defToUse
= styleSheet
->FindListStyle(newPara
->GetAttributes().GetListStyleName());
4145 int thisIndent
= newPara
->GetAttributes().GetLeftIndent();
4146 int thisLevel
= defToUse
->FindLevelForIndent(thisIndent
);
4148 // If we've specified a level to apply to all, change the level.
4149 if (specifiedLevel
!= -1)
4150 thisLevel
= specifiedLevel
;
4152 // Do promotion if specified
4153 if ((promoteBy
!= 0) && !para
->GetRange().IsOutside(promotionRange
))
4155 thisLevel
= thisLevel
- promoteBy
;
4162 // Apply the overall list style, and item style for this level
4163 wxRichTextAttr
listStyle(defToUse
->GetCombinedStyleForLevel(thisLevel
, styleSheet
));
4164 wxRichTextApplyStyle(newPara
->GetAttributes(), listStyle
);
4166 // OK, we've (re)applied the style, now let's get the numbering right.
4168 if (currentLevel
== -1)
4169 currentLevel
= thisLevel
;
4171 // Same level as before, do nothing except increment level's number afterwards
4172 if (currentLevel
== thisLevel
)
4175 // A deeper level: start renumbering all levels after current level
4176 else if (thisLevel
> currentLevel
)
4178 for (i
= currentLevel
+1; i
<= thisLevel
; i
++)
4182 currentLevel
= thisLevel
;
4184 else if (thisLevel
< currentLevel
)
4186 currentLevel
= thisLevel
;
4189 // Use the current numbering if -1 and we have a bullet number already
4190 if (levels
[currentLevel
] == -1)
4192 if (newPara
->GetAttributes().HasBulletNumber())
4193 levels
[currentLevel
] = newPara
->GetAttributes().GetBulletNumber();
4195 levels
[currentLevel
] = 1;
4199 levels
[currentLevel
] ++;
4202 newPara
->GetAttributes().SetBulletNumber(levels
[currentLevel
]);
4204 // Create the bullet text if an outline list
4205 if (listStyle
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
)
4208 for (i
= 0; i
<= currentLevel
; i
++)
4210 if (!text
.IsEmpty())
4212 text
+= wxString::Format(wxT("%d"), levels
[i
]);
4214 newPara
->GetAttributes().SetBulletText(text
);
4220 node
= node
->GetNext();
4223 // Do action, or delay it until end of batch.
4224 if (haveControl
&& withUndo
)
4225 buffer
->SubmitAction(action
);
4230 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
)
4232 wxRichTextBuffer
* buffer
= GetBuffer();
4233 if (buffer
->GetStyleSheet())
4235 wxRichTextListStyleDefinition
* def
= NULL
;
4236 if (!defName
.IsEmpty())
4237 def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4238 return NumberList(range
, def
, flags
, startFrom
, specifiedLevel
);
4243 /// Promote the list items within the given range. promoteBy can be a positive or negative number, e.g. 1 or -1
4244 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy
, const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int specifiedLevel
)
4247 // One strategy is to first work out the range within which renumbering must occur. Then could pass these two ranges
4248 // to NumberList with a flag indicating promotion is required within one of the ranges.
4249 // Find first and last paragraphs in range. Then for first, calculate new indentation and look back until we find
4250 // a paragraph that either has no list style, or has one that is different or whose indentation is less.
4251 // We start renumbering from the para after that different para we found. We specify that the numbering of that
4252 // list position will start from 1.
4253 // Similarly, we look after the last para in the promote range for an indentation that is less (or no list style).
4254 // We can end the renumbering at this point.
4256 // For now, only renumber within the promotion range.
4258 return DoNumberList(range
, range
, promoteBy
, def
, flags
, 1, specifiedLevel
);
4261 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy
, const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int specifiedLevel
)
4263 wxRichTextBuffer
* buffer
= GetBuffer();
4264 if (buffer
->GetStyleSheet())
4266 wxRichTextListStyleDefinition
* def
= NULL
;
4267 if (!defName
.IsEmpty())
4268 def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4269 return PromoteList(promoteBy
, range
, def
, flags
, specifiedLevel
);
4274 /// Fills in the attributes for numbering a paragraph after previousParagraph. It also finds the
4275 /// position of the paragraph that it had to start looking from.
4276 bool wxRichTextParagraphLayoutBox::FindNextParagraphNumber(wxRichTextParagraph
* previousParagraph
, wxRichTextAttr
& attr
) const
4278 if (!previousParagraph
->GetAttributes().HasFlag(wxTEXT_ATTR_BULLET_STYLE
) || previousParagraph
->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
)
4281 wxRichTextBuffer
* buffer
= GetBuffer();
4282 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
4283 if (styleSheet
&& !previousParagraph
->GetAttributes().GetListStyleName().IsEmpty())
4285 wxRichTextListStyleDefinition
* def
= styleSheet
->FindListStyle(previousParagraph
->GetAttributes().GetListStyleName());
4288 // int thisIndent = previousParagraph->GetAttributes().GetLeftIndent();
4289 // int thisLevel = def->FindLevelForIndent(thisIndent);
4291 bool isOutline
= (previousParagraph
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
) != 0;
4293 attr
.SetFlags(previousParagraph
->GetAttributes().GetFlags() & (wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_BULLET_NUMBER
|wxTEXT_ATTR_BULLET_TEXT
|wxTEXT_ATTR_BULLET_NAME
));
4294 if (previousParagraph
->GetAttributes().HasBulletName())
4295 attr
.SetBulletName(previousParagraph
->GetAttributes().GetBulletName());
4296 attr
.SetBulletStyle(previousParagraph
->GetAttributes().GetBulletStyle());
4297 attr
.SetListStyleName(previousParagraph
->GetAttributes().GetListStyleName());
4299 int nextNumber
= previousParagraph
->GetAttributes().GetBulletNumber() + 1;
4300 attr
.SetBulletNumber(nextNumber
);
4304 wxString text
= previousParagraph
->GetAttributes().GetBulletText();
4305 if (!text
.IsEmpty())
4307 int pos
= text
.Find(wxT('.'), true);
4308 if (pos
!= wxNOT_FOUND
)
4310 text
= text
.Mid(0, text
.Length() - pos
- 1);
4313 text
= wxEmptyString
;
4314 if (!text
.IsEmpty())
4316 text
+= wxString::Format(wxT("%d"), nextNumber
);
4317 attr
.SetBulletText(text
);
4331 * wxRichTextParagraph
4332 * This object represents a single paragraph (or in a straight text editor, a line).
4335 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraph
, wxRichTextCompositeObject
)
4337 wxArrayInt
wxRichTextParagraph::sm_defaultTabs
;
4339 wxRichTextParagraph::wxRichTextParagraph(wxRichTextObject
* parent
, wxRichTextAttr
* style
):
4340 wxRichTextCompositeObject(parent
)
4343 SetAttributes(*style
);
4346 wxRichTextParagraph::wxRichTextParagraph(const wxString
& text
, wxRichTextObject
* parent
, wxRichTextAttr
* paraStyle
, wxRichTextAttr
* charStyle
):
4347 wxRichTextCompositeObject(parent
)
4350 SetAttributes(*paraStyle
);
4352 AppendChild(new wxRichTextPlainText(text
, this, charStyle
));
4355 wxRichTextParagraph::~wxRichTextParagraph()
4361 bool wxRichTextParagraph::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int WXUNUSED(descent
), int style
)
4366 // Currently we don't merge these attributes with the parent, but we
4367 // should consider whether we should (e.g. if we set a border colour
4368 // for all paragraphs). But generally box attributes are likely to be
4369 // different for different objects.
4370 wxRect paraRect
= GetRect();
4371 wxRichTextAttr attr
= GetCombinedAttributes();
4372 context
.ApplyVirtualAttributes(attr
, this);
4374 DrawBoxAttributes(dc
, GetBuffer(), attr
, paraRect
);
4376 // Draw the bullet, if any
4377 if (attr
.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE
)
4379 if (attr
.GetLeftSubIndent() != 0)
4381 int spaceBeforePara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingBefore());
4382 int leftIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftIndent());
4384 wxRichTextAttr
bulletAttr(attr
);
4386 // Combine with the font of the first piece of content, if one is specified
4387 if (GetChildren().GetCount() > 0)
4389 wxRichTextObject
* firstObj
= (wxRichTextObject
*) GetChildren().GetFirst()->GetData();
4390 if (!firstObj
->IsFloatable() && firstObj
->GetAttributes().HasFont())
4392 wxRichTextApplyStyle(bulletAttr
, firstObj
->GetAttributes());
4396 // Get line height from first line, if any
4397 wxRichTextLine
* line
= m_cachedLines
.GetFirst() ? (wxRichTextLine
* ) m_cachedLines
.GetFirst()->GetData() : NULL
;
4400 int lineHeight
wxDUMMY_INITIALIZE(0);
4403 lineHeight
= line
->GetSize().y
;
4404 linePos
= line
->GetPosition() + GetPosition();
4409 if (bulletAttr
.HasFont() && GetBuffer())
4410 font
= GetBuffer()->GetFontTable().FindFont(bulletAttr
);
4412 font
= (*wxNORMAL_FONT
);
4414 wxCheckSetFont(dc
, font
);
4416 lineHeight
= dc
.GetCharHeight();
4417 linePos
= GetPosition();
4418 linePos
.y
+= spaceBeforePara
;
4421 wxRect
bulletRect(GetPosition().x
+ leftIndent
, linePos
.y
, linePos
.x
- (GetPosition().x
+ leftIndent
), lineHeight
);
4423 if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
)
4425 if (wxRichTextBuffer::GetRenderer())
4426 wxRichTextBuffer::GetRenderer()->DrawBitmapBullet(this, dc
, bulletAttr
, bulletRect
);
4428 else if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_STANDARD
)
4430 if (wxRichTextBuffer::GetRenderer())
4431 wxRichTextBuffer::GetRenderer()->DrawStandardBullet(this, dc
, bulletAttr
, bulletRect
);
4435 wxString bulletText
= GetBulletText();
4437 if (!bulletText
.empty() && wxRichTextBuffer::GetRenderer())
4438 wxRichTextBuffer::GetRenderer()->DrawTextBullet(this, dc
, bulletAttr
, bulletRect
, bulletText
);
4443 // Draw the range for each line, one object at a time.
4445 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
4448 wxRichTextLine
* line
= node
->GetData();
4449 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
4451 // Lines are specified relative to the paragraph
4453 wxPoint linePosition
= line
->GetPosition() + GetPosition();
4455 // Don't draw if off the screen
4456 if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) != 0) || ((linePosition
.y
+ line
->GetSize().y
) >= rect
.y
&& linePosition
.y
<= rect
.y
+ rect
.height
))
4458 wxPoint objectPosition
= linePosition
;
4459 int maxDescent
= line
->GetDescent();
4461 // Loop through objects until we get to the one within range
4462 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
4467 wxRichTextObject
* child
= node2
->GetData();
4469 if (!child
->IsFloating() && child
->GetRange().GetLength() > 0 && !child
->GetRange().IsOutside(lineRange
) && !lineRange
.IsOutside(range
))
4471 // Draw this part of the line at the correct position
4472 wxRichTextRange
objectRange(child
->GetRange());
4473 objectRange
.LimitTo(lineRange
);
4476 if (child
->IsTopLevel())
4478 objectSize
= child
->GetCachedSize();
4479 objectRange
= child
->GetOwnRange();
4483 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING && wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4484 if (i
< (int) line
->GetObjectSizes().GetCount())
4486 objectSize
.x
= line
->GetObjectSizes()[(size_t) i
];
4492 child
->GetRangeSize(objectRange
, objectSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, objectPosition
);
4496 // Use the child object's width, but the whole line's height
4497 wxRect
childRect(objectPosition
, wxSize(objectSize
.x
, line
->GetSize().y
));
4498 child
->Draw(dc
, context
, objectRange
, selection
, childRect
, maxDescent
, style
);
4500 objectPosition
.x
+= objectSize
.x
;
4503 else if (child
->GetRange().GetStart() > lineRange
.GetEnd())
4504 // Can break out of inner loop now since we've passed this line's range
4507 node2
= node2
->GetNext();
4511 node
= node
->GetNext();
4517 // Get the range width using partial extents calculated for the whole paragraph.
4518 static int wxRichTextGetRangeWidth(const wxRichTextParagraph
& para
, const wxRichTextRange
& range
, const wxArrayInt
& partialExtents
)
4520 wxASSERT(partialExtents
.GetCount() >= (size_t) range
.GetLength());
4522 if (partialExtents
.GetCount() < (size_t) range
.GetLength())
4525 int leftMostPos
= 0;
4526 if (range
.GetStart() - para
.GetRange().GetStart() > 0)
4527 leftMostPos
= partialExtents
[range
.GetStart() - para
.GetRange().GetStart() - 1];
4529 int rightMostPos
= partialExtents
[range
.GetEnd() - para
.GetRange().GetStart()];
4531 int w
= rightMostPos
- leftMostPos
;
4536 /// Lay the item out
4537 bool wxRichTextParagraph::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
)
4539 // Deal with floating objects firstly before the normal layout
4540 wxRichTextBuffer
* buffer
= GetBuffer();
4542 wxRichTextFloatCollector
* collector
= GetContainer()->GetFloatCollector();
4543 wxASSERT(collector
);
4544 LayoutFloat(dc
, context
, rect
, style
, collector
);
4546 wxRichTextAttr attr
= GetCombinedAttributes();
4547 context
.ApplyVirtualAttributes(attr
, this);
4551 // Increase the size of the paragraph due to spacing
4552 int spaceBeforePara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingBefore());
4553 int spaceAfterPara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingAfter());
4554 int leftIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftIndent());
4555 int leftSubIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftSubIndent());
4556 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
4558 int lineSpacing
= 0;
4560 // Let's assume line spacing of 10 is normal, 15 is 1.5, 20 is 2, etc.
4561 if (attr
.HasLineSpacing() && attr
.GetLineSpacing() > 0 && attr
.GetFont().IsOk())
4563 wxCheckSetFont(dc
, attr
.GetFont());
4564 lineSpacing
= (int) (double(dc
.GetCharHeight()) * (double(attr
.GetLineSpacing())/10.0 - 1.0));
4567 // Start position for each line relative to the paragraph
4568 int startPositionFirstLine
= leftIndent
;
4569 int startPositionSubsequentLines
= leftIndent
+ leftSubIndent
;
4571 // If we have a bullet in this paragraph, the start position for the first line's text
4572 // is actually leftIndent + leftSubIndent.
4573 if (attr
.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE
)
4574 startPositionFirstLine
= startPositionSubsequentLines
;
4576 long lastEndPos
= GetRange().GetStart()-1;
4577 long lastCompletedEndPos
= lastEndPos
;
4579 int currentWidth
= 0;
4580 SetPosition(rect
.GetPosition());
4582 wxPoint
currentPosition(0, spaceBeforePara
); // We will calculate lines relative to paragraph
4585 int maxHeight
= currentPosition
.y
;
4590 int lineDescent
= 0;
4592 wxRichTextObjectList::compatibility_iterator node
;
4594 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4596 wxArrayInt partialExtents
;
4599 int paraDescent
= 0;
4601 // This calculates the partial text extents
4602 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, rect
.GetPosition(), & partialExtents
);
4604 node
= m_children
.GetFirst();
4607 wxRichTextObject
* child
= node
->GetData();
4609 //child->SetCachedSize(wxDefaultSize);
4610 child
->Layout(dc
, context
, rect
, style
);
4612 node
= node
->GetNext();
4618 // We may need to go back to a previous child, in which case create the new line,
4619 // find the child corresponding to the start position of the string, and
4622 wxRect availableRect
;
4624 node
= m_children
.GetFirst();
4627 wxRichTextObject
* child
= node
->GetData();
4629 // If floating, ignore. We already laid out floats.
4630 // Also ignore if empty object, except if we haven't got any
4632 if (child
->IsFloating() || !child
->IsShown() ||
4633 (child
->GetRange().GetLength() == 0 && maxHeight
> spaceBeforePara
)
4636 node
= node
->GetNext();
4640 // If this is e.g. a composite text box, it will need to be laid out itself.
4641 // But if just a text fragment or image, for example, this will
4642 // do nothing. NB: won't we need to set the position after layout?
4643 // since for example if position is dependent on vertical line size, we
4644 // can't tell the position until the size is determined. So possibly introduce
4645 // another layout phase.
4647 // We may only be looking at part of a child, if we searched back for wrapping
4648 // and found a suitable point some way into the child. So get the size for the fragment
4651 long nextBreakPos
= GetFirstLineBreakPosition(lastEndPos
+1);
4652 long lastPosToUse
= child
->GetRange().GetEnd();
4653 bool lineBreakInThisObject
= (nextBreakPos
> -1 && nextBreakPos
<= child
->GetRange().GetEnd());
4655 if (lineBreakInThisObject
)
4656 lastPosToUse
= nextBreakPos
;
4659 int childDescent
= 0;
4661 int startOffset
= (lineCount
== 0 ? startPositionFirstLine
: startPositionSubsequentLines
);
4662 availableRect
= wxRect(rect
.x
+ startOffset
, rect
.y
+ currentPosition
.y
,
4663 rect
.width
- startOffset
- rightIndent
, rect
.height
);
4665 if (child
->IsTopLevel())
4667 wxSize oldSize
= child
->GetCachedSize();
4669 child
->Invalidate(wxRICHTEXT_ALL
);
4670 child
->SetPosition(wxPoint(0, 0));
4672 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4673 // lays out the object again using the minimum size
4674 // The position will be determined by its location in its line,
4675 // and not by the child's actual position.
4676 child
->LayoutToBestSize(dc
, context
, buffer
,
4677 attr
, child
->GetAttributes(), availableRect
, parentRect
, style
);
4679 if (oldSize
!= child
->GetCachedSize())
4681 partialExtents
.Clear();
4683 // Recalculate the partial text extents since the child object changed size
4684 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), & partialExtents
);
4688 // Problem: we need to layout composites here for which we need the available width,
4689 // but we can't get the available width without using the float collector which
4690 // needs to know the object height.
4692 if ((nextBreakPos
== -1) && (lastEndPos
== child
->GetRange().GetStart() - 1)) // i.e. we want to get the whole thing
4694 childSize
= child
->GetCachedSize();
4695 childDescent
= child
->GetDescent();
4699 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4700 // Get height only, then the width using the partial extents
4701 GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
);
4702 childSize
.x
= wxRichTextGetRangeWidth(*this, wxRichTextRange(lastEndPos
+1, lastPosToUse
), partialExtents
);
4704 GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, rect
.GetPosition());
4709 int loopIterations
= 0;
4711 // If there are nested objects that need to lay themselves out, we have to do this in a
4712 // loop because the height of the object may well depend on the available width.
4713 // And because of floating object positioning, the available width depends on the
4714 // height of the object and whether it will clash with the floating objects.
4715 // So, we see whether the available width changes due to the presence of floating images.
4716 // If it does, then we'll use the new restricted width to find the object height again.
4717 // If this causes another restriction in the available width, we'll try again, until
4718 // either we lose patience or the available width settles down.
4723 wxRect oldAvailableRect
= availableRect
;
4725 // Available width depends on the floating objects and the line height.
4726 // Note: the floating objects may be placed vertically along the two sides of
4727 // buffer, so we may have different available line widths with different
4728 // [startY, endY]. So, we can't determine how wide the available
4729 // space is until we know the exact line height.
4730 if (childDescent
== 0)
4732 lineHeight
= wxMax(lineHeight
, childSize
.y
);
4733 lineDescent
= maxDescent
;
4734 lineAscent
= maxAscent
;
4738 lineDescent
= wxMax(childDescent
, maxDescent
);
4739 lineAscent
= wxMax(childSize
.y
-childDescent
, maxAscent
);
4741 lineHeight
= wxMax(lineHeight
, (lineDescent
+ lineAscent
));
4742 wxRect floatAvailableRect
= collector
->GetAvailableRect(rect
.y
+ currentPosition
.y
, rect
.y
+ currentPosition
.y
+ lineHeight
);
4744 // Adjust availableRect to the space that is available when taking floating objects into account.
4746 if (floatAvailableRect
.x
+ startOffset
> availableRect
.x
)
4748 int newX
= floatAvailableRect
.x
+ startOffset
;
4749 int newW
= availableRect
.width
- (newX
- availableRect
.x
);
4750 availableRect
.x
= newX
;
4751 availableRect
.width
= newW
;
4754 if (floatAvailableRect
.width
< availableRect
.width
)
4755 availableRect
.width
= floatAvailableRect
.width
;
4757 currentPosition
.x
= availableRect
.x
- rect
.x
;
4759 if (child
->IsTopLevel() && loopIterations
<= 20)
4761 if (availableRect
!= oldAvailableRect
)
4763 wxSize oldSize
= child
->GetCachedSize();
4765 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4766 // lays out the object again using the minimum size
4767 child
->Invalidate(wxRICHTEXT_ALL
);
4768 child
->LayoutToBestSize(dc
, context
, buffer
,
4769 attr
, child
->GetAttributes(), availableRect
, parentRect
, style
);
4770 childSize
= child
->GetCachedSize();
4771 childDescent
= child
->GetDescent();
4773 if (oldSize
!= child
->GetCachedSize())
4775 partialExtents
.Clear();
4777 // Recalculate the partial text extents since the child object changed size
4778 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), & partialExtents
);
4781 // Go around the loop finding the available rect for the given floating objects
4791 if (child
->IsTopLevel())
4793 // We can move it to the correct position at this point
4794 // TODO: probably need to add margin
4795 child
->Move(GetPosition() + wxPoint(currentWidth
+ (wxMax(leftIndent
, leftIndent
+ leftSubIndent
)), currentPosition
.y
));
4799 // 1) There was a line break BEFORE the natural break
4800 // 2) There was a line break AFTER the natural break
4801 // 3) It's the last line
4802 // 4) The child still fits (carry on) - 'else' clause
4804 if ((lineBreakInThisObject
&& (childSize
.x
+ currentWidth
<= availableRect
.width
))
4806 (childSize
.x
+ currentWidth
> availableRect
.width
)
4808 ((childSize
.x
+ currentWidth
<= availableRect
.width
) && !node
->GetNext())
4812 long wrapPosition
= 0;
4813 if ((childSize
.x
+ currentWidth
<= availableRect
.width
) && !node
->GetNext() && !lineBreakInThisObject
)
4814 wrapPosition
= child
->GetRange().GetEnd();
4817 // Find a place to wrap. This may walk back to previous children,
4818 // for example if a word spans several objects.
4819 // Note: one object must contains only one wxTextAtrr, so the line height will not
4820 // change inside one object. Thus, we can pass the remain line width to the
4821 // FindWrapPosition function.
4822 if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos
+1, child
->GetRange().GetEnd()), dc
, context
, availableRect
.width
, wrapPosition
, & partialExtents
))
4824 // If the function failed, just cut it off at the end of this child.
4825 wrapPosition
= child
->GetRange().GetEnd();
4828 // FindWrapPosition can still return a value that will put us in an endless wrapping loop
4829 if (wrapPosition
<= lastCompletedEndPos
)
4830 wrapPosition
= wxMax(lastCompletedEndPos
+1,child
->GetRange().GetEnd());
4832 // Line end position shouldn't be the same as the end, or greater.
4833 if (wrapPosition
>= GetRange().GetEnd())
4834 wrapPosition
= GetRange().GetEnd()-1;
4836 // wxLogDebug(wxT("Split at %ld"), wrapPosition);
4838 // Let's find the actual size of the current line now
4840 wxRichTextRange
actualRange(lastCompletedEndPos
+1, wrapPosition
);
4844 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4845 if (!child
->IsEmpty())
4847 // Get height only, then the width using the partial extents
4848 GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
);
4849 actualSize
.x
= wxRichTextGetRangeWidth(*this, actualRange
, partialExtents
);
4853 GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
4855 currentWidth
= actualSize
.x
;
4857 // The descent for the whole line at this point, is the correct max descent
4858 maxDescent
= childDescent
;
4860 maxAscent
= actualSize
.y
-childDescent
;
4862 // lineHeight is given by the height for the whole line, since it will
4863 // take into account ascend/descend.
4864 lineHeight
= actualSize
.y
;
4866 if (lineHeight
== 0 && buffer
)
4868 wxFont
font(buffer
->GetFontTable().FindFont(attr
));
4869 wxCheckSetFont(dc
, font
);
4870 lineHeight
= dc
.GetCharHeight();
4873 if (maxDescent
== 0)
4876 dc
.GetTextExtent(wxT("X"), & w
, &h
, & maxDescent
);
4880 wxRichTextLine
* line
= AllocateLine(lineCount
);
4882 // Set relative range so we won't have to change line ranges when paragraphs are moved
4883 line
->SetRange(wxRichTextRange(actualRange
.GetStart() - GetRange().GetStart(), actualRange
.GetEnd() - GetRange().GetStart()));
4884 line
->SetPosition(currentPosition
);
4885 line
->SetSize(wxSize(currentWidth
, lineHeight
));
4886 line
->SetDescent(maxDescent
);
4888 maxHeight
= currentPosition
.y
+ lineHeight
;
4890 // Now move down a line. TODO: add margins, spacing
4891 currentPosition
.y
+= lineHeight
;
4892 currentPosition
.y
+= lineSpacing
;
4895 maxWidth
= wxMax(maxWidth
, currentWidth
+startOffset
);
4900 // TODO: account for zero-length objects
4901 // wxASSERT(wrapPosition > lastCompletedEndPos);
4903 lastEndPos
= wrapPosition
;
4904 lastCompletedEndPos
= lastEndPos
;
4908 if (wrapPosition
< GetRange().GetEnd()-1)
4910 // May need to set the node back to a previous one, due to searching back in wrapping
4911 wxRichTextObject
* childAfterWrapPosition
= FindObjectAtPosition(wrapPosition
+1);
4912 if (childAfterWrapPosition
)
4913 node
= m_children
.Find(childAfterWrapPosition
);
4915 node
= node
->GetNext();
4918 node
= node
->GetNext();
4920 // Apply paragraph styles such as alignment to the wrapped line
4921 ApplyParagraphStyle(line
, attr
, availableRect
, dc
);
4925 // We still fit, so don't add a line, and keep going
4926 currentWidth
+= childSize
.x
;
4928 if (childDescent
== 0)
4930 // An object with a zero descend value wants to take up the whole
4931 // height regardless of baseline
4932 lineHeight
= wxMax(lineHeight
, childSize
.y
);
4936 maxDescent
= wxMax(childDescent
, maxDescent
);
4937 maxAscent
= wxMax(childSize
.y
-childDescent
, maxAscent
);
4940 lineHeight
= wxMax(lineHeight
, (maxDescent
+ maxAscent
));
4942 maxWidth
= wxMax(maxWidth
, currentWidth
+startOffset
);
4943 lastEndPos
= child
->GetRange().GetEnd();
4945 node
= node
->GetNext();
4949 //wxASSERT(!(lastCompletedEndPos != -1 && lastCompletedEndPos < GetRange().GetEnd()-1));
4951 // Remove remaining unused line objects, if any
4952 ClearUnusedLines(lineCount
);
4954 // We need to add back the margins etc.
4956 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
4957 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxWidth
, currentPosition
.y
+ spaceAfterPara
));
4958 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
4959 SetCachedSize(marginRect
.GetSize());
4962 // The maximum size is the length of the paragraph stretched out into a line.
4963 // So if there were a single word, or an image, or a fixed-size text box, the object could be shrunk around
4964 // this size. TODO: take into account line breaks.
4966 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
4967 contentRect
= wxRect(wxPoint(0, 0), wxSize(paraSize
.x
+ wxMax(leftIndent
, leftIndent
+ leftSubIndent
) + rightIndent
, currentPosition
.y
+ spaceAfterPara
));
4968 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
4969 SetMaxSize(marginRect
.GetSize());
4972 // Find the greatest minimum size. Currently we only look at non-text objects,
4973 // which isn't ideal but it would be slow to find the maximum word width to
4974 // use as the minimum.
4977 node
= m_children
.GetFirst();
4980 wxRichTextObject
* child
= node
->GetData();
4982 // If floating, ignore. We already laid out floats.
4983 // Also ignore if empty object, except if we haven't got any
4985 if (!child
->IsFloating() && child
->GetRange().GetLength() != 0 && !wxDynamicCast(child
, wxRichTextPlainText
))
4987 if (child
->GetCachedSize().x
> minWidth
)
4988 minWidth
= child
->GetMinSize().x
;
4990 node
= node
->GetNext();
4993 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
4994 contentRect
= wxRect(wxPoint(0, 0), wxSize(minWidth
, currentPosition
.y
+ spaceAfterPara
));
4995 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
4996 SetMinSize(marginRect
.GetSize());
4999 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5000 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
5001 // Use the text extents to calculate the size of each fragment in each line
5002 wxRichTextLineList::compatibility_iterator lineNode
= m_cachedLines
.GetFirst();
5005 wxRichTextLine
* line
= lineNode
->GetData();
5006 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5008 // Loop through objects until we get to the one within range
5009 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
5013 wxRichTextObject
* child
= node2
->GetData();
5015 if (child
->GetRange().GetLength() > 0 && !child
->GetRange().IsOutside(lineRange
))
5017 wxRichTextRange rangeToUse
= lineRange
;
5018 rangeToUse
.LimitTo(child
->GetRange());
5020 // Find the size of the child from the text extents, and store in an array
5021 // for drawing later
5023 if (rangeToUse
.GetStart() > GetRange().GetStart())
5024 left
= partialExtents
[(rangeToUse
.GetStart()-1) - GetRange().GetStart()];
5025 int right
= partialExtents
[rangeToUse
.GetEnd() - GetRange().GetStart()];
5026 int sz
= right
- left
;
5027 line
->GetObjectSizes().Add(sz
);
5029 else if (child
->GetRange().GetStart() > lineRange
.GetEnd())
5030 // Can break out of inner loop now since we've passed this line's range
5033 node2
= node2
->GetNext();
5036 lineNode
= lineNode
->GetNext();
5044 /// Apply paragraph styles, such as centering, to wrapped lines
5045 /// TODO: take into account box attributes, possibly
5046 void wxRichTextParagraph::ApplyParagraphStyle(wxRichTextLine
* line
, const wxRichTextAttr
& attr
, const wxRect
& rect
, wxDC
& dc
)
5048 if (!attr
.HasAlignment())
5051 wxPoint pos
= line
->GetPosition();
5052 wxPoint originalPos
= pos
;
5053 wxSize size
= line
->GetSize();
5055 // centering, right-justification
5056 if (attr
.HasAlignment() && attr
.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE
)
5058 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
5059 pos
.x
= (rect
.GetWidth() - rightIndent
- size
.x
)/2 + pos
.x
;
5060 line
->SetPosition(pos
);
5062 else if (attr
.HasAlignment() && attr
.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT
)
5064 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
5065 pos
.x
= pos
.x
+ rect
.GetWidth() - size
.x
- rightIndent
;
5066 line
->SetPosition(pos
);
5069 if (pos
!= originalPos
)
5071 wxPoint inc
= pos
- originalPos
;
5073 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5077 wxRichTextObject
* child
= node
->GetData();
5078 if (child
->IsTopLevel() && !child
->GetRange().IsOutside(line
->GetAbsoluteRange()))
5079 child
->Move(child
->GetPosition() + inc
);
5081 node
= node
->GetNext();
5086 /// Insert text at the given position
5087 bool wxRichTextParagraph::InsertText(long pos
, const wxString
& text
)
5089 wxRichTextObject
* childToUse
= NULL
;
5090 wxRichTextObjectList::compatibility_iterator nodeToUse
= wxRichTextObjectList::compatibility_iterator();
5092 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5095 wxRichTextObject
* child
= node
->GetData();
5096 if (child
->GetRange().Contains(pos
) && child
->GetRange().GetLength() > 0)
5103 node
= node
->GetNext();
5108 wxRichTextPlainText
* textObject
= wxDynamicCast(childToUse
, wxRichTextPlainText
);
5111 int posInString
= pos
- textObject
->GetRange().GetStart();
5113 wxString newText
= textObject
->GetText().Mid(0, posInString
) +
5114 text
+ textObject
->GetText().Mid(posInString
);
5115 textObject
->SetText(newText
);
5117 int textLength
= text
.length();
5119 textObject
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart(),
5120 textObject
->GetRange().GetEnd() + textLength
));
5122 // Increment the end range of subsequent fragments in this paragraph.
5123 // We'll set the paragraph range itself at a higher level.
5125 wxRichTextObjectList::compatibility_iterator node
= nodeToUse
->GetNext();
5128 wxRichTextObject
* child
= node
->GetData();
5129 child
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart() + textLength
,
5130 textObject
->GetRange().GetEnd() + textLength
));
5132 node
= node
->GetNext();
5139 // TODO: if not a text object, insert at closest position, e.g. in front of it
5145 // Don't pass parent initially to suppress auto-setting of parent range.
5146 // We'll do that at a higher level.
5147 wxRichTextPlainText
* textObject
= new wxRichTextPlainText(text
, this);
5149 AppendChild(textObject
);
5156 void wxRichTextParagraph::Copy(const wxRichTextParagraph
& obj
)
5158 wxRichTextCompositeObject::Copy(obj
);
5161 /// Clear the cached lines
5162 void wxRichTextParagraph::ClearLines()
5164 WX_CLEAR_LIST(wxRichTextLineList
, m_cachedLines
);
5167 /// Get/set the object size for the given range. Returns false if the range
5168 /// is invalid for this object.
5169 bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
5171 if (!range
.IsWithin(GetRange()))
5174 if (flags
& wxRICHTEXT_UNFORMATTED
)
5176 // Just use unformatted data, assume no line breaks
5179 wxArrayInt childExtents
;
5188 int maxLineHeight
= 0;
5190 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5193 wxRichTextObject
* child
= node
->GetData();
5194 if (!child
->GetRange().IsOutside(range
))
5196 // Floating objects have a zero size within the paragraph.
5197 if (child
->IsFloating())
5202 if (partialExtents
->GetCount() > 0)
5203 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5207 partialExtents
->Add(0 /* zero size */ + lastSize
);
5214 wxRichTextRange rangeToUse
= range
;
5215 rangeToUse
.LimitTo(child
->GetRange());
5216 int childDescent
= 0;
5218 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we've already cached the size,
5219 // but it's only going to be used after caching has taken place.
5220 if ((flags
& wxRICHTEXT_HEIGHT_ONLY
) && child
->GetCachedSize().y
!= 0)
5222 childDescent
= child
->GetDescent();
5223 childSize
= child
->GetCachedSize();
5225 if (childDescent
== 0)
5227 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5231 maxDescent
= wxMax(maxDescent
, childDescent
);
5232 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5235 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5237 sz
.y
= wxMax(sz
.y
, maxLineHeight
);
5238 sz
.x
+= childSize
.x
;
5239 descent
= maxDescent
;
5241 else if (child
->IsTopLevel())
5243 childDescent
= child
->GetDescent();
5244 childSize
= child
->GetCachedSize();
5246 if (childDescent
== 0)
5248 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5252 maxDescent
= wxMax(maxDescent
, childDescent
);
5253 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5256 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5258 sz
.y
= wxMax(sz
.y
, maxLineHeight
);
5259 sz
.x
+= childSize
.x
;
5260 descent
= maxDescent
;
5262 // FIXME: this won't change the original values.
5263 // Should we be calling GetRangeSize above instead of using cached values?
5265 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange()))
5267 child
->SetCachedSize(childSize
);
5268 child
->SetDescent(childDescent
);
5275 if (partialExtents
->GetCount() > 0)
5276 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5280 partialExtents
->Add(childSize
.x
+ lastSize
);
5283 else if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, context
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
), p
))
5285 if (childDescent
== 0)
5287 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5291 maxDescent
= wxMax(maxDescent
, childDescent
);
5292 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5295 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5297 sz
.y
= wxMax(sz
.y
, maxLineHeight
);
5298 sz
.x
+= childSize
.x
;
5299 descent
= maxDescent
;
5301 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange()))
5303 child
->SetCachedSize(childSize
);
5304 child
->SetDescent(childDescent
);
5310 if (partialExtents
->GetCount() > 0)
5311 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5316 for (i
= 0; i
< childExtents
.GetCount(); i
++)
5318 partialExtents
->Add(childExtents
[i
] + lastSize
);
5328 node
= node
->GetNext();
5334 // Use formatted data, with line breaks
5337 // We're going to loop through each line, and then for each line,
5338 // call GetRangeSize for the fragment that comprises that line.
5339 // Only we have to do that multiple times within the line, because
5340 // the line may be broken into pieces. For now ignore line break commands
5341 // (so we can assume that getting the unformatted size for a fragment
5342 // within a line is the actual size)
5344 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5347 wxRichTextLine
* line
= node
->GetData();
5348 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5349 if (!lineRange
.IsOutside(range
))
5353 int maxLineHeight
= 0;
5354 int maxLineWidth
= 0;
5356 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
5359 wxRichTextObject
* child
= node2
->GetData();
5361 if (!child
->IsFloating() && !child
->GetRange().IsOutside(lineRange
))
5363 wxRichTextRange rangeToUse
= lineRange
;
5364 rangeToUse
.LimitTo(child
->GetRange());
5365 if (child
->IsTopLevel())
5366 rangeToUse
= child
->GetOwnRange();
5369 int childDescent
= 0;
5370 if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, context
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
)))
5372 if (childDescent
== 0)
5374 // Assume that if descent is zero, this child can occupy the full line height
5375 // and does not need space for the line's maximum descent. So we influence
5376 // the overall max line height only.
5377 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5381 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5382 maxDescent
= wxMax(maxAscent
, childDescent
);
5384 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5385 maxLineWidth
+= childSize
.x
;
5389 node2
= node2
->GetNext();
5392 descent
= wxMax(descent
, maxDescent
);
5394 // Increase size by a line (TODO: paragraph spacing)
5395 sz
.y
+= maxLineHeight
;
5396 sz
.x
= wxMax(sz
.x
, maxLineWidth
);
5398 node
= node
->GetNext();
5405 /// Finds the absolute position and row height for the given character position
5406 bool wxRichTextParagraph::FindPosition(wxDC
& dc
, wxRichTextDrawingContext
& context
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
5410 wxRichTextLine
* line
= ((wxRichTextParagraphLayoutBox
*)GetParent())->GetLineAtPosition(0);
5412 *height
= line
->GetSize().y
;
5414 *height
= dc
.GetCharHeight();
5416 // -1 means 'the start of the buffer'.
5419 pt
= pt
+ line
->GetPosition();
5424 // The final position in a paragraph is taken to mean the position
5425 // at the start of the next paragraph.
5426 if (index
== GetRange().GetEnd())
5428 wxRichTextParagraphLayoutBox
* parent
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
5429 wxASSERT( parent
!= NULL
);
5431 // Find the height at the next paragraph, if any
5432 wxRichTextLine
* line
= parent
->GetLineAtPosition(index
+ 1);
5435 *height
= line
->GetSize().y
;
5436 pt
= line
->GetAbsolutePosition();
5440 *height
= dc
.GetCharHeight();
5441 int indent
= ConvertTenthsMMToPixels(dc
, m_attributes
.GetLeftIndent());
5442 pt
= wxPoint(indent
, GetCachedSize().y
);
5448 if (index
< GetRange().GetStart() || index
> GetRange().GetEnd())
5451 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5454 wxRichTextLine
* line
= node
->GetData();
5455 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5456 if (index
>= lineRange
.GetStart() && index
<= lineRange
.GetEnd())
5458 // If this is the last point in the line, and we're forcing the
5459 // returned value to be the start of the next line, do the required
5461 if (index
== lineRange
.GetEnd() && forceLineStart
)
5463 if (node
->GetNext())
5465 wxRichTextLine
* nextLine
= node
->GetNext()->GetData();
5466 *height
= nextLine
->GetSize().y
;
5467 pt
= nextLine
->GetAbsolutePosition();
5472 pt
.y
= line
->GetPosition().y
+ GetPosition().y
;
5474 wxRichTextRange
r(lineRange
.GetStart(), index
);
5478 // We find the size of the line up to this point,
5479 // then we can add this size to the line start position and
5480 // paragraph start position to find the actual position.
5482 if (GetRangeSize(r
, rangeSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, line
->GetPosition()+ GetPosition()))
5484 pt
.x
= line
->GetPosition().x
+ GetPosition().x
+ rangeSize
.x
;
5485 *height
= line
->GetSize().y
;
5492 node
= node
->GetNext();
5498 /// Hit-testing: returns a flag indicating hit test details, plus
5499 /// information about position
5500 int wxRichTextParagraph::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
5503 return wxRICHTEXT_HITTEST_NONE
;
5505 // If we're in the top-level container, then we can return
5506 // a suitable hit test code even if the point is outside the container area,
5507 // so that we can position the caret sensibly even if we don't
5508 // click on valid content. If we're not at the top-level, and the point
5509 // is not within this paragraph object, then we don't want to stop more
5510 // precise hit-testing from working prematurely, so return immediately.
5511 // NEW STRATEGY: use the parent boundary to test whether we're in the
5512 // right region, not the paragraph, since the paragraph may be positioned
5513 // some way in from where the user clicks.
5516 wxRichTextObject
* tempObj
, *tempContextObj
;
5517 if (GetParent() && GetParent()->wxRichTextObject::HitTest(dc
, context
, pt
, tmpPos
, & tempObj
, & tempContextObj
, flags
) == wxRICHTEXT_HITTEST_NONE
)
5518 return wxRICHTEXT_HITTEST_NONE
;
5521 wxRichTextObjectList::compatibility_iterator objNode
= m_children
.GetFirst();
5524 wxRichTextObject
* child
= objNode
->GetData();
5525 // Don't recurse if we have wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS,
5526 // and also, if this seems composite but actually is marked as atomic,
5528 if (child
->IsTopLevel() && ((flags
& wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS
) == 0) &&
5529 (! (((flags
& wxRICHTEXT_HITTEST_HONOUR_ATOMIC
) != 0) && child
->IsAtomic())))
5532 int hitTest
= child
->HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
);
5533 if (hitTest
!= wxRICHTEXT_HITTEST_NONE
)
5538 objNode
= objNode
->GetNext();
5541 wxPoint paraPos
= GetPosition();
5543 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5546 wxRichTextLine
* line
= node
->GetData();
5547 wxPoint linePos
= paraPos
+ line
->GetPosition();
5548 wxSize lineSize
= line
->GetSize();
5549 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5551 if (pt
.y
<= linePos
.y
+ lineSize
.y
)
5553 if (pt
.x
< linePos
.x
)
5555 textPosition
= lineRange
.GetStart();
5556 *obj
= FindObjectAtPosition(textPosition
);
5557 *contextObj
= GetContainer();
5558 return wxRICHTEXT_HITTEST_BEFORE
|wxRICHTEXT_HITTEST_OUTSIDE
;
5560 else if (pt
.x
>= (linePos
.x
+ lineSize
.x
))
5562 textPosition
= lineRange
.GetEnd();
5563 *obj
= FindObjectAtPosition(textPosition
);
5564 *contextObj
= GetContainer();
5565 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
;
5569 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5570 wxArrayInt partialExtents
;
5575 // This calculates the partial text extents
5576 GetRangeSize(lineRange
, paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, linePos
, & partialExtents
);
5578 int lastX
= linePos
.x
;
5580 for (i
= 0; i
< partialExtents
.GetCount(); i
++)
5582 int nextX
= partialExtents
[i
] + linePos
.x
;
5584 if (pt
.x
>= lastX
&& pt
.x
<= nextX
)
5586 textPosition
= i
+ lineRange
.GetStart(); // minus 1?
5588 *obj
= FindObjectAtPosition(textPosition
);
5589 *contextObj
= GetContainer();
5591 // So now we know it's between i-1 and i.
5592 // Let's see if we can be more precise about
5593 // which side of the position it's on.
5595 int midPoint
= (nextX
+ lastX
)/2;
5596 if (pt
.x
>= midPoint
)
5597 return wxRICHTEXT_HITTEST_AFTER
;
5599 return wxRICHTEXT_HITTEST_BEFORE
;
5606 int lastX
= linePos
.x
;
5607 for (i
= lineRange
.GetStart(); i
<= lineRange
.GetEnd(); i
++)
5612 wxRichTextRange
rangeToUse(lineRange
.GetStart(), i
);
5614 GetRangeSize(rangeToUse
, childSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, linePos
);
5616 int nextX
= childSize
.x
+ linePos
.x
;
5618 if (pt
.x
>= lastX
&& pt
.x
<= nextX
)
5622 *obj
= FindObjectAtPosition(textPosition
);
5623 *contextObj
= GetContainer();
5625 // So now we know it's between i-1 and i.
5626 // Let's see if we can be more precise about
5627 // which side of the position it's on.
5629 int midPoint
= (nextX
+ lastX
)/2;
5630 if (pt
.x
>= midPoint
)
5631 return wxRICHTEXT_HITTEST_AFTER
;
5633 return wxRICHTEXT_HITTEST_BEFORE
;
5644 node
= node
->GetNext();
5647 return wxRICHTEXT_HITTEST_NONE
;
5650 /// Split an object at this position if necessary, and return
5651 /// the previous object, or NULL if inserting at beginning.
5652 wxRichTextObject
* wxRichTextParagraph::SplitAt(long pos
, wxRichTextObject
** previousObject
)
5654 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5657 wxRichTextObject
* child
= node
->GetData();
5659 if (pos
== child
->GetRange().GetStart())
5663 if (node
->GetPrevious())
5664 *previousObject
= node
->GetPrevious()->GetData();
5666 *previousObject
= NULL
;
5672 if (child
->GetRange().Contains(pos
))
5674 // This should create a new object, transferring part of
5675 // the content to the old object and the rest to the new object.
5676 wxRichTextObject
* newObject
= child
->DoSplit(pos
);
5678 // If we couldn't split this object, just insert in front of it.
5681 // Maybe this is an empty string, try the next one
5686 // Insert the new object after 'child'
5687 if (node
->GetNext())
5688 m_children
.Insert(node
->GetNext(), newObject
);
5690 m_children
.Append(newObject
);
5691 newObject
->SetParent(this);
5694 *previousObject
= child
;
5700 node
= node
->GetNext();
5703 *previousObject
= NULL
;
5707 /// Move content to a list from obj on
5708 void wxRichTextParagraph::MoveToList(wxRichTextObject
* obj
, wxList
& list
)
5710 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(obj
);
5713 wxRichTextObject
* child
= node
->GetData();
5716 wxRichTextObjectList::compatibility_iterator oldNode
= node
;
5718 node
= node
->GetNext();
5720 m_children
.DeleteNode(oldNode
);
5724 /// Add content back from list
5725 void wxRichTextParagraph::MoveFromList(wxList
& list
)
5727 for (wxList::compatibility_iterator node
= list
.GetFirst(); node
; node
= node
->GetNext())
5729 AppendChild((wxRichTextObject
*) node
->GetData());
5734 void wxRichTextParagraph::CalculateRange(long start
, long& end
)
5736 wxRichTextCompositeObject::CalculateRange(start
, end
);
5738 // Add one for end of paragraph
5741 m_range
.SetRange(start
, end
);
5744 /// Find the object at the given position
5745 wxRichTextObject
* wxRichTextParagraph::FindObjectAtPosition(long position
)
5747 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5750 wxRichTextObject
* obj
= node
->GetData();
5751 if (obj
->GetRange().Contains(position
) ||
5752 obj
->GetRange().GetStart() == position
||
5753 obj
->GetRange().GetEnd() == position
)
5756 node
= node
->GetNext();
5761 /// Get the plain text searching from the start or end of the range.
5762 /// The resulting string may be shorter than the range given.
5763 bool wxRichTextParagraph::GetContiguousPlainText(wxString
& text
, const wxRichTextRange
& range
, bool fromStart
)
5765 text
= wxEmptyString
;
5769 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5772 wxRichTextObject
* obj
= node
->GetData();
5773 if (!obj
->GetRange().IsOutside(range
))
5775 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5778 text
+= textObj
->GetTextForRange(range
);
5786 node
= node
->GetNext();
5791 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetLast();
5794 wxRichTextObject
* obj
= node
->GetData();
5795 if (!obj
->GetRange().IsOutside(range
))
5797 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5800 text
= textObj
->GetTextForRange(range
) + text
;
5804 text
= wxT(" ") + text
;
5808 node
= node
->GetPrevious();
5815 /// Find a suitable wrap position.
5816 bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange
& range
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int availableSpace
, long& wrapPosition
, wxArrayInt
* partialExtents
)
5818 if (range
.GetLength() <= 0)
5821 // Find the first position where the line exceeds the available space.
5823 long breakPosition
= range
.GetEnd();
5825 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5826 if (partialExtents
&& partialExtents
->GetCount() >= (size_t) (GetRange().GetLength()-1)) // the final position in a paragraph is the newline
5830 if (range
.GetStart() > GetRange().GetStart())
5831 widthBefore
= (*partialExtents
)[range
.GetStart() - GetRange().GetStart() - 1];
5836 for (i
= (size_t) range
.GetStart(); i
<= (size_t) range
.GetEnd(); i
++)
5838 int widthFromStartOfThisRange
= (*partialExtents
)[i
- GetRange().GetStart()] - widthBefore
;
5840 if (widthFromStartOfThisRange
> availableSpace
)
5842 breakPosition
= i
-1;
5850 // Binary chop for speed
5851 long minPos
= range
.GetStart();
5852 long maxPos
= range
.GetEnd();
5855 if (minPos
== maxPos
)
5858 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
5860 if (sz
.x
> availableSpace
)
5861 breakPosition
= minPos
- 1;
5864 else if ((maxPos
- minPos
) == 1)
5867 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
5869 if (sz
.x
> availableSpace
)
5870 breakPosition
= minPos
- 1;
5873 GetRangeSize(wxRichTextRange(range
.GetStart(), maxPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
5874 if (sz
.x
> availableSpace
)
5875 breakPosition
= maxPos
-1;
5881 long nextPos
= minPos
+ ((maxPos
- minPos
) / 2);
5884 GetRangeSize(wxRichTextRange(range
.GetStart(), nextPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
5886 if (sz
.x
> availableSpace
)
5898 // Now we know the last position on the line.
5899 // Let's try to find a word break.
5902 if (GetContiguousPlainText(plainText
, wxRichTextRange(range
.GetStart(), breakPosition
), false))
5904 int newLinePos
= plainText
.Find(wxRichTextLineBreakChar
);
5905 if (newLinePos
!= wxNOT_FOUND
)
5907 breakPosition
= wxMax(0, range
.GetStart() + newLinePos
);
5911 int spacePos
= plainText
.Find(wxT(' '), true);
5912 int tabPos
= plainText
.Find(wxT('\t'), true);
5913 int pos
= wxMax(spacePos
, tabPos
);
5914 if (pos
!= wxNOT_FOUND
)
5916 int positionsFromEndOfString
= plainText
.length() - pos
- 1;
5917 breakPosition
= breakPosition
- positionsFromEndOfString
;
5922 wrapPosition
= breakPosition
;
5927 /// Get the bullet text for this paragraph.
5928 wxString
wxRichTextParagraph::GetBulletText()
5930 if (GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
||
5931 (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
))
5932 return wxEmptyString
;
5934 int number
= GetAttributes().GetBulletNumber();
5937 if ((GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ARABIC
) || (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
))
5939 text
.Printf(wxT("%d"), number
);
5941 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_UPPER
)
5943 // TODO: Unicode, and also check if number > 26
5944 text
.Printf(wxT("%c"), (wxChar
) (number
+64));
5946 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_LOWER
)
5948 // TODO: Unicode, and also check if number > 26
5949 text
.Printf(wxT("%c"), (wxChar
) (number
+96));
5951 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER
)
5953 text
= wxRichTextDecimalToRoman(number
);
5955 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER
)
5957 text
= wxRichTextDecimalToRoman(number
);
5960 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
)
5962 text
= GetAttributes().GetBulletText();
5965 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
)
5967 // The outline style relies on the text being computed statically,
5968 // since it depends on other levels points (e.g. 1.2.1.1). So normally the bullet text
5969 // should be stored in the attributes; if not, just use the number for this
5970 // level, as previously computed.
5971 if (!GetAttributes().GetBulletText().IsEmpty())
5972 text
= GetAttributes().GetBulletText();
5975 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PARENTHESES
)
5977 text
= wxT("(") + text
+ wxT(")");
5979 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_RIGHT_PARENTHESIS
)
5981 text
= text
+ wxT(")");
5984 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PERIOD
)
5992 /// Allocate or reuse a line object
5993 wxRichTextLine
* wxRichTextParagraph::AllocateLine(int pos
)
5995 if (pos
< (int) m_cachedLines
.GetCount())
5997 wxRichTextLine
* line
= m_cachedLines
.Item(pos
)->GetData();
6003 wxRichTextLine
* line
= new wxRichTextLine(this);
6004 m_cachedLines
.Append(line
);
6009 /// Clear remaining unused line objects, if any
6010 bool wxRichTextParagraph::ClearUnusedLines(int lineCount
)
6012 int cachedLineCount
= m_cachedLines
.GetCount();
6013 if ((int) cachedLineCount
> lineCount
)
6015 for (int i
= 0; i
< (int) (cachedLineCount
- lineCount
); i
++)
6017 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetLast();
6018 wxRichTextLine
* line
= node
->GetData();
6019 m_cachedLines
.Erase(node
);
6026 /// Get combined attributes of the base style, paragraph style and character style. We use this to dynamically
6027 /// retrieve the actual style.
6028 wxRichTextAttr
wxRichTextParagraph::GetCombinedAttributes(const wxRichTextAttr
& contentStyle
, bool includingBoxAttr
) const
6030 wxRichTextAttr attr
;
6031 wxRichTextParagraphLayoutBox
* buf
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
6034 attr
= buf
->GetBasicStyle();
6035 if (!includingBoxAttr
)
6037 attr
.GetTextBoxAttr().Reset();
6038 // The background colour will be painted by the container, and we don't
6039 // want to unnecessarily overwrite the background when we're drawing text
6040 // because this may erase the guideline (which appears just under the text
6041 // if there's no padding).
6042 attr
.SetFlags(attr
.GetFlags() & ~wxTEXT_ATTR_BACKGROUND_COLOUR
);
6044 wxRichTextApplyStyle(attr
, GetAttributes());
6047 attr
= GetAttributes();
6049 wxRichTextApplyStyle(attr
, contentStyle
);
6053 /// Get combined attributes of the base style and paragraph style.
6054 wxRichTextAttr
wxRichTextParagraph::GetCombinedAttributes(bool includingBoxAttr
) const
6056 wxRichTextAttr attr
;
6057 wxRichTextParagraphLayoutBox
* buf
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
6060 attr
= buf
->GetBasicStyle();
6061 if (!includingBoxAttr
)
6062 attr
.GetTextBoxAttr().Reset();
6063 wxRichTextApplyStyle(attr
, GetAttributes());
6066 attr
= GetAttributes();
6071 // Create default tabstop array
6072 void wxRichTextParagraph::InitDefaultTabs()
6074 // create a default tab list at 10 mm each.
6075 for (int i
= 0; i
< 20; ++i
)
6077 sm_defaultTabs
.Add(i
*100);
6081 // Clear default tabstop array
6082 void wxRichTextParagraph::ClearDefaultTabs()
6084 sm_defaultTabs
.Clear();
6087 void wxRichTextParagraph::LayoutFloat(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, int style
, wxRichTextFloatCollector
* floatCollector
)
6089 wxRichTextObjectList::compatibility_iterator node
= GetChildren().GetFirst();
6092 wxRichTextObject
* anchored
= node
->GetData();
6093 if (anchored
&& anchored
->IsFloating() && !floatCollector
->HasFloat(anchored
))
6097 anchored
->GetRangeSize(anchored
->GetRange(), size
, descent
, dc
, context
, style
);
6100 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().IsValid())
6102 offsetY
= anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetValue();
6103 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
6105 offsetY
= ConvertTenthsMMToPixels(dc
, offsetY
);
6109 int pos
= floatCollector
->GetFitPosition(anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode(), rect
.y
+ offsetY
, size
.y
);
6111 /* Update the offset */
6112 int newOffsetY
= pos
- rect
.y
;
6113 if (newOffsetY
!= offsetY
)
6115 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
6116 newOffsetY
= ConvertPixelsToTenthsMM(dc
, newOffsetY
);
6117 anchored
->GetAttributes().GetTextBoxAttr().GetTop().SetValue(newOffsetY
);
6120 if (anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT
)
6122 else if (anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT
)
6123 x
= rect
.x
+ rect
.width
- size
.x
;
6125 anchored
->SetPosition(wxPoint(x
, pos
));
6126 anchored
->SetCachedSize(size
);
6127 floatCollector
->CollectFloat(this, anchored
);
6130 node
= node
->GetNext();
6134 // Get the first position from pos that has a line break character.
6135 long wxRichTextParagraph::GetFirstLineBreakPosition(long pos
)
6137 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
6140 wxRichTextObject
* obj
= node
->GetData();
6141 if (pos
>= obj
->GetRange().GetStart() && pos
<= obj
->GetRange().GetEnd())
6143 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
6146 long breakPos
= textObj
->GetFirstLineBreakPosition(pos
);
6151 node
= node
->GetNext();
6158 * This object represents a line in a paragraph, and stores
6159 * offsets from the start of the paragraph representing the
6160 * start and end positions of the line.
6163 wxRichTextLine::wxRichTextLine(wxRichTextParagraph
* parent
)
6169 void wxRichTextLine::Init(wxRichTextParagraph
* parent
)
6172 m_range
.SetRange(-1, -1);
6173 m_pos
= wxPoint(0, 0);
6174 m_size
= wxSize(0, 0);
6176 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6177 m_objectSizes
.Clear();
6182 void wxRichTextLine::Copy(const wxRichTextLine
& obj
)
6184 m_range
= obj
.m_range
;
6185 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6186 m_objectSizes
= obj
.m_objectSizes
;
6190 /// Get the absolute object position
6191 wxPoint
wxRichTextLine::GetAbsolutePosition() const
6193 return m_parent
->GetPosition() + m_pos
;
6196 /// Get the absolute range
6197 wxRichTextRange
wxRichTextLine::GetAbsoluteRange() const
6199 wxRichTextRange
range(m_range
.GetStart() + m_parent
->GetRange().GetStart(), 0);
6200 range
.SetEnd(range
.GetStart() + m_range
.GetLength()-1);
6205 * wxRichTextPlainText
6206 * This object represents a single piece of text.
6209 IMPLEMENT_DYNAMIC_CLASS(wxRichTextPlainText
, wxRichTextObject
)
6211 wxRichTextPlainText::wxRichTextPlainText(const wxString
& text
, wxRichTextObject
* parent
, wxRichTextAttr
* style
):
6212 wxRichTextObject(parent
)
6215 SetAttributes(*style
);
6220 #define USE_KERNING_FIX 1
6222 // If insufficient tabs are defined, this is the tab width used
6223 #define WIDTH_FOR_DEFAULT_TABS 50
6226 bool wxRichTextPlainText::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int WXUNUSED(style
))
6228 wxRichTextParagraph
* para
= wxDynamicCast(GetParent(), wxRichTextParagraph
);
6229 wxASSERT (para
!= NULL
);
6231 wxRichTextAttr
textAttr(para
? para
->GetCombinedAttributes(GetAttributes(), false /* no box attributes */) : GetAttributes());
6232 context
.ApplyVirtualAttributes(textAttr
, this);
6234 // Let's make the assumption for now that for content in a paragraph, including
6235 // text, we never have a discontinuous selection. So we only deal with a
6237 wxRichTextRange selectionRange
;
6238 if (selection
.IsValid())
6240 wxRichTextRangeArray selectionRanges
= selection
.GetSelectionForObject(this);
6241 if (selectionRanges
.GetCount() > 0)
6242 selectionRange
= selectionRanges
[0];
6244 selectionRange
= wxRICHTEXT_NO_SELECTION
;
6247 selectionRange
= wxRICHTEXT_NO_SELECTION
;
6249 int offset
= GetRange().GetStart();
6251 // Replace line break characters with spaces
6252 wxString str
= m_text
;
6253 wxString toRemove
= wxRichTextLineBreakChar
;
6254 str
.Replace(toRemove
, wxT(" "));
6255 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS
))
6258 long len
= range
.GetLength();
6259 wxString stringChunk
= str
.Mid(range
.GetStart() - offset
, (size_t) len
);
6261 // Test for the optimized situations where all is selected, or none
6264 wxFont
textFont(GetBuffer()->GetFontTable().FindFont(textAttr
));
6265 wxCheckSetFont(dc
, textFont
);
6266 int charHeight
= dc
.GetCharHeight();
6269 if ( textFont
.IsOk() )
6271 if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
) )
6273 if (textFont
.IsUsingSizeInPixels())
6275 double size
= static_cast<double>(textFont
.GetPixelSize().y
) / wxSCRIPT_MUL_FACTOR
;
6276 textFont
.SetPixelSize(wxSize(0, static_cast<int>(size
)));
6282 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6283 textFont
.SetPointSize(static_cast<int>(size
));
6287 wxCheckSetFont(dc
, textFont
);
6289 else if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) )
6291 if (textFont
.IsUsingSizeInPixels())
6293 double size
= static_cast<double>(textFont
.GetPixelSize().y
) / wxSCRIPT_MUL_FACTOR
;
6294 textFont
.SetPixelSize(wxSize(0, static_cast<int>(size
)));
6296 int sub_height
= static_cast<int>(static_cast<double>(charHeight
) / wxSCRIPT_MUL_FACTOR
);
6297 y
= rect
.y
+ (rect
.height
- sub_height
+ (descent
- m_descent
));
6301 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6302 textFont
.SetPointSize(static_cast<int>(size
));
6304 int sub_height
= static_cast<int>(static_cast<double>(charHeight
) / wxSCRIPT_MUL_FACTOR
);
6305 y
= rect
.y
+ (rect
.height
- sub_height
+ (descent
- m_descent
));
6307 wxCheckSetFont(dc
, textFont
);
6312 y
= rect
.y
+ (rect
.height
- charHeight
- (descent
- m_descent
));
6318 y
= rect
.y
+ (rect
.height
- charHeight
- (descent
- m_descent
));
6321 // TODO: new selection code
6323 // (a) All selected.
6324 if (selectionRange
.GetStart() <= range
.GetStart() && selectionRange
.GetEnd() >= range
.GetEnd())
6326 DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, true);
6328 // (b) None selected.
6329 else if (selectionRange
.GetEnd() < range
.GetStart() || selectionRange
.GetStart() > range
.GetEnd())
6331 // Draw all unselected
6332 DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, false);
6336 // (c) Part selected, part not
6337 // Let's draw unselected chunk, selected chunk, then unselected chunk.
6339 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6341 // 1. Initial unselected chunk, if any, up until start of selection.
6342 if (selectionRange
.GetStart() > range
.GetStart() && selectionRange
.GetStart() <= range
.GetEnd())
6344 int r1
= range
.GetStart();
6345 int s1
= selectionRange
.GetStart()-1;
6346 int fragmentLen
= s1
- r1
+ 1;
6347 if (fragmentLen
< 0)
6349 wxLogDebug(wxT("Mid(%d, %d"), (int)(r1
- offset
), (int)fragmentLen
);
6351 wxString stringFragment
= str
.Mid(r1
- offset
, fragmentLen
);
6353 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false);
6356 if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
)
6358 // Compensate for kerning difference
6359 wxString
stringFragment2(str
.Mid(r1
- offset
, fragmentLen
+1));
6360 wxString
stringFragment3(str
.Mid(r1
- offset
+ fragmentLen
, 1));
6362 wxCoord w1
, h1
, w2
, h2
, w3
, h3
;
6363 dc
.GetTextExtent(stringFragment
, & w1
, & h1
);
6364 dc
.GetTextExtent(stringFragment2
, & w2
, & h2
);
6365 dc
.GetTextExtent(stringFragment3
, & w3
, & h3
);
6367 int kerningDiff
= (w1
+ w3
) - w2
;
6368 x
= x
- kerningDiff
;
6373 // 2. Selected chunk, if any.
6374 if (selectionRange
.GetEnd() >= range
.GetStart())
6376 int s1
= wxMax(selectionRange
.GetStart(), range
.GetStart());
6377 int s2
= wxMin(selectionRange
.GetEnd(), range
.GetEnd());
6379 int fragmentLen
= s2
- s1
+ 1;
6380 if (fragmentLen
< 0)
6382 wxLogDebug(wxT("Mid(%d, %d"), (int)(s1
- offset
), (int)fragmentLen
);
6384 wxString stringFragment
= str
.Mid(s1
- offset
, fragmentLen
);
6386 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, true);
6389 if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
)
6391 // Compensate for kerning difference
6392 wxString
stringFragment2(str
.Mid(s1
- offset
, fragmentLen
+1));
6393 wxString
stringFragment3(str
.Mid(s1
- offset
+ fragmentLen
, 1));
6395 wxCoord w1
, h1
, w2
, h2
, w3
, h3
;
6396 dc
.GetTextExtent(stringFragment
, & w1
, & h1
);
6397 dc
.GetTextExtent(stringFragment2
, & w2
, & h2
);
6398 dc
.GetTextExtent(stringFragment3
, & w3
, & h3
);
6400 int kerningDiff
= (w1
+ w3
) - w2
;
6401 x
= x
- kerningDiff
;
6406 // 3. Remaining unselected chunk, if any
6407 if (selectionRange
.GetEnd() < range
.GetEnd())
6409 int s2
= wxMin(selectionRange
.GetEnd()+1, range
.GetEnd());
6410 int r2
= range
.GetEnd();
6412 int fragmentLen
= r2
- s2
+ 1;
6413 if (fragmentLen
< 0)
6415 wxLogDebug(wxT("Mid(%d, %d"), (int)(s2
- offset
), (int)fragmentLen
);
6417 wxString stringFragment
= str
.Mid(s2
- offset
, fragmentLen
);
6419 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false);
6426 bool wxRichTextPlainText::DrawTabbedString(wxDC
& dc
, const wxRichTextAttr
& attr
, const wxRect
& rect
,wxString
& str
, wxCoord
& x
, wxCoord
& y
, bool selected
)
6428 bool hasTabs
= (str
.Find(wxT('\t')) != wxNOT_FOUND
);
6430 wxArrayInt tabArray
;
6434 if (attr
.GetTabs().IsEmpty())
6435 tabArray
= wxRichTextParagraph::GetDefaultTabs();
6437 tabArray
= attr
.GetTabs();
6438 tabCount
= tabArray
.GetCount();
6440 for (int i
= 0; i
< tabCount
; ++i
)
6442 int pos
= tabArray
[i
];
6443 pos
= ConvertTenthsMMToPixels(dc
, pos
);
6450 int nextTabPos
= -1;
6456 wxColour
highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
));
6457 wxColour
highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
));
6459 wxCheckSetBrush(dc
, wxBrush(highlightColour
));
6460 wxCheckSetPen(dc
, wxPen(highlightColour
));
6461 dc
.SetTextForeground(highlightTextColour
);
6462 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6466 dc
.SetTextForeground(attr
.GetTextColour());
6468 if (attr
.HasFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
) && attr
.GetBackgroundColour().IsOk())
6470 dc
.SetBackgroundMode(wxBRUSHSTYLE_SOLID
);
6471 dc
.SetTextBackground(attr
.GetBackgroundColour());
6474 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6477 wxCoord x_orig
= GetParent()->GetPosition().x
;
6480 // the string has a tab
6481 // break up the string at the Tab
6482 wxString stringChunk
= str
.BeforeFirst(wxT('\t'));
6483 str
= str
.AfterFirst(wxT('\t'));
6484 dc
.GetTextExtent(stringChunk
, & w
, & h
);
6486 bool not_found
= true;
6487 for (int i
= 0; i
< tabCount
&& not_found
; ++i
)
6489 nextTabPos
= tabArray
.Item(i
) + x_orig
;
6491 // Find the next tab position.
6492 // Even if we're at the end of the tab array, we must still draw the chunk.
6494 if (nextTabPos
> tabPos
|| (i
== (tabCount
- 1)))
6496 if (nextTabPos
<= tabPos
)
6498 int defaultTabWidth
= ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
);
6499 nextTabPos
= tabPos
+ defaultTabWidth
;
6506 wxRect
selRect(x
, rect
.y
, w
, rect
.GetHeight());
6507 dc
.DrawRectangle(selRect
);
6509 dc
.DrawText(stringChunk
, x
, y
);
6511 if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
))
6513 wxPen oldPen
= dc
.GetPen();
6514 wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1));
6515 dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5));
6516 wxCheckSetPen(dc
, oldPen
);
6522 hasTabs
= (str
.Find(wxT('\t')) != wxNOT_FOUND
);
6527 dc
.GetTextExtent(str
, & w
, & h
);
6530 wxRect
selRect(x
, rect
.y
, w
, rect
.GetHeight());
6531 dc
.DrawRectangle(selRect
);
6533 dc
.DrawText(str
, x
, y
);
6535 if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
))
6537 wxPen oldPen
= dc
.GetPen();
6538 wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1));
6539 dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5));
6540 wxCheckSetPen(dc
, oldPen
);
6549 /// Lay the item out
6550 bool wxRichTextPlainText::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& WXUNUSED(rect
), const wxRect
& WXUNUSED(parentRect
), int WXUNUSED(style
))
6552 // Only lay out if we haven't already cached the size
6554 GetRangeSize(GetRange(), m_size
, m_descent
, dc
, context
, 0, wxPoint(0, 0));
6556 // Eventually we want to have a reasonable estimate of minimum size.
6557 m_minSize
= wxSize(0, 0);
6562 void wxRichTextPlainText::Copy(const wxRichTextPlainText
& obj
)
6564 wxRichTextObject::Copy(obj
);
6566 m_text
= obj
.m_text
;
6569 /// Get/set the object size for the given range. Returns false if the range
6570 /// is invalid for this object.
6571 bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int WXUNUSED(flags
), wxPoint position
, wxArrayInt
* partialExtents
) const
6573 if (!range
.IsWithin(GetRange()))
6576 wxRichTextParagraph
* para
= wxDynamicCast(GetParent(), wxRichTextParagraph
);
6577 wxASSERT (para
!= NULL
);
6579 int relativeX
= position
.x
- GetParent()->GetPosition().x
;
6581 wxRichTextAttr
textAttr(para
? para
->GetCombinedAttributes(GetAttributes()) : GetAttributes());
6582 context
.ApplyVirtualAttributes(textAttr
, (wxRichTextObject
*) this);
6584 // Always assume unformatted text, since at this level we have no knowledge
6585 // of line breaks - and we don't need it, since we'll calculate size within
6586 // formatted text by doing it in chunks according to the line ranges
6588 bool bScript(false);
6589 wxFont
font(GetBuffer()->GetFontTable().FindFont(textAttr
));
6592 if ( textAttr
.HasTextEffects() && ( (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
)
6593 || (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) ) )
6595 wxFont textFont
= font
;
6596 if (textFont
.IsUsingSizeInPixels())
6598 double size
= static_cast<double>(textFont
.GetPixelSize().y
) / wxSCRIPT_MUL_FACTOR
;
6599 textFont
.SetPixelSize(wxSize(0, static_cast<int>(size
)));
6603 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6604 textFont
.SetPointSize(static_cast<int>(size
));
6606 wxCheckSetFont(dc
, textFont
);
6611 wxCheckSetFont(dc
, font
);
6615 bool haveDescent
= false;
6616 int startPos
= range
.GetStart() - GetRange().GetStart();
6617 long len
= range
.GetLength();
6619 wxString
str(m_text
);
6620 wxString toReplace
= wxRichTextLineBreakChar
;
6621 str
.Replace(toReplace
, wxT(" "));
6623 wxString stringChunk
= str
.Mid(startPos
, (size_t) len
);
6625 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS
))
6626 stringChunk
.MakeUpper();
6630 if (stringChunk
.Find(wxT('\t')) != wxNOT_FOUND
)
6632 // the string has a tab
6633 wxArrayInt tabArray
;
6634 if (textAttr
.GetTabs().IsEmpty())
6635 tabArray
= wxRichTextParagraph::GetDefaultTabs();
6637 tabArray
= textAttr
.GetTabs();
6639 int tabCount
= tabArray
.GetCount();
6641 for (int i
= 0; i
< tabCount
; ++i
)
6643 int pos
= tabArray
[i
];
6644 pos
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, pos
);
6648 int nextTabPos
= -1;
6650 while (stringChunk
.Find(wxT('\t')) >= 0)
6652 int absoluteWidth
= 0;
6654 // the string has a tab
6655 // break up the string at the Tab
6656 wxString stringFragment
= stringChunk
.BeforeFirst(wxT('\t'));
6657 stringChunk
= stringChunk
.AfterFirst(wxT('\t'));
6662 if (partialExtents
->GetCount() > 0)
6663 oldWidth
= (*partialExtents
)[partialExtents
->GetCount()-1];
6667 // Add these partial extents
6669 dc
.GetPartialTextExtents(stringFragment
, p
);
6671 for (j
= 0; j
< p
.GetCount(); j
++)
6672 partialExtents
->Add(oldWidth
+ p
[j
]);
6674 if (partialExtents
->GetCount() > 0)
6675 absoluteWidth
= (*partialExtents
)[(*partialExtents
).GetCount()-1] + relativeX
;
6677 absoluteWidth
= relativeX
;
6681 dc
.GetTextExtent(stringFragment
, & w
, & h
);
6683 absoluteWidth
= width
+ relativeX
;
6687 bool notFound
= true;
6688 for (int i
= 0; i
< tabCount
&& notFound
; ++i
)
6690 nextTabPos
= tabArray
.Item(i
);
6692 // Find the next tab position.
6693 // Even if we're at the end of the tab array, we must still process the chunk.
6695 if (nextTabPos
> absoluteWidth
|| (i
== (tabCount
- 1)))
6697 if (nextTabPos
<= absoluteWidth
)
6699 int defaultTabWidth
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
);
6700 nextTabPos
= absoluteWidth
+ defaultTabWidth
;
6704 width
= nextTabPos
- relativeX
;
6707 partialExtents
->Add(width
);
6713 if (!stringChunk
.IsEmpty())
6718 if (partialExtents
->GetCount() > 0)
6719 oldWidth
= (*partialExtents
)[partialExtents
->GetCount()-1];
6723 // Add these partial extents
6725 dc
.GetPartialTextExtents(stringChunk
, p
);
6727 for (j
= 0; j
< p
.GetCount(); j
++)
6728 partialExtents
->Add(oldWidth
+ p
[j
]);
6732 dc
.GetTextExtent(stringChunk
, & w
, & h
, & descent
);
6740 int charHeight
= dc
.GetCharHeight();
6741 if ((*partialExtents
).GetCount() > 0)
6742 w
= (*partialExtents
)[partialExtents
->GetCount()-1];
6745 size
= wxSize(w
, charHeight
);
6749 size
= wxSize(width
, dc
.GetCharHeight());
6753 dc
.GetTextExtent(wxT("X"), & w
, & h
, & descent
);
6761 /// Do a split, returning an object containing the second part, and setting
6762 /// the first part in 'this'.
6763 wxRichTextObject
* wxRichTextPlainText::DoSplit(long pos
)
6765 long index
= pos
- GetRange().GetStart();
6767 if (index
< 0 || index
>= (int) m_text
.length())
6770 wxString firstPart
= m_text
.Mid(0, index
);
6771 wxString secondPart
= m_text
.Mid(index
);
6775 wxRichTextPlainText
* newObject
= new wxRichTextPlainText(secondPart
);
6776 newObject
->SetAttributes(GetAttributes());
6777 newObject
->SetProperties(GetProperties());
6779 newObject
->SetRange(wxRichTextRange(pos
, GetRange().GetEnd()));
6780 GetRange().SetEnd(pos
-1);
6786 void wxRichTextPlainText::CalculateRange(long start
, long& end
)
6788 end
= start
+ m_text
.length() - 1;
6789 m_range
.SetRange(start
, end
);
6793 bool wxRichTextPlainText::DeleteRange(const wxRichTextRange
& range
)
6795 wxRichTextRange r
= range
;
6797 r
.LimitTo(GetRange());
6799 if (r
.GetStart() == GetRange().GetStart() && r
.GetEnd() == GetRange().GetEnd())
6805 long startIndex
= r
.GetStart() - GetRange().GetStart();
6806 long len
= r
.GetLength();
6808 m_text
= m_text
.Mid(0, startIndex
) + m_text
.Mid(startIndex
+len
);
6812 /// Get text for the given range.
6813 wxString
wxRichTextPlainText::GetTextForRange(const wxRichTextRange
& range
) const
6815 wxRichTextRange r
= range
;
6817 r
.LimitTo(GetRange());
6819 long startIndex
= r
.GetStart() - GetRange().GetStart();
6820 long len
= r
.GetLength();
6822 return m_text
.Mid(startIndex
, len
);
6825 /// Returns true if this object can merge itself with the given one.
6826 bool wxRichTextPlainText::CanMerge(wxRichTextObject
* object
) const
6828 return object
->GetClassInfo() == wxCLASSINFO(wxRichTextPlainText
) &&
6829 (m_text
.empty() || (wxTextAttrEq(GetAttributes(), object
->GetAttributes()) && m_properties
== object
->GetProperties()));
6832 /// Returns true if this object merged itself with the given one.
6833 /// The calling code will then delete the given object.
6834 bool wxRichTextPlainText::Merge(wxRichTextObject
* object
)
6836 wxRichTextPlainText
* textObject
= wxDynamicCast(object
, wxRichTextPlainText
);
6837 wxASSERT( textObject
!= NULL
);
6841 m_text
+= textObject
->GetText();
6842 wxRichTextApplyStyle(m_attributes
, textObject
->GetAttributes());
6849 /// Dump to output stream for debugging
6850 void wxRichTextPlainText::Dump(wxTextOutputStream
& stream
)
6852 wxRichTextObject::Dump(stream
);
6853 stream
<< m_text
<< wxT("\n");
6856 /// Get the first position from pos that has a line break character.
6857 long wxRichTextPlainText::GetFirstLineBreakPosition(long pos
)
6860 int len
= m_text
.length();
6861 int startPos
= pos
- m_range
.GetStart();
6862 for (i
= startPos
; i
< len
; i
++)
6864 wxChar ch
= m_text
[i
];
6865 if (ch
== wxRichTextLineBreakChar
)
6867 return i
+ m_range
.GetStart();
6875 * This is a kind of box, used to represent the whole buffer
6878 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBuffer
, wxRichTextParagraphLayoutBox
)
6880 wxList
wxRichTextBuffer::sm_handlers
;
6881 wxList
wxRichTextBuffer::sm_drawingHandlers
;
6882 wxRichTextFieldTypeHashMap
wxRichTextBuffer::sm_fieldTypes
;
6883 wxRichTextRenderer
* wxRichTextBuffer::sm_renderer
= NULL
;
6884 int wxRichTextBuffer::sm_bulletRightMargin
= 20;
6885 float wxRichTextBuffer::sm_bulletProportion
= (float) 0.3;
6888 void wxRichTextBuffer::Init()
6890 m_commandProcessor
= new wxCommandProcessor
;
6891 m_styleSheet
= NULL
;
6893 m_batchedCommandDepth
= 0;
6894 m_batchedCommand
= NULL
;
6898 m_dimensionScale
= 1.0;
6904 wxRichTextBuffer::~wxRichTextBuffer()
6906 delete m_commandProcessor
;
6907 delete m_batchedCommand
;
6910 ClearEventHandlers();
6913 void wxRichTextBuffer::ResetAndClearCommands()
6917 GetCommandProcessor()->ClearCommands();
6920 Invalidate(wxRICHTEXT_ALL
);
6923 void wxRichTextBuffer::Copy(const wxRichTextBuffer
& obj
)
6925 wxRichTextParagraphLayoutBox::Copy(obj
);
6927 m_styleSheet
= obj
.m_styleSheet
;
6928 m_modified
= obj
.m_modified
;
6929 m_batchedCommandDepth
= 0;
6930 if (m_batchedCommand
)
6931 delete m_batchedCommand
;
6932 m_batchedCommand
= NULL
;
6933 m_suppressUndo
= obj
.m_suppressUndo
;
6934 m_invalidRange
= obj
.m_invalidRange
;
6935 m_dimensionScale
= obj
.m_dimensionScale
;
6936 m_fontScale
= obj
.m_fontScale
;
6939 /// Push style sheet to top of stack
6940 bool wxRichTextBuffer::PushStyleSheet(wxRichTextStyleSheet
* styleSheet
)
6943 styleSheet
->InsertSheet(m_styleSheet
);
6945 SetStyleSheet(styleSheet
);
6950 /// Pop style sheet from top of stack
6951 wxRichTextStyleSheet
* wxRichTextBuffer::PopStyleSheet()
6955 wxRichTextStyleSheet
* oldSheet
= m_styleSheet
;
6956 m_styleSheet
= oldSheet
->GetNextSheet();
6965 /// Submit command to insert paragraphs
6966 bool wxRichTextBuffer::InsertParagraphsWithUndo(long pos
, const wxRichTextParagraphLayoutBox
& paragraphs
, wxRichTextCtrl
* ctrl
, int flags
)
6968 return ctrl
->GetFocusObject()->InsertParagraphsWithUndo(this, pos
, paragraphs
, ctrl
, flags
);
6971 /// Submit command to insert paragraphs
6972 bool wxRichTextParagraphLayoutBox::InsertParagraphsWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxRichTextParagraphLayoutBox
& paragraphs
, wxRichTextCtrl
* ctrl
, int WXUNUSED(flags
))
6974 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6976 action
->GetNewParagraphs() = paragraphs
;
6978 action
->SetPosition(pos
);
6980 wxRichTextRange range
= wxRichTextRange(pos
, pos
+ paragraphs
.GetOwnRange().GetEnd() - 1);
6981 if (!paragraphs
.GetPartialParagraph())
6982 range
.SetEnd(range
.GetEnd()+1);
6984 // Set the range we'll need to delete in Undo
6985 action
->SetRange(range
);
6987 buffer
->SubmitAction(action
);
6992 /// Submit command to insert the given text
6993 bool wxRichTextBuffer::InsertTextWithUndo(long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
, int flags
)
6995 return ctrl
->GetFocusObject()->InsertTextWithUndo(this, pos
, text
, ctrl
, flags
);
6998 /// Submit command to insert the given text
6999 bool wxRichTextParagraphLayoutBox::InsertTextWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
, int flags
)
7001 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7003 wxRichTextAttr
* p
= NULL
;
7004 wxRichTextAttr paraAttr
;
7005 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7007 // Get appropriate paragraph style
7008 paraAttr
= GetStyleForNewParagraph(buffer
, pos
, false, false);
7009 if (!paraAttr
.IsDefault())
7013 action
->GetNewParagraphs().AddParagraphs(text
, p
);
7015 int length
= action
->GetNewParagraphs().GetOwnRange().GetLength();
7017 if (!text
.empty() && text
.Last() != wxT('\n'))
7019 // Don't count the newline when undoing
7021 action
->GetNewParagraphs().SetPartialParagraph(true);
7023 else if (!text
.empty() && text
.Last() == wxT('\n'))
7026 action
->SetPosition(pos
);
7028 // Set the range we'll need to delete in Undo
7029 action
->SetRange(wxRichTextRange(pos
, pos
+ length
- 1));
7031 buffer
->SubmitAction(action
);
7036 /// Submit command to insert the given text
7037 bool wxRichTextBuffer::InsertNewlineWithUndo(long pos
, wxRichTextCtrl
* ctrl
, int flags
)
7039 return ctrl
->GetFocusObject()->InsertNewlineWithUndo(this, pos
, ctrl
, flags
);
7042 /// Submit command to insert the given text
7043 bool wxRichTextParagraphLayoutBox::InsertNewlineWithUndo(wxRichTextBuffer
* buffer
, long pos
, wxRichTextCtrl
* ctrl
, int flags
)
7045 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7047 wxRichTextAttr
* p
= NULL
;
7048 wxRichTextAttr paraAttr
;
7049 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7051 paraAttr
= GetStyleForNewParagraph(buffer
, pos
, false, true /* look for next paragraph style */);
7052 if (!paraAttr
.IsDefault())
7056 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7057 // Don't include box attributes such as margins
7058 attr
.GetTextBoxAttr().Reset();
7060 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(wxEmptyString
, this, & attr
);
7061 action
->GetNewParagraphs().AppendChild(newPara
);
7062 action
->GetNewParagraphs().UpdateRanges();
7063 action
->GetNewParagraphs().SetPartialParagraph(false);
7064 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
, false);
7068 newPara
->SetAttributes(*p
);
7070 if (flags
& wxRICHTEXT_INSERT_INTERACTIVE
)
7072 if (para
&& para
->GetRange().GetEnd() == pos
)
7075 // Now see if we need to number the paragraph.
7076 if (newPara
->GetAttributes().HasBulletNumber())
7078 wxRichTextAttr numberingAttr
;
7079 if (FindNextParagraphNumber(para
, numberingAttr
))
7080 wxRichTextApplyStyle(newPara
->GetAttributes(), (const wxRichTextAttr
&) numberingAttr
);
7084 action
->SetPosition(pos
);
7086 // Use the default character style
7087 if (!buffer
->GetDefaultStyle().IsDefault() && newPara
->GetChildren().GetFirst())
7089 // Check whether the default style merely reflects the paragraph/basic style,
7090 // in which case don't apply it.
7091 wxRichTextAttr
defaultStyle(buffer
->GetDefaultStyle());
7092 defaultStyle
.GetTextBoxAttr().Reset();
7093 wxRichTextAttr toApply
;
7096 wxRichTextAttr combinedAttr
= para
->GetCombinedAttributes(true /* include box attributes */);
7097 wxRichTextAttr newAttr
;
7098 // This filters out attributes that are accounted for by the current
7099 // paragraph/basic style
7100 wxRichTextApplyStyle(toApply
, defaultStyle
, & combinedAttr
);
7103 toApply
= defaultStyle
;
7105 if (!toApply
.IsDefault())
7106 newPara
->GetChildren().GetFirst()->GetData()->SetAttributes(toApply
);
7109 // Set the range we'll need to delete in Undo
7110 action
->SetRange(wxRichTextRange(pos1
, pos1
));
7112 buffer
->SubmitAction(action
);
7117 /// Submit command to insert the given image
7118 bool wxRichTextBuffer::InsertImageWithUndo(long pos
, const wxRichTextImageBlock
& imageBlock
, wxRichTextCtrl
* ctrl
, int flags
,
7119 const wxRichTextAttr
& textAttr
)
7121 return ctrl
->GetFocusObject()->InsertImageWithUndo(this, pos
, imageBlock
, ctrl
, flags
, textAttr
);
7124 /// Submit command to insert the given image
7125 bool wxRichTextParagraphLayoutBox::InsertImageWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxRichTextImageBlock
& imageBlock
,
7126 wxRichTextCtrl
* ctrl
, int flags
,
7127 const wxRichTextAttr
& textAttr
)
7129 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7131 wxRichTextAttr
* p
= NULL
;
7132 wxRichTextAttr paraAttr
;
7133 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7135 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
7136 if (!paraAttr
.IsDefault())
7140 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7142 // Don't include box attributes such as margins
7143 attr
.GetTextBoxAttr().Reset();
7145 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7147 newPara
->SetAttributes(*p
);
7149 wxRichTextImage
* imageObject
= new wxRichTextImage(imageBlock
, newPara
);
7150 newPara
->AppendChild(imageObject
);
7151 imageObject
->SetAttributes(textAttr
);
7152 action
->GetNewParagraphs().AppendChild(newPara
);
7153 action
->GetNewParagraphs().UpdateRanges();
7155 action
->GetNewParagraphs().SetPartialParagraph(true);
7157 action
->SetPosition(pos
);
7159 // Set the range we'll need to delete in Undo
7160 action
->SetRange(wxRichTextRange(pos
, pos
));
7162 buffer
->SubmitAction(action
);
7167 // Insert an object with no change of it
7168 wxRichTextObject
* wxRichTextBuffer::InsertObjectWithUndo(long pos
, wxRichTextObject
*object
, wxRichTextCtrl
* ctrl
, int flags
)
7170 return ctrl
->GetFocusObject()->InsertObjectWithUndo(this, pos
, object
, ctrl
, flags
);
7173 // Insert an object with no change of it
7174 wxRichTextObject
* wxRichTextParagraphLayoutBox::InsertObjectWithUndo(wxRichTextBuffer
* buffer
, long pos
, wxRichTextObject
*object
, wxRichTextCtrl
* ctrl
, int flags
)
7176 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Object"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7178 wxRichTextAttr
* p
= NULL
;
7179 wxRichTextAttr paraAttr
;
7180 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7182 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
7183 if (!paraAttr
.IsDefault())
7187 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7189 // Don't include box attributes such as margins
7190 attr
.GetTextBoxAttr().Reset();
7192 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7194 newPara
->SetAttributes(*p
);
7196 newPara
->AppendChild(object
);
7197 action
->GetNewParagraphs().AppendChild(newPara
);
7198 action
->GetNewParagraphs().UpdateRanges();
7200 action
->GetNewParagraphs().SetPartialParagraph(true);
7202 action
->SetPosition(pos
);
7204 // Set the range we'll need to delete in Undo
7205 action
->SetRange(wxRichTextRange(pos
, pos
));
7207 buffer
->SubmitAction(action
);
7209 wxRichTextObject
* obj
= GetLeafObjectAtPosition(pos
);
7213 wxRichTextField
* wxRichTextParagraphLayoutBox::InsertFieldWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxString
& fieldType
,
7214 const wxRichTextProperties
& properties
,
7215 wxRichTextCtrl
* ctrl
, int flags
,
7216 const wxRichTextAttr
& textAttr
)
7218 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Field"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7220 wxRichTextAttr
* p
= NULL
;
7221 wxRichTextAttr paraAttr
;
7222 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7224 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
7225 if (!paraAttr
.IsDefault())
7229 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7231 // Don't include box attributes such as margins
7232 attr
.GetTextBoxAttr().Reset();
7234 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7236 newPara
->SetAttributes(*p
);
7238 wxRichTextField
* fieldObject
= new wxRichTextField();
7239 fieldObject
->wxRichTextObject::SetProperties(properties
);
7240 fieldObject
->SetFieldType(fieldType
);
7241 fieldObject
->SetAttributes(textAttr
);
7242 newPara
->AppendChild(fieldObject
);
7243 action
->GetNewParagraphs().AppendChild(newPara
);
7244 action
->GetNewParagraphs().UpdateRanges();
7245 action
->GetNewParagraphs().SetPartialParagraph(true);
7246 action
->SetPosition(pos
);
7248 // Set the range we'll need to delete in Undo
7249 action
->SetRange(wxRichTextRange(pos
, pos
));
7251 buffer
->SubmitAction(action
);
7253 wxRichTextField
* obj
= wxDynamicCast(GetLeafObjectAtPosition(pos
), wxRichTextField
);
7257 /// Get the style that is appropriate for a new paragraph at this position.
7258 /// If the previous paragraph has a paragraph style name, look up the next-paragraph
7260 wxRichTextAttr
wxRichTextParagraphLayoutBox::GetStyleForNewParagraph(wxRichTextBuffer
* buffer
, long pos
, bool caretPosition
, bool lookUpNewParaStyle
) const
7262 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
, caretPosition
);
7265 wxRichTextAttr attr
;
7266 bool foundAttributes
= false;
7268 // Look for a matching paragraph style
7269 if (lookUpNewParaStyle
&& !para
->GetAttributes().GetParagraphStyleName().IsEmpty() && buffer
->GetStyleSheet())
7271 wxRichTextParagraphStyleDefinition
* paraDef
= buffer
->GetStyleSheet()->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
7274 // If we're not at the end of the paragraph, then we apply THIS style, and not the designated next style.
7275 if (para
->GetRange().GetEnd() == pos
&& !paraDef
->GetNextStyle().IsEmpty())
7277 wxRichTextParagraphStyleDefinition
* nextParaDef
= buffer
->GetStyleSheet()->FindParagraphStyle(paraDef
->GetNextStyle());
7280 foundAttributes
= true;
7281 attr
= nextParaDef
->GetStyleMergedWithBase(buffer
->GetStyleSheet());
7285 // If we didn't find the 'next style', use this style instead.
7286 if (!foundAttributes
)
7288 foundAttributes
= true;
7289 attr
= paraDef
->GetStyleMergedWithBase(buffer
->GetStyleSheet());
7294 // Also apply list style if present
7295 if (lookUpNewParaStyle
&& !para
->GetAttributes().GetListStyleName().IsEmpty() && buffer
->GetStyleSheet())
7297 wxRichTextListStyleDefinition
* listDef
= buffer
->GetStyleSheet()->FindListStyle(para
->GetAttributes().GetListStyleName());
7300 int thisIndent
= para
->GetAttributes().GetLeftIndent();
7301 int thisLevel
= para
->GetAttributes().HasOutlineLevel() ? para
->GetAttributes().GetOutlineLevel() : listDef
->FindLevelForIndent(thisIndent
);
7303 // Apply the overall list style, and item style for this level
7304 wxRichTextAttr
listStyle(listDef
->GetCombinedStyleForLevel(thisLevel
, buffer
->GetStyleSheet()));
7305 wxRichTextApplyStyle(attr
, listStyle
);
7306 attr
.SetOutlineLevel(thisLevel
);
7307 if (para
->GetAttributes().HasBulletNumber())
7308 attr
.SetBulletNumber(para
->GetAttributes().GetBulletNumber());
7312 if (!foundAttributes
)
7314 attr
= para
->GetAttributes();
7315 int flags
= attr
.GetFlags();
7317 // Eliminate character styles
7318 flags
&= ( (~ wxTEXT_ATTR_FONT
) |
7319 (~ wxTEXT_ATTR_TEXT_COLOUR
) |
7320 (~ wxTEXT_ATTR_BACKGROUND_COLOUR
) );
7321 attr
.SetFlags(flags
);
7327 return wxRichTextAttr();
7330 /// Submit command to delete this range
7331 bool wxRichTextBuffer::DeleteRangeWithUndo(const wxRichTextRange
& range
, wxRichTextCtrl
* ctrl
)
7333 return ctrl
->GetFocusObject()->DeleteRangeWithUndo(range
, ctrl
, this);
7336 /// Submit command to delete this range
7337 bool wxRichTextParagraphLayoutBox::DeleteRangeWithUndo(const wxRichTextRange
& range
, wxRichTextCtrl
* ctrl
, wxRichTextBuffer
* buffer
)
7339 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Delete"), wxRICHTEXT_DELETE
, buffer
, this, ctrl
);
7341 action
->SetPosition(ctrl
->GetCaretPosition());
7343 // Set the range to delete
7344 action
->SetRange(range
);
7346 // Copy the fragment that we'll need to restore in Undo
7347 CopyFragment(range
, action
->GetOldParagraphs());
7349 // See if we're deleting a paragraph marker, in which case we need to
7350 // make a note not to copy the attributes from the 2nd paragraph to the 1st.
7351 if (range
.GetStart() == range
.GetEnd())
7353 wxRichTextParagraph
* para
= GetParagraphAtPosition(range
.GetStart());
7354 if (para
&& para
->GetRange().GetEnd() == range
.GetEnd())
7356 wxRichTextParagraph
* nextPara
= GetParagraphAtPosition(range
.GetStart()+1);
7357 if (nextPara
&& nextPara
!= para
)
7359 action
->GetOldParagraphs().GetChildren().GetFirst()->GetData()->SetAttributes(nextPara
->GetAttributes());
7360 action
->GetOldParagraphs().GetAttributes().SetFlags(action
->GetOldParagraphs().GetAttributes().GetFlags() | wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
);
7365 buffer
->SubmitAction(action
);
7370 /// Collapse undo/redo commands
7371 bool wxRichTextBuffer::BeginBatchUndo(const wxString
& cmdName
)
7373 if (m_batchedCommandDepth
== 0)
7375 wxASSERT(m_batchedCommand
== NULL
);
7376 if (m_batchedCommand
)
7378 GetCommandProcessor()->Store(m_batchedCommand
);
7380 m_batchedCommand
= new wxRichTextCommand(cmdName
);
7383 m_batchedCommandDepth
++;
7388 /// Collapse undo/redo commands
7389 bool wxRichTextBuffer::EndBatchUndo()
7391 m_batchedCommandDepth
--;
7393 wxASSERT(m_batchedCommandDepth
>= 0);
7394 wxASSERT(m_batchedCommand
!= NULL
);
7396 if (m_batchedCommandDepth
== 0)
7398 GetCommandProcessor()->Store(m_batchedCommand
);
7399 m_batchedCommand
= NULL
;
7405 /// Submit immediately, or delay according to whether collapsing is on
7406 bool wxRichTextBuffer::SubmitAction(wxRichTextAction
* action
)
7408 if (action
&& !action
->GetNewParagraphs().IsEmpty())
7409 PrepareContent(action
->GetNewParagraphs());
7411 if (BatchingUndo() && m_batchedCommand
&& !SuppressingUndo())
7413 wxRichTextCommand
* cmd
= new wxRichTextCommand(action
->GetName());
7414 cmd
->AddAction(action
);
7416 cmd
->GetActions().Clear();
7419 m_batchedCommand
->AddAction(action
);
7423 wxRichTextCommand
* cmd
= new wxRichTextCommand(action
->GetName());
7424 cmd
->AddAction(action
);
7426 // Only store it if we're not suppressing undo.
7427 return GetCommandProcessor()->Submit(cmd
, !SuppressingUndo());
7433 /// Begin suppressing undo/redo commands.
7434 bool wxRichTextBuffer::BeginSuppressUndo()
7441 /// End suppressing undo/redo commands.
7442 bool wxRichTextBuffer::EndSuppressUndo()
7449 /// Begin using a style
7450 bool wxRichTextBuffer::BeginStyle(const wxRichTextAttr
& style
)
7452 wxRichTextAttr
newStyle(GetDefaultStyle());
7453 newStyle
.GetTextBoxAttr().Reset();
7455 // Save the old default style
7456 m_attributeStack
.Append((wxObject
*) new wxRichTextAttr(newStyle
));
7458 wxRichTextApplyStyle(newStyle
, style
);
7459 newStyle
.SetFlags(style
.GetFlags()|newStyle
.GetFlags());
7461 SetDefaultStyle(newStyle
);
7467 bool wxRichTextBuffer::EndStyle()
7469 if (!m_attributeStack
.GetFirst())
7471 wxLogDebug(_("Too many EndStyle calls!"));
7475 wxList::compatibility_iterator node
= m_attributeStack
.GetLast();
7476 wxRichTextAttr
* attr
= (wxRichTextAttr
*)node
->GetData();
7477 m_attributeStack
.Erase(node
);
7479 SetDefaultStyle(*attr
);
7486 bool wxRichTextBuffer::EndAllStyles()
7488 while (m_attributeStack
.GetCount() != 0)
7493 /// Clear the style stack
7494 void wxRichTextBuffer::ClearStyleStack()
7496 for (wxList::compatibility_iterator node
= m_attributeStack
.GetFirst(); node
; node
= node
->GetNext())
7497 delete (wxRichTextAttr
*) node
->GetData();
7498 m_attributeStack
.Clear();
7501 /// Begin using bold
7502 bool wxRichTextBuffer::BeginBold()
7504 wxRichTextAttr attr
;
7505 attr
.SetFontWeight(wxFONTWEIGHT_BOLD
);
7507 return BeginStyle(attr
);
7510 /// Begin using italic
7511 bool wxRichTextBuffer::BeginItalic()
7513 wxRichTextAttr attr
;
7514 attr
.SetFontStyle(wxFONTSTYLE_ITALIC
);
7516 return BeginStyle(attr
);
7519 /// Begin using underline
7520 bool wxRichTextBuffer::BeginUnderline()
7522 wxRichTextAttr attr
;
7523 attr
.SetFontUnderlined(true);
7525 return BeginStyle(attr
);
7528 /// Begin using point size
7529 bool wxRichTextBuffer::BeginFontSize(int pointSize
)
7531 wxRichTextAttr attr
;
7532 attr
.SetFontSize(pointSize
);
7534 return BeginStyle(attr
);
7537 /// Begin using this font
7538 bool wxRichTextBuffer::BeginFont(const wxFont
& font
)
7540 wxRichTextAttr attr
;
7543 return BeginStyle(attr
);
7546 /// Begin using this colour
7547 bool wxRichTextBuffer::BeginTextColour(const wxColour
& colour
)
7549 wxRichTextAttr attr
;
7550 attr
.SetFlags(wxTEXT_ATTR_TEXT_COLOUR
);
7551 attr
.SetTextColour(colour
);
7553 return BeginStyle(attr
);
7556 /// Begin using alignment
7557 bool wxRichTextBuffer::BeginAlignment(wxTextAttrAlignment alignment
)
7559 wxRichTextAttr attr
;
7560 attr
.SetFlags(wxTEXT_ATTR_ALIGNMENT
);
7561 attr
.SetAlignment(alignment
);
7563 return BeginStyle(attr
);
7566 /// Begin left indent
7567 bool wxRichTextBuffer::BeginLeftIndent(int leftIndent
, int leftSubIndent
)
7569 wxRichTextAttr attr
;
7570 attr
.SetFlags(wxTEXT_ATTR_LEFT_INDENT
);
7571 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7573 return BeginStyle(attr
);
7576 /// Begin right indent
7577 bool wxRichTextBuffer::BeginRightIndent(int rightIndent
)
7579 wxRichTextAttr attr
;
7580 attr
.SetFlags(wxTEXT_ATTR_RIGHT_INDENT
);
7581 attr
.SetRightIndent(rightIndent
);
7583 return BeginStyle(attr
);
7586 /// Begin paragraph spacing
7587 bool wxRichTextBuffer::BeginParagraphSpacing(int before
, int after
)
7591 flags
|= wxTEXT_ATTR_PARA_SPACING_BEFORE
;
7593 flags
|= wxTEXT_ATTR_PARA_SPACING_AFTER
;
7595 wxRichTextAttr attr
;
7596 attr
.SetFlags(flags
);
7597 attr
.SetParagraphSpacingBefore(before
);
7598 attr
.SetParagraphSpacingAfter(after
);
7600 return BeginStyle(attr
);
7603 /// Begin line spacing
7604 bool wxRichTextBuffer::BeginLineSpacing(int lineSpacing
)
7606 wxRichTextAttr attr
;
7607 attr
.SetFlags(wxTEXT_ATTR_LINE_SPACING
);
7608 attr
.SetLineSpacing(lineSpacing
);
7610 return BeginStyle(attr
);
7613 /// Begin numbered bullet
7614 bool wxRichTextBuffer::BeginNumberedBullet(int bulletNumber
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7616 wxRichTextAttr attr
;
7617 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7618 attr
.SetBulletStyle(bulletStyle
);
7619 attr
.SetBulletNumber(bulletNumber
);
7620 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7622 return BeginStyle(attr
);
7625 /// Begin symbol bullet
7626 bool wxRichTextBuffer::BeginSymbolBullet(const wxString
& symbol
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7628 wxRichTextAttr attr
;
7629 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7630 attr
.SetBulletStyle(bulletStyle
);
7631 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7632 attr
.SetBulletText(symbol
);
7634 return BeginStyle(attr
);
7637 /// Begin standard bullet
7638 bool wxRichTextBuffer::BeginStandardBullet(const wxString
& bulletName
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7640 wxRichTextAttr attr
;
7641 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7642 attr
.SetBulletStyle(bulletStyle
);
7643 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7644 attr
.SetBulletName(bulletName
);
7646 return BeginStyle(attr
);
7649 /// Begin named character style
7650 bool wxRichTextBuffer::BeginCharacterStyle(const wxString
& characterStyle
)
7652 if (GetStyleSheet())
7654 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->FindCharacterStyle(characterStyle
);
7657 wxRichTextAttr attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7658 return BeginStyle(attr
);
7664 /// Begin named paragraph style
7665 bool wxRichTextBuffer::BeginParagraphStyle(const wxString
& paragraphStyle
)
7667 if (GetStyleSheet())
7669 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(paragraphStyle
);
7672 wxRichTextAttr attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7673 return BeginStyle(attr
);
7679 /// Begin named list style
7680 bool wxRichTextBuffer::BeginListStyle(const wxString
& listStyle
, int level
, int number
)
7682 if (GetStyleSheet())
7684 wxRichTextListStyleDefinition
* def
= GetStyleSheet()->FindListStyle(listStyle
);
7687 wxRichTextAttr
attr(def
->GetCombinedStyleForLevel(level
));
7689 attr
.SetBulletNumber(number
);
7691 return BeginStyle(attr
);
7698 bool wxRichTextBuffer::BeginURL(const wxString
& url
, const wxString
& characterStyle
)
7700 wxRichTextAttr attr
;
7702 if (!characterStyle
.IsEmpty() && GetStyleSheet())
7704 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->FindCharacterStyle(characterStyle
);
7707 attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7712 return BeginStyle(attr
);
7715 /// Adds a handler to the end
7716 void wxRichTextBuffer::AddHandler(wxRichTextFileHandler
*handler
)
7718 sm_handlers
.Append(handler
);
7721 /// Inserts a handler at the front
7722 void wxRichTextBuffer::InsertHandler(wxRichTextFileHandler
*handler
)
7724 sm_handlers
.Insert( handler
);
7727 /// Removes a handler
7728 bool wxRichTextBuffer::RemoveHandler(const wxString
& name
)
7730 wxRichTextFileHandler
*handler
= FindHandler(name
);
7733 sm_handlers
.DeleteObject(handler
);
7741 /// Finds a handler by filename or, if supplied, type
7742 wxRichTextFileHandler
*wxRichTextBuffer::FindHandlerFilenameOrType(const wxString
& filename
,
7743 wxRichTextFileType imageType
)
7745 if (imageType
!= wxRICHTEXT_TYPE_ANY
)
7746 return FindHandler(imageType
);
7747 else if (!filename
.IsEmpty())
7749 wxString path
, file
, ext
;
7750 wxFileName::SplitPath(filename
, & path
, & file
, & ext
);
7751 return FindHandler(ext
, imageType
);
7758 /// Finds a handler by name
7759 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(const wxString
& name
)
7761 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7764 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7765 if (handler
->GetName().Lower() == name
.Lower()) return handler
;
7767 node
= node
->GetNext();
7772 /// Finds a handler by extension and type
7773 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(const wxString
& extension
, wxRichTextFileType type
)
7775 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7778 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7779 if ( handler
->GetExtension().Lower() == extension
.Lower() &&
7780 (type
== wxRICHTEXT_TYPE_ANY
|| handler
->GetType() == type
) )
7782 node
= node
->GetNext();
7787 /// Finds a handler by type
7788 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(wxRichTextFileType type
)
7790 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7793 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7794 if (handler
->GetType() == type
) return handler
;
7795 node
= node
->GetNext();
7800 void wxRichTextBuffer::InitStandardHandlers()
7802 if (!FindHandler(wxRICHTEXT_TYPE_TEXT
))
7803 AddHandler(new wxRichTextPlainTextHandler
);
7806 void wxRichTextBuffer::CleanUpHandlers()
7808 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7811 wxRichTextFileHandler
* handler
= (wxRichTextFileHandler
*)node
->GetData();
7812 wxList::compatibility_iterator next
= node
->GetNext();
7817 sm_handlers
.Clear();
7820 wxString
wxRichTextBuffer::GetExtWildcard(bool combine
, bool save
, wxArrayInt
* types
)
7827 wxList::compatibility_iterator node
= GetHandlers().GetFirst();
7831 wxRichTextFileHandler
* handler
= (wxRichTextFileHandler
*) node
->GetData();
7832 if (handler
->IsVisible() && ((save
&& handler
->CanSave()) || (!save
&& handler
->CanLoad())))
7837 wildcard
+= wxT(";");
7838 wildcard
+= wxT("*.") + handler
->GetExtension();
7843 wildcard
+= wxT("|");
7844 wildcard
+= handler
->GetName();
7845 wildcard
+= wxT(" ");
7846 wildcard
+= _("files");
7847 wildcard
+= wxT(" (*.");
7848 wildcard
+= handler
->GetExtension();
7849 wildcard
+= wxT(")|*.");
7850 wildcard
+= handler
->GetExtension();
7852 types
->Add(handler
->GetType());
7857 node
= node
->GetNext();
7861 wildcard
= wxT("(") + wildcard
+ wxT(")|") + wildcard
;
7866 bool wxRichTextBuffer::LoadFile(const wxString
& filename
, wxRichTextFileType type
)
7868 wxRichTextFileHandler
* handler
= FindHandlerFilenameOrType(filename
, type
);
7871 SetDefaultStyle(wxRichTextAttr());
7872 handler
->SetFlags(GetHandlerFlags());
7873 bool success
= handler
->LoadFile(this, filename
);
7874 Invalidate(wxRICHTEXT_ALL
);
7882 bool wxRichTextBuffer::SaveFile(const wxString
& filename
, wxRichTextFileType type
)
7884 wxRichTextFileHandler
* handler
= FindHandlerFilenameOrType(filename
, type
);
7887 handler
->SetFlags(GetHandlerFlags());
7888 return handler
->SaveFile(this, filename
);
7894 /// Load from a stream
7895 bool wxRichTextBuffer::LoadFile(wxInputStream
& stream
, wxRichTextFileType type
)
7897 wxRichTextFileHandler
* handler
= FindHandler(type
);
7900 SetDefaultStyle(wxRichTextAttr());
7901 handler
->SetFlags(GetHandlerFlags());
7902 bool success
= handler
->LoadFile(this, stream
);
7903 Invalidate(wxRICHTEXT_ALL
);
7910 /// Save to a stream
7911 bool wxRichTextBuffer::SaveFile(wxOutputStream
& stream
, wxRichTextFileType type
)
7913 wxRichTextFileHandler
* handler
= FindHandler(type
);
7916 handler
->SetFlags(GetHandlerFlags());
7917 return handler
->SaveFile(this, stream
);
7923 /// Copy the range to the clipboard
7924 bool wxRichTextBuffer::CopyToClipboard(const wxRichTextRange
& range
)
7926 bool success
= false;
7927 wxRichTextParagraphLayoutBox
* container
= this;
7928 if (GetRichTextCtrl())
7929 container
= GetRichTextCtrl()->GetFocusObject();
7931 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
7933 if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open())
7935 wxTheClipboard
->Clear();
7937 // Add composite object
7939 wxDataObjectComposite
* compositeObject
= new wxDataObjectComposite();
7942 wxString text
= container
->GetTextForRange(range
);
7945 text
= wxTextFile::Translate(text
, wxTextFileType_Dos
);
7948 compositeObject
->Add(new wxTextDataObject(text
), false /* not preferred */);
7951 // Add rich text buffer data object. This needs the XML handler to be present.
7953 if (FindHandler(wxRICHTEXT_TYPE_XML
))
7955 wxRichTextBuffer
* richTextBuf
= new wxRichTextBuffer
;
7956 container
->CopyFragment(range
, *richTextBuf
);
7958 compositeObject
->Add(new wxRichTextBufferDataObject(richTextBuf
), true /* preferred */);
7961 if (wxTheClipboard
->SetData(compositeObject
))
7964 wxTheClipboard
->Close();
7973 /// Paste the clipboard content to the buffer
7974 bool wxRichTextBuffer::PasteFromClipboard(long position
)
7976 bool success
= false;
7977 wxRichTextParagraphLayoutBox
* container
= this;
7978 if (GetRichTextCtrl())
7979 container
= GetRichTextCtrl()->GetFocusObject();
7981 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
7982 if (CanPasteFromClipboard())
7984 if (wxTheClipboard
->Open())
7986 if (wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())))
7988 wxRichTextBufferDataObject data
;
7989 wxTheClipboard
->GetData(data
);
7990 wxRichTextBuffer
* richTextBuffer
= data
.GetRichTextBuffer();
7993 container
->InsertParagraphsWithUndo(this, position
+1, *richTextBuffer
, GetRichTextCtrl(), 0);
7994 if (GetRichTextCtrl())
7995 GetRichTextCtrl()->ShowPosition(position
+ richTextBuffer
->GetOwnRange().GetEnd());
7996 delete richTextBuffer
;
7999 else if (wxTheClipboard
->IsSupported(wxDF_TEXT
)
8001 || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
)
8005 wxTextDataObject data
;
8006 wxTheClipboard
->GetData(data
);
8007 wxString
text(data
.GetText());
8010 text2
.Alloc(text
.Length()+1);
8012 for (i
= 0; i
< text
.Length(); i
++)
8014 wxChar ch
= text
[i
];
8015 if (ch
!= wxT('\r'))
8019 wxString text2
= text
;
8021 container
->InsertTextWithUndo(this, position
+1, text2
, GetRichTextCtrl(), wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
);
8023 if (GetRichTextCtrl())
8024 GetRichTextCtrl()->ShowPosition(position
+ text2
.Length());
8028 else if (wxTheClipboard
->IsSupported(wxDF_BITMAP
))
8030 wxBitmapDataObject data
;
8031 wxTheClipboard
->GetData(data
);
8032 wxBitmap
bitmap(data
.GetBitmap());
8033 wxImage
image(bitmap
.ConvertToImage());
8035 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, this, container
, GetRichTextCtrl(), false);
8037 action
->GetNewParagraphs().AddImage(image
);
8039 if (action
->GetNewParagraphs().GetChildCount() == 1)
8040 action
->GetNewParagraphs().SetPartialParagraph(true);
8042 action
->SetPosition(position
+1);
8044 // Set the range we'll need to delete in Undo
8045 action
->SetRange(wxRichTextRange(position
+1, position
+1));
8047 SubmitAction(action
);
8051 wxTheClipboard
->Close();
8055 wxUnusedVar(position
);
8060 /// Can we paste from the clipboard?
8061 bool wxRichTextBuffer::CanPasteFromClipboard() const
8063 bool canPaste
= false;
8064 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
8065 if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open())
8067 if (wxTheClipboard
->IsSupported(wxDF_TEXT
)
8069 || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
)
8071 || wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())) ||
8072 wxTheClipboard
->IsSupported(wxDF_BITMAP
))
8076 wxTheClipboard
->Close();
8082 /// Dumps contents of buffer for debugging purposes
8083 void wxRichTextBuffer::Dump()
8087 wxStringOutputStream
stream(& text
);
8088 wxTextOutputStream
textStream(stream
);
8095 /// Add an event handler
8096 bool wxRichTextBuffer::AddEventHandler(wxEvtHandler
* handler
)
8098 m_eventHandlers
.Append(handler
);
8102 /// Remove an event handler
8103 bool wxRichTextBuffer::RemoveEventHandler(wxEvtHandler
* handler
, bool deleteHandler
)
8105 wxList::compatibility_iterator node
= m_eventHandlers
.Find(handler
);
8108 m_eventHandlers
.Erase(node
);
8118 /// Clear event handlers
8119 void wxRichTextBuffer::ClearEventHandlers()
8121 m_eventHandlers
.Clear();
8124 /// Send event to event handlers. If sendToAll is true, will send to all event handlers,
8125 /// otherwise will stop at the first successful one.
8126 bool wxRichTextBuffer::SendEvent(wxEvent
& event
, bool sendToAll
)
8128 bool success
= false;
8129 for (wxList::compatibility_iterator node
= m_eventHandlers
.GetFirst(); node
; node
= node
->GetNext())
8131 wxEvtHandler
* handler
= (wxEvtHandler
*) node
->GetData();
8132 if (handler
->ProcessEvent(event
))
8142 /// Set style sheet and notify of the change
8143 bool wxRichTextBuffer::SetStyleSheetAndNotify(wxRichTextStyleSheet
* sheet
)
8145 wxRichTextStyleSheet
* oldSheet
= GetStyleSheet();
8147 wxWindowID winid
= wxID_ANY
;
8148 if (GetRichTextCtrl())
8149 winid
= GetRichTextCtrl()->GetId();
8151 wxRichTextEvent
event(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACING
, winid
);
8152 event
.SetEventObject(GetRichTextCtrl());
8153 event
.SetContainer(GetRichTextCtrl()->GetFocusObject());
8154 event
.SetOldStyleSheet(oldSheet
);
8155 event
.SetNewStyleSheet(sheet
);
8158 if (SendEvent(event
) && !event
.IsAllowed())
8160 if (sheet
!= oldSheet
)
8166 if (oldSheet
&& oldSheet
!= sheet
)
8169 SetStyleSheet(sheet
);
8171 event
.SetEventType(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACED
);
8172 event
.SetOldStyleSheet(NULL
);
8175 return SendEvent(event
);
8178 /// Set renderer, deleting old one
8179 void wxRichTextBuffer::SetRenderer(wxRichTextRenderer
* renderer
)
8183 sm_renderer
= renderer
;
8186 /// Hit-testing: returns a flag indicating hit test details, plus
8187 /// information about position
8188 int wxRichTextBuffer::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
8190 int ret
= wxRichTextParagraphLayoutBox::HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
8191 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
8197 textPosition
= m_ownRange
.GetEnd()-1;
8200 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
;
8204 void wxRichTextBuffer::SetFontScale(double fontScale
)
8206 m_fontScale
= fontScale
;
8207 m_fontTable
.SetFontScale(fontScale
);
8210 void wxRichTextBuffer::SetDimensionScale(double dimScale
)
8212 m_dimensionScale
= dimScale
;
8215 bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxRichTextAttr
& bulletAttr
, const wxRect
& rect
)
8217 if (bulletAttr
.GetTextColour().IsOk())
8219 wxCheckSetPen(dc
, wxPen(bulletAttr
.GetTextColour()));
8220 wxCheckSetBrush(dc
, wxBrush(bulletAttr
.GetTextColour()));
8224 wxCheckSetPen(dc
, *wxBLACK_PEN
);
8225 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
8229 if (bulletAttr
.HasFont())
8231 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(bulletAttr
);
8234 font
= (*wxNORMAL_FONT
);
8236 wxCheckSetFont(dc
, font
);
8238 int charHeight
= dc
.GetCharHeight();
8240 int bulletWidth
= (int) (((float) charHeight
) * wxRichTextBuffer::GetBulletProportion());
8241 int bulletHeight
= bulletWidth
;
8245 // Calculate the top position of the character (as opposed to the whole line height)
8246 int y
= rect
.y
+ (rect
.height
- charHeight
);
8248 // Calculate where the bullet should be positioned
8249 y
= y
+ (charHeight
+1)/2 - (bulletHeight
+1)/2;
8251 // The margin between a bullet and text.
8252 int margin
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer::GetBulletRightMargin());
8254 if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
)
8255 x
= rect
.x
+ rect
.width
- bulletWidth
- margin
;
8256 else if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
)
8257 x
= x
+ (rect
.width
)/2 - bulletWidth
/2;
8259 if (bulletAttr
.GetBulletName() == wxT("standard/square"))
8261 dc
.DrawRectangle(x
, y
, bulletWidth
, bulletHeight
);
8263 else if (bulletAttr
.GetBulletName() == wxT("standard/diamond"))
8266 pts
[0].x
= x
; pts
[0].y
= y
+ bulletHeight
/2;
8267 pts
[1].x
= x
+ bulletWidth
/2; pts
[1].y
= y
;
8268 pts
[2].x
= x
+ bulletWidth
; pts
[2].y
= y
+ bulletHeight
/2;
8269 pts
[3].x
= x
+ bulletWidth
/2; pts
[3].y
= y
+ bulletHeight
;
8271 dc
.DrawPolygon(4, pts
);
8273 else if (bulletAttr
.GetBulletName() == wxT("standard/triangle"))
8276 pts
[0].x
= x
; pts
[0].y
= y
;
8277 pts
[1].x
= x
+ bulletWidth
; pts
[1].y
= y
+ bulletHeight
/2;
8278 pts
[2].x
= x
; pts
[2].y
= y
+ bulletHeight
;
8280 dc
.DrawPolygon(3, pts
);
8282 else if (bulletAttr
.GetBulletName() == wxT("standard/circle-outline"))
8284 wxCheckSetBrush(dc
, *wxTRANSPARENT_BRUSH
);
8285 dc
.DrawEllipse(x
, y
, bulletWidth
, bulletHeight
);
8287 else // "standard/circle", and catch-all
8289 dc
.DrawEllipse(x
, y
, bulletWidth
, bulletHeight
);
8295 bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxRichTextAttr
& attr
, const wxRect
& rect
, const wxString
& text
)
8300 if ((attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
) && !attr
.GetBulletFont().IsEmpty() && attr
.HasFont())
8302 wxRichTextAttr fontAttr
;
8303 if (attr
.HasFontPixelSize())
8304 fontAttr
.SetFontPixelSize(attr
.GetFontSize());
8306 fontAttr
.SetFontPointSize(attr
.GetFontSize());
8307 fontAttr
.SetFontStyle(attr
.GetFontStyle());
8308 fontAttr
.SetFontWeight(attr
.GetFontWeight());
8309 fontAttr
.SetFontUnderlined(attr
.GetFontUnderlined());
8310 fontAttr
.SetFontFaceName(attr
.GetBulletFont());
8311 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(fontAttr
);
8313 else if (attr
.HasFont())
8314 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(attr
);
8316 font
= (*wxNORMAL_FONT
);
8318 wxCheckSetFont(dc
, font
);
8320 if (attr
.GetTextColour().IsOk())
8321 dc
.SetTextForeground(attr
.GetTextColour());
8323 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
8325 int charHeight
= dc
.GetCharHeight();
8327 dc
.GetTextExtent(text
, & tw
, & th
);
8331 // Calculate the top position of the character (as opposed to the whole line height)
8332 int y
= rect
.y
+ (rect
.height
- charHeight
);
8334 // The margin between a bullet and text.
8335 int margin
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer::GetBulletRightMargin());
8337 if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
)
8338 x
= (rect
.x
+ rect
.width
) - tw
- margin
;
8339 else if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
)
8340 x
= x
+ (rect
.width
)/2 - tw
/2;
8342 dc
.DrawText(text
, x
, y
);
8350 bool wxRichTextStdRenderer::DrawBitmapBullet(wxRichTextParagraph
* WXUNUSED(paragraph
), wxDC
& WXUNUSED(dc
), const wxRichTextAttr
& WXUNUSED(attr
), const wxRect
& WXUNUSED(rect
))
8352 // Currently unimplemented. The intention is to store bitmaps by name in a media store associated
8353 // with the buffer. The store will allow retrieval from memory, disk or other means.
8357 /// Enumerate the standard bullet names currently supported
8358 bool wxRichTextStdRenderer::EnumerateStandardBulletNames(wxArrayString
& bulletNames
)
8360 bulletNames
.Add(wxTRANSLATE("standard/circle"));
8361 bulletNames
.Add(wxTRANSLATE("standard/circle-outline"));
8362 bulletNames
.Add(wxTRANSLATE("standard/square"));
8363 bulletNames
.Add(wxTRANSLATE("standard/diamond"));
8364 bulletNames
.Add(wxTRANSLATE("standard/triangle"));
8373 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBox
, wxRichTextParagraphLayoutBox
)
8375 wxRichTextBox::wxRichTextBox(wxRichTextObject
* parent
):
8376 wxRichTextParagraphLayoutBox(parent
)
8381 bool wxRichTextBox::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8386 // TODO: if the active object in the control, draw an indication.
8387 // We need to add the concept of active object, and not just focus object,
8388 // so we can apply commands (properties, delete, ...) to objects such as text boxes and images.
8389 // Ultimately we would like to be able to interactively resize an active object
8390 // using drag handles.
8391 return wxRichTextParagraphLayoutBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8395 void wxRichTextBox::Copy(const wxRichTextBox
& obj
)
8397 wxRichTextParagraphLayoutBox::Copy(obj
);
8400 // Edit properties via a GUI
8401 bool wxRichTextBox::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8403 wxRichTextObjectPropertiesDialog
boxDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Box Properties"));
8404 boxDlg
.SetAttributes(GetAttributes());
8406 if (boxDlg
.ShowModal() == wxID_OK
)
8408 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
8409 // indeterminate in the object.
8410 boxDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
8421 IMPLEMENT_DYNAMIC_CLASS(wxRichTextField
, wxRichTextParagraphLayoutBox
)
8423 wxRichTextField::wxRichTextField(const wxString
& fieldType
, wxRichTextObject
* parent
):
8424 wxRichTextParagraphLayoutBox(parent
)
8426 SetFieldType(fieldType
);
8430 bool wxRichTextField::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8435 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8436 if (fieldType
&& fieldType
->Draw(this, dc
, context
, range
, selection
, rect
, descent
, style
))
8439 // Fallback; but don't draw guidelines.
8440 style
&= ~wxRICHTEXT_DRAW_GUIDELINES
;
8441 return wxRichTextParagraphLayoutBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8444 bool wxRichTextField::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
)
8446 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8447 if (fieldType
&& fieldType
->Layout(this, dc
, context
, rect
, parentRect
, style
))
8451 return wxRichTextParagraphLayoutBox::Layout(dc
, context
, rect
, parentRect
, style
);
8454 bool wxRichTextField::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
8456 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8458 return fieldType
->GetRangeSize((wxRichTextField
*) this, range
, size
, descent
, dc
, context
, flags
, position
, partialExtents
);
8460 return wxRichTextParagraphLayoutBox::GetRangeSize(range
, size
, descent
, dc
, context
, flags
, position
, partialExtents
);
8464 void wxRichTextField::CalculateRange(long start
, long& end
)
8467 wxRichTextParagraphLayoutBox::CalculateRange(start
, end
);
8469 wxRichTextObject::CalculateRange(start
, end
);
8473 void wxRichTextField::Copy(const wxRichTextField
& obj
)
8475 wxRichTextParagraphLayoutBox::Copy(obj
);
8477 UpdateField(GetBuffer());
8480 // Edit properties via a GUI
8481 bool wxRichTextField::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8483 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8485 return fieldType
->EditProperties(this, parent
, buffer
);
8490 bool wxRichTextField::CanEditProperties() const
8492 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8494 return fieldType
->CanEditProperties((wxRichTextField
*) this);
8499 wxString
wxRichTextField::GetPropertiesMenuLabel() const
8501 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8503 return fieldType
->GetPropertiesMenuLabel((wxRichTextField
*) this);
8505 return wxEmptyString
;
8508 bool wxRichTextField::UpdateField(wxRichTextBuffer
* buffer
)
8510 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8512 return fieldType
->UpdateField(buffer
, (wxRichTextField
*) this);
8517 bool wxRichTextField::IsTopLevel() const
8519 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8521 return fieldType
->IsTopLevel((wxRichTextField
*) this);
8526 IMPLEMENT_CLASS(wxRichTextFieldType
, wxObject
)
8528 IMPLEMENT_CLASS(wxRichTextFieldTypeStandard
, wxRichTextFieldType
)
8530 wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString
& name
, const wxString
& label
, int displayStyle
)
8536 SetDisplayStyle(displayStyle
);
8539 wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString
& name
, const wxBitmap
& bitmap
, int displayStyle
)
8545 SetDisplayStyle(displayStyle
);
8548 void wxRichTextFieldTypeStandard::Init()
8550 m_displayStyle
= wxRICHTEXT_FIELD_STYLE_RECTANGLE
;
8551 m_font
= wxFont(6, wxFONTFAMILY_SWISS
, wxFONTSTYLE_NORMAL
, wxFONTWEIGHT_NORMAL
);
8552 m_textColour
= *wxWHITE
;
8553 m_borderColour
= *wxBLACK
;
8554 m_backgroundColour
= *wxBLACK
;
8555 m_verticalPadding
= 1;
8556 m_horizontalPadding
= 3;
8557 m_horizontalMargin
= 2;
8558 m_verticalMargin
= 0;
8561 void wxRichTextFieldTypeStandard::Copy(const wxRichTextFieldTypeStandard
& field
)
8563 wxRichTextFieldType::Copy(field
);
8565 m_label
= field
.m_label
;
8566 m_displayStyle
= field
.m_displayStyle
;
8567 m_font
= field
.m_font
;
8568 m_textColour
= field
.m_textColour
;
8569 m_borderColour
= field
.m_borderColour
;
8570 m_backgroundColour
= field
.m_backgroundColour
;
8571 m_verticalPadding
= field
.m_verticalPadding
;
8572 m_horizontalPadding
= field
.m_horizontalPadding
;
8573 m_horizontalMargin
= field
.m_horizontalMargin
;
8574 m_bitmap
= field
.m_bitmap
;
8577 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
))
8579 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_COMPOSITE
)
8580 return false; // USe default composite drawing
8581 else // if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_RECTANGLE || m_displayStyle == wxRICHTEXT_FIELD_STYLE_NOBORDER)
8585 wxPen
borderPen(m_borderColour
, 1, wxSOLID
);
8586 wxBrush
backgroundBrush(m_backgroundColour
);
8587 wxColour
textColour(m_textColour
);
8589 if (selection
.WithinSelection(obj
->GetRange().GetStart(), obj
))
8591 wxColour
highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
));
8592 wxColour
highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
));
8594 borderPen
= wxPen(highlightTextColour
, 1, wxSOLID
);
8595 backgroundBrush
= wxBrush(highlightColour
);
8597 wxCheckSetBrush(dc
, backgroundBrush
);
8598 wxCheckSetPen(dc
, wxPen(highlightColour
, 1, wxSOLID
));
8599 dc
.DrawRectangle(rect
);
8602 if (m_displayStyle
!= wxRICHTEXT_FIELD_STYLE_NO_BORDER
)
8605 // objectRect is the area where the content is drawn, after margins around it have been taken into account
8606 wxRect objectRect
= wxRect(wxPoint(rect
.x
+ m_horizontalMargin
, rect
.y
+ wxMax(0, rect
.height
- descent
- obj
->GetCachedSize().y
)),
8607 wxSize(obj
->GetCachedSize().x
- 2*m_horizontalMargin
- borderSize
, obj
->GetCachedSize().y
));
8609 // clientArea is where the text is actually written
8610 wxRect clientArea
= objectRect
;
8612 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_RECTANGLE
)
8614 dc
.SetPen(borderPen
);
8615 dc
.SetBrush(backgroundBrush
);
8616 dc
.DrawRoundedRectangle(objectRect
, 4.0);
8618 else if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_START_TAG
)
8620 int arrowLength
= objectRect
.height
/2;
8621 clientArea
.width
-= (arrowLength
- m_horizontalPadding
);
8624 pts
[0].x
= objectRect
.x
; pts
[0].y
= objectRect
.y
;
8625 pts
[1].x
= objectRect
.x
+ objectRect
.width
- arrowLength
; pts
[1].y
= objectRect
.y
;
8626 pts
[2].x
= objectRect
.x
+ objectRect
.width
; pts
[2].y
= objectRect
.y
+ (objectRect
.height
/2);
8627 pts
[3].x
= objectRect
.x
+ objectRect
.width
- arrowLength
; pts
[3].y
= objectRect
.y
+ objectRect
.height
;
8628 pts
[4].x
= objectRect
.x
; pts
[4].y
= objectRect
.y
+ objectRect
.height
;
8629 dc
.SetPen(borderPen
);
8630 dc
.SetBrush(backgroundBrush
);
8631 dc
.DrawPolygon(5, pts
);
8633 else if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_END_TAG
)
8635 int arrowLength
= objectRect
.height
/2;
8636 clientArea
.width
-= (arrowLength
- m_horizontalPadding
);
8637 clientArea
.x
+= (arrowLength
- m_horizontalPadding
);
8640 pts
[0].x
= objectRect
.x
+ objectRect
.width
; pts
[0].y
= objectRect
.y
;
8641 pts
[1].x
= objectRect
.x
+ arrowLength
; pts
[1].y
= objectRect
.y
;
8642 pts
[2].x
= objectRect
.x
; pts
[2].y
= objectRect
.y
+ (objectRect
.height
/2);
8643 pts
[3].x
= objectRect
.x
+ arrowLength
; pts
[3].y
= objectRect
.y
+ objectRect
.height
;
8644 pts
[4].x
= objectRect
.x
+ objectRect
.width
; pts
[4].y
= objectRect
.y
+ objectRect
.height
;
8645 dc
.SetPen(borderPen
);
8646 dc
.SetBrush(backgroundBrush
);
8647 dc
.DrawPolygon(5, pts
);
8650 if (m_bitmap
.IsOk())
8652 int x
= clientArea
.x
+ (clientArea
.width
- m_bitmap
.GetWidth())/2;
8653 int y
= clientArea
.y
+ m_verticalPadding
;
8654 dc
.DrawBitmap(m_bitmap
, x
, y
, true);
8656 if (selection
.WithinSelection(obj
->GetRange().GetStart(), obj
))
8658 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
8659 wxCheckSetPen(dc
, *wxBLACK_PEN
);
8660 dc
.SetLogicalFunction(wxINVERT
);
8661 dc
.DrawRectangle(wxRect(x
, y
, m_bitmap
.GetWidth(), m_bitmap
.GetHeight()));
8662 dc
.SetLogicalFunction(wxCOPY
);
8667 wxString
label(m_label
);
8668 if (label
.IsEmpty())
8670 int w
, h
, maxDescent
;
8672 dc
.GetTextExtent(m_label
, & w
, &h
, & maxDescent
);
8673 dc
.SetTextForeground(textColour
);
8675 int x
= clientArea
.x
+ (clientArea
.width
- w
)/2;
8676 int y
= clientArea
.y
+ (clientArea
.height
- (h
- maxDescent
))/2;
8677 dc
.DrawText(m_label
, x
, y
);
8684 bool wxRichTextFieldTypeStandard::Layout(wxRichTextField
* obj
, wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& WXUNUSED(rect
), const wxRect
& WXUNUSED(parentRect
), int style
)
8686 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_COMPOSITE
)
8687 return false; // USe default composite layout
8689 wxSize size
= GetSize(obj
, dc
, context
, style
);
8690 obj
->SetCachedSize(size
);
8691 obj
->SetMinSize(size
);
8692 obj
->SetMaxSize(size
);
8696 bool wxRichTextFieldTypeStandard::GetRangeSize(wxRichTextField
* obj
, const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
8698 if (IsTopLevel(obj
))
8699 return obj
->wxRichTextParagraphLayoutBox::GetRangeSize(range
, size
, descent
, dc
, context
, flags
, position
);
8702 wxSize sz
= GetSize(obj
, dc
, context
, 0);
8706 if (partialExtents
->GetCount() > 0)
8707 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
8710 partialExtents
->Add(lastSize
+ sz
.x
);
8717 wxSize
wxRichTextFieldTypeStandard::GetSize(wxRichTextField
* WXUNUSED(obj
), wxDC
& dc
, wxRichTextDrawingContext
& WXUNUSED(context
), int WXUNUSED(style
)) const
8720 int w
= 0, h
= 0, maxDescent
= 0;
8723 if (m_bitmap
.IsOk())
8725 w
= m_bitmap
.GetWidth();
8726 h
= m_bitmap
.GetHeight();
8728 sz
= wxSize(w
+ m_horizontalMargin
*2, h
+ m_verticalMargin
*2);
8732 wxString
label(m_label
);
8733 if (label
.IsEmpty())
8736 dc
.GetTextExtent(label
, & w
, &h
, & maxDescent
);
8738 sz
= wxSize(w
+ m_horizontalPadding
*2 + m_horizontalMargin
*2, h
+ m_verticalPadding
*2 + m_verticalMargin
*2);
8741 if (m_displayStyle
!= wxRICHTEXT_FIELD_STYLE_NO_BORDER
)
8743 sz
.x
+= borderSize
*2;
8744 sz
.y
+= borderSize
*2;
8747 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_START_TAG
|| m_displayStyle
== wxRICHTEXT_FIELD_STYLE_END_TAG
)
8749 // Add space for the arrow
8750 sz
.x
+= (sz
.y
/2 - m_horizontalPadding
);
8756 IMPLEMENT_DYNAMIC_CLASS(wxRichTextCell
, wxRichTextBox
)
8758 wxRichTextCell::wxRichTextCell(wxRichTextObject
* parent
):
8759 wxRichTextBox(parent
)
8764 bool wxRichTextCell::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8766 return wxRichTextBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8770 void wxRichTextCell::Copy(const wxRichTextCell
& obj
)
8772 wxRichTextBox::Copy(obj
);
8775 // Edit properties via a GUI
8776 bool wxRichTextCell::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8778 // We need to gather common attributes for all selected cells.
8780 wxRichTextTable
* table
= wxDynamicCast(GetParent(), wxRichTextTable
);
8781 bool multipleCells
= false;
8782 wxRichTextAttr attr
;
8784 if (table
&& buffer
&& buffer
->GetRichTextCtrl() && buffer
->GetRichTextCtrl()->GetSelection().IsValid() &&
8785 buffer
->GetRichTextCtrl()->GetSelection().GetContainer() == GetParent())
8787 wxRichTextAttr clashingAttr
, absentAttr
;
8788 const wxRichTextSelection
& sel
= buffer
->GetRichTextCtrl()->GetSelection();
8790 int selectedCellCount
= 0;
8791 for (i
= 0; i
< sel
.GetCount(); i
++)
8793 const wxRichTextRange
& range
= sel
[i
];
8794 wxRichTextCell
* cell
= table
->GetCell(range
.GetStart());
8797 wxRichTextAttr cellStyle
= cell
->GetAttributes();
8799 CollectStyle(attr
, cellStyle
, clashingAttr
, absentAttr
);
8801 selectedCellCount
++;
8804 multipleCells
= selectedCellCount
> 1;
8808 attr
= GetAttributes();
8813 caption
= _("Multiple Cell Properties");
8815 caption
= _("Cell Properties");
8817 wxRichTextObjectPropertiesDialog
cellDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, caption
);
8818 cellDlg
.SetAttributes(attr
);
8820 wxRichTextSizePage
* sizePage
= wxDynamicCast(cellDlg
.FindPage(wxCLASSINFO(wxRichTextSizePage
)), wxRichTextSizePage
);
8823 // We don't want position and floating controls for a cell.
8824 sizePage
->ShowPositionControls(false);
8825 sizePage
->ShowFloatingControls(false);
8828 if (cellDlg
.ShowModal() == wxID_OK
)
8832 const wxRichTextSelection
& sel
= buffer
->GetRichTextCtrl()->GetSelection();
8833 // Apply the style; we interpret indeterminate attributes as 'don't touch this attribute'
8834 // since it may represent clashing attributes across multiple objects.
8835 table
->SetCellStyle(sel
, attr
);
8838 // For a single object, indeterminate attributes set by the user should be reflected in the
8839 // actual object style, so pass the wxRICHTEXT_SETSTYLE_RESET flag to assign
8840 // the style directly instead of applying (which ignores indeterminate attributes,
8841 // leaving them as they were).
8842 cellDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
8849 WX_DEFINE_OBJARRAY(wxRichTextObjectPtrArrayArray
)
8851 IMPLEMENT_DYNAMIC_CLASS(wxRichTextTable
, wxRichTextBox
)
8853 wxRichTextTable::wxRichTextTable(wxRichTextObject
* parent
): wxRichTextBox(parent
)
8859 // Draws the object.
8860 bool wxRichTextTable::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8862 return wxRichTextBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8865 WX_DECLARE_OBJARRAY(wxRect
, wxRichTextRectArray
);
8866 WX_DEFINE_OBJARRAY(wxRichTextRectArray
);
8868 // Lays the object out. rect is the space available for layout. Often it will
8869 // be the specified overall space for this object, if trying to constrain
8870 // layout to a particular size, or it could be the total space available in the
8871 // parent. rect is the overall size, so we must subtract margins and padding.
8872 // to get the actual available space.
8873 bool wxRichTextTable::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& WXUNUSED(parentRect
), int style
)
8875 SetPosition(rect
.GetPosition());
8877 // TODO: the meaty bit. Calculate sizes of all cells and rows. Try to use
8878 // minimum size if within alloted size, then divide up remaining size
8879 // between rows/cols.
8882 wxRichTextBuffer
* buffer
= GetBuffer();
8883 if (buffer
) scale
= buffer
->GetScale();
8885 wxRect availableSpace
= GetAvailableContentArea(dc
, context
, rect
);
8886 wxTextAttrDimensionConverter
converter(dc
, scale
, availableSpace
.GetSize());
8888 wxRichTextAttr
attr(GetAttributes());
8889 context
.ApplyVirtualAttributes(attr
, this);
8891 // If we have no fixed table size, and assuming we're not pushed for
8892 // space, then we don't have to try to stretch the table to fit the contents.
8893 bool stretchToFitTableWidth
= false;
8895 int tableWidth
= rect
.width
;
8896 if (attr
.GetTextBoxAttr().GetWidth().IsValid())
8898 tableWidth
= converter
.GetPixels(attr
.GetTextBoxAttr().GetWidth());
8900 // Fixed table width, so we do want to stretch columns out if necessary.
8901 stretchToFitTableWidth
= true;
8903 // Shouldn't be able to exceed the size passed to this function
8904 tableWidth
= wxMin(rect
.width
, tableWidth
);
8907 // Get internal padding
8908 int paddingLeft
= 0, paddingTop
= 0;
8909 if (attr
.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
8910 paddingLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetLeft());
8911 if (attr
.GetTextBoxAttr().GetPadding().GetTop().IsValid())
8912 paddingTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetTop());
8914 // Assume that left and top padding are also used for inter-cell padding.
8915 int paddingX
= paddingLeft
;
8916 int paddingY
= paddingTop
;
8918 int totalLeftMargin
= 0, totalRightMargin
= 0, totalTopMargin
= 0, totalBottomMargin
= 0;
8919 GetTotalMargin(dc
, buffer
, attr
, totalLeftMargin
, totalRightMargin
, totalTopMargin
, totalBottomMargin
);
8921 // Internal table width - the area for content
8922 int internalTableWidth
= tableWidth
- totalLeftMargin
- totalRightMargin
;
8924 int rowCount
= m_cells
.GetCount();
8925 if (m_colCount
== 0 || rowCount
== 0)
8927 wxRect
overallRect(rect
.x
, rect
.y
, totalLeftMargin
+ totalRightMargin
, totalTopMargin
+ totalBottomMargin
);
8928 SetCachedSize(overallRect
.GetSize());
8930 // Zero content size
8931 SetMinSize(overallRect
.GetSize());
8932 SetMaxSize(GetMinSize());
8936 // The final calculated widths
8937 wxArrayInt colWidths
;
8938 colWidths
.Add(0, m_colCount
);
8940 wxArrayInt absoluteColWidths
;
8941 absoluteColWidths
.Add(0, m_colCount
);
8943 wxArrayInt percentageColWidths
;
8944 percentageColWidths
.Add(0, m_colCount
);
8945 // wxArrayInt percentageColWidthsSpanning(m_colCount);
8946 // These are only relevant when the first column contains spanning information.
8947 // wxArrayInt columnSpans(m_colCount); // Each contains 1 for non-spanning cell, > 1 for spanning cell.
8948 wxArrayInt maxColWidths
;
8949 maxColWidths
.Add(0, m_colCount
);
8950 wxArrayInt minColWidths
;
8951 minColWidths
.Add(0, m_colCount
);
8953 wxSize
tableSize(tableWidth
, 0);
8957 for (i
= 0; i
< m_colCount
; i
++)
8959 absoluteColWidths
[i
] = 0;
8960 // absoluteColWidthsSpanning[i] = 0;
8961 percentageColWidths
[i
] = -1;
8962 // percentageColWidthsSpanning[i] = -1;
8964 maxColWidths
[i
] = 0;
8965 minColWidths
[i
] = 0;
8966 // columnSpans[i] = 1;
8969 // (0) Determine which cells are visible according to spans
8971 // __________________
8976 // |------------------|
8977 // |__________________| 4
8979 // To calculate cell visibility:
8980 // First find all spanning cells. Build an array of span records with start x, y and end x, y.
8981 // Then for each cell, test whether we're within one of those cells, and unless we're at the start of
8982 // that cell, hide the cell.
8984 // We can also use this array to match the size of spanning cells to the grid. Or just do
8985 // this when we iterate through all cells.
8987 // 0.1: add spanning cells to an array
8988 wxRichTextRectArray rectArray
;
8989 for (j
= 0; j
< m_rowCount
; j
++)
8991 for (i
= 0; i
< m_colCount
; i
++)
8993 wxRichTextBox
* cell
= GetCell(j
, i
);
8994 int colSpan
= 1, rowSpan
= 1;
8995 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8996 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8997 if (cell
->GetProperties().HasProperty(wxT("rowspan")))
8998 rowSpan
= cell
->GetProperties().GetPropertyLong(wxT("rowspan"));
8999 if (colSpan
> 1 || rowSpan
> 1)
9001 rectArray
.Add(wxRect(i
, j
, colSpan
, rowSpan
));
9005 // 0.2: find which cells are subsumed by a spanning cell
9006 for (j
= 0; j
< m_rowCount
; j
++)
9008 for (i
= 0; i
< m_colCount
; i
++)
9010 wxRichTextBox
* cell
= GetCell(j
, i
);
9011 if (rectArray
.GetCount() == 0)
9017 int colSpan
= 1, rowSpan
= 1;
9018 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9019 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9020 if (cell
->GetProperties().HasProperty(wxT("rowspan")))
9021 rowSpan
= cell
->GetProperties().GetPropertyLong(wxT("rowspan"));
9022 if (colSpan
> 1 || rowSpan
> 1)
9024 // Assume all spanning cells are shown
9030 for (k
= 0; k
< (int) rectArray
.GetCount(); k
++)
9032 if (rectArray
[k
].Contains(wxPoint(i
, j
)))
9044 // TODO: find the first spanned cell in each row that spans the most columns and doesn't
9045 // overlap with a spanned cell starting at a previous column position.
9046 // This means we need to keep an array of rects so we can check. However
9047 // it does also mean that some spans simply may not be taken into account
9048 // where there are different spans happening on different rows. In these cases,
9049 // they will simply be as wide as their constituent columns.
9051 // (1) Do an initial layout for all cells to get minimum and maximum size, and get
9052 // the absolute or percentage width of each column.
9054 for (j
= 0; j
< m_rowCount
; j
++)
9056 // First get the overall margins so we can calculate percentage widths based on
9057 // the available content space for all cells on the row
9059 int overallRowContentMargin
= 0;
9060 int visibleCellCount
= 0;
9062 for (i
= 0; i
< m_colCount
; i
++)
9064 wxRichTextBox
* cell
= GetCell(j
, i
);
9065 if (cell
->IsShown())
9067 int cellTotalLeftMargin
= 0, cellTotalRightMargin
= 0, cellTotalTopMargin
= 0, cellTotalBottomMargin
= 0;
9068 GetTotalMargin(dc
, buffer
, cell
->GetAttributes(), cellTotalLeftMargin
, cellTotalRightMargin
, cellTotalTopMargin
, cellTotalBottomMargin
);
9070 overallRowContentMargin
+= (cellTotalLeftMargin
+ cellTotalRightMargin
);
9071 visibleCellCount
++;
9075 // Add in inter-cell padding
9076 overallRowContentMargin
+= ((visibleCellCount
-1) * paddingX
);
9078 int rowContentWidth
= internalTableWidth
- overallRowContentMargin
;
9079 wxSize
rowTableSize(rowContentWidth
, 0);
9080 wxTextAttrDimensionConverter
converter(dc
, scale
, rowTableSize
);
9082 for (i
= 0; i
< m_colCount
; i
++)
9084 wxRichTextBox
* cell
= GetCell(j
, i
);
9085 if (cell
->IsShown())
9088 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9089 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9091 // Lay out cell to find min/max widths
9092 cell
->Invalidate(wxRICHTEXT_ALL
);
9093 cell
->Layout(dc
, context
, availableSpace
, availableSpace
, style
);
9097 int absoluteCellWidth
= -1;
9098 int percentageCellWidth
= -1;
9100 // I think we need to calculate percentages from the internal table size,
9101 // minus the padding between cells which we'll need to calculate from the
9102 // (number of VISIBLE cells - 1)*paddingX. Then percentages that add up to 100%
9103 // will add up to 100%. In CSS, the width specifies the cell's content rect width,
9104 // so if we want to conform to that we'll need to add in the overall cell margins.
9105 // However, this will make it difficult to specify percentages that add up to
9106 // 100% and still fit within the table width.
9107 // Let's say two cells have 50% width. They have 10 pixels of overall margin each.
9108 // The table content rect is 500 pixels and the inter-cell padding is 20 pixels.
9109 // If we're using internal content size for the width, we would calculate the
9110 // the overall cell width for n cells as:
9111 // (500 - 20*(n-1) - overallCellMargin1 - overallCellMargin2 - ...) * percentage / 100
9112 // + thisOverallCellMargin
9113 // = 500 - 20 - 10 - 10) * 0.5 + 10 = 240 pixels overall cell width.
9114 // Adding this back, we get 240 + 240 + 20 = 500 pixels.
9116 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9118 int w
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetWidth());
9119 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
9121 percentageCellWidth
= w
;
9125 absoluteCellWidth
= w
;
9127 // Override absolute width with minimum width if necessary
9128 if (cell
->GetMinSize().x
> 0 && absoluteCellWidth
!=1 && cell
->GetMinSize().x
> absoluteCellWidth
)
9129 absoluteCellWidth
= cell
->GetMinSize().x
;
9132 if (absoluteCellWidth
!= -1)
9134 if (absoluteCellWidth
> absoluteColWidths
[i
])
9135 absoluteColWidths
[i
] = absoluteCellWidth
;
9138 if (percentageCellWidth
!= -1)
9140 if (percentageCellWidth
> percentageColWidths
[i
])
9141 percentageColWidths
[i
] = percentageCellWidth
;
9144 if (colSpan
== 1 && cell
->GetMinSize().x
&& cell
->GetMinSize().x
> minColWidths
[i
])
9145 minColWidths
[i
] = cell
->GetMinSize().x
;
9146 if (colSpan
== 1 && cell
->GetMaxSize().x
&& cell
->GetMaxSize().x
> maxColWidths
[i
])
9147 maxColWidths
[i
] = cell
->GetMaxSize().x
;
9153 // (2) Allocate initial column widths from minimum widths, absolute values and proportions
9154 // TODO: simply merge this into (1).
9155 for (i
= 0; i
< m_colCount
; i
++)
9157 if (absoluteColWidths
[i
] > 0)
9159 colWidths
[i
] = absoluteColWidths
[i
];
9161 else if (percentageColWidths
[i
] > 0)
9163 colWidths
[i
] = percentageColWidths
[i
];
9165 // This is rubbish - we calculated the absolute widths from percentages, so
9166 // we can't do it again here.
9167 //colWidths[i] = (int) (double(percentageColWidths[i]) * double(tableWidth) / 100.0 + 0.5);
9171 // (3) Process absolute or proportional widths of spanning columns,
9172 // now that we know what our fixed column widths are going to be.
9173 // Spanned cells will try to adjust columns so the span will fit.
9174 // Even existing fixed column widths can be expanded if necessary.
9175 // Actually, currently fixed columns widths aren't adjusted; instead,
9176 // the algorithm favours earlier rows and adjusts unspecified column widths
9177 // the first time only. After that, we can't know whether the column has been
9178 // specified explicitly or not. (We could make a note if necessary.)
9179 for (j
= 0; j
< m_rowCount
; j
++)
9181 // First get the overall margins so we can calculate percentage widths based on
9182 // the available content space for all cells on the row
9184 int overallRowContentMargin
= 0;
9185 int visibleCellCount
= 0;
9187 for (i
= 0; i
< m_colCount
; i
++)
9189 wxRichTextBox
* cell
= GetCell(j
, i
);
9190 if (cell
->IsShown())
9192 int cellTotalLeftMargin
= 0, cellTotalRightMargin
= 0, cellTotalTopMargin
= 0, cellTotalBottomMargin
= 0;
9193 GetTotalMargin(dc
, buffer
, cell
->GetAttributes(), cellTotalLeftMargin
, cellTotalRightMargin
, cellTotalTopMargin
, cellTotalBottomMargin
);
9195 overallRowContentMargin
+= (cellTotalLeftMargin
+ cellTotalRightMargin
);
9196 visibleCellCount
++;
9200 // Add in inter-cell padding
9201 overallRowContentMargin
+= ((visibleCellCount
-1) * paddingX
);
9203 int rowContentWidth
= internalTableWidth
- overallRowContentMargin
;
9204 wxSize
rowTableSize(rowContentWidth
, 0);
9205 wxTextAttrDimensionConverter
converter(dc
, scale
, rowTableSize
);
9207 for (i
= 0; i
< m_colCount
; i
++)
9209 wxRichTextBox
* cell
= GetCell(j
, i
);
9210 if (cell
->IsShown())
9213 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9214 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9218 int spans
= wxMin(colSpan
, m_colCount
- i
);
9222 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9224 cellWidth
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetWidth());
9225 // Override absolute width with minimum width if necessary
9226 if (cell
->GetMinSize().x
> 0 && cellWidth
!=1 && cell
->GetMinSize().x
> cellWidth
)
9227 cellWidth
= cell
->GetMinSize().x
;
9231 // Do we want to do this? It's the only chance we get to
9232 // use the cell's min/max sizes, so we need to work out
9233 // how we're going to balance the unspecified spanning cell
9234 // width with the possibility more-constrained constituent cell widths.
9235 // Say there's a tiny bitmap giving it a max width of 10 pixels. We
9236 // don't want to constraint all the spanned columns to fit into this cell.
9237 // OK, let's say that if any of the constituent columns don't fit,
9238 // then we simply stop constraining the columns; instead, we'll just fit the spanning
9239 // cells to the columns later.
9240 cellWidth
= cell
->GetMinSize().x
;
9241 if (cell
->GetMaxSize().x
> cellWidth
)
9242 cellWidth
= cell
->GetMaxSize().x
;
9245 // Subtract the padding between cells
9246 int spanningWidth
= cellWidth
;
9247 spanningWidth
-= paddingX
* (spans
-1);
9249 if (spanningWidth
> 0)
9251 // Now share the spanning width between columns within that span
9252 // TODO: take into account min widths of columns within the span
9253 int spanningWidthLeft
= spanningWidth
;
9254 int stretchColCount
= 0;
9255 for (k
= i
; k
< (i
+spans
); k
++)
9257 if (colWidths
[k
] > 0) // absolute or proportional width has been specified
9258 spanningWidthLeft
-= colWidths
[k
];
9262 // Now divide what's left between the remaining columns
9264 if (stretchColCount
> 0)
9265 colShare
= spanningWidthLeft
/ stretchColCount
;
9266 int colShareRemainder
= spanningWidthLeft
- (colShare
* stretchColCount
);
9268 // If fixed-width columns are currently too big, then we'll later
9269 // stretch the spanned cell to fit.
9271 if (spanningWidthLeft
> 0)
9273 for (k
= i
; k
< (i
+spans
); k
++)
9275 if (colWidths
[k
] <= 0) // absolute or proportional width has not been specified
9277 int newWidth
= colShare
;
9278 if (k
== (i
+spans
-1))
9279 newWidth
+= colShareRemainder
; // ensure all pixels are filled
9280 colWidths
[k
] = newWidth
;
9291 // (4) Next, share any remaining space out between columns that have not yet been calculated.
9292 // TODO: take into account min widths of columns within the span
9293 int tableWidthMinusPadding
= internalTableWidth
- (m_colCount
-1)*paddingX
;
9294 int widthLeft
= tableWidthMinusPadding
;
9295 int stretchColCount
= 0;
9296 for (i
= 0; i
< m_colCount
; i
++)
9298 // TODO: we need to take into account min widths.
9299 // Subtract min width from width left, then
9300 // add the colShare to the min width
9301 if (colWidths
[i
] > 0) // absolute or proportional width has been specified
9302 widthLeft
-= colWidths
[i
];
9305 if (minColWidths
[i
] > 0)
9306 widthLeft
-= minColWidths
[i
];
9312 // Now divide what's left between the remaining columns
9314 if (stretchColCount
> 0)
9315 colShare
= widthLeft
/ stretchColCount
;
9316 int colShareRemainder
= widthLeft
- (colShare
* stretchColCount
);
9318 // Check we don't have enough space, in which case shrink all columns, overriding
9319 // any absolute/proportional widths
9320 // TODO: actually we would like to divide up the shrinkage according to size.
9321 // How do we calculate the proportions that will achieve this?
9322 // Could first choose an arbitrary value for stretching cells, and then calculate
9323 // factors to multiply each width by.
9324 // TODO: want to record this fact and pass to an iteration that tries e.g. min widths
9325 if (widthLeft
< 0 || (stretchToFitTableWidth
&& (stretchColCount
== 0)))
9327 colShare
= tableWidthMinusPadding
/ m_colCount
;
9328 colShareRemainder
= tableWidthMinusPadding
- (colShare
* m_colCount
);
9329 for (i
= 0; i
< m_colCount
; i
++)
9332 minColWidths
[i
] = 0;
9336 // We have to adjust the columns if either we need to shrink the
9337 // table to fit the parent/table width, or we explicitly set the
9338 // table width and need to stretch out the table.
9339 if (widthLeft
< 0 || stretchToFitTableWidth
)
9341 for (i
= 0; i
< m_colCount
; i
++)
9343 if (colWidths
[i
] <= 0) // absolute or proportional width has not been specified
9345 if (minColWidths
[i
] > 0)
9346 colWidths
[i
] = minColWidths
[i
] + colShare
;
9348 colWidths
[i
] = colShare
;
9349 if (i
== (m_colCount
-1))
9350 colWidths
[i
] += colShareRemainder
; // ensure all pixels are filled
9355 // TODO: if spanned cells have no specified or max width, make them the
9356 // as big as the columns they span. Do this for all spanned cells in all
9357 // rows, of course. Size any spanned cells left over at the end - even if they
9358 // have width > 0, make sure they're limited to the appropriate column edge.
9362 Sort out confusion between content width
9363 and overall width later. For now, assume we specify overall width.
9365 So, now we've laid out the table to fit into the given space
9366 and have used specified widths and minimum widths.
9368 Now we need to consider how we will try to take maximum width into account.
9372 // (??) TODO: take max width into account
9374 // (6) Lay out all cells again with the current values
9377 int y
= availableSpace
.y
;
9378 for (j
= 0; j
< m_rowCount
; j
++)
9380 int x
= availableSpace
.x
; // TODO: take into account centering etc.
9381 int maxCellHeight
= 0;
9382 int maxSpecifiedCellHeight
= 0;
9384 wxArrayInt actualWidths
;
9385 actualWidths
.Add(0, m_colCount
);
9387 wxTextAttrDimensionConverter
converter(dc
, scale
);
9388 for (i
= 0; i
< m_colCount
; i
++)
9390 wxRichTextCell
* cell
= GetCell(j
, i
);
9391 if (cell
->IsShown())
9393 // Get max specified cell height
9394 // Don't handle percentages for height
9395 if (cell
->GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && cell
->GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() != wxTEXT_ATTR_UNITS_PERCENTAGE
)
9397 int h
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetHeight());
9398 if (h
> maxSpecifiedCellHeight
)
9399 maxSpecifiedCellHeight
= h
;
9402 if (colWidths
[i
] > 0) // absolute or proportional width has been specified
9405 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9406 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9408 wxRect availableCellSpace
;
9410 // TODO: take into acount spans
9413 // Calculate the size of this spanning cell from its constituent columns
9415 int spans
= wxMin(colSpan
, m_colCount
- i
);
9416 for (k
= i
; k
< spans
; k
++)
9422 availableCellSpace
= wxRect(x
, y
, xx
, -1);
9425 availableCellSpace
= wxRect(x
, y
, colWidths
[i
], -1);
9427 // Store actual width so we can force cell to be the appropriate width on the final loop
9428 actualWidths
[i
] = availableCellSpace
.GetWidth();
9431 cell
->Invalidate(wxRICHTEXT_ALL
);
9432 cell
->Layout(dc
, context
, availableCellSpace
, availableSpace
, style
);
9434 // TODO: use GetCachedSize().x to compute 'natural' size
9436 x
+= (availableCellSpace
.GetWidth() + paddingX
);
9437 if (cell
->GetCachedSize().y
> maxCellHeight
)
9438 maxCellHeight
= cell
->GetCachedSize().y
;
9443 maxCellHeight
= wxMax(maxCellHeight
, maxSpecifiedCellHeight
);
9445 for (i
= 0; i
< m_colCount
; i
++)
9447 wxRichTextCell
* cell
= GetCell(j
, i
);
9448 if (cell
->IsShown())
9450 wxRect availableCellSpace
= wxRect(cell
->GetPosition(), wxSize(actualWidths
[i
], maxCellHeight
));
9451 // Lay out cell with new height
9452 cell
->Invalidate(wxRICHTEXT_ALL
);
9453 cell
->Layout(dc
, context
, availableCellSpace
, availableSpace
, style
);
9455 // Make sure the cell size really is the appropriate size,
9456 // not the calculated box size
9457 cell
->SetCachedSize(wxSize(actualWidths
[i
], maxCellHeight
));
9459 maxRight
= wxMax(maxRight
, cell
->GetPosition().x
+ cell
->GetCachedSize().x
);
9464 if (j
< (m_rowCount
-1))
9468 // We need to add back the margins etc.
9470 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
9471 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxRight
- availableSpace
.x
, y
- availableSpace
.y
));
9472 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
9473 SetCachedSize(marginRect
.GetSize());
9476 // TODO: calculate max size
9478 SetMaxSize(GetCachedSize());
9481 // TODO: calculate min size
9483 SetMinSize(GetCachedSize());
9486 // TODO: currently we use either a fixed table width or the parent's size.
9487 // We also want to be able to calculate the table width from its content,
9488 // whether using fixed column widths or cell content min/max width.
9489 // Probably need a boolean flag to say whether we need to stretch cells
9490 // to fit the table width, or to simply use min/max cell widths. The
9491 // trouble with this is that if cell widths are not specified, they
9492 // will be tiny; we could use arbitrary defaults but this seems unsatisfactory.
9493 // Anyway, ignoring that problem, we probably need to factor layout into a function
9494 // that can can calculate the maximum unconstrained layout in case table size is
9495 // not specified. Then LayoutToBestSize() can choose to use either parent size to
9496 // constrain Layout(), or the previously-calculated max size to constraint layout.
9501 // Finds the absolute position and row height for the given character position
9502 bool wxRichTextTable::FindPosition(wxDC
& dc
, wxRichTextDrawingContext
& context
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
9504 wxRichTextCell
* child
= GetCell(index
+1);
9507 // Find the position at the start of the child cell, since the table doesn't
9508 // have any caret position of its own.
9509 return child
->FindPosition(dc
, context
, -1, pt
, height
, forceLineStart
);
9515 // Get the cell at the given character position (in the range of the table).
9516 wxRichTextCell
* wxRichTextTable::GetCell(long pos
) const
9518 int row
= 0, col
= 0;
9519 if (GetCellRowColumnPosition(pos
, row
, col
))
9521 return GetCell(row
, col
);
9527 // Get the row/column for a given character position
9528 bool wxRichTextTable::GetCellRowColumnPosition(long pos
, int& row
, int& col
) const
9530 if (m_colCount
== 0 || m_rowCount
== 0)
9533 row
= (int) (pos
/ m_colCount
);
9534 col
= pos
- (row
* m_colCount
);
9536 wxASSERT(row
< m_rowCount
&& col
< m_colCount
);
9538 if (row
< m_rowCount
&& col
< m_colCount
)
9544 // Calculate range, taking row/cell ordering into account instead of relying
9545 // on list ordering.
9546 void wxRichTextTable::CalculateRange(long start
, long& end
)
9548 long current
= start
;
9549 long lastEnd
= current
;
9558 for (i
= 0; i
< m_rowCount
; i
++)
9560 for (j
= 0; j
< m_colCount
; j
++)
9562 wxRichTextCell
* child
= GetCell(i
, j
);
9567 child
->CalculateRange(current
, childEnd
);
9570 current
= childEnd
+ 1;
9575 // A top-level object always has a range of size 1,
9576 // because its children don't count at this level.
9578 m_range
.SetRange(start
, start
);
9580 // An object with no children has zero length
9581 if (m_children
.GetCount() == 0)
9583 m_ownRange
.SetRange(0, lastEnd
);
9586 // Gets the range size.
9587 bool wxRichTextTable::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
9589 return wxRichTextBox::GetRangeSize(range
, size
, descent
, dc
, context
, flags
, position
, partialExtents
);
9592 // Deletes content in the given range.
9593 bool wxRichTextTable::DeleteRange(const wxRichTextRange
& WXUNUSED(range
))
9595 // TODO: implement deletion of cells
9599 // Gets any text in this object for the given range.
9600 wxString
wxRichTextTable::GetTextForRange(const wxRichTextRange
& range
) const
9602 return wxRichTextBox::GetTextForRange(range
);
9605 // Copies this object.
9606 void wxRichTextTable::Copy(const wxRichTextTable
& obj
)
9608 wxRichTextBox::Copy(obj
);
9612 m_rowCount
= obj
.m_rowCount
;
9613 m_colCount
= obj
.m_colCount
;
9615 m_cells
.Add(wxRichTextObjectPtrArray(), m_rowCount
);
9618 for (i
= 0; i
< m_rowCount
; i
++)
9620 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
9621 for (j
= 0; j
< m_colCount
; j
++)
9623 wxRichTextCell
* cell
= wxDynamicCast(obj
.GetCell(i
, j
)->Clone(), wxRichTextCell
);
9631 void wxRichTextTable::ClearTable()
9637 bool wxRichTextTable::CreateTable(int rows
, int cols
)
9644 m_cells
.Add(wxRichTextObjectPtrArray(), rows
);
9647 for (i
= 0; i
< rows
; i
++)
9649 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
9650 for (j
= 0; j
< cols
; j
++)
9652 wxRichTextCell
* cell
= new wxRichTextCell
;
9654 cell
->AddParagraph(wxEmptyString
);
9663 wxRichTextCell
* wxRichTextTable::GetCell(int row
, int col
) const
9665 wxASSERT(row
< m_rowCount
);
9666 wxASSERT(col
< m_colCount
);
9668 if (row
< m_rowCount
&& col
< m_colCount
)
9670 wxRichTextObjectPtrArray
& colArray
= m_cells
[row
];
9671 wxRichTextObject
* obj
= colArray
[col
];
9672 return wxDynamicCast(obj
, wxRichTextCell
);
9678 // Returns a selection object specifying the selections between start and end character positions.
9679 // For example, a table would deduce what cells (of range length 1) are selected when dragging across the table.
9680 wxRichTextSelection
wxRichTextTable::GetSelection(long start
, long end
) const
9682 wxRichTextSelection selection
;
9683 selection
.SetContainer((wxRichTextTable
*) this);
9692 wxASSERT( start
>= 0 && end
< (m_colCount
* m_rowCount
));
9694 if (end
>= (m_colCount
* m_rowCount
))
9697 // We need to find the rectangle of cells that is described by the rectangle
9698 // with start, end as the diagonal. Make sure we don't add cells that are
9699 // not currenty visible because they are overlapped by spanning cells.
9701 --------------------------
9702 | 0 | 1 | 2 | 3 | 4 |
9703 --------------------------
9704 | 5 | 6 | 7 | 8 | 9 |
9705 --------------------------
9706 | 10 | 11 | 12 | 13 | 14 |
9707 --------------------------
9708 | 15 | 16 | 17 | 18 | 19 |
9709 --------------------------
9711 Let's say we select 6 -> 18.
9713 Left and right edge cols of rectangle are 1 and 3 inclusive. Find least/greatest to find
9714 which is left and which is right.
9716 Top and bottom edge rows are 1 and 3 inclusive. Again, find least/greatest to find top and bottom.
9718 Now go through rows from 1 to 3 and only add cells that are (a) within above column range
9724 int leftCol
= start
- m_colCount
* int(start
/m_colCount
);
9725 int rightCol
= end
- m_colCount
* int(end
/m_colCount
);
9727 int topRow
= int(start
/m_colCount
);
9728 int bottomRow
= int(end
/m_colCount
);
9730 if (leftCol
> rightCol
)
9737 if (topRow
> bottomRow
)
9739 int tmp
= bottomRow
;
9745 for (i
= topRow
; i
<= bottomRow
; i
++)
9747 for (j
= leftCol
; j
<= rightCol
; j
++)
9749 wxRichTextCell
* cell
= GetCell(i
, j
);
9750 if (cell
&& cell
->IsShown())
9751 selection
.Add(cell
->GetRange());
9758 // Sets the attributes for the cells specified by the selection.
9759 bool wxRichTextTable::SetCellStyle(const wxRichTextSelection
& selection
, const wxRichTextAttr
& style
, int flags
)
9761 if (selection
.GetContainer() != this)
9764 wxRichTextBuffer
* buffer
= GetBuffer();
9765 bool haveControl
= (buffer
&& buffer
->GetRichTextCtrl() != NULL
);
9766 bool withUndo
= haveControl
&& ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
9769 buffer
->BeginBatchUndo(_("Set Cell Style"));
9771 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
9774 wxRichTextCell
* cell
= wxDynamicCast(node
->GetData(), wxRichTextCell
);
9775 if (cell
&& selection
.WithinSelection(cell
->GetRange().GetStart()))
9776 SetStyle(cell
, style
, flags
);
9777 node
= node
->GetNext();
9780 // Do action, or delay it until end of batch.
9782 buffer
->EndBatchUndo();
9787 bool wxRichTextTable::DeleteRows(int startRow
, int noRows
)
9789 wxASSERT((startRow
+ noRows
) < m_rowCount
);
9790 if ((startRow
+ noRows
) >= m_rowCount
)
9794 for (i
= startRow
; i
< (startRow
+noRows
); i
++)
9796 wxRichTextObjectPtrArray
& colArray
= m_cells
[startRow
];
9797 for (j
= 0; j
< (int) colArray
.GetCount(); j
++)
9799 wxRichTextObject
* cell
= colArray
[j
];
9800 RemoveChild(cell
, true);
9803 // Keep deleting at the same position, since we move all
9805 m_cells
.RemoveAt(startRow
);
9808 m_rowCount
= m_rowCount
- noRows
;
9813 bool wxRichTextTable::DeleteColumns(int startCol
, int noCols
)
9815 wxASSERT((startCol
+ noCols
) < m_colCount
);
9816 if ((startCol
+ noCols
) >= m_colCount
)
9819 bool deleteRows
= (noCols
== m_colCount
);
9822 for (i
= 0; i
< m_rowCount
; i
++)
9824 wxRichTextObjectPtrArray
& colArray
= m_cells
[deleteRows
? 0 : i
];
9825 for (j
= startCol
; j
< (startCol
+noCols
); j
++)
9827 wxRichTextObject
* cell
= colArray
[j
];
9828 RemoveChild(cell
, true);
9832 m_cells
.RemoveAt(0);
9837 m_colCount
= m_colCount
- noCols
;
9842 bool wxRichTextTable::AddRows(int startRow
, int noRows
, const wxRichTextAttr
& attr
)
9844 wxASSERT(startRow
<= m_rowCount
);
9845 if (startRow
> m_rowCount
)
9849 for (i
= 0; i
< noRows
; i
++)
9852 if (startRow
== m_rowCount
)
9854 m_cells
.Add(wxRichTextObjectPtrArray());
9855 idx
= m_cells
.GetCount() - 1;
9859 m_cells
.Insert(wxRichTextObjectPtrArray(), startRow
+i
);
9863 wxRichTextObjectPtrArray
& colArray
= m_cells
[idx
];
9864 for (j
= 0; j
< m_colCount
; j
++)
9866 wxRichTextCell
* cell
= new wxRichTextCell
;
9867 cell
->GetAttributes() = attr
;
9874 m_rowCount
= m_rowCount
+ noRows
;
9878 bool wxRichTextTable::AddColumns(int startCol
, int noCols
, const wxRichTextAttr
& attr
)
9880 wxASSERT(startCol
<= m_colCount
);
9881 if (startCol
> m_colCount
)
9885 for (i
= 0; i
< m_rowCount
; i
++)
9887 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
9888 for (j
= 0; j
< noCols
; j
++)
9890 wxRichTextCell
* cell
= new wxRichTextCell
;
9891 cell
->GetAttributes() = attr
;
9895 if (startCol
== m_colCount
)
9898 colArray
.Insert(cell
, startCol
+j
);
9902 m_colCount
= m_colCount
+ noCols
;
9907 // Edit properties via a GUI
9908 bool wxRichTextTable::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
9910 wxRichTextObjectPropertiesDialog
boxDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Table Properties"));
9911 boxDlg
.SetAttributes(GetAttributes());
9913 if (boxDlg
.ShowModal() == wxID_OK
)
9915 boxDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
9923 * Module to initialise and clean up handlers
9926 class wxRichTextModule
: public wxModule
9928 DECLARE_DYNAMIC_CLASS(wxRichTextModule
)
9930 wxRichTextModule() {}
9933 wxRichTextBuffer::SetRenderer(new wxRichTextStdRenderer
);
9934 wxRichTextBuffer::InitStandardHandlers();
9935 wxRichTextParagraph::InitDefaultTabs();
9937 wxRichTextXMLHandler::RegisterNodeName(wxT("text"), wxT("wxRichTextPlainText"));
9938 wxRichTextXMLHandler::RegisterNodeName(wxT("symbol"), wxT("wxRichTextPlainText"));
9939 wxRichTextXMLHandler::RegisterNodeName(wxT("image"), wxT("wxRichTextImage"));
9940 wxRichTextXMLHandler::RegisterNodeName(wxT("paragraph"), wxT("wxRichTextParagraph"));
9941 wxRichTextXMLHandler::RegisterNodeName(wxT("paragraphlayout"), wxT("wxRichTextParagraphLayoutBox"));
9942 wxRichTextXMLHandler::RegisterNodeName(wxT("textbox"), wxT("wxRichTextBox"));
9943 wxRichTextXMLHandler::RegisterNodeName(wxT("cell"), wxT("wxRichTextCell"));
9944 wxRichTextXMLHandler::RegisterNodeName(wxT("table"), wxT("wxRichTextTable"));
9945 wxRichTextXMLHandler::RegisterNodeName(wxT("field"), wxT("wxRichTextField"));
9951 wxRichTextBuffer::CleanUpHandlers();
9952 wxRichTextBuffer::CleanUpDrawingHandlers();
9953 wxRichTextBuffer::CleanUpFieldTypes();
9954 wxRichTextXMLHandler::ClearNodeToClassMap();
9955 wxRichTextDecimalToRoman(-1);
9956 wxRichTextParagraph::ClearDefaultTabs();
9957 wxRichTextCtrl::ClearAvailableFontNames();
9958 wxRichTextBuffer::SetRenderer(NULL
);
9962 IMPLEMENT_DYNAMIC_CLASS(wxRichTextModule
, wxModule
)
9965 // If the richtext lib is dynamically loaded after the app has already started
9966 // (such as from wxPython) then the built-in module system will not init this
9967 // module. Provide this function to do it manually.
9968 void wxRichTextModuleInit()
9970 wxModule
* module = new wxRichTextModule
;
9972 wxModule::RegisterModule(module);
9977 * Commands for undo/redo
9981 wxRichTextCommand::wxRichTextCommand(const wxString
& name
, wxRichTextCommandId id
, wxRichTextBuffer
* buffer
,
9982 wxRichTextParagraphLayoutBox
* container
, wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
): wxCommand(true, name
)
9984 /* wxRichTextAction* action = */ new wxRichTextAction(this, name
, id
, buffer
, container
, ctrl
, ignoreFirstTime
);
9987 wxRichTextCommand::wxRichTextCommand(const wxString
& name
): wxCommand(true, name
)
9991 wxRichTextCommand::~wxRichTextCommand()
9996 void wxRichTextCommand::AddAction(wxRichTextAction
* action
)
9998 if (!m_actions
.Member(action
))
9999 m_actions
.Append(action
);
10002 bool wxRichTextCommand::Do()
10004 for (wxList::compatibility_iterator node
= m_actions
.GetFirst(); node
; node
= node
->GetNext())
10006 wxRichTextAction
* action
= (wxRichTextAction
*) node
->GetData();
10013 bool wxRichTextCommand::Undo()
10015 for (wxList::compatibility_iterator node
= m_actions
.GetLast(); node
; node
= node
->GetPrevious())
10017 wxRichTextAction
* action
= (wxRichTextAction
*) node
->GetData();
10024 void wxRichTextCommand::ClearActions()
10026 WX_CLEAR_LIST(wxList
, m_actions
);
10030 * Individual action
10034 wxRichTextAction::wxRichTextAction(wxRichTextCommand
* cmd
, const wxString
& name
, wxRichTextCommandId id
,
10035 wxRichTextBuffer
* buffer
, wxRichTextParagraphLayoutBox
* container
,
10036 wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
)
10040 m_containerAddress
.Create(buffer
, container
);
10041 m_ignoreThis
= ignoreFirstTime
;
10046 m_newParagraphs
.SetDefaultStyle(buffer
->GetDefaultStyle());
10047 m_newParagraphs
.SetBasicStyle(buffer
->GetBasicStyle());
10049 cmd
->AddAction(this);
10052 wxRichTextAction::~wxRichTextAction()
10058 // Returns the container that this action refers to, using the container address and top-level buffer.
10059 wxRichTextParagraphLayoutBox
* wxRichTextAction::GetContainer() const
10061 wxRichTextParagraphLayoutBox
* container
= wxDynamicCast(GetContainerAddress().GetObject(m_buffer
), wxRichTextParagraphLayoutBox
);
10066 void wxRichTextAction::CalculateRefreshOptimizations(wxArrayInt
& optimizationLineCharPositions
, wxArrayInt
& optimizationLineYPositions
)
10068 // Store a list of line start character and y positions so we can figure out which area
10069 // we need to refresh
10071 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10072 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10073 wxASSERT(container
!= NULL
);
10077 // NOTE: we're assuming that the buffer is laid out correctly at this point.
10078 // If we had several actions, which only invalidate and leave layout until the
10079 // paint handler is called, then this might not be true. So we may need to switch
10080 // optimisation on only when we're simply adding text and not simultaneously
10081 // deleting a selection, for example. Or, we make sure the buffer is laid out correctly
10082 // first, but of course this means we'll be doing it twice.
10083 if (!m_buffer
->IsDirty() && m_ctrl
) // can only do optimisation if the buffer is already laid out correctly
10085 wxSize clientSize
= m_ctrl
->GetUnscaledSize(m_ctrl
->GetClientSize());
10086 wxPoint firstVisiblePt
= m_ctrl
->GetUnscaledPoint(m_ctrl
->GetFirstVisiblePoint());
10087 int lastY
= firstVisiblePt
.y
+ clientSize
.y
;
10089 wxRichTextParagraph
* para
= container
->GetParagraphAtPosition(GetRange().GetStart());
10090 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(para
);
10093 wxRichTextParagraph
* child
= (wxRichTextParagraph
*) node
->GetData();
10094 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
10097 wxRichTextLine
* line
= node2
->GetData();
10098 wxPoint pt
= line
->GetAbsolutePosition();
10099 wxRichTextRange range
= line
->GetAbsoluteRange();
10103 node2
= wxRichTextLineList::compatibility_iterator();
10104 node
= wxRichTextObjectList::compatibility_iterator();
10106 else if (range
.GetStart() > GetPosition() && pt
.y
>= firstVisiblePt
.y
)
10108 optimizationLineCharPositions
.Add(range
.GetStart());
10109 optimizationLineYPositions
.Add(pt
.y
);
10113 node2
= node2
->GetNext();
10117 node
= node
->GetNext();
10123 bool wxRichTextAction::Do()
10125 m_buffer
->Modify(true);
10127 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10128 wxASSERT(container
!= NULL
);
10134 case wxRICHTEXT_INSERT
:
10136 // Store a list of line start character and y positions so we can figure out which area
10137 // we need to refresh
10138 wxArrayInt optimizationLineCharPositions
;
10139 wxArrayInt optimizationLineYPositions
;
10141 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10142 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
10145 container
->InsertFragment(GetRange().GetStart(), m_newParagraphs
);
10146 container
->UpdateRanges();
10148 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10149 // Layout() would stop prematurely at the top level.
10150 container
->InvalidateHierarchy(wxRichTextRange(wxMax(0, GetRange().GetStart()-1), GetRange().GetEnd()));
10152 long newCaretPosition
= GetPosition() + m_newParagraphs
.GetOwnRange().GetLength();
10154 // Character position to caret position
10155 newCaretPosition
--;
10157 // Don't take into account the last newline
10158 if (m_newParagraphs
.GetPartialParagraph())
10159 newCaretPosition
--;
10161 if (m_newParagraphs
.GetChildren().GetCount() > 1)
10163 wxRichTextObject
* p
= (wxRichTextObject
*) m_newParagraphs
.GetChildren().GetLast()->GetData();
10164 if (p
->GetRange().GetLength() == 1)
10165 newCaretPosition
--;
10168 newCaretPosition
= wxMin(newCaretPosition
, (container
->GetOwnRange().GetEnd()-1));
10170 UpdateAppearance(newCaretPosition
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */);
10172 wxRichTextEvent
cmdEvent(
10173 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED
,
10174 m_ctrl
? m_ctrl
->GetId() : -1);
10175 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10176 cmdEvent
.SetRange(GetRange());
10177 cmdEvent
.SetPosition(GetRange().GetStart());
10178 cmdEvent
.SetContainer(container
);
10180 m_buffer
->SendEvent(cmdEvent
);
10184 case wxRICHTEXT_DELETE
:
10186 wxArrayInt optimizationLineCharPositions
;
10187 wxArrayInt optimizationLineYPositions
;
10189 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10190 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
10193 container
->DeleteRange(GetRange());
10194 container
->UpdateRanges();
10195 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10196 // Layout() would stop prematurely at the top level.
10197 container
->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
10199 long caretPos
= GetRange().GetStart()-1;
10200 if (caretPos
>= container
->GetOwnRange().GetEnd())
10203 UpdateAppearance(caretPos
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */);
10205 wxRichTextEvent
cmdEvent(
10206 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED
,
10207 m_ctrl
? m_ctrl
->GetId() : -1);
10208 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10209 cmdEvent
.SetRange(GetRange());
10210 cmdEvent
.SetPosition(GetRange().GetStart());
10211 cmdEvent
.SetContainer(container
);
10213 m_buffer
->SendEvent(cmdEvent
);
10217 case wxRICHTEXT_CHANGE_STYLE
:
10218 case wxRICHTEXT_CHANGE_PROPERTIES
:
10220 ApplyParagraphs(GetNewParagraphs());
10222 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10223 // Layout() would stop prematurely at the top level.
10224 container
->InvalidateHierarchy(GetRange());
10226 UpdateAppearance(GetPosition());
10228 wxRichTextEvent
cmdEvent(
10229 m_cmdId
== wxRICHTEXT_CHANGE_STYLE
? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
: wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED
,
10230 m_ctrl
? m_ctrl
->GetId() : -1);
10231 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10232 cmdEvent
.SetRange(GetRange());
10233 cmdEvent
.SetPosition(GetRange().GetStart());
10234 cmdEvent
.SetContainer(container
);
10236 m_buffer
->SendEvent(cmdEvent
);
10240 case wxRICHTEXT_CHANGE_ATTRIBUTES
:
10242 wxRichTextObject
* obj
= m_objectAddress
.GetObject(m_buffer
); // container->GetChildAtPosition(GetRange().GetStart());
10245 wxRichTextAttr oldAttr
= obj
->GetAttributes();
10246 obj
->GetAttributes() = m_attributes
;
10247 m_attributes
= oldAttr
;
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 wxEVT_COMMAND_RICHTEXT_STYLE_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_OBJECT
:
10270 wxRichTextObject
* obj
= m_objectAddress
.GetObject(m_buffer
);
10271 // wxRichTextObject* obj = container->GetChildAtPosition(GetRange().GetStart());
10272 if (obj
&& m_object
)
10274 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(obj
);
10277 wxRichTextObject
* obj
= node
->GetData();
10278 node
->SetData(m_object
);
10283 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10284 // Layout() would stop prematurely at the top level.
10285 container
->InvalidateHierarchy(GetRange());
10287 UpdateAppearance(GetPosition());
10289 // TODO: send new kind of modification event
10300 bool wxRichTextAction::Undo()
10302 m_buffer
->Modify(true);
10304 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10305 wxASSERT(container
!= NULL
);
10311 case wxRICHTEXT_INSERT
:
10313 wxArrayInt optimizationLineCharPositions
;
10314 wxArrayInt optimizationLineYPositions
;
10316 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10317 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
10320 container
->DeleteRange(GetRange());
10321 container
->UpdateRanges();
10323 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10324 // Layout() would stop prematurely at the top level.
10325 container
->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
10327 long newCaretPosition
= GetPosition() - 1;
10329 UpdateAppearance(newCaretPosition
, true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */);
10331 wxRichTextEvent
cmdEvent(
10332 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED
,
10333 m_ctrl
? m_ctrl
->GetId() : -1);
10334 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10335 cmdEvent
.SetRange(GetRange());
10336 cmdEvent
.SetPosition(GetRange().GetStart());
10337 cmdEvent
.SetContainer(container
);
10339 m_buffer
->SendEvent(cmdEvent
);
10343 case wxRICHTEXT_DELETE
:
10345 wxArrayInt optimizationLineCharPositions
;
10346 wxArrayInt optimizationLineYPositions
;
10348 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10349 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
10352 container
->InsertFragment(GetRange().GetStart(), m_oldParagraphs
);
10353 container
->UpdateRanges();
10355 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10356 // Layout() would stop prematurely at the top level.
10357 container
->InvalidateHierarchy(GetRange());
10359 UpdateAppearance(GetPosition(), true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */);
10361 wxRichTextEvent
cmdEvent(
10362 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED
,
10363 m_ctrl
? m_ctrl
->GetId() : -1);
10364 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10365 cmdEvent
.SetRange(GetRange());
10366 cmdEvent
.SetPosition(GetRange().GetStart());
10367 cmdEvent
.SetContainer(container
);
10369 m_buffer
->SendEvent(cmdEvent
);
10373 case wxRICHTEXT_CHANGE_STYLE
:
10374 case wxRICHTEXT_CHANGE_PROPERTIES
:
10376 ApplyParagraphs(GetOldParagraphs());
10377 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10378 // Layout() would stop prematurely at the top level.
10379 container
->InvalidateHierarchy(GetRange());
10381 UpdateAppearance(GetPosition());
10383 wxRichTextEvent
cmdEvent(
10384 m_cmdId
== wxRICHTEXT_CHANGE_STYLE
? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
: wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED
,
10385 m_ctrl
? m_ctrl
->GetId() : -1);
10386 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10387 cmdEvent
.SetRange(GetRange());
10388 cmdEvent
.SetPosition(GetRange().GetStart());
10389 cmdEvent
.SetContainer(container
);
10391 m_buffer
->SendEvent(cmdEvent
);
10395 case wxRICHTEXT_CHANGE_ATTRIBUTES
:
10396 case wxRICHTEXT_CHANGE_OBJECT
:
10407 /// Update the control appearance
10408 void wxRichTextAction::UpdateAppearance(long caretPosition
, bool sendUpdateEvent
, wxArrayInt
* optimizationLineCharPositions
, wxArrayInt
* optimizationLineYPositions
, bool isDoCmd
)
10410 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10411 wxASSERT(container
!= NULL
);
10417 m_ctrl
->SetFocusObject(container
);
10418 m_ctrl
->SetCaretPosition(caretPosition
);
10420 if (!m_ctrl
->IsFrozen())
10422 wxRect containerRect
= container
->GetRect();
10424 m_ctrl
->LayoutContent();
10426 // Refresh everything if there were floating objects or the container changed size
10427 // (we can't yet optimize in these cases, since more complex interaction with other content occurs)
10428 if (container
->GetFloatingObjectCount() > 0 || (container
->GetParent() && containerRect
!= container
->GetRect()))
10430 m_ctrl
->Refresh(false);
10434 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10435 // Find refresh rectangle if we are in a position to optimise refresh
10436 if ((m_cmdId
== wxRICHTEXT_INSERT
|| m_cmdId
== wxRICHTEXT_DELETE
) && optimizationLineCharPositions
)
10440 wxSize clientSize
= m_ctrl
->GetUnscaledSize(m_ctrl
->GetClientSize());
10441 wxPoint firstVisiblePt
= m_ctrl
->GetUnscaledPoint(m_ctrl
->GetFirstVisiblePoint());
10443 // Start/end positions
10445 int lastY
= firstVisiblePt
.y
+ clientSize
.y
;
10447 bool foundEnd
= false;
10449 // position offset - how many characters were inserted
10450 int positionOffset
= GetRange().GetLength();
10452 // Determine whether this is Do or Undo, and adjust positionOffset accordingly
10453 if ((m_cmdId
== wxRICHTEXT_DELETE
&& isDoCmd
) || (m_cmdId
== wxRICHTEXT_INSERT
&& !isDoCmd
))
10454 positionOffset
= - positionOffset
;
10456 // find the first line which is being drawn at the same position as it was
10457 // before. Since we're talking about a simple insertion, we can assume
10458 // that the rest of the window does not need to be redrawn.
10460 wxRichTextParagraph
* para
= container
->GetParagraphAtPosition(GetPosition());
10461 // Since we support floating layout, we should redraw the whole para instead of just
10462 // the first line touching the invalid range.
10465 firstY
= para
->GetPosition().y
;
10468 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(para
);
10471 wxRichTextParagraph
* child
= (wxRichTextParagraph
*) node
->GetData();
10472 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
10475 wxRichTextLine
* line
= node2
->GetData();
10476 wxPoint pt
= line
->GetAbsolutePosition();
10477 wxRichTextRange range
= line
->GetAbsoluteRange();
10479 // we want to find the first line that is in the same position
10480 // as before. This will mean we're at the end of the changed text.
10482 if (pt
.y
> lastY
) // going past the end of the window, no more info
10484 node2
= wxRichTextLineList::compatibility_iterator();
10485 node
= wxRichTextObjectList::compatibility_iterator();
10487 // Detect last line in the buffer
10488 else if (!node2
->GetNext() && para
->GetRange().Contains(container
->GetOwnRange().GetEnd()))
10490 // If deleting text, make sure we refresh below as well as above
10491 if (positionOffset
>= 0)
10494 lastY
= pt
.y
+ line
->GetSize().y
;
10497 node2
= wxRichTextLineList::compatibility_iterator();
10498 node
= wxRichTextObjectList::compatibility_iterator();
10504 // search for this line being at the same position as before
10505 for (i
= 0; i
< optimizationLineCharPositions
->GetCount(); i
++)
10507 if (((*optimizationLineCharPositions
)[i
] + positionOffset
== range
.GetStart()) &&
10508 ((*optimizationLineYPositions
)[i
] == pt
.y
))
10510 // Stop, we're now the same as we were
10515 node2
= wxRichTextLineList::compatibility_iterator();
10516 node
= wxRichTextObjectList::compatibility_iterator();
10524 node2
= node2
->GetNext();
10528 node
= node
->GetNext();
10531 firstY
= wxMax(firstVisiblePt
.y
, firstY
);
10533 lastY
= firstVisiblePt
.y
+ clientSize
.y
;
10535 // Convert to device coordinates
10536 wxRect
rect(m_ctrl
->GetPhysicalPoint(m_ctrl
->GetScaledPoint(wxPoint(firstVisiblePt
.x
, firstY
))), m_ctrl
->GetScaledSize(wxSize(clientSize
.x
, lastY
- firstY
)));
10537 m_ctrl
->RefreshRect(rect
);
10541 m_ctrl
->Refresh(false);
10543 m_ctrl
->PositionCaret();
10545 // This causes styles to persist when doing programmatic
10546 // content creation except when Freeze/Thaw is used, so
10547 // disable this and check for the consequences.
10548 // m_ctrl->SetDefaultStyleToCursorStyle();
10550 if (sendUpdateEvent
)
10551 wxTextCtrl::SendTextUpdatedEvent(m_ctrl
);
10556 /// Replace the buffer paragraphs with the new ones.
10557 void wxRichTextAction::ApplyParagraphs(const wxRichTextParagraphLayoutBox
& fragment
)
10559 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10560 wxASSERT(container
!= NULL
);
10564 wxRichTextObjectList::compatibility_iterator node
= fragment
.GetChildren().GetFirst();
10567 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
10568 wxASSERT (para
!= NULL
);
10570 // We'll replace the existing paragraph by finding the paragraph at this position,
10571 // delete its node data, and setting a copy as the new node data.
10572 // TODO: make more efficient by simply swapping old and new paragraph objects.
10574 wxRichTextParagraph
* existingPara
= container
->GetParagraphAtPosition(para
->GetRange().GetStart());
10577 wxRichTextObjectList::compatibility_iterator bufferParaNode
= container
->GetChildren().Find(existingPara
);
10578 if (bufferParaNode
)
10580 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(*para
);
10581 newPara
->SetParent(container
);
10583 bufferParaNode
->SetData(newPara
);
10585 delete existingPara
;
10589 node
= node
->GetNext();
10596 * This stores beginning and end positions for a range of data.
10599 WX_DEFINE_OBJARRAY(wxRichTextRangeArray
);
10601 /// Limit this range to be within 'range'
10602 bool wxRichTextRange::LimitTo(const wxRichTextRange
& range
)
10604 if (m_start
< range
.m_start
)
10605 m_start
= range
.m_start
;
10607 if (m_end
> range
.m_end
)
10608 m_end
= range
.m_end
;
10614 * wxRichTextImage implementation
10615 * This object represents an image.
10618 IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage
, wxRichTextObject
)
10620 wxRichTextImage::wxRichTextImage(const wxImage
& image
, wxRichTextObject
* parent
, wxRichTextAttr
* charStyle
):
10621 wxRichTextObject(parent
)
10624 m_imageBlock
.MakeImageBlockDefaultQuality(image
, wxBITMAP_TYPE_PNG
);
10626 SetAttributes(*charStyle
);
10629 wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock
& imageBlock
, wxRichTextObject
* parent
, wxRichTextAttr
* charStyle
):
10630 wxRichTextObject(parent
)
10633 m_imageBlock
= imageBlock
;
10635 SetAttributes(*charStyle
);
10638 void wxRichTextImage::Init()
10640 m_originalImageSize
= wxSize(-1, -1);
10643 /// Create a cached image at the required size
10644 bool wxRichTextImage::LoadImageCache(wxDC
& dc
, bool resetCache
)
10646 if (!m_imageBlock
.IsOk())
10649 // If we have an original image size, use that to compute the cached bitmap size
10650 // instead of loading the image each time. This way we can avoid loading
10651 // the image so long as the new cached bitmap size hasn't changed.
10654 if (resetCache
|| m_originalImageSize
== wxSize(-1, -1))
10656 m_imageCache
= wxNullBitmap
;
10658 m_imageBlock
.Load(image
);
10662 m_originalImageSize
= wxSize(image
.GetWidth(), image
.GetHeight());
10665 int width
= m_originalImageSize
.GetWidth();
10666 int height
= m_originalImageSize
.GetHeight();
10668 int parentWidth
= 0;
10669 int parentHeight
= 0;
10672 int maxHeight
= -1;
10674 wxRichTextBuffer
* buffer
= GetBuffer();
10678 if (buffer
->GetRichTextCtrl())
10680 // Subtract borders
10681 sz
= buffer
->GetRichTextCtrl()->GetClientSize();
10683 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
10684 marginRect
= wxRect(0, 0, sz
.x
, sz
.y
);
10685 buffer
->GetBoxRects(dc
, buffer
, buffer
->GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
10687 sz
= contentRect
.GetSize();
10689 // Start with a maximum width of the control size, even if not specified by the content,
10690 // to minimize the amount of picture overlapping the right-hand side
10694 sz
= buffer
->GetCachedSize();
10695 parentWidth
= sz
.GetWidth();
10696 parentHeight
= sz
.GetHeight();
10699 if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetWidth().GetValue() > 0)
10701 if (parentWidth
> 0 && GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
10702 width
= (int) ((GetAttributes().GetTextBoxAttr().GetWidth().GetValue() * parentWidth
)/100.0);
10703 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
10704 width
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetWidth().GetValue());
10705 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
10706 width
= GetAttributes().GetTextBoxAttr().GetWidth().GetValue();
10709 // Limit to max width
10711 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() > 0)
10715 if (parentWidth
> 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
10716 mw
= (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() * parentWidth
)/100.0);
10717 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
10718 mw
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue());
10719 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
10720 mw
= GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue();
10722 // If we already have a smaller max width due to the constraints of the control size,
10723 // don't use the larger max width.
10724 if (mw
!= -1 && ((maxWidth
== -1) || (mw
< maxWidth
)))
10728 if (maxWidth
> 0 && width
> maxWidth
)
10731 // Preserve the aspect ratio
10732 if (width
!= m_originalImageSize
.GetWidth())
10733 height
= (int) (float(m_originalImageSize
.GetHeight()) * (float(width
)/float(m_originalImageSize
.GetWidth())));
10735 if (GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetHeight().GetValue() > 0)
10737 if (parentHeight
> 0 && GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
10738 height
= (int) ((GetAttributes().GetTextBoxAttr().GetHeight().GetValue() * parentHeight
)/100.0);
10739 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
10740 height
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetHeight().GetValue());
10741 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
10742 height
= GetAttributes().GetTextBoxAttr().GetHeight().GetValue();
10744 // Preserve the aspect ratio
10745 if (height
!= m_originalImageSize
.GetHeight())
10746 width
= (int) (float(m_originalImageSize
.GetWidth()) * (float(height
)/float(m_originalImageSize
.GetHeight())));
10749 // Limit to max height
10751 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() > 0)
10753 if (parentHeight
> 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
10754 maxHeight
= (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() * parentHeight
)/100.0);
10755 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
10756 maxHeight
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue());
10757 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
10758 maxHeight
= GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue();
10761 if (maxHeight
> 0 && height
> maxHeight
)
10763 height
= maxHeight
;
10765 // Preserve the aspect ratio
10766 if (height
!= m_originalImageSize
.GetHeight())
10767 width
= (int) (float(m_originalImageSize
.GetWidth()) * (float(height
)/float(m_originalImageSize
.GetHeight())));
10770 if (m_imageCache
.IsOk() && m_imageCache
.GetWidth() == width
&& m_imageCache
.GetHeight() == height
)
10772 // Do nothing, we didn't need to change the image cache
10778 m_imageBlock
.Load(image
);
10783 if (image
.GetWidth() == width
&& image
.GetHeight() == height
)
10784 m_imageCache
= wxBitmap(image
);
10787 // If the original width and height is small, e.g. 400 or below,
10788 // scale up and then down to improve image quality. This can make
10789 // a big difference, with not much performance hit.
10790 int upscaleThreshold
= 400;
10792 if (image
.GetWidth() <= upscaleThreshold
|| image
.GetHeight() <= upscaleThreshold
)
10794 img
= image
.Scale(image
.GetWidth()*2, image
.GetHeight()*2);
10795 img
.Rescale(width
, height
, wxIMAGE_QUALITY_HIGH
);
10798 img
= image
.Scale(width
, height
, wxIMAGE_QUALITY_HIGH
);
10799 m_imageCache
= wxBitmap(img
);
10803 return m_imageCache
.IsOk();
10807 bool wxRichTextImage::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& WXUNUSED(range
), const wxRichTextSelection
& selection
, const wxRect
& rect
, int WXUNUSED(descent
), int WXUNUSED(style
))
10812 // Don't need cached size AFAIK
10813 // wxSize size = GetCachedSize();
10814 if (!LoadImageCache(dc
))
10817 wxRichTextAttr
attr(GetAttributes());
10818 context
.ApplyVirtualAttributes(attr
, this);
10820 DrawBoxAttributes(dc
, GetBuffer(), attr
, wxRect(rect
.GetPosition(), GetCachedSize()));
10822 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
10823 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
10824 marginRect
= rect
; // outer rectangle, will calculate contentRect
10825 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
10827 dc
.DrawBitmap(m_imageCache
, contentRect
.x
, contentRect
.y
, true);
10829 if (selection
.WithinSelection(GetRange().GetStart(), this))
10831 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
10832 wxCheckSetPen(dc
, *wxBLACK_PEN
);
10833 dc
.SetLogicalFunction(wxINVERT
);
10834 dc
.DrawRectangle(contentRect
);
10835 dc
.SetLogicalFunction(wxCOPY
);
10841 /// Lay the item out
10842 bool wxRichTextImage::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& WXUNUSED(parentRect
), int WXUNUSED(style
))
10844 if (!LoadImageCache(dc
))
10847 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
10848 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
10849 contentRect
= wxRect(wxPoint(0,0), imageSize
);
10851 wxRichTextAttr
attr(GetAttributes());
10852 context
.ApplyVirtualAttributes(attr
, this);
10854 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
10856 wxSize overallSize
= marginRect
.GetSize();
10858 SetCachedSize(overallSize
);
10859 SetMaxSize(overallSize
);
10860 SetMinSize(overallSize
);
10861 SetPosition(rect
.GetPosition());
10866 /// Get/set the object size for the given range. Returns false if the range
10867 /// is invalid for this object.
10868 bool wxRichTextImage::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& WXUNUSED(descent
), wxDC
& dc
, wxRichTextDrawingContext
& context
, int WXUNUSED(flags
), wxPoint
WXUNUSED(position
), wxArrayInt
* partialExtents
) const
10870 if (!range
.IsWithin(GetRange()))
10873 if (!((wxRichTextImage
*)this)->LoadImageCache(dc
))
10875 size
.x
= 0; size
.y
= 0;
10876 if (partialExtents
)
10877 partialExtents
->Add(0);
10881 wxRichTextAttr
attr(GetAttributes());
10882 context
.ApplyVirtualAttributes(attr
, (wxRichTextObject
*) this);
10884 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
10885 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
10886 contentRect
= wxRect(wxPoint(0,0), imageSize
);
10887 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
10889 wxSize overallSize
= marginRect
.GetSize();
10891 if (partialExtents
)
10892 partialExtents
->Add(overallSize
.x
);
10894 size
= overallSize
;
10899 // Get the 'natural' size for an object. For an image, it would be the
10901 wxTextAttrSize
wxRichTextImage::GetNaturalSize() const
10903 wxTextAttrSize size
;
10904 if (GetImageCache().IsOk())
10906 size
.SetWidth(GetImageCache().GetWidth(), wxTEXT_ATTR_UNITS_PIXELS
);
10907 size
.SetHeight(GetImageCache().GetHeight(), wxTEXT_ATTR_UNITS_PIXELS
);
10914 void wxRichTextImage::Copy(const wxRichTextImage
& obj
)
10916 wxRichTextObject::Copy(obj
);
10918 m_imageBlock
= obj
.m_imageBlock
;
10919 m_originalImageSize
= obj
.m_originalImageSize
;
10922 /// Edit properties via a GUI
10923 bool wxRichTextImage::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
10925 wxRichTextObjectPropertiesDialog
imageDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Picture Properties"));
10926 imageDlg
.SetAttributes(GetAttributes());
10928 if (imageDlg
.ShowModal() == wxID_OK
)
10930 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
10931 // indeterminate in the object.
10932 imageDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
10944 /// Compare two attribute objects
10945 bool wxTextAttrEq(const wxRichTextAttr
& attr1
, const wxRichTextAttr
& attr2
)
10947 return (attr1
== attr2
);
10951 bool wxRichTextTabsEq(const wxArrayInt
& tabs1
, const wxArrayInt
& tabs2
)
10953 if (tabs1
.GetCount() != tabs2
.GetCount())
10957 for (i
= 0; i
< tabs1
.GetCount(); i
++)
10959 if (tabs1
[i
] != tabs2
[i
])
10965 bool wxRichTextApplyStyle(wxRichTextAttr
& destStyle
, const wxRichTextAttr
& style
, wxRichTextAttr
* compareWith
)
10967 return destStyle
.Apply(style
, compareWith
);
10970 // Remove attributes
10971 bool wxRichTextRemoveStyle(wxRichTextAttr
& destStyle
, const wxRichTextAttr
& style
)
10973 return destStyle
.RemoveStyle(style
);
10976 /// Combine two bitlists, specifying the bits of interest with separate flags.
10977 bool wxRichTextCombineBitlists(int& valueA
, int valueB
, int& flagsA
, int flagsB
)
10979 return wxRichTextAttr::CombineBitlists(valueA
, valueB
, flagsA
, flagsB
);
10982 /// Compare two bitlists
10983 bool wxRichTextBitlistsEqPartial(int valueA
, int valueB
, int flags
)
10985 return wxRichTextAttr::BitlistsEqPartial(valueA
, valueB
, flags
);
10988 /// Split into paragraph and character styles
10989 bool wxRichTextSplitParaCharStyles(const wxRichTextAttr
& style
, wxRichTextAttr
& parStyle
, wxRichTextAttr
& charStyle
)
10991 return wxRichTextAttr::SplitParaCharStyles(style
, parStyle
, charStyle
);
10994 /// Convert a decimal to Roman numerals
10995 wxString
wxRichTextDecimalToRoman(long n
)
10997 static wxArrayInt decimalNumbers
;
10998 static wxArrayString romanNumbers
;
11003 decimalNumbers
.Clear();
11004 romanNumbers
.Clear();
11005 return wxEmptyString
;
11008 if (decimalNumbers
.GetCount() == 0)
11010 #define wxRichTextAddDecRom(n, r) decimalNumbers.Add(n); romanNumbers.Add(r);
11012 wxRichTextAddDecRom(1000, wxT("M"));
11013 wxRichTextAddDecRom(900, wxT("CM"));
11014 wxRichTextAddDecRom(500, wxT("D"));
11015 wxRichTextAddDecRom(400, wxT("CD"));
11016 wxRichTextAddDecRom(100, wxT("C"));
11017 wxRichTextAddDecRom(90, wxT("XC"));
11018 wxRichTextAddDecRom(50, wxT("L"));
11019 wxRichTextAddDecRom(40, wxT("XL"));
11020 wxRichTextAddDecRom(10, wxT("X"));
11021 wxRichTextAddDecRom(9, wxT("IX"));
11022 wxRichTextAddDecRom(5, wxT("V"));
11023 wxRichTextAddDecRom(4, wxT("IV"));
11024 wxRichTextAddDecRom(1, wxT("I"));
11030 while (n
> 0 && i
< 13)
11032 if (n
>= decimalNumbers
[i
])
11034 n
-= decimalNumbers
[i
];
11035 roman
+= romanNumbers
[i
];
11042 if (roman
.IsEmpty())
11048 * wxRichTextFileHandler
11049 * Base class for file handlers
11052 IMPLEMENT_CLASS(wxRichTextFileHandler
, wxObject
)
11054 #if wxUSE_FFILE && wxUSE_STREAMS
11055 bool wxRichTextFileHandler::LoadFile(wxRichTextBuffer
*buffer
, const wxString
& filename
)
11057 wxFFileInputStream
stream(filename
);
11059 return LoadFile(buffer
, stream
);
11064 bool wxRichTextFileHandler::SaveFile(wxRichTextBuffer
*buffer
, const wxString
& filename
)
11066 wxFFileOutputStream
stream(filename
);
11068 return SaveFile(buffer
, stream
);
11072 #endif // wxUSE_FFILE && wxUSE_STREAMS
11074 /// Can we handle this filename (if using files)? By default, checks the extension.
11075 bool wxRichTextFileHandler::CanHandle(const wxString
& filename
) const
11077 wxString path
, file
, ext
;
11078 wxFileName::SplitPath(filename
, & path
, & file
, & ext
);
11080 return (ext
.Lower() == GetExtension());
11084 * wxRichTextTextHandler
11085 * Plain text handler
11088 IMPLEMENT_CLASS(wxRichTextPlainTextHandler
, wxRichTextFileHandler
)
11091 bool wxRichTextPlainTextHandler::DoLoadFile(wxRichTextBuffer
*buffer
, wxInputStream
& stream
)
11093 if (!stream
.IsOk())
11099 while (!stream
.Eof())
11101 int ch
= stream
.GetC();
11105 if (ch
== 10 && lastCh
!= 13)
11108 if (ch
> 0 && ch
!= 10)
11115 buffer
->ResetAndClearCommands();
11117 buffer
->AddParagraphs(str
);
11118 buffer
->UpdateRanges();
11123 bool wxRichTextPlainTextHandler::DoSaveFile(wxRichTextBuffer
*buffer
, wxOutputStream
& stream
)
11125 if (!stream
.IsOk())
11128 wxString text
= buffer
->GetText();
11130 wxString newLine
= wxRichTextLineBreakChar
;
11131 text
.Replace(newLine
, wxT("\n"));
11133 wxCharBuffer buf
= text
.ToAscii();
11135 stream
.Write((const char*) buf
, text
.length());
11138 #endif // wxUSE_STREAMS
11141 * Stores information about an image, in binary in-memory form
11144 wxRichTextImageBlock::wxRichTextImageBlock()
11149 wxRichTextImageBlock::wxRichTextImageBlock(const wxRichTextImageBlock
& block
):wxObject()
11155 wxRichTextImageBlock::~wxRichTextImageBlock()
11160 void wxRichTextImageBlock::Init()
11164 m_imageType
= wxBITMAP_TYPE_INVALID
;
11167 void wxRichTextImageBlock::Clear()
11171 m_imageType
= wxBITMAP_TYPE_INVALID
;
11175 // Load the original image into a memory block.
11176 // If the image is not a JPEG, we must convert it into a JPEG
11177 // to conserve space.
11178 // If it's not a JPEG we can make use of 'image', already scaled, so we don't have to
11179 // load the image a 2nd time.
11181 bool wxRichTextImageBlock::MakeImageBlock(const wxString
& filename
, wxBitmapType imageType
,
11182 wxImage
& image
, bool convertToJPEG
)
11184 m_imageType
= imageType
;
11186 wxString
filenameToRead(filename
);
11187 bool removeFile
= false;
11189 if (imageType
== wxBITMAP_TYPE_INVALID
)
11190 return false; // Could not determine image type
11192 if ((imageType
!= wxBITMAP_TYPE_JPEG
) && convertToJPEG
)
11194 wxString tempFile
=
11195 wxFileName::CreateTempFileName(_("image"));
11197 wxASSERT(!tempFile
.IsEmpty());
11199 image
.SaveFile(tempFile
, wxBITMAP_TYPE_JPEG
);
11200 filenameToRead
= tempFile
;
11203 m_imageType
= wxBITMAP_TYPE_JPEG
;
11206 if (!file
.Open(filenameToRead
))
11209 m_dataSize
= (size_t) file
.Length();
11214 m_data
= ReadBlock(filenameToRead
, m_dataSize
);
11217 wxRemoveFile(filenameToRead
);
11219 return (m_data
!= NULL
);
11222 // Make an image block from the wxImage in the given
11224 bool wxRichTextImageBlock::MakeImageBlock(wxImage
& image
, wxBitmapType imageType
, int quality
)
11226 image
.SetOption(wxT("quality"), quality
);
11228 if (imageType
== wxBITMAP_TYPE_INVALID
)
11229 return false; // Could not determine image type
11231 return DoMakeImageBlock(image
, imageType
);
11234 // Uses a const wxImage for efficiency, but can't set quality (only relevant for JPEG)
11235 bool wxRichTextImageBlock::MakeImageBlockDefaultQuality(const wxImage
& image
, wxBitmapType imageType
)
11237 if (imageType
== wxBITMAP_TYPE_INVALID
)
11238 return false; // Could not determine image type
11240 return DoMakeImageBlock(image
, imageType
);
11243 // Makes the image block
11244 bool wxRichTextImageBlock::DoMakeImageBlock(const wxImage
& image
, wxBitmapType imageType
)
11246 wxMemoryOutputStream memStream
;
11247 if (!image
.SaveFile(memStream
, imageType
))
11252 unsigned char* block
= new unsigned char[memStream
.GetSize()];
11260 m_imageType
= imageType
;
11261 m_dataSize
= memStream
.GetSize();
11263 memStream
.CopyTo(m_data
, m_dataSize
);
11265 return (m_data
!= NULL
);
11269 bool wxRichTextImageBlock::Write(const wxString
& filename
)
11271 return WriteBlock(filename
, m_data
, m_dataSize
);
11274 void wxRichTextImageBlock::Copy(const wxRichTextImageBlock
& block
)
11276 m_imageType
= block
.m_imageType
;
11278 m_dataSize
= block
.m_dataSize
;
11279 if (m_dataSize
== 0)
11282 m_data
= new unsigned char[m_dataSize
];
11284 for (i
= 0; i
< m_dataSize
; i
++)
11285 m_data
[i
] = block
.m_data
[i
];
11289 void wxRichTextImageBlock::operator=(const wxRichTextImageBlock
& block
)
11294 // Load a wxImage from the block
11295 bool wxRichTextImageBlock::Load(wxImage
& image
)
11300 // Read in the image.
11302 wxMemoryInputStream
mstream(m_data
, m_dataSize
);
11303 bool success
= image
.LoadFile(mstream
, GetImageType());
11305 wxString tempFile
= wxFileName::CreateTempFileName(_("image"));
11306 wxASSERT(!tempFile
.IsEmpty());
11308 if (!WriteBlock(tempFile
, m_data
, m_dataSize
))
11312 success
= image
.LoadFile(tempFile
, GetImageType());
11313 wxRemoveFile(tempFile
);
11319 // Write data in hex to a stream
11320 bool wxRichTextImageBlock::WriteHex(wxOutputStream
& stream
)
11322 if (m_dataSize
== 0)
11325 int bufSize
= 100000;
11326 if (int(2*m_dataSize
) < bufSize
)
11327 bufSize
= 2*m_dataSize
;
11328 char* buf
= new char[bufSize
+1];
11330 int left
= m_dataSize
;
11335 if (left
*2 > bufSize
)
11337 n
= bufSize
; left
-= (bufSize
/2);
11341 n
= left
*2; left
= 0;
11345 for (i
= 0; i
< (n
/2); i
++)
11347 wxDecToHex(m_data
[j
], b
, b
+1);
11352 stream
.Write((const char*) buf
, n
);
11358 // Read data in hex from a stream
11359 bool wxRichTextImageBlock::ReadHex(wxInputStream
& stream
, int length
, wxBitmapType imageType
)
11361 int dataSize
= length
/2;
11366 // create a null terminated temporary string:
11370 m_data
= new unsigned char[dataSize
];
11372 for (i
= 0; i
< dataSize
; i
++)
11374 str
[0] = (char)stream
.GetC();
11375 str
[1] = (char)stream
.GetC();
11377 m_data
[i
] = (unsigned char)wxHexToDec(str
);
11380 m_dataSize
= dataSize
;
11381 m_imageType
= imageType
;
11386 // Allocate and read from stream as a block of memory
11387 unsigned char* wxRichTextImageBlock::ReadBlock(wxInputStream
& stream
, size_t size
)
11389 unsigned char* block
= new unsigned char[size
];
11393 stream
.Read(block
, size
);
11398 unsigned char* wxRichTextImageBlock::ReadBlock(const wxString
& filename
, size_t size
)
11400 wxFileInputStream
stream(filename
);
11401 if (!stream
.IsOk())
11404 return ReadBlock(stream
, size
);
11407 // Write memory block to stream
11408 bool wxRichTextImageBlock::WriteBlock(wxOutputStream
& stream
, unsigned char* block
, size_t size
)
11410 stream
.Write((void*) block
, size
);
11411 return stream
.IsOk();
11415 // Write memory block to file
11416 bool wxRichTextImageBlock::WriteBlock(const wxString
& filename
, unsigned char* block
, size_t size
)
11418 wxFileOutputStream
outStream(filename
);
11419 if (!outStream
.IsOk())
11422 return WriteBlock(outStream
, block
, size
);
11425 // Gets the extension for the block's type
11426 wxString
wxRichTextImageBlock::GetExtension() const
11428 wxImageHandler
* handler
= wxImage::FindHandler(GetImageType());
11430 return handler
->GetExtension();
11432 return wxEmptyString
;
11438 * The data object for a wxRichTextBuffer
11441 const wxChar
*wxRichTextBufferDataObject::ms_richTextBufferFormatId
= wxT("wxShape");
11443 wxRichTextBufferDataObject::wxRichTextBufferDataObject(wxRichTextBuffer
* richTextBuffer
)
11445 m_richTextBuffer
= richTextBuffer
;
11447 // this string should uniquely identify our format, but is otherwise
11449 m_formatRichTextBuffer
.SetId(GetRichTextBufferFormatId());
11451 SetFormat(m_formatRichTextBuffer
);
11454 wxRichTextBufferDataObject::~wxRichTextBufferDataObject()
11456 delete m_richTextBuffer
;
11459 // after a call to this function, the richTextBuffer is owned by the caller and it
11460 // is responsible for deleting it!
11461 wxRichTextBuffer
* wxRichTextBufferDataObject::GetRichTextBuffer()
11463 wxRichTextBuffer
* richTextBuffer
= m_richTextBuffer
;
11464 m_richTextBuffer
= NULL
;
11466 return richTextBuffer
;
11469 wxDataFormat
wxRichTextBufferDataObject::GetPreferredFormat(Direction
WXUNUSED(dir
)) const
11471 return m_formatRichTextBuffer
;
11474 size_t wxRichTextBufferDataObject::GetDataSize() const
11476 if (!m_richTextBuffer
)
11482 wxStringOutputStream
stream(& bufXML
);
11483 if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
))
11485 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
11491 wxCharBuffer buffer
= bufXML
.mb_str(wxConvUTF8
);
11492 return strlen(buffer
) + 1;
11494 return bufXML
.Length()+1;
11498 bool wxRichTextBufferDataObject::GetDataHere(void *pBuf
) const
11500 if (!pBuf
|| !m_richTextBuffer
)
11506 wxStringOutputStream
stream(& bufXML
);
11507 if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
))
11509 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
11515 wxCharBuffer buffer
= bufXML
.mb_str(wxConvUTF8
);
11516 size_t len
= strlen(buffer
);
11517 memcpy((char*) pBuf
, (const char*) buffer
, len
);
11518 ((char*) pBuf
)[len
] = 0;
11520 size_t len
= bufXML
.Length();
11521 memcpy((char*) pBuf
, (const char*) bufXML
.c_str(), len
);
11522 ((char*) pBuf
)[len
] = 0;
11528 bool wxRichTextBufferDataObject::SetData(size_t WXUNUSED(len
), const void *buf
)
11530 wxDELETE(m_richTextBuffer
);
11532 wxString
bufXML((const char*) buf
, wxConvUTF8
);
11534 m_richTextBuffer
= new wxRichTextBuffer
;
11536 wxStringInputStream
stream(bufXML
);
11537 if (!m_richTextBuffer
->LoadFile(stream
, wxRICHTEXT_TYPE_XML
))
11539 wxLogError(wxT("Could not read the buffer from an XML stream.\nYou may have forgotten to add the XML file handler."));
11541 wxDELETE(m_richTextBuffer
);
11553 * wxRichTextFontTable
11554 * Manages quick access to a pool of fonts for rendering rich text
11557 WX_DECLARE_STRING_HASH_MAP_WITH_DECL(wxFont
, wxRichTextFontTableHashMap
, class WXDLLIMPEXP_RICHTEXT
);
11559 class wxRichTextFontTableData
: public wxObjectRefData
11562 wxRichTextFontTableData() {}
11564 wxFont
FindFont(const wxRichTextAttr
& fontSpec
, double fontScale
);
11566 wxRichTextFontTableHashMap m_hashMap
;
11569 wxFont
wxRichTextFontTableData::FindFont(const wxRichTextAttr
& fontSpec
, double fontScale
)
11571 wxString
facename(fontSpec
.GetFontFaceName());
11573 int fontSize
= fontSpec
.GetFontSize();
11574 if (fontScale
!= 1.0)
11575 fontSize
= (int) ((double(fontSize
) * fontScale
) + 0.5);
11578 if (fontSpec
.HasFontPixelSize() && !fontSpec
.HasFontPointSize())
11582 wxString spec
= wxString::Format(wxT("%d-%s-%d-%d-%d-%d-%s-%d"),
11583 fontSize
, units
.c_str(), fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), (int) fontSpec
.GetFontUnderlined(), (int) fontSpec
.GetFontStrikethrough(),
11584 facename
.c_str(), (int) fontSpec
.GetFontEncoding());
11586 wxRichTextFontTableHashMap::iterator entry
= m_hashMap
.find(spec
);
11587 if ( entry
== m_hashMap
.end() )
11589 if (fontSpec
.HasFontPixelSize() && !fontSpec
.HasFontPointSize())
11591 wxFont
font(wxSize(0, fontSize
), wxFONTFAMILY_DEFAULT
, fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), fontSpec
.GetFontUnderlined(), facename
);
11592 if (fontSpec
.HasFontStrikethrough() && fontSpec
.GetFontStrikethrough())
11593 font
.SetStrikethrough(true);
11594 m_hashMap
[spec
] = font
;
11599 wxFont
font(fontSize
, wxFONTFAMILY_DEFAULT
, fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), fontSpec
.GetFontUnderlined(), facename
.c_str());
11600 if (fontSpec
.HasFontStrikethrough() && fontSpec
.GetFontStrikethrough())
11601 font
.SetStrikethrough(true);
11603 m_hashMap
[spec
] = font
;
11609 return entry
->second
;
11613 IMPLEMENT_DYNAMIC_CLASS(wxRichTextFontTable
, wxObject
)
11615 wxRichTextFontTable::wxRichTextFontTable()
11617 m_refData
= new wxRichTextFontTableData
;
11621 wxRichTextFontTable::wxRichTextFontTable(const wxRichTextFontTable
& table
)
11627 wxRichTextFontTable::~wxRichTextFontTable()
11632 bool wxRichTextFontTable::operator == (const wxRichTextFontTable
& table
) const
11634 return (m_refData
== table
.m_refData
);
11637 void wxRichTextFontTable::operator= (const wxRichTextFontTable
& table
)
11640 m_fontScale
= table
.m_fontScale
;
11643 wxFont
wxRichTextFontTable::FindFont(const wxRichTextAttr
& fontSpec
)
11645 wxRichTextFontTableData
* data
= (wxRichTextFontTableData
*) m_refData
;
11647 return data
->FindFont(fontSpec
, m_fontScale
);
11652 void wxRichTextFontTable::Clear()
11654 wxRichTextFontTableData
* data
= (wxRichTextFontTableData
*) m_refData
;
11656 data
->m_hashMap
.clear();
11659 void wxRichTextFontTable::SetFontScale(double fontScale
)
11661 if (fontScale
!= m_fontScale
)
11663 m_fontScale
= fontScale
;
11668 void wxTextBoxAttr::Reset()
11671 m_floatMode
= wxTEXT_BOX_ATTR_FLOAT_NONE
;
11672 m_clearMode
= wxTEXT_BOX_ATTR_CLEAR_NONE
;
11673 m_collapseMode
= wxTEXT_BOX_ATTR_COLLAPSE_NONE
;
11674 m_verticalAlignment
= wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_NONE
;
11675 m_boxStyleName
= wxEmptyString
;
11679 m_position
.Reset();
11690 bool wxTextBoxAttr::operator== (const wxTextBoxAttr
& attr
) const
11693 m_flags
== attr
.m_flags
&&
11694 m_floatMode
== attr
.m_floatMode
&&
11695 m_clearMode
== attr
.m_clearMode
&&
11696 m_collapseMode
== attr
.m_collapseMode
&&
11697 m_verticalAlignment
== attr
.m_verticalAlignment
&&
11699 m_margins
== attr
.m_margins
&&
11700 m_padding
== attr
.m_padding
&&
11701 m_position
== attr
.m_position
&&
11703 m_size
== attr
.m_size
&&
11704 m_minSize
== attr
.m_minSize
&&
11705 m_maxSize
== attr
.m_maxSize
&&
11707 m_border
== attr
.m_border
&&
11708 m_outline
== attr
.m_outline
&&
11710 m_boxStyleName
== attr
.m_boxStyleName
11714 // Partial equality test
11715 bool wxTextBoxAttr::EqPartial(const wxTextBoxAttr
& attr
, bool weakTest
) const
11718 ((!HasFloatMode() && attr
.HasFloatMode()) ||
11719 (!HasClearMode() && attr
.HasClearMode()) ||
11720 (!HasCollapseBorders() && attr
.HasCollapseBorders()) ||
11721 (!HasVerticalAlignment() && attr
.HasVerticalAlignment()) ||
11722 (!HasBoxStyleName() && attr
.HasBoxStyleName())))
11726 if (attr
.HasFloatMode() && HasFloatMode() && (GetFloatMode() != attr
.GetFloatMode()))
11729 if (attr
.HasClearMode() && HasClearMode() && (GetClearMode() != attr
.GetClearMode()))
11732 if (attr
.HasCollapseBorders() && HasCollapseBorders() && (attr
.GetCollapseBorders() != GetCollapseBorders()))
11735 if (attr
.HasVerticalAlignment() && HasVerticalAlignment() && (attr
.GetVerticalAlignment() != GetVerticalAlignment()))
11738 if (attr
.HasBoxStyleName() && HasBoxStyleName() && (attr
.GetBoxStyleName() != GetBoxStyleName()))
11743 if (!m_position
.EqPartial(attr
.m_position
, weakTest
))
11748 if (!m_size
.EqPartial(attr
.m_size
, weakTest
))
11750 if (!m_minSize
.EqPartial(attr
.m_minSize
, weakTest
))
11752 if (!m_maxSize
.EqPartial(attr
.m_maxSize
, weakTest
))
11757 if (!m_margins
.EqPartial(attr
.m_margins
, weakTest
))
11762 if (!m_padding
.EqPartial(attr
.m_padding
, weakTest
))
11767 if (!GetBorder().EqPartial(attr
.GetBorder(), weakTest
))
11772 if (!GetOutline().EqPartial(attr
.GetOutline(), weakTest
))
11778 // Merges the given attributes. If compareWith
11779 // is non-NULL, then it will be used to mask out those attributes that are the same in style
11780 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
11781 bool wxTextBoxAttr::Apply(const wxTextBoxAttr
& attr
, const wxTextBoxAttr
* compareWith
)
11783 if (attr
.HasFloatMode())
11785 if (!(compareWith
&& compareWith
->HasFloatMode() && compareWith
->GetFloatMode() == attr
.GetFloatMode()))
11786 SetFloatMode(attr
.GetFloatMode());
11789 if (attr
.HasClearMode())
11791 if (!(compareWith
&& compareWith
->HasClearMode() && compareWith
->GetClearMode() == attr
.GetClearMode()))
11792 SetClearMode(attr
.GetClearMode());
11795 if (attr
.HasCollapseBorders())
11797 if (!(compareWith
&& compareWith
->HasCollapseBorders() && compareWith
->GetCollapseBorders() == attr
.GetCollapseBorders()))
11798 SetCollapseBorders(attr
.GetCollapseBorders());
11801 if (attr
.HasVerticalAlignment())
11803 if (!(compareWith
&& compareWith
->HasVerticalAlignment() && compareWith
->GetVerticalAlignment() == attr
.GetVerticalAlignment()))
11804 SetVerticalAlignment(attr
.GetVerticalAlignment());
11807 if (attr
.HasBoxStyleName())
11809 if (!(compareWith
&& compareWith
->HasBoxStyleName() && compareWith
->GetBoxStyleName() == attr
.GetBoxStyleName()))
11810 SetBoxStyleName(attr
.GetBoxStyleName());
11813 m_margins
.Apply(attr
.m_margins
, compareWith
? (& attr
.m_margins
) : (const wxTextAttrDimensions
*) NULL
);
11814 m_padding
.Apply(attr
.m_padding
, compareWith
? (& attr
.m_padding
) : (const wxTextAttrDimensions
*) NULL
);
11815 m_position
.Apply(attr
.m_position
, compareWith
? (& attr
.m_position
) : (const wxTextAttrDimensions
*) NULL
);
11817 m_size
.Apply(attr
.m_size
, compareWith
? (& attr
.m_size
) : (const wxTextAttrSize
*) NULL
);
11818 m_minSize
.Apply(attr
.m_minSize
, compareWith
? (& attr
.m_minSize
) : (const wxTextAttrSize
*) NULL
);
11819 m_maxSize
.Apply(attr
.m_maxSize
, compareWith
? (& attr
.m_maxSize
) : (const wxTextAttrSize
*) NULL
);
11821 m_border
.Apply(attr
.m_border
, compareWith
? (& attr
.m_border
) : (const wxTextAttrBorders
*) NULL
);
11822 m_outline
.Apply(attr
.m_outline
, compareWith
? (& attr
.m_outline
) : (const wxTextAttrBorders
*) NULL
);
11827 // Remove specified attributes from this object
11828 bool wxTextBoxAttr::RemoveStyle(const wxTextBoxAttr
& attr
)
11830 if (attr
.HasFloatMode())
11831 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT
);
11833 if (attr
.HasClearMode())
11834 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR
);
11836 if (attr
.HasCollapseBorders())
11837 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
11839 if (attr
.HasVerticalAlignment())
11840 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
11842 if (attr
.HasBoxStyleName())
11844 SetBoxStyleName(wxEmptyString
);
11845 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
11848 m_margins
.RemoveStyle(attr
.m_margins
);
11849 m_padding
.RemoveStyle(attr
.m_padding
);
11850 m_position
.RemoveStyle(attr
.m_position
);
11852 m_size
.RemoveStyle(attr
.m_size
);
11853 m_minSize
.RemoveStyle(attr
.m_minSize
);
11854 m_maxSize
.RemoveStyle(attr
.m_maxSize
);
11856 m_border
.RemoveStyle(attr
.m_border
);
11857 m_outline
.RemoveStyle(attr
.m_outline
);
11862 // Collects the attributes that are common to a range of content, building up a note of
11863 // which attributes are absent in some objects and which clash in some objects.
11864 void wxTextBoxAttr::CollectCommonAttributes(const wxTextBoxAttr
& attr
, wxTextBoxAttr
& clashingAttr
, wxTextBoxAttr
& absentAttr
)
11866 if (attr
.HasFloatMode())
11868 if (!clashingAttr
.HasFloatMode() && !absentAttr
.HasFloatMode())
11870 if (HasFloatMode())
11872 if (GetFloatMode() != attr
.GetFloatMode())
11874 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_FLOAT
);
11875 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT
);
11879 SetFloatMode(attr
.GetFloatMode());
11883 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_FLOAT
);
11885 if (attr
.HasClearMode())
11887 if (!clashingAttr
.HasClearMode() && !absentAttr
.HasClearMode())
11889 if (HasClearMode())
11891 if (GetClearMode() != attr
.GetClearMode())
11893 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_CLEAR
);
11894 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR
);
11898 SetClearMode(attr
.GetClearMode());
11902 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_CLEAR
);
11904 if (attr
.HasCollapseBorders())
11906 if (!clashingAttr
.HasCollapseBorders() && !absentAttr
.HasCollapseBorders())
11908 if (HasCollapseBorders())
11910 if (GetCollapseBorders() != attr
.GetCollapseBorders())
11912 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
11913 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
11917 SetCollapseBorders(attr
.GetCollapseBorders());
11921 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
11923 if (attr
.HasVerticalAlignment())
11925 if (!clashingAttr
.HasVerticalAlignment() && !absentAttr
.HasVerticalAlignment())
11927 if (HasVerticalAlignment())
11929 if (GetVerticalAlignment() != attr
.GetVerticalAlignment())
11931 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
11932 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
11936 SetVerticalAlignment(attr
.GetVerticalAlignment());
11940 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
11942 if (attr
.HasBoxStyleName())
11944 if (!clashingAttr
.HasBoxStyleName() && !absentAttr
.HasBoxStyleName())
11946 if (HasBoxStyleName())
11948 if (GetBoxStyleName() != attr
.GetBoxStyleName())
11950 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
11951 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
11955 SetBoxStyleName(attr
.GetBoxStyleName());
11959 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
11961 m_margins
.CollectCommonAttributes(attr
.m_margins
, clashingAttr
.m_margins
, absentAttr
.m_margins
);
11962 m_padding
.CollectCommonAttributes(attr
.m_padding
, clashingAttr
.m_padding
, absentAttr
.m_padding
);
11963 m_position
.CollectCommonAttributes(attr
.m_position
, clashingAttr
.m_position
, absentAttr
.m_position
);
11965 m_size
.CollectCommonAttributes(attr
.m_size
, clashingAttr
.m_size
, absentAttr
.m_size
);
11966 m_minSize
.CollectCommonAttributes(attr
.m_minSize
, clashingAttr
.m_minSize
, absentAttr
.m_minSize
);
11967 m_maxSize
.CollectCommonAttributes(attr
.m_maxSize
, clashingAttr
.m_maxSize
, absentAttr
.m_maxSize
);
11969 m_border
.CollectCommonAttributes(attr
.m_border
, clashingAttr
.m_border
, absentAttr
.m_border
);
11970 m_outline
.CollectCommonAttributes(attr
.m_outline
, clashingAttr
.m_outline
, absentAttr
.m_outline
);
11973 bool wxTextBoxAttr::IsDefault() const
11975 return GetFlags() == 0 && !m_border
.IsValid() && !m_outline
.IsValid() &&
11976 !m_size
.IsValid() && !m_minSize
.IsValid() && !m_maxSize
.IsValid() &&
11977 !m_position
.IsValid() && !m_padding
.IsValid() && !m_margins
.IsValid();
11982 void wxRichTextAttr::Copy(const wxRichTextAttr
& attr
)
11984 wxTextAttr::Copy(attr
);
11986 m_textBoxAttr
= attr
.m_textBoxAttr
;
11989 bool wxRichTextAttr::operator==(const wxRichTextAttr
& attr
) const
11991 if (!(wxTextAttr::operator==(attr
)))
11994 return (m_textBoxAttr
== attr
.m_textBoxAttr
);
11997 // Partial equality test
11998 bool wxRichTextAttr::EqPartial(const wxRichTextAttr
& attr
, bool weakTest
) const
12000 if (!(wxTextAttr::EqPartial(attr
, weakTest
)))
12003 return m_textBoxAttr
.EqPartial(attr
.m_textBoxAttr
, weakTest
);
12006 // Merges the given attributes. If compareWith
12007 // is non-NULL, then it will be used to mask out those attributes that are the same in style
12008 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
12009 bool wxRichTextAttr::Apply(const wxRichTextAttr
& style
, const wxRichTextAttr
* compareWith
)
12011 wxTextAttr::Apply(style
, compareWith
);
12013 return m_textBoxAttr
.Apply(style
.m_textBoxAttr
, compareWith
? (& compareWith
->m_textBoxAttr
) : (const wxTextBoxAttr
*) NULL
);
12016 // Remove specified attributes from this object
12017 bool wxRichTextAttr::RemoveStyle(const wxRichTextAttr
& attr
)
12019 wxTextAttr::RemoveStyle(*this, attr
);
12021 return m_textBoxAttr
.RemoveStyle(attr
.m_textBoxAttr
);
12024 // Collects the attributes that are common to a range of content, building up a note of
12025 // which attributes are absent in some objects and which clash in some objects.
12026 void wxRichTextAttr::CollectCommonAttributes(const wxRichTextAttr
& attr
, wxRichTextAttr
& clashingAttr
, wxRichTextAttr
& absentAttr
)
12028 wxTextAttrCollectCommonAttributes(*this, attr
, clashingAttr
, absentAttr
);
12030 m_textBoxAttr
.CollectCommonAttributes(attr
.m_textBoxAttr
, clashingAttr
.m_textBoxAttr
, absentAttr
.m_textBoxAttr
);
12033 // Partial equality test
12034 bool wxTextAttrBorder::EqPartial(const wxTextAttrBorder
& border
, bool weakTest
) const
12037 ((!HasStyle() && border
.HasStyle()) ||
12038 (!HasColour() && border
.HasColour()) ||
12039 (!HasWidth() && border
.HasWidth())))
12044 if (border
.HasStyle() && HasStyle() && (border
.GetStyle() != GetStyle()))
12047 if (border
.HasColour() && HasColour() && (border
.GetColourLong() != GetColourLong()))
12050 if (border
.HasWidth() && HasWidth() && !(border
.GetWidth() == GetWidth()))
12056 // Apply border to 'this', but not if the same as compareWith
12057 bool wxTextAttrBorder::Apply(const wxTextAttrBorder
& border
, const wxTextAttrBorder
* compareWith
)
12059 if (border
.HasStyle())
12061 if (!(compareWith
&& (border
.GetStyle() == compareWith
->GetStyle())))
12062 SetStyle(border
.GetStyle());
12064 if (border
.HasColour())
12066 if (!(compareWith
&& (border
.GetColourLong() == compareWith
->GetColourLong())))
12067 SetColour(border
.GetColourLong());
12069 if (border
.HasWidth())
12071 if (!(compareWith
&& (border
.GetWidth() == compareWith
->GetWidth())))
12072 SetWidth(border
.GetWidth());
12078 // Remove specified attributes from this object
12079 bool wxTextAttrBorder::RemoveStyle(const wxTextAttrBorder
& attr
)
12081 if (attr
.HasStyle() && HasStyle())
12082 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_STYLE
);
12083 if (attr
.HasColour() && HasColour())
12084 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_COLOUR
);
12085 if (attr
.HasWidth() && HasWidth())
12086 m_borderWidth
.Reset();
12091 // Collects the attributes that are common to a range of content, building up a note of
12092 // which attributes are absent in some objects and which clash in some objects.
12093 void wxTextAttrBorder::CollectCommonAttributes(const wxTextAttrBorder
& attr
, wxTextAttrBorder
& clashingAttr
, wxTextAttrBorder
& absentAttr
)
12095 if (attr
.HasStyle())
12097 if (!clashingAttr
.HasStyle() && !absentAttr
.HasStyle())
12101 if (GetStyle() != attr
.GetStyle())
12103 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
12104 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
12108 SetStyle(attr
.GetStyle());
12112 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
12114 if (attr
.HasColour())
12116 if (!clashingAttr
.HasColour() && !absentAttr
.HasColour())
12120 if (GetColour() != attr
.GetColour())
12122 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
12123 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
12127 SetColour(attr
.GetColourLong());
12131 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
12133 m_borderWidth
.CollectCommonAttributes(attr
.m_borderWidth
, clashingAttr
.m_borderWidth
, absentAttr
.m_borderWidth
);
12136 // Partial equality test
12137 bool wxTextAttrBorders::EqPartial(const wxTextAttrBorders
& borders
, bool weakTest
) const
12139 return m_left
.EqPartial(borders
.m_left
, weakTest
) && m_right
.EqPartial(borders
.m_right
, weakTest
) &&
12140 m_top
.EqPartial(borders
.m_top
, weakTest
) && m_bottom
.EqPartial(borders
.m_bottom
, weakTest
);
12143 // Apply border to 'this', but not if the same as compareWith
12144 bool wxTextAttrBorders::Apply(const wxTextAttrBorders
& borders
, const wxTextAttrBorders
* compareWith
)
12146 m_left
.Apply(borders
.m_left
, compareWith
? (& compareWith
->m_left
) : (const wxTextAttrBorder
*) NULL
);
12147 m_right
.Apply(borders
.m_right
, compareWith
? (& compareWith
->m_right
) : (const wxTextAttrBorder
*) NULL
);
12148 m_top
.Apply(borders
.m_top
, compareWith
? (& compareWith
->m_top
) : (const wxTextAttrBorder
*) NULL
);
12149 m_bottom
.Apply(borders
.m_bottom
, compareWith
? (& compareWith
->m_bottom
) : (const wxTextAttrBorder
*) NULL
);
12153 // Remove specified attributes from this object
12154 bool wxTextAttrBorders::RemoveStyle(const wxTextAttrBorders
& attr
)
12156 m_left
.RemoveStyle(attr
.m_left
);
12157 m_right
.RemoveStyle(attr
.m_right
);
12158 m_top
.RemoveStyle(attr
.m_top
);
12159 m_bottom
.RemoveStyle(attr
.m_bottom
);
12163 // Collects the attributes that are common to a range of content, building up a note of
12164 // which attributes are absent in some objects and which clash in some objects.
12165 void wxTextAttrBorders::CollectCommonAttributes(const wxTextAttrBorders
& attr
, wxTextAttrBorders
& clashingAttr
, wxTextAttrBorders
& absentAttr
)
12167 m_left
.CollectCommonAttributes(attr
.m_left
, clashingAttr
.m_left
, absentAttr
.m_left
);
12168 m_right
.CollectCommonAttributes(attr
.m_right
, clashingAttr
.m_right
, absentAttr
.m_right
);
12169 m_top
.CollectCommonAttributes(attr
.m_top
, clashingAttr
.m_top
, absentAttr
.m_top
);
12170 m_bottom
.CollectCommonAttributes(attr
.m_bottom
, clashingAttr
.m_bottom
, absentAttr
.m_bottom
);
12173 // Set style of all borders
12174 void wxTextAttrBorders::SetStyle(int style
)
12176 m_left
.SetStyle(style
);
12177 m_right
.SetStyle(style
);
12178 m_top
.SetStyle(style
);
12179 m_bottom
.SetStyle(style
);
12182 // Set colour of all borders
12183 void wxTextAttrBorders::SetColour(unsigned long colour
)
12185 m_left
.SetColour(colour
);
12186 m_right
.SetColour(colour
);
12187 m_top
.SetColour(colour
);
12188 m_bottom
.SetColour(colour
);
12191 void wxTextAttrBorders::SetColour(const wxColour
& colour
)
12193 m_left
.SetColour(colour
);
12194 m_right
.SetColour(colour
);
12195 m_top
.SetColour(colour
);
12196 m_bottom
.SetColour(colour
);
12199 // Set width of all borders
12200 void wxTextAttrBorders::SetWidth(const wxTextAttrDimension
& width
)
12202 m_left
.SetWidth(width
);
12203 m_right
.SetWidth(width
);
12204 m_top
.SetWidth(width
);
12205 m_bottom
.SetWidth(width
);
12208 // Partial equality test
12209 bool wxTextAttrDimension::EqPartial(const wxTextAttrDimension
& dim
, bool weakTest
) const
12211 if (!weakTest
&& !IsValid() && dim
.IsValid())
12214 if (dim
.IsValid() && IsValid() && !((*this) == dim
))
12220 bool wxTextAttrDimension::Apply(const wxTextAttrDimension
& dim
, const wxTextAttrDimension
* compareWith
)
12224 if (!(compareWith
&& dim
== (*compareWith
)))
12231 // Collects the attributes that are common to a range of content, building up a note of
12232 // which attributes are absent in some objects and which clash in some objects.
12233 void wxTextAttrDimension::CollectCommonAttributes(const wxTextAttrDimension
& attr
, wxTextAttrDimension
& clashingAttr
, wxTextAttrDimension
& absentAttr
)
12235 if (attr
.IsValid())
12237 if (!clashingAttr
.IsValid() && !absentAttr
.IsValid())
12241 if (!((*this) == attr
))
12243 clashingAttr
.SetValid(true);
12252 absentAttr
.SetValid(true);
12255 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(wxDC
& dc
, double scale
, const wxSize
& parentSize
)
12257 m_ppi
= dc
.GetPPI().x
; m_scale
= scale
; m_parentSize
= parentSize
;
12260 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(int ppi
, double scale
, const wxSize
& parentSize
)
12262 m_ppi
= ppi
; m_scale
= scale
; m_parentSize
= parentSize
;
12265 int wxTextAttrDimensionConverter::ConvertTenthsMMToPixels(int units
) const
12267 return wxRichTextObject::ConvertTenthsMMToPixels(m_ppi
, units
, m_scale
);
12270 int wxTextAttrDimensionConverter::ConvertPixelsToTenthsMM(int pixels
) const
12272 return wxRichTextObject::ConvertPixelsToTenthsMM(m_ppi
, pixels
, m_scale
);
12275 int wxTextAttrDimensionConverter::GetPixels(const wxTextAttrDimension
& dim
, int direction
) const
12277 if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
12278 return ConvertTenthsMMToPixels(dim
.GetValue());
12279 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
12280 return dim
.GetValue();
12281 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
12283 wxASSERT(m_parentSize
!= wxDefaultSize
);
12284 if (direction
== wxHORIZONTAL
)
12285 return (int) (double(m_parentSize
.x
) * double(dim
.GetValue()) / 100.0);
12287 return (int) (double(m_parentSize
.y
) * double(dim
.GetValue()) / 100.0);
12296 int wxTextAttrDimensionConverter::GetTenthsMM(const wxTextAttrDimension
& dim
) const
12298 if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
12299 return dim
.GetValue();
12300 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
12301 return ConvertPixelsToTenthsMM(dim
.GetValue());
12309 // Partial equality test
12310 bool wxTextAttrDimensions::EqPartial(const wxTextAttrDimensions
& dims
, bool weakTest
) const
12312 if (!m_left
.EqPartial(dims
.m_left
, weakTest
))
12315 if (!m_right
.EqPartial(dims
.m_right
, weakTest
))
12318 if (!m_top
.EqPartial(dims
.m_top
, weakTest
))
12321 if (!m_bottom
.EqPartial(dims
.m_bottom
, weakTest
))
12327 // Apply border to 'this', but not if the same as compareWith
12328 bool wxTextAttrDimensions::Apply(const wxTextAttrDimensions
& dims
, const wxTextAttrDimensions
* compareWith
)
12330 m_left
.Apply(dims
.m_left
, compareWith
? (& compareWith
->m_left
) : (const wxTextAttrDimension
*) NULL
);
12331 m_right
.Apply(dims
.m_right
, compareWith
? (& compareWith
->m_right
): (const wxTextAttrDimension
*) NULL
);
12332 m_top
.Apply(dims
.m_top
, compareWith
? (& compareWith
->m_top
): (const wxTextAttrDimension
*) NULL
);
12333 m_bottom
.Apply(dims
.m_bottom
, compareWith
? (& compareWith
->m_bottom
): (const wxTextAttrDimension
*) NULL
);
12338 // Remove specified attributes from this object
12339 bool wxTextAttrDimensions::RemoveStyle(const wxTextAttrDimensions
& attr
)
12341 if (attr
.m_left
.IsValid())
12343 if (attr
.m_right
.IsValid())
12345 if (attr
.m_top
.IsValid())
12347 if (attr
.m_bottom
.IsValid())
12353 // Collects the attributes that are common to a range of content, building up a note of
12354 // which attributes are absent in some objects and which clash in some objects.
12355 void wxTextAttrDimensions::CollectCommonAttributes(const wxTextAttrDimensions
& attr
, wxTextAttrDimensions
& clashingAttr
, wxTextAttrDimensions
& absentAttr
)
12357 m_left
.CollectCommonAttributes(attr
.m_left
, clashingAttr
.m_left
, absentAttr
.m_left
);
12358 m_right
.CollectCommonAttributes(attr
.m_right
, clashingAttr
.m_right
, absentAttr
.m_right
);
12359 m_top
.CollectCommonAttributes(attr
.m_top
, clashingAttr
.m_top
, absentAttr
.m_top
);
12360 m_bottom
.CollectCommonAttributes(attr
.m_bottom
, clashingAttr
.m_bottom
, absentAttr
.m_bottom
);
12363 // Partial equality test
12364 bool wxTextAttrSize::EqPartial(const wxTextAttrSize
& size
, bool weakTest
) const
12366 if (!m_width
.EqPartial(size
.m_width
, weakTest
))
12369 if (!m_height
.EqPartial(size
.m_height
, weakTest
))
12375 // Apply border to 'this', but not if the same as compareWith
12376 bool wxTextAttrSize::Apply(const wxTextAttrSize
& size
, const wxTextAttrSize
* compareWith
)
12378 m_width
.Apply(size
.m_width
, compareWith
? (& compareWith
->m_width
) : (const wxTextAttrDimension
*) NULL
);
12379 m_height
.Apply(size
.m_height
, compareWith
? (& compareWith
->m_height
): (const wxTextAttrDimension
*) NULL
);
12384 // Remove specified attributes from this object
12385 bool wxTextAttrSize::RemoveStyle(const wxTextAttrSize
& attr
)
12387 if (attr
.m_width
.IsValid())
12389 if (attr
.m_height
.IsValid())
12395 // Collects the attributes that are common to a range of content, building up a note of
12396 // which attributes are absent in some objects and which clash in some objects.
12397 void wxTextAttrSize::CollectCommonAttributes(const wxTextAttrSize
& attr
, wxTextAttrSize
& clashingAttr
, wxTextAttrSize
& absentAttr
)
12399 m_width
.CollectCommonAttributes(attr
.m_width
, clashingAttr
.m_width
, absentAttr
.m_width
);
12400 m_height
.CollectCommonAttributes(attr
.m_height
, clashingAttr
.m_height
, absentAttr
.m_height
);
12403 // Collects the attributes that are common to a range of content, building up a note of
12404 // which attributes are absent in some objects and which clash in some objects.
12405 void wxTextAttrCollectCommonAttributes(wxTextAttr
& currentStyle
, const wxTextAttr
& attr
, wxTextAttr
& clashingAttr
, wxTextAttr
& absentAttr
)
12407 absentAttr
.SetFlags(absentAttr
.GetFlags() | (~attr
.GetFlags() & wxTEXT_ATTR_ALL
));
12408 absentAttr
.SetTextEffectFlags(absentAttr
.GetTextEffectFlags() | (~attr
.GetTextEffectFlags() & 0xFFFF));
12410 long forbiddenFlags
= clashingAttr
.GetFlags()|absentAttr
.GetFlags();
12412 if (attr
.HasFont())
12414 // If different font size units are being used, this is a clash.
12415 if (((attr
.GetFlags() & wxTEXT_ATTR_FONT_SIZE
) | (currentStyle
.GetFlags() & wxTEXT_ATTR_FONT_SIZE
)) == wxTEXT_ATTR_FONT_SIZE
)
12417 currentStyle
.SetFontSize(0);
12418 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_SIZE
);
12419 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_SIZE
);
12423 if (attr
.HasFontPointSize() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_POINT_SIZE
))
12425 if (currentStyle
.HasFontPointSize())
12427 if (currentStyle
.GetFontSize() != attr
.GetFontSize())
12429 // Clash of attr - mark as such
12430 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE
);
12431 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE
);
12435 currentStyle
.SetFontSize(attr
.GetFontSize());
12438 if (attr
.HasFontPixelSize() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_PIXEL_SIZE
))
12440 if (currentStyle
.HasFontPixelSize())
12442 if (currentStyle
.GetFontSize() != attr
.GetFontSize())
12444 // Clash of attr - mark as such
12445 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE
);
12446 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE
);
12450 currentStyle
.SetFontPixelSize(attr
.GetFontSize());
12454 if (attr
.HasFontItalic() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_ITALIC
))
12456 if (currentStyle
.HasFontItalic())
12458 if (currentStyle
.GetFontStyle() != attr
.GetFontStyle())
12460 // Clash of attr - mark as such
12461 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_ITALIC
);
12462 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC
);
12466 currentStyle
.SetFontStyle(attr
.GetFontStyle());
12469 if (attr
.HasFontFamily() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_FAMILY
))
12471 if (currentStyle
.HasFontFamily())
12473 if (currentStyle
.GetFontFamily() != attr
.GetFontFamily())
12475 // Clash of attr - mark as such
12476 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FAMILY
);
12477 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY
);
12481 currentStyle
.SetFontFamily(attr
.GetFontFamily());
12484 if (attr
.HasFontWeight() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_WEIGHT
))
12486 if (currentStyle
.HasFontWeight())
12488 if (currentStyle
.GetFontWeight() != attr
.GetFontWeight())
12490 // Clash of attr - mark as such
12491 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_WEIGHT
);
12492 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT
);
12496 currentStyle
.SetFontWeight(attr
.GetFontWeight());
12499 if (attr
.HasFontFaceName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_FACE
))
12501 if (currentStyle
.HasFontFaceName())
12503 wxString
faceName1(currentStyle
.GetFontFaceName());
12504 wxString
faceName2(attr
.GetFontFaceName());
12506 if (faceName1
!= faceName2
)
12508 // Clash of attr - mark as such
12509 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FACE
);
12510 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FACE
);
12514 currentStyle
.SetFontFaceName(attr
.GetFontFaceName());
12517 if (attr
.HasFontUnderlined() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_UNDERLINE
))
12519 if (currentStyle
.HasFontUnderlined())
12521 if (currentStyle
.GetFontUnderlined() != attr
.GetFontUnderlined())
12523 // Clash of attr - mark as such
12524 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
12525 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
12529 currentStyle
.SetFontUnderlined(attr
.GetFontUnderlined());
12532 if (attr
.HasFontStrikethrough() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_STRIKETHROUGH
))
12534 if (currentStyle
.HasFontStrikethrough())
12536 if (currentStyle
.GetFontStrikethrough() != attr
.GetFontStrikethrough())
12538 // Clash of attr - mark as such
12539 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH
);
12540 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH
);
12544 currentStyle
.SetFontStrikethrough(attr
.GetFontStrikethrough());
12548 if (attr
.HasTextColour() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_TEXT_COLOUR
))
12550 if (currentStyle
.HasTextColour())
12552 if (currentStyle
.GetTextColour() != attr
.GetTextColour())
12554 // Clash of attr - mark as such
12555 clashingAttr
.AddFlag(wxTEXT_ATTR_TEXT_COLOUR
);
12556 currentStyle
.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR
);
12560 currentStyle
.SetTextColour(attr
.GetTextColour());
12563 if (attr
.HasBackgroundColour() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BACKGROUND_COLOUR
))
12565 if (currentStyle
.HasBackgroundColour())
12567 if (currentStyle
.GetBackgroundColour() != attr
.GetBackgroundColour())
12569 // Clash of attr - mark as such
12570 clashingAttr
.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
12571 currentStyle
.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
12575 currentStyle
.SetBackgroundColour(attr
.GetBackgroundColour());
12578 if (attr
.HasAlignment() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_ALIGNMENT
))
12580 if (currentStyle
.HasAlignment())
12582 if (currentStyle
.GetAlignment() != attr
.GetAlignment())
12584 // Clash of attr - mark as such
12585 clashingAttr
.AddFlag(wxTEXT_ATTR_ALIGNMENT
);
12586 currentStyle
.RemoveFlag(wxTEXT_ATTR_ALIGNMENT
);
12590 currentStyle
.SetAlignment(attr
.GetAlignment());
12593 if (attr
.HasTabs() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_TABS
))
12595 if (currentStyle
.HasTabs())
12597 if (!wxRichTextTabsEq(currentStyle
.GetTabs(), attr
.GetTabs()))
12599 // Clash of attr - mark as such
12600 clashingAttr
.AddFlag(wxTEXT_ATTR_TABS
);
12601 currentStyle
.RemoveFlag(wxTEXT_ATTR_TABS
);
12605 currentStyle
.SetTabs(attr
.GetTabs());
12608 if (attr
.HasLeftIndent() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LEFT_INDENT
))
12610 if (currentStyle
.HasLeftIndent())
12612 if (currentStyle
.GetLeftIndent() != attr
.GetLeftIndent() || currentStyle
.GetLeftSubIndent() != attr
.GetLeftSubIndent())
12614 // Clash of attr - mark as such
12615 clashingAttr
.AddFlag(wxTEXT_ATTR_LEFT_INDENT
);
12616 currentStyle
.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT
);
12620 currentStyle
.SetLeftIndent(attr
.GetLeftIndent(), attr
.GetLeftSubIndent());
12623 if (attr
.HasRightIndent() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_RIGHT_INDENT
))
12625 if (currentStyle
.HasRightIndent())
12627 if (currentStyle
.GetRightIndent() != attr
.GetRightIndent())
12629 // Clash of attr - mark as such
12630 clashingAttr
.AddFlag(wxTEXT_ATTR_RIGHT_INDENT
);
12631 currentStyle
.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT
);
12635 currentStyle
.SetRightIndent(attr
.GetRightIndent());
12638 if (attr
.HasParagraphSpacingAfter() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARA_SPACING_AFTER
))
12640 if (currentStyle
.HasParagraphSpacingAfter())
12642 if (currentStyle
.GetParagraphSpacingAfter() != attr
.GetParagraphSpacingAfter())
12644 // Clash of attr - mark as such
12645 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
12646 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
12650 currentStyle
.SetParagraphSpacingAfter(attr
.GetParagraphSpacingAfter());
12653 if (attr
.HasParagraphSpacingBefore() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARA_SPACING_BEFORE
))
12655 if (currentStyle
.HasParagraphSpacingBefore())
12657 if (currentStyle
.GetParagraphSpacingBefore() != attr
.GetParagraphSpacingBefore())
12659 // Clash of attr - mark as such
12660 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
12661 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
12665 currentStyle
.SetParagraphSpacingBefore(attr
.GetParagraphSpacingBefore());
12668 if (attr
.HasLineSpacing() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LINE_SPACING
))
12670 if (currentStyle
.HasLineSpacing())
12672 if (currentStyle
.GetLineSpacing() != attr
.GetLineSpacing())
12674 // Clash of attr - mark as such
12675 clashingAttr
.AddFlag(wxTEXT_ATTR_LINE_SPACING
);
12676 currentStyle
.RemoveFlag(wxTEXT_ATTR_LINE_SPACING
);
12680 currentStyle
.SetLineSpacing(attr
.GetLineSpacing());
12683 if (attr
.HasCharacterStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_CHARACTER_STYLE_NAME
))
12685 if (currentStyle
.HasCharacterStyleName())
12687 if (currentStyle
.GetCharacterStyleName() != attr
.GetCharacterStyleName())
12689 // Clash of attr - mark as such
12690 clashingAttr
.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
12691 currentStyle
.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
12695 currentStyle
.SetCharacterStyleName(attr
.GetCharacterStyleName());
12698 if (attr
.HasParagraphStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
))
12700 if (currentStyle
.HasParagraphStyleName())
12702 if (currentStyle
.GetParagraphStyleName() != attr
.GetParagraphStyleName())
12704 // Clash of attr - mark as such
12705 clashingAttr
.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
12706 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
12710 currentStyle
.SetParagraphStyleName(attr
.GetParagraphStyleName());
12713 if (attr
.HasListStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LIST_STYLE_NAME
))
12715 if (currentStyle
.HasListStyleName())
12717 if (currentStyle
.GetListStyleName() != attr
.GetListStyleName())
12719 // Clash of attr - mark as such
12720 clashingAttr
.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
12721 currentStyle
.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
12725 currentStyle
.SetListStyleName(attr
.GetListStyleName());
12728 if (attr
.HasBulletStyle() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_STYLE
))
12730 if (currentStyle
.HasBulletStyle())
12732 if (currentStyle
.GetBulletStyle() != attr
.GetBulletStyle())
12734 // Clash of attr - mark as such
12735 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_STYLE
);
12736 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE
);
12740 currentStyle
.SetBulletStyle(attr
.GetBulletStyle());
12743 if (attr
.HasBulletNumber() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_NUMBER
))
12745 if (currentStyle
.HasBulletNumber())
12747 if (currentStyle
.GetBulletNumber() != attr
.GetBulletNumber())
12749 // Clash of attr - mark as such
12750 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NUMBER
);
12751 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER
);
12755 currentStyle
.SetBulletNumber(attr
.GetBulletNumber());
12758 if (attr
.HasBulletText() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_TEXT
))
12760 if (currentStyle
.HasBulletText())
12762 if (currentStyle
.GetBulletText() != attr
.GetBulletText())
12764 // Clash of attr - mark as such
12765 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_TEXT
);
12766 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT
);
12771 currentStyle
.SetBulletText(attr
.GetBulletText());
12772 currentStyle
.SetBulletFont(attr
.GetBulletFont());
12776 if (attr
.HasBulletName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_NAME
))
12778 if (currentStyle
.HasBulletName())
12780 if (currentStyle
.GetBulletName() != attr
.GetBulletName())
12782 // Clash of attr - mark as such
12783 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NAME
);
12784 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NAME
);
12789 currentStyle
.SetBulletName(attr
.GetBulletName());
12793 if (attr
.HasURL() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_URL
))
12795 if (currentStyle
.HasURL())
12797 if (currentStyle
.GetURL() != attr
.GetURL())
12799 // Clash of attr - mark as such
12800 clashingAttr
.AddFlag(wxTEXT_ATTR_URL
);
12801 currentStyle
.RemoveFlag(wxTEXT_ATTR_URL
);
12806 currentStyle
.SetURL(attr
.GetURL());
12810 if (attr
.HasTextEffects() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_EFFECTS
))
12812 if (currentStyle
.HasTextEffects())
12814 // We need to find the bits in the new attr that are different:
12815 // just look at those bits that are specified by the new attr.
12817 // We need to remove the bits and flags that are not common between current attr
12818 // and new attr. In so doing we need to take account of the styles absent from one or more of the
12819 // previous styles.
12821 int currentRelevantTextEffects
= currentStyle
.GetTextEffects() & attr
.GetTextEffectFlags();
12822 int newRelevantTextEffects
= attr
.GetTextEffects() & attr
.GetTextEffectFlags();
12824 if (currentRelevantTextEffects
!= newRelevantTextEffects
)
12826 // Find the text effects that were different, using XOR
12827 int differentEffects
= currentRelevantTextEffects
^ newRelevantTextEffects
;
12829 // Clash of attr - mark as such
12830 clashingAttr
.SetTextEffectFlags(clashingAttr
.GetTextEffectFlags() | differentEffects
);
12831 currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~differentEffects
);
12836 currentStyle
.SetTextEffects(attr
.GetTextEffects());
12837 currentStyle
.SetTextEffectFlags(attr
.GetTextEffectFlags());
12840 // Mask out the flags and values that cannot be common because they were absent in one or more objecrs
12841 // that we've looked at so far
12842 currentStyle
.SetTextEffects(currentStyle
.GetTextEffects() & ~absentAttr
.GetTextEffectFlags());
12843 currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~absentAttr
.GetTextEffectFlags());
12845 if (currentStyle
.GetTextEffectFlags() == 0)
12846 currentStyle
.RemoveFlag(wxTEXT_ATTR_EFFECTS
);
12849 if (attr
.HasOutlineLevel() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_OUTLINE_LEVEL
))
12851 if (currentStyle
.HasOutlineLevel())
12853 if (currentStyle
.GetOutlineLevel() != attr
.GetOutlineLevel())
12855 // Clash of attr - mark as such
12856 clashingAttr
.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
12857 currentStyle
.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
12861 currentStyle
.SetOutlineLevel(attr
.GetOutlineLevel());
12865 WX_DEFINE_OBJARRAY(wxRichTextVariantArray
);
12867 IMPLEMENT_DYNAMIC_CLASS(wxRichTextProperties
, wxObject
)
12869 bool wxRichTextProperties::operator==(const wxRichTextProperties
& props
) const
12871 if (m_properties
.GetCount() != props
.GetCount())
12875 for (i
= 0; i
< m_properties
.GetCount(); i
++)
12877 const wxVariant
& var1
= m_properties
[i
];
12878 int idx
= props
.Find(var1
.GetName());
12881 const wxVariant
& var2
= props
.m_properties
[idx
];
12882 if (!(var1
== var2
))
12889 wxArrayString
wxRichTextProperties::GetPropertyNames() const
12893 for (i
= 0; i
< m_properties
.GetCount(); i
++)
12895 arr
.Add(m_properties
[i
].GetName());
12900 int wxRichTextProperties::Find(const wxString
& name
) const
12903 for (i
= 0; i
< m_properties
.GetCount(); i
++)
12905 if (m_properties
[i
].GetName() == name
)
12911 bool wxRichTextProperties::Remove(const wxString
& name
)
12913 int idx
= Find(name
);
12916 m_properties
.RemoveAt(idx
);
12923 wxVariant
* wxRichTextProperties::FindOrCreateProperty(const wxString
& name
)
12925 int idx
= Find(name
);
12926 if (idx
== wxNOT_FOUND
)
12927 SetProperty(name
, wxString());
12929 if (idx
!= wxNOT_FOUND
)
12931 return & (*this)[idx
];
12937 const wxVariant
& wxRichTextProperties::GetProperty(const wxString
& name
) const
12939 static const wxVariant nullVariant
;
12940 int idx
= Find(name
);
12942 return m_properties
[idx
];
12944 return nullVariant
;
12947 wxString
wxRichTextProperties::GetPropertyString(const wxString
& name
) const
12949 return GetProperty(name
).GetString();
12952 long wxRichTextProperties::GetPropertyLong(const wxString
& name
) const
12954 return GetProperty(name
).GetLong();
12957 bool wxRichTextProperties::GetPropertyBool(const wxString
& name
) const
12959 return GetProperty(name
).GetBool();
12962 double wxRichTextProperties::GetPropertyDouble(const wxString
& name
) const
12964 return GetProperty(name
).GetDouble();
12967 void wxRichTextProperties::SetProperty(const wxVariant
& variant
)
12969 wxASSERT(!variant
.GetName().IsEmpty());
12971 int idx
= Find(variant
.GetName());
12974 m_properties
.Add(variant
);
12976 m_properties
[idx
] = variant
;
12979 void wxRichTextProperties::SetProperty(const wxString
& name
, const wxVariant
& variant
)
12981 int idx
= Find(name
);
12982 wxVariant
var(variant
);
12986 m_properties
.Add(var
);
12988 m_properties
[idx
] = var
;
12991 void wxRichTextProperties::SetProperty(const wxString
& name
, const wxString
& value
)
12993 SetProperty(name
, wxVariant(value
, name
));
12996 void wxRichTextProperties::SetProperty(const wxString
& name
, long value
)
12998 SetProperty(name
, wxVariant(value
, name
));
13001 void wxRichTextProperties::SetProperty(const wxString
& name
, double value
)
13003 SetProperty(name
, wxVariant(value
, name
));
13006 void wxRichTextProperties::SetProperty(const wxString
& name
, bool value
)
13008 SetProperty(name
, wxVariant(value
, name
));
13011 void wxRichTextProperties::RemoveProperties(const wxRichTextProperties
& properties
)
13014 for (i
= 0; i
< properties
.GetCount(); i
++)
13016 wxString name
= properties
.GetProperties()[i
].GetName();
13017 if (HasProperty(name
))
13022 void wxRichTextProperties::MergeProperties(const wxRichTextProperties
& properties
)
13025 for (i
= 0; i
< properties
.GetCount(); i
++)
13027 SetProperty(properties
.GetProperties()[i
]);
13031 wxRichTextObject
* wxRichTextObjectAddress::GetObject(wxRichTextParagraphLayoutBox
* topLevelContainer
) const
13033 if (m_address
.GetCount() == 0)
13034 return topLevelContainer
;
13036 wxRichTextCompositeObject
* p
= topLevelContainer
;
13038 while (p
&& i
< m_address
.GetCount())
13040 int pos
= m_address
[i
];
13041 wxASSERT(pos
>= 0 && pos
< (int) p
->GetChildren().GetCount());
13042 if (pos
< 0 || pos
>= (int) p
->GetChildren().GetCount())
13045 wxRichTextObject
* p1
= p
->GetChild(pos
);
13046 if (i
== (m_address
.GetCount()-1))
13049 p
= wxDynamicCast(p1
, wxRichTextCompositeObject
);
13055 bool wxRichTextObjectAddress::Create(wxRichTextParagraphLayoutBox
* topLevelContainer
, wxRichTextObject
* obj
)
13059 if (topLevelContainer
== obj
)
13062 wxRichTextObject
* o
= obj
;
13065 wxRichTextCompositeObject
* p
= wxDynamicCast(o
->GetParent(), wxRichTextCompositeObject
);
13069 int pos
= p
->GetChildren().IndexOf(o
);
13073 m_address
.Insert(pos
, 0);
13075 if (p
== topLevelContainer
)
13084 bool wxRichTextSelection::operator==(const wxRichTextSelection
& sel
) const
13086 if (m_container
!= sel
.m_container
)
13088 if (m_ranges
.GetCount() != sel
.m_ranges
.GetCount())
13091 for (i
= 0; i
< m_ranges
.GetCount(); i
++)
13092 if (!(m_ranges
[i
] == sel
.m_ranges
[i
]))
13097 // Get the selections appropriate to the specified object, if any; returns wxRICHTEXT_NO_SELECTION if none
13098 // or none at the level of the object's container.
13099 wxRichTextRangeArray
wxRichTextSelection::GetSelectionForObject(wxRichTextObject
* obj
) const
13103 wxRichTextParagraphLayoutBox
* container
= obj
->GetParentContainer();
13105 if (container
== m_container
)
13108 container
= obj
->GetContainer();
13111 if (container
->GetParent())
13113 // If we found that our object's container is within the range of
13114 // a selection higher up, then assume the whole original object
13115 // is also selected.
13116 wxRichTextParagraphLayoutBox
* parentContainer
= container
->GetParentContainer();
13117 if (parentContainer
== m_container
)
13119 if (WithinSelection(container
->GetRange().GetStart(), m_ranges
))
13121 wxRichTextRangeArray ranges
;
13122 ranges
.Add(obj
->GetRange());
13127 container
= parentContainer
;
13136 return wxRichTextRangeArray();
13139 // Is the given position within the selection?
13140 bool wxRichTextSelection::WithinSelection(long pos
, wxRichTextObject
* obj
) const
13146 wxRichTextRangeArray selectionRanges
= GetSelectionForObject(obj
);
13147 return WithinSelection(pos
, selectionRanges
);
13151 // Is the given position within the selection range?
13152 bool wxRichTextSelection::WithinSelection(long pos
, const wxRichTextRangeArray
& ranges
)
13155 for (i
= 0; i
< ranges
.GetCount(); i
++)
13157 const wxRichTextRange
& range
= ranges
[i
];
13158 if (pos
>= range
.GetStart() && pos
<= range
.GetEnd())
13164 // Is the given range completely within the selection range?
13165 bool wxRichTextSelection::WithinSelection(const wxRichTextRange
& range
, const wxRichTextRangeArray
& ranges
)
13168 for (i
= 0; i
< ranges
.GetCount(); i
++)
13170 const wxRichTextRange
& eachRange
= ranges
[i
];
13171 if (range
.IsWithin(eachRange
))
13177 IMPLEMENT_CLASS(wxRichTextDrawingHandler
, wxObject
)
13178 IMPLEMENT_CLASS(wxRichTextDrawingContext
, wxObject
)
13180 bool wxRichTextDrawingContext::HasVirtualAttributes(wxRichTextObject
* obj
) const
13182 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
13185 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13186 if (handler
->HasVirtualAttributes(obj
))
13189 node
= node
->GetNext();
13194 wxRichTextAttr
wxRichTextDrawingContext::GetVirtualAttributes(wxRichTextObject
* obj
) const
13196 wxRichTextAttr attr
;
13197 // We apply all handlers, so we can may combine several different attributes
13198 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
13201 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13202 if (handler
->HasVirtualAttributes(obj
))
13204 bool success
= handler
->GetVirtualAttributes(attr
, obj
);
13206 wxUnusedVar(success
);
13209 node
= node
->GetNext();
13214 bool wxRichTextDrawingContext::ApplyVirtualAttributes(wxRichTextAttr
& attr
, wxRichTextObject
* obj
) const
13216 if (HasVirtualAttributes(obj
))
13218 wxRichTextAttr
a(GetVirtualAttributes(obj
));
13226 /// Adds a handler to the end
13227 void wxRichTextBuffer::AddDrawingHandler(wxRichTextDrawingHandler
*handler
)
13229 sm_drawingHandlers
.Append(handler
);
13232 /// Inserts a handler at the front
13233 void wxRichTextBuffer::InsertDrawingHandler(wxRichTextDrawingHandler
*handler
)
13235 sm_drawingHandlers
.Insert( handler
);
13238 /// Removes a handler
13239 bool wxRichTextBuffer::RemoveDrawingHandler(const wxString
& name
)
13241 wxRichTextDrawingHandler
*handler
= FindDrawingHandler(name
);
13244 sm_drawingHandlers
.DeleteObject(handler
);
13252 wxRichTextDrawingHandler
* wxRichTextBuffer::FindDrawingHandler(const wxString
& name
)
13254 wxList::compatibility_iterator node
= sm_drawingHandlers
.GetFirst();
13257 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13258 if (handler
->GetName().Lower() == name
.Lower()) return handler
;
13260 node
= node
->GetNext();
13265 void wxRichTextBuffer::CleanUpDrawingHandlers()
13267 wxList::compatibility_iterator node
= sm_drawingHandlers
.GetFirst();
13270 wxRichTextDrawingHandler
* handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13271 wxList::compatibility_iterator next
= node
->GetNext();
13276 sm_drawingHandlers
.Clear();
13279 void wxRichTextBuffer::AddFieldType(wxRichTextFieldType
*fieldType
)
13281 sm_fieldTypes
[fieldType
->GetName()] = fieldType
;
13284 bool wxRichTextBuffer::RemoveFieldType(const wxString
& name
)
13286 wxRichTextFieldTypeHashMap::iterator it
= sm_fieldTypes
.find(name
);
13287 if (it
== sm_fieldTypes
.end())
13291 wxRichTextFieldType
* fieldType
= it
->second
;
13292 sm_fieldTypes
.erase(it
);
13298 wxRichTextFieldType
*wxRichTextBuffer::FindFieldType(const wxString
& name
)
13300 wxRichTextFieldTypeHashMap::iterator it
= sm_fieldTypes
.find(name
);
13301 if (it
== sm_fieldTypes
.end())
13307 void wxRichTextBuffer::CleanUpFieldTypes()
13309 wxRichTextFieldTypeHashMap::iterator it
;
13310 for( it
= sm_fieldTypes
.begin(); it
!= sm_fieldTypes
.end(); ++it
)
13312 wxRichTextFieldType
* fieldType
= it
->second
;
13316 sm_fieldTypes
.clear();