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"
46 #include "wx/listimpl.cpp"
47 #include "wx/arrimpl.cpp"
49 WX_DEFINE_LIST(wxRichTextObjectList
)
50 WX_DEFINE_LIST(wxRichTextLineList
)
52 // Switch off if the platform doesn't like it for some reason
53 #define wxRICHTEXT_USE_OPTIMIZED_DRAWING 1
55 // Use GetPartialTextExtents for platforms that support it natively
56 #define wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS 1
58 const wxChar wxRichTextLineBreakChar
= (wxChar
) 29;
60 // Helper classes for floating layout
61 struct wxRichTextFloatRectMap
63 wxRichTextFloatRectMap(int sY
, int eY
, int w
, wxRichTextObject
* obj
)
73 wxRichTextObject
* anchor
;
76 WX_DEFINE_SORTED_ARRAY(wxRichTextFloatRectMap
*, wxRichTextFloatRectMapArray
);
78 int wxRichTextFloatRectMapCmp(wxRichTextFloatRectMap
* r1
, wxRichTextFloatRectMap
* r2
)
80 return r1
->startY
- r2
->startY
;
83 class wxRichTextFloatCollector
86 wxRichTextFloatCollector(const wxRect
& availableRect
);
87 ~wxRichTextFloatCollector();
89 // Collect the floating objects info in the given paragraph
90 void CollectFloat(wxRichTextParagraph
* para
);
91 void CollectFloat(wxRichTextParagraph
* para
, wxRichTextObject
* floating
);
93 // Return the last paragraph we collected
94 wxRichTextParagraph
* LastParagraph();
96 // Given the start y position and the height of the line,
97 // find out how wide the line can be
98 wxRect
GetAvailableRect(int startY
, int endY
);
100 // Given a floating box, find its fit position
101 int GetFitPosition(int direction
, int start
, int height
) const;
102 int GetFitPosition(const wxRichTextFloatRectMapArray
& array
, int start
, int height
) const;
104 // Find the last y position
105 int GetLastRectBottom();
107 // Draw the floats inside a rect
108 void Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
);
110 // HitTest the floats
111 int HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, int flags
);
113 // Get floating object count
114 int GetFloatingObjectCount() const { return m_left
.GetCount() + m_right
.GetCount(); }
116 // Get floating objects
117 bool GetFloatingObjects(wxRichTextObjectList
& objects
) const;
120 bool DeleteFloat(wxRichTextObject
* obj
);
122 // Do we have this float already?
123 bool HasFloat(wxRichTextObject
* obj
);
125 bool HasFloats() const { return m_left
.GetCount() >0 || m_right
.GetCount() > 0; }
127 static int SearchAdjacentRect(const wxRichTextFloatRectMapArray
& array
, int point
);
129 static int GetWidthFromFloatRect(const wxRichTextFloatRectMapArray
& array
, int index
, int startY
, int endY
);
131 static void FreeFloatRectMapArray(wxRichTextFloatRectMapArray
& array
);
133 static void DrawFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
);
135 static int HitTestFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, int flags
);
138 wxRichTextFloatRectMapArray m_left
;
139 wxRichTextFloatRectMapArray m_right
;
141 wxRect m_availableRect
;
142 wxRichTextParagraph
* m_para
;
146 bool wxRichTextFloatCollector::DeleteFloat(wxRichTextObject
* obj
)
149 for (i
= 0; i
< m_left
.GetCount(); i
++)
151 if (m_left
[i
]->anchor
== obj
)
157 for (i
= 0; i
< m_right
.GetCount(); i
++)
159 if (m_right
[i
]->anchor
== obj
)
168 // Do we have this float already?
169 bool wxRichTextFloatCollector::HasFloat(wxRichTextObject
* obj
)
172 for (i
= 0; i
< m_left
.GetCount(); i
++)
174 if (m_left
[i
]->anchor
== obj
)
179 for (i
= 0; i
< m_right
.GetCount(); i
++)
181 if (m_right
[i
]->anchor
== obj
)
189 // Get floating objects
190 bool wxRichTextFloatCollector::GetFloatingObjects(wxRichTextObjectList
& objects
) const
193 for (i
= 0; i
< m_left
.GetCount(); i
++)
194 objects
.Append(m_left
[i
]->anchor
);
195 for (i
= 0; i
< m_right
.GetCount(); i
++)
196 objects
.Append(m_right
[i
]->anchor
);
202 * Binary search helper function
203 * The argument point is the Y coordinate, and this fuction
204 * always return the floating rect that contain this coordinate
205 * or under this coordinate.
207 int wxRichTextFloatCollector::SearchAdjacentRect(const wxRichTextFloatRectMapArray
& array
, int point
)
209 int end
= array
.GetCount() - 1;
222 int mid
= (start
+ end
) / 2;
223 if (array
[mid
]->startY
<= point
&& array
[mid
]->endY
>= point
)
225 else if (array
[mid
]->startY
> point
)
230 else if (array
[mid
]->endY
< point
)
240 int wxRichTextFloatCollector::GetWidthFromFloatRect(const wxRichTextFloatRectMapArray
& array
, int index
, int startY
, int endY
)
243 int len
= array
.GetCount();
245 wxASSERT(index
>= 0 && index
< len
);
247 if (array
[index
]->startY
< startY
&& array
[index
]->endY
> startY
)
248 ret
= ret
< array
[index
]->width
? array
[index
]->width
: ret
;
249 while (index
< len
&& array
[index
]->startY
<= endY
)
251 ret
= ret
< array
[index
]->width
? array
[index
]->width
: ret
;
258 wxRichTextFloatCollector::wxRichTextFloatCollector(const wxRect
& rect
) : m_left(wxRichTextFloatRectMapCmp
), m_right(wxRichTextFloatRectMapCmp
)
260 m_availableRect
= rect
;
264 void wxRichTextFloatCollector::FreeFloatRectMapArray(wxRichTextFloatRectMapArray
& array
)
266 int len
= array
.GetCount();
267 for (int i
= 0; i
< len
; i
++)
271 wxRichTextFloatCollector::~wxRichTextFloatCollector()
273 FreeFloatRectMapArray(m_left
);
274 FreeFloatRectMapArray(m_right
);
277 int wxRichTextFloatCollector::GetFitPosition(const wxRichTextFloatRectMapArray
& array
, int start
, int height
) const
279 if (array
.GetCount() == 0)
282 int i
= SearchAdjacentRect(array
, start
);
284 while (i
< (int) array
.GetCount())
286 if (array
[i
]->startY
- last
>= height
)
288 last
= array
[i
]->endY
;
295 int wxRichTextFloatCollector::GetFitPosition(int direction
, int start
, int height
) const
297 if (direction
== wxTEXT_BOX_ATTR_FLOAT_LEFT
)
298 return GetFitPosition(m_left
, start
, height
);
299 else if (direction
== wxTEXT_BOX_ATTR_FLOAT_RIGHT
)
300 return GetFitPosition(m_right
, start
, height
);
303 wxASSERT("Never should be here");
308 // Adds a floating image to the float collector.
309 // The actual positioning is done by wxRichTextParagraph::LayoutFloat.
310 void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph
* para
, wxRichTextObject
* floating
)
312 int direction
= floating
->GetFloatDirection();
314 wxPoint pos
= floating
->GetPosition();
315 wxSize size
= floating
->GetCachedSize();
316 wxRichTextFloatRectMap
*map
= new wxRichTextFloatRectMap(pos
.y
, pos
.y
+ size
.y
, size
.x
, floating
);
319 case wxTEXT_BOX_ATTR_FLOAT_NONE
:
322 case wxTEXT_BOX_ATTR_FLOAT_LEFT
:
323 // Just a not-enough simple assertion
324 wxASSERT (m_left
.Index(map
) == wxNOT_FOUND
);
327 case wxTEXT_BOX_ATTR_FLOAT_RIGHT
:
328 wxASSERT (m_right
.Index(map
) == wxNOT_FOUND
);
333 wxASSERT("Unrecognised float attribute.");
339 void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph
* para
)
341 wxRichTextObjectList::compatibility_iterator node
= para
->GetChildren().GetFirst();
344 wxRichTextObject
* floating
= node
->GetData();
346 if (floating
->IsFloating())
348 CollectFloat(para
, floating
);
351 node
= node
->GetNext();
357 wxRichTextParagraph
* wxRichTextFloatCollector::LastParagraph()
362 wxRect
wxRichTextFloatCollector::GetAvailableRect(int startY
, int endY
)
364 int widthLeft
= 0, widthRight
= 0;
365 if (m_left
.GetCount() != 0)
367 int i
= SearchAdjacentRect(m_left
, startY
);
368 if (i
< (int) m_left
.GetCount())
369 widthLeft
= GetWidthFromFloatRect(m_left
, i
, startY
, endY
);
371 if (m_right
.GetCount() != 0)
373 int j
= SearchAdjacentRect(m_right
, startY
);
374 if (j
< (int) m_right
.GetCount())
375 widthRight
= GetWidthFromFloatRect(m_right
, j
, startY
, endY
);
378 // TODO: actually we want to use the actual image positions to find the
379 // available remaining space, since the image might not be right up against
380 // the left or right edge of the container.
381 return wxRect(widthLeft
+ m_availableRect
.x
, 0, m_availableRect
.width
- widthLeft
- widthRight
, 0);
384 int wxRichTextFloatCollector::GetLastRectBottom()
387 int len
= m_left
.GetCount();
389 ret
= ret
> m_left
[len
-1]->endY
? ret
: m_left
[len
-1]->endY
;
391 len
= m_right
.GetCount();
393 ret
= ret
> m_right
[len
-1]->endY
? ret
: m_right
[len
-1]->endY
;
399 void wxRichTextFloatCollector::DrawFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& WXUNUSED(range
), const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
402 int end
= rect
.y
+ rect
.height
;
404 i
= SearchAdjacentRect(array
, start
);
405 if (i
< 0 || i
>= (int) array
.GetCount())
407 j
= SearchAdjacentRect(array
, end
);
408 if (j
< 0 || j
>= (int) array
.GetCount())
409 j
= array
.GetCount() - 1;
412 wxRichTextObject
* obj
= array
[i
]->anchor
;
413 wxRichTextRange r
= obj
->GetRange();
414 obj
->Draw(dc
, context
, r
, selection
, wxRect(obj
->GetPosition(), obj
->GetCachedSize()), descent
, style
);
419 void wxRichTextFloatCollector::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
421 if (m_left
.GetCount() > 0)
422 DrawFloat(m_left
, dc
, context
, range
, selection
, rect
, descent
, style
);
423 if (m_right
.GetCount() > 0)
424 DrawFloat(m_right
, dc
, context
, range
, selection
, rect
, descent
, style
);
427 int wxRichTextFloatCollector::HitTestFloat(const wxRichTextFloatRectMapArray
& array
, wxDC
& WXUNUSED(dc
), wxRichTextDrawingContext
& WXUNUSED(context
), const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, int WXUNUSED(flags
))
430 if (array
.GetCount() == 0)
431 return wxRICHTEXT_HITTEST_NONE
;
432 i
= SearchAdjacentRect(array
, pt
.y
);
433 if (i
< 0 || i
>= (int) array
.GetCount())
434 return wxRICHTEXT_HITTEST_NONE
;
435 if (!array
[i
]->anchor
->IsShown())
436 return wxRICHTEXT_HITTEST_NONE
;
438 wxPoint point
= array
[i
]->anchor
->GetPosition();
439 wxSize size
= array
[i
]->anchor
->GetCachedSize();
440 if (point
.x
<= pt
.x
&& point
.x
+ size
.x
>= pt
.x
441 && point
.y
<= pt
.y
&& point
.y
+ size
.y
>= pt
.y
)
443 textPosition
= array
[i
]->anchor
->GetRange().GetStart();
444 * obj
= array
[i
]->anchor
;
445 if (pt
.x
> (pt
.x
+ pt
.x
+ size
.x
) / 2)
446 return wxRICHTEXT_HITTEST_BEFORE
;
448 return wxRICHTEXT_HITTEST_AFTER
;
451 return wxRICHTEXT_HITTEST_NONE
;
454 int wxRichTextFloatCollector::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, int flags
)
456 int ret
= HitTestFloat(m_left
, dc
, context
, pt
, textPosition
, obj
, flags
);
457 if (ret
== wxRICHTEXT_HITTEST_NONE
)
459 ret
= HitTestFloat(m_right
, dc
, context
, pt
, textPosition
, obj
, flags
);
464 // Helpers for efficiency
465 inline void wxCheckSetFont(wxDC
& dc
, const wxFont
& font
)
467 // JACS: did I do this some time ago when testing? Should we re-enable it?
469 const wxFont
& font1
= dc
.GetFont();
470 if (font1
.IsOk() && font
.IsOk())
472 if (font1
.GetPointSize() == font
.GetPointSize() &&
473 font1
.GetFamily() == font
.GetFamily() &&
474 font1
.GetStyle() == font
.GetStyle() &&
475 font1
.GetWeight() == font
.GetWeight() &&
476 font1
.GetUnderlined() == font
.GetUnderlined() &&
477 font1
.GetFamily() == font
.GetFamily() &&
478 font1
.GetFaceName() == font
.GetFaceName())
485 inline void wxCheckSetPen(wxDC
& dc
, const wxPen
& pen
)
487 const wxPen
& pen1
= dc
.GetPen();
488 if (pen1
.IsOk() && pen
.IsOk())
490 if (pen1
.GetWidth() == pen
.GetWidth() &&
491 pen1
.GetStyle() == pen
.GetStyle() &&
492 pen1
.GetColour() == pen
.GetColour())
498 inline void wxCheckSetBrush(wxDC
& dc
, const wxBrush
& brush
)
500 const wxBrush
& brush1
= dc
.GetBrush();
501 if (brush1
.IsOk() && brush
.IsOk())
503 if (brush1
.GetStyle() == brush
.GetStyle() &&
504 brush1
.GetColour() == brush
.GetColour())
512 * This is the base for drawable objects.
515 IMPLEMENT_CLASS(wxRichTextObject
, wxObject
)
517 wxRichTextObject::wxRichTextObject(wxRichTextObject
* parent
)
525 wxRichTextObject::~wxRichTextObject()
529 void wxRichTextObject::Dereference()
537 void wxRichTextObject::Copy(const wxRichTextObject
& obj
)
540 m_maxSize
= obj
.m_maxSize
;
541 m_minSize
= obj
.m_minSize
;
543 m_range
= obj
.m_range
;
544 m_ownRange
= obj
.m_ownRange
;
545 m_attributes
= obj
.m_attributes
;
546 m_properties
= obj
.m_properties
;
547 m_descent
= obj
.m_descent
;
551 // Get/set the top-level container of this object.
552 wxRichTextParagraphLayoutBox
* wxRichTextObject::GetContainer() const
554 const wxRichTextObject
* p
= this;
559 return wxDynamicCast(p
, wxRichTextParagraphLayoutBox
);
566 void wxRichTextObject::SetMargins(int margin
)
568 SetMargins(margin
, margin
, margin
, margin
);
571 void wxRichTextObject::SetMargins(int leftMargin
, int rightMargin
, int topMargin
, int bottomMargin
)
573 GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().SetValue(leftMargin
, wxTEXT_ATTR_UNITS_PIXELS
);
574 GetAttributes().GetTextBoxAttr().GetMargins().GetRight().SetValue(rightMargin
, wxTEXT_ATTR_UNITS_PIXELS
);
575 GetAttributes().GetTextBoxAttr().GetMargins().GetTop().SetValue(topMargin
, wxTEXT_ATTR_UNITS_PIXELS
);
576 GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().SetValue(bottomMargin
, wxTEXT_ATTR_UNITS_PIXELS
);
579 int wxRichTextObject::GetLeftMargin() const
581 return GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().GetValue();
584 int wxRichTextObject::GetRightMargin() const
586 return GetAttributes().GetTextBoxAttr().GetMargins().GetRight().GetValue();
589 int wxRichTextObject::GetTopMargin() const
591 return GetAttributes().GetTextBoxAttr().GetMargins().GetTop().GetValue();
594 int wxRichTextObject::GetBottomMargin() const
596 return GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().GetValue();
599 // Calculate the available content space in the given rectangle, given the
600 // margins, border and padding specified in the object's attributes.
601 wxRect
wxRichTextObject::GetAvailableContentArea(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& outerRect
) const
603 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
604 marginRect
= outerRect
;
605 wxRichTextAttr
attr(GetAttributes());
606 context
.ApplyVirtualAttributes(attr
, (wxRichTextObject
*) this);
607 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
611 // Invalidate the buffer. With no argument, invalidates whole buffer.
612 void wxRichTextObject::Invalidate(const wxRichTextRange
& invalidRange
)
614 if (invalidRange
!= wxRICHTEXT_NONE
)
616 SetCachedSize(wxDefaultSize
);
617 SetMaxSize(wxDefaultSize
);
618 SetMinSize(wxDefaultSize
);
622 // Convert units in tenths of a millimetre to device units
623 int wxRichTextObject::ConvertTenthsMMToPixels(wxDC
& dc
, int units
) const
628 scale
= GetBuffer()->GetScale();
629 int p
= ConvertTenthsMMToPixels(dc
.GetPPI().x
, units
, scale
);
634 // Convert units in tenths of a millimetre to device units
635 int wxRichTextObject::ConvertTenthsMMToPixels(int ppi
, int units
, double scale
)
637 // There are ppi pixels in 254.1 "1/10 mm"
639 double pixels
= ((double) units
* (double)ppi
) / 254.1;
643 // If the result is very small, make it at least one pixel in size.
644 if (pixels
== 0 && units
> 0)
650 // Convert units in pixels to tenths of a millimetre
651 int wxRichTextObject::ConvertPixelsToTenthsMM(wxDC
& dc
, int pixels
) const
656 scale
= GetBuffer()->GetScale();
658 return ConvertPixelsToTenthsMM(dc
.GetPPI().x
, p
, scale
);
661 int wxRichTextObject::ConvertPixelsToTenthsMM(int ppi
, int pixels
, double scale
)
663 // There are ppi pixels in 254.1 "1/10 mm"
665 double p
= double(pixels
);
670 int units
= int( p
* 254.1 / (double) ppi
);
674 // Draw the borders and background for the given rectangle and attributes.
675 // Width and height are taken to be the outer margin size, not the content.
676 bool wxRichTextObject::DrawBoxAttributes(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& attr
, const wxRect
& boxRect
, int flags
)
678 // Assume boxRect is the area around the content
679 wxRect marginRect
= boxRect
;
680 wxRect contentRect
, borderRect
, paddingRect
, outlineRect
;
682 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
684 // Margin is transparent. Draw background from margin.
685 if (attr
.HasBackgroundColour() || (flags
& wxRICHTEXT_DRAW_SELECTED
))
688 if (flags
& wxRICHTEXT_DRAW_SELECTED
)
690 // TODO: get selection colour from control?
691 colour
= wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
);
694 colour
= attr
.GetBackgroundColour();
697 wxBrush
brush(colour
);
701 dc
.DrawRectangle(borderRect
);
704 if (flags
& wxRICHTEXT_DRAW_GUIDELINES
)
706 wxRichTextAttr editBorderAttr
= attr
;
707 // TODO: make guideline colour configurable
708 editBorderAttr
.GetTextBoxAttr().GetBorder().SetColour(*wxLIGHT_GREY
);
709 editBorderAttr
.GetTextBoxAttr().GetBorder().SetWidth(1, wxTEXT_ATTR_UNITS_PIXELS
);
710 editBorderAttr
.GetTextBoxAttr().GetBorder().SetStyle(wxTEXT_BOX_ATTR_BORDER_SOLID
);
712 DrawBorder(dc
, buffer
, editBorderAttr
.GetTextBoxAttr().GetBorder(), borderRect
, flags
);
715 if (attr
.GetTextBoxAttr().GetBorder().IsValid())
716 DrawBorder(dc
, buffer
, attr
.GetTextBoxAttr().GetBorder(), borderRect
);
718 if (attr
.GetTextBoxAttr().GetOutline().IsValid())
719 DrawBorder(dc
, buffer
, attr
.GetTextBoxAttr().GetOutline(), outlineRect
);
725 bool wxRichTextObject::DrawBorder(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxTextAttrBorders
& attr
, const wxRect
& rect
, int WXUNUSED(flags
))
727 int borderLeft
= 0, borderRight
= 0, borderTop
= 0, borderBottom
= 0;
728 wxTextAttrDimensionConverter
converter(dc
, buffer
? buffer
->GetScale() : 1.0);
730 if (attr
.GetLeft().IsValid() && attr
.GetLeft().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
732 borderLeft
= converter
.GetPixels(attr
.GetLeft().GetWidth());
733 wxColour
col(attr
.GetLeft().GetColour());
735 // If pen width is > 1, resorts to a solid rectangle.
738 int penStyle
= wxSOLID
;
739 if (attr
.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
741 else if (attr
.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
742 penStyle
= wxLONG_DASH
;
743 wxPen
pen(col
, 1, penStyle
);
745 dc
.DrawLine(rect
.x
, rect
.y
, rect
.x
, rect
.y
+ rect
.height
);
748 else if (borderLeft
> 1)
754 dc
.DrawRectangle(rect
.x
, rect
.y
, borderLeft
, rect
.height
);
758 if (attr
.GetRight().IsValid() && attr
.GetRight().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
760 borderRight
= converter
.GetPixels(attr
.GetRight().GetWidth());
762 wxColour
col(attr
.GetRight().GetColour());
764 // If pen width is > 1, resorts to a solid rectangle.
765 if (borderRight
== 1)
767 int penStyle
= wxSOLID
;
768 if (attr
.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
770 else if (attr
.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
771 penStyle
= wxLONG_DASH
;
772 wxPen
pen(col
, 1, penStyle
);
774 dc
.DrawLine(rect
.x
+ rect
.width
, rect
.y
, rect
.x
+ rect
.width
, rect
.y
+ rect
.height
+ 1);
777 else if (borderRight
> 1)
783 dc
.DrawRectangle(rect
.x
+ rect
.width
- borderRight
, rect
.y
, borderRight
, rect
.height
);
787 if (attr
.GetTop().IsValid() && attr
.GetTop().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
789 borderTop
= converter
.GetPixels(attr
.GetTop().GetWidth());
791 wxColour
col(attr
.GetTop().GetColour());
793 // If pen width is > 1, resorts to a solid rectangle.
796 int penStyle
= wxSOLID
;
797 if (attr
.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
799 else if (attr
.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
800 penStyle
= wxLONG_DASH
;
801 wxPen
pen(col
, 1, penStyle
);
803 dc
.DrawLine(rect
.x
, rect
.y
, rect
.x
+ rect
.width
, rect
.y
);
806 else if (borderTop
> 1)
812 dc
.DrawRectangle(rect
.x
, rect
.y
, rect
.width
, borderTop
);
816 if (attr
.GetBottom().IsValid() && attr
.GetBottom().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
818 borderBottom
= converter
.GetPixels(attr
.GetBottom().GetWidth());
819 wxColour
col(attr
.GetTop().GetColour());
821 // If pen width is > 1, resorts to a solid rectangle.
822 if (borderBottom
== 1)
824 int penStyle
= wxSOLID
;
825 if (attr
.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
827 else if (attr
.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
828 penStyle
= wxLONG_DASH
;
829 wxPen
pen(col
, 1, penStyle
);
831 dc
.DrawLine(rect
.x
, rect
.y
+ rect
.height
, rect
.x
+ rect
.width
, rect
.y
+ rect
.height
);
834 else if (borderBottom
> 1)
840 dc
.DrawRectangle(rect
.x
, rect
.y
+ rect
.height
- borderBottom
, rect
.width
, borderBottom
);
847 // Get the various rectangles of the box model in pixels. You can either specify contentRect (inner)
848 // or marginRect (outer), and the other must be the default rectangle (no width or height).
849 // Note that the outline doesn't affect the position of the rectangle, it's drawn in whatever space
852 // | Margin | Border | Padding | CONTENT | Padding | Border | Margin |
854 bool wxRichTextObject::GetBoxRects(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& attr
, wxRect
& marginRect
, wxRect
& borderRect
, wxRect
& contentRect
, wxRect
& paddingRect
, wxRect
& outlineRect
)
856 int borderLeft
= 0, borderRight
= 0, borderTop
= 0, borderBottom
= 0;
857 int outlineLeft
= 0, outlineRight
= 0, outlineTop
= 0, outlineBottom
= 0;
858 int paddingLeft
= 0, paddingRight
= 0, paddingTop
= 0, paddingBottom
= 0;
859 int marginLeft
= 0, marginRight
= 0, marginTop
= 0, marginBottom
= 0;
861 wxTextAttrDimensionConverter
converter(dc
, buffer
? buffer
->GetScale() : 1.0);
863 if (attr
.GetTextBoxAttr().GetMargins().GetLeft().IsValid())
864 marginLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetLeft());
865 if (attr
.GetTextBoxAttr().GetMargins().GetRight().IsValid())
866 marginRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetRight());
867 if (attr
.GetTextBoxAttr().GetMargins().GetTop().IsValid())
868 marginTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetTop());
869 if (attr
.GetTextBoxAttr().GetMargins().GetBottom().IsValid())
870 marginBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetBottom());
872 if (attr
.GetTextBoxAttr().GetBorder().GetLeft().GetWidth().IsValid())
873 borderLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetLeft().GetWidth());
874 if (attr
.GetTextBoxAttr().GetBorder().GetRight().GetWidth().IsValid())
875 borderRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetRight().GetWidth());
876 if (attr
.GetTextBoxAttr().GetBorder().GetTop().GetWidth().IsValid())
877 borderTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetTop().GetWidth());
878 if (attr
.GetTextBoxAttr().GetBorder().GetBottom().GetWidth().IsValid())
879 borderBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetBottom().GetWidth());
881 if (attr
.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
882 paddingLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetLeft());
883 if (attr
.GetTextBoxAttr().GetPadding().GetRight().IsValid())
884 paddingRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetRight());
885 if (attr
.GetTextBoxAttr().GetPadding().GetTop().IsValid())
886 paddingTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetTop());
887 if (attr
.GetTextBoxAttr().GetPadding().GetBottom().IsValid())
888 paddingBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetBottom());
890 if (attr
.GetTextBoxAttr().GetOutline().GetLeft().GetWidth().IsValid())
891 outlineLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetLeft().GetWidth());
892 if (attr
.GetTextBoxAttr().GetOutline().GetRight().GetWidth().IsValid())
893 outlineRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetRight().GetWidth());
894 if (attr
.GetTextBoxAttr().GetOutline().GetTop().GetWidth().IsValid())
895 outlineTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetTop().GetWidth());
896 if (attr
.GetTextBoxAttr().GetOutline().GetBottom().GetWidth().IsValid())
897 outlineBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetBottom().GetWidth());
899 int leftTotal
= marginLeft
+ borderLeft
+ paddingLeft
;
900 int rightTotal
= marginRight
+ borderRight
+ paddingRight
;
901 int topTotal
= marginTop
+ borderTop
+ paddingTop
;
902 int bottomTotal
= marginBottom
+ borderBottom
+ paddingBottom
;
904 if (marginRect
!= wxRect())
906 contentRect
.x
= marginRect
.x
+ leftTotal
;
907 contentRect
.y
= marginRect
.y
+ topTotal
;
908 contentRect
.width
= marginRect
.width
- (leftTotal
+ rightTotal
);
909 contentRect
.height
= marginRect
.height
- (topTotal
+ bottomTotal
);
913 marginRect
.x
= contentRect
.x
- leftTotal
;
914 marginRect
.y
= contentRect
.y
- topTotal
;
915 marginRect
.width
= contentRect
.width
+ (leftTotal
+ rightTotal
);
916 marginRect
.height
= contentRect
.height
+ (topTotal
+ bottomTotal
);
919 borderRect
.x
= marginRect
.x
+ marginLeft
;
920 borderRect
.y
= marginRect
.y
+ marginTop
;
921 borderRect
.width
= marginRect
.width
- (marginLeft
+ marginRight
);
922 borderRect
.height
= marginRect
.height
- (marginTop
+ marginBottom
);
924 paddingRect
.x
= marginRect
.x
+ marginLeft
+ borderLeft
;
925 paddingRect
.y
= marginRect
.y
+ marginTop
+ borderTop
;
926 paddingRect
.width
= marginRect
.width
- (marginLeft
+ marginRight
+ borderLeft
+ borderRight
);
927 paddingRect
.height
= marginRect
.height
- (marginTop
+ marginBottom
+ borderTop
+ borderBottom
);
929 // The outline is outside the margin and doesn't influence the overall box position or content size.
930 outlineRect
.x
= marginRect
.x
- outlineLeft
;
931 outlineRect
.y
= marginRect
.y
- outlineTop
;
932 outlineRect
.width
= marginRect
.width
+ (outlineLeft
+ outlineRight
);
933 outlineRect
.height
= marginRect
.height
+ (outlineTop
+ outlineBottom
);
938 // Get the total margin for the object in pixels, taking into account margin, padding and border size
939 bool wxRichTextObject::GetTotalMargin(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& attr
, int& leftMargin
, int& rightMargin
,
940 int& topMargin
, int& bottomMargin
)
942 // Assume boxRect is the area around the content
943 wxRect contentRect
, marginRect
, borderRect
, paddingRect
, outlineRect
;
944 marginRect
= wxRect(0, 0, 1000, 1000);
946 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
948 leftMargin
= contentRect
.GetLeft() - marginRect
.GetLeft();
949 rightMargin
= marginRect
.GetRight() - contentRect
.GetRight();
950 topMargin
= contentRect
.GetTop() - marginRect
.GetTop();
951 bottomMargin
= marginRect
.GetBottom() - contentRect
.GetBottom();
956 // Returns the rectangle which the child has available to it given restrictions specified in the
957 // child attribute, e.g. 50% width of the parent, 400 pixels, x position 20% of the parent, etc.
958 // availableContainerSpace might be a parent that the cell has to compute its width relative to.
959 // E.g. a cell that's 50% of its parent.
960 wxRect
wxRichTextObject::AdjustAvailableSpace(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& WXUNUSED(parentAttr
), const wxRichTextAttr
& childAttr
, const wxRect
& availableParentSpace
, const wxRect
& availableContainerSpace
)
962 wxRect rect
= availableParentSpace
;
965 scale
= buffer
->GetScale();
967 wxTextAttrDimensionConverter
converter(dc
, scale
, availableContainerSpace
.GetSize());
969 if (childAttr
.GetTextBoxAttr().GetWidth().IsValid())
970 rect
.width
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetWidth());
972 if (childAttr
.GetTextBoxAttr().GetHeight().IsValid())
973 rect
.height
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetHeight());
975 // Can specify either left or right for the position (we're assuming we can't
976 // set the left and right edges to effectively set the size. Would we want to do that?)
977 if (childAttr
.GetTextBoxAttr().GetPosition().GetLeft().IsValid())
979 rect
.x
= rect
.x
+ converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetLeft());
981 else if (childAttr
.GetTextBoxAttr().GetPosition().GetRight().IsValid())
983 int x
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetRight());
984 if (childAttr
.GetTextBoxAttr().GetPosition().GetRight().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE
)
985 rect
.x
= availableContainerSpace
.x
+ availableContainerSpace
.width
- rect
.width
;
990 if (childAttr
.GetTextBoxAttr().GetPosition().GetTop().IsValid())
992 rect
.y
= rect
.y
+ converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetTop());
994 else if (childAttr
.GetTextBoxAttr().GetPosition().GetBottom().IsValid())
996 int y
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetBottom());
997 if (childAttr
.GetTextBoxAttr().GetPosition().GetBottom().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE
)
998 rect
.y
= availableContainerSpace
.y
+ availableContainerSpace
.height
- rect
.height
;
1003 if (rect
.GetWidth() > availableParentSpace
.GetWidth())
1004 rect
.SetWidth(availableParentSpace
.GetWidth());
1009 // Dump to output stream for debugging
1010 void wxRichTextObject::Dump(wxTextOutputStream
& stream
)
1012 stream
<< GetClassInfo()->GetClassName() << wxT("\n");
1013 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");
1014 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");
1017 // Gets the containing buffer
1018 wxRichTextBuffer
* wxRichTextObject::GetBuffer() const
1020 const wxRichTextObject
* obj
= this;
1021 while (obj
&& !obj
->IsKindOf(CLASSINFO(wxRichTextBuffer
)))
1022 obj
= obj
->GetParent();
1023 return wxDynamicCast(obj
, wxRichTextBuffer
);
1026 // Get the absolute object position, by traversing up the child/parent hierarchy
1027 wxPoint
wxRichTextObject::GetAbsolutePosition() const
1029 wxPoint pt
= GetPosition();
1031 wxRichTextObject
* p
= GetParent();
1034 pt
= pt
+ p
->GetPosition();
1041 // Hit-testing: returns a flag indicating hit test details, plus
1042 // information about position
1043 int wxRichTextObject::HitTest(wxDC
& WXUNUSED(dc
), wxRichTextDrawingContext
& WXUNUSED(context
), const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int WXUNUSED(flags
))
1046 return wxRICHTEXT_HITTEST_NONE
;
1048 wxRect rect
= GetRect();
1049 if (pt
.x
>= rect
.x
&& pt
.x
< rect
.x
+ rect
.width
&&
1050 pt
.y
>= rect
.y
&& pt
.y
< rect
.y
+ rect
.height
)
1053 *contextObj
= GetParentContainer();
1054 textPosition
= GetRange().GetStart();
1055 return wxRICHTEXT_HITTEST_ON
;
1058 return wxRICHTEXT_HITTEST_NONE
;
1061 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1062 // lays out the object again using the maximum ('best') size
1063 bool wxRichTextObject::LayoutToBestSize(wxDC
& dc
, wxRichTextDrawingContext
& context
, wxRichTextBuffer
* buffer
,
1064 const wxRichTextAttr
& parentAttr
, const wxRichTextAttr
& attr
,
1065 const wxRect
& availableParentSpace
, const wxRect
& availableContainerSpace
,
1068 wxRect availableChildRect
= AdjustAvailableSpace(dc
, buffer
, parentAttr
, attr
, availableParentSpace
, availableContainerSpace
);
1069 wxRect originalAvailableRect
= availableChildRect
;
1070 Layout(dc
, context
, availableChildRect
, availableContainerSpace
, style
);
1072 wxSize maxSize
= GetMaxSize();
1074 // Don't ignore if maxSize.x is zero, since we need to redo the paragraph's lines
1076 if (!attr
.GetTextBoxAttr().GetWidth().IsValid() && maxSize
.x
< availableChildRect
.width
)
1078 // Redo the layout with a fixed, minimum size this time.
1079 Invalidate(wxRICHTEXT_ALL
);
1080 wxRichTextAttr
newAttr(attr
);
1081 newAttr
.GetTextBoxAttr().GetWidth().SetValue(maxSize
.x
, wxTEXT_ATTR_UNITS_PIXELS
);
1082 newAttr
.GetTextBoxAttr().GetWidth().SetPosition(wxTEXT_BOX_ATTR_POSITION_ABSOLUTE
);
1084 availableChildRect
= AdjustAvailableSpace(dc
, buffer
, parentAttr
, newAttr
, availableParentSpace
, availableContainerSpace
);
1086 // If a paragraph, align the whole paragraph.
1087 // Problem with this: if we're limited by a floating object, a line may be centered
1088 // w.r.t. the smaller resulting box rather than the actual available width.
1089 if (attr
.HasAlignment() && !GetContainer()->GetFloatCollector()->HasFloats()) // FIXME: aligning whole paragraph not compatible with floating objects
1091 // centering, right-justification
1092 if (attr
.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE
)
1094 availableChildRect
.x
= (originalAvailableRect
.GetWidth() - availableChildRect
.GetWidth())/2 + availableChildRect
.x
;
1096 else if (attr
.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT
)
1098 availableChildRect
.x
= availableChildRect
.x
+ originalAvailableRect
.GetWidth() - availableChildRect
.GetWidth();
1102 Layout(dc
, context
, availableChildRect
, availableContainerSpace
, style
);
1116 // Move the object recursively, by adding the offset from old to new
1117 void wxRichTextObject::Move(const wxPoint
& pt
)
1124 * wxRichTextCompositeObject
1125 * This is the base for drawable objects.
1128 IMPLEMENT_CLASS(wxRichTextCompositeObject
, wxRichTextObject
)
1130 wxRichTextCompositeObject::wxRichTextCompositeObject(wxRichTextObject
* parent
):
1131 wxRichTextObject(parent
)
1135 wxRichTextCompositeObject::~wxRichTextCompositeObject()
1140 /// Get the nth child
1141 wxRichTextObject
* wxRichTextCompositeObject::GetChild(size_t n
) const
1143 wxASSERT ( n
< m_children
.GetCount() );
1145 return m_children
.Item(n
)->GetData();
1148 /// Append a child, returning the position
1149 size_t wxRichTextCompositeObject::AppendChild(wxRichTextObject
* child
)
1151 m_children
.Append(child
);
1152 child
->SetParent(this);
1153 return m_children
.GetCount() - 1;
1156 /// Insert the child in front of the given object, or at the beginning
1157 bool wxRichTextCompositeObject::InsertChild(wxRichTextObject
* child
, wxRichTextObject
* inFrontOf
)
1161 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(inFrontOf
);
1162 m_children
.Insert(node
, child
);
1165 m_children
.Insert(child
);
1166 child
->SetParent(this);
1171 /// Delete the child
1172 bool wxRichTextCompositeObject::RemoveChild(wxRichTextObject
* child
, bool deleteChild
)
1174 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(child
);
1177 wxRichTextObject
* obj
= node
->GetData();
1178 m_children
.Erase(node
);
1187 /// Delete all children
1188 bool wxRichTextCompositeObject::DeleteChildren()
1190 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1193 wxRichTextObjectList::compatibility_iterator oldNode
= node
;
1195 wxRichTextObject
* child
= node
->GetData();
1196 child
->Dereference(); // Only delete if reference count is zero
1198 node
= node
->GetNext();
1199 m_children
.Erase(oldNode
);
1205 /// Get the child count
1206 size_t wxRichTextCompositeObject::GetChildCount() const
1208 return m_children
.GetCount();
1212 void wxRichTextCompositeObject::Copy(const wxRichTextCompositeObject
& obj
)
1214 wxRichTextObject::Copy(obj
);
1218 wxRichTextObjectList::compatibility_iterator node
= obj
.m_children
.GetFirst();
1221 wxRichTextObject
* child
= node
->GetData();
1222 wxRichTextObject
* newChild
= child
->Clone();
1223 newChild
->SetParent(this);
1224 m_children
.Append(newChild
);
1226 node
= node
->GetNext();
1230 /// Hit-testing: returns a flag indicating hit test details, plus
1231 /// information about position
1232 int wxRichTextCompositeObject::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
1235 return wxRICHTEXT_HITTEST_NONE
;
1237 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1240 wxRichTextObject
* child
= node
->GetData();
1242 if (child
->IsShown() && child
->IsTopLevel() && (flags
& wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS
))
1244 // Just check if we hit the overall object
1245 int ret
= child
->wxRichTextObject::HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
1246 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
1249 else if (child
->IsShown())
1251 int ret
= child
->HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
1252 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
1256 node
= node
->GetNext();
1259 return wxRICHTEXT_HITTEST_NONE
;
1262 /// Finds the absolute position and row height for the given character position
1263 bool wxRichTextCompositeObject::FindPosition(wxDC
& dc
, wxRichTextDrawingContext
& context
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
1265 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1268 wxRichTextObject
* child
= node
->GetData();
1270 // Don't recurse if the child is a top-level object,
1271 // such as a text box, because the character position will no longer
1272 // apply. By definition, a top-level object has its own range of
1273 // character positions.
1274 if (!child
->IsTopLevel() && child
->FindPosition(dc
, context
, index
, pt
, height
, forceLineStart
))
1277 node
= node
->GetNext();
1284 void wxRichTextCompositeObject::CalculateRange(long start
, long& end
)
1286 long current
= start
;
1287 long lastEnd
= current
;
1295 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1298 wxRichTextObject
* child
= node
->GetData();
1301 child
->CalculateRange(current
, childEnd
);
1304 current
= childEnd
+ 1;
1306 node
= node
->GetNext();
1311 // A top-level object always has a range of size 1,
1312 // because its children don't count at this level.
1314 m_range
.SetRange(start
, start
);
1316 // An object with no children has zero length
1317 if (m_children
.GetCount() == 0)
1319 m_ownRange
.SetRange(0, lastEnd
);
1325 // An object with no children has zero length
1326 if (m_children
.GetCount() == 0)
1329 m_range
.SetRange(start
, end
);
1333 /// Delete range from layout.
1334 bool wxRichTextCompositeObject::DeleteRange(const wxRichTextRange
& range
)
1336 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1340 wxRichTextObject
* obj
= (wxRichTextObject
*) node
->GetData();
1341 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
1343 // Delete the range in each paragraph
1345 // When a chunk has been deleted, internally the content does not
1346 // now match the ranges.
1347 // However, so long as deletion is not done on the same object twice this is OK.
1348 // If you may delete content from the same object twice, recalculate
1349 // the ranges inbetween DeleteRange calls by calling CalculateRanges, and
1350 // adjust the range you're deleting accordingly.
1352 if (!obj
->GetRange().IsOutside(range
))
1354 // No need to delete within a top-level object; just removing this object will do fine
1355 if (!obj
->IsTopLevel())
1356 obj
->DeleteRange(range
);
1358 // Delete an empty object, or paragraph within this range.
1359 if (obj
->IsEmpty() ||
1360 (range
.GetStart() <= obj
->GetRange().GetStart() && range
.GetEnd() >= obj
->GetRange().GetEnd()))
1362 // An empty paragraph has length 1, so won't be deleted unless the
1363 // whole range is deleted.
1364 RemoveChild(obj
, true);
1374 /// Get any text in this object for the given range
1375 wxString
wxRichTextCompositeObject::GetTextForRange(const wxRichTextRange
& range
) const
1378 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1381 wxRichTextObject
* child
= node
->GetData();
1382 wxRichTextRange childRange
= range
;
1383 if (!child
->GetRange().IsOutside(range
))
1385 childRange
.LimitTo(child
->GetRange());
1387 wxString childText
= child
->GetTextForRange(childRange
);
1391 node
= node
->GetNext();
1397 /// Get the child object at the given character position
1398 wxRichTextObject
* wxRichTextCompositeObject::GetChildAtPosition(long pos
) const
1400 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1403 wxRichTextObject
* child
= node
->GetData();
1404 if (child
->GetRange().GetStart() == pos
)
1406 node
= node
->GetNext();
1411 /// Recursively merge all pieces that can be merged.
1412 bool wxRichTextCompositeObject::Defragment(const wxRichTextRange
& range
)
1414 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1417 wxRichTextObject
* child
= node
->GetData();
1418 if (range
== wxRICHTEXT_ALL
|| !child
->GetRange().IsOutside(range
))
1420 wxRichTextCompositeObject
* composite
= wxDynamicCast(child
, wxRichTextCompositeObject
);
1422 composite
->Defragment();
1424 if (node
->GetNext())
1426 wxRichTextObject
* nextChild
= node
->GetNext()->GetData();
1427 if (child
->CanMerge(nextChild
) && child
->Merge(nextChild
))
1429 nextChild
->Dereference();
1430 m_children
.Erase(node
->GetNext());
1432 // Don't set node -- we'll see if we can merge again with the next
1436 node
= node
->GetNext();
1439 node
= node
->GetNext();
1442 node
= node
->GetNext();
1445 // Delete any remaining empty objects, but leave at least one empty object per composite object.
1446 if (GetChildCount() > 1)
1448 node
= m_children
.GetFirst();
1451 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
1452 wxRichTextObject
* child
= node
->GetData();
1453 if (range
== wxRICHTEXT_ALL
|| !child
->GetRange().IsOutside(range
))
1455 if (child
->IsEmpty())
1457 child
->Dereference();
1458 m_children
.Erase(node
);
1463 node
= node
->GetNext();
1470 /// Dump to output stream for debugging
1471 void wxRichTextCompositeObject::Dump(wxTextOutputStream
& stream
)
1473 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1476 wxRichTextObject
* child
= node
->GetData();
1477 child
->Dump(stream
);
1478 node
= node
->GetNext();
1482 /// Get/set the object size for the given range. Returns false if the range
1483 /// is invalid for this object.
1484 bool wxRichTextCompositeObject::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
1486 if (!range
.IsWithin(GetRange()))
1491 wxArrayInt childExtents
;
1498 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1501 wxRichTextObject
* child
= node
->GetData();
1502 if (!child
->GetRange().IsOutside(range
))
1504 // Floating objects have a zero size within the paragraph.
1505 if (child
->IsFloating())
1510 if (partialExtents
->GetCount() > 0)
1511 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
1515 partialExtents
->Add(0 /* zero size */ + lastSize
);
1522 wxRichTextRange rangeToUse
= range
;
1523 rangeToUse
.LimitTo(child
->GetRange());
1524 if (child
->IsTopLevel())
1525 rangeToUse
= child
->GetOwnRange();
1527 int childDescent
= 0;
1529 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we're already cached the size,
1530 // but it's only going to be used after caching has taken place.
1531 if ((flags
& wxRICHTEXT_HEIGHT_ONLY
) && child
->GetCachedSize().y
!= 0)
1533 childDescent
= child
->GetDescent();
1534 childSize
= child
->GetCachedSize();
1536 sz
.y
= wxMax(sz
.y
, childSize
.y
);
1537 sz
.x
+= childSize
.x
;
1538 descent
= wxMax(descent
, childDescent
);
1540 else if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, context
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
), p
))
1542 sz
.y
= wxMax(sz
.y
, childSize
.y
);
1543 sz
.x
+= childSize
.x
;
1544 descent
= wxMax(descent
, childDescent
);
1546 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange() || child
->IsTopLevel()))
1548 child
->SetCachedSize(childSize
);
1549 child
->SetDescent(childDescent
);
1555 if (partialExtents
->GetCount() > 0)
1556 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
1561 for (i
= 0; i
< childExtents
.GetCount(); i
++)
1563 partialExtents
->Add(childExtents
[i
] + lastSize
);
1573 node
= node
->GetNext();
1579 // Invalidate the buffer. With no argument, invalidates whole buffer.
1580 void wxRichTextCompositeObject::Invalidate(const wxRichTextRange
& invalidRange
)
1582 wxRichTextObject::Invalidate(invalidRange
);
1584 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1587 wxRichTextObject
* child
= node
->GetData();
1588 if (invalidRange
!= wxRICHTEXT_ALL
&& invalidRange
!= wxRICHTEXT_NONE
&& child
->GetRange().IsOutside(invalidRange
))
1592 else if (child
->IsTopLevel())
1594 if (invalidRange
== wxRICHTEXT_NONE
)
1595 child
->Invalidate(wxRICHTEXT_NONE
);
1597 child
->Invalidate(wxRICHTEXT_ALL
); // All children must be invalidated if within parent range
1600 child
->Invalidate(invalidRange
);
1601 node
= node
->GetNext();
1605 // Move the object recursively, by adding the offset from old to new
1606 void wxRichTextCompositeObject::Move(const wxPoint
& pt
)
1608 wxPoint oldPos
= GetPosition();
1610 wxPoint offset
= pt
- oldPos
;
1612 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1615 wxRichTextObject
* child
= node
->GetData();
1616 wxPoint childPos
= child
->GetPosition() + offset
;
1617 child
->Move(childPos
);
1618 node
= node
->GetNext();
1624 * wxRichTextParagraphLayoutBox
1625 * This box knows how to lay out paragraphs.
1628 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraphLayoutBox
, wxRichTextCompositeObject
)
1630 wxRichTextParagraphLayoutBox::wxRichTextParagraphLayoutBox(wxRichTextObject
* parent
):
1631 wxRichTextCompositeObject(parent
)
1636 wxRichTextParagraphLayoutBox::~wxRichTextParagraphLayoutBox()
1638 if (m_floatCollector
)
1640 delete m_floatCollector
;
1641 m_floatCollector
= NULL
;
1645 /// Initialize the object.
1646 void wxRichTextParagraphLayoutBox::Init()
1650 // For now, assume is the only box and has no initial size.
1651 m_range
= wxRichTextRange(0, -1);
1652 m_ownRange
= wxRichTextRange(0, -1);
1654 m_invalidRange
= wxRICHTEXT_ALL
;
1657 m_partialParagraph
= false;
1658 m_floatCollector
= NULL
;
1661 void wxRichTextParagraphLayoutBox::Clear()
1665 if (m_floatCollector
)
1666 delete m_floatCollector
;
1667 m_floatCollector
= NULL
;
1668 m_partialParagraph
= false;
1672 void wxRichTextParagraphLayoutBox::Copy(const wxRichTextParagraphLayoutBox
& obj
)
1676 wxRichTextCompositeObject::Copy(obj
);
1678 m_partialParagraph
= obj
.m_partialParagraph
;
1679 m_defaultAttributes
= obj
.m_defaultAttributes
;
1682 // Gather information about floating objects; only gather floats for those paragraphs that
1683 // will not be formatted again due to optimization, after which floats will be gathered per-paragraph
1685 bool wxRichTextParagraphLayoutBox::UpdateFloatingObjects(const wxRect
& availableRect
, wxRichTextObject
* untilObj
)
1687 if (m_floatCollector
!= NULL
)
1688 delete m_floatCollector
;
1689 m_floatCollector
= new wxRichTextFloatCollector(availableRect
);
1690 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1691 // Only gather floats up to the point we'll start formatting paragraphs.
1692 while (untilObj
&& node
&& node
->GetData() != untilObj
)
1694 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
1695 wxASSERT (child
!= NULL
);
1697 m_floatCollector
->CollectFloat(child
);
1698 node
= node
->GetNext();
1704 // Returns the style sheet associated with the overall buffer.
1705 wxRichTextStyleSheet
* wxRichTextParagraphLayoutBox::GetStyleSheet() const
1707 return GetBuffer() ? GetBuffer()->GetStyleSheet() : (wxRichTextStyleSheet
*) NULL
;
1710 // Get the number of floating objects at this level
1711 int wxRichTextParagraphLayoutBox::GetFloatingObjectCount() const
1713 if (m_floatCollector
)
1714 return m_floatCollector
->GetFloatingObjectCount();
1719 // Get a list of floating objects
1720 bool wxRichTextParagraphLayoutBox::GetFloatingObjects(wxRichTextObjectList
& objects
) const
1722 if (m_floatCollector
)
1724 return m_floatCollector
->GetFloatingObjects(objects
);
1731 void wxRichTextParagraphLayoutBox::UpdateRanges()
1735 start
= GetRange().GetStart();
1737 CalculateRange(start
, end
);
1741 int wxRichTextParagraphLayoutBox::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
1744 return wxRICHTEXT_HITTEST_NONE
;
1746 int ret
= wxRICHTEXT_HITTEST_NONE
;
1747 if (m_floatCollector
&& (flags
& wxRICHTEXT_HITTEST_NO_FLOATING_OBJECTS
) == 0)
1748 ret
= m_floatCollector
->HitTest(dc
, context
, pt
, textPosition
, obj
, flags
);
1750 if (ret
== wxRICHTEXT_HITTEST_NONE
)
1751 return wxRichTextCompositeObject::HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
1759 /// Draw the floating objects
1760 void wxRichTextParagraphLayoutBox::DrawFloats(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
1762 if (m_floatCollector
)
1763 m_floatCollector
->Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
1766 void wxRichTextParagraphLayoutBox::MoveAnchoredObjectToParagraph(wxRichTextParagraph
* from
, wxRichTextParagraph
* to
, wxRichTextObject
* obj
)
1771 from
->RemoveChild(obj
);
1772 to
->AppendChild(obj
);
1776 bool wxRichTextParagraphLayoutBox::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
1781 wxRect
thisRect(GetPosition(), GetCachedSize());
1783 wxRichTextAttr
attr(GetAttributes());
1784 context
.ApplyVirtualAttributes(attr
, this);
1787 if (selection
.IsValid() && GetParentContainer() != this && selection
.WithinSelection(GetRange().GetStart(), GetParentContainer()))
1788 flags
|= wxRICHTEXT_DRAW_SELECTED
;
1790 // Don't draw guidelines if at top level
1791 int theseFlags
= flags
;
1793 theseFlags
&= ~wxRICHTEXT_DRAW_GUIDELINES
;
1794 DrawBoxAttributes(dc
, GetBuffer(), attr
, thisRect
, theseFlags
);
1796 DrawFloats(dc
, context
, range
, selection
, rect
, descent
, style
);
1797 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1800 wxRichTextObject
* child
= node
->GetData();
1802 if (child
&& !child
->GetRange().IsOutside(range
))
1804 wxRect
childRect(child
->GetPosition(), child
->GetCachedSize());
1805 wxRichTextRange childRange
= range
;
1806 if (child
->IsTopLevel())
1808 childRange
= child
->GetOwnRange();
1811 if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) == 0) && childRect
.GetTop() > rect
.GetBottom())
1816 else if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) == 0) && childRect
.GetBottom() < rect
.GetTop())
1821 child
->Draw(dc
, context
, childRange
, selection
, rect
, descent
, style
);
1824 node
= node
->GetNext();
1829 /// Lay the item out
1830 bool wxRichTextParagraphLayoutBox::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
)
1832 SetPosition(rect
.GetPosition());
1837 wxRect availableSpace
;
1838 bool formatRect
= (style
& wxRICHTEXT_LAYOUT_SPECIFIED_RECT
) == wxRICHTEXT_LAYOUT_SPECIFIED_RECT
;
1840 wxRichTextAttr
attr(GetAttributes());
1841 context
.ApplyVirtualAttributes(attr
, this);
1843 // If only laying out a specific area, the passed rect has a different meaning:
1844 // the visible part of the buffer. This is used in wxRichTextCtrl::OnSize,
1845 // so that during a size, only the visible part will be relaid out, or
1846 // it would take too long causing flicker. As an approximation, we assume that
1847 // everything up to the start of the visible area is laid out correctly.
1850 wxRect
rect2(0, 0, rect
.width
, rect
.height
);
1851 availableSpace
= GetAvailableContentArea(dc
, context
, rect2
);
1853 // Invalidate the part of the buffer from the first visible line
1854 // to the end. If other parts of the buffer are currently invalid,
1855 // then they too will be taken into account if they are above
1856 // the visible point.
1858 wxRichTextLine
* line
= GetLineAtYPosition(rect
.y
);
1860 startPos
= line
->GetAbsoluteRange().GetStart();
1862 Invalidate(wxRichTextRange(startPos
, GetOwnRange().GetEnd()));
1866 availableSpace
= GetAvailableContentArea(dc
, context
, rect
);
1869 int leftMargin
, rightMargin
, topMargin
, bottomMargin
;
1870 wxRichTextObject::GetTotalMargin(dc
, GetBuffer(), attr
, leftMargin
, rightMargin
,
1871 topMargin
, bottomMargin
);
1876 // The maximum paragraph maximum width, so we can set the overall maximum width for this object
1877 int maxMaxWidth
= 0;
1879 // The maximum paragraph minimum width, so we can set the overall minimum width for this object
1880 int maxMinWidth
= 0;
1882 // If we have vertical alignment, we must recalculate everything.
1883 bool hasVerticalAlignment
= (attr
.GetTextBoxAttr().HasVerticalAlignment() &&
1884 (attr
.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP
));
1886 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1888 bool layoutAll
= true;
1890 // Get invalid range, rounding to paragraph start/end.
1891 wxRichTextRange invalidRange
= GetInvalidRange(true);
1893 if (invalidRange
== wxRICHTEXT_NONE
&& !formatRect
)
1896 if (invalidRange
== wxRICHTEXT_ALL
|| hasVerticalAlignment
)
1898 else // If we know what range is affected, start laying out from that point on.
1899 if (invalidRange
.GetStart() >= GetOwnRange().GetStart())
1901 wxRichTextParagraph
* firstParagraph
= GetParagraphAtPosition(invalidRange
.GetStart());
1904 wxRichTextObjectList::compatibility_iterator firstNode
= m_children
.Find(firstParagraph
);
1905 wxRichTextObjectList::compatibility_iterator previousNode
;
1907 previousNode
= firstNode
->GetPrevious();
1912 wxRichTextParagraph
* previousParagraph
= wxDynamicCast(previousNode
->GetData(), wxRichTextParagraph
);
1913 availableSpace
.y
= previousParagraph
->GetPosition().y
+ previousParagraph
->GetCachedSize().y
;
1916 // Now we're going to start iterating from the first affected paragraph.
1924 // Gather information about only those floating objects that will not be formatted,
1925 // after which floats will be gathered per-paragraph during layout.
1926 UpdateFloatingObjects(availableSpace
, node
? node
->GetData() : (wxRichTextObject
*) NULL
);
1928 // A way to force speedy rest-of-buffer layout (the 'else' below)
1929 bool forceQuickLayout
= false;
1931 // First get the size of the paragraphs we won't be laying out
1932 wxRichTextObjectList::compatibility_iterator n
= m_children
.GetFirst();
1933 while (n
&& n
!= node
)
1935 wxRichTextParagraph
* child
= wxDynamicCast(n
->GetData(), wxRichTextParagraph
);
1938 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
1939 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
1940 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
1947 // Assume this box only contains paragraphs
1949 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
1950 // Unsure if this is needed
1951 // wxCHECK_MSG( child, false, wxT("Unknown object in layout") );
1953 if (child
&& child
->IsShown())
1955 // TODO: what if the child hasn't been laid out (e.g. involved in Undo) but still has 'old' lines
1956 if ( !forceQuickLayout
&&
1958 child
->GetLines().IsEmpty() ||
1959 !child
->GetRange().IsOutside(invalidRange
)) )
1961 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1962 // lays out the object again using the minimum size
1963 child
->LayoutToBestSize(dc
, context
, GetBuffer(),
1964 attr
, child
->GetAttributes(), availableSpace
, rect
, style
&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT
);
1966 // Layout must set the cached size
1967 availableSpace
.y
+= child
->GetCachedSize().y
;
1968 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
1969 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
1970 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
1972 // If we're just formatting the visible part of the buffer,
1973 // and we're now past the bottom of the window, and we don't have any
1974 // floating objects (since they may cause wrapping to change for the rest of the
1975 // the buffer), start quick layout.
1976 if (!hasVerticalAlignment
&& formatRect
&& child
->GetPosition().y
> rect
.GetBottom() && GetFloatingObjectCount() == 0)
1977 forceQuickLayout
= true;
1981 // We're outside the immediately affected range, so now let's just
1982 // move everything up or down. This assumes that all the children have previously
1983 // been laid out and have wrapped line lists associated with them.
1984 // TODO: check all paragraphs before the affected range.
1986 int inc
= availableSpace
.y
- child
->GetPosition().y
;
1990 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
1993 if (child
->GetLines().GetCount() == 0)
1995 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1996 // lays out the object again using the minimum size
1997 child
->LayoutToBestSize(dc
, context
, GetBuffer(),
1998 attr
, child
->GetAttributes(), availableSpace
, rect
, style
&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT
);
2000 //child->Layout(dc, availableChildRect, style);
2003 child
->Move(wxPoint(child
->GetPosition().x
, child
->GetPosition().y
+ inc
));
2005 availableSpace
.y
+= child
->GetCachedSize().y
;
2006 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
2007 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
2008 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
2011 node
= node
->GetNext();
2017 node
= node
->GetNext();
2020 node
= m_children
.GetLast();
2021 if (node
&& node
->GetData()->IsShown())
2023 wxRichTextObject
* child
= node
->GetData();
2024 maxHeight
= child
->GetPosition().y
- (GetPosition().y
+ topMargin
) + child
->GetCachedSize().y
;
2027 maxHeight
= 0; // topMargin + bottomMargin;
2029 if (attr
.GetTextBoxAttr().GetSize().GetWidth().IsValid())
2031 wxRect r
= AdjustAvailableSpace(dc
, GetBuffer(), wxRichTextAttr() /* not used */, attr
, parentRect
, parentRect
);
2032 int w
= r
.GetWidth();
2034 // Convert external to content rect
2035 w
= w
- leftMargin
- rightMargin
;
2036 maxWidth
= wxMax(maxWidth
, w
);
2037 maxMaxWidth
= wxMax(maxMaxWidth
, w
);
2040 // TODO: (also in para layout) should set the
2041 // object's size to an absolute one if specified,
2042 // but if not specified, calculate it from content.
2044 // We need to add back the margins etc.
2046 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2047 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxWidth
, maxHeight
));
2048 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2049 SetCachedSize(marginRect
.GetSize());
2052 // The maximum size is the greatest of all maximum widths for all paragraphs.
2054 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2055 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxMaxWidth
, maxHeight
)); // Actually max height is a lie, we can't know it
2056 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2057 SetMaxSize(marginRect
.GetSize());
2060 // The minimum size is the greatest of all minimum widths for all paragraphs.
2062 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2063 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxMinWidth
, maxHeight
)); // Actually max height is a lie, we can't know it
2064 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2065 SetMinSize(marginRect
.GetSize());
2068 if (attr
.GetTextBoxAttr().HasVerticalAlignment() &&
2069 (attr
.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP
))
2072 int leftOverSpace
= availableSpace
.height
- topMargin
- bottomMargin
- maxHeight
;
2073 if (leftOverSpace
> 0)
2075 if (attr
.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_CENTRE
)
2077 yOffset
= (leftOverSpace
/2);
2079 else if (attr
.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_BOTTOM
)
2081 yOffset
= leftOverSpace
;
2085 // Move all the children to vertically align the content
2086 // This doesn't take into account floating objects, unfortunately.
2089 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2092 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2094 child
->Move(wxPoint(child
->GetPosition().x
, child
->GetPosition().y
+ yOffset
));
2096 node
= node
->GetNext();
2101 m_invalidRange
= wxRICHTEXT_NONE
;
2106 /// Get/set the size for the given range.
2107 bool wxRichTextParagraphLayoutBox::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* WXUNUSED(partialExtents
)) const
2111 wxRichTextObjectList::compatibility_iterator startPara
= wxRichTextObjectList::compatibility_iterator();
2112 wxRichTextObjectList::compatibility_iterator endPara
= wxRichTextObjectList::compatibility_iterator();
2114 // First find the first paragraph whose starting position is within the range.
2115 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2118 // child is a paragraph
2119 wxRichTextObject
* child
= node
->GetData();
2120 const wxRichTextRange
& r
= child
->GetRange();
2122 if (r
.GetStart() <= range
.GetStart() && r
.GetEnd() >= range
.GetStart())
2128 node
= node
->GetNext();
2131 // Next find the last paragraph containing part of the range
2132 node
= m_children
.GetFirst();
2135 // child is a paragraph
2136 wxRichTextObject
* child
= node
->GetData();
2137 const wxRichTextRange
& r
= child
->GetRange();
2139 if (r
.GetStart() <= range
.GetEnd() && r
.GetEnd() >= range
.GetEnd())
2145 node
= node
->GetNext();
2148 if (!startPara
|| !endPara
)
2151 // Now we can add up the sizes
2152 for (node
= startPara
; node
; node
= node
->GetNext())
2154 // child is a paragraph
2155 wxRichTextObject
* child
= node
->GetData();
2156 const wxRichTextRange
& childRange
= child
->GetRange();
2157 wxRichTextRange rangeToFind
= range
;
2158 rangeToFind
.LimitTo(childRange
);
2160 if (child
->IsTopLevel())
2161 rangeToFind
= child
->GetOwnRange();
2165 int childDescent
= 0;
2166 child
->GetRangeSize(rangeToFind
, childSize
, childDescent
, dc
, context
, flags
, position
);
2168 descent
= wxMax(childDescent
, descent
);
2170 sz
.x
= wxMax(sz
.x
, childSize
.x
);
2171 sz
.y
+= childSize
.y
;
2173 if (node
== endPara
)
2182 /// Get the paragraph at the given position
2183 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphAtPosition(long pos
, bool caretPosition
) const
2188 // First find the first paragraph whose starting position is within the range.
2189 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2192 // child is a paragraph
2193 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2194 // wxASSERT (child != NULL);
2198 // Return first child in buffer if position is -1
2202 if (child
->GetRange().Contains(pos
))
2206 node
= node
->GetNext();
2211 /// Get the line at the given position
2212 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineAtPosition(long pos
, bool caretPosition
) const
2217 // First find the first paragraph whose starting position is within the range.
2218 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2221 wxRichTextObject
* obj
= (wxRichTextObject
*) node
->GetData();
2222 if (obj
->GetRange().Contains(pos
))
2224 // child is a paragraph
2225 wxRichTextParagraph
* child
= wxDynamicCast(obj
, wxRichTextParagraph
);
2226 // wxASSERT (child != NULL);
2230 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2233 wxRichTextLine
* line
= node2
->GetData();
2235 wxRichTextRange range
= line
->GetAbsoluteRange();
2237 if (range
.Contains(pos
) ||
2239 // If the position is end-of-paragraph, then return the last line of
2240 // of the paragraph.
2241 ((range
.GetEnd() == child
->GetRange().GetEnd()-1) && (pos
== child
->GetRange().GetEnd())))
2244 node2
= node2
->GetNext();
2249 node
= node
->GetNext();
2252 int lineCount
= GetLineCount();
2254 return GetLineForVisibleLineNumber(lineCount
-1);
2259 /// Get the line at the given y pixel position, or the last line.
2260 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineAtYPosition(int y
) const
2262 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2265 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2266 // wxASSERT (child != NULL);
2270 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2273 wxRichTextLine
* line
= node2
->GetData();
2275 wxRect
rect(line
->GetRect());
2277 if (y
<= rect
.GetBottom())
2280 node2
= node2
->GetNext();
2284 node
= node
->GetNext();
2288 int lineCount
= GetLineCount();
2290 return GetLineForVisibleLineNumber(lineCount
-1);
2295 /// Get the number of visible lines
2296 int wxRichTextParagraphLayoutBox::GetLineCount() const
2300 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2303 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2304 // wxASSERT (child != NULL);
2307 count
+= child
->GetLines().GetCount();
2309 node
= node
->GetNext();
2315 /// Get the paragraph for a given line
2316 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphForLine(wxRichTextLine
* line
) const
2318 return GetParagraphAtPosition(line
->GetAbsoluteRange().GetStart());
2321 /// Get the line size at the given position
2322 wxSize
wxRichTextParagraphLayoutBox::GetLineSizeAtPosition(long pos
, bool caretPosition
) const
2324 wxRichTextLine
* line
= GetLineAtPosition(pos
, caretPosition
);
2327 return line
->GetSize();
2330 return wxSize(0, 0);
2334 /// Convenience function to add a paragraph of text
2335 wxRichTextRange
wxRichTextParagraphLayoutBox::AddParagraph(const wxString
& text
, wxRichTextAttr
* paraStyle
)
2337 // Don't use the base style, just the default style, and the base style will
2338 // be combined at display time.
2339 // Divide into paragraph and character styles.
2341 wxRichTextAttr defaultCharStyle
;
2342 wxRichTextAttr defaultParaStyle
;
2344 // If the default style is a named paragraph style, don't apply any character formatting
2345 // to the initial text string.
2346 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2348 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2350 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2353 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2355 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2356 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2358 wxRichTextParagraph
* para
= new wxRichTextParagraph(text
, this, pStyle
, cStyle
);
2364 return para
->GetRange();
2367 /// Adds multiple paragraphs, based on newlines.
2368 wxRichTextRange
wxRichTextParagraphLayoutBox::AddParagraphs(const wxString
& text
, wxRichTextAttr
* paraStyle
)
2370 // Don't use the base style, just the default style, and the base style will
2371 // be combined at display time.
2372 // Divide into paragraph and character styles.
2374 wxRichTextAttr defaultCharStyle
;
2375 wxRichTextAttr defaultParaStyle
;
2377 // If the default style is a named paragraph style, don't apply any character formatting
2378 // to the initial text string.
2379 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2381 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2383 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2386 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2388 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2389 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2391 wxRichTextParagraph
* firstPara
= NULL
;
2392 wxRichTextParagraph
* lastPara
= NULL
;
2394 wxRichTextRange
range(-1, -1);
2397 size_t len
= text
.length();
2399 wxRichTextParagraph
* para
= new wxRichTextParagraph(wxEmptyString
, this, pStyle
, cStyle
);
2408 wxChar ch
= text
[i
];
2409 if (ch
== wxT('\n') || ch
== wxT('\r'))
2413 wxRichTextPlainText
* plainText
= (wxRichTextPlainText
*) para
->GetChildren().GetFirst()->GetData();
2414 plainText
->SetText(line
);
2416 para
= new wxRichTextParagraph(wxEmptyString
, this, pStyle
, cStyle
);
2421 line
= wxEmptyString
;
2432 wxRichTextPlainText
* plainText
= (wxRichTextPlainText
*) para
->GetChildren().GetFirst()->GetData();
2433 plainText
->SetText(line
);
2438 return wxRichTextRange(firstPara
->GetRange().GetStart(), lastPara
->GetRange().GetEnd());
2441 /// Convenience function to add an image
2442 wxRichTextRange
wxRichTextParagraphLayoutBox::AddImage(const wxImage
& image
, wxRichTextAttr
* paraStyle
)
2444 // Don't use the base style, just the default style, and the base style will
2445 // be combined at display time.
2446 // Divide into paragraph and character styles.
2448 wxRichTextAttr defaultCharStyle
;
2449 wxRichTextAttr defaultParaStyle
;
2451 // If the default style is a named paragraph style, don't apply any character formatting
2452 // to the initial text string.
2453 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2455 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2457 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2460 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2462 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2463 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2465 wxRichTextParagraph
* para
= new wxRichTextParagraph(this, pStyle
);
2467 para
->AppendChild(new wxRichTextImage(image
, this, cStyle
));
2471 return para
->GetRange();
2475 /// Insert fragment into this box at the given position. If partialParagraph is true,
2476 /// it is assumed that the last (or only) paragraph is just a piece of data with no paragraph
2479 bool wxRichTextParagraphLayoutBox::InsertFragment(long position
, wxRichTextParagraphLayoutBox
& fragment
)
2481 // First, find the first paragraph whose starting position is within the range.
2482 wxRichTextParagraph
* para
= GetParagraphAtPosition(position
);
2485 wxRichTextAttr originalAttr
= para
->GetAttributes();
2487 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(para
);
2489 // Now split at this position, returning the object to insert the new
2490 // ones in front of.
2491 wxRichTextObject
* nextObject
= para
->SplitAt(position
);
2493 // Special case: partial paragraph, just one paragraph. Might be a small amount of
2494 // text, for example, so let's optimize.
2496 if (fragment
.GetPartialParagraph() && fragment
.GetChildren().GetCount() == 1)
2498 // Add the first para to this para...
2499 wxRichTextObjectList::compatibility_iterator firstParaNode
= fragment
.GetChildren().GetFirst();
2503 // Iterate through the fragment paragraph inserting the content into this paragraph.
2504 wxRichTextParagraph
* firstPara
= wxDynamicCast(firstParaNode
->GetData(), wxRichTextParagraph
);
2505 wxASSERT (firstPara
!= NULL
);
2507 wxRichTextObjectList::compatibility_iterator objectNode
= firstPara
->GetChildren().GetFirst();
2510 wxRichTextObject
* newObj
= objectNode
->GetData()->Clone();
2515 para
->AppendChild(newObj
);
2519 // Insert before nextObject
2520 para
->InsertChild(newObj
, nextObject
);
2523 objectNode
= objectNode
->GetNext();
2530 // Procedure for inserting a fragment consisting of a number of
2533 // 1. Remove and save the content that's after the insertion point, for adding
2534 // back once we've added the fragment.
2535 // 2. Add the content from the first fragment paragraph to the current
2537 // 3. Add remaining fragment paragraphs after the current paragraph.
2538 // 4. Add back the saved content from the first paragraph. If partialParagraph
2539 // is true, add it to the last paragraph added and not a new one.
2541 // 1. Remove and save objects after split point.
2542 wxList savedObjects
;
2544 para
->MoveToList(nextObject
, savedObjects
);
2546 // 2. Add the content from the 1st fragment paragraph.
2547 wxRichTextObjectList::compatibility_iterator firstParaNode
= fragment
.GetChildren().GetFirst();
2551 wxRichTextParagraph
* firstPara
= wxDynamicCast(firstParaNode
->GetData(), wxRichTextParagraph
);
2552 wxASSERT(firstPara
!= NULL
);
2554 if (!(fragment
.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
))
2555 para
->SetAttributes(firstPara
->GetAttributes());
2557 // Save empty paragraph attributes for appending later
2558 // These are character attributes deliberately set for a new paragraph. Without this,
2559 // we couldn't pass default attributes when appending a new paragraph.
2560 wxRichTextAttr emptyParagraphAttributes
;
2562 wxRichTextObjectList::compatibility_iterator objectNode
= firstPara
->GetChildren().GetFirst();
2564 if (objectNode
&& firstPara
->GetChildren().GetCount() == 1 && objectNode
->GetData()->IsEmpty())
2565 emptyParagraphAttributes
= objectNode
->GetData()->GetAttributes();
2569 wxRichTextObject
* newObj
= objectNode
->GetData()->Clone();
2572 para
->AppendChild(newObj
);
2574 objectNode
= objectNode
->GetNext();
2577 // 3. Add remaining fragment paragraphs after the current paragraph.
2578 wxRichTextObjectList::compatibility_iterator nextParagraphNode
= node
->GetNext();
2579 wxRichTextObject
* nextParagraph
= NULL
;
2580 if (nextParagraphNode
)
2581 nextParagraph
= nextParagraphNode
->GetData();
2583 wxRichTextObjectList::compatibility_iterator i
= fragment
.GetChildren().GetFirst()->GetNext();
2584 wxRichTextParagraph
* finalPara
= para
;
2586 bool needExtraPara
= (!i
|| !fragment
.GetPartialParagraph());
2588 // If there was only one paragraph, we need to insert a new one.
2591 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2592 wxASSERT( para
!= NULL
);
2594 finalPara
= (wxRichTextParagraph
*) para
->Clone();
2597 InsertChild(finalPara
, nextParagraph
);
2599 AppendChild(finalPara
);
2604 // If there was only one paragraph, or we have full paragraphs in our fragment,
2605 // we need to insert a new one.
2608 finalPara
= new wxRichTextParagraph
;
2611 InsertChild(finalPara
, nextParagraph
);
2613 AppendChild(finalPara
);
2616 // 4. Add back the remaining content.
2620 finalPara
->MoveFromList(savedObjects
);
2622 // Ensure there's at least one object
2623 if (finalPara
->GetChildCount() == 0)
2625 wxRichTextPlainText
* text
= new wxRichTextPlainText(wxEmptyString
);
2626 text
->SetAttributes(emptyParagraphAttributes
);
2628 finalPara
->AppendChild(text
);
2632 if ((fragment
.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
) && firstPara
)
2633 finalPara
->SetAttributes(firstPara
->GetAttributes());
2634 else if (finalPara
&& finalPara
!= para
)
2635 finalPara
->SetAttributes(originalAttr
);
2643 wxRichTextObjectList::compatibility_iterator i
= fragment
.GetChildren().GetFirst();
2646 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2647 wxASSERT( para
!= NULL
);
2649 AppendChild(para
->Clone());
2658 /// Make a copy of the fragment corresponding to the given range, putting it in 'fragment'.
2659 /// If there was an incomplete paragraph at the end, partialParagraph is set to true.
2660 bool wxRichTextParagraphLayoutBox::CopyFragment(const wxRichTextRange
& range
, wxRichTextParagraphLayoutBox
& fragment
)
2662 wxRichTextObjectList::compatibility_iterator i
= GetChildren().GetFirst();
2665 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2666 wxASSERT( para
!= NULL
);
2668 if (!para
->GetRange().IsOutside(range
))
2670 fragment
.AppendChild(para
->Clone());
2675 // Now top and tail the first and last paragraphs in our new fragment (which might be the same).
2676 if (!fragment
.IsEmpty())
2678 wxRichTextParagraph
* firstPara
= wxDynamicCast(fragment
.GetChildren().GetFirst()->GetData(), wxRichTextParagraph
);
2679 wxASSERT( firstPara
!= NULL
);
2681 wxRichTextParagraph
* lastPara
= wxDynamicCast(fragment
.GetChildren().GetLast()->GetData(), wxRichTextParagraph
);
2682 wxASSERT( lastPara
!= NULL
);
2684 if (!firstPara
|| !lastPara
)
2687 bool isFragment
= (range
.GetEnd() < lastPara
->GetRange().GetEnd());
2689 long firstPos
= firstPara
->GetRange().GetStart();
2691 // Adjust for renumbering from zero
2692 wxRichTextRange
topTailRange(range
.GetStart() - firstPos
, range
.GetEnd() - firstPos
);
2695 fragment
.CalculateRange(0, end
);
2697 // Chop off the start of the paragraph
2698 if (topTailRange
.GetStart() > 0)
2700 wxRichTextRange
r(0, topTailRange
.GetStart()-1);
2701 firstPara
->DeleteRange(r
);
2703 // Make sure the numbering is correct
2704 fragment
.CalculateRange(0, end
);
2706 // Now, we've deleted some positions, so adjust the range
2708 topTailRange
.SetStart(range
.GetLength());
2709 topTailRange
.SetEnd(fragment
.GetOwnRange().GetEnd());
2713 topTailRange
.SetStart(range
.GetLength());
2714 topTailRange
.SetEnd(fragment
.GetOwnRange().GetEnd());
2717 if (topTailRange
.GetStart() < lastPara
->GetRange().GetEnd())
2719 lastPara
->DeleteRange(topTailRange
);
2721 // Make sure the numbering is correct
2723 fragment
.CalculateRange(0, end
);
2725 // We only have part of a paragraph at the end
2726 fragment
.SetPartialParagraph(true);
2730 // We have a partial paragraph (don't save last new paragraph marker)
2731 // or complete paragraph
2732 fragment
.SetPartialParagraph(isFragment
);
2739 /// Given a position, get the number of the visible line (potentially many to a paragraph),
2740 /// starting from zero at the start of the buffer.
2741 long wxRichTextParagraphLayoutBox::GetVisibleLineNumber(long pos
, bool caretPosition
, bool startOfLine
) const
2748 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2751 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2752 // wxASSERT( child != NULL );
2756 if (child
->GetRange().Contains(pos
))
2758 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2761 wxRichTextLine
* line
= node2
->GetData();
2762 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
2764 if (lineRange
.Contains(pos
) || pos
== lineRange
.GetStart())
2766 // If the caret is displayed at the end of the previous wrapped line,
2767 // we want to return the line it's _displayed_ at (not the actual line
2768 // containing the position).
2769 if (lineRange
.GetStart() == pos
&& !startOfLine
&& child
->GetRange().GetStart() != pos
)
2770 return lineCount
- 1;
2777 node2
= node2
->GetNext();
2779 // If we didn't find it in the lines, it must be
2780 // the last position of the paragraph. So return the last line.
2784 lineCount
+= child
->GetLines().GetCount();
2787 node
= node
->GetNext();
2794 /// Given a line number, get the corresponding wxRichTextLine object.
2795 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineForVisibleLineNumber(long lineNumber
) const
2799 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2802 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2803 // wxASSERT(child != NULL);
2807 if (lineNumber
< (int) (child
->GetLines().GetCount() + lineCount
))
2809 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2812 wxRichTextLine
* line
= node2
->GetData();
2814 if (lineCount
== lineNumber
)
2819 node2
= node2
->GetNext();
2823 lineCount
+= child
->GetLines().GetCount();
2826 node
= node
->GetNext();
2833 /// Delete range from layout.
2834 bool wxRichTextParagraphLayoutBox::DeleteRange(const wxRichTextRange
& range
)
2836 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2838 wxRichTextParagraph
* firstPara
= NULL
;
2841 wxRichTextParagraph
* obj
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2842 // wxASSERT (obj != NULL);
2844 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
2848 // Delete the range in each paragraph
2850 if (!obj
->GetRange().IsOutside(range
))
2852 // Deletes the content of this object within the given range
2853 obj
->DeleteRange(range
);
2855 wxRichTextRange thisRange
= obj
->GetRange();
2856 wxRichTextAttr thisAttr
= obj
->GetAttributes();
2858 // If the whole paragraph is within the range to delete,
2859 // delete the whole thing.
2860 if (range
.GetStart() <= thisRange
.GetStart() && range
.GetEnd() >= thisRange
.GetEnd())
2862 // Delete the whole object
2863 RemoveChild(obj
, true);
2866 else if (!firstPara
)
2869 // If the range includes the paragraph end, we need to join this
2870 // and the next paragraph.
2871 if (range
.GetEnd() <= thisRange
.GetEnd())
2873 // We need to move the objects from the next paragraph
2874 // to this paragraph
2876 wxRichTextParagraph
* nextParagraph
= NULL
;
2877 if ((range
.GetEnd() < thisRange
.GetEnd()) && obj
)
2878 nextParagraph
= obj
;
2881 // We're ending at the end of the paragraph, so merge the _next_ paragraph.
2883 nextParagraph
= wxDynamicCast(next
->GetData(), wxRichTextParagraph
);
2886 bool applyFinalParagraphStyle
= firstPara
&& nextParagraph
&& nextParagraph
!= firstPara
;
2888 wxRichTextAttr nextParaAttr
;
2889 if (applyFinalParagraphStyle
)
2891 // Special case when deleting the end of a paragraph - use _this_ paragraph's style,
2892 // not the next one.
2893 if (range
.GetStart() == range
.GetEnd() && range
.GetStart() == thisRange
.GetEnd())
2894 nextParaAttr
= thisAttr
;
2896 nextParaAttr
= nextParagraph
->GetAttributes();
2899 if (firstPara
&& nextParagraph
&& firstPara
!= nextParagraph
)
2901 // Move the objects to the previous para
2902 wxRichTextObjectList::compatibility_iterator node1
= nextParagraph
->GetChildren().GetFirst();
2906 wxRichTextObject
* obj1
= node1
->GetData();
2908 firstPara
->AppendChild(obj1
);
2910 wxRichTextObjectList::compatibility_iterator next1
= node1
->GetNext();
2911 nextParagraph
->GetChildren().Erase(node1
);
2916 // Delete the paragraph
2917 RemoveChild(nextParagraph
, true);
2920 // Avoid empty paragraphs
2921 if (firstPara
&& firstPara
->GetChildren().GetCount() == 0)
2923 wxRichTextPlainText
* text
= new wxRichTextPlainText(wxEmptyString
);
2924 firstPara
->AppendChild(text
);
2927 if (applyFinalParagraphStyle
)
2928 firstPara
->SetAttributes(nextParaAttr
);
2941 /// Get any text in this object for the given range
2942 wxString
wxRichTextParagraphLayoutBox::GetTextForRange(const wxRichTextRange
& range
) const
2946 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2949 wxRichTextObject
* child
= node
->GetData();
2950 if (!child
->GetRange().IsOutside(range
))
2952 wxRichTextRange childRange
= range
;
2953 childRange
.LimitTo(child
->GetRange());
2955 wxString childText
= child
->GetTextForRange(childRange
);
2959 if ((childRange
.GetEnd() == child
->GetRange().GetEnd()) && node
->GetNext())
2964 node
= node
->GetNext();
2970 /// Get all the text
2971 wxString
wxRichTextParagraphLayoutBox::GetText() const
2973 return GetTextForRange(GetOwnRange());
2976 /// Get the paragraph by number
2977 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphAtLine(long paragraphNumber
) const
2979 if ((size_t) paragraphNumber
>= GetChildCount())
2982 return (wxRichTextParagraph
*) GetChild((size_t) paragraphNumber
);
2985 /// Get the length of the paragraph
2986 int wxRichTextParagraphLayoutBox::GetParagraphLength(long paragraphNumber
) const
2988 wxRichTextParagraph
* para
= GetParagraphAtLine(paragraphNumber
);
2990 return para
->GetRange().GetLength() - 1; // don't include newline
2995 /// Get the text of the paragraph
2996 wxString
wxRichTextParagraphLayoutBox::GetParagraphText(long paragraphNumber
) const
2998 wxRichTextParagraph
* para
= GetParagraphAtLine(paragraphNumber
);
3000 return para
->GetTextForRange(para
->GetRange());
3002 return wxEmptyString
;
3005 /// Convert zero-based line column and paragraph number to a position.
3006 long wxRichTextParagraphLayoutBox::XYToPosition(long x
, long y
) const
3008 wxRichTextParagraph
* para
= GetParagraphAtLine(y
);
3011 return para
->GetRange().GetStart() + x
;
3017 /// Convert zero-based position to line column and paragraph number
3018 bool wxRichTextParagraphLayoutBox::PositionToXY(long pos
, long* x
, long* y
) const
3020 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
);
3024 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3027 wxRichTextObject
* child
= node
->GetData();
3031 node
= node
->GetNext();
3035 *x
= pos
- para
->GetRange().GetStart();
3043 /// Get the leaf object in a paragraph at this position.
3044 /// Given a line number, get the corresponding wxRichTextLine object.
3045 wxRichTextObject
* wxRichTextParagraphLayoutBox::GetLeafObjectAtPosition(long position
) const
3047 wxRichTextParagraph
* para
= GetParagraphAtPosition(position
);
3050 wxRichTextObjectList::compatibility_iterator node
= para
->GetChildren().GetFirst();
3054 wxRichTextObject
* child
= node
->GetData();
3055 if (child
->GetRange().Contains(position
))
3058 node
= node
->GetNext();
3060 if (position
== para
->GetRange().GetEnd() && para
->GetChildCount() > 0)
3061 return para
->GetChildren().GetLast()->GetData();
3066 /// Set character or paragraph text attributes: apply character styles only to immediate text nodes
3067 bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange
& range
, const wxRichTextAttr
& style
, int flags
)
3069 bool characterStyle
= false;
3070 bool paragraphStyle
= false;
3072 if (style
.IsCharacterStyle())
3073 characterStyle
= true;
3074 if (style
.IsParagraphStyle())
3075 paragraphStyle
= true;
3077 wxRichTextBuffer
* buffer
= GetBuffer();
3079 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
3080 bool applyMinimal
= ((flags
& wxRICHTEXT_SETSTYLE_OPTIMIZE
) != 0);
3081 bool parasOnly
= ((flags
& wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY
) != 0);
3082 bool charactersOnly
= ((flags
& wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY
) != 0);
3083 bool resetExistingStyle
= ((flags
& wxRICHTEXT_SETSTYLE_RESET
) != 0);
3084 bool removeStyle
= ((flags
& wxRICHTEXT_SETSTYLE_REMOVE
) != 0);
3086 // Apply paragraph style first, if any
3087 wxRichTextAttr
wholeStyle(style
);
3089 if (!removeStyle
&& wholeStyle
.HasParagraphStyleName() && buffer
->GetStyleSheet())
3091 wxRichTextParagraphStyleDefinition
* def
= buffer
->GetStyleSheet()->FindParagraphStyle(wholeStyle
.GetParagraphStyleName());
3093 wxRichTextApplyStyle(wholeStyle
, def
->GetStyleMergedWithBase(buffer
->GetStyleSheet()));
3096 // Limit the attributes to be set to the content to only character attributes.
3097 wxRichTextAttr
characterAttributes(wholeStyle
);
3098 characterAttributes
.SetFlags(characterAttributes
.GetFlags() & (wxTEXT_ATTR_CHARACTER
));
3100 if (!removeStyle
&& characterAttributes
.HasCharacterStyleName() && buffer
->GetStyleSheet())
3102 wxRichTextCharacterStyleDefinition
* def
= buffer
->GetStyleSheet()->FindCharacterStyle(characterAttributes
.GetCharacterStyleName());
3104 wxRichTextApplyStyle(characterAttributes
, def
->GetStyleMergedWithBase(buffer
->GetStyleSheet()));
3107 // If we are associated with a control, make undoable; otherwise, apply immediately
3110 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3112 wxRichTextAction
* action
= NULL
;
3114 if (haveControl
&& withUndo
)
3116 action
= new wxRichTextAction(NULL
, _("Change Style"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
3117 action
->SetRange(range
);
3118 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3121 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3124 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3125 // wxASSERT (para != NULL);
3127 if (para
&& para
->GetChildCount() > 0)
3129 // Stop searching if we're beyond the range of interest
3130 if (para
->GetRange().GetStart() > range
.GetEnd())
3133 if (!para
->GetRange().IsOutside(range
))
3135 // We'll be using a copy of the paragraph to make style changes,
3136 // not updating the buffer directly.
3137 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3139 if (haveControl
&& withUndo
)
3141 newPara
= new wxRichTextParagraph(*para
);
3142 action
->GetNewParagraphs().AppendChild(newPara
);
3144 // Also store the old ones for Undo
3145 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3150 // If we're specifying paragraphs only, then we really mean character formatting
3151 // to be included in the paragraph style
3152 if ((paragraphStyle
|| parasOnly
) && !charactersOnly
)
3156 // Removes the given style from the paragraph
3157 wxRichTextRemoveStyle(newPara
->GetAttributes(), style
);
3159 else if (resetExistingStyle
)
3160 newPara
->GetAttributes() = wholeStyle
;
3165 // Only apply attributes that will make a difference to the combined
3166 // style as seen on the display
3167 wxRichTextAttr
combinedAttr(para
->GetCombinedAttributes(true));
3168 wxRichTextApplyStyle(newPara
->GetAttributes(), wholeStyle
, & combinedAttr
);
3171 wxRichTextApplyStyle(newPara
->GetAttributes(), wholeStyle
);
3175 // When applying paragraph styles dynamically, don't change the text objects' attributes
3176 // since they will computed as needed. Only apply the character styling if it's _only_
3177 // character styling. This policy is subject to change and might be put under user control.
3179 // Hm. we might well be applying a mix of paragraph and character styles, in which
3180 // case we _do_ want to apply character styles regardless of what para styles are set.
3181 // But if we're applying a paragraph style, which has some character attributes, but
3182 // we only want the paragraphs to hold this character style, then we _don't_ want to
3183 // apply the character style. So we need to be able to choose.
3185 if (!parasOnly
&& (characterStyle
|charactersOnly
) && range
.GetStart() != newPara
->GetRange().GetEnd())
3187 wxRichTextRange
childRange(range
);
3188 childRange
.LimitTo(newPara
->GetRange());
3190 // Find the starting position and if necessary split it so
3191 // we can start applying a different style.
3192 // TODO: check that the style actually changes or is different
3193 // from style outside of range
3194 wxRichTextObject
* firstObject
wxDUMMY_INITIALIZE(NULL
);
3195 wxRichTextObject
* lastObject
wxDUMMY_INITIALIZE(NULL
);
3197 if (childRange
.GetStart() == newPara
->GetRange().GetStart())
3198 firstObject
= newPara
->GetChildren().GetFirst()->GetData();
3200 firstObject
= newPara
->SplitAt(range
.GetStart());
3202 // Increment by 1 because we're apply the style one _after_ the split point
3203 long splitPoint
= childRange
.GetEnd();
3204 if (splitPoint
!= newPara
->GetRange().GetEnd())
3208 if (splitPoint
== newPara
->GetRange().GetEnd())
3209 lastObject
= newPara
->GetChildren().GetLast()->GetData();
3211 // lastObject is set as a side-effect of splitting. It's
3212 // returned as the object before the new object.
3213 (void) newPara
->SplitAt(splitPoint
, & lastObject
);
3215 wxASSERT(firstObject
!= NULL
);
3216 wxASSERT(lastObject
!= NULL
);
3218 if (!firstObject
|| !lastObject
)
3221 wxRichTextObjectList::compatibility_iterator firstNode
= newPara
->GetChildren().Find(firstObject
);
3222 wxRichTextObjectList::compatibility_iterator lastNode
= newPara
->GetChildren().Find(lastObject
);
3224 wxASSERT(firstNode
);
3227 wxRichTextObjectList::compatibility_iterator node2
= firstNode
;
3231 wxRichTextObject
* child
= node2
->GetData();
3235 // Removes the given style from the paragraph
3236 wxRichTextRemoveStyle(child
->GetAttributes(), style
);
3238 else if (resetExistingStyle
)
3239 child
->GetAttributes() = characterAttributes
;
3244 // Only apply attributes that will make a difference to the combined
3245 // style as seen on the display
3246 wxRichTextAttr
combinedAttr(newPara
->GetCombinedAttributes(child
->GetAttributes(), true));
3247 wxRichTextApplyStyle(child
->GetAttributes(), characterAttributes
, & combinedAttr
);
3250 wxRichTextApplyStyle(child
->GetAttributes(), characterAttributes
);
3253 if (node2
== lastNode
)
3256 node2
= node2
->GetNext();
3262 node
= node
->GetNext();
3265 // Do action, or delay it until end of batch.
3266 if (haveControl
&& withUndo
)
3267 buffer
->SubmitAction(action
);
3272 // Just change the attributes for this single object.
3273 void wxRichTextParagraphLayoutBox::SetStyle(wxRichTextObject
* obj
, const wxRichTextAttr
& textAttr
, int flags
)
3275 wxRichTextBuffer
* buffer
= GetBuffer();
3276 bool withUndo
= flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
;
3277 bool resetExistingStyle
= ((flags
& wxRICHTEXT_SETSTYLE_RESET
) != 0);
3278 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3280 wxRichTextAction
*action
= NULL
;
3281 wxRichTextAttr newAttr
= obj
->GetAttributes();
3282 if (resetExistingStyle
)
3285 newAttr
.Apply(textAttr
);
3287 if (haveControl
&& withUndo
)
3289 action
= new wxRichTextAction(NULL
, _("Change Object Style"), wxRICHTEXT_CHANGE_ATTRIBUTES
, buffer
, obj
->GetContainer(), buffer
->GetRichTextCtrl());
3290 action
->SetRange(obj
->GetRange().FromInternal());
3291 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3292 action
->MakeObject(obj
);
3294 action
->GetAttributes() = newAttr
;
3297 obj
->GetAttributes() = newAttr
;
3299 if (haveControl
&& withUndo
)
3300 buffer
->SubmitAction(action
);
3303 /// Get the text attributes for this position.
3304 bool wxRichTextParagraphLayoutBox::GetStyle(long position
, wxRichTextAttr
& style
)
3306 return DoGetStyle(position
, style
, true);
3309 bool wxRichTextParagraphLayoutBox::GetUncombinedStyle(long position
, wxRichTextAttr
& style
)
3311 return DoGetStyle(position
, style
, false);
3314 /// Implementation helper for GetStyle. If combineStyles is true, combine base, paragraph and
3315 /// context attributes.
3316 bool wxRichTextParagraphLayoutBox::DoGetStyle(long position
, wxRichTextAttr
& style
, bool combineStyles
)
3318 wxRichTextObject
* obj
wxDUMMY_INITIALIZE(NULL
);
3320 if (style
.IsParagraphStyle())
3322 obj
= GetParagraphAtPosition(position
);
3327 // Start with the base style
3328 style
= GetAttributes();
3330 // Apply the paragraph style
3331 wxRichTextApplyStyle(style
, obj
->GetAttributes());
3334 style
= obj
->GetAttributes();
3341 obj
= GetLeafObjectAtPosition(position
);
3346 wxRichTextParagraph
* para
= wxDynamicCast(obj
->GetParent(), wxRichTextParagraph
);
3347 style
= para
? para
->GetCombinedAttributes(obj
->GetAttributes()) : obj
->GetAttributes();
3350 style
= obj
->GetAttributes();
3358 static bool wxHasStyle(long flags
, long style
)
3360 return (flags
& style
) != 0;
3363 /// Combines 'style' with 'currentStyle' for the purpose of summarising the attributes of a range of
3365 bool wxRichTextParagraphLayoutBox::CollectStyle(wxRichTextAttr
& currentStyle
, const wxRichTextAttr
& style
, wxRichTextAttr
& clashingAttr
, wxRichTextAttr
& absentAttr
)
3367 currentStyle
.CollectCommonAttributes(style
, clashingAttr
, absentAttr
);
3372 /// Get the combined style for a range - if any attribute is different within the range,
3373 /// that attribute is not present within the flags.
3374 /// *** Note that this is not recursive, and so assumes that content inside a paragraph is not itself
3376 bool wxRichTextParagraphLayoutBox::GetStyleForRange(const wxRichTextRange
& range
, wxRichTextAttr
& style
)
3378 style
= wxRichTextAttr();
3380 wxRichTextAttr clashingAttr
;
3381 wxRichTextAttr absentAttrPara
, absentAttrChar
;
3383 wxRichTextObjectList::compatibility_iterator node
= GetChildren().GetFirst();
3386 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3387 if (para
&& !(para
->GetRange().GetStart() > range
.GetEnd() || para
->GetRange().GetEnd() < range
.GetStart()))
3389 if (para
->GetChildren().GetCount() == 0)
3391 wxRichTextAttr paraStyle
= para
->GetCombinedAttributes(true /* use box attributes */);
3393 CollectStyle(style
, paraStyle
, clashingAttr
, absentAttrPara
);
3397 wxRichTextRange
paraRange(para
->GetRange());
3398 paraRange
.LimitTo(range
);
3400 // First collect paragraph attributes only
3401 wxRichTextAttr paraStyle
= para
->GetCombinedAttributes();
3402 paraStyle
.SetFlags(paraStyle
.GetFlags() & wxTEXT_ATTR_PARAGRAPH
);
3403 CollectStyle(style
, paraStyle
, clashingAttr
, absentAttrPara
);
3405 wxRichTextObjectList::compatibility_iterator childNode
= para
->GetChildren().GetFirst();
3409 wxRichTextObject
* child
= childNode
->GetData();
3410 if (!(child
->GetRange().GetStart() > range
.GetEnd() || child
->GetRange().GetEnd() < range
.GetStart()))
3412 wxRichTextAttr childStyle
= para
->GetCombinedAttributes(child
->GetAttributes(), true /* include box attributes */);
3414 // Now collect character attributes only
3415 childStyle
.SetFlags(childStyle
.GetFlags() & wxTEXT_ATTR_CHARACTER
);
3417 CollectStyle(style
, childStyle
, clashingAttr
, absentAttrChar
);
3420 childNode
= childNode
->GetNext();
3424 node
= node
->GetNext();
3429 /// Set default style
3430 bool wxRichTextParagraphLayoutBox::SetDefaultStyle(const wxRichTextAttr
& style
)
3432 m_defaultAttributes
= style
;
3436 /// Test if this whole range has character attributes of the specified kind. If any
3437 /// of the attributes are different within the range, the test fails. You
3438 /// can use this to implement, for example, bold button updating. style must have
3439 /// flags indicating which attributes are of interest.
3440 bool wxRichTextParagraphLayoutBox::HasCharacterAttributes(const wxRichTextRange
& range
, const wxRichTextAttr
& style
) const
3443 int matchingCount
= 0;
3445 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3448 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3449 // wxASSERT (para != NULL);
3453 // Stop searching if we're beyond the range of interest
3454 if (para
->GetRange().GetStart() > range
.GetEnd())
3455 return foundCount
== matchingCount
&& foundCount
!= 0;
3457 if (!para
->GetRange().IsOutside(range
))
3459 wxRichTextObjectList::compatibility_iterator node2
= para
->GetChildren().GetFirst();
3463 wxRichTextObject
* child
= node2
->GetData();
3464 // Allow for empty string if no buffer
3465 wxRichTextRange childRange
= child
->GetRange();
3466 if (childRange
.GetLength() == 0 && GetRange().GetLength() == 1)
3467 childRange
.SetEnd(childRange
.GetEnd()+1);
3469 if (!childRange
.IsOutside(range
) && child
->IsKindOf(CLASSINFO(wxRichTextPlainText
)))
3472 wxRichTextAttr textAttr
= para
->GetCombinedAttributes(child
->GetAttributes());
3474 if (wxTextAttrEqPartial(textAttr
, style
))
3478 node2
= node2
->GetNext();
3483 node
= node
->GetNext();
3486 return foundCount
== matchingCount
&& foundCount
!= 0;
3489 /// Test if this whole range has paragraph attributes of the specified kind. If any
3490 /// of the attributes are different within the range, the test fails. You
3491 /// can use this to implement, for example, centering button updating. style must have
3492 /// flags indicating which attributes are of interest.
3493 bool wxRichTextParagraphLayoutBox::HasParagraphAttributes(const wxRichTextRange
& range
, const wxRichTextAttr
& style
) const
3496 int matchingCount
= 0;
3498 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3501 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3502 // wxASSERT (para != NULL);
3506 // Stop searching if we're beyond the range of interest
3507 if (para
->GetRange().GetStart() > range
.GetEnd())
3508 return foundCount
== matchingCount
&& foundCount
!= 0;
3510 if (!para
->GetRange().IsOutside(range
))
3512 wxRichTextAttr textAttr
= GetAttributes();
3513 // Apply the paragraph style
3514 wxRichTextApplyStyle(textAttr
, para
->GetAttributes());
3517 if (wxTextAttrEqPartial(textAttr
, style
))
3522 node
= node
->GetNext();
3524 return foundCount
== matchingCount
&& foundCount
!= 0;
3527 void wxRichTextParagraphLayoutBox::PrepareContent(wxRichTextParagraphLayoutBox
& container
)
3529 wxRichTextBuffer
* buffer
= GetBuffer();
3530 if (buffer
&& buffer
->GetRichTextCtrl())
3531 buffer
->GetRichTextCtrl()->PrepareContent(container
);
3535 /// Set character or paragraph properties
3536 bool wxRichTextParagraphLayoutBox::SetProperties(const wxRichTextRange
& range
, const wxRichTextProperties
& properties
, int flags
)
3538 wxRichTextBuffer
* buffer
= GetBuffer();
3540 bool withUndo
= ((flags
& wxRICHTEXT_SETPROPERTIES_WITH_UNDO
) != 0);
3541 bool parasOnly
= ((flags
& wxRICHTEXT_SETPROPERTIES_PARAGRAPHS_ONLY
) != 0);
3542 bool charactersOnly
= ((flags
& wxRICHTEXT_SETPROPERTIES_CHARACTERS_ONLY
) != 0);
3543 bool resetExistingProperties
= ((flags
& wxRICHTEXT_SETPROPERTIES_RESET
) != 0);
3544 bool removeProperties
= ((flags
& wxRICHTEXT_SETPROPERTIES_REMOVE
) != 0);
3546 // If we are associated with a control, make undoable; otherwise, apply immediately
3549 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3551 wxRichTextAction
* action
= NULL
;
3553 if (haveControl
&& withUndo
)
3555 action
= new wxRichTextAction(NULL
, _("Change Properties"), wxRICHTEXT_CHANGE_PROPERTIES
, buffer
, this, buffer
->GetRichTextCtrl());
3556 action
->SetRange(range
);
3557 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3560 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3563 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3564 // wxASSERT (para != NULL);
3566 if (para
&& para
->GetChildCount() > 0)
3568 // Stop searching if we're beyond the range of interest
3569 if (para
->GetRange().GetStart() > range
.GetEnd())
3572 if (!para
->GetRange().IsOutside(range
))
3574 // We'll be using a copy of the paragraph to make style changes,
3575 // not updating the buffer directly.
3576 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3578 if (haveControl
&& withUndo
)
3580 newPara
= new wxRichTextParagraph(*para
);
3581 action
->GetNewParagraphs().AppendChild(newPara
);
3583 // Also store the old ones for Undo
3584 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3591 if (removeProperties
)
3593 // Removes the given style from the paragraph
3595 newPara
->GetProperties().RemoveProperties(properties
);
3597 else if (resetExistingProperties
)
3598 newPara
->GetProperties() = properties
;
3600 newPara
->GetProperties().MergeProperties(properties
);
3603 // When applying paragraph styles dynamically, don't change the text objects' attributes
3604 // since they will computed as needed. Only apply the character styling if it's _only_
3605 // character styling. This policy is subject to change and might be put under user control.
3607 // Hm. we might well be applying a mix of paragraph and character styles, in which
3608 // case we _do_ want to apply character styles regardless of what para styles are set.
3609 // But if we're applying a paragraph style, which has some character attributes, but
3610 // we only want the paragraphs to hold this character style, then we _don't_ want to
3611 // apply the character style. So we need to be able to choose.
3613 if (!parasOnly
&& charactersOnly
&& range
.GetStart() != newPara
->GetRange().GetEnd())
3615 wxRichTextRange
childRange(range
);
3616 childRange
.LimitTo(newPara
->GetRange());
3618 // Find the starting position and if necessary split it so
3619 // we can start applying different properties.
3620 // TODO: check that the properties actually change or are different
3621 // from properties outside of range
3622 wxRichTextObject
* firstObject
wxDUMMY_INITIALIZE(NULL
);
3623 wxRichTextObject
* lastObject
wxDUMMY_INITIALIZE(NULL
);
3625 if (childRange
.GetStart() == newPara
->GetRange().GetStart())
3626 firstObject
= newPara
->GetChildren().GetFirst()->GetData();
3628 firstObject
= newPara
->SplitAt(range
.GetStart());
3630 // Increment by 1 because we're apply the style one _after_ the split point
3631 long splitPoint
= childRange
.GetEnd();
3632 if (splitPoint
!= newPara
->GetRange().GetEnd())
3636 if (splitPoint
== newPara
->GetRange().GetEnd())
3637 lastObject
= newPara
->GetChildren().GetLast()->GetData();
3639 // lastObject is set as a side-effect of splitting. It's
3640 // returned as the object before the new object.
3641 (void) newPara
->SplitAt(splitPoint
, & lastObject
);
3643 wxASSERT(firstObject
!= NULL
);
3644 wxASSERT(lastObject
!= NULL
);
3646 if (!firstObject
|| !lastObject
)
3649 wxRichTextObjectList::compatibility_iterator firstNode
= newPara
->GetChildren().Find(firstObject
);
3650 wxRichTextObjectList::compatibility_iterator lastNode
= newPara
->GetChildren().Find(lastObject
);
3652 wxASSERT(firstNode
);
3655 wxRichTextObjectList::compatibility_iterator node2
= firstNode
;
3659 wxRichTextObject
* child
= node2
->GetData();
3661 if (removeProperties
)
3663 // Removes the given properties from the paragraph
3664 child
->GetProperties().RemoveProperties(properties
);
3666 else if (resetExistingProperties
)
3667 child
->GetProperties() = properties
;
3670 child
->GetProperties().MergeProperties(properties
);
3673 if (node2
== lastNode
)
3676 node2
= node2
->GetNext();
3682 node
= node
->GetNext();
3685 // Do action, or delay it until end of batch.
3686 if (haveControl
&& withUndo
)
3687 buffer
->SubmitAction(action
);
3692 void wxRichTextParagraphLayoutBox::Reset()
3696 wxRichTextBuffer
* buffer
= GetBuffer();
3697 if (buffer
&& buffer
->GetRichTextCtrl())
3699 wxRichTextEvent
event(wxEVT_COMMAND_RICHTEXT_BUFFER_RESET
, buffer
->GetRichTextCtrl()->GetId());
3700 event
.SetEventObject(buffer
->GetRichTextCtrl());
3701 event
.SetContainer(this);
3703 buffer
->SendEvent(event
, true);
3706 AddParagraph(wxEmptyString
);
3708 PrepareContent(*this);
3710 InvalidateHierarchy(wxRICHTEXT_ALL
);
3713 /// Invalidate the buffer. With no argument, invalidates whole buffer.
3714 void wxRichTextParagraphLayoutBox::Invalidate(const wxRichTextRange
& invalidRange
)
3716 wxRichTextCompositeObject::Invalidate(invalidRange
);
3718 DoInvalidate(invalidRange
);
3721 // Do the (in)validation for this object only
3722 void wxRichTextParagraphLayoutBox::DoInvalidate(const wxRichTextRange
& invalidRange
)
3724 if (invalidRange
== wxRICHTEXT_ALL
)
3726 m_invalidRange
= wxRICHTEXT_ALL
;
3728 // Already invalidating everything
3729 else if (m_invalidRange
== wxRICHTEXT_ALL
)
3734 if ((invalidRange
.GetStart() < m_invalidRange
.GetStart()) || m_invalidRange
.GetStart() == -1)
3735 m_invalidRange
.SetStart(invalidRange
.GetStart());
3736 if (invalidRange
.GetEnd() > m_invalidRange
.GetEnd())
3737 m_invalidRange
.SetEnd(invalidRange
.GetEnd());
3741 // Do the (in)validation both up and down the hierarchy
3742 void wxRichTextParagraphLayoutBox::InvalidateHierarchy(const wxRichTextRange
& invalidRange
)
3744 Invalidate(invalidRange
);
3746 if (invalidRange
!= wxRICHTEXT_NONE
)
3748 // Now go up the hierarchy
3749 wxRichTextObject
* thisObj
= this;
3750 wxRichTextObject
* p
= GetParent();
3753 wxRichTextParagraphLayoutBox
* l
= wxDynamicCast(p
, wxRichTextParagraphLayoutBox
);
3755 l
->DoInvalidate(thisObj
->GetRange());
3763 /// Get invalid range, rounding to entire paragraphs if argument is true.
3764 wxRichTextRange
wxRichTextParagraphLayoutBox::GetInvalidRange(bool wholeParagraphs
) const
3766 if (m_invalidRange
== wxRICHTEXT_ALL
|| m_invalidRange
== wxRICHTEXT_NONE
)
3767 return m_invalidRange
;
3769 wxRichTextRange range
= m_invalidRange
;
3771 if (wholeParagraphs
)
3773 wxRichTextParagraph
* para1
= GetParagraphAtPosition(range
.GetStart());
3775 range
.SetStart(para1
->GetRange().GetStart());
3776 // floating layout make all child should be relayout
3777 range
.SetEnd(GetOwnRange().GetEnd());
3782 /// Apply the style sheet to the buffer, for example if the styles have changed.
3783 bool wxRichTextParagraphLayoutBox::ApplyStyleSheet(wxRichTextStyleSheet
* styleSheet
)
3785 wxASSERT(styleSheet
!= NULL
);
3791 wxRichTextAttr
attr(GetBasicStyle());
3792 if (GetBasicStyle().HasParagraphStyleName())
3794 wxRichTextParagraphStyleDefinition
* paraDef
= styleSheet
->FindParagraphStyle(GetBasicStyle().GetParagraphStyleName());
3797 attr
.Apply(paraDef
->GetStyleMergedWithBase(styleSheet
));
3798 SetBasicStyle(attr
);
3803 if (GetBasicStyle().HasCharacterStyleName())
3805 wxRichTextCharacterStyleDefinition
* charDef
= styleSheet
->FindCharacterStyle(GetBasicStyle().GetCharacterStyleName());
3808 attr
.Apply(charDef
->GetStyleMergedWithBase(styleSheet
));
3809 SetBasicStyle(attr
);
3814 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3817 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3818 // wxASSERT (para != NULL);
3822 // Combine paragraph and list styles. If there is a list style in the original attributes,
3823 // the current indentation overrides anything else and is used to find the item indentation.
3824 // Also, for applying paragraph styles, consider having 2 modes: (1) we merge with what we have,
3825 // thereby taking into account all user changes, (2) reset the style completely (except for indentation/list
3826 // exception as above).
3827 // Problem: when changing from one list style to another, there's a danger that the level info will get lost.
3828 // So when changing a list style interactively, could retrieve level based on current style, then
3829 // set appropriate indent and apply new style.
3833 if (para
->GetAttributes().HasOutlineLevel())
3834 outline
= para
->GetAttributes().GetOutlineLevel();
3835 if (para
->GetAttributes().HasBulletNumber())
3836 num
= para
->GetAttributes().GetBulletNumber();
3838 if (!para
->GetAttributes().GetParagraphStyleName().IsEmpty() && !para
->GetAttributes().GetListStyleName().IsEmpty())
3840 int currentIndent
= para
->GetAttributes().GetLeftIndent();
3842 wxRichTextParagraphStyleDefinition
* paraDef
= styleSheet
->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
3843 wxRichTextListStyleDefinition
* listDef
= styleSheet
->FindListStyle(para
->GetAttributes().GetListStyleName());
3844 if (paraDef
&& !listDef
)
3846 para
->GetAttributes() = paraDef
->GetStyleMergedWithBase(styleSheet
);
3849 else if (listDef
&& !paraDef
)
3851 // Set overall style defined for the list style definition
3852 para
->GetAttributes() = listDef
->GetStyleMergedWithBase(styleSheet
);
3854 // Apply the style for this level
3855 wxRichTextApplyStyle(para
->GetAttributes(), * listDef
->GetLevelAttributes(listDef
->FindLevelForIndent(currentIndent
)));
3858 else if (listDef
&& paraDef
)
3860 // Combines overall list style, style for level, and paragraph style
3861 para
->GetAttributes() = listDef
->CombineWithParagraphStyle(currentIndent
, paraDef
->GetStyleMergedWithBase(styleSheet
));
3865 else if (para
->GetAttributes().GetParagraphStyleName().IsEmpty() && !para
->GetAttributes().GetListStyleName().IsEmpty())
3867 int currentIndent
= para
->GetAttributes().GetLeftIndent();
3869 wxRichTextListStyleDefinition
* listDef
= styleSheet
->FindListStyle(para
->GetAttributes().GetListStyleName());
3871 // Overall list definition style
3872 para
->GetAttributes() = listDef
->GetStyleMergedWithBase(styleSheet
);
3874 // Style for this level
3875 wxRichTextApplyStyle(para
->GetAttributes(), * listDef
->GetLevelAttributes(listDef
->FindLevelForIndent(currentIndent
)));
3879 else if (!para
->GetAttributes().GetParagraphStyleName().IsEmpty() && para
->GetAttributes().GetListStyleName().IsEmpty())
3881 wxRichTextParagraphStyleDefinition
* def
= styleSheet
->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
3884 para
->GetAttributes() = def
->GetStyleMergedWithBase(styleSheet
);
3890 para
->GetAttributes().SetOutlineLevel(outline
);
3892 para
->GetAttributes().SetBulletNumber(num
);
3895 node
= node
->GetNext();
3897 return foundCount
!= 0;
3901 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
3903 wxRichTextBuffer
* buffer
= GetBuffer();
3904 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
3906 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
3907 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
3908 bool specifyLevel
= ((flags
& wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL
) != 0);
3909 bool renumber
= ((flags
& wxRICHTEXT_SETSTYLE_RENUMBER
) != 0);
3911 // Current number, if numbering
3914 wxASSERT (!specifyLevel
|| (specifyLevel
&& (specifiedLevel
>= 0)));
3916 // If we are associated with a control, make undoable; otherwise, apply immediately
3919 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3921 wxRichTextAction
* action
= NULL
;
3923 if (haveControl
&& withUndo
)
3925 action
= new wxRichTextAction(NULL
, _("Change List Style"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
3926 action
->SetRange(range
);
3927 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3930 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3933 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3934 // wxASSERT (para != NULL);
3936 if (para
&& para
->GetChildCount() > 0)
3938 // Stop searching if we're beyond the range of interest
3939 if (para
->GetRange().GetStart() > range
.GetEnd())
3942 if (!para
->GetRange().IsOutside(range
))
3944 // We'll be using a copy of the paragraph to make style changes,
3945 // not updating the buffer directly.
3946 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3948 if (haveControl
&& withUndo
)
3950 newPara
= new wxRichTextParagraph(*para
);
3951 action
->GetNewParagraphs().AppendChild(newPara
);
3953 // Also store the old ones for Undo
3954 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3961 int thisIndent
= newPara
->GetAttributes().GetLeftIndent();
3962 int thisLevel
= specifyLevel
? specifiedLevel
: def
->FindLevelForIndent(thisIndent
);
3964 // How is numbering going to work?
3965 // If we are renumbering, or numbering for the first time, we need to keep
3966 // track of the number for each level. But we might be simply applying a different
3968 // In Word, applying a style to several paragraphs, even if at different levels,
3969 // reverts the level back to the same one. So we could do the same here.
3970 // Renumbering will need to be done when we promote/demote a paragraph.
3972 // Apply the overall list style, and item style for this level
3973 wxRichTextAttr
listStyle(def
->GetCombinedStyleForLevel(thisLevel
, styleSheet
));
3974 wxRichTextApplyStyle(newPara
->GetAttributes(), listStyle
);
3976 // Now we need to do numbering
3979 newPara
->GetAttributes().SetBulletNumber(n
);
3984 else if (!newPara
->GetAttributes().GetListStyleName().IsEmpty())
3986 // if def is NULL, remove list style, applying any associated paragraph style
3987 // to restore the attributes
3989 newPara
->GetAttributes().SetListStyleName(wxEmptyString
);
3990 newPara
->GetAttributes().SetLeftIndent(0, 0);
3991 newPara
->GetAttributes().SetBulletText(wxEmptyString
);
3993 // Eliminate the main list-related attributes
3994 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
);
3996 if (styleSheet
&& !newPara
->GetAttributes().GetParagraphStyleName().IsEmpty())
3998 wxRichTextParagraphStyleDefinition
* def
= styleSheet
->FindParagraphStyle(newPara
->GetAttributes().GetParagraphStyleName());
4001 newPara
->GetAttributes() = def
->GetStyleMergedWithBase(styleSheet
);
4008 node
= node
->GetNext();
4011 // Do action, or delay it until end of batch.
4012 if (haveControl
&& withUndo
)
4013 buffer
->SubmitAction(action
);
4018 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
)
4020 wxRichTextBuffer
* buffer
= GetBuffer();
4021 if (buffer
&& buffer
->GetStyleSheet())
4023 wxRichTextListStyleDefinition
* def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4025 return SetListStyle(range
, def
, flags
, startFrom
, specifiedLevel
);
4030 /// Clear list for given range
4031 bool wxRichTextParagraphLayoutBox::ClearListStyle(const wxRichTextRange
& range
, int flags
)
4033 return SetListStyle(range
, NULL
, flags
);
4036 /// Number/renumber any list elements in the given range
4037 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
4039 return DoNumberList(range
, range
, 0, def
, flags
, startFrom
, specifiedLevel
);
4042 /// Number/renumber any list elements in the given range. Also do promotion or demotion of items, if specified
4043 bool wxRichTextParagraphLayoutBox::DoNumberList(const wxRichTextRange
& range
, const wxRichTextRange
& promotionRange
, int promoteBy
,
4044 wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
4046 wxRichTextBuffer
* buffer
= GetBuffer();
4047 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
4049 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
4050 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
4052 bool specifyLevel
= ((flags
& wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL
) != 0);
4055 bool renumber
= ((flags
& wxRICHTEXT_SETSTYLE_RENUMBER
) != 0);
4057 // Max number of levels
4058 const int maxLevels
= 10;
4060 // The level we're looking at now
4061 int currentLevel
= -1;
4063 // The item number for each level
4064 int levels
[maxLevels
];
4067 // Reset all numbering
4068 for (i
= 0; i
< maxLevels
; i
++)
4070 if (startFrom
!= -1)
4071 levels
[i
] = startFrom
-1;
4072 else if (renumber
) // start again
4075 levels
[i
] = -1; // start from the number we found, if any
4079 wxASSERT(!specifyLevel
|| (specifyLevel
&& (specifiedLevel
>= 0)));
4082 // If we are associated with a control, make undoable; otherwise, apply immediately
4085 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
4087 wxRichTextAction
* action
= NULL
;
4089 if (haveControl
&& withUndo
)
4091 action
= new wxRichTextAction(NULL
, _("Renumber List"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
4092 action
->SetRange(range
);
4093 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
4096 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
4099 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
4100 // wxASSERT (para != NULL);
4102 if (para
&& para
->GetChildCount() > 0)
4104 // Stop searching if we're beyond the range of interest
4105 if (para
->GetRange().GetStart() > range
.GetEnd())
4108 if (!para
->GetRange().IsOutside(range
))
4110 // We'll be using a copy of the paragraph to make style changes,
4111 // not updating the buffer directly.
4112 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
4114 if (haveControl
&& withUndo
)
4116 newPara
= new wxRichTextParagraph(*para
);
4117 action
->GetNewParagraphs().AppendChild(newPara
);
4119 // Also store the old ones for Undo
4120 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
4125 wxRichTextListStyleDefinition
* defToUse
= def
;
4128 if (styleSheet
&& !newPara
->GetAttributes().GetListStyleName().IsEmpty())
4129 defToUse
= styleSheet
->FindListStyle(newPara
->GetAttributes().GetListStyleName());
4134 int thisIndent
= newPara
->GetAttributes().GetLeftIndent();
4135 int thisLevel
= defToUse
->FindLevelForIndent(thisIndent
);
4137 // If we've specified a level to apply to all, change the level.
4138 if (specifiedLevel
!= -1)
4139 thisLevel
= specifiedLevel
;
4141 // Do promotion if specified
4142 if ((promoteBy
!= 0) && !para
->GetRange().IsOutside(promotionRange
))
4144 thisLevel
= thisLevel
- promoteBy
;
4151 // Apply the overall list style, and item style for this level
4152 wxRichTextAttr
listStyle(defToUse
->GetCombinedStyleForLevel(thisLevel
, styleSheet
));
4153 wxRichTextApplyStyle(newPara
->GetAttributes(), listStyle
);
4155 // OK, we've (re)applied the style, now let's get the numbering right.
4157 if (currentLevel
== -1)
4158 currentLevel
= thisLevel
;
4160 // Same level as before, do nothing except increment level's number afterwards
4161 if (currentLevel
== thisLevel
)
4164 // A deeper level: start renumbering all levels after current level
4165 else if (thisLevel
> currentLevel
)
4167 for (i
= currentLevel
+1; i
<= thisLevel
; i
++)
4171 currentLevel
= thisLevel
;
4173 else if (thisLevel
< currentLevel
)
4175 currentLevel
= thisLevel
;
4178 // Use the current numbering if -1 and we have a bullet number already
4179 if (levels
[currentLevel
] == -1)
4181 if (newPara
->GetAttributes().HasBulletNumber())
4182 levels
[currentLevel
] = newPara
->GetAttributes().GetBulletNumber();
4184 levels
[currentLevel
] = 1;
4188 levels
[currentLevel
] ++;
4191 newPara
->GetAttributes().SetBulletNumber(levels
[currentLevel
]);
4193 // Create the bullet text if an outline list
4194 if (listStyle
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
)
4197 for (i
= 0; i
<= currentLevel
; i
++)
4199 if (!text
.IsEmpty())
4201 text
+= wxString::Format(wxT("%d"), levels
[i
]);
4203 newPara
->GetAttributes().SetBulletText(text
);
4209 node
= node
->GetNext();
4212 // Do action, or delay it until end of batch.
4213 if (haveControl
&& withUndo
)
4214 buffer
->SubmitAction(action
);
4219 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
)
4221 wxRichTextBuffer
* buffer
= GetBuffer();
4222 if (buffer
->GetStyleSheet())
4224 wxRichTextListStyleDefinition
* def
= NULL
;
4225 if (!defName
.IsEmpty())
4226 def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4227 return NumberList(range
, def
, flags
, startFrom
, specifiedLevel
);
4232 /// Promote the list items within the given range. promoteBy can be a positive or negative number, e.g. 1 or -1
4233 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy
, const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int specifiedLevel
)
4236 // One strategy is to first work out the range within which renumbering must occur. Then could pass these two ranges
4237 // to NumberList with a flag indicating promotion is required within one of the ranges.
4238 // Find first and last paragraphs in range. Then for first, calculate new indentation and look back until we find
4239 // a paragraph that either has no list style, or has one that is different or whose indentation is less.
4240 // We start renumbering from the para after that different para we found. We specify that the numbering of that
4241 // list position will start from 1.
4242 // Similarly, we look after the last para in the promote range for an indentation that is less (or no list style).
4243 // We can end the renumbering at this point.
4245 // For now, only renumber within the promotion range.
4247 return DoNumberList(range
, range
, promoteBy
, def
, flags
, 1, specifiedLevel
);
4250 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy
, const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int specifiedLevel
)
4252 wxRichTextBuffer
* buffer
= GetBuffer();
4253 if (buffer
->GetStyleSheet())
4255 wxRichTextListStyleDefinition
* def
= NULL
;
4256 if (!defName
.IsEmpty())
4257 def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4258 return PromoteList(promoteBy
, range
, def
, flags
, specifiedLevel
);
4263 /// Fills in the attributes for numbering a paragraph after previousParagraph. It also finds the
4264 /// position of the paragraph that it had to start looking from.
4265 bool wxRichTextParagraphLayoutBox::FindNextParagraphNumber(wxRichTextParagraph
* previousParagraph
, wxRichTextAttr
& attr
) const
4267 if (!previousParagraph
->GetAttributes().HasFlag(wxTEXT_ATTR_BULLET_STYLE
) || previousParagraph
->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
)
4270 wxRichTextBuffer
* buffer
= GetBuffer();
4271 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
4272 if (styleSheet
&& !previousParagraph
->GetAttributes().GetListStyleName().IsEmpty())
4274 wxRichTextListStyleDefinition
* def
= styleSheet
->FindListStyle(previousParagraph
->GetAttributes().GetListStyleName());
4277 // int thisIndent = previousParagraph->GetAttributes().GetLeftIndent();
4278 // int thisLevel = def->FindLevelForIndent(thisIndent);
4280 bool isOutline
= (previousParagraph
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
) != 0;
4282 attr
.SetFlags(previousParagraph
->GetAttributes().GetFlags() & (wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_BULLET_NUMBER
|wxTEXT_ATTR_BULLET_TEXT
|wxTEXT_ATTR_BULLET_NAME
));
4283 if (previousParagraph
->GetAttributes().HasBulletName())
4284 attr
.SetBulletName(previousParagraph
->GetAttributes().GetBulletName());
4285 attr
.SetBulletStyle(previousParagraph
->GetAttributes().GetBulletStyle());
4286 attr
.SetListStyleName(previousParagraph
->GetAttributes().GetListStyleName());
4288 int nextNumber
= previousParagraph
->GetAttributes().GetBulletNumber() + 1;
4289 attr
.SetBulletNumber(nextNumber
);
4293 wxString text
= previousParagraph
->GetAttributes().GetBulletText();
4294 if (!text
.IsEmpty())
4296 int pos
= text
.Find(wxT('.'), true);
4297 if (pos
!= wxNOT_FOUND
)
4299 text
= text
.Mid(0, text
.Length() - pos
- 1);
4302 text
= wxEmptyString
;
4303 if (!text
.IsEmpty())
4305 text
+= wxString::Format(wxT("%d"), nextNumber
);
4306 attr
.SetBulletText(text
);
4320 * wxRichTextParagraph
4321 * This object represents a single paragraph (or in a straight text editor, a line).
4324 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraph
, wxRichTextCompositeObject
)
4326 wxArrayInt
wxRichTextParagraph::sm_defaultTabs
;
4328 wxRichTextParagraph::wxRichTextParagraph(wxRichTextObject
* parent
, wxRichTextAttr
* style
):
4329 wxRichTextCompositeObject(parent
)
4332 SetAttributes(*style
);
4335 wxRichTextParagraph::wxRichTextParagraph(const wxString
& text
, wxRichTextObject
* parent
, wxRichTextAttr
* paraStyle
, wxRichTextAttr
* charStyle
):
4336 wxRichTextCompositeObject(parent
)
4339 SetAttributes(*paraStyle
);
4341 AppendChild(new wxRichTextPlainText(text
, this, charStyle
));
4344 wxRichTextParagraph::~wxRichTextParagraph()
4350 bool wxRichTextParagraph::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int WXUNUSED(descent
), int style
)
4355 // Currently we don't merge these attributes with the parent, but we
4356 // should consider whether we should (e.g. if we set a border colour
4357 // for all paragraphs). But generally box attributes are likely to be
4358 // different for different objects.
4359 wxRect paraRect
= GetRect();
4360 wxRichTextAttr attr
= GetCombinedAttributes();
4361 context
.ApplyVirtualAttributes(attr
, this);
4363 DrawBoxAttributes(dc
, GetBuffer(), attr
, paraRect
);
4365 // Draw the bullet, if any
4366 if (attr
.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE
)
4368 if (attr
.GetLeftSubIndent() != 0)
4370 int spaceBeforePara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingBefore());
4371 int leftIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftIndent());
4373 wxRichTextAttr
bulletAttr(attr
);
4375 // Combine with the font of the first piece of content, if one is specified
4376 if (GetChildren().GetCount() > 0)
4378 wxRichTextObject
* firstObj
= (wxRichTextObject
*) GetChildren().GetFirst()->GetData();
4379 if (!firstObj
->IsFloatable() && firstObj
->GetAttributes().HasFont())
4381 wxRichTextApplyStyle(bulletAttr
, firstObj
->GetAttributes());
4385 // Get line height from first line, if any
4386 wxRichTextLine
* line
= m_cachedLines
.GetFirst() ? (wxRichTextLine
* ) m_cachedLines
.GetFirst()->GetData() : NULL
;
4389 int lineHeight
wxDUMMY_INITIALIZE(0);
4392 lineHeight
= line
->GetSize().y
;
4393 linePos
= line
->GetPosition() + GetPosition();
4398 if (bulletAttr
.HasFont() && GetBuffer())
4399 font
= GetBuffer()->GetFontTable().FindFont(bulletAttr
);
4401 font
= (*wxNORMAL_FONT
);
4403 wxCheckSetFont(dc
, font
);
4405 lineHeight
= dc
.GetCharHeight();
4406 linePos
= GetPosition();
4407 linePos
.y
+= spaceBeforePara
;
4410 wxRect
bulletRect(GetPosition().x
+ leftIndent
, linePos
.y
, linePos
.x
- (GetPosition().x
+ leftIndent
), lineHeight
);
4412 if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
)
4414 if (wxRichTextBuffer::GetRenderer())
4415 wxRichTextBuffer::GetRenderer()->DrawBitmapBullet(this, dc
, bulletAttr
, bulletRect
);
4417 else if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_STANDARD
)
4419 if (wxRichTextBuffer::GetRenderer())
4420 wxRichTextBuffer::GetRenderer()->DrawStandardBullet(this, dc
, bulletAttr
, bulletRect
);
4424 wxString bulletText
= GetBulletText();
4426 if (!bulletText
.empty() && wxRichTextBuffer::GetRenderer())
4427 wxRichTextBuffer::GetRenderer()->DrawTextBullet(this, dc
, bulletAttr
, bulletRect
, bulletText
);
4432 // Draw the range for each line, one object at a time.
4434 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
4437 wxRichTextLine
* line
= node
->GetData();
4438 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
4440 // Lines are specified relative to the paragraph
4442 wxPoint linePosition
= line
->GetPosition() + GetPosition();
4444 // Don't draw if off the screen
4445 if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) != 0) || ((linePosition
.y
+ line
->GetSize().y
) >= rect
.y
&& linePosition
.y
<= rect
.y
+ rect
.height
))
4447 wxPoint objectPosition
= linePosition
;
4448 int maxDescent
= line
->GetDescent();
4450 // Loop through objects until we get to the one within range
4451 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
4456 wxRichTextObject
* child
= node2
->GetData();
4458 if (!child
->IsFloating() && child
->GetRange().GetLength() > 0 && !child
->GetRange().IsOutside(lineRange
) && !lineRange
.IsOutside(range
))
4460 // Draw this part of the line at the correct position
4461 wxRichTextRange
objectRange(child
->GetRange());
4462 objectRange
.LimitTo(lineRange
);
4465 if (child
->IsTopLevel())
4467 objectSize
= child
->GetCachedSize();
4468 objectRange
= child
->GetOwnRange();
4472 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING && wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4473 if (i
< (int) line
->GetObjectSizes().GetCount())
4475 objectSize
.x
= line
->GetObjectSizes()[(size_t) i
];
4481 child
->GetRangeSize(objectRange
, objectSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, objectPosition
);
4485 // Use the child object's width, but the whole line's height
4486 wxRect
childRect(objectPosition
, wxSize(objectSize
.x
, line
->GetSize().y
));
4487 child
->Draw(dc
, context
, objectRange
, selection
, childRect
, maxDescent
, style
);
4489 objectPosition
.x
+= objectSize
.x
;
4492 else if (child
->GetRange().GetStart() > lineRange
.GetEnd())
4493 // Can break out of inner loop now since we've passed this line's range
4496 node2
= node2
->GetNext();
4500 node
= node
->GetNext();
4506 // Get the range width using partial extents calculated for the whole paragraph.
4507 static int wxRichTextGetRangeWidth(const wxRichTextParagraph
& para
, const wxRichTextRange
& range
, const wxArrayInt
& partialExtents
)
4509 wxASSERT(partialExtents
.GetCount() >= (size_t) range
.GetLength());
4511 if (partialExtents
.GetCount() < (size_t) range
.GetLength())
4514 int leftMostPos
= 0;
4515 if (range
.GetStart() - para
.GetRange().GetStart() > 0)
4516 leftMostPos
= partialExtents
[range
.GetStart() - para
.GetRange().GetStart() - 1];
4518 int rightMostPos
= partialExtents
[range
.GetEnd() - para
.GetRange().GetStart()];
4520 int w
= rightMostPos
- leftMostPos
;
4525 /// Lay the item out
4526 bool wxRichTextParagraph::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
)
4528 // Deal with floating objects firstly before the normal layout
4529 wxRichTextBuffer
* buffer
= GetBuffer();
4531 wxRichTextFloatCollector
* collector
= GetContainer()->GetFloatCollector();
4532 wxASSERT(collector
);
4533 LayoutFloat(dc
, context
, rect
, style
, collector
);
4535 wxRichTextAttr attr
= GetCombinedAttributes();
4536 context
.ApplyVirtualAttributes(attr
, this);
4540 // Increase the size of the paragraph due to spacing
4541 int spaceBeforePara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingBefore());
4542 int spaceAfterPara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingAfter());
4543 int leftIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftIndent());
4544 int leftSubIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftSubIndent());
4545 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
4547 int lineSpacing
= 0;
4549 // Let's assume line spacing of 10 is normal, 15 is 1.5, 20 is 2, etc.
4550 if (attr
.HasLineSpacing() && attr
.GetLineSpacing() > 0 && attr
.GetFont().IsOk())
4552 wxCheckSetFont(dc
, attr
.GetFont());
4553 lineSpacing
= (int) (double(dc
.GetCharHeight()) * (double(attr
.GetLineSpacing())/10.0 - 1.0));
4556 // Start position for each line relative to the paragraph
4557 int startPositionFirstLine
= leftIndent
;
4558 int startPositionSubsequentLines
= leftIndent
+ leftSubIndent
;
4560 // If we have a bullet in this paragraph, the start position for the first line's text
4561 // is actually leftIndent + leftSubIndent.
4562 if (attr
.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE
)
4563 startPositionFirstLine
= startPositionSubsequentLines
;
4565 long lastEndPos
= GetRange().GetStart()-1;
4566 long lastCompletedEndPos
= lastEndPos
;
4568 int currentWidth
= 0;
4569 SetPosition(rect
.GetPosition());
4571 wxPoint
currentPosition(0, spaceBeforePara
); // We will calculate lines relative to paragraph
4574 int maxHeight
= currentPosition
.y
;
4579 int lineDescent
= 0;
4581 wxRichTextObjectList::compatibility_iterator node
;
4583 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4585 wxArrayInt partialExtents
;
4588 int paraDescent
= 0;
4590 // This calculates the partial text extents
4591 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, rect
.GetPosition(), & partialExtents
);
4593 node
= m_children
.GetFirst();
4596 wxRichTextObject
* child
= node
->GetData();
4598 //child->SetCachedSize(wxDefaultSize);
4599 child
->Layout(dc
, context
, rect
, style
);
4601 node
= node
->GetNext();
4608 // We may need to go back to a previous child, in which case create the new line,
4609 // find the child corresponding to the start position of the string, and
4612 wxRect availableRect
;
4614 node
= m_children
.GetFirst();
4617 wxRichTextObject
* child
= node
->GetData();
4619 // If floating, ignore. We already laid out floats.
4620 // Also ignore if empty object, except if we haven't got any
4622 if (child
->IsFloating() || !child
->IsShown() ||
4623 (child
->GetRange().GetLength() == 0 && maxHeight
> spaceBeforePara
)
4626 node
= node
->GetNext();
4630 // If this is e.g. a composite text box, it will need to be laid out itself.
4631 // But if just a text fragment or image, for example, this will
4632 // do nothing. NB: won't we need to set the position after layout?
4633 // since for example if position is dependent on vertical line size, we
4634 // can't tell the position until the size is determined. So possibly introduce
4635 // another layout phase.
4637 // We may only be looking at part of a child, if we searched back for wrapping
4638 // and found a suitable point some way into the child. So get the size for the fragment
4641 long nextBreakPos
= GetFirstLineBreakPosition(lastEndPos
+1);
4642 long lastPosToUse
= child
->GetRange().GetEnd();
4643 bool lineBreakInThisObject
= (nextBreakPos
> -1 && nextBreakPos
<= child
->GetRange().GetEnd());
4645 if (lineBreakInThisObject
)
4646 lastPosToUse
= nextBreakPos
;
4649 int childDescent
= 0;
4651 int startOffset
= (lineCount
== 0 ? startPositionFirstLine
: startPositionSubsequentLines
);
4652 availableRect
= wxRect(rect
.x
+ startOffset
, rect
.y
+ currentPosition
.y
,
4653 rect
.width
- startOffset
- rightIndent
, rect
.height
);
4655 if (child
->IsTopLevel())
4657 wxSize oldSize
= child
->GetCachedSize();
4659 child
->Invalidate(wxRICHTEXT_ALL
);
4660 child
->SetPosition(wxPoint(0, 0));
4662 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4663 // lays out the object again using the minimum size
4664 // The position will be determined by its location in its line,
4665 // and not by the child's actual position.
4666 child
->LayoutToBestSize(dc
, context
, buffer
,
4667 attr
, child
->GetAttributes(), availableRect
, parentRect
, style
);
4669 if (oldSize
!= child
->GetCachedSize())
4671 partialExtents
.Clear();
4673 // Recalculate the partial text extents since the child object changed size
4674 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), & partialExtents
);
4678 // Problem: we need to layout composites here for which we need the available width,
4679 // but we can't get the available width without using the float collector which
4680 // needs to know the object height.
4682 if ((nextBreakPos
== -1) && (lastEndPos
== child
->GetRange().GetStart() - 1)) // i.e. we want to get the whole thing
4684 childSize
= child
->GetCachedSize();
4685 childDescent
= child
->GetDescent();
4689 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4690 // Get height only, then the width using the partial extents
4691 GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
);
4692 childSize
.x
= wxRichTextGetRangeWidth(*this, wxRichTextRange(lastEndPos
+1, lastPosToUse
), partialExtents
);
4694 GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, rect
.GetPosition());
4699 int loopIterations
= 0;
4701 // If there are nested objects that need to lay themselves out, we have to do this in a
4702 // loop because the height of the object may well depend on the available width.
4703 // And because of floating object positioning, the available width depends on the
4704 // height of the object and whether it will clash with the floating objects.
4705 // So, we see whether the available width changes due to the presence of floating images.
4706 // If it does, then we'll use the new restricted width to find the object height again.
4707 // If this causes another restriction in the available width, we'll try again, until
4708 // either we lose patience or the available width settles down.
4713 wxRect oldAvailableRect
= availableRect
;
4715 // Available width depends on the floating objects and the line height.
4716 // Note: the floating objects may be placed vertically along the two side of
4717 // buffer, so we may have different available line widths with different
4718 // [startY, endY]. So, we can't determine how wide the available
4719 // space is until we know the exact line height.
4720 lineDescent
= wxMax(childDescent
, maxDescent
);
4721 lineAscent
= wxMax(childSize
.y
-childDescent
, maxAscent
);
4722 lineHeight
= lineDescent
+ lineAscent
;
4723 wxRect floatAvailableRect
= collector
->GetAvailableRect(rect
.y
+ currentPosition
.y
, rect
.y
+ currentPosition
.y
+ lineHeight
);
4725 // Adjust availableRect to the space that is available when taking floating objects into account.
4727 if (floatAvailableRect
.x
+ startOffset
> availableRect
.x
)
4729 int newX
= floatAvailableRect
.x
+ startOffset
;
4730 int newW
= availableRect
.width
- (newX
- availableRect
.x
);
4731 availableRect
.x
= newX
;
4732 availableRect
.width
= newW
;
4735 if (floatAvailableRect
.width
< availableRect
.width
)
4736 availableRect
.width
= floatAvailableRect
.width
;
4738 currentPosition
.x
= availableRect
.x
- rect
.x
;
4740 if (child
->IsTopLevel() && loopIterations
<= 20)
4742 if (availableRect
!= oldAvailableRect
)
4744 wxSize oldSize
= child
->GetCachedSize();
4746 //child->SetCachedSize(wxDefaultSize);
4747 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4748 // lays out the object again using the minimum size
4749 child
->Invalidate(wxRICHTEXT_ALL
);
4750 child
->LayoutToBestSize(dc
, context
, buffer
,
4751 attr
, child
->GetAttributes(), availableRect
, parentRect
, style
);
4752 childSize
= child
->GetCachedSize();
4753 childDescent
= child
->GetDescent();
4754 //child->SetPosition(availableRect.GetPosition());
4756 if (oldSize
!= child
->GetCachedSize())
4758 partialExtents
.Clear();
4760 // Recalculate the partial text extents since the child object changed size
4761 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), & partialExtents
);
4764 // Go around the loop finding the available rect for the given floating objects
4775 // 1) There was a line break BEFORE the natural break
4776 // 2) There was a line break AFTER the natural break
4777 // 3) It's the last line
4778 // 4) The child still fits (carry on) - 'else' clause
4780 if ((lineBreakInThisObject
&& (childSize
.x
+ currentWidth
<= availableRect
.width
))
4782 (childSize
.x
+ currentWidth
> availableRect
.width
)
4784 ((childSize
.x
+ currentWidth
<= availableRect
.width
) && !node
->GetNext())
4788 if (child
->IsTopLevel())
4790 // We can move it to the correct position at this point
4791 child
->Move(GetPosition() + wxPoint(currentWidth
, currentPosition
.y
));
4794 long wrapPosition
= 0;
4795 if ((childSize
.x
+ currentWidth
<= availableRect
.width
) && !node
->GetNext() && !lineBreakInThisObject
)
4796 wrapPosition
= child
->GetRange().GetEnd();
4799 // Find a place to wrap. This may walk back to previous children,
4800 // for example if a word spans several objects.
4801 // Note: one object must contains only one wxTextAtrr, so the line height will not
4802 // change inside one object. Thus, we can pass the remain line width to the
4803 // FindWrapPosition function.
4804 if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos
+1, child
->GetRange().GetEnd()), dc
, context
, availableRect
.width
, wrapPosition
, & partialExtents
))
4806 // If the function failed, just cut it off at the end of this child.
4807 wrapPosition
= child
->GetRange().GetEnd();
4810 // FindWrapPosition can still return a value that will put us in an endless wrapping loop
4811 if (wrapPosition
<= lastCompletedEndPos
)
4812 wrapPosition
= wxMax(lastCompletedEndPos
+1,child
->GetRange().GetEnd());
4814 // Line end position shouldn't be the same as the end, or greater.
4815 if (wrapPosition
>= GetRange().GetEnd())
4816 wrapPosition
= GetRange().GetEnd()-1;
4818 // wxLogDebug(wxT("Split at %ld"), wrapPosition);
4820 // Let's find the actual size of the current line now
4822 wxRichTextRange
actualRange(lastCompletedEndPos
+1, wrapPosition
);
4824 /// Use previous descent, not the wrapping descent we just found, since this may be too big
4825 /// for the fragment we're about to add.
4826 childDescent
= maxDescent
;
4828 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4829 if (!child
->IsEmpty())
4831 // Get height only, then the width using the partial extents
4832 GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
);
4833 actualSize
.x
= wxRichTextGetRangeWidth(*this, actualRange
, partialExtents
);
4837 GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
4839 currentWidth
= actualSize
.x
;
4840 maxDescent
= wxMax(childDescent
, maxDescent
);
4841 maxAscent
= wxMax(actualSize
.y
-childDescent
, maxAscent
);
4842 lineHeight
= maxDescent
+ maxAscent
;
4844 if (lineHeight
== 0 && buffer
)
4846 wxFont
font(buffer
->GetFontTable().FindFont(attr
));
4847 wxCheckSetFont(dc
, font
);
4848 lineHeight
= dc
.GetCharHeight();
4851 if (maxDescent
== 0)
4854 dc
.GetTextExtent(wxT("X"), & w
, &h
, & maxDescent
);
4858 wxRichTextLine
* line
= AllocateLine(lineCount
);
4860 // Set relative range so we won't have to change line ranges when paragraphs are moved
4861 line
->SetRange(wxRichTextRange(actualRange
.GetStart() - GetRange().GetStart(), actualRange
.GetEnd() - GetRange().GetStart()));
4862 line
->SetPosition(currentPosition
);
4863 line
->SetSize(wxSize(currentWidth
, lineHeight
));
4864 line
->SetDescent(maxDescent
);
4866 maxHeight
= currentPosition
.y
+ lineHeight
;
4868 // Now move down a line. TODO: add margins, spacing
4869 currentPosition
.y
+= lineHeight
;
4870 currentPosition
.y
+= lineSpacing
;
4873 maxWidth
= wxMax(maxWidth
, currentWidth
+startOffset
);
4878 // TODO: account for zero-length objects, such as fields
4879 // wxASSERT(wrapPosition > lastCompletedEndPos);
4881 lastEndPos
= wrapPosition
;
4882 lastCompletedEndPos
= lastEndPos
;
4886 if (wrapPosition
< GetRange().GetEnd()-1)
4888 // May need to set the node back to a previous one, due to searching back in wrapping
4889 wxRichTextObject
* childAfterWrapPosition
= FindObjectAtPosition(wrapPosition
+1);
4890 if (childAfterWrapPosition
)
4891 node
= m_children
.Find(childAfterWrapPosition
);
4893 node
= node
->GetNext();
4896 node
= node
->GetNext();
4898 // Apply paragraph styles such as alignment to the wrapped line
4899 ApplyParagraphStyle(line
, attr
, availableRect
, dc
);
4903 // We still fit, so don't add a line, and keep going
4904 currentWidth
+= childSize
.x
;
4905 maxDescent
= wxMax(childDescent
, maxDescent
);
4906 maxAscent
= wxMax(childSize
.y
-childDescent
, maxAscent
);
4907 lineHeight
= maxDescent
+ maxAscent
;
4909 maxWidth
= wxMax(maxWidth
, currentWidth
+startOffset
);
4910 lastEndPos
= child
->GetRange().GetEnd();
4912 node
= node
->GetNext();
4916 //wxASSERT(!(lastCompletedEndPos != -1 && lastCompletedEndPos < GetRange().GetEnd()-1));
4918 // Remove remaining unused line objects, if any
4919 ClearUnusedLines(lineCount
);
4921 // We need to add back the margins etc.
4923 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
4924 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxWidth
, currentPosition
.y
+ spaceAfterPara
));
4925 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
4926 SetCachedSize(marginRect
.GetSize());
4929 // The maximum size is the length of the paragraph stretched out into a line.
4930 // So if there were a single word, or an image, or a fixed-size text box, the object could be shrunk around
4931 // this size. TODO: take into account line breaks.
4933 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
4934 contentRect
= wxRect(wxPoint(0, 0), wxSize(paraSize
.x
+ wxMax(leftIndent
, leftIndent
+ leftSubIndent
) + rightIndent
, currentPosition
.y
+ spaceAfterPara
));
4935 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
4936 SetMaxSize(marginRect
.GetSize());
4939 // Find the greatest minimum size. Currently we only look at non-text objects,
4940 // which isn't ideal but it would be slow to find the maximum word width to
4941 // use as the minimum.
4944 node
= m_children
.GetFirst();
4947 wxRichTextObject
* child
= node
->GetData();
4949 // If floating, ignore. We already laid out floats.
4950 // Also ignore if empty object, except if we haven't got any
4952 if (!child
->IsFloating() && child
->GetRange().GetLength() != 0 && !child
->IsKindOf(CLASSINFO(wxRichTextPlainText
)))
4954 if (child
->GetCachedSize().x
> minWidth
)
4955 minWidth
= child
->GetMinSize().x
;
4957 node
= node
->GetNext();
4960 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
4961 contentRect
= wxRect(wxPoint(0, 0), wxSize(minWidth
, currentPosition
.y
+ spaceAfterPara
));
4962 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
4963 SetMinSize(marginRect
.GetSize());
4967 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4968 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
4969 // Use the text extents to calculate the size of each fragment in each line
4970 wxRichTextLineList::compatibility_iterator lineNode
= m_cachedLines
.GetFirst();
4973 wxRichTextLine
* line
= lineNode
->GetData();
4974 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
4976 // Loop through objects until we get to the one within range
4977 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
4981 wxRichTextObject
* child
= node2
->GetData();
4983 if (child
->GetRange().GetLength() > 0 && !child
->GetRange().IsOutside(lineRange
))
4985 wxRichTextRange rangeToUse
= lineRange
;
4986 rangeToUse
.LimitTo(child
->GetRange());
4988 // Find the size of the child from the text extents, and store in an array
4989 // for drawing later
4991 if (rangeToUse
.GetStart() > GetRange().GetStart())
4992 left
= partialExtents
[(rangeToUse
.GetStart()-1) - GetRange().GetStart()];
4993 int right
= partialExtents
[rangeToUse
.GetEnd() - GetRange().GetStart()];
4994 int sz
= right
- left
;
4995 line
->GetObjectSizes().Add(sz
);
4997 else if (child
->GetRange().GetStart() > lineRange
.GetEnd())
4998 // Can break out of inner loop now since we've passed this line's range
5001 node2
= node2
->GetNext();
5004 lineNode
= lineNode
->GetNext();
5012 /// Apply paragraph styles, such as centering, to wrapped lines
5013 /// TODO: take into account box attributes, possibly
5014 void wxRichTextParagraph::ApplyParagraphStyle(wxRichTextLine
* line
, const wxRichTextAttr
& attr
, const wxRect
& rect
, wxDC
& dc
)
5016 if (!attr
.HasAlignment())
5019 wxPoint pos
= line
->GetPosition();
5020 wxSize size
= line
->GetSize();
5022 // centering, right-justification
5023 if (attr
.HasAlignment() && attr
.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE
)
5025 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
5026 pos
.x
= (rect
.GetWidth() - rightIndent
- size
.x
)/2 + pos
.x
;
5027 line
->SetPosition(pos
);
5029 else if (attr
.HasAlignment() && attr
.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT
)
5031 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
5032 pos
.x
= pos
.x
+ rect
.GetWidth() - size
.x
- rightIndent
;
5033 line
->SetPosition(pos
);
5037 /// Insert text at the given position
5038 bool wxRichTextParagraph::InsertText(long pos
, const wxString
& text
)
5040 wxRichTextObject
* childToUse
= NULL
;
5041 wxRichTextObjectList::compatibility_iterator nodeToUse
= wxRichTextObjectList::compatibility_iterator();
5043 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5046 wxRichTextObject
* child
= node
->GetData();
5047 if (child
->GetRange().Contains(pos
) && child
->GetRange().GetLength() > 0)
5054 node
= node
->GetNext();
5059 wxRichTextPlainText
* textObject
= wxDynamicCast(childToUse
, wxRichTextPlainText
);
5062 int posInString
= pos
- textObject
->GetRange().GetStart();
5064 wxString newText
= textObject
->GetText().Mid(0, posInString
) +
5065 text
+ textObject
->GetText().Mid(posInString
);
5066 textObject
->SetText(newText
);
5068 int textLength
= text
.length();
5070 textObject
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart(),
5071 textObject
->GetRange().GetEnd() + textLength
));
5073 // Increment the end range of subsequent fragments in this paragraph.
5074 // We'll set the paragraph range itself at a higher level.
5076 wxRichTextObjectList::compatibility_iterator node
= nodeToUse
->GetNext();
5079 wxRichTextObject
* child
= node
->GetData();
5080 child
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart() + textLength
,
5081 textObject
->GetRange().GetEnd() + textLength
));
5083 node
= node
->GetNext();
5090 // TODO: if not a text object, insert at closest position, e.g. in front of it
5096 // Don't pass parent initially to suppress auto-setting of parent range.
5097 // We'll do that at a higher level.
5098 wxRichTextPlainText
* textObject
= new wxRichTextPlainText(text
, this);
5100 AppendChild(textObject
);
5107 void wxRichTextParagraph::Copy(const wxRichTextParagraph
& obj
)
5109 wxRichTextCompositeObject::Copy(obj
);
5112 /// Clear the cached lines
5113 void wxRichTextParagraph::ClearLines()
5115 WX_CLEAR_LIST(wxRichTextLineList
, m_cachedLines
);
5118 /// Get/set the object size for the given range. Returns false if the range
5119 /// is invalid for this object.
5120 bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
5122 if (!range
.IsWithin(GetRange()))
5125 if (flags
& wxRICHTEXT_UNFORMATTED
)
5127 // Just use unformatted data, assume no line breaks
5128 // TODO: take into account line breaks
5132 wxArrayInt childExtents
;
5139 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5143 wxRichTextObject
* child
= node
->GetData();
5144 if (!child
->GetRange().IsOutside(range
))
5146 // Floating objects have a zero size within the paragraph.
5147 if (child
->IsFloating())
5152 if (partialExtents
->GetCount() > 0)
5153 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5157 partialExtents
->Add(0 /* zero size */ + lastSize
);
5164 wxRichTextRange rangeToUse
= range
;
5165 rangeToUse
.LimitTo(child
->GetRange());
5167 if (child
->IsTopLevel())
5168 rangeToUse
= child
->GetOwnRange();
5170 int childDescent
= 0;
5172 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we're already cached the size,
5173 // but it's only going to be used after caching has taken place.
5174 if ((flags
& wxRICHTEXT_HEIGHT_ONLY
) && child
->GetCachedSize().y
!= 0)
5176 childDescent
= child
->GetDescent();
5177 childSize
= child
->GetCachedSize();
5179 sz
.y
= wxMax(sz
.y
, childSize
.y
);
5180 sz
.x
+= childSize
.x
;
5181 descent
= wxMax(descent
, childDescent
);
5183 else if (child
->IsTopLevel())
5185 childDescent
= child
->GetDescent();
5186 childSize
= child
->GetCachedSize();
5188 sz
.y
= wxMax(sz
.y
, childSize
.y
);
5189 sz
.x
+= childSize
.x
;
5190 descent
= wxMax(descent
, childDescent
);
5191 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange()))
5193 child
->SetCachedSize(childSize
);
5194 child
->SetDescent(childDescent
);
5200 if (partialExtents
->GetCount() > 0)
5201 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5205 partialExtents
->Add(childSize
.x
+ lastSize
);
5208 else if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, context
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
), p
))
5210 sz
.y
= wxMax(sz
.y
, childSize
.y
);
5211 sz
.x
+= childSize
.x
;
5212 descent
= wxMax(descent
, childDescent
);
5214 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange()))
5216 child
->SetCachedSize(childSize
);
5217 child
->SetDescent(childDescent
);
5223 if (partialExtents
->GetCount() > 0)
5224 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5229 for (i
= 0; i
< childExtents
.GetCount(); i
++)
5231 partialExtents
->Add(childExtents
[i
] + lastSize
);
5241 node
= node
->GetNext();
5247 // Use formatted data, with line breaks
5250 // We're going to loop through each line, and then for each line,
5251 // call GetRangeSize for the fragment that comprises that line.
5252 // Only we have to do that multiple times within the line, because
5253 // the line may be broken into pieces. For now ignore line break commands
5254 // (so we can assume that getting the unformatted size for a fragment
5255 // within a line is the actual size)
5257 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5260 wxRichTextLine
* line
= node
->GetData();
5261 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5262 if (!lineRange
.IsOutside(range
))
5266 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
5269 wxRichTextObject
* child
= node2
->GetData();
5271 if (!child
->IsFloating() && !child
->GetRange().IsOutside(lineRange
))
5273 wxRichTextRange rangeToUse
= lineRange
;
5274 rangeToUse
.LimitTo(child
->GetRange());
5275 if (child
->IsTopLevel())
5276 rangeToUse
= child
->GetOwnRange();
5279 int childDescent
= 0;
5280 if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, context
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
)))
5282 lineSize
.y
= wxMax(lineSize
.y
, childSize
.y
);
5283 lineSize
.x
+= childSize
.x
;
5285 descent
= wxMax(descent
, childDescent
);
5288 node2
= node2
->GetNext();
5291 // Increase size by a line (TODO: paragraph spacing)
5293 sz
.x
= wxMax(sz
.x
, lineSize
.x
);
5295 node
= node
->GetNext();
5302 /// Finds the absolute position and row height for the given character position
5303 bool wxRichTextParagraph::FindPosition(wxDC
& dc
, wxRichTextDrawingContext
& context
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
5307 wxRichTextLine
* line
= ((wxRichTextParagraphLayoutBox
*)GetParent())->GetLineAtPosition(0);
5309 *height
= line
->GetSize().y
;
5311 *height
= dc
.GetCharHeight();
5313 // -1 means 'the start of the buffer'.
5316 pt
= pt
+ line
->GetPosition();
5321 // The final position in a paragraph is taken to mean the position
5322 // at the start of the next paragraph.
5323 if (index
== GetRange().GetEnd())
5325 wxRichTextParagraphLayoutBox
* parent
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
5326 wxASSERT( parent
!= NULL
);
5328 // Find the height at the next paragraph, if any
5329 wxRichTextLine
* line
= parent
->GetLineAtPosition(index
+ 1);
5332 *height
= line
->GetSize().y
;
5333 pt
= line
->GetAbsolutePosition();
5337 *height
= dc
.GetCharHeight();
5338 int indent
= ConvertTenthsMMToPixels(dc
, m_attributes
.GetLeftIndent());
5339 pt
= wxPoint(indent
, GetCachedSize().y
);
5345 if (index
< GetRange().GetStart() || index
> GetRange().GetEnd())
5348 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5351 wxRichTextLine
* line
= node
->GetData();
5352 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5353 if (index
>= lineRange
.GetStart() && index
<= lineRange
.GetEnd())
5355 // If this is the last point in the line, and we're forcing the
5356 // returned value to be the start of the next line, do the required
5358 if (index
== lineRange
.GetEnd() && forceLineStart
)
5360 if (node
->GetNext())
5362 wxRichTextLine
* nextLine
= node
->GetNext()->GetData();
5363 *height
= nextLine
->GetSize().y
;
5364 pt
= nextLine
->GetAbsolutePosition();
5369 pt
.y
= line
->GetPosition().y
+ GetPosition().y
;
5371 wxRichTextRange
r(lineRange
.GetStart(), index
);
5375 // We find the size of the line up to this point,
5376 // then we can add this size to the line start position and
5377 // paragraph start position to find the actual position.
5379 if (GetRangeSize(r
, rangeSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, line
->GetPosition()+ GetPosition()))
5381 pt
.x
= line
->GetPosition().x
+ GetPosition().x
+ rangeSize
.x
;
5382 *height
= line
->GetSize().y
;
5389 node
= node
->GetNext();
5395 /// Hit-testing: returns a flag indicating hit test details, plus
5396 /// information about position
5397 int wxRichTextParagraph::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
5400 return wxRICHTEXT_HITTEST_NONE
;
5402 // If we're in the top-level container, then we can return
5403 // a suitable hit test code even if the point is outside the container area,
5404 // so that we can position the caret sensibly even if we don't
5405 // click on valid content. If we're not at the top-level, and the point
5406 // is not within this paragraph object, then we don't want to stop more
5407 // precise hit-testing from working prematurely, so return immediately.
5408 // NEW STRATEGY: use the parent boundary to test whether we're in the
5409 // right region, not the paragraph, since the paragraph may be positioned
5410 // some way in from where the user clicks.
5413 wxRichTextObject
* tempObj
, *tempContextObj
;
5414 if (GetParent() && GetParent()->wxRichTextObject::HitTest(dc
, context
, pt
, tmpPos
, & tempObj
, & tempContextObj
, flags
) == wxRICHTEXT_HITTEST_NONE
)
5415 return wxRICHTEXT_HITTEST_NONE
;
5418 wxRichTextObjectList::compatibility_iterator objNode
= m_children
.GetFirst();
5421 wxRichTextObject
* child
= objNode
->GetData();
5422 if (child
->IsTopLevel() && ((flags
& wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS
) == 0))
5425 int hitTest
= child
->HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
);
5426 if (hitTest
!= wxRICHTEXT_HITTEST_NONE
)
5431 objNode
= objNode
->GetNext();
5434 wxPoint paraPos
= GetPosition();
5436 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5439 wxRichTextLine
* line
= node
->GetData();
5440 wxPoint linePos
= paraPos
+ line
->GetPosition();
5441 wxSize lineSize
= line
->GetSize();
5442 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5444 if (pt
.y
<= linePos
.y
+ lineSize
.y
)
5446 if (pt
.x
< linePos
.x
)
5448 textPosition
= lineRange
.GetStart();
5449 *obj
= FindObjectAtPosition(textPosition
);
5450 *contextObj
= GetContainer();
5451 return wxRICHTEXT_HITTEST_BEFORE
|wxRICHTEXT_HITTEST_OUTSIDE
;
5453 else if (pt
.x
>= (linePos
.x
+ lineSize
.x
))
5455 textPosition
= lineRange
.GetEnd();
5456 *obj
= FindObjectAtPosition(textPosition
);
5457 *contextObj
= GetContainer();
5458 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
;
5462 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5463 wxArrayInt partialExtents
;
5468 // This calculates the partial text extents
5469 GetRangeSize(lineRange
, paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, linePos
, & partialExtents
);
5471 int lastX
= linePos
.x
;
5473 for (i
= 0; i
< partialExtents
.GetCount(); i
++)
5475 int nextX
= partialExtents
[i
] + linePos
.x
;
5477 if (pt
.x
>= lastX
&& pt
.x
<= nextX
)
5479 textPosition
= i
+ lineRange
.GetStart(); // minus 1?
5481 *obj
= FindObjectAtPosition(textPosition
);
5482 *contextObj
= GetContainer();
5484 // So now we know it's between i-1 and i.
5485 // Let's see if we can be more precise about
5486 // which side of the position it's on.
5488 int midPoint
= (nextX
+ lastX
)/2;
5489 if (pt
.x
>= midPoint
)
5490 return wxRICHTEXT_HITTEST_AFTER
;
5492 return wxRICHTEXT_HITTEST_BEFORE
;
5499 int lastX
= linePos
.x
;
5500 for (i
= lineRange
.GetStart(); i
<= lineRange
.GetEnd(); i
++)
5505 wxRichTextRange
rangeToUse(lineRange
.GetStart(), i
);
5507 GetRangeSize(rangeToUse
, childSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, linePos
);
5509 int nextX
= childSize
.x
+ linePos
.x
;
5511 if (pt
.x
>= lastX
&& pt
.x
<= nextX
)
5515 *obj
= FindObjectAtPosition(textPosition
);
5516 *contextObj
= GetContainer();
5518 // So now we know it's between i-1 and i.
5519 // Let's see if we can be more precise about
5520 // which side of the position it's on.
5522 int midPoint
= (nextX
+ lastX
)/2;
5523 if (pt
.x
>= midPoint
)
5524 return wxRICHTEXT_HITTEST_AFTER
;
5526 return wxRICHTEXT_HITTEST_BEFORE
;
5537 node
= node
->GetNext();
5540 return wxRICHTEXT_HITTEST_NONE
;
5543 /// Split an object at this position if necessary, and return
5544 /// the previous object, or NULL if inserting at beginning.
5545 wxRichTextObject
* wxRichTextParagraph::SplitAt(long pos
, wxRichTextObject
** previousObject
)
5547 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5550 wxRichTextObject
* child
= node
->GetData();
5552 if (pos
== child
->GetRange().GetStart())
5556 if (node
->GetPrevious())
5557 *previousObject
= node
->GetPrevious()->GetData();
5559 *previousObject
= NULL
;
5565 if (child
->GetRange().Contains(pos
))
5567 // This should create a new object, transferring part of
5568 // the content to the old object and the rest to the new object.
5569 wxRichTextObject
* newObject
= child
->DoSplit(pos
);
5571 // If we couldn't split this object, just insert in front of it.
5574 // Maybe this is an empty string, try the next one
5579 // Insert the new object after 'child'
5580 if (node
->GetNext())
5581 m_children
.Insert(node
->GetNext(), newObject
);
5583 m_children
.Append(newObject
);
5584 newObject
->SetParent(this);
5587 *previousObject
= child
;
5593 node
= node
->GetNext();
5596 *previousObject
= NULL
;
5600 /// Move content to a list from obj on
5601 void wxRichTextParagraph::MoveToList(wxRichTextObject
* obj
, wxList
& list
)
5603 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(obj
);
5606 wxRichTextObject
* child
= node
->GetData();
5609 wxRichTextObjectList::compatibility_iterator oldNode
= node
;
5611 node
= node
->GetNext();
5613 m_children
.DeleteNode(oldNode
);
5617 /// Add content back from list
5618 void wxRichTextParagraph::MoveFromList(wxList
& list
)
5620 for (wxList::compatibility_iterator node
= list
.GetFirst(); node
; node
= node
->GetNext())
5622 AppendChild((wxRichTextObject
*) node
->GetData());
5627 void wxRichTextParagraph::CalculateRange(long start
, long& end
)
5629 wxRichTextCompositeObject::CalculateRange(start
, end
);
5631 // Add one for end of paragraph
5634 m_range
.SetRange(start
, end
);
5637 /// Find the object at the given position
5638 wxRichTextObject
* wxRichTextParagraph::FindObjectAtPosition(long position
)
5640 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5643 wxRichTextObject
* obj
= node
->GetData();
5644 if (obj
->GetRange().Contains(position
) ||
5645 obj
->GetRange().GetStart() == position
||
5646 obj
->GetRange().GetEnd() == position
)
5649 node
= node
->GetNext();
5654 /// Get the plain text searching from the start or end of the range.
5655 /// The resulting string may be shorter than the range given.
5656 bool wxRichTextParagraph::GetContiguousPlainText(wxString
& text
, const wxRichTextRange
& range
, bool fromStart
)
5658 text
= wxEmptyString
;
5662 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5665 wxRichTextObject
* obj
= node
->GetData();
5666 if (!obj
->GetRange().IsOutside(range
))
5668 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5671 text
+= textObj
->GetTextForRange(range
);
5679 node
= node
->GetNext();
5684 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetLast();
5687 wxRichTextObject
* obj
= node
->GetData();
5688 if (!obj
->GetRange().IsOutside(range
))
5690 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5693 text
= textObj
->GetTextForRange(range
) + text
;
5697 text
= wxT(" ") + text
;
5701 node
= node
->GetPrevious();
5708 /// Find a suitable wrap position.
5709 bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange
& range
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int availableSpace
, long& wrapPosition
, wxArrayInt
* partialExtents
)
5711 if (range
.GetLength() <= 0)
5714 // Find the first position where the line exceeds the available space.
5716 long breakPosition
= range
.GetEnd();
5718 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5719 if (partialExtents
&& partialExtents
->GetCount() >= (size_t) (GetRange().GetLength()-1)) // the final position in a paragraph is the newline
5723 if (range
.GetStart() > GetRange().GetStart())
5724 widthBefore
= (*partialExtents
)[range
.GetStart() - GetRange().GetStart() - 1];
5729 for (i
= (size_t) range
.GetStart(); i
<= (size_t) range
.GetEnd(); i
++)
5731 int widthFromStartOfThisRange
= (*partialExtents
)[i
- GetRange().GetStart()] - widthBefore
;
5733 if (widthFromStartOfThisRange
> availableSpace
)
5735 breakPosition
= i
-1;
5743 // Binary chop for speed
5744 long minPos
= range
.GetStart();
5745 long maxPos
= range
.GetEnd();
5748 if (minPos
== maxPos
)
5751 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
5753 if (sz
.x
> availableSpace
)
5754 breakPosition
= minPos
- 1;
5757 else if ((maxPos
- minPos
) == 1)
5760 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
5762 if (sz
.x
> availableSpace
)
5763 breakPosition
= minPos
- 1;
5766 GetRangeSize(wxRichTextRange(range
.GetStart(), maxPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
5767 if (sz
.x
> availableSpace
)
5768 breakPosition
= maxPos
-1;
5774 long nextPos
= minPos
+ ((maxPos
- minPos
) / 2);
5777 GetRangeSize(wxRichTextRange(range
.GetStart(), nextPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
5779 if (sz
.x
> availableSpace
)
5791 // Now we know the last position on the line.
5792 // Let's try to find a word break.
5795 if (GetContiguousPlainText(plainText
, wxRichTextRange(range
.GetStart(), breakPosition
), false))
5797 int newLinePos
= plainText
.Find(wxRichTextLineBreakChar
);
5798 if (newLinePos
!= wxNOT_FOUND
)
5800 breakPosition
= wxMax(0, range
.GetStart() + newLinePos
);
5804 int spacePos
= plainText
.Find(wxT(' '), true);
5805 int tabPos
= plainText
.Find(wxT('\t'), true);
5806 int pos
= wxMax(spacePos
, tabPos
);
5807 if (pos
!= wxNOT_FOUND
)
5809 int positionsFromEndOfString
= plainText
.length() - pos
- 1;
5810 breakPosition
= breakPosition
- positionsFromEndOfString
;
5815 wrapPosition
= breakPosition
;
5820 /// Get the bullet text for this paragraph.
5821 wxString
wxRichTextParagraph::GetBulletText()
5823 if (GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
||
5824 (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
))
5825 return wxEmptyString
;
5827 int number
= GetAttributes().GetBulletNumber();
5830 if ((GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ARABIC
) || (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
))
5832 text
.Printf(wxT("%d"), number
);
5834 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_UPPER
)
5836 // TODO: Unicode, and also check if number > 26
5837 text
.Printf(wxT("%c"), (wxChar
) (number
+64));
5839 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_LOWER
)
5841 // TODO: Unicode, and also check if number > 26
5842 text
.Printf(wxT("%c"), (wxChar
) (number
+96));
5844 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER
)
5846 text
= wxRichTextDecimalToRoman(number
);
5848 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER
)
5850 text
= wxRichTextDecimalToRoman(number
);
5853 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
)
5855 text
= GetAttributes().GetBulletText();
5858 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
)
5860 // The outline style relies on the text being computed statically,
5861 // since it depends on other levels points (e.g. 1.2.1.1). So normally the bullet text
5862 // should be stored in the attributes; if not, just use the number for this
5863 // level, as previously computed.
5864 if (!GetAttributes().GetBulletText().IsEmpty())
5865 text
= GetAttributes().GetBulletText();
5868 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PARENTHESES
)
5870 text
= wxT("(") + text
+ wxT(")");
5872 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_RIGHT_PARENTHESIS
)
5874 text
= text
+ wxT(")");
5877 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PERIOD
)
5885 /// Allocate or reuse a line object
5886 wxRichTextLine
* wxRichTextParagraph::AllocateLine(int pos
)
5888 if (pos
< (int) m_cachedLines
.GetCount())
5890 wxRichTextLine
* line
= m_cachedLines
.Item(pos
)->GetData();
5896 wxRichTextLine
* line
= new wxRichTextLine(this);
5897 m_cachedLines
.Append(line
);
5902 /// Clear remaining unused line objects, if any
5903 bool wxRichTextParagraph::ClearUnusedLines(int lineCount
)
5905 int cachedLineCount
= m_cachedLines
.GetCount();
5906 if ((int) cachedLineCount
> lineCount
)
5908 for (int i
= 0; i
< (int) (cachedLineCount
- lineCount
); i
++)
5910 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetLast();
5911 wxRichTextLine
* line
= node
->GetData();
5912 m_cachedLines
.Erase(node
);
5919 /// Get combined attributes of the base style, paragraph style and character style. We use this to dynamically
5920 /// retrieve the actual style.
5921 wxRichTextAttr
wxRichTextParagraph::GetCombinedAttributes(const wxRichTextAttr
& contentStyle
, bool includingBoxAttr
) const
5923 wxRichTextAttr attr
;
5924 wxRichTextParagraphLayoutBox
* buf
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
5927 attr
= buf
->GetBasicStyle();
5928 if (!includingBoxAttr
)
5930 attr
.GetTextBoxAttr().Reset();
5931 // The background colour will be painted by the container, and we don't
5932 // want to unnecessarily overwrite the background when we're drawing text
5933 // because this may erase the guideline (which appears just under the text
5934 // if there's no padding).
5935 attr
.SetFlags(attr
.GetFlags() & ~wxTEXT_ATTR_BACKGROUND_COLOUR
);
5937 wxRichTextApplyStyle(attr
, GetAttributes());
5940 attr
= GetAttributes();
5942 wxRichTextApplyStyle(attr
, contentStyle
);
5946 /// Get combined attributes of the base style and paragraph style.
5947 wxRichTextAttr
wxRichTextParagraph::GetCombinedAttributes(bool includingBoxAttr
) const
5949 wxRichTextAttr attr
;
5950 wxRichTextParagraphLayoutBox
* buf
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
5953 attr
= buf
->GetBasicStyle();
5954 if (!includingBoxAttr
)
5955 attr
.GetTextBoxAttr().Reset();
5956 wxRichTextApplyStyle(attr
, GetAttributes());
5959 attr
= GetAttributes();
5964 // Create default tabstop array
5965 void wxRichTextParagraph::InitDefaultTabs()
5967 // create a default tab list at 10 mm each.
5968 for (int i
= 0; i
< 20; ++i
)
5970 sm_defaultTabs
.Add(i
*100);
5974 // Clear default tabstop array
5975 void wxRichTextParagraph::ClearDefaultTabs()
5977 sm_defaultTabs
.Clear();
5980 void wxRichTextParagraph::LayoutFloat(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, int style
, wxRichTextFloatCollector
* floatCollector
)
5982 wxRichTextObjectList::compatibility_iterator node
= GetChildren().GetFirst();
5985 wxRichTextObject
* anchored
= node
->GetData();
5986 if (anchored
&& anchored
->IsFloating() && !floatCollector
->HasFloat(anchored
))
5990 anchored
->GetRangeSize(anchored
->GetRange(), size
, descent
, dc
, context
, style
);
5993 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().IsValid())
5995 offsetY
= anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetValue();
5996 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
5998 offsetY
= ConvertTenthsMMToPixels(dc
, offsetY
);
6002 int pos
= floatCollector
->GetFitPosition(anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode(), rect
.y
+ offsetY
, size
.y
);
6004 /* Update the offset */
6005 int newOffsetY
= pos
- rect
.y
;
6006 if (newOffsetY
!= offsetY
)
6008 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
6009 newOffsetY
= ConvertPixelsToTenthsMM(dc
, newOffsetY
);
6010 anchored
->GetAttributes().GetTextBoxAttr().GetTop().SetValue(newOffsetY
);
6013 if (anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT
)
6015 else if (anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT
)
6016 x
= rect
.x
+ rect
.width
- size
.x
;
6018 anchored
->SetPosition(wxPoint(x
, pos
));
6019 anchored
->SetCachedSize(size
);
6020 floatCollector
->CollectFloat(this, anchored
);
6023 node
= node
->GetNext();
6027 // Get the first position from pos that has a line break character.
6028 long wxRichTextParagraph::GetFirstLineBreakPosition(long pos
)
6030 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
6033 wxRichTextObject
* obj
= node
->GetData();
6034 if (pos
>= obj
->GetRange().GetStart() && pos
<= obj
->GetRange().GetEnd())
6036 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
6039 long breakPos
= textObj
->GetFirstLineBreakPosition(pos
);
6044 node
= node
->GetNext();
6051 * This object represents a line in a paragraph, and stores
6052 * offsets from the start of the paragraph representing the
6053 * start and end positions of the line.
6056 wxRichTextLine::wxRichTextLine(wxRichTextParagraph
* parent
)
6062 void wxRichTextLine::Init(wxRichTextParagraph
* parent
)
6065 m_range
.SetRange(-1, -1);
6066 m_pos
= wxPoint(0, 0);
6067 m_size
= wxSize(0, 0);
6069 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6070 m_objectSizes
.Clear();
6075 void wxRichTextLine::Copy(const wxRichTextLine
& obj
)
6077 m_range
= obj
.m_range
;
6078 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6079 m_objectSizes
= obj
.m_objectSizes
;
6083 /// Get the absolute object position
6084 wxPoint
wxRichTextLine::GetAbsolutePosition() const
6086 return m_parent
->GetPosition() + m_pos
;
6089 /// Get the absolute range
6090 wxRichTextRange
wxRichTextLine::GetAbsoluteRange() const
6092 wxRichTextRange
range(m_range
.GetStart() + m_parent
->GetRange().GetStart(), 0);
6093 range
.SetEnd(range
.GetStart() + m_range
.GetLength()-1);
6098 * wxRichTextPlainText
6099 * This object represents a single piece of text.
6102 IMPLEMENT_DYNAMIC_CLASS(wxRichTextPlainText
, wxRichTextObject
)
6104 wxRichTextPlainText::wxRichTextPlainText(const wxString
& text
, wxRichTextObject
* parent
, wxRichTextAttr
* style
):
6105 wxRichTextObject(parent
)
6108 SetAttributes(*style
);
6113 #define USE_KERNING_FIX 1
6115 // If insufficient tabs are defined, this is the tab width used
6116 #define WIDTH_FOR_DEFAULT_TABS 50
6119 bool wxRichTextPlainText::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int WXUNUSED(style
))
6121 wxRichTextParagraph
* para
= wxDynamicCast(GetParent(), wxRichTextParagraph
);
6122 wxASSERT (para
!= NULL
);
6124 wxRichTextAttr
textAttr(para
? para
->GetCombinedAttributes(GetAttributes(), false /* no box attributes */) : GetAttributes());
6125 context
.ApplyVirtualAttributes(textAttr
, this);
6127 // Let's make the assumption for now that for content in a paragraph, including
6128 // text, we never have a discontinuous selection. So we only deal with a
6130 wxRichTextRange selectionRange
;
6131 if (selection
.IsValid())
6133 wxRichTextRangeArray selectionRanges
= selection
.GetSelectionForObject(this);
6134 if (selectionRanges
.GetCount() > 0)
6135 selectionRange
= selectionRanges
[0];
6137 selectionRange
= wxRICHTEXT_NO_SELECTION
;
6140 selectionRange
= wxRICHTEXT_NO_SELECTION
;
6142 int offset
= GetRange().GetStart();
6144 // Replace line break characters with spaces
6145 wxString str
= m_text
;
6146 wxString toRemove
= wxRichTextLineBreakChar
;
6147 str
.Replace(toRemove
, wxT(" "));
6148 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS
))
6151 long len
= range
.GetLength();
6152 wxString stringChunk
= str
.Mid(range
.GetStart() - offset
, (size_t) len
);
6154 // Test for the optimized situations where all is selected, or none
6157 wxFont
textFont(GetBuffer()->GetFontTable().FindFont(textAttr
));
6158 wxCheckSetFont(dc
, textFont
);
6159 int charHeight
= dc
.GetCharHeight();
6162 if ( textFont
.IsOk() )
6164 if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
) )
6166 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6167 textFont
.SetPointSize( static_cast<int>(size
) );
6170 wxCheckSetFont(dc
, textFont
);
6172 else if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) )
6174 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6175 textFont
.SetPointSize( static_cast<int>(size
) );
6177 int sub_height
= static_cast<int>( static_cast<double>(charHeight
) / wxSCRIPT_MUL_FACTOR
);
6178 y
= rect
.y
+ (rect
.height
- sub_height
+ (descent
- m_descent
));
6179 wxCheckSetFont(dc
, textFont
);
6184 y
= rect
.y
+ (rect
.height
- charHeight
- (descent
- m_descent
));
6190 y
= rect
.y
+ (rect
.height
- charHeight
- (descent
- m_descent
));
6193 // TODO: new selection code
6195 // (a) All selected.
6196 if (selectionRange
.GetStart() <= range
.GetStart() && selectionRange
.GetEnd() >= range
.GetEnd())
6198 DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, true);
6200 // (b) None selected.
6201 else if (selectionRange
.GetEnd() < range
.GetStart() || selectionRange
.GetStart() > range
.GetEnd())
6203 // Draw all unselected
6204 DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, false);
6208 // (c) Part selected, part not
6209 // Let's draw unselected chunk, selected chunk, then unselected chunk.
6211 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6213 // 1. Initial unselected chunk, if any, up until start of selection.
6214 if (selectionRange
.GetStart() > range
.GetStart() && selectionRange
.GetStart() <= range
.GetEnd())
6216 int r1
= range
.GetStart();
6217 int s1
= selectionRange
.GetStart()-1;
6218 int fragmentLen
= s1
- r1
+ 1;
6219 if (fragmentLen
< 0)
6221 wxLogDebug(wxT("Mid(%d, %d"), (int)(r1
- offset
), (int)fragmentLen
);
6223 wxString stringFragment
= str
.Mid(r1
- offset
, fragmentLen
);
6225 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false);
6228 if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
)
6230 // Compensate for kerning difference
6231 wxString
stringFragment2(str
.Mid(r1
- offset
, fragmentLen
+1));
6232 wxString
stringFragment3(str
.Mid(r1
- offset
+ fragmentLen
, 1));
6234 wxCoord w1
, h1
, w2
, h2
, w3
, h3
;
6235 dc
.GetTextExtent(stringFragment
, & w1
, & h1
);
6236 dc
.GetTextExtent(stringFragment2
, & w2
, & h2
);
6237 dc
.GetTextExtent(stringFragment3
, & w3
, & h3
);
6239 int kerningDiff
= (w1
+ w3
) - w2
;
6240 x
= x
- kerningDiff
;
6245 // 2. Selected chunk, if any.
6246 if (selectionRange
.GetEnd() >= range
.GetStart())
6248 int s1
= wxMax(selectionRange
.GetStart(), range
.GetStart());
6249 int s2
= wxMin(selectionRange
.GetEnd(), range
.GetEnd());
6251 int fragmentLen
= s2
- s1
+ 1;
6252 if (fragmentLen
< 0)
6254 wxLogDebug(wxT("Mid(%d, %d"), (int)(s1
- offset
), (int)fragmentLen
);
6256 wxString stringFragment
= str
.Mid(s1
- offset
, fragmentLen
);
6258 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, true);
6261 if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
)
6263 // Compensate for kerning difference
6264 wxString
stringFragment2(str
.Mid(s1
- offset
, fragmentLen
+1));
6265 wxString
stringFragment3(str
.Mid(s1
- offset
+ fragmentLen
, 1));
6267 wxCoord w1
, h1
, w2
, h2
, w3
, h3
;
6268 dc
.GetTextExtent(stringFragment
, & w1
, & h1
);
6269 dc
.GetTextExtent(stringFragment2
, & w2
, & h2
);
6270 dc
.GetTextExtent(stringFragment3
, & w3
, & h3
);
6272 int kerningDiff
= (w1
+ w3
) - w2
;
6273 x
= x
- kerningDiff
;
6278 // 3. Remaining unselected chunk, if any
6279 if (selectionRange
.GetEnd() < range
.GetEnd())
6281 int s2
= wxMin(selectionRange
.GetEnd()+1, range
.GetEnd());
6282 int r2
= range
.GetEnd();
6284 int fragmentLen
= r2
- s2
+ 1;
6285 if (fragmentLen
< 0)
6287 wxLogDebug(wxT("Mid(%d, %d"), (int)(s2
- offset
), (int)fragmentLen
);
6289 wxString stringFragment
= str
.Mid(s2
- offset
, fragmentLen
);
6291 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false);
6298 bool wxRichTextPlainText::DrawTabbedString(wxDC
& dc
, const wxRichTextAttr
& attr
, const wxRect
& rect
,wxString
& str
, wxCoord
& x
, wxCoord
& y
, bool selected
)
6300 bool hasTabs
= (str
.Find(wxT('\t')) != wxNOT_FOUND
);
6302 wxArrayInt tabArray
;
6306 if (attr
.GetTabs().IsEmpty())
6307 tabArray
= wxRichTextParagraph::GetDefaultTabs();
6309 tabArray
= attr
.GetTabs();
6310 tabCount
= tabArray
.GetCount();
6312 for (int i
= 0; i
< tabCount
; ++i
)
6314 int pos
= tabArray
[i
];
6315 pos
= ConvertTenthsMMToPixels(dc
, pos
);
6322 int nextTabPos
= -1;
6328 wxColour
highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
));
6329 wxColour
highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
));
6331 wxCheckSetBrush(dc
, wxBrush(highlightColour
));
6332 wxCheckSetPen(dc
, wxPen(highlightColour
));
6333 dc
.SetTextForeground(highlightTextColour
);
6334 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6338 dc
.SetTextForeground(attr
.GetTextColour());
6340 if (attr
.HasFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
) && attr
.GetBackgroundColour().IsOk())
6342 dc
.SetBackgroundMode(wxBRUSHSTYLE_SOLID
);
6343 dc
.SetTextBackground(attr
.GetBackgroundColour());
6346 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6349 wxCoord x_orig
= GetParent()->GetPosition().x
;
6352 // the string has a tab
6353 // break up the string at the Tab
6354 wxString stringChunk
= str
.BeforeFirst(wxT('\t'));
6355 str
= str
.AfterFirst(wxT('\t'));
6356 dc
.GetTextExtent(stringChunk
, & w
, & h
);
6358 bool not_found
= true;
6359 for (int i
= 0; i
< tabCount
&& not_found
; ++i
)
6361 nextTabPos
= tabArray
.Item(i
) + x_orig
;
6363 // Find the next tab position.
6364 // Even if we're at the end of the tab array, we must still draw the chunk.
6366 if (nextTabPos
> tabPos
|| (i
== (tabCount
- 1)))
6368 if (nextTabPos
<= tabPos
)
6370 int defaultTabWidth
= ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
);
6371 nextTabPos
= tabPos
+ defaultTabWidth
;
6378 wxRect
selRect(x
, rect
.y
, w
, rect
.GetHeight());
6379 dc
.DrawRectangle(selRect
);
6381 dc
.DrawText(stringChunk
, x
, y
);
6383 if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
))
6385 wxPen oldPen
= dc
.GetPen();
6386 wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1));
6387 dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5));
6388 wxCheckSetPen(dc
, oldPen
);
6394 hasTabs
= (str
.Find(wxT('\t')) != wxNOT_FOUND
);
6399 dc
.GetTextExtent(str
, & w
, & h
);
6402 wxRect
selRect(x
, rect
.y
, w
, rect
.GetHeight());
6403 dc
.DrawRectangle(selRect
);
6405 dc
.DrawText(str
, x
, y
);
6407 if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
))
6409 wxPen oldPen
= dc
.GetPen();
6410 wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1));
6411 dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5));
6412 wxCheckSetPen(dc
, oldPen
);
6421 /// Lay the item out
6422 bool wxRichTextPlainText::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& WXUNUSED(rect
), const wxRect
& WXUNUSED(parentRect
), int WXUNUSED(style
))
6424 // Only lay out if we haven't already cached the size
6426 GetRangeSize(GetRange(), m_size
, m_descent
, dc
, context
, 0, wxPoint(0, 0));
6428 // Eventually we want to have a reasonable estimate of minimum size.
6429 m_minSize
= wxSize(0, 0);
6434 void wxRichTextPlainText::Copy(const wxRichTextPlainText
& obj
)
6436 wxRichTextObject::Copy(obj
);
6438 m_text
= obj
.m_text
;
6441 /// Get/set the object size for the given range. Returns false if the range
6442 /// is invalid for this object.
6443 bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int WXUNUSED(flags
), wxPoint position
, wxArrayInt
* partialExtents
) const
6445 if (!range
.IsWithin(GetRange()))
6448 wxRichTextParagraph
* para
= wxDynamicCast(GetParent(), wxRichTextParagraph
);
6449 wxASSERT (para
!= NULL
);
6451 int relativeX
= position
.x
- GetParent()->GetPosition().x
;
6453 wxRichTextAttr
textAttr(para
? para
->GetCombinedAttributes(GetAttributes()) : GetAttributes());
6454 context
.ApplyVirtualAttributes(textAttr
, (wxRichTextObject
*) this);
6456 // Always assume unformatted text, since at this level we have no knowledge
6457 // of line breaks - and we don't need it, since we'll calculate size within
6458 // formatted text by doing it in chunks according to the line ranges
6460 bool bScript(false);
6461 wxFont
font(GetBuffer()->GetFontTable().FindFont(textAttr
));
6464 if ( textAttr
.HasTextEffects() && ( (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
)
6465 || (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) ) )
6467 wxFont textFont
= font
;
6468 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6469 textFont
.SetPointSize( static_cast<int>(size
) );
6470 wxCheckSetFont(dc
, textFont
);
6475 wxCheckSetFont(dc
, font
);
6479 bool haveDescent
= false;
6480 int startPos
= range
.GetStart() - GetRange().GetStart();
6481 long len
= range
.GetLength();
6483 wxString
str(m_text
);
6484 wxString toReplace
= wxRichTextLineBreakChar
;
6485 str
.Replace(toReplace
, wxT(" "));
6487 wxString stringChunk
= str
.Mid(startPos
, (size_t) len
);
6489 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS
))
6490 stringChunk
.MakeUpper();
6494 if (stringChunk
.Find(wxT('\t')) != wxNOT_FOUND
)
6496 // the string has a tab
6497 wxArrayInt tabArray
;
6498 if (textAttr
.GetTabs().IsEmpty())
6499 tabArray
= wxRichTextParagraph::GetDefaultTabs();
6501 tabArray
= textAttr
.GetTabs();
6503 int tabCount
= tabArray
.GetCount();
6505 for (int i
= 0; i
< tabCount
; ++i
)
6507 int pos
= tabArray
[i
];
6508 pos
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, pos
);
6512 int nextTabPos
= -1;
6514 while (stringChunk
.Find(wxT('\t')) >= 0)
6516 int absoluteWidth
= 0;
6518 // the string has a tab
6519 // break up the string at the Tab
6520 wxString stringFragment
= stringChunk
.BeforeFirst(wxT('\t'));
6521 stringChunk
= stringChunk
.AfterFirst(wxT('\t'));
6526 if (partialExtents
->GetCount() > 0)
6527 oldWidth
= (*partialExtents
)[partialExtents
->GetCount()-1];
6531 // Add these partial extents
6533 dc
.GetPartialTextExtents(stringFragment
, p
);
6535 for (j
= 0; j
< p
.GetCount(); j
++)
6536 partialExtents
->Add(oldWidth
+ p
[j
]);
6538 if (partialExtents
->GetCount() > 0)
6539 absoluteWidth
= (*partialExtents
)[(*partialExtents
).GetCount()-1] + relativeX
;
6541 absoluteWidth
= relativeX
;
6545 dc
.GetTextExtent(stringFragment
, & w
, & h
);
6547 absoluteWidth
= width
+ relativeX
;
6551 bool notFound
= true;
6552 for (int i
= 0; i
< tabCount
&& notFound
; ++i
)
6554 nextTabPos
= tabArray
.Item(i
);
6556 // Find the next tab position.
6557 // Even if we're at the end of the tab array, we must still process the chunk.
6559 if (nextTabPos
> absoluteWidth
|| (i
== (tabCount
- 1)))
6561 if (nextTabPos
<= absoluteWidth
)
6563 int defaultTabWidth
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
);
6564 nextTabPos
= absoluteWidth
+ defaultTabWidth
;
6568 width
= nextTabPos
- relativeX
;
6571 partialExtents
->Add(width
);
6577 if (!stringChunk
.IsEmpty())
6582 if (partialExtents
->GetCount() > 0)
6583 oldWidth
= (*partialExtents
)[partialExtents
->GetCount()-1];
6587 // Add these partial extents
6589 dc
.GetPartialTextExtents(stringChunk
, p
);
6591 for (j
= 0; j
< p
.GetCount(); j
++)
6592 partialExtents
->Add(oldWidth
+ p
[j
]);
6596 dc
.GetTextExtent(stringChunk
, & w
, & h
, & descent
);
6604 int charHeight
= dc
.GetCharHeight();
6605 if ((*partialExtents
).GetCount() > 0)
6606 w
= (*partialExtents
)[partialExtents
->GetCount()-1];
6609 size
= wxSize(w
, charHeight
);
6613 size
= wxSize(width
, dc
.GetCharHeight());
6617 dc
.GetTextExtent(wxT("X"), & w
, & h
, & descent
);
6625 /// Do a split, returning an object containing the second part, and setting
6626 /// the first part in 'this'.
6627 wxRichTextObject
* wxRichTextPlainText::DoSplit(long pos
)
6629 long index
= pos
- GetRange().GetStart();
6631 if (index
< 0 || index
>= (int) m_text
.length())
6634 wxString firstPart
= m_text
.Mid(0, index
);
6635 wxString secondPart
= m_text
.Mid(index
);
6639 wxRichTextPlainText
* newObject
= new wxRichTextPlainText(secondPart
);
6640 newObject
->SetAttributes(GetAttributes());
6641 newObject
->SetProperties(GetProperties());
6643 newObject
->SetRange(wxRichTextRange(pos
, GetRange().GetEnd()));
6644 GetRange().SetEnd(pos
-1);
6650 void wxRichTextPlainText::CalculateRange(long start
, long& end
)
6652 end
= start
+ m_text
.length() - 1;
6653 m_range
.SetRange(start
, end
);
6657 bool wxRichTextPlainText::DeleteRange(const wxRichTextRange
& range
)
6659 wxRichTextRange r
= range
;
6661 r
.LimitTo(GetRange());
6663 if (r
.GetStart() == GetRange().GetStart() && r
.GetEnd() == GetRange().GetEnd())
6669 long startIndex
= r
.GetStart() - GetRange().GetStart();
6670 long len
= r
.GetLength();
6672 m_text
= m_text
.Mid(0, startIndex
) + m_text
.Mid(startIndex
+len
);
6676 /// Get text for the given range.
6677 wxString
wxRichTextPlainText::GetTextForRange(const wxRichTextRange
& range
) const
6679 wxRichTextRange r
= range
;
6681 r
.LimitTo(GetRange());
6683 long startIndex
= r
.GetStart() - GetRange().GetStart();
6684 long len
= r
.GetLength();
6686 return m_text
.Mid(startIndex
, len
);
6689 /// Returns true if this object can merge itself with the given one.
6690 bool wxRichTextPlainText::CanMerge(wxRichTextObject
* object
) const
6692 return object
->GetClassInfo() == CLASSINFO(wxRichTextPlainText
) &&
6693 (m_text
.empty() || (wxTextAttrEq(GetAttributes(), object
->GetAttributes()) && m_properties
== object
->GetProperties()));
6696 /// Returns true if this object merged itself with the given one.
6697 /// The calling code will then delete the given object.
6698 bool wxRichTextPlainText::Merge(wxRichTextObject
* object
)
6700 wxRichTextPlainText
* textObject
= wxDynamicCast(object
, wxRichTextPlainText
);
6701 wxASSERT( textObject
!= NULL
);
6705 m_text
+= textObject
->GetText();
6706 wxRichTextApplyStyle(m_attributes
, textObject
->GetAttributes());
6713 /// Dump to output stream for debugging
6714 void wxRichTextPlainText::Dump(wxTextOutputStream
& stream
)
6716 wxRichTextObject::Dump(stream
);
6717 stream
<< m_text
<< wxT("\n");
6720 /// Get the first position from pos that has a line break character.
6721 long wxRichTextPlainText::GetFirstLineBreakPosition(long pos
)
6724 int len
= m_text
.length();
6725 int startPos
= pos
- m_range
.GetStart();
6726 for (i
= startPos
; i
< len
; i
++)
6728 wxChar ch
= m_text
[i
];
6729 if (ch
== wxRichTextLineBreakChar
)
6731 return i
+ m_range
.GetStart();
6739 * This is a kind of box, used to represent the whole buffer
6742 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBuffer
, wxRichTextParagraphLayoutBox
)
6744 wxList
wxRichTextBuffer::sm_handlers
;
6745 wxList
wxRichTextBuffer::sm_drawingHandlers
;
6746 wxRichTextRenderer
* wxRichTextBuffer::sm_renderer
= NULL
;
6747 int wxRichTextBuffer::sm_bulletRightMargin
= 20;
6748 float wxRichTextBuffer::sm_bulletProportion
= (float) 0.3;
6751 void wxRichTextBuffer::Init()
6753 m_commandProcessor
= new wxCommandProcessor
;
6754 m_styleSheet
= NULL
;
6756 m_batchedCommandDepth
= 0;
6757 m_batchedCommand
= NULL
;
6764 wxRichTextBuffer::~wxRichTextBuffer()
6766 delete m_commandProcessor
;
6767 delete m_batchedCommand
;
6770 ClearEventHandlers();
6773 void wxRichTextBuffer::ResetAndClearCommands()
6777 GetCommandProcessor()->ClearCommands();
6780 Invalidate(wxRICHTEXT_ALL
);
6783 void wxRichTextBuffer::Copy(const wxRichTextBuffer
& obj
)
6785 wxRichTextParagraphLayoutBox::Copy(obj
);
6787 m_styleSheet
= obj
.m_styleSheet
;
6788 m_modified
= obj
.m_modified
;
6789 m_batchedCommandDepth
= 0;
6790 if (m_batchedCommand
)
6791 delete m_batchedCommand
;
6792 m_batchedCommand
= NULL
;
6793 m_suppressUndo
= obj
.m_suppressUndo
;
6794 m_invalidRange
= obj
.m_invalidRange
;
6797 /// Push style sheet to top of stack
6798 bool wxRichTextBuffer::PushStyleSheet(wxRichTextStyleSheet
* styleSheet
)
6801 styleSheet
->InsertSheet(m_styleSheet
);
6803 SetStyleSheet(styleSheet
);
6808 /// Pop style sheet from top of stack
6809 wxRichTextStyleSheet
* wxRichTextBuffer::PopStyleSheet()
6813 wxRichTextStyleSheet
* oldSheet
= m_styleSheet
;
6814 m_styleSheet
= oldSheet
->GetNextSheet();
6823 /// Submit command to insert paragraphs
6824 bool wxRichTextBuffer::InsertParagraphsWithUndo(long pos
, const wxRichTextParagraphLayoutBox
& paragraphs
, wxRichTextCtrl
* ctrl
, int flags
)
6826 return ctrl
->GetFocusObject()->InsertParagraphsWithUndo(this, pos
, paragraphs
, ctrl
, flags
);
6829 /// Submit command to insert paragraphs
6830 bool wxRichTextParagraphLayoutBox::InsertParagraphsWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxRichTextParagraphLayoutBox
& paragraphs
, wxRichTextCtrl
* ctrl
, int WXUNUSED(flags
))
6832 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6834 action
->GetNewParagraphs() = paragraphs
;
6836 action
->SetPosition(pos
);
6838 wxRichTextRange range
= wxRichTextRange(pos
, pos
+ paragraphs
.GetOwnRange().GetEnd() - 1);
6839 if (!paragraphs
.GetPartialParagraph())
6840 range
.SetEnd(range
.GetEnd()+1);
6842 // Set the range we'll need to delete in Undo
6843 action
->SetRange(range
);
6845 buffer
->SubmitAction(action
);
6850 /// Submit command to insert the given text
6851 bool wxRichTextBuffer::InsertTextWithUndo(long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
, int flags
)
6853 return ctrl
->GetFocusObject()->InsertTextWithUndo(this, pos
, text
, ctrl
, flags
);
6856 /// Submit command to insert the given text
6857 bool wxRichTextParagraphLayoutBox::InsertTextWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
, int flags
)
6859 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6861 wxRichTextAttr
* p
= NULL
;
6862 wxRichTextAttr paraAttr
;
6863 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
6865 // Get appropriate paragraph style
6866 paraAttr
= GetStyleForNewParagraph(buffer
, pos
, false, false);
6867 if (!paraAttr
.IsDefault())
6871 action
->GetNewParagraphs().AddParagraphs(text
, p
);
6873 int length
= action
->GetNewParagraphs().GetOwnRange().GetLength();
6875 if (!text
.empty() && text
.Last() != wxT('\n'))
6877 // Don't count the newline when undoing
6879 action
->GetNewParagraphs().SetPartialParagraph(true);
6881 else if (!text
.empty() && text
.Last() == wxT('\n'))
6884 action
->SetPosition(pos
);
6886 // Set the range we'll need to delete in Undo
6887 action
->SetRange(wxRichTextRange(pos
, pos
+ length
- 1));
6889 buffer
->SubmitAction(action
);
6894 /// Submit command to insert the given text
6895 bool wxRichTextBuffer::InsertNewlineWithUndo(long pos
, wxRichTextCtrl
* ctrl
, int flags
)
6897 return ctrl
->GetFocusObject()->InsertNewlineWithUndo(this, pos
, ctrl
, flags
);
6900 /// Submit command to insert the given text
6901 bool wxRichTextParagraphLayoutBox::InsertNewlineWithUndo(wxRichTextBuffer
* buffer
, long pos
, wxRichTextCtrl
* ctrl
, int flags
)
6903 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6905 wxRichTextAttr
* p
= NULL
;
6906 wxRichTextAttr paraAttr
;
6907 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
6909 paraAttr
= GetStyleForNewParagraph(buffer
, pos
, false, true /* look for next paragraph style */);
6910 if (!paraAttr
.IsDefault())
6914 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
6916 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(wxEmptyString
, this, & attr
);
6917 action
->GetNewParagraphs().AppendChild(newPara
);
6918 action
->GetNewParagraphs().UpdateRanges();
6919 action
->GetNewParagraphs().SetPartialParagraph(false);
6920 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
, false);
6924 newPara
->SetAttributes(*p
);
6926 if (flags
& wxRICHTEXT_INSERT_INTERACTIVE
)
6928 if (para
&& para
->GetRange().GetEnd() == pos
)
6931 // Now see if we need to number the paragraph.
6932 if (newPara
->GetAttributes().HasBulletNumber())
6934 wxRichTextAttr numberingAttr
;
6935 if (FindNextParagraphNumber(para
, numberingAttr
))
6936 wxRichTextApplyStyle(newPara
->GetAttributes(), (const wxRichTextAttr
&) numberingAttr
);
6940 action
->SetPosition(pos
);
6942 // Use the default character style
6943 // Use the default character style
6944 if (!buffer
->GetDefaultStyle().IsDefault() && newPara
->GetChildren().GetFirst())
6946 // Check whether the default style merely reflects the paragraph/basic style,
6947 // in which case don't apply it.
6948 wxRichTextAttr
defaultStyle(buffer
->GetDefaultStyle());
6949 wxRichTextAttr toApply
;
6952 wxRichTextAttr combinedAttr
= para
->GetCombinedAttributes(true /* include box attributes */);
6953 wxRichTextAttr newAttr
;
6954 // This filters out attributes that are accounted for by the current
6955 // paragraph/basic style
6956 wxRichTextApplyStyle(toApply
, defaultStyle
, & combinedAttr
);
6959 toApply
= defaultStyle
;
6961 if (!toApply
.IsDefault())
6962 newPara
->GetChildren().GetFirst()->GetData()->SetAttributes(toApply
);
6965 // Set the range we'll need to delete in Undo
6966 action
->SetRange(wxRichTextRange(pos1
, pos1
));
6968 buffer
->SubmitAction(action
);
6973 /// Submit command to insert the given image
6974 bool wxRichTextBuffer::InsertImageWithUndo(long pos
, const wxRichTextImageBlock
& imageBlock
, wxRichTextCtrl
* ctrl
, int flags
,
6975 const wxRichTextAttr
& textAttr
)
6977 return ctrl
->GetFocusObject()->InsertImageWithUndo(this, pos
, imageBlock
, ctrl
, flags
, textAttr
);
6980 /// Submit command to insert the given image
6981 bool wxRichTextParagraphLayoutBox::InsertImageWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxRichTextImageBlock
& imageBlock
,
6982 wxRichTextCtrl
* ctrl
, int flags
,
6983 const wxRichTextAttr
& textAttr
)
6985 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6987 wxRichTextAttr
* p
= NULL
;
6988 wxRichTextAttr paraAttr
;
6989 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
6991 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
6992 if (!paraAttr
.IsDefault())
6996 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
6998 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7000 newPara
->SetAttributes(*p
);
7002 wxRichTextImage
* imageObject
= new wxRichTextImage(imageBlock
, newPara
);
7003 newPara
->AppendChild(imageObject
);
7004 imageObject
->SetAttributes(textAttr
);
7005 action
->GetNewParagraphs().AppendChild(newPara
);
7006 action
->GetNewParagraphs().UpdateRanges();
7008 action
->GetNewParagraphs().SetPartialParagraph(true);
7010 action
->SetPosition(pos
);
7012 // Set the range we'll need to delete in Undo
7013 action
->SetRange(wxRichTextRange(pos
, pos
));
7015 buffer
->SubmitAction(action
);
7020 // Insert an object with no change of it
7021 wxRichTextObject
* wxRichTextBuffer::InsertObjectWithUndo(long pos
, wxRichTextObject
*object
, wxRichTextCtrl
* ctrl
, int flags
)
7023 return ctrl
->GetFocusObject()->InsertObjectWithUndo(this, pos
, object
, ctrl
, flags
);
7026 // Insert an object with no change of it
7027 wxRichTextObject
* wxRichTextParagraphLayoutBox::InsertObjectWithUndo(wxRichTextBuffer
* buffer
, long pos
, wxRichTextObject
*object
, wxRichTextCtrl
* ctrl
, int flags
)
7029 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Object"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7031 wxRichTextAttr
* p
= NULL
;
7032 wxRichTextAttr paraAttr
;
7033 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7035 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
7036 if (!paraAttr
.IsDefault())
7040 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7042 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7044 newPara
->SetAttributes(*p
);
7046 newPara
->AppendChild(object
);
7047 action
->GetNewParagraphs().AppendChild(newPara
);
7048 action
->GetNewParagraphs().UpdateRanges();
7050 action
->GetNewParagraphs().SetPartialParagraph(true);
7052 action
->SetPosition(pos
);
7054 // Set the range we'll need to delete in Undo
7055 action
->SetRange(wxRichTextRange(pos
, pos
));
7057 buffer
->SubmitAction(action
);
7059 wxRichTextObject
* obj
= GetLeafObjectAtPosition(pos
);
7063 /// Get the style that is appropriate for a new paragraph at this position.
7064 /// If the previous paragraph has a paragraph style name, look up the next-paragraph
7066 wxRichTextAttr
wxRichTextParagraphLayoutBox::GetStyleForNewParagraph(wxRichTextBuffer
* buffer
, long pos
, bool caretPosition
, bool lookUpNewParaStyle
) const
7068 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
, caretPosition
);
7071 wxRichTextAttr attr
;
7072 bool foundAttributes
= false;
7074 // Look for a matching paragraph style
7075 if (lookUpNewParaStyle
&& !para
->GetAttributes().GetParagraphStyleName().IsEmpty() && buffer
->GetStyleSheet())
7077 wxRichTextParagraphStyleDefinition
* paraDef
= buffer
->GetStyleSheet()->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
7080 // If we're not at the end of the paragraph, then we apply THIS style, and not the designated next style.
7081 if (para
->GetRange().GetEnd() == pos
&& !paraDef
->GetNextStyle().IsEmpty())
7083 wxRichTextParagraphStyleDefinition
* nextParaDef
= buffer
->GetStyleSheet()->FindParagraphStyle(paraDef
->GetNextStyle());
7086 foundAttributes
= true;
7087 attr
= nextParaDef
->GetStyleMergedWithBase(buffer
->GetStyleSheet());
7091 // If we didn't find the 'next style', use this style instead.
7092 if (!foundAttributes
)
7094 foundAttributes
= true;
7095 attr
= paraDef
->GetStyleMergedWithBase(buffer
->GetStyleSheet());
7100 // Also apply list style if present
7101 if (lookUpNewParaStyle
&& !para
->GetAttributes().GetListStyleName().IsEmpty() && buffer
->GetStyleSheet())
7103 wxRichTextListStyleDefinition
* listDef
= buffer
->GetStyleSheet()->FindListStyle(para
->GetAttributes().GetListStyleName());
7106 int thisIndent
= para
->GetAttributes().GetLeftIndent();
7107 int thisLevel
= para
->GetAttributes().HasOutlineLevel() ? para
->GetAttributes().GetOutlineLevel() : listDef
->FindLevelForIndent(thisIndent
);
7109 // Apply the overall list style, and item style for this level
7110 wxRichTextAttr
listStyle(listDef
->GetCombinedStyleForLevel(thisLevel
, buffer
->GetStyleSheet()));
7111 wxRichTextApplyStyle(attr
, listStyle
);
7112 attr
.SetOutlineLevel(thisLevel
);
7113 if (para
->GetAttributes().HasBulletNumber())
7114 attr
.SetBulletNumber(para
->GetAttributes().GetBulletNumber());
7118 if (!foundAttributes
)
7120 attr
= para
->GetAttributes();
7121 int flags
= attr
.GetFlags();
7123 // Eliminate character styles
7124 flags
&= ( (~ wxTEXT_ATTR_FONT
) |
7125 (~ wxTEXT_ATTR_TEXT_COLOUR
) |
7126 (~ wxTEXT_ATTR_BACKGROUND_COLOUR
) );
7127 attr
.SetFlags(flags
);
7133 return wxRichTextAttr();
7136 /// Submit command to delete this range
7137 bool wxRichTextBuffer::DeleteRangeWithUndo(const wxRichTextRange
& range
, wxRichTextCtrl
* ctrl
)
7139 return ctrl
->GetFocusObject()->DeleteRangeWithUndo(range
, ctrl
, this);
7142 /// Submit command to delete this range
7143 bool wxRichTextParagraphLayoutBox::DeleteRangeWithUndo(const wxRichTextRange
& range
, wxRichTextCtrl
* ctrl
, wxRichTextBuffer
* buffer
)
7145 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Delete"), wxRICHTEXT_DELETE
, buffer
, this, ctrl
);
7147 action
->SetPosition(ctrl
->GetCaretPosition());
7149 // Set the range to delete
7150 action
->SetRange(range
);
7152 // Copy the fragment that we'll need to restore in Undo
7153 CopyFragment(range
, action
->GetOldParagraphs());
7155 // See if we're deleting a paragraph marker, in which case we need to
7156 // make a note not to copy the attributes from the 2nd paragraph to the 1st.
7157 if (range
.GetStart() == range
.GetEnd())
7159 wxRichTextParagraph
* para
= GetParagraphAtPosition(range
.GetStart());
7160 if (para
&& para
->GetRange().GetEnd() == range
.GetEnd())
7162 wxRichTextParagraph
* nextPara
= GetParagraphAtPosition(range
.GetStart()+1);
7163 if (nextPara
&& nextPara
!= para
)
7165 action
->GetOldParagraphs().GetChildren().GetFirst()->GetData()->SetAttributes(nextPara
->GetAttributes());
7166 action
->GetOldParagraphs().GetAttributes().SetFlags(action
->GetOldParagraphs().GetAttributes().GetFlags() | wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
);
7171 buffer
->SubmitAction(action
);
7176 /// Collapse undo/redo commands
7177 bool wxRichTextBuffer::BeginBatchUndo(const wxString
& cmdName
)
7179 if (m_batchedCommandDepth
== 0)
7181 wxASSERT(m_batchedCommand
== NULL
);
7182 if (m_batchedCommand
)
7184 GetCommandProcessor()->Store(m_batchedCommand
);
7186 m_batchedCommand
= new wxRichTextCommand(cmdName
);
7189 m_batchedCommandDepth
++;
7194 /// Collapse undo/redo commands
7195 bool wxRichTextBuffer::EndBatchUndo()
7197 m_batchedCommandDepth
--;
7199 wxASSERT(m_batchedCommandDepth
>= 0);
7200 wxASSERT(m_batchedCommand
!= NULL
);
7202 if (m_batchedCommandDepth
== 0)
7204 GetCommandProcessor()->Store(m_batchedCommand
);
7205 m_batchedCommand
= NULL
;
7211 /// Submit immediately, or delay according to whether collapsing is on
7212 bool wxRichTextBuffer::SubmitAction(wxRichTextAction
* action
)
7214 if (action
&& !action
->GetNewParagraphs().IsEmpty())
7215 PrepareContent(action
->GetNewParagraphs());
7217 if (BatchingUndo() && m_batchedCommand
&& !SuppressingUndo())
7219 wxRichTextCommand
* cmd
= new wxRichTextCommand(action
->GetName());
7220 cmd
->AddAction(action
);
7222 cmd
->GetActions().Clear();
7225 m_batchedCommand
->AddAction(action
);
7229 wxRichTextCommand
* cmd
= new wxRichTextCommand(action
->GetName());
7230 cmd
->AddAction(action
);
7232 // Only store it if we're not suppressing undo.
7233 return GetCommandProcessor()->Submit(cmd
, !SuppressingUndo());
7239 /// Begin suppressing undo/redo commands.
7240 bool wxRichTextBuffer::BeginSuppressUndo()
7247 /// End suppressing undo/redo commands.
7248 bool wxRichTextBuffer::EndSuppressUndo()
7255 /// Begin using a style
7256 bool wxRichTextBuffer::BeginStyle(const wxRichTextAttr
& style
)
7258 wxRichTextAttr
newStyle(GetDefaultStyle());
7260 // Save the old default style
7261 m_attributeStack
.Append((wxObject
*) new wxRichTextAttr(GetDefaultStyle()));
7263 wxRichTextApplyStyle(newStyle
, style
);
7264 newStyle
.SetFlags(style
.GetFlags()|newStyle
.GetFlags());
7266 SetDefaultStyle(newStyle
);
7272 bool wxRichTextBuffer::EndStyle()
7274 if (!m_attributeStack
.GetFirst())
7276 wxLogDebug(_("Too many EndStyle calls!"));
7280 wxList::compatibility_iterator node
= m_attributeStack
.GetLast();
7281 wxRichTextAttr
* attr
= (wxRichTextAttr
*)node
->GetData();
7282 m_attributeStack
.Erase(node
);
7284 SetDefaultStyle(*attr
);
7291 bool wxRichTextBuffer::EndAllStyles()
7293 while (m_attributeStack
.GetCount() != 0)
7298 /// Clear the style stack
7299 void wxRichTextBuffer::ClearStyleStack()
7301 for (wxList::compatibility_iterator node
= m_attributeStack
.GetFirst(); node
; node
= node
->GetNext())
7302 delete (wxRichTextAttr
*) node
->GetData();
7303 m_attributeStack
.Clear();
7306 /// Begin using bold
7307 bool wxRichTextBuffer::BeginBold()
7309 wxRichTextAttr attr
;
7310 attr
.SetFontWeight(wxFONTWEIGHT_BOLD
);
7312 return BeginStyle(attr
);
7315 /// Begin using italic
7316 bool wxRichTextBuffer::BeginItalic()
7318 wxRichTextAttr attr
;
7319 attr
.SetFontStyle(wxFONTSTYLE_ITALIC
);
7321 return BeginStyle(attr
);
7324 /// Begin using underline
7325 bool wxRichTextBuffer::BeginUnderline()
7327 wxRichTextAttr attr
;
7328 attr
.SetFontUnderlined(true);
7330 return BeginStyle(attr
);
7333 /// Begin using point size
7334 bool wxRichTextBuffer::BeginFontSize(int pointSize
)
7336 wxRichTextAttr attr
;
7337 attr
.SetFontSize(pointSize
);
7339 return BeginStyle(attr
);
7342 /// Begin using this font
7343 bool wxRichTextBuffer::BeginFont(const wxFont
& font
)
7345 wxRichTextAttr attr
;
7348 return BeginStyle(attr
);
7351 /// Begin using this colour
7352 bool wxRichTextBuffer::BeginTextColour(const wxColour
& colour
)
7354 wxRichTextAttr attr
;
7355 attr
.SetFlags(wxTEXT_ATTR_TEXT_COLOUR
);
7356 attr
.SetTextColour(colour
);
7358 return BeginStyle(attr
);
7361 /// Begin using alignment
7362 bool wxRichTextBuffer::BeginAlignment(wxTextAttrAlignment alignment
)
7364 wxRichTextAttr attr
;
7365 attr
.SetFlags(wxTEXT_ATTR_ALIGNMENT
);
7366 attr
.SetAlignment(alignment
);
7368 return BeginStyle(attr
);
7371 /// Begin left indent
7372 bool wxRichTextBuffer::BeginLeftIndent(int leftIndent
, int leftSubIndent
)
7374 wxRichTextAttr attr
;
7375 attr
.SetFlags(wxTEXT_ATTR_LEFT_INDENT
);
7376 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7378 return BeginStyle(attr
);
7381 /// Begin right indent
7382 bool wxRichTextBuffer::BeginRightIndent(int rightIndent
)
7384 wxRichTextAttr attr
;
7385 attr
.SetFlags(wxTEXT_ATTR_RIGHT_INDENT
);
7386 attr
.SetRightIndent(rightIndent
);
7388 return BeginStyle(attr
);
7391 /// Begin paragraph spacing
7392 bool wxRichTextBuffer::BeginParagraphSpacing(int before
, int after
)
7396 flags
|= wxTEXT_ATTR_PARA_SPACING_BEFORE
;
7398 flags
|= wxTEXT_ATTR_PARA_SPACING_AFTER
;
7400 wxRichTextAttr attr
;
7401 attr
.SetFlags(flags
);
7402 attr
.SetParagraphSpacingBefore(before
);
7403 attr
.SetParagraphSpacingAfter(after
);
7405 return BeginStyle(attr
);
7408 /// Begin line spacing
7409 bool wxRichTextBuffer::BeginLineSpacing(int lineSpacing
)
7411 wxRichTextAttr attr
;
7412 attr
.SetFlags(wxTEXT_ATTR_LINE_SPACING
);
7413 attr
.SetLineSpacing(lineSpacing
);
7415 return BeginStyle(attr
);
7418 /// Begin numbered bullet
7419 bool wxRichTextBuffer::BeginNumberedBullet(int bulletNumber
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7421 wxRichTextAttr attr
;
7422 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7423 attr
.SetBulletStyle(bulletStyle
);
7424 attr
.SetBulletNumber(bulletNumber
);
7425 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7427 return BeginStyle(attr
);
7430 /// Begin symbol bullet
7431 bool wxRichTextBuffer::BeginSymbolBullet(const wxString
& symbol
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7433 wxRichTextAttr attr
;
7434 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7435 attr
.SetBulletStyle(bulletStyle
);
7436 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7437 attr
.SetBulletText(symbol
);
7439 return BeginStyle(attr
);
7442 /// Begin standard bullet
7443 bool wxRichTextBuffer::BeginStandardBullet(const wxString
& bulletName
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7445 wxRichTextAttr attr
;
7446 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7447 attr
.SetBulletStyle(bulletStyle
);
7448 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7449 attr
.SetBulletName(bulletName
);
7451 return BeginStyle(attr
);
7454 /// Begin named character style
7455 bool wxRichTextBuffer::BeginCharacterStyle(const wxString
& characterStyle
)
7457 if (GetStyleSheet())
7459 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->FindCharacterStyle(characterStyle
);
7462 wxRichTextAttr attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7463 return BeginStyle(attr
);
7469 /// Begin named paragraph style
7470 bool wxRichTextBuffer::BeginParagraphStyle(const wxString
& paragraphStyle
)
7472 if (GetStyleSheet())
7474 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(paragraphStyle
);
7477 wxRichTextAttr attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7478 return BeginStyle(attr
);
7484 /// Begin named list style
7485 bool wxRichTextBuffer::BeginListStyle(const wxString
& listStyle
, int level
, int number
)
7487 if (GetStyleSheet())
7489 wxRichTextListStyleDefinition
* def
= GetStyleSheet()->FindListStyle(listStyle
);
7492 wxRichTextAttr
attr(def
->GetCombinedStyleForLevel(level
));
7494 attr
.SetBulletNumber(number
);
7496 return BeginStyle(attr
);
7503 bool wxRichTextBuffer::BeginURL(const wxString
& url
, const wxString
& characterStyle
)
7505 wxRichTextAttr attr
;
7507 if (!characterStyle
.IsEmpty() && GetStyleSheet())
7509 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->FindCharacterStyle(characterStyle
);
7512 attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7517 return BeginStyle(attr
);
7520 /// Adds a handler to the end
7521 void wxRichTextBuffer::AddHandler(wxRichTextFileHandler
*handler
)
7523 sm_handlers
.Append(handler
);
7526 /// Inserts a handler at the front
7527 void wxRichTextBuffer::InsertHandler(wxRichTextFileHandler
*handler
)
7529 sm_handlers
.Insert( handler
);
7532 /// Removes a handler
7533 bool wxRichTextBuffer::RemoveHandler(const wxString
& name
)
7535 wxRichTextFileHandler
*handler
= FindHandler(name
);
7538 sm_handlers
.DeleteObject(handler
);
7546 /// Finds a handler by filename or, if supplied, type
7547 wxRichTextFileHandler
*wxRichTextBuffer::FindHandlerFilenameOrType(const wxString
& filename
,
7548 wxRichTextFileType imageType
)
7550 if (imageType
!= wxRICHTEXT_TYPE_ANY
)
7551 return FindHandler(imageType
);
7552 else if (!filename
.IsEmpty())
7554 wxString path
, file
, ext
;
7555 wxFileName::SplitPath(filename
, & path
, & file
, & ext
);
7556 return FindHandler(ext
, imageType
);
7563 /// Finds a handler by name
7564 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(const wxString
& name
)
7566 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7569 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7570 if (handler
->GetName().Lower() == name
.Lower()) return handler
;
7572 node
= node
->GetNext();
7577 /// Finds a handler by extension and type
7578 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(const wxString
& extension
, wxRichTextFileType type
)
7580 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7583 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7584 if ( handler
->GetExtension().Lower() == extension
.Lower() &&
7585 (type
== wxRICHTEXT_TYPE_ANY
|| handler
->GetType() == type
) )
7587 node
= node
->GetNext();
7592 /// Finds a handler by type
7593 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(wxRichTextFileType type
)
7595 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7598 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7599 if (handler
->GetType() == type
) return handler
;
7600 node
= node
->GetNext();
7605 void wxRichTextBuffer::InitStandardHandlers()
7607 if (!FindHandler(wxRICHTEXT_TYPE_TEXT
))
7608 AddHandler(new wxRichTextPlainTextHandler
);
7611 void wxRichTextBuffer::CleanUpHandlers()
7613 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7616 wxRichTextFileHandler
* handler
= (wxRichTextFileHandler
*)node
->GetData();
7617 wxList::compatibility_iterator next
= node
->GetNext();
7622 sm_handlers
.Clear();
7625 wxString
wxRichTextBuffer::GetExtWildcard(bool combine
, bool save
, wxArrayInt
* types
)
7632 wxList::compatibility_iterator node
= GetHandlers().GetFirst();
7636 wxRichTextFileHandler
* handler
= (wxRichTextFileHandler
*) node
->GetData();
7637 if (handler
->IsVisible() && ((save
&& handler
->CanSave()) || (!save
&& handler
->CanLoad())))
7642 wildcard
+= wxT(";");
7643 wildcard
+= wxT("*.") + handler
->GetExtension();
7648 wildcard
+= wxT("|");
7649 wildcard
+= handler
->GetName();
7650 wildcard
+= wxT(" ");
7651 wildcard
+= _("files");
7652 wildcard
+= wxT(" (*.");
7653 wildcard
+= handler
->GetExtension();
7654 wildcard
+= wxT(")|*.");
7655 wildcard
+= handler
->GetExtension();
7657 types
->Add(handler
->GetType());
7662 node
= node
->GetNext();
7666 wildcard
= wxT("(") + wildcard
+ wxT(")|") + wildcard
;
7671 bool wxRichTextBuffer::LoadFile(const wxString
& filename
, wxRichTextFileType type
)
7673 wxRichTextFileHandler
* handler
= FindHandlerFilenameOrType(filename
, type
);
7676 SetDefaultStyle(wxRichTextAttr());
7677 handler
->SetFlags(GetHandlerFlags());
7678 bool success
= handler
->LoadFile(this, filename
);
7679 Invalidate(wxRICHTEXT_ALL
);
7687 bool wxRichTextBuffer::SaveFile(const wxString
& filename
, wxRichTextFileType type
)
7689 wxRichTextFileHandler
* handler
= FindHandlerFilenameOrType(filename
, type
);
7692 handler
->SetFlags(GetHandlerFlags());
7693 return handler
->SaveFile(this, filename
);
7699 /// Load from a stream
7700 bool wxRichTextBuffer::LoadFile(wxInputStream
& stream
, wxRichTextFileType type
)
7702 wxRichTextFileHandler
* handler
= FindHandler(type
);
7705 SetDefaultStyle(wxRichTextAttr());
7706 handler
->SetFlags(GetHandlerFlags());
7707 bool success
= handler
->LoadFile(this, stream
);
7708 Invalidate(wxRICHTEXT_ALL
);
7715 /// Save to a stream
7716 bool wxRichTextBuffer::SaveFile(wxOutputStream
& stream
, wxRichTextFileType type
)
7718 wxRichTextFileHandler
* handler
= FindHandler(type
);
7721 handler
->SetFlags(GetHandlerFlags());
7722 return handler
->SaveFile(this, stream
);
7728 /// Copy the range to the clipboard
7729 bool wxRichTextBuffer::CopyToClipboard(const wxRichTextRange
& range
)
7731 bool success
= false;
7732 wxRichTextParagraphLayoutBox
* container
= this;
7733 if (GetRichTextCtrl())
7734 container
= GetRichTextCtrl()->GetFocusObject();
7736 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
7738 if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open())
7740 wxTheClipboard
->Clear();
7742 // Add composite object
7744 wxDataObjectComposite
* compositeObject
= new wxDataObjectComposite();
7747 wxString text
= container
->GetTextForRange(range
);
7750 text
= wxTextFile::Translate(text
, wxTextFileType_Dos
);
7753 compositeObject
->Add(new wxTextDataObject(text
), false /* not preferred */);
7756 // Add rich text buffer data object. This needs the XML handler to be present.
7758 if (FindHandler(wxRICHTEXT_TYPE_XML
))
7760 wxRichTextBuffer
* richTextBuf
= new wxRichTextBuffer
;
7761 container
->CopyFragment(range
, *richTextBuf
);
7763 compositeObject
->Add(new wxRichTextBufferDataObject(richTextBuf
), true /* preferred */);
7766 if (wxTheClipboard
->SetData(compositeObject
))
7769 wxTheClipboard
->Close();
7778 /// Paste the clipboard content to the buffer
7779 bool wxRichTextBuffer::PasteFromClipboard(long position
)
7781 bool success
= false;
7782 wxRichTextParagraphLayoutBox
* container
= this;
7783 if (GetRichTextCtrl())
7784 container
= GetRichTextCtrl()->GetFocusObject();
7786 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
7787 if (CanPasteFromClipboard())
7789 if (wxTheClipboard
->Open())
7791 if (wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())))
7793 wxRichTextBufferDataObject data
;
7794 wxTheClipboard
->GetData(data
);
7795 wxRichTextBuffer
* richTextBuffer
= data
.GetRichTextBuffer();
7798 container
->InsertParagraphsWithUndo(this, position
+1, *richTextBuffer
, GetRichTextCtrl(), 0);
7799 if (GetRichTextCtrl())
7800 GetRichTextCtrl()->ShowPosition(position
+ richTextBuffer
->GetOwnRange().GetEnd());
7801 delete richTextBuffer
;
7804 else if (wxTheClipboard
->IsSupported(wxDF_TEXT
)
7806 || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
)
7810 wxTextDataObject data
;
7811 wxTheClipboard
->GetData(data
);
7812 wxString
text(data
.GetText());
7815 text2
.Alloc(text
.Length()+1);
7817 for (i
= 0; i
< text
.Length(); i
++)
7819 wxChar ch
= text
[i
];
7820 if (ch
!= wxT('\r'))
7824 wxString text2
= text
;
7826 container
->InsertTextWithUndo(this, position
+1, text2
, GetRichTextCtrl(), wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
);
7828 if (GetRichTextCtrl())
7829 GetRichTextCtrl()->ShowPosition(position
+ text2
.Length());
7833 else if (wxTheClipboard
->IsSupported(wxDF_BITMAP
))
7835 wxBitmapDataObject data
;
7836 wxTheClipboard
->GetData(data
);
7837 wxBitmap
bitmap(data
.GetBitmap());
7838 wxImage
image(bitmap
.ConvertToImage());
7840 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, this, container
, GetRichTextCtrl(), false);
7842 action
->GetNewParagraphs().AddImage(image
);
7844 if (action
->GetNewParagraphs().GetChildCount() == 1)
7845 action
->GetNewParagraphs().SetPartialParagraph(true);
7847 action
->SetPosition(position
+1);
7849 // Set the range we'll need to delete in Undo
7850 action
->SetRange(wxRichTextRange(position
+1, position
+1));
7852 SubmitAction(action
);
7856 wxTheClipboard
->Close();
7860 wxUnusedVar(position
);
7865 /// Can we paste from the clipboard?
7866 bool wxRichTextBuffer::CanPasteFromClipboard() const
7868 bool canPaste
= false;
7869 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
7870 if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open())
7872 if (wxTheClipboard
->IsSupported(wxDF_TEXT
)
7874 || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
)
7876 || wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())) ||
7877 wxTheClipboard
->IsSupported(wxDF_BITMAP
))
7881 wxTheClipboard
->Close();
7887 /// Dumps contents of buffer for debugging purposes
7888 void wxRichTextBuffer::Dump()
7892 wxStringOutputStream
stream(& text
);
7893 wxTextOutputStream
textStream(stream
);
7900 /// Add an event handler
7901 bool wxRichTextBuffer::AddEventHandler(wxEvtHandler
* handler
)
7903 m_eventHandlers
.Append(handler
);
7907 /// Remove an event handler
7908 bool wxRichTextBuffer::RemoveEventHandler(wxEvtHandler
* handler
, bool deleteHandler
)
7910 wxList::compatibility_iterator node
= m_eventHandlers
.Find(handler
);
7913 m_eventHandlers
.Erase(node
);
7923 /// Clear event handlers
7924 void wxRichTextBuffer::ClearEventHandlers()
7926 m_eventHandlers
.Clear();
7929 /// Send event to event handlers. If sendToAll is true, will send to all event handlers,
7930 /// otherwise will stop at the first successful one.
7931 bool wxRichTextBuffer::SendEvent(wxEvent
& event
, bool sendToAll
)
7933 bool success
= false;
7934 for (wxList::compatibility_iterator node
= m_eventHandlers
.GetFirst(); node
; node
= node
->GetNext())
7936 wxEvtHandler
* handler
= (wxEvtHandler
*) node
->GetData();
7937 if (handler
->ProcessEvent(event
))
7947 /// Set style sheet and notify of the change
7948 bool wxRichTextBuffer::SetStyleSheetAndNotify(wxRichTextStyleSheet
* sheet
)
7950 wxRichTextStyleSheet
* oldSheet
= GetStyleSheet();
7952 wxWindowID winid
= wxID_ANY
;
7953 if (GetRichTextCtrl())
7954 winid
= GetRichTextCtrl()->GetId();
7956 wxRichTextEvent
event(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACING
, winid
);
7957 event
.SetEventObject(GetRichTextCtrl());
7958 event
.SetContainer(GetRichTextCtrl()->GetFocusObject());
7959 event
.SetOldStyleSheet(oldSheet
);
7960 event
.SetNewStyleSheet(sheet
);
7963 if (SendEvent(event
) && !event
.IsAllowed())
7965 if (sheet
!= oldSheet
)
7971 if (oldSheet
&& oldSheet
!= sheet
)
7974 SetStyleSheet(sheet
);
7976 event
.SetEventType(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACED
);
7977 event
.SetOldStyleSheet(NULL
);
7980 return SendEvent(event
);
7983 /// Set renderer, deleting old one
7984 void wxRichTextBuffer::SetRenderer(wxRichTextRenderer
* renderer
)
7988 sm_renderer
= renderer
;
7991 /// Hit-testing: returns a flag indicating hit test details, plus
7992 /// information about position
7993 int wxRichTextBuffer::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
7995 int ret
= wxRichTextParagraphLayoutBox::HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
7996 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
8002 textPosition
= m_ownRange
.GetEnd()-1;
8005 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
;
8009 bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxRichTextAttr
& bulletAttr
, const wxRect
& rect
)
8011 if (bulletAttr
.GetTextColour().IsOk())
8013 wxCheckSetPen(dc
, wxPen(bulletAttr
.GetTextColour()));
8014 wxCheckSetBrush(dc
, wxBrush(bulletAttr
.GetTextColour()));
8018 wxCheckSetPen(dc
, *wxBLACK_PEN
);
8019 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
8023 if (bulletAttr
.HasFont())
8025 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(bulletAttr
);
8028 font
= (*wxNORMAL_FONT
);
8030 wxCheckSetFont(dc
, font
);
8032 int charHeight
= dc
.GetCharHeight();
8034 int bulletWidth
= (int) (((float) charHeight
) * wxRichTextBuffer::GetBulletProportion());
8035 int bulletHeight
= bulletWidth
;
8039 // Calculate the top position of the character (as opposed to the whole line height)
8040 int y
= rect
.y
+ (rect
.height
- charHeight
);
8042 // Calculate where the bullet should be positioned
8043 y
= y
+ (charHeight
+1)/2 - (bulletHeight
+1)/2;
8045 // The margin between a bullet and text.
8046 int margin
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer::GetBulletRightMargin());
8048 if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
)
8049 x
= rect
.x
+ rect
.width
- bulletWidth
- margin
;
8050 else if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
)
8051 x
= x
+ (rect
.width
)/2 - bulletWidth
/2;
8053 if (bulletAttr
.GetBulletName() == wxT("standard/square"))
8055 dc
.DrawRectangle(x
, y
, bulletWidth
, bulletHeight
);
8057 else if (bulletAttr
.GetBulletName() == wxT("standard/diamond"))
8060 pts
[0].x
= x
; pts
[0].y
= y
+ bulletHeight
/2;
8061 pts
[1].x
= x
+ bulletWidth
/2; pts
[1].y
= y
;
8062 pts
[2].x
= x
+ bulletWidth
; pts
[2].y
= y
+ bulletHeight
/2;
8063 pts
[3].x
= x
+ bulletWidth
/2; pts
[3].y
= y
+ bulletHeight
;
8065 dc
.DrawPolygon(4, pts
);
8067 else if (bulletAttr
.GetBulletName() == wxT("standard/triangle"))
8070 pts
[0].x
= x
; pts
[0].y
= y
;
8071 pts
[1].x
= x
+ bulletWidth
; pts
[1].y
= y
+ bulletHeight
/2;
8072 pts
[2].x
= x
; pts
[2].y
= y
+ bulletHeight
;
8074 dc
.DrawPolygon(3, pts
);
8076 else if (bulletAttr
.GetBulletName() == wxT("standard/circle-outline"))
8078 wxCheckSetBrush(dc
, *wxTRANSPARENT_BRUSH
);
8079 dc
.DrawEllipse(x
, y
, bulletWidth
, bulletHeight
);
8081 else // "standard/circle", and catch-all
8083 dc
.DrawEllipse(x
, y
, bulletWidth
, bulletHeight
);
8089 bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxRichTextAttr
& attr
, const wxRect
& rect
, const wxString
& text
)
8094 if ((attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
) && !attr
.GetBulletFont().IsEmpty() && attr
.HasFont())
8096 wxRichTextAttr fontAttr
;
8097 fontAttr
.SetFontSize(attr
.GetFontSize());
8098 fontAttr
.SetFontStyle(attr
.GetFontStyle());
8099 fontAttr
.SetFontWeight(attr
.GetFontWeight());
8100 fontAttr
.SetFontUnderlined(attr
.GetFontUnderlined());
8101 fontAttr
.SetFontFaceName(attr
.GetBulletFont());
8102 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(fontAttr
);
8104 else if (attr
.HasFont())
8105 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(attr
);
8107 font
= (*wxNORMAL_FONT
);
8109 wxCheckSetFont(dc
, font
);
8111 if (attr
.GetTextColour().IsOk())
8112 dc
.SetTextForeground(attr
.GetTextColour());
8114 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
8116 int charHeight
= dc
.GetCharHeight();
8118 dc
.GetTextExtent(text
, & tw
, & th
);
8122 // Calculate the top position of the character (as opposed to the whole line height)
8123 int y
= rect
.y
+ (rect
.height
- charHeight
);
8125 // The margin between a bullet and text.
8126 int margin
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer::GetBulletRightMargin());
8128 if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
)
8129 x
= (rect
.x
+ rect
.width
) - tw
- margin
;
8130 else if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
)
8131 x
= x
+ (rect
.width
)/2 - tw
/2;
8133 dc
.DrawText(text
, x
, y
);
8141 bool wxRichTextStdRenderer::DrawBitmapBullet(wxRichTextParagraph
* WXUNUSED(paragraph
), wxDC
& WXUNUSED(dc
), const wxRichTextAttr
& WXUNUSED(attr
), const wxRect
& WXUNUSED(rect
))
8143 // Currently unimplemented. The intention is to store bitmaps by name in a media store associated
8144 // with the buffer. The store will allow retrieval from memory, disk or other means.
8148 /// Enumerate the standard bullet names currently supported
8149 bool wxRichTextStdRenderer::EnumerateStandardBulletNames(wxArrayString
& bulletNames
)
8151 bulletNames
.Add(wxTRANSLATE("standard/circle"));
8152 bulletNames
.Add(wxTRANSLATE("standard/circle-outline"));
8153 bulletNames
.Add(wxTRANSLATE("standard/square"));
8154 bulletNames
.Add(wxTRANSLATE("standard/diamond"));
8155 bulletNames
.Add(wxTRANSLATE("standard/triangle"));
8164 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBox
, wxRichTextParagraphLayoutBox
)
8166 wxRichTextBox::wxRichTextBox(wxRichTextObject
* parent
):
8167 wxRichTextParagraphLayoutBox(parent
)
8172 bool wxRichTextBox::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8177 // TODO: if the active object in the control, draw an indication.
8178 // We need to add the concept of active object, and not just focus object,
8179 // so we can apply commands (properties, delete, ...) to objects such as text boxes and images.
8180 // Ultimately we would like to be able to interactively resize an active object
8181 // using drag handles.
8182 return wxRichTextParagraphLayoutBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8186 void wxRichTextBox::Copy(const wxRichTextBox
& obj
)
8188 wxRichTextParagraphLayoutBox::Copy(obj
);
8191 // Edit properties via a GUI
8192 bool wxRichTextBox::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8194 wxRichTextObjectPropertiesDialog
boxDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Box Properties"));
8195 boxDlg
.SetAttributes(GetAttributes());
8197 if (boxDlg
.ShowModal() == wxID_OK
)
8199 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
8200 // indeterminate in the object.
8201 boxDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
8208 IMPLEMENT_DYNAMIC_CLASS(wxRichTextCell
, wxRichTextBox
)
8210 wxRichTextCell::wxRichTextCell(wxRichTextObject
* parent
):
8211 wxRichTextBox(parent
)
8216 bool wxRichTextCell::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8218 return wxRichTextBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8222 void wxRichTextCell::Copy(const wxRichTextCell
& obj
)
8224 wxRichTextBox::Copy(obj
);
8227 // Edit properties via a GUI
8228 bool wxRichTextCell::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8230 // We need to gather common attributes for all selected cells.
8232 wxRichTextTable
* table
= wxDynamicCast(GetParent(), wxRichTextTable
);
8233 bool multipleCells
= false;
8234 wxRichTextAttr attr
;
8236 if (table
&& buffer
&& buffer
->GetRichTextCtrl() && buffer
->GetRichTextCtrl()->GetSelection().IsValid() &&
8237 buffer
->GetRichTextCtrl()->GetSelection().GetContainer() == GetParent())
8239 wxRichTextAttr clashingAttr
, absentAttr
;
8240 const wxRichTextSelection
& sel
= buffer
->GetRichTextCtrl()->GetSelection();
8242 int selectedCellCount
= 0;
8243 for (i
= 0; i
< sel
.GetCount(); i
++)
8245 const wxRichTextRange
& range
= sel
[i
];
8246 wxRichTextCell
* cell
= table
->GetCell(range
.GetStart());
8249 wxRichTextAttr cellStyle
= cell
->GetAttributes();
8251 CollectStyle(attr
, cellStyle
, clashingAttr
, absentAttr
);
8253 selectedCellCount
++;
8256 multipleCells
= selectedCellCount
> 1;
8260 attr
= GetAttributes();
8265 caption
= _("Multiple Cell Properties");
8267 caption
= _("Cell Properties");
8269 wxRichTextObjectPropertiesDialog
cellDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, caption
);
8270 cellDlg
.SetAttributes(attr
);
8272 wxRichTextSizePage
* sizePage
= wxDynamicCast(cellDlg
.FindPage(CLASSINFO(wxRichTextSizePage
)), wxRichTextSizePage
);
8275 // We don't want position and floating controls for a cell.
8276 sizePage
->ShowPositionControls(false);
8277 sizePage
->ShowFloatingControls(false);
8280 if (cellDlg
.ShowModal() == wxID_OK
)
8284 const wxRichTextSelection
& sel
= buffer
->GetRichTextCtrl()->GetSelection();
8285 // Apply the style; we interpret indeterminate attributes as 'don't touch this attribute'
8286 // since it may represent clashing attributes across multiple objects.
8287 table
->SetCellStyle(sel
, attr
);
8290 // For a single object, indeterminate attributes set by the user should be reflected in the
8291 // actual object style, so pass the wxRICHTEXT_SETSTYLE_RESET flag to assign
8292 // the style directly instead of applying (which ignores indeterminate attributes,
8293 // leaving them as they were).
8294 cellDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
8301 WX_DEFINE_OBJARRAY(wxRichTextObjectPtrArrayArray
)
8303 IMPLEMENT_DYNAMIC_CLASS(wxRichTextTable
, wxRichTextBox
)
8305 wxRichTextTable::wxRichTextTable(wxRichTextObject
* parent
): wxRichTextBox(parent
)
8311 // Draws the object.
8312 bool wxRichTextTable::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8314 return wxRichTextBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8317 WX_DECLARE_OBJARRAY(wxRect
, wxRichTextRectArray
);
8318 WX_DEFINE_OBJARRAY(wxRichTextRectArray
);
8320 // Lays the object out. rect is the space available for layout. Often it will
8321 // be the specified overall space for this object, if trying to constrain
8322 // layout to a particular size, or it could be the total space available in the
8323 // parent. rect is the overall size, so we must subtract margins and padding.
8324 // to get the actual available space.
8325 bool wxRichTextTable::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& WXUNUSED(parentRect
), int style
)
8327 SetPosition(rect
.GetPosition());
8329 // TODO: the meaty bit. Calculate sizes of all cells and rows. Try to use
8330 // minimum size if within alloted size, then divide up remaining size
8331 // between rows/cols.
8334 wxRichTextBuffer
* buffer
= GetBuffer();
8335 if (buffer
) scale
= buffer
->GetScale();
8337 wxRect availableSpace
= GetAvailableContentArea(dc
, context
, rect
);
8338 wxTextAttrDimensionConverter
converter(dc
, scale
, availableSpace
.GetSize());
8340 wxRichTextAttr
attr(GetAttributes());
8341 context
.ApplyVirtualAttributes(attr
, this);
8343 // If we have no fixed table size, and assuming we're not pushed for
8344 // space, then we don't have to try to stretch the table to fit the contents.
8345 bool stretchToFitTableWidth
= false;
8347 int tableWidth
= rect
.width
;
8348 if (attr
.GetTextBoxAttr().GetWidth().IsValid())
8350 tableWidth
= converter
.GetPixels(attr
.GetTextBoxAttr().GetWidth());
8352 // Fixed table width, so we do want to stretch columns out if necessary.
8353 stretchToFitTableWidth
= true;
8355 // Shouldn't be able to exceed the size passed to this function
8356 tableWidth
= wxMin(rect
.width
, tableWidth
);
8359 // Get internal padding
8360 int paddingLeft
= 0, paddingTop
= 0;
8361 if (attr
.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
8362 paddingLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetLeft());
8363 if (attr
.GetTextBoxAttr().GetPadding().GetTop().IsValid())
8364 paddingTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetTop());
8366 // Assume that left and top padding are also used for inter-cell padding.
8367 int paddingX
= paddingLeft
;
8368 int paddingY
= paddingTop
;
8370 int totalLeftMargin
= 0, totalRightMargin
= 0, totalTopMargin
= 0, totalBottomMargin
= 0;
8371 GetTotalMargin(dc
, buffer
, attr
, totalLeftMargin
, totalRightMargin
, totalTopMargin
, totalBottomMargin
);
8373 // Internal table width - the area for content
8374 int internalTableWidth
= tableWidth
- totalLeftMargin
- totalRightMargin
;
8376 int rowCount
= m_cells
.GetCount();
8377 if (m_colCount
== 0 || rowCount
== 0)
8379 wxRect
overallRect(rect
.x
, rect
.y
, totalLeftMargin
+ totalRightMargin
, totalTopMargin
+ totalBottomMargin
);
8380 SetCachedSize(overallRect
.GetSize());
8382 // Zero content size
8383 SetMinSize(overallRect
.GetSize());
8384 SetMaxSize(GetMinSize());
8388 // The final calculated widths
8389 wxArrayInt colWidths
;
8390 colWidths
.Add(0, m_colCount
);
8392 wxArrayInt absoluteColWidths
;
8393 absoluteColWidths
.Add(0, m_colCount
);
8394 // wxArrayInt absoluteColWidthsSpanning(m_colCount);
8395 wxArrayInt percentageColWidths
;
8396 percentageColWidths
.Add(0, m_colCount
);
8397 // wxArrayInt percentageColWidthsSpanning(m_colCount);
8398 // These are only relevant when the first column contains spanning information.
8399 // wxArrayInt columnSpans(m_colCount); // Each contains 1 for non-spanning cell, > 1 for spanning cell.
8400 wxArrayInt maxColWidths
;
8401 maxColWidths
.Add(0, m_colCount
);
8402 wxArrayInt minColWidths
;
8403 minColWidths
.Add(0, m_colCount
);
8405 wxSize
tableSize(tableWidth
, 0);
8409 for (i
= 0; i
< m_colCount
; i
++)
8411 absoluteColWidths
[i
] = 0;
8412 // absoluteColWidthsSpanning[i] = 0;
8413 percentageColWidths
[i
] = -1;
8414 // percentageColWidthsSpanning[i] = -1;
8416 maxColWidths
[i
] = 0;
8417 minColWidths
[i
] = 0;
8418 // columnSpans[i] = 1;
8421 // (0) Determine which cells are visible according to spans
8423 // __________________
8428 // |------------------|
8429 // |__________________| 4
8431 // To calculate cell visibility:
8432 // First find all spanning cells. Build an array of span records with start x, y and end x, y.
8433 // Then for each cell, test whether we're within one of those cells, and unless we're at the start of
8434 // that cell, hide the cell.
8436 // We can also use this array to match the size of spanning cells to the grid. Or just do
8437 // this when we iterate through all cells.
8439 // 0.1: add spanning cells to an array
8440 wxRichTextRectArray rectArray
;
8441 for (j
= 0; j
< m_rowCount
; j
++)
8443 for (i
= 0; i
< m_colCount
; i
++)
8445 wxRichTextBox
* cell
= GetCell(j
, i
);
8446 int colSpan
= 1, rowSpan
= 1;
8447 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8448 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8449 if (cell
->GetProperties().HasProperty(wxT("rowspan")))
8450 rowSpan
= cell
->GetProperties().GetPropertyLong(wxT("rowspan"));
8451 if (colSpan
> 1 || rowSpan
> 1)
8453 rectArray
.Add(wxRect(i
, j
, colSpan
, rowSpan
));
8457 // 0.2: find which cells are subsumed by a spanning cell
8458 for (j
= 0; j
< m_rowCount
; j
++)
8460 for (i
= 0; i
< m_colCount
; i
++)
8462 wxRichTextBox
* cell
= GetCell(j
, i
);
8463 if (rectArray
.GetCount() == 0)
8469 int colSpan
= 1, rowSpan
= 1;
8470 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8471 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8472 if (cell
->GetProperties().HasProperty(wxT("rowspan")))
8473 rowSpan
= cell
->GetProperties().GetPropertyLong(wxT("rowspan"));
8474 if (colSpan
> 1 || rowSpan
> 1)
8476 // Assume all spanning cells are shown
8482 for (k
= 0; k
< (int) rectArray
.GetCount(); k
++)
8484 if (rectArray
[k
].Contains(wxPoint(i
, j
)))
8496 // TODO: find the first spanned cell in each row that spans the most columns and doesn't
8497 // overlap with a spanned cell starting at a previous column position.
8498 // This means we need to keep an array of rects so we can check. However
8499 // it does also mean that some spans simply may not be taken into account
8500 // where there are different spans happening on different rows. In these cases,
8501 // they will simply be as wide as their constituent columns.
8503 // (1) Do an initial layout for all cells to get minimum and maximum size, and get
8504 // the absolute or percentage width of each column.
8506 for (j
= 0; j
< m_rowCount
; j
++)
8508 // First get the overall margins so we can calculate percentage widths based on
8509 // the available content space for all cells on the row
8511 int overallRowContentMargin
= 0;
8512 int visibleCellCount
= 0;
8514 for (i
= 0; i
< m_colCount
; i
++)
8516 wxRichTextBox
* cell
= GetCell(j
, i
);
8517 if (cell
->IsShown())
8519 int cellTotalLeftMargin
= 0, cellTotalRightMargin
= 0, cellTotalTopMargin
= 0, cellTotalBottomMargin
= 0;
8520 GetTotalMargin(dc
, buffer
, cell
->GetAttributes(), cellTotalLeftMargin
, cellTotalRightMargin
, cellTotalTopMargin
, cellTotalBottomMargin
);
8522 overallRowContentMargin
+= (cellTotalLeftMargin
+ cellTotalRightMargin
);
8523 visibleCellCount
++;
8527 // Add in inter-cell padding
8528 overallRowContentMargin
+= ((visibleCellCount
-1) * paddingX
);
8530 int rowContentWidth
= internalTableWidth
- overallRowContentMargin
;
8531 wxSize
rowTableSize(rowContentWidth
, 0);
8532 wxTextAttrDimensionConverter
converter(dc
, scale
, rowTableSize
);
8534 for (i
= 0; i
< m_colCount
; i
++)
8536 wxRichTextBox
* cell
= GetCell(j
, i
);
8537 if (cell
->IsShown())
8540 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8541 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8543 // Lay out cell to find min/max widths
8544 cell
->Invalidate(wxRICHTEXT_ALL
);
8545 cell
->Layout(dc
, context
, availableSpace
, availableSpace
, style
);
8549 int absoluteCellWidth
= -1;
8550 int percentageCellWidth
= -1;
8552 // I think we need to calculate percentages from the internal table size,
8553 // minus the padding between cells which we'll need to calculate from the
8554 // (number of VISIBLE cells - 1)*paddingX. Then percentages that add up to 100%
8555 // will add up to 100%. In CSS, the width specifies the cell's content rect width,
8556 // so if we want to conform to that we'll need to add in the overall cell margins.
8557 // However, this will make it difficult to specify percentages that add up to
8558 // 100% and still fit within the table width.
8559 // Let's say two cells have 50% width. They have 10 pixels of overall margin each.
8560 // The table content rect is 500 pixels and the inter-cell padding is 20 pixels.
8561 // If we're using internal content size for the width, we would calculate the
8562 // the overall cell width for n cells as:
8563 // (500 - 20*(n-1) - overallCellMargin1 - overallCellMargin2 - ...) * percentage / 100
8564 // + thisOverallCellMargin
8565 // = 500 - 20 - 10 - 10) * 0.5 + 10 = 240 pixels overall cell width.
8566 // Adding this back, we get 240 + 240 + 20 = 500 pixels.
8568 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
8570 int w
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetWidth());
8571 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
8573 percentageCellWidth
= w
;
8577 absoluteCellWidth
= w
;
8579 // Override absolute width with minimum width if necessary
8580 if (cell
->GetMinSize().x
> 0 && absoluteCellWidth
!=1 && cell
->GetMinSize().x
> absoluteCellWidth
)
8581 absoluteCellWidth
= cell
->GetMinSize().x
;
8584 if (absoluteCellWidth
!= -1)
8586 if (absoluteCellWidth
> absoluteColWidths
[i
])
8587 absoluteColWidths
[i
] = absoluteCellWidth
;
8590 if (percentageCellWidth
!= -1)
8592 if (percentageCellWidth
> percentageColWidths
[i
])
8593 percentageColWidths
[i
] = percentageCellWidth
;
8596 if (colSpan
== 1 && cell
->GetMinSize().x
&& cell
->GetMinSize().x
> minColWidths
[i
])
8597 minColWidths
[i
] = cell
->GetMinSize().x
;
8598 if (colSpan
== 1 && cell
->GetMaxSize().x
&& cell
->GetMaxSize().x
> maxColWidths
[i
])
8599 maxColWidths
[i
] = cell
->GetMaxSize().x
;
8605 // (2) Allocate initial column widths from minimum widths, absolute values and proportions
8606 // TODO: simply merge this into (1).
8607 for (i
= 0; i
< m_colCount
; i
++)
8609 if (absoluteColWidths
[i
] > 0)
8611 colWidths
[i
] = absoluteColWidths
[i
];
8613 else if (percentageColWidths
[i
] > 0)
8615 colWidths
[i
] = percentageColWidths
[i
];
8617 // This is rubbish - we calculated the absolute widths from percentages, so
8618 // we can't do it again here.
8619 //colWidths[i] = (int) (double(percentageColWidths[i]) * double(tableWidth) / 100.0 + 0.5);
8623 // (3) Process absolute or proportional widths of spanning columns,
8624 // now that we know what our fixed column widths are going to be.
8625 // Spanned cells will try to adjust columns so the span will fit.
8626 // Even existing fixed column widths can be expanded if necessary.
8627 // Actually, currently fixed columns widths aren't adjusted; instead,
8628 // the algorithm favours earlier rows and adjusts unspecified column widths
8629 // the first time only. After that, we can't know whether the column has been
8630 // specified explicitly or not. (We could make a note if necessary.)
8631 for (j
= 0; j
< m_rowCount
; j
++)
8633 // First get the overall margins so we can calculate percentage widths based on
8634 // the available content space for all cells on the row
8636 int overallRowContentMargin
= 0;
8637 int visibleCellCount
= 0;
8639 for (i
= 0; i
< m_colCount
; i
++)
8641 wxRichTextBox
* cell
= GetCell(j
, i
);
8642 if (cell
->IsShown())
8644 int cellTotalLeftMargin
= 0, cellTotalRightMargin
= 0, cellTotalTopMargin
= 0, cellTotalBottomMargin
= 0;
8645 GetTotalMargin(dc
, buffer
, cell
->GetAttributes(), cellTotalLeftMargin
, cellTotalRightMargin
, cellTotalTopMargin
, cellTotalBottomMargin
);
8647 overallRowContentMargin
+= (cellTotalLeftMargin
+ cellTotalRightMargin
);
8648 visibleCellCount
++;
8652 // Add in inter-cell padding
8653 overallRowContentMargin
+= ((visibleCellCount
-1) * paddingX
);
8655 int rowContentWidth
= internalTableWidth
- overallRowContentMargin
;
8656 wxSize
rowTableSize(rowContentWidth
, 0);
8657 wxTextAttrDimensionConverter
converter(dc
, scale
, rowTableSize
);
8659 for (i
= 0; i
< m_colCount
; i
++)
8661 wxRichTextBox
* cell
= GetCell(j
, i
);
8662 if (cell
->IsShown())
8665 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8666 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8670 int spans
= wxMin(colSpan
, m_colCount
- i
);
8674 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
8676 cellWidth
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetWidth());
8677 // Override absolute width with minimum width if necessary
8678 if (cell
->GetMinSize().x
> 0 && cellWidth
!=1 && cell
->GetMinSize().x
> cellWidth
)
8679 cellWidth
= cell
->GetMinSize().x
;
8683 // Do we want to do this? It's the only chance we get to
8684 // use the cell's min/max sizes, so we need to work out
8685 // how we're going to balance the unspecified spanning cell
8686 // width with the possibility more-constrained constituent cell widths.
8687 // Say there's a tiny bitmap giving it a max width of 10 pixels. We
8688 // don't want to constraint all the spanned columns to fit into this cell.
8689 // OK, let's say that if any of the constituent columns don't fit,
8690 // then we simply stop constraining the columns; instead, we'll just fit the spanning
8691 // cells to the columns later.
8692 cellWidth
= cell
->GetMinSize().x
;
8693 if (cell
->GetMaxSize().x
> cellWidth
)
8694 cellWidth
= cell
->GetMaxSize().x
;
8697 // Subtract the padding between cells
8698 int spanningWidth
= cellWidth
;
8699 spanningWidth
-= paddingX
* (spans
-1);
8701 if (spanningWidth
> 0)
8703 // Now share the spanning width between columns within that span
8704 // TODO: take into account min widths of columns within the span
8705 int spanningWidthLeft
= spanningWidth
;
8706 int stretchColCount
= 0;
8707 for (k
= i
; k
< (i
+spans
); k
++)
8709 if (colWidths
[k
] > 0) // absolute or proportional width has been specified
8710 spanningWidthLeft
-= colWidths
[k
];
8714 // Now divide what's left between the remaining columns
8716 if (stretchColCount
> 0)
8717 colShare
= spanningWidthLeft
/ stretchColCount
;
8718 int colShareRemainder
= spanningWidthLeft
- (colShare
* stretchColCount
);
8720 // If fixed-width columns are currently too big, then we'll later
8721 // stretch the spanned cell to fit.
8723 if (spanningWidthLeft
> 0)
8725 for (k
= i
; k
< (i
+spans
); k
++)
8727 if (colWidths
[k
] <= 0) // absolute or proportional width has not been specified
8729 int newWidth
= colShare
;
8730 if (k
== (i
+spans
-1))
8731 newWidth
+= colShareRemainder
; // ensure all pixels are filled
8732 colWidths
[k
] = newWidth
;
8743 // (4) Next, share any remaining space out between columns that have not yet been calculated.
8744 // TODO: take into account min widths of columns within the span
8745 int tableWidthMinusPadding
= internalTableWidth
- (m_colCount
-1)*paddingX
;
8746 int widthLeft
= tableWidthMinusPadding
;
8747 int stretchColCount
= 0;
8748 for (i
= 0; i
< m_colCount
; i
++)
8750 // TODO: we need to take into account min widths.
8751 // Subtract min width from width left, then
8752 // add the colShare to the min width
8753 if (colWidths
[i
] > 0) // absolute or proportional width has been specified
8754 widthLeft
-= colWidths
[i
];
8757 if (minColWidths
[i
] > 0)
8758 widthLeft
-= minColWidths
[i
];
8764 // Now divide what's left between the remaining columns
8766 if (stretchColCount
> 0)
8767 colShare
= widthLeft
/ stretchColCount
;
8768 int colShareRemainder
= widthLeft
- (colShare
* stretchColCount
);
8770 // Check we don't have enough space, in which case shrink all columns, overriding
8771 // any absolute/proportional widths
8772 // TODO: actually we would like to divide up the shrinkage according to size.
8773 // How do we calculate the proportions that will achieve this?
8774 // Could first choose an arbitrary value for stretching cells, and then calculate
8775 // factors to multiply each width by.
8776 // TODO: want to record this fact and pass to an iteration that tries e.g. min widths
8777 if (widthLeft
< 0 || (stretchToFitTableWidth
&& (stretchColCount
== 0)))
8779 colShare
= tableWidthMinusPadding
/ m_colCount
;
8780 colShareRemainder
= tableWidthMinusPadding
- (colShare
* m_colCount
);
8781 for (i
= 0; i
< m_colCount
; i
++)
8784 minColWidths
[i
] = 0;
8788 // We have to adjust the columns if either we need to shrink the
8789 // table to fit the parent/table width, or we explicitly set the
8790 // table width and need to stretch out the table.
8791 if (widthLeft
< 0 || stretchToFitTableWidth
)
8793 for (i
= 0; i
< m_colCount
; i
++)
8795 if (colWidths
[i
] <= 0) // absolute or proportional width has not been specified
8797 if (minColWidths
[i
] > 0)
8798 colWidths
[i
] = minColWidths
[i
] + colShare
;
8800 colWidths
[i
] = colShare
;
8801 if (i
== (m_colCount
-1))
8802 colWidths
[i
] += colShareRemainder
; // ensure all pixels are filled
8807 // TODO: if spanned cells have no specified or max width, make them the
8808 // as big as the columns they span. Do this for all spanned cells in all
8809 // rows, of course. Size any spanned cells left over at the end - even if they
8810 // have width > 0, make sure they're limited to the appropriate column edge.
8814 Sort out confusion between content width
8815 and overall width later. For now, assume we specify overall width.
8817 So, now we've laid out the table to fit into the given space
8818 and have used specified widths and minimum widths.
8820 Now we need to consider how we will try to take maximum width into account.
8824 // (??) TODO: take max width into account
8826 // (6) Lay out all cells again with the current values
8829 int y
= availableSpace
.y
;
8830 for (j
= 0; j
< m_rowCount
; j
++)
8832 int x
= availableSpace
.x
; // TODO: take into account centering etc.
8833 int maxCellHeight
= 0;
8834 int maxSpecifiedCellHeight
= 0;
8836 wxArrayInt actualWidths
;
8837 actualWidths
.Add(0, m_colCount
);
8839 wxTextAttrDimensionConverter
converter(dc
, scale
);
8840 for (i
= 0; i
< m_colCount
; i
++)
8842 wxRichTextCell
* cell
= GetCell(j
, i
);
8843 if (cell
->IsShown())
8845 // Get max specified cell height
8846 // Don't handle percentages for height
8847 if (cell
->GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && cell
->GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() != wxTEXT_ATTR_UNITS_PERCENTAGE
)
8849 int h
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetHeight());
8850 if (h
> maxSpecifiedCellHeight
)
8851 maxSpecifiedCellHeight
= h
;
8854 if (colWidths
[i
] > 0) // absolute or proportional width has been specified
8857 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8858 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8860 wxRect availableCellSpace
;
8862 // TODO: take into acount spans
8865 // Calculate the size of this spanning cell from its constituent columns
8867 int spans
= wxMin(colSpan
, m_colCount
- i
);
8868 for (k
= i
; k
< spans
; k
++)
8874 availableCellSpace
= wxRect(x
, y
, xx
, -1);
8877 availableCellSpace
= wxRect(x
, y
, colWidths
[i
], -1);
8879 // Store actual width so we can force cell to be the appropriate width on the final loop
8880 actualWidths
[i
] = availableCellSpace
.GetWidth();
8883 cell
->Invalidate(wxRICHTEXT_ALL
);
8884 cell
->Layout(dc
, context
, availableCellSpace
, availableSpace
, style
);
8886 // TODO: use GetCachedSize().x to compute 'natural' size
8888 x
+= (availableCellSpace
.GetWidth() + paddingX
);
8889 if (cell
->GetCachedSize().y
> maxCellHeight
)
8890 maxCellHeight
= cell
->GetCachedSize().y
;
8895 maxCellHeight
= wxMax(maxCellHeight
, maxSpecifiedCellHeight
);
8897 for (i
= 0; i
< m_colCount
; i
++)
8899 wxRichTextCell
* cell
= GetCell(j
, i
);
8900 if (cell
->IsShown())
8902 wxRect availableCellSpace
= wxRect(cell
->GetPosition(), wxSize(actualWidths
[i
], maxCellHeight
));
8903 // Lay out cell with new height
8904 cell
->Invalidate(wxRICHTEXT_ALL
);
8905 cell
->Layout(dc
, context
, availableCellSpace
, availableSpace
, style
);
8907 // Make sure the cell size really is the appropriate size,
8908 // not the calculated box size
8909 cell
->SetCachedSize(wxSize(actualWidths
[i
], maxCellHeight
));
8911 maxRight
= wxMax(maxRight
, cell
->GetPosition().x
+ cell
->GetCachedSize().x
);
8916 if (j
< (m_rowCount
-1))
8920 // We need to add back the margins etc.
8922 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
8923 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxRight
- availableSpace
.x
, y
- availableSpace
.y
));
8924 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
8925 SetCachedSize(marginRect
.GetSize());
8928 // TODO: calculate max size
8930 SetMaxSize(GetCachedSize());
8933 // TODO: calculate min size
8935 SetMinSize(GetCachedSize());
8938 // TODO: currently we use either a fixed table width or the parent's size.
8939 // We also want to be able to calculate the table width from its content,
8940 // whether using fixed column widths or cell content min/max width.
8941 // Probably need a boolean flag to say whether we need to stretch cells
8942 // to fit the table width, or to simply use min/max cell widths. The
8943 // trouble with this is that if cell widths are not specified, they
8944 // will be tiny; we could use arbitrary defaults but this seems unsatisfactory.
8945 // Anyway, ignoring that problem, we probably need to factor layout into a function
8946 // that can can calculate the maximum unconstrained layout in case table size is
8947 // not specified. Then LayoutToBestSize() can choose to use either parent size to
8948 // constrain Layout(), or the previously-calculated max size to constraint layout.
8953 // Finds the absolute position and row height for the given character position
8954 bool wxRichTextTable::FindPosition(wxDC
& dc
, wxRichTextDrawingContext
& context
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
8956 wxRichTextCell
* child
= GetCell(index
+1);
8959 // Find the position at the start of the child cell, since the table doesn't
8960 // have any caret position of its own.
8961 return child
->FindPosition(dc
, context
, -1, pt
, height
, forceLineStart
);
8967 // Get the cell at the given character position (in the range of the table).
8968 wxRichTextCell
* wxRichTextTable::GetCell(long pos
) const
8970 int row
= 0, col
= 0;
8971 if (GetCellRowColumnPosition(pos
, row
, col
))
8973 return GetCell(row
, col
);
8979 // Get the row/column for a given character position
8980 bool wxRichTextTable::GetCellRowColumnPosition(long pos
, int& row
, int& col
) const
8982 if (m_colCount
== 0 || m_rowCount
== 0)
8985 row
= (int) (pos
/ m_colCount
);
8986 col
= pos
- (row
* m_colCount
);
8988 wxASSERT(row
< m_rowCount
&& col
< m_colCount
);
8990 if (row
< m_rowCount
&& col
< m_colCount
)
8996 // Calculate range, taking row/cell ordering into account instead of relying
8997 // on list ordering.
8998 void wxRichTextTable::CalculateRange(long start
, long& end
)
9000 long current
= start
;
9001 long lastEnd
= current
;
9010 for (i
= 0; i
< m_rowCount
; i
++)
9012 for (j
= 0; j
< m_colCount
; j
++)
9014 wxRichTextCell
* child
= GetCell(i
, j
);
9019 child
->CalculateRange(current
, childEnd
);
9022 current
= childEnd
+ 1;
9027 // A top-level object always has a range of size 1,
9028 // because its children don't count at this level.
9030 m_range
.SetRange(start
, start
);
9032 // An object with no children has zero length
9033 if (m_children
.GetCount() == 0)
9035 m_ownRange
.SetRange(0, lastEnd
);
9038 // Gets the range size.
9039 bool wxRichTextTable::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
9041 return wxRichTextBox::GetRangeSize(range
, size
, descent
, dc
, context
, flags
, position
, partialExtents
);
9044 // Deletes content in the given range.
9045 bool wxRichTextTable::DeleteRange(const wxRichTextRange
& WXUNUSED(range
))
9047 // TODO: implement deletion of cells
9051 // Gets any text in this object for the given range.
9052 wxString
wxRichTextTable::GetTextForRange(const wxRichTextRange
& range
) const
9054 return wxRichTextBox::GetTextForRange(range
);
9057 // Copies this object.
9058 void wxRichTextTable::Copy(const wxRichTextTable
& obj
)
9060 wxRichTextBox::Copy(obj
);
9064 m_rowCount
= obj
.m_rowCount
;
9065 m_colCount
= obj
.m_colCount
;
9067 m_cells
.Add(wxRichTextObjectPtrArray(), m_rowCount
);
9070 for (i
= 0; i
< m_rowCount
; i
++)
9072 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
9073 for (j
= 0; j
< m_colCount
; j
++)
9075 wxRichTextCell
* cell
= wxDynamicCast(obj
.GetCell(i
, j
)->Clone(), wxRichTextCell
);
9083 void wxRichTextTable::ClearTable()
9089 bool wxRichTextTable::CreateTable(int rows
, int cols
)
9096 m_cells
.Add(wxRichTextObjectPtrArray(), rows
);
9099 for (i
= 0; i
< rows
; i
++)
9101 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
9102 for (j
= 0; j
< cols
; j
++)
9104 wxRichTextCell
* cell
= new wxRichTextCell
;
9106 cell
->AddParagraph(wxEmptyString
);
9115 wxRichTextCell
* wxRichTextTable::GetCell(int row
, int col
) const
9117 wxASSERT(row
< m_rowCount
);
9118 wxASSERT(col
< m_colCount
);
9120 if (row
< m_rowCount
&& col
< m_colCount
)
9122 wxRichTextObjectPtrArray
& colArray
= m_cells
[row
];
9123 wxRichTextObject
* obj
= colArray
[col
];
9124 return wxDynamicCast(obj
, wxRichTextCell
);
9130 // Returns a selection object specifying the selections between start and end character positions.
9131 // For example, a table would deduce what cells (of range length 1) are selected when dragging across the table.
9132 wxRichTextSelection
wxRichTextTable::GetSelection(long start
, long end
) const
9134 wxRichTextSelection selection
;
9135 selection
.SetContainer((wxRichTextTable
*) this);
9144 wxASSERT( start
>= 0 && end
< (m_colCount
* m_rowCount
));
9146 if (end
>= (m_colCount
* m_rowCount
))
9149 // We need to find the rectangle of cells that is described by the rectangle
9150 // with start, end as the diagonal. Make sure we don't add cells that are
9151 // not currenty visible because they are overlapped by spanning cells.
9153 --------------------------
9154 | 0 | 1 | 2 | 3 | 4 |
9155 --------------------------
9156 | 5 | 6 | 7 | 8 | 9 |
9157 --------------------------
9158 | 10 | 11 | 12 | 13 | 14 |
9159 --------------------------
9160 | 15 | 16 | 17 | 18 | 19 |
9161 --------------------------
9163 Let's say we select 6 -> 18.
9165 Left and right edge cols of rectangle are 1 and 3 inclusive. Find least/greatest to find
9166 which is left and which is right.
9168 Top and bottom edge rows are 1 and 3 inclusive. Again, find least/greatest to find top and bottom.
9170 Now go through rows from 1 to 3 and only add cells that are (a) within above column range
9176 int leftCol
= start
- m_colCount
* int(start
/m_colCount
);
9177 int rightCol
= end
- m_colCount
* int(end
/m_colCount
);
9179 int topRow
= int(start
/m_colCount
);
9180 int bottomRow
= int(end
/m_colCount
);
9182 if (leftCol
> rightCol
)
9189 if (topRow
> bottomRow
)
9191 int tmp
= bottomRow
;
9197 for (i
= topRow
; i
<= bottomRow
; i
++)
9199 for (j
= leftCol
; j
<= rightCol
; j
++)
9201 wxRichTextCell
* cell
= GetCell(i
, j
);
9202 if (cell
&& cell
->IsShown())
9203 selection
.Add(cell
->GetRange());
9210 // Sets the attributes for the cells specified by the selection.
9211 bool wxRichTextTable::SetCellStyle(const wxRichTextSelection
& selection
, const wxRichTextAttr
& style
, int flags
)
9213 if (selection
.GetContainer() != this)
9216 wxRichTextBuffer
* buffer
= GetBuffer();
9217 bool haveControl
= (buffer
&& buffer
->GetRichTextCtrl() != NULL
);
9218 bool withUndo
= haveControl
&& ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
9221 buffer
->BeginBatchUndo(_("Set Cell Style"));
9223 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
9226 wxRichTextCell
* cell
= wxDynamicCast(node
->GetData(), wxRichTextCell
);
9227 if (cell
&& selection
.WithinSelection(cell
->GetRange().GetStart()))
9228 SetStyle(cell
, style
, flags
);
9229 node
= node
->GetNext();
9232 // Do action, or delay it until end of batch.
9234 buffer
->EndBatchUndo();
9239 bool wxRichTextTable::DeleteRows(int startRow
, int noRows
)
9241 wxASSERT((startRow
+ noRows
) < m_rowCount
);
9242 if ((startRow
+ noRows
) >= m_rowCount
)
9246 for (i
= startRow
; i
< (startRow
+noRows
); i
++)
9248 wxRichTextObjectPtrArray
& colArray
= m_cells
[startRow
];
9249 for (j
= 0; j
< (int) colArray
.GetCount(); j
++)
9251 wxRichTextObject
* cell
= colArray
[j
];
9252 RemoveChild(cell
, true);
9255 // Keep deleting at the same position, since we move all
9257 m_cells
.RemoveAt(startRow
);
9260 m_rowCount
= m_rowCount
- noRows
;
9265 bool wxRichTextTable::DeleteColumns(int startCol
, int noCols
)
9267 wxASSERT((startCol
+ noCols
) < m_colCount
);
9268 if ((startCol
+ noCols
) >= m_colCount
)
9271 bool deleteRows
= (noCols
== m_colCount
);
9274 for (i
= 0; i
< m_rowCount
; i
++)
9276 wxRichTextObjectPtrArray
& colArray
= m_cells
[deleteRows
? 0 : i
];
9277 for (j
= startCol
; j
< (startCol
+noCols
); j
++)
9279 wxRichTextObject
* cell
= colArray
[j
];
9280 RemoveChild(cell
, true);
9284 m_cells
.RemoveAt(0);
9289 m_colCount
= m_colCount
- noCols
;
9294 bool wxRichTextTable::AddRows(int startRow
, int noRows
, const wxRichTextAttr
& attr
)
9296 wxASSERT(startRow
<= m_rowCount
);
9297 if (startRow
> m_rowCount
)
9301 for (i
= 0; i
< noRows
; i
++)
9304 if (startRow
== m_rowCount
)
9306 m_cells
.Add(wxRichTextObjectPtrArray());
9307 idx
= m_cells
.GetCount() - 1;
9311 m_cells
.Insert(wxRichTextObjectPtrArray(), startRow
+i
);
9315 wxRichTextObjectPtrArray
& colArray
= m_cells
[idx
];
9316 for (j
= 0; j
< m_colCount
; j
++)
9318 wxRichTextCell
* cell
= new wxRichTextCell
;
9319 cell
->GetAttributes() = attr
;
9326 m_rowCount
= m_rowCount
+ noRows
;
9330 bool wxRichTextTable::AddColumns(int startCol
, int noCols
, const wxRichTextAttr
& attr
)
9332 wxASSERT(startCol
<= m_colCount
);
9333 if (startCol
> m_colCount
)
9337 for (i
= 0; i
< m_rowCount
; i
++)
9339 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
9340 for (j
= 0; j
< noCols
; j
++)
9342 wxRichTextCell
* cell
= new wxRichTextCell
;
9343 cell
->GetAttributes() = attr
;
9347 if (startCol
== m_colCount
)
9350 colArray
.Insert(cell
, startCol
+j
);
9354 m_colCount
= m_colCount
+ noCols
;
9359 // Edit properties via a GUI
9360 bool wxRichTextTable::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
9362 wxRichTextObjectPropertiesDialog
boxDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Table Properties"));
9363 boxDlg
.SetAttributes(GetAttributes());
9365 if (boxDlg
.ShowModal() == wxID_OK
)
9367 boxDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
9375 * Module to initialise and clean up handlers
9378 class wxRichTextModule
: public wxModule
9380 DECLARE_DYNAMIC_CLASS(wxRichTextModule
)
9382 wxRichTextModule() {}
9385 wxRichTextBuffer::SetRenderer(new wxRichTextStdRenderer
);
9386 wxRichTextBuffer::InitStandardHandlers();
9387 wxRichTextParagraph::InitDefaultTabs();
9392 wxRichTextBuffer::CleanUpHandlers();
9393 wxRichTextBuffer::CleanUpDrawingHandlers();
9394 wxRichTextDecimalToRoman(-1);
9395 wxRichTextParagraph::ClearDefaultTabs();
9396 wxRichTextCtrl::ClearAvailableFontNames();
9397 wxRichTextBuffer::SetRenderer(NULL
);
9401 IMPLEMENT_DYNAMIC_CLASS(wxRichTextModule
, wxModule
)
9404 // If the richtext lib is dynamically loaded after the app has already started
9405 // (such as from wxPython) then the built-in module system will not init this
9406 // module. Provide this function to do it manually.
9407 void wxRichTextModuleInit()
9409 wxModule
* module = new wxRichTextModule
;
9411 wxModule::RegisterModule(module);
9416 * Commands for undo/redo
9420 wxRichTextCommand::wxRichTextCommand(const wxString
& name
, wxRichTextCommandId id
, wxRichTextBuffer
* buffer
,
9421 wxRichTextParagraphLayoutBox
* container
, wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
): wxCommand(true, name
)
9423 /* wxRichTextAction* action = */ new wxRichTextAction(this, name
, id
, buffer
, container
, ctrl
, ignoreFirstTime
);
9426 wxRichTextCommand::wxRichTextCommand(const wxString
& name
): wxCommand(true, name
)
9430 wxRichTextCommand::~wxRichTextCommand()
9435 void wxRichTextCommand::AddAction(wxRichTextAction
* action
)
9437 if (!m_actions
.Member(action
))
9438 m_actions
.Append(action
);
9441 bool wxRichTextCommand::Do()
9443 for (wxList::compatibility_iterator node
= m_actions
.GetFirst(); node
; node
= node
->GetNext())
9445 wxRichTextAction
* action
= (wxRichTextAction
*) node
->GetData();
9452 bool wxRichTextCommand::Undo()
9454 for (wxList::compatibility_iterator node
= m_actions
.GetLast(); node
; node
= node
->GetPrevious())
9456 wxRichTextAction
* action
= (wxRichTextAction
*) node
->GetData();
9463 void wxRichTextCommand::ClearActions()
9465 WX_CLEAR_LIST(wxList
, m_actions
);
9473 wxRichTextAction::wxRichTextAction(wxRichTextCommand
* cmd
, const wxString
& name
, wxRichTextCommandId id
,
9474 wxRichTextBuffer
* buffer
, wxRichTextParagraphLayoutBox
* container
,
9475 wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
)
9479 m_containerAddress
.Create(buffer
, container
);
9480 m_ignoreThis
= ignoreFirstTime
;
9485 m_newParagraphs
.SetDefaultStyle(buffer
->GetDefaultStyle());
9486 m_newParagraphs
.SetBasicStyle(buffer
->GetBasicStyle());
9488 cmd
->AddAction(this);
9491 wxRichTextAction::~wxRichTextAction()
9497 // Returns the container that this action refers to, using the container address and top-level buffer.
9498 wxRichTextParagraphLayoutBox
* wxRichTextAction::GetContainer() const
9500 wxRichTextParagraphLayoutBox
* container
= wxDynamicCast(GetContainerAddress().GetObject(m_buffer
), wxRichTextParagraphLayoutBox
);
9505 void wxRichTextAction::CalculateRefreshOptimizations(wxArrayInt
& optimizationLineCharPositions
, wxArrayInt
& optimizationLineYPositions
)
9507 // Store a list of line start character and y positions so we can figure out which area
9508 // we need to refresh
9510 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9511 wxRichTextParagraphLayoutBox
* container
= GetContainer();
9512 wxASSERT(container
!= NULL
);
9516 // NOTE: we're assuming that the buffer is laid out correctly at this point.
9517 // If we had several actions, which only invalidate and leave layout until the
9518 // paint handler is called, then this might not be true. So we may need to switch
9519 // optimisation on only when we're simply adding text and not simultaneously
9520 // deleting a selection, for example. Or, we make sure the buffer is laid out correctly
9521 // first, but of course this means we'll be doing it twice.
9522 if (!m_buffer
->IsDirty() && m_ctrl
) // can only do optimisation if the buffer is already laid out correctly
9524 wxSize clientSize
= m_ctrl
->GetClientSize();
9525 wxPoint firstVisiblePt
= m_ctrl
->GetFirstVisiblePoint();
9526 int lastY
= firstVisiblePt
.y
+ clientSize
.y
;
9528 wxRichTextParagraph
* para
= container
->GetParagraphAtPosition(GetRange().GetStart());
9529 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(para
);
9532 wxRichTextParagraph
* child
= (wxRichTextParagraph
*) node
->GetData();
9533 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
9536 wxRichTextLine
* line
= node2
->GetData();
9537 wxPoint pt
= line
->GetAbsolutePosition();
9538 wxRichTextRange range
= line
->GetAbsoluteRange();
9542 node2
= wxRichTextLineList::compatibility_iterator();
9543 node
= wxRichTextObjectList::compatibility_iterator();
9545 else if (range
.GetStart() > GetPosition() && pt
.y
>= firstVisiblePt
.y
)
9547 optimizationLineCharPositions
.Add(range
.GetStart());
9548 optimizationLineYPositions
.Add(pt
.y
);
9552 node2
= node2
->GetNext();
9556 node
= node
->GetNext();
9562 bool wxRichTextAction::Do()
9564 m_buffer
->Modify(true);
9566 wxRichTextParagraphLayoutBox
* container
= GetContainer();
9567 wxASSERT(container
!= NULL
);
9573 case wxRICHTEXT_INSERT
:
9575 // Store a list of line start character and y positions so we can figure out which area
9576 // we need to refresh
9577 wxArrayInt optimizationLineCharPositions
;
9578 wxArrayInt optimizationLineYPositions
;
9580 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9581 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
9584 container
->InsertFragment(GetRange().GetStart(), m_newParagraphs
);
9585 container
->UpdateRanges();
9587 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9588 // Layout() would stop prematurely at the top level.
9589 container
->InvalidateHierarchy(wxRichTextRange(wxMax(0, GetRange().GetStart()-1), GetRange().GetEnd()));
9591 long newCaretPosition
= GetPosition() + m_newParagraphs
.GetOwnRange().GetLength();
9593 // Character position to caret position
9594 newCaretPosition
--;
9596 // Don't take into account the last newline
9597 if (m_newParagraphs
.GetPartialParagraph())
9598 newCaretPosition
--;
9600 if (m_newParagraphs
.GetChildren().GetCount() > 1)
9602 wxRichTextObject
* p
= (wxRichTextObject
*) m_newParagraphs
.GetChildren().GetLast()->GetData();
9603 if (p
->GetRange().GetLength() == 1)
9604 newCaretPosition
--;
9607 newCaretPosition
= wxMin(newCaretPosition
, (container
->GetOwnRange().GetEnd()-1));
9609 UpdateAppearance(newCaretPosition
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */);
9611 wxRichTextEvent
cmdEvent(
9612 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED
,
9613 m_ctrl
? m_ctrl
->GetId() : -1);
9614 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9615 cmdEvent
.SetRange(GetRange());
9616 cmdEvent
.SetPosition(GetRange().GetStart());
9617 cmdEvent
.SetContainer(container
);
9619 m_buffer
->SendEvent(cmdEvent
);
9623 case wxRICHTEXT_DELETE
:
9625 wxArrayInt optimizationLineCharPositions
;
9626 wxArrayInt optimizationLineYPositions
;
9628 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9629 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
9632 container
->DeleteRange(GetRange());
9633 container
->UpdateRanges();
9634 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9635 // Layout() would stop prematurely at the top level.
9636 container
->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
9638 long caretPos
= GetRange().GetStart()-1;
9639 if (caretPos
>= container
->GetOwnRange().GetEnd())
9642 UpdateAppearance(caretPos
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */);
9644 wxRichTextEvent
cmdEvent(
9645 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED
,
9646 m_ctrl
? m_ctrl
->GetId() : -1);
9647 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9648 cmdEvent
.SetRange(GetRange());
9649 cmdEvent
.SetPosition(GetRange().GetStart());
9650 cmdEvent
.SetContainer(container
);
9652 m_buffer
->SendEvent(cmdEvent
);
9656 case wxRICHTEXT_CHANGE_STYLE
:
9657 case wxRICHTEXT_CHANGE_PROPERTIES
:
9659 ApplyParagraphs(GetNewParagraphs());
9661 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9662 // Layout() would stop prematurely at the top level.
9663 container
->InvalidateHierarchy(GetRange());
9665 UpdateAppearance(GetPosition());
9667 wxRichTextEvent
cmdEvent(
9668 m_cmdId
== wxRICHTEXT_CHANGE_STYLE
? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
: wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED
,
9669 m_ctrl
? m_ctrl
->GetId() : -1);
9670 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9671 cmdEvent
.SetRange(GetRange());
9672 cmdEvent
.SetPosition(GetRange().GetStart());
9673 cmdEvent
.SetContainer(container
);
9675 m_buffer
->SendEvent(cmdEvent
);
9679 case wxRICHTEXT_CHANGE_ATTRIBUTES
:
9681 wxRichTextObject
* obj
= m_objectAddress
.GetObject(m_buffer
); // container->GetChildAtPosition(GetRange().GetStart());
9684 wxRichTextAttr oldAttr
= obj
->GetAttributes();
9685 obj
->GetAttributes() = m_attributes
;
9686 m_attributes
= oldAttr
;
9689 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9690 // Layout() would stop prematurely at the top level.
9691 container
->InvalidateHierarchy(GetRange());
9693 UpdateAppearance(GetPosition());
9695 wxRichTextEvent
cmdEvent(
9696 wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
,
9697 m_ctrl
? m_ctrl
->GetId() : -1);
9698 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9699 cmdEvent
.SetRange(GetRange());
9700 cmdEvent
.SetPosition(GetRange().GetStart());
9701 cmdEvent
.SetContainer(container
);
9703 m_buffer
->SendEvent(cmdEvent
);
9707 case wxRICHTEXT_CHANGE_OBJECT
:
9709 wxRichTextObject
* obj
= m_objectAddress
.GetObject(m_buffer
);
9710 // wxRichTextObject* obj = container->GetChildAtPosition(GetRange().GetStart());
9711 if (obj
&& m_object
)
9713 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(obj
);
9716 wxRichTextObject
* obj
= node
->GetData();
9717 node
->SetData(m_object
);
9722 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9723 // Layout() would stop prematurely at the top level.
9724 container
->InvalidateHierarchy(GetRange());
9726 UpdateAppearance(GetPosition());
9728 // TODO: send new kind of modification event
9739 bool wxRichTextAction::Undo()
9741 m_buffer
->Modify(true);
9743 wxRichTextParagraphLayoutBox
* container
= GetContainer();
9744 wxASSERT(container
!= NULL
);
9750 case wxRICHTEXT_INSERT
:
9752 wxArrayInt optimizationLineCharPositions
;
9753 wxArrayInt optimizationLineYPositions
;
9755 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9756 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
9759 container
->DeleteRange(GetRange());
9760 container
->UpdateRanges();
9761 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9762 // Layout() would stop prematurely at the top level.
9763 container
->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
9765 long newCaretPosition
= GetPosition() - 1;
9767 UpdateAppearance(newCaretPosition
, true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */);
9769 wxRichTextEvent
cmdEvent(
9770 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED
,
9771 m_ctrl
? m_ctrl
->GetId() : -1);
9772 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9773 cmdEvent
.SetRange(GetRange());
9774 cmdEvent
.SetPosition(GetRange().GetStart());
9775 cmdEvent
.SetContainer(container
);
9777 m_buffer
->SendEvent(cmdEvent
);
9781 case wxRICHTEXT_DELETE
:
9783 wxArrayInt optimizationLineCharPositions
;
9784 wxArrayInt optimizationLineYPositions
;
9786 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9787 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
9790 container
->InsertFragment(GetRange().GetStart(), m_oldParagraphs
);
9791 container
->UpdateRanges();
9792 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9793 // Layout() would stop prematurely at the top level.
9794 container
->InvalidateHierarchy(GetRange());
9796 UpdateAppearance(GetPosition(), true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */);
9798 wxRichTextEvent
cmdEvent(
9799 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED
,
9800 m_ctrl
? m_ctrl
->GetId() : -1);
9801 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9802 cmdEvent
.SetRange(GetRange());
9803 cmdEvent
.SetPosition(GetRange().GetStart());
9804 cmdEvent
.SetContainer(container
);
9806 m_buffer
->SendEvent(cmdEvent
);
9810 case wxRICHTEXT_CHANGE_STYLE
:
9811 case wxRICHTEXT_CHANGE_PROPERTIES
:
9813 ApplyParagraphs(GetOldParagraphs());
9814 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9815 // Layout() would stop prematurely at the top level.
9816 container
->InvalidateHierarchy(GetRange());
9818 UpdateAppearance(GetPosition());
9820 wxRichTextEvent
cmdEvent(
9821 m_cmdId
== wxRICHTEXT_CHANGE_STYLE
? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
: wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED
,
9822 m_ctrl
? m_ctrl
->GetId() : -1);
9823 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9824 cmdEvent
.SetRange(GetRange());
9825 cmdEvent
.SetPosition(GetRange().GetStart());
9826 cmdEvent
.SetContainer(container
);
9828 m_buffer
->SendEvent(cmdEvent
);
9832 case wxRICHTEXT_CHANGE_ATTRIBUTES
:
9833 case wxRICHTEXT_CHANGE_OBJECT
:
9844 /// Update the control appearance
9845 void wxRichTextAction::UpdateAppearance(long caretPosition
, bool sendUpdateEvent
, wxArrayInt
* optimizationLineCharPositions
, wxArrayInt
* optimizationLineYPositions
, bool isDoCmd
)
9847 wxRichTextParagraphLayoutBox
* container
= GetContainer();
9848 wxASSERT(container
!= NULL
);
9854 m_ctrl
->SetFocusObject(container
);
9855 m_ctrl
->SetCaretPosition(caretPosition
);
9857 if (!m_ctrl
->IsFrozen())
9859 wxRect containerRect
= container
->GetRect();
9861 m_ctrl
->LayoutContent();
9863 // Refresh everything if there were floating objects or the container changed size
9864 // (we can't yet optimize in these cases, since more complex interaction with other content occurs)
9865 if (container
->GetFloatingObjectCount() > 0 || (container
->GetParent() && containerRect
!= container
->GetRect()))
9867 m_ctrl
->Refresh(false);
9871 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9872 // Find refresh rectangle if we are in a position to optimise refresh
9873 if ((m_cmdId
== wxRICHTEXT_INSERT
|| m_cmdId
== wxRICHTEXT_DELETE
) && optimizationLineCharPositions
)
9877 wxSize clientSize
= m_ctrl
->GetClientSize();
9878 wxPoint firstVisiblePt
= m_ctrl
->GetFirstVisiblePoint();
9880 // Start/end positions
9882 int lastY
= firstVisiblePt
.y
+ clientSize
.y
;
9884 bool foundEnd
= false;
9886 // position offset - how many characters were inserted
9887 int positionOffset
= GetRange().GetLength();
9889 // Determine whether this is Do or Undo, and adjust positionOffset accordingly
9890 if ((m_cmdId
== wxRICHTEXT_DELETE
&& isDoCmd
) || (m_cmdId
== wxRICHTEXT_INSERT
&& !isDoCmd
))
9891 positionOffset
= - positionOffset
;
9893 // find the first line which is being drawn at the same position as it was
9894 // before. Since we're talking about a simple insertion, we can assume
9895 // that the rest of the window does not need to be redrawn.
9897 wxRichTextParagraph
* para
= container
->GetParagraphAtPosition(GetPosition());
9898 // Since we support floating layout, we should redraw the whole para instead of just
9899 // the first line touching the invalid range.
9902 firstY
= para
->GetPosition().y
;
9905 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(para
);
9908 wxRichTextParagraph
* child
= (wxRichTextParagraph
*) node
->GetData();
9909 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
9912 wxRichTextLine
* line
= node2
->GetData();
9913 wxPoint pt
= line
->GetAbsolutePosition();
9914 wxRichTextRange range
= line
->GetAbsoluteRange();
9916 // we want to find the first line that is in the same position
9917 // as before. This will mean we're at the end of the changed text.
9919 if (pt
.y
> lastY
) // going past the end of the window, no more info
9921 node2
= wxRichTextLineList::compatibility_iterator();
9922 node
= wxRichTextObjectList::compatibility_iterator();
9924 // Detect last line in the buffer
9925 else if (!node2
->GetNext() && para
->GetRange().Contains(container
->GetOwnRange().GetEnd()))
9927 // If deleting text, make sure we refresh below as well as above
9928 if (positionOffset
>= 0)
9931 lastY
= pt
.y
+ line
->GetSize().y
;
9934 node2
= wxRichTextLineList::compatibility_iterator();
9935 node
= wxRichTextObjectList::compatibility_iterator();
9941 // search for this line being at the same position as before
9942 for (i
= 0; i
< optimizationLineCharPositions
->GetCount(); i
++)
9944 if (((*optimizationLineCharPositions
)[i
] + positionOffset
== range
.GetStart()) &&
9945 ((*optimizationLineYPositions
)[i
] == pt
.y
))
9947 // Stop, we're now the same as we were
9952 node2
= wxRichTextLineList::compatibility_iterator();
9953 node
= wxRichTextObjectList::compatibility_iterator();
9961 node2
= node2
->GetNext();
9965 node
= node
->GetNext();
9968 firstY
= wxMax(firstVisiblePt
.y
, firstY
);
9970 lastY
= firstVisiblePt
.y
+ clientSize
.y
;
9972 // Convert to device coordinates
9973 wxRect
rect(m_ctrl
->GetPhysicalPoint(wxPoint(firstVisiblePt
.x
, firstY
)), wxSize(clientSize
.x
, lastY
- firstY
));
9974 m_ctrl
->RefreshRect(rect
);
9978 m_ctrl
->Refresh(false);
9980 m_ctrl
->PositionCaret();
9982 // This causes styles to persist when doing programmatic
9983 // content creation except when Freeze/Thaw is used, so
9984 // disable this and check for the consequences.
9985 // m_ctrl->SetDefaultStyleToCursorStyle();
9987 if (sendUpdateEvent
)
9988 wxTextCtrl::SendTextUpdatedEvent(m_ctrl
);
9993 /// Replace the buffer paragraphs with the new ones.
9994 void wxRichTextAction::ApplyParagraphs(const wxRichTextParagraphLayoutBox
& fragment
)
9996 wxRichTextParagraphLayoutBox
* container
= GetContainer();
9997 wxASSERT(container
!= NULL
);
10001 wxRichTextObjectList::compatibility_iterator node
= fragment
.GetChildren().GetFirst();
10004 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
10005 wxASSERT (para
!= NULL
);
10007 // We'll replace the existing paragraph by finding the paragraph at this position,
10008 // delete its node data, and setting a copy as the new node data.
10009 // TODO: make more efficient by simply swapping old and new paragraph objects.
10011 wxRichTextParagraph
* existingPara
= container
->GetParagraphAtPosition(para
->GetRange().GetStart());
10014 wxRichTextObjectList::compatibility_iterator bufferParaNode
= container
->GetChildren().Find(existingPara
);
10015 if (bufferParaNode
)
10017 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(*para
);
10018 newPara
->SetParent(container
);
10020 bufferParaNode
->SetData(newPara
);
10022 delete existingPara
;
10026 node
= node
->GetNext();
10033 * This stores beginning and end positions for a range of data.
10036 WX_DEFINE_OBJARRAY(wxRichTextRangeArray
);
10038 /// Limit this range to be within 'range'
10039 bool wxRichTextRange::LimitTo(const wxRichTextRange
& range
)
10041 if (m_start
< range
.m_start
)
10042 m_start
= range
.m_start
;
10044 if (m_end
> range
.m_end
)
10045 m_end
= range
.m_end
;
10051 * wxRichTextImage implementation
10052 * This object represents an image.
10055 IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage
, wxRichTextObject
)
10057 wxRichTextImage::wxRichTextImage(const wxImage
& image
, wxRichTextObject
* parent
, wxRichTextAttr
* charStyle
):
10058 wxRichTextObject(parent
)
10060 m_imageBlock
.MakeImageBlockDefaultQuality(image
, wxBITMAP_TYPE_PNG
);
10062 SetAttributes(*charStyle
);
10065 wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock
& imageBlock
, wxRichTextObject
* parent
, wxRichTextAttr
* charStyle
):
10066 wxRichTextObject(parent
)
10068 m_imageBlock
= imageBlock
;
10070 SetAttributes(*charStyle
);
10073 /// Create a cached image at the required size
10074 bool wxRichTextImage::LoadImageCache(wxDC
& dc
, bool resetCache
)
10076 if (resetCache
|| !m_imageCache
.IsOk() /* || m_imageCache.GetWidth() != size.x || m_imageCache.GetHeight() != size.y */)
10078 if (!m_imageBlock
.IsOk())
10082 m_imageBlock
.Load(image
);
10086 int width
= image
.GetWidth();
10087 int height
= image
.GetHeight();
10089 if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetWidth().GetValue() > 0)
10091 if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
10092 width
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetWidth().GetValue());
10094 width
= GetAttributes().GetTextBoxAttr().GetWidth().GetValue();
10096 if (GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetHeight().GetValue() > 0)
10098 if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
10099 height
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetHeight().GetValue());
10101 height
= GetAttributes().GetTextBoxAttr().GetHeight().GetValue();
10104 if (image
.GetWidth() == width
&& image
.GetHeight() == height
)
10105 m_imageCache
= wxBitmap(image
);
10108 // If the original width and height is small, e.g. 400 or below,
10109 // scale up and then down to improve image quality. This can make
10110 // a big difference, with not much performance hit.
10111 int upscaleThreshold
= 400;
10113 if (image
.GetWidth() <= upscaleThreshold
|| image
.GetHeight() <= upscaleThreshold
)
10115 img
= image
.Scale(image
.GetWidth()*2, image
.GetHeight()*2);
10116 img
.Rescale(width
, height
, wxIMAGE_QUALITY_HIGH
);
10119 img
= image
.Scale(width
, height
, wxIMAGE_QUALITY_HIGH
);
10120 m_imageCache
= wxBitmap(img
);
10124 return m_imageCache
.IsOk();
10128 bool wxRichTextImage::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int WXUNUSED(descent
), int WXUNUSED(style
))
10133 // Don't need cached size AFAIK
10134 // wxSize size = GetCachedSize();
10135 if (!LoadImageCache(dc
))
10138 wxRichTextAttr
attr(GetAttributes());
10139 context
.ApplyVirtualAttributes(attr
, this);
10141 DrawBoxAttributes(dc
, GetBuffer(), attr
, wxRect(rect
.GetPosition(), GetCachedSize()));
10144 int y
= rect
.y
+ (rect
.height
- m_imageCache
.GetHeight());
10146 dc
.DrawBitmap(m_imageCache
, rect
.x
, y
, true);
10149 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
10150 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
10151 marginRect
= rect
; // outer rectangle, will calculate contentRect
10152 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
10154 dc
.DrawBitmap(m_imageCache
, contentRect
.x
, contentRect
.y
, true);
10156 if (selection
.WithinSelection(range
.GetStart(), this))
10158 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
10159 wxCheckSetPen(dc
, *wxBLACK_PEN
);
10160 dc
.SetLogicalFunction(wxINVERT
);
10161 dc
.DrawRectangle(contentRect
);
10162 dc
.SetLogicalFunction(wxCOPY
);
10168 /// Lay the item out
10169 bool wxRichTextImage::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& WXUNUSED(parentRect
), int WXUNUSED(style
))
10171 if (!LoadImageCache(dc
))
10174 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
10175 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
10176 contentRect
= wxRect(wxPoint(0,0), imageSize
);
10178 wxRichTextAttr
attr(GetAttributes());
10179 context
.ApplyVirtualAttributes(attr
, this);
10181 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
10183 wxSize overallSize
= marginRect
.GetSize();
10185 SetCachedSize(overallSize
);
10186 SetMaxSize(overallSize
);
10187 SetMinSize(overallSize
);
10188 SetPosition(rect
.GetPosition());
10193 /// Get/set the object size for the given range. Returns false if the range
10194 /// is invalid for this object.
10195 bool wxRichTextImage::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& WXUNUSED(descent
), wxDC
& dc
, wxRichTextDrawingContext
& context
, int WXUNUSED(flags
), wxPoint
WXUNUSED(position
), wxArrayInt
* partialExtents
) const
10197 if (!range
.IsWithin(GetRange()))
10200 if (!((wxRichTextImage
*)this)->LoadImageCache(dc
))
10202 size
.x
= 0; size
.y
= 0;
10203 if (partialExtents
)
10204 partialExtents
->Add(0);
10208 wxRichTextAttr
attr(GetAttributes());
10209 context
.ApplyVirtualAttributes(attr
, (wxRichTextObject
*) this);
10211 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
10212 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
10213 contentRect
= wxRect(wxPoint(0,0), imageSize
);
10214 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
10216 wxSize overallSize
= marginRect
.GetSize();
10218 if (partialExtents
)
10219 partialExtents
->Add(overallSize
.x
);
10221 size
= overallSize
;
10226 // Get the 'natural' size for an object. For an image, it would be the
10228 wxTextAttrSize
wxRichTextImage::GetNaturalSize() const
10230 wxTextAttrSize size
;
10231 if (GetImageCache().IsOk())
10233 size
.SetWidth(GetImageCache().GetWidth(), wxTEXT_ATTR_UNITS_PIXELS
);
10234 size
.SetHeight(GetImageCache().GetHeight(), wxTEXT_ATTR_UNITS_PIXELS
);
10241 void wxRichTextImage::Copy(const wxRichTextImage
& obj
)
10243 wxRichTextObject::Copy(obj
);
10245 m_imageBlock
= obj
.m_imageBlock
;
10248 /// Edit properties via a GUI
10249 bool wxRichTextImage::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
10251 wxRichTextObjectPropertiesDialog
imageDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Picture Properties"));
10252 imageDlg
.SetAttributes(GetAttributes());
10254 if (imageDlg
.ShowModal() == wxID_OK
)
10256 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
10257 // indeterminate in the object.
10258 imageDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
10270 /// Compare two attribute objects
10271 bool wxTextAttrEq(const wxRichTextAttr
& attr1
, const wxRichTextAttr
& attr2
)
10273 return (attr1
== attr2
);
10276 // Partial equality test taking flags into account
10277 bool wxTextAttrEqPartial(const wxRichTextAttr
& attr1
, const wxRichTextAttr
& attr2
)
10279 return attr1
.EqPartial(attr2
);
10283 bool wxRichTextTabsEq(const wxArrayInt
& tabs1
, const wxArrayInt
& tabs2
)
10285 if (tabs1
.GetCount() != tabs2
.GetCount())
10289 for (i
= 0; i
< tabs1
.GetCount(); i
++)
10291 if (tabs1
[i
] != tabs2
[i
])
10297 bool wxRichTextApplyStyle(wxRichTextAttr
& destStyle
, const wxRichTextAttr
& style
, wxRichTextAttr
* compareWith
)
10299 return destStyle
.Apply(style
, compareWith
);
10302 // Remove attributes
10303 bool wxRichTextRemoveStyle(wxRichTextAttr
& destStyle
, const wxRichTextAttr
& style
)
10305 return destStyle
.RemoveStyle(style
);
10308 /// Combine two bitlists, specifying the bits of interest with separate flags.
10309 bool wxRichTextCombineBitlists(int& valueA
, int valueB
, int& flagsA
, int flagsB
)
10311 return wxRichTextAttr::CombineBitlists(valueA
, valueB
, flagsA
, flagsB
);
10314 /// Compare two bitlists
10315 bool wxRichTextBitlistsEqPartial(int valueA
, int valueB
, int flags
)
10317 return wxRichTextAttr::BitlistsEqPartial(valueA
, valueB
, flags
);
10320 /// Split into paragraph and character styles
10321 bool wxRichTextSplitParaCharStyles(const wxRichTextAttr
& style
, wxRichTextAttr
& parStyle
, wxRichTextAttr
& charStyle
)
10323 return wxRichTextAttr::SplitParaCharStyles(style
, parStyle
, charStyle
);
10326 /// Convert a decimal to Roman numerals
10327 wxString
wxRichTextDecimalToRoman(long n
)
10329 static wxArrayInt decimalNumbers
;
10330 static wxArrayString romanNumbers
;
10335 decimalNumbers
.Clear();
10336 romanNumbers
.Clear();
10337 return wxEmptyString
;
10340 if (decimalNumbers
.GetCount() == 0)
10342 #define wxRichTextAddDecRom(n, r) decimalNumbers.Add(n); romanNumbers.Add(r);
10344 wxRichTextAddDecRom(1000, wxT("M"));
10345 wxRichTextAddDecRom(900, wxT("CM"));
10346 wxRichTextAddDecRom(500, wxT("D"));
10347 wxRichTextAddDecRom(400, wxT("CD"));
10348 wxRichTextAddDecRom(100, wxT("C"));
10349 wxRichTextAddDecRom(90, wxT("XC"));
10350 wxRichTextAddDecRom(50, wxT("L"));
10351 wxRichTextAddDecRom(40, wxT("XL"));
10352 wxRichTextAddDecRom(10, wxT("X"));
10353 wxRichTextAddDecRom(9, wxT("IX"));
10354 wxRichTextAddDecRom(5, wxT("V"));
10355 wxRichTextAddDecRom(4, wxT("IV"));
10356 wxRichTextAddDecRom(1, wxT("I"));
10362 while (n
> 0 && i
< 13)
10364 if (n
>= decimalNumbers
[i
])
10366 n
-= decimalNumbers
[i
];
10367 roman
+= romanNumbers
[i
];
10374 if (roman
.IsEmpty())
10380 * wxRichTextFileHandler
10381 * Base class for file handlers
10384 IMPLEMENT_CLASS(wxRichTextFileHandler
, wxObject
)
10386 #if wxUSE_FFILE && wxUSE_STREAMS
10387 bool wxRichTextFileHandler::LoadFile(wxRichTextBuffer
*buffer
, const wxString
& filename
)
10389 wxFFileInputStream
stream(filename
);
10391 return LoadFile(buffer
, stream
);
10396 bool wxRichTextFileHandler::SaveFile(wxRichTextBuffer
*buffer
, const wxString
& filename
)
10398 wxFFileOutputStream
stream(filename
);
10400 return SaveFile(buffer
, stream
);
10404 #endif // wxUSE_FFILE && wxUSE_STREAMS
10406 /// Can we handle this filename (if using files)? By default, checks the extension.
10407 bool wxRichTextFileHandler::CanHandle(const wxString
& filename
) const
10409 wxString path
, file
, ext
;
10410 wxFileName::SplitPath(filename
, & path
, & file
, & ext
);
10412 return (ext
.Lower() == GetExtension());
10416 * wxRichTextTextHandler
10417 * Plain text handler
10420 IMPLEMENT_CLASS(wxRichTextPlainTextHandler
, wxRichTextFileHandler
)
10423 bool wxRichTextPlainTextHandler::DoLoadFile(wxRichTextBuffer
*buffer
, wxInputStream
& stream
)
10425 if (!stream
.IsOk())
10431 while (!stream
.Eof())
10433 int ch
= stream
.GetC();
10437 if (ch
== 10 && lastCh
!= 13)
10440 if (ch
> 0 && ch
!= 10)
10447 buffer
->ResetAndClearCommands();
10449 buffer
->AddParagraphs(str
);
10450 buffer
->UpdateRanges();
10455 bool wxRichTextPlainTextHandler::DoSaveFile(wxRichTextBuffer
*buffer
, wxOutputStream
& stream
)
10457 if (!stream
.IsOk())
10460 wxString text
= buffer
->GetText();
10462 wxString newLine
= wxRichTextLineBreakChar
;
10463 text
.Replace(newLine
, wxT("\n"));
10465 wxCharBuffer buf
= text
.ToAscii();
10467 stream
.Write((const char*) buf
, text
.length());
10470 #endif // wxUSE_STREAMS
10473 * Stores information about an image, in binary in-memory form
10476 wxRichTextImageBlock::wxRichTextImageBlock()
10481 wxRichTextImageBlock::wxRichTextImageBlock(const wxRichTextImageBlock
& block
):wxObject()
10487 wxRichTextImageBlock::~wxRichTextImageBlock()
10492 void wxRichTextImageBlock::Init()
10496 m_imageType
= wxBITMAP_TYPE_INVALID
;
10499 void wxRichTextImageBlock::Clear()
10503 m_imageType
= wxBITMAP_TYPE_INVALID
;
10507 // Load the original image into a memory block.
10508 // If the image is not a JPEG, we must convert it into a JPEG
10509 // to conserve space.
10510 // If it's not a JPEG we can make use of 'image', already scaled, so we don't have to
10511 // load the image a 2nd time.
10513 bool wxRichTextImageBlock::MakeImageBlock(const wxString
& filename
, wxBitmapType imageType
,
10514 wxImage
& image
, bool convertToJPEG
)
10516 m_imageType
= imageType
;
10518 wxString
filenameToRead(filename
);
10519 bool removeFile
= false;
10521 if (imageType
== wxBITMAP_TYPE_INVALID
)
10522 return false; // Could not determine image type
10524 if ((imageType
!= wxBITMAP_TYPE_JPEG
) && convertToJPEG
)
10526 wxString tempFile
=
10527 wxFileName::CreateTempFileName(_("image"));
10529 wxASSERT(!tempFile
.IsEmpty());
10531 image
.SaveFile(tempFile
, wxBITMAP_TYPE_JPEG
);
10532 filenameToRead
= tempFile
;
10535 m_imageType
= wxBITMAP_TYPE_JPEG
;
10538 if (!file
.Open(filenameToRead
))
10541 m_dataSize
= (size_t) file
.Length();
10546 m_data
= ReadBlock(filenameToRead
, m_dataSize
);
10549 wxRemoveFile(filenameToRead
);
10551 return (m_data
!= NULL
);
10554 // Make an image block from the wxImage in the given
10556 bool wxRichTextImageBlock::MakeImageBlock(wxImage
& image
, wxBitmapType imageType
, int quality
)
10558 image
.SetOption(wxT("quality"), quality
);
10560 if (imageType
== wxBITMAP_TYPE_INVALID
)
10561 return false; // Could not determine image type
10563 return DoMakeImageBlock(image
, imageType
);
10566 // Uses a const wxImage for efficiency, but can't set quality (only relevant for JPEG)
10567 bool wxRichTextImageBlock::MakeImageBlockDefaultQuality(const wxImage
& image
, wxBitmapType imageType
)
10569 if (imageType
== wxBITMAP_TYPE_INVALID
)
10570 return false; // Could not determine image type
10572 return DoMakeImageBlock(image
, imageType
);
10575 // Makes the image block
10576 bool wxRichTextImageBlock::DoMakeImageBlock(const wxImage
& image
, wxBitmapType imageType
)
10578 wxMemoryOutputStream memStream
;
10579 if (!image
.SaveFile(memStream
, imageType
))
10584 unsigned char* block
= new unsigned char[memStream
.GetSize()];
10592 m_imageType
= imageType
;
10593 m_dataSize
= memStream
.GetSize();
10595 memStream
.CopyTo(m_data
, m_dataSize
);
10597 return (m_data
!= NULL
);
10601 bool wxRichTextImageBlock::Write(const wxString
& filename
)
10603 return WriteBlock(filename
, m_data
, m_dataSize
);
10606 void wxRichTextImageBlock::Copy(const wxRichTextImageBlock
& block
)
10608 m_imageType
= block
.m_imageType
;
10610 m_dataSize
= block
.m_dataSize
;
10611 if (m_dataSize
== 0)
10614 m_data
= new unsigned char[m_dataSize
];
10616 for (i
= 0; i
< m_dataSize
; i
++)
10617 m_data
[i
] = block
.m_data
[i
];
10621 void wxRichTextImageBlock::operator=(const wxRichTextImageBlock
& block
)
10626 // Load a wxImage from the block
10627 bool wxRichTextImageBlock::Load(wxImage
& image
)
10632 // Read in the image.
10634 wxMemoryInputStream
mstream(m_data
, m_dataSize
);
10635 bool success
= image
.LoadFile(mstream
, GetImageType());
10637 wxString tempFile
= wxFileName::CreateTempFileName(_("image"));
10638 wxASSERT(!tempFile
.IsEmpty());
10640 if (!WriteBlock(tempFile
, m_data
, m_dataSize
))
10644 success
= image
.LoadFile(tempFile
, GetImageType());
10645 wxRemoveFile(tempFile
);
10651 // Write data in hex to a stream
10652 bool wxRichTextImageBlock::WriteHex(wxOutputStream
& stream
)
10654 if (m_dataSize
== 0)
10657 int bufSize
= 100000;
10658 if (int(2*m_dataSize
) < bufSize
)
10659 bufSize
= 2*m_dataSize
;
10660 char* buf
= new char[bufSize
+1];
10662 int left
= m_dataSize
;
10667 if (left
*2 > bufSize
)
10669 n
= bufSize
; left
-= (bufSize
/2);
10673 n
= left
*2; left
= 0;
10677 for (i
= 0; i
< (n
/2); i
++)
10679 wxDecToHex(m_data
[j
], b
, b
+1);
10684 stream
.Write((const char*) buf
, n
);
10690 // Read data in hex from a stream
10691 bool wxRichTextImageBlock::ReadHex(wxInputStream
& stream
, int length
, wxBitmapType imageType
)
10693 int dataSize
= length
/2;
10698 // create a null terminated temporary string:
10702 m_data
= new unsigned char[dataSize
];
10704 for (i
= 0; i
< dataSize
; i
++)
10706 str
[0] = (char)stream
.GetC();
10707 str
[1] = (char)stream
.GetC();
10709 m_data
[i
] = (unsigned char)wxHexToDec(str
);
10712 m_dataSize
= dataSize
;
10713 m_imageType
= imageType
;
10718 // Allocate and read from stream as a block of memory
10719 unsigned char* wxRichTextImageBlock::ReadBlock(wxInputStream
& stream
, size_t size
)
10721 unsigned char* block
= new unsigned char[size
];
10725 stream
.Read(block
, size
);
10730 unsigned char* wxRichTextImageBlock::ReadBlock(const wxString
& filename
, size_t size
)
10732 wxFileInputStream
stream(filename
);
10733 if (!stream
.IsOk())
10736 return ReadBlock(stream
, size
);
10739 // Write memory block to stream
10740 bool wxRichTextImageBlock::WriteBlock(wxOutputStream
& stream
, unsigned char* block
, size_t size
)
10742 stream
.Write((void*) block
, size
);
10743 return stream
.IsOk();
10747 // Write memory block to file
10748 bool wxRichTextImageBlock::WriteBlock(const wxString
& filename
, unsigned char* block
, size_t size
)
10750 wxFileOutputStream
outStream(filename
);
10751 if (!outStream
.IsOk())
10754 return WriteBlock(outStream
, block
, size
);
10757 // Gets the extension for the block's type
10758 wxString
wxRichTextImageBlock::GetExtension() const
10760 wxImageHandler
* handler
= wxImage::FindHandler(GetImageType());
10762 return handler
->GetExtension();
10764 return wxEmptyString
;
10770 * The data object for a wxRichTextBuffer
10773 const wxChar
*wxRichTextBufferDataObject::ms_richTextBufferFormatId
= wxT("wxShape");
10775 wxRichTextBufferDataObject::wxRichTextBufferDataObject(wxRichTextBuffer
* richTextBuffer
)
10777 m_richTextBuffer
= richTextBuffer
;
10779 // this string should uniquely identify our format, but is otherwise
10781 m_formatRichTextBuffer
.SetId(GetRichTextBufferFormatId());
10783 SetFormat(m_formatRichTextBuffer
);
10786 wxRichTextBufferDataObject::~wxRichTextBufferDataObject()
10788 delete m_richTextBuffer
;
10791 // after a call to this function, the richTextBuffer is owned by the caller and it
10792 // is responsible for deleting it!
10793 wxRichTextBuffer
* wxRichTextBufferDataObject::GetRichTextBuffer()
10795 wxRichTextBuffer
* richTextBuffer
= m_richTextBuffer
;
10796 m_richTextBuffer
= NULL
;
10798 return richTextBuffer
;
10801 wxDataFormat
wxRichTextBufferDataObject::GetPreferredFormat(Direction
WXUNUSED(dir
)) const
10803 return m_formatRichTextBuffer
;
10806 size_t wxRichTextBufferDataObject::GetDataSize() const
10808 if (!m_richTextBuffer
)
10814 wxStringOutputStream
stream(& bufXML
);
10815 if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
))
10817 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
10823 wxCharBuffer buffer
= bufXML
.mb_str(wxConvUTF8
);
10824 return strlen(buffer
) + 1;
10826 return bufXML
.Length()+1;
10830 bool wxRichTextBufferDataObject::GetDataHere(void *pBuf
) const
10832 if (!pBuf
|| !m_richTextBuffer
)
10838 wxStringOutputStream
stream(& bufXML
);
10839 if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
))
10841 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
10847 wxCharBuffer buffer
= bufXML
.mb_str(wxConvUTF8
);
10848 size_t len
= strlen(buffer
);
10849 memcpy((char*) pBuf
, (const char*) buffer
, len
);
10850 ((char*) pBuf
)[len
] = 0;
10852 size_t len
= bufXML
.Length();
10853 memcpy((char*) pBuf
, (const char*) bufXML
.c_str(), len
);
10854 ((char*) pBuf
)[len
] = 0;
10860 bool wxRichTextBufferDataObject::SetData(size_t WXUNUSED(len
), const void *buf
)
10862 wxDELETE(m_richTextBuffer
);
10864 wxString
bufXML((const char*) buf
, wxConvUTF8
);
10866 m_richTextBuffer
= new wxRichTextBuffer
;
10868 wxStringInputStream
stream(bufXML
);
10869 if (!m_richTextBuffer
->LoadFile(stream
, wxRICHTEXT_TYPE_XML
))
10871 wxLogError(wxT("Could not read the buffer from an XML stream.\nYou may have forgotten to add the XML file handler."));
10873 wxDELETE(m_richTextBuffer
);
10885 * wxRichTextFontTable
10886 * Manages quick access to a pool of fonts for rendering rich text
10889 WX_DECLARE_STRING_HASH_MAP_WITH_DECL(wxFont
, wxRichTextFontTableHashMap
, class WXDLLIMPEXP_RICHTEXT
);
10891 class wxRichTextFontTableData
: public wxObjectRefData
10894 wxRichTextFontTableData() {}
10896 wxFont
FindFont(const wxRichTextAttr
& fontSpec
);
10898 wxRichTextFontTableHashMap m_hashMap
;
10901 wxFont
wxRichTextFontTableData::FindFont(const wxRichTextAttr
& fontSpec
)
10903 wxString
facename(fontSpec
.GetFontFaceName());
10904 wxString
spec(wxString::Format(wxT("%d-%d-%d-%d-%s-%d"), fontSpec
.GetFontSize(), fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), (int) fontSpec
.GetFontUnderlined(), facename
.c_str(), (int) fontSpec
.GetFontEncoding()));
10905 wxRichTextFontTableHashMap::iterator entry
= m_hashMap
.find(spec
);
10907 if ( entry
== m_hashMap
.end() )
10909 wxFont
font(fontSpec
.GetFontSize(), wxDEFAULT
, fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), fontSpec
.GetFontUnderlined(), facename
.c_str());
10910 m_hashMap
[spec
] = font
;
10915 return entry
->second
;
10919 IMPLEMENT_DYNAMIC_CLASS(wxRichTextFontTable
, wxObject
)
10921 wxRichTextFontTable::wxRichTextFontTable()
10923 m_refData
= new wxRichTextFontTableData
;
10926 wxRichTextFontTable::wxRichTextFontTable(const wxRichTextFontTable
& table
)
10932 wxRichTextFontTable::~wxRichTextFontTable()
10937 bool wxRichTextFontTable::operator == (const wxRichTextFontTable
& table
) const
10939 return (m_refData
== table
.m_refData
);
10942 void wxRichTextFontTable::operator= (const wxRichTextFontTable
& table
)
10947 wxFont
wxRichTextFontTable::FindFont(const wxRichTextAttr
& fontSpec
)
10949 wxRichTextFontTableData
* data
= (wxRichTextFontTableData
*) m_refData
;
10951 return data
->FindFont(fontSpec
);
10956 void wxRichTextFontTable::Clear()
10958 wxRichTextFontTableData
* data
= (wxRichTextFontTableData
*) m_refData
;
10960 data
->m_hashMap
.clear();
10966 void wxTextBoxAttr::Reset()
10969 m_floatMode
= wxTEXT_BOX_ATTR_FLOAT_NONE
;
10970 m_clearMode
= wxTEXT_BOX_ATTR_CLEAR_NONE
;
10971 m_collapseMode
= wxTEXT_BOX_ATTR_COLLAPSE_NONE
;
10972 m_verticalAlignment
= wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_NONE
;
10973 m_boxStyleName
= wxEmptyString
;
10977 m_position
.Reset();
10988 bool wxTextBoxAttr::operator== (const wxTextBoxAttr
& attr
) const
10991 m_flags
== attr
.m_flags
&&
10992 m_floatMode
== attr
.m_floatMode
&&
10993 m_clearMode
== attr
.m_clearMode
&&
10994 m_collapseMode
== attr
.m_collapseMode
&&
10995 m_verticalAlignment
== attr
.m_verticalAlignment
&&
10997 m_margins
== attr
.m_margins
&&
10998 m_padding
== attr
.m_padding
&&
10999 m_position
== attr
.m_position
&&
11001 m_size
== attr
.m_size
&&
11002 m_minSize
== attr
.m_minSize
&&
11003 m_maxSize
== attr
.m_maxSize
&&
11005 m_border
== attr
.m_border
&&
11006 m_outline
== attr
.m_outline
&&
11008 m_boxStyleName
== attr
.m_boxStyleName
11012 // Partial equality test
11013 bool wxTextBoxAttr::EqPartial(const wxTextBoxAttr
& attr
) const
11015 if (attr
.HasFloatMode() && HasFloatMode() && (GetFloatMode() != attr
.GetFloatMode()))
11018 if (attr
.HasClearMode() && HasClearMode() && (GetClearMode() != attr
.GetClearMode()))
11021 if (attr
.HasCollapseBorders() && HasCollapseBorders() && (attr
.GetCollapseBorders() != GetCollapseBorders()))
11024 if (attr
.HasVerticalAlignment() && HasVerticalAlignment() && (attr
.GetVerticalAlignment() != GetVerticalAlignment()))
11027 if (attr
.HasBoxStyleName() && HasBoxStyleName() && (attr
.GetBoxStyleName() != GetBoxStyleName()))
11032 if (!m_position
.EqPartial(attr
.m_position
))
11037 if (!m_size
.EqPartial(attr
.m_size
))
11039 if (!m_minSize
.EqPartial(attr
.m_minSize
))
11041 if (!m_maxSize
.EqPartial(attr
.m_maxSize
))
11046 if (!m_margins
.EqPartial(attr
.m_margins
))
11051 if (!m_padding
.EqPartial(attr
.m_padding
))
11056 if (!GetBorder().EqPartial(attr
.GetBorder()))
11061 if (!GetOutline().EqPartial(attr
.GetOutline()))
11067 // Merges the given attributes. If compareWith
11068 // is non-NULL, then it will be used to mask out those attributes that are the same in style
11069 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
11070 bool wxTextBoxAttr::Apply(const wxTextBoxAttr
& attr
, const wxTextBoxAttr
* compareWith
)
11072 if (attr
.HasFloatMode())
11074 if (!(compareWith
&& compareWith
->HasFloatMode() && compareWith
->GetFloatMode() == attr
.GetFloatMode()))
11075 SetFloatMode(attr
.GetFloatMode());
11078 if (attr
.HasClearMode())
11080 if (!(compareWith
&& compareWith
->HasClearMode() && compareWith
->GetClearMode() == attr
.GetClearMode()))
11081 SetClearMode(attr
.GetClearMode());
11084 if (attr
.HasCollapseBorders())
11086 if (!(compareWith
&& compareWith
->HasCollapseBorders() && compareWith
->GetCollapseBorders() == attr
.GetCollapseBorders()))
11087 SetCollapseBorders(attr
.GetCollapseBorders());
11090 if (attr
.HasVerticalAlignment())
11092 if (!(compareWith
&& compareWith
->HasVerticalAlignment() && compareWith
->GetVerticalAlignment() == attr
.GetVerticalAlignment()))
11093 SetVerticalAlignment(attr
.GetVerticalAlignment());
11096 if (attr
.HasBoxStyleName())
11098 if (!(compareWith
&& compareWith
->HasBoxStyleName() && compareWith
->GetBoxStyleName() == attr
.GetBoxStyleName()))
11099 SetBoxStyleName(attr
.GetBoxStyleName());
11102 m_margins
.Apply(attr
.m_margins
, compareWith
? (& attr
.m_margins
) : (const wxTextAttrDimensions
*) NULL
);
11103 m_padding
.Apply(attr
.m_padding
, compareWith
? (& attr
.m_padding
) : (const wxTextAttrDimensions
*) NULL
);
11104 m_position
.Apply(attr
.m_position
, compareWith
? (& attr
.m_position
) : (const wxTextAttrDimensions
*) NULL
);
11106 m_size
.Apply(attr
.m_size
, compareWith
? (& attr
.m_size
) : (const wxTextAttrSize
*) NULL
);
11107 m_minSize
.Apply(attr
.m_minSize
, compareWith
? (& attr
.m_minSize
) : (const wxTextAttrSize
*) NULL
);
11108 m_maxSize
.Apply(attr
.m_maxSize
, compareWith
? (& attr
.m_maxSize
) : (const wxTextAttrSize
*) NULL
);
11110 m_border
.Apply(attr
.m_border
, compareWith
? (& attr
.m_border
) : (const wxTextAttrBorders
*) NULL
);
11111 m_outline
.Apply(attr
.m_outline
, compareWith
? (& attr
.m_outline
) : (const wxTextAttrBorders
*) NULL
);
11116 // Remove specified attributes from this object
11117 bool wxTextBoxAttr::RemoveStyle(const wxTextBoxAttr
& attr
)
11119 if (attr
.HasFloatMode())
11120 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT
);
11122 if (attr
.HasClearMode())
11123 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR
);
11125 if (attr
.HasCollapseBorders())
11126 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
11128 if (attr
.HasVerticalAlignment())
11129 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
11131 if (attr
.HasBoxStyleName())
11133 SetBoxStyleName(wxEmptyString
);
11134 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
11137 m_margins
.RemoveStyle(attr
.m_margins
);
11138 m_padding
.RemoveStyle(attr
.m_padding
);
11139 m_position
.RemoveStyle(attr
.m_position
);
11141 m_size
.RemoveStyle(attr
.m_size
);
11142 m_minSize
.RemoveStyle(attr
.m_minSize
);
11143 m_maxSize
.RemoveStyle(attr
.m_maxSize
);
11145 m_border
.RemoveStyle(attr
.m_border
);
11146 m_outline
.RemoveStyle(attr
.m_outline
);
11151 // Collects the attributes that are common to a range of content, building up a note of
11152 // which attributes are absent in some objects and which clash in some objects.
11153 void wxTextBoxAttr::CollectCommonAttributes(const wxTextBoxAttr
& attr
, wxTextBoxAttr
& clashingAttr
, wxTextBoxAttr
& absentAttr
)
11155 if (attr
.HasFloatMode())
11157 if (!clashingAttr
.HasFloatMode() && !absentAttr
.HasFloatMode())
11159 if (HasFloatMode())
11161 if (GetFloatMode() != attr
.GetFloatMode())
11163 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_FLOAT
);
11164 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT
);
11168 SetFloatMode(attr
.GetFloatMode());
11172 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_FLOAT
);
11174 if (attr
.HasClearMode())
11176 if (!clashingAttr
.HasClearMode() && !absentAttr
.HasClearMode())
11178 if (HasClearMode())
11180 if (GetClearMode() != attr
.GetClearMode())
11182 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_CLEAR
);
11183 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR
);
11187 SetClearMode(attr
.GetClearMode());
11191 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_CLEAR
);
11193 if (attr
.HasCollapseBorders())
11195 if (!clashingAttr
.HasCollapseBorders() && !absentAttr
.HasCollapseBorders())
11197 if (HasCollapseBorders())
11199 if (GetCollapseBorders() != attr
.GetCollapseBorders())
11201 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
11202 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
11206 SetCollapseBorders(attr
.GetCollapseBorders());
11210 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
11212 if (attr
.HasVerticalAlignment())
11214 if (!clashingAttr
.HasVerticalAlignment() && !absentAttr
.HasVerticalAlignment())
11216 if (HasVerticalAlignment())
11218 if (GetVerticalAlignment() != attr
.GetVerticalAlignment())
11220 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
11221 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
11225 SetVerticalAlignment(attr
.GetVerticalAlignment());
11229 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
11231 if (attr
.HasBoxStyleName())
11233 if (!clashingAttr
.HasBoxStyleName() && !absentAttr
.HasBoxStyleName())
11235 if (HasBoxStyleName())
11237 if (GetBoxStyleName() != attr
.GetBoxStyleName())
11239 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
11240 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
11244 SetBoxStyleName(attr
.GetBoxStyleName());
11248 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
11250 m_margins
.CollectCommonAttributes(attr
.m_margins
, clashingAttr
.m_margins
, absentAttr
.m_margins
);
11251 m_padding
.CollectCommonAttributes(attr
.m_padding
, clashingAttr
.m_padding
, absentAttr
.m_padding
);
11252 m_position
.CollectCommonAttributes(attr
.m_position
, clashingAttr
.m_position
, absentAttr
.m_position
);
11254 m_size
.CollectCommonAttributes(attr
.m_size
, clashingAttr
.m_size
, absentAttr
.m_size
);
11255 m_minSize
.CollectCommonAttributes(attr
.m_minSize
, clashingAttr
.m_minSize
, absentAttr
.m_minSize
);
11256 m_maxSize
.CollectCommonAttributes(attr
.m_maxSize
, clashingAttr
.m_maxSize
, absentAttr
.m_maxSize
);
11258 m_border
.CollectCommonAttributes(attr
.m_border
, clashingAttr
.m_border
, absentAttr
.m_border
);
11259 m_outline
.CollectCommonAttributes(attr
.m_outline
, clashingAttr
.m_outline
, absentAttr
.m_outline
);
11262 bool wxTextBoxAttr::IsDefault() const
11264 return GetFlags() == 0 && !m_border
.IsValid() && !m_outline
.IsValid() &&
11265 !m_size
.IsValid() && !m_minSize
.IsValid() && !m_maxSize
.IsValid() &&
11266 !m_position
.IsValid() && !m_padding
.IsValid() && !m_margins
.IsValid();
11271 void wxRichTextAttr::Copy(const wxRichTextAttr
& attr
)
11273 wxTextAttr::Copy(attr
);
11275 m_textBoxAttr
= attr
.m_textBoxAttr
;
11278 bool wxRichTextAttr::operator==(const wxRichTextAttr
& attr
) const
11280 if (!(wxTextAttr::operator==(attr
)))
11283 return (m_textBoxAttr
== attr
.m_textBoxAttr
);
11286 // Partial equality test taking comparison object into account
11287 bool wxRichTextAttr::EqPartial(const wxRichTextAttr
& attr
) const
11289 if (!(wxTextAttr::EqPartial(attr
)))
11292 return m_textBoxAttr
.EqPartial(attr
.m_textBoxAttr
);
11295 // Merges the given attributes. If compareWith
11296 // is non-NULL, then it will be used to mask out those attributes that are the same in style
11297 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
11298 bool wxRichTextAttr::Apply(const wxRichTextAttr
& style
, const wxRichTextAttr
* compareWith
)
11300 wxTextAttr::Apply(style
, compareWith
);
11302 return m_textBoxAttr
.Apply(style
.m_textBoxAttr
, compareWith
? (& compareWith
->m_textBoxAttr
) : (const wxTextBoxAttr
*) NULL
);
11305 // Remove specified attributes from this object
11306 bool wxRichTextAttr::RemoveStyle(const wxRichTextAttr
& attr
)
11308 wxTextAttr::RemoveStyle(*this, attr
);
11310 return m_textBoxAttr
.RemoveStyle(attr
.m_textBoxAttr
);
11313 // Collects the attributes that are common to a range of content, building up a note of
11314 // which attributes are absent in some objects and which clash in some objects.
11315 void wxRichTextAttr::CollectCommonAttributes(const wxRichTextAttr
& attr
, wxRichTextAttr
& clashingAttr
, wxRichTextAttr
& absentAttr
)
11317 wxTextAttrCollectCommonAttributes(*this, attr
, clashingAttr
, absentAttr
);
11319 m_textBoxAttr
.CollectCommonAttributes(attr
.m_textBoxAttr
, clashingAttr
.m_textBoxAttr
, absentAttr
.m_textBoxAttr
);
11322 // Partial equality test
11323 bool wxTextAttrBorder::EqPartial(const wxTextAttrBorder
& border
) const
11325 if (border
.HasStyle() && !HasStyle() && (border
.GetStyle() != GetStyle()))
11328 if (border
.HasColour() && !HasColour() && (border
.GetColourLong() != GetColourLong()))
11331 if (border
.HasWidth() && !HasWidth() && !(border
.GetWidth() == GetWidth()))
11337 // Apply border to 'this', but not if the same as compareWith
11338 bool wxTextAttrBorder::Apply(const wxTextAttrBorder
& border
, const wxTextAttrBorder
* compareWith
)
11340 if (border
.HasStyle())
11342 if (!(compareWith
&& (border
.GetStyle() == compareWith
->GetStyle())))
11343 SetStyle(border
.GetStyle());
11345 if (border
.HasColour())
11347 if (!(compareWith
&& (border
.GetColourLong() == compareWith
->GetColourLong())))
11348 SetColour(border
.GetColourLong());
11350 if (border
.HasWidth())
11352 if (!(compareWith
&& (border
.GetWidth() == compareWith
->GetWidth())))
11353 SetWidth(border
.GetWidth());
11359 // Remove specified attributes from this object
11360 bool wxTextAttrBorder::RemoveStyle(const wxTextAttrBorder
& attr
)
11362 if (attr
.HasStyle() && HasStyle())
11363 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_STYLE
);
11364 if (attr
.HasColour() && HasColour())
11365 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_COLOUR
);
11366 if (attr
.HasWidth() && HasWidth())
11367 m_borderWidth
.Reset();
11372 // Collects the attributes that are common to a range of content, building up a note of
11373 // which attributes are absent in some objects and which clash in some objects.
11374 void wxTextAttrBorder::CollectCommonAttributes(const wxTextAttrBorder
& attr
, wxTextAttrBorder
& clashingAttr
, wxTextAttrBorder
& absentAttr
)
11376 if (attr
.HasStyle())
11378 if (!clashingAttr
.HasStyle() && !absentAttr
.HasStyle())
11382 if (GetStyle() != attr
.GetStyle())
11384 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
11385 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
11389 SetStyle(attr
.GetStyle());
11393 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
11395 if (attr
.HasColour())
11397 if (!clashingAttr
.HasColour() && !absentAttr
.HasColour())
11401 if (GetColour() != attr
.GetColour())
11403 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
11404 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
11408 SetColour(attr
.GetColourLong());
11412 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
11414 m_borderWidth
.CollectCommonAttributes(attr
.m_borderWidth
, clashingAttr
.m_borderWidth
, absentAttr
.m_borderWidth
);
11417 // Partial equality test
11418 bool wxTextAttrBorders::EqPartial(const wxTextAttrBorders
& borders
) const
11420 return m_left
.EqPartial(borders
.m_left
) && m_right
.EqPartial(borders
.m_right
) &&
11421 m_top
.EqPartial(borders
.m_top
) && m_bottom
.EqPartial(borders
.m_bottom
);
11424 // Apply border to 'this', but not if the same as compareWith
11425 bool wxTextAttrBorders::Apply(const wxTextAttrBorders
& borders
, const wxTextAttrBorders
* compareWith
)
11427 m_left
.Apply(borders
.m_left
, compareWith
? (& compareWith
->m_left
) : (const wxTextAttrBorder
*) NULL
);
11428 m_right
.Apply(borders
.m_right
, compareWith
? (& compareWith
->m_right
) : (const wxTextAttrBorder
*) NULL
);
11429 m_top
.Apply(borders
.m_top
, compareWith
? (& compareWith
->m_top
) : (const wxTextAttrBorder
*) NULL
);
11430 m_bottom
.Apply(borders
.m_bottom
, compareWith
? (& compareWith
->m_bottom
) : (const wxTextAttrBorder
*) NULL
);
11434 // Remove specified attributes from this object
11435 bool wxTextAttrBorders::RemoveStyle(const wxTextAttrBorders
& attr
)
11437 m_left
.RemoveStyle(attr
.m_left
);
11438 m_right
.RemoveStyle(attr
.m_right
);
11439 m_top
.RemoveStyle(attr
.m_top
);
11440 m_bottom
.RemoveStyle(attr
.m_bottom
);
11444 // Collects the attributes that are common to a range of content, building up a note of
11445 // which attributes are absent in some objects and which clash in some objects.
11446 void wxTextAttrBorders::CollectCommonAttributes(const wxTextAttrBorders
& attr
, wxTextAttrBorders
& clashingAttr
, wxTextAttrBorders
& absentAttr
)
11448 m_left
.CollectCommonAttributes(attr
.m_left
, clashingAttr
.m_left
, absentAttr
.m_left
);
11449 m_right
.CollectCommonAttributes(attr
.m_right
, clashingAttr
.m_right
, absentAttr
.m_right
);
11450 m_top
.CollectCommonAttributes(attr
.m_top
, clashingAttr
.m_top
, absentAttr
.m_top
);
11451 m_bottom
.CollectCommonAttributes(attr
.m_bottom
, clashingAttr
.m_bottom
, absentAttr
.m_bottom
);
11454 // Set style of all borders
11455 void wxTextAttrBorders::SetStyle(int style
)
11457 m_left
.SetStyle(style
);
11458 m_right
.SetStyle(style
);
11459 m_top
.SetStyle(style
);
11460 m_bottom
.SetStyle(style
);
11463 // Set colour of all borders
11464 void wxTextAttrBorders::SetColour(unsigned long colour
)
11466 m_left
.SetColour(colour
);
11467 m_right
.SetColour(colour
);
11468 m_top
.SetColour(colour
);
11469 m_bottom
.SetColour(colour
);
11472 void wxTextAttrBorders::SetColour(const wxColour
& colour
)
11474 m_left
.SetColour(colour
);
11475 m_right
.SetColour(colour
);
11476 m_top
.SetColour(colour
);
11477 m_bottom
.SetColour(colour
);
11480 // Set width of all borders
11481 void wxTextAttrBorders::SetWidth(const wxTextAttrDimension
& width
)
11483 m_left
.SetWidth(width
);
11484 m_right
.SetWidth(width
);
11485 m_top
.SetWidth(width
);
11486 m_bottom
.SetWidth(width
);
11489 // Partial equality test
11490 bool wxTextAttrDimension::EqPartial(const wxTextAttrDimension
& dim
) const
11492 if (dim
.IsValid() && IsValid() && !((*this) == dim
))
11498 bool wxTextAttrDimension::Apply(const wxTextAttrDimension
& dim
, const wxTextAttrDimension
* compareWith
)
11502 if (!(compareWith
&& dim
== (*compareWith
)))
11509 // Collects the attributes that are common to a range of content, building up a note of
11510 // which attributes are absent in some objects and which clash in some objects.
11511 void wxTextAttrDimension::CollectCommonAttributes(const wxTextAttrDimension
& attr
, wxTextAttrDimension
& clashingAttr
, wxTextAttrDimension
& absentAttr
)
11513 if (attr
.IsValid())
11515 if (!clashingAttr
.IsValid() && !absentAttr
.IsValid())
11519 if (!((*this) == attr
))
11521 clashingAttr
.SetValid(true);
11530 absentAttr
.SetValid(true);
11533 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(wxDC
& dc
, double scale
, const wxSize
& parentSize
)
11535 m_ppi
= dc
.GetPPI().x
; m_scale
= scale
; m_parentSize
= parentSize
;
11538 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(int ppi
, double scale
, const wxSize
& parentSize
)
11540 m_ppi
= ppi
; m_scale
= scale
; m_parentSize
= parentSize
;
11543 int wxTextAttrDimensionConverter::ConvertTenthsMMToPixels(int units
) const
11545 return wxRichTextObject::ConvertTenthsMMToPixels(m_ppi
, units
, m_scale
);
11548 int wxTextAttrDimensionConverter::ConvertPixelsToTenthsMM(int pixels
) const
11550 return wxRichTextObject::ConvertPixelsToTenthsMM(m_ppi
, pixels
, m_scale
);
11553 int wxTextAttrDimensionConverter::GetPixels(const wxTextAttrDimension
& dim
, int direction
) const
11555 if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
11556 return ConvertTenthsMMToPixels(dim
.GetValue());
11557 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
11558 return dim
.GetValue();
11559 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
11561 wxASSERT(m_parentSize
!= wxDefaultSize
);
11562 if (direction
== wxHORIZONTAL
)
11563 return (int) (double(m_parentSize
.x
) * double(dim
.GetValue()) / 100.0);
11565 return (int) (double(m_parentSize
.y
) * double(dim
.GetValue()) / 100.0);
11574 int wxTextAttrDimensionConverter::GetTenthsMM(const wxTextAttrDimension
& dim
) const
11576 if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
11577 return dim
.GetValue();
11578 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
11579 return ConvertPixelsToTenthsMM(dim
.GetValue());
11587 // Partial equality test
11588 bool wxTextAttrDimensions::EqPartial(const wxTextAttrDimensions
& dims
) const
11590 if (!m_left
.EqPartial(dims
.m_left
))
11593 if (!m_right
.EqPartial(dims
.m_right
))
11596 if (!m_top
.EqPartial(dims
.m_top
))
11599 if (!m_bottom
.EqPartial(dims
.m_bottom
))
11605 // Apply border to 'this', but not if the same as compareWith
11606 bool wxTextAttrDimensions::Apply(const wxTextAttrDimensions
& dims
, const wxTextAttrDimensions
* compareWith
)
11608 m_left
.Apply(dims
.m_left
, compareWith
? (& compareWith
->m_left
) : (const wxTextAttrDimension
*) NULL
);
11609 m_right
.Apply(dims
.m_right
, compareWith
? (& compareWith
->m_right
): (const wxTextAttrDimension
*) NULL
);
11610 m_top
.Apply(dims
.m_top
, compareWith
? (& compareWith
->m_top
): (const wxTextAttrDimension
*) NULL
);
11611 m_bottom
.Apply(dims
.m_bottom
, compareWith
? (& compareWith
->m_bottom
): (const wxTextAttrDimension
*) NULL
);
11616 // Remove specified attributes from this object
11617 bool wxTextAttrDimensions::RemoveStyle(const wxTextAttrDimensions
& attr
)
11619 if (attr
.m_left
.IsValid())
11621 if (attr
.m_right
.IsValid())
11623 if (attr
.m_top
.IsValid())
11625 if (attr
.m_bottom
.IsValid())
11631 // Collects the attributes that are common to a range of content, building up a note of
11632 // which attributes are absent in some objects and which clash in some objects.
11633 void wxTextAttrDimensions::CollectCommonAttributes(const wxTextAttrDimensions
& attr
, wxTextAttrDimensions
& clashingAttr
, wxTextAttrDimensions
& absentAttr
)
11635 m_left
.CollectCommonAttributes(attr
.m_left
, clashingAttr
.m_left
, absentAttr
.m_left
);
11636 m_right
.CollectCommonAttributes(attr
.m_right
, clashingAttr
.m_right
, absentAttr
.m_right
);
11637 m_top
.CollectCommonAttributes(attr
.m_top
, clashingAttr
.m_top
, absentAttr
.m_top
);
11638 m_bottom
.CollectCommonAttributes(attr
.m_bottom
, clashingAttr
.m_bottom
, absentAttr
.m_bottom
);
11641 // Partial equality test
11642 bool wxTextAttrSize::EqPartial(const wxTextAttrSize
& size
) const
11644 if (!m_width
.EqPartial(size
.m_width
))
11647 if (!m_height
.EqPartial(size
.m_height
))
11653 // Apply border to 'this', but not if the same as compareWith
11654 bool wxTextAttrSize::Apply(const wxTextAttrSize
& size
, const wxTextAttrSize
* compareWith
)
11656 m_width
.Apply(size
.m_width
, compareWith
? (& compareWith
->m_width
) : (const wxTextAttrDimension
*) NULL
);
11657 m_height
.Apply(size
.m_height
, compareWith
? (& compareWith
->m_height
): (const wxTextAttrDimension
*) NULL
);
11662 // Remove specified attributes from this object
11663 bool wxTextAttrSize::RemoveStyle(const wxTextAttrSize
& attr
)
11665 if (attr
.m_width
.IsValid())
11667 if (attr
.m_height
.IsValid())
11673 // Collects the attributes that are common to a range of content, building up a note of
11674 // which attributes are absent in some objects and which clash in some objects.
11675 void wxTextAttrSize::CollectCommonAttributes(const wxTextAttrSize
& attr
, wxTextAttrSize
& clashingAttr
, wxTextAttrSize
& absentAttr
)
11677 m_width
.CollectCommonAttributes(attr
.m_width
, clashingAttr
.m_width
, absentAttr
.m_width
);
11678 m_height
.CollectCommonAttributes(attr
.m_height
, clashingAttr
.m_height
, absentAttr
.m_height
);
11681 // Collects the attributes that are common to a range of content, building up a note of
11682 // which attributes are absent in some objects and which clash in some objects.
11683 void wxTextAttrCollectCommonAttributes(wxTextAttr
& currentStyle
, const wxTextAttr
& attr
, wxTextAttr
& clashingAttr
, wxTextAttr
& absentAttr
)
11685 absentAttr
.SetFlags(absentAttr
.GetFlags() | (~attr
.GetFlags() & wxTEXT_ATTR_ALL
));
11686 absentAttr
.SetTextEffectFlags(absentAttr
.GetTextEffectFlags() | (~attr
.GetTextEffectFlags() & 0xFFFF));
11688 long forbiddenFlags
= clashingAttr
.GetFlags()|absentAttr
.GetFlags();
11690 if (attr
.HasFont())
11692 if (attr
.HasFontSize() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_SIZE
))
11694 if (currentStyle
.HasFontSize())
11696 if (currentStyle
.GetFontSize() != attr
.GetFontSize())
11698 // Clash of attr - mark as such
11699 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_SIZE
);
11700 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_SIZE
);
11704 currentStyle
.SetFontSize(attr
.GetFontSize());
11707 if (attr
.HasFontItalic() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_ITALIC
))
11709 if (currentStyle
.HasFontItalic())
11711 if (currentStyle
.GetFontStyle() != attr
.GetFontStyle())
11713 // Clash of attr - mark as such
11714 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_ITALIC
);
11715 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC
);
11719 currentStyle
.SetFontStyle(attr
.GetFontStyle());
11722 if (attr
.HasFontFamily() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_FAMILY
))
11724 if (currentStyle
.HasFontFamily())
11726 if (currentStyle
.GetFontFamily() != attr
.GetFontFamily())
11728 // Clash of attr - mark as such
11729 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FAMILY
);
11730 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY
);
11734 currentStyle
.SetFontFamily(attr
.GetFontFamily());
11737 if (attr
.HasFontWeight() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_WEIGHT
))
11739 if (currentStyle
.HasFontWeight())
11741 if (currentStyle
.GetFontWeight() != attr
.GetFontWeight())
11743 // Clash of attr - mark as such
11744 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_WEIGHT
);
11745 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT
);
11749 currentStyle
.SetFontWeight(attr
.GetFontWeight());
11752 if (attr
.HasFontFaceName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_FACE
))
11754 if (currentStyle
.HasFontFaceName())
11756 wxString
faceName1(currentStyle
.GetFontFaceName());
11757 wxString
faceName2(attr
.GetFontFaceName());
11759 if (faceName1
!= faceName2
)
11761 // Clash of attr - mark as such
11762 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FACE
);
11763 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FACE
);
11767 currentStyle
.SetFontFaceName(attr
.GetFontFaceName());
11770 if (attr
.HasFontUnderlined() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_UNDERLINE
))
11772 if (currentStyle
.HasFontUnderlined())
11774 if (currentStyle
.GetFontUnderlined() != attr
.GetFontUnderlined())
11776 // Clash of attr - mark as such
11777 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
11778 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
11782 currentStyle
.SetFontUnderlined(attr
.GetFontUnderlined());
11786 if (attr
.HasTextColour() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_TEXT_COLOUR
))
11788 if (currentStyle
.HasTextColour())
11790 if (currentStyle
.GetTextColour() != attr
.GetTextColour())
11792 // Clash of attr - mark as such
11793 clashingAttr
.AddFlag(wxTEXT_ATTR_TEXT_COLOUR
);
11794 currentStyle
.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR
);
11798 currentStyle
.SetTextColour(attr
.GetTextColour());
11801 if (attr
.HasBackgroundColour() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BACKGROUND_COLOUR
))
11803 if (currentStyle
.HasBackgroundColour())
11805 if (currentStyle
.GetBackgroundColour() != attr
.GetBackgroundColour())
11807 // Clash of attr - mark as such
11808 clashingAttr
.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
11809 currentStyle
.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
11813 currentStyle
.SetBackgroundColour(attr
.GetBackgroundColour());
11816 if (attr
.HasAlignment() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_ALIGNMENT
))
11818 if (currentStyle
.HasAlignment())
11820 if (currentStyle
.GetAlignment() != attr
.GetAlignment())
11822 // Clash of attr - mark as such
11823 clashingAttr
.AddFlag(wxTEXT_ATTR_ALIGNMENT
);
11824 currentStyle
.RemoveFlag(wxTEXT_ATTR_ALIGNMENT
);
11828 currentStyle
.SetAlignment(attr
.GetAlignment());
11831 if (attr
.HasTabs() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_TABS
))
11833 if (currentStyle
.HasTabs())
11835 if (!wxRichTextTabsEq(currentStyle
.GetTabs(), attr
.GetTabs()))
11837 // Clash of attr - mark as such
11838 clashingAttr
.AddFlag(wxTEXT_ATTR_TABS
);
11839 currentStyle
.RemoveFlag(wxTEXT_ATTR_TABS
);
11843 currentStyle
.SetTabs(attr
.GetTabs());
11846 if (attr
.HasLeftIndent() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LEFT_INDENT
))
11848 if (currentStyle
.HasLeftIndent())
11850 if (currentStyle
.GetLeftIndent() != attr
.GetLeftIndent() || currentStyle
.GetLeftSubIndent() != attr
.GetLeftSubIndent())
11852 // Clash of attr - mark as such
11853 clashingAttr
.AddFlag(wxTEXT_ATTR_LEFT_INDENT
);
11854 currentStyle
.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT
);
11858 currentStyle
.SetLeftIndent(attr
.GetLeftIndent(), attr
.GetLeftSubIndent());
11861 if (attr
.HasRightIndent() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_RIGHT_INDENT
))
11863 if (currentStyle
.HasRightIndent())
11865 if (currentStyle
.GetRightIndent() != attr
.GetRightIndent())
11867 // Clash of attr - mark as such
11868 clashingAttr
.AddFlag(wxTEXT_ATTR_RIGHT_INDENT
);
11869 currentStyle
.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT
);
11873 currentStyle
.SetRightIndent(attr
.GetRightIndent());
11876 if (attr
.HasParagraphSpacingAfter() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARA_SPACING_AFTER
))
11878 if (currentStyle
.HasParagraphSpacingAfter())
11880 if (currentStyle
.GetParagraphSpacingAfter() != attr
.GetParagraphSpacingAfter())
11882 // Clash of attr - mark as such
11883 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
11884 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
11888 currentStyle
.SetParagraphSpacingAfter(attr
.GetParagraphSpacingAfter());
11891 if (attr
.HasParagraphSpacingBefore() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARA_SPACING_BEFORE
))
11893 if (currentStyle
.HasParagraphSpacingBefore())
11895 if (currentStyle
.GetParagraphSpacingBefore() != attr
.GetParagraphSpacingBefore())
11897 // Clash of attr - mark as such
11898 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
11899 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
11903 currentStyle
.SetParagraphSpacingBefore(attr
.GetParagraphSpacingBefore());
11906 if (attr
.HasLineSpacing() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LINE_SPACING
))
11908 if (currentStyle
.HasLineSpacing())
11910 if (currentStyle
.GetLineSpacing() != attr
.GetLineSpacing())
11912 // Clash of attr - mark as such
11913 clashingAttr
.AddFlag(wxTEXT_ATTR_LINE_SPACING
);
11914 currentStyle
.RemoveFlag(wxTEXT_ATTR_LINE_SPACING
);
11918 currentStyle
.SetLineSpacing(attr
.GetLineSpacing());
11921 if (attr
.HasCharacterStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_CHARACTER_STYLE_NAME
))
11923 if (currentStyle
.HasCharacterStyleName())
11925 if (currentStyle
.GetCharacterStyleName() != attr
.GetCharacterStyleName())
11927 // Clash of attr - mark as such
11928 clashingAttr
.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
11929 currentStyle
.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
11933 currentStyle
.SetCharacterStyleName(attr
.GetCharacterStyleName());
11936 if (attr
.HasParagraphStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
))
11938 if (currentStyle
.HasParagraphStyleName())
11940 if (currentStyle
.GetParagraphStyleName() != attr
.GetParagraphStyleName())
11942 // Clash of attr - mark as such
11943 clashingAttr
.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
11944 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
11948 currentStyle
.SetParagraphStyleName(attr
.GetParagraphStyleName());
11951 if (attr
.HasListStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LIST_STYLE_NAME
))
11953 if (currentStyle
.HasListStyleName())
11955 if (currentStyle
.GetListStyleName() != attr
.GetListStyleName())
11957 // Clash of attr - mark as such
11958 clashingAttr
.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
11959 currentStyle
.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
11963 currentStyle
.SetListStyleName(attr
.GetListStyleName());
11966 if (attr
.HasBulletStyle() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_STYLE
))
11968 if (currentStyle
.HasBulletStyle())
11970 if (currentStyle
.GetBulletStyle() != attr
.GetBulletStyle())
11972 // Clash of attr - mark as such
11973 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_STYLE
);
11974 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE
);
11978 currentStyle
.SetBulletStyle(attr
.GetBulletStyle());
11981 if (attr
.HasBulletNumber() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_NUMBER
))
11983 if (currentStyle
.HasBulletNumber())
11985 if (currentStyle
.GetBulletNumber() != attr
.GetBulletNumber())
11987 // Clash of attr - mark as such
11988 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NUMBER
);
11989 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER
);
11993 currentStyle
.SetBulletNumber(attr
.GetBulletNumber());
11996 if (attr
.HasBulletText() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_TEXT
))
11998 if (currentStyle
.HasBulletText())
12000 if (currentStyle
.GetBulletText() != attr
.GetBulletText())
12002 // Clash of attr - mark as such
12003 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_TEXT
);
12004 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT
);
12009 currentStyle
.SetBulletText(attr
.GetBulletText());
12010 currentStyle
.SetBulletFont(attr
.GetBulletFont());
12014 if (attr
.HasBulletName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_NAME
))
12016 if (currentStyle
.HasBulletName())
12018 if (currentStyle
.GetBulletName() != attr
.GetBulletName())
12020 // Clash of attr - mark as such
12021 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NAME
);
12022 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NAME
);
12027 currentStyle
.SetBulletName(attr
.GetBulletName());
12031 if (attr
.HasURL() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_URL
))
12033 if (currentStyle
.HasURL())
12035 if (currentStyle
.GetURL() != attr
.GetURL())
12037 // Clash of attr - mark as such
12038 clashingAttr
.AddFlag(wxTEXT_ATTR_URL
);
12039 currentStyle
.RemoveFlag(wxTEXT_ATTR_URL
);
12044 currentStyle
.SetURL(attr
.GetURL());
12048 if (attr
.HasTextEffects() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_EFFECTS
))
12050 if (currentStyle
.HasTextEffects())
12052 // We need to find the bits in the new attr that are different:
12053 // just look at those bits that are specified by the new attr.
12055 // We need to remove the bits and flags that are not common between current attr
12056 // and new attr. In so doing we need to take account of the styles absent from one or more of the
12057 // previous styles.
12059 int currentRelevantTextEffects
= currentStyle
.GetTextEffects() & attr
.GetTextEffectFlags();
12060 int newRelevantTextEffects
= attr
.GetTextEffects() & attr
.GetTextEffectFlags();
12062 if (currentRelevantTextEffects
!= newRelevantTextEffects
)
12064 // Find the text effects that were different, using XOR
12065 int differentEffects
= currentRelevantTextEffects
^ newRelevantTextEffects
;
12067 // Clash of attr - mark as such
12068 clashingAttr
.SetTextEffectFlags(clashingAttr
.GetTextEffectFlags() | differentEffects
);
12069 currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~differentEffects
);
12074 currentStyle
.SetTextEffects(attr
.GetTextEffects());
12075 currentStyle
.SetTextEffectFlags(attr
.GetTextEffectFlags());
12078 // Mask out the flags and values that cannot be common because they were absent in one or more objecrs
12079 // that we've looked at so far
12080 currentStyle
.SetTextEffects(currentStyle
.GetTextEffects() & ~absentAttr
.GetTextEffectFlags());
12081 currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~absentAttr
.GetTextEffectFlags());
12083 if (currentStyle
.GetTextEffectFlags() == 0)
12084 currentStyle
.RemoveFlag(wxTEXT_ATTR_EFFECTS
);
12087 if (attr
.HasOutlineLevel() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_OUTLINE_LEVEL
))
12089 if (currentStyle
.HasOutlineLevel())
12091 if (currentStyle
.GetOutlineLevel() != attr
.GetOutlineLevel())
12093 // Clash of attr - mark as such
12094 clashingAttr
.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
12095 currentStyle
.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
12099 currentStyle
.SetOutlineLevel(attr
.GetOutlineLevel());
12103 WX_DEFINE_OBJARRAY(wxRichTextVariantArray
);
12105 IMPLEMENT_DYNAMIC_CLASS(wxRichTextProperties
, wxObject
)
12107 bool wxRichTextProperties::operator==(const wxRichTextProperties
& props
) const
12109 if (m_properties
.GetCount() != props
.GetCount())
12113 for (i
= 0; i
< m_properties
.GetCount(); i
++)
12115 const wxVariant
& var1
= m_properties
[i
];
12116 int idx
= props
.Find(var1
.GetName());
12119 const wxVariant
& var2
= props
.m_properties
[idx
];
12120 if (!(var1
== var2
))
12127 wxArrayString
wxRichTextProperties::GetPropertyNames() const
12131 for (i
= 0; i
< m_properties
.GetCount(); i
++)
12133 arr
.Add(m_properties
[i
].GetName());
12138 int wxRichTextProperties::Find(const wxString
& name
) const
12141 for (i
= 0; i
< m_properties
.GetCount(); i
++)
12143 if (m_properties
[i
].GetName() == name
)
12149 bool wxRichTextProperties::Remove(const wxString
& name
)
12151 int idx
= Find(name
);
12154 m_properties
.RemoveAt(idx
);
12161 wxVariant
* wxRichTextProperties::FindOrCreateProperty(const wxString
& name
)
12163 int idx
= Find(name
);
12164 if (idx
== wxNOT_FOUND
)
12165 SetProperty(name
, wxString());
12167 if (idx
!= wxNOT_FOUND
)
12169 return & (*this)[idx
];
12175 const wxVariant
& wxRichTextProperties::GetProperty(const wxString
& name
) const
12177 static const wxVariant nullVariant
;
12178 int idx
= Find(name
);
12180 return m_properties
[idx
];
12182 return nullVariant
;
12185 wxString
wxRichTextProperties::GetPropertyString(const wxString
& name
) const
12187 return GetProperty(name
).GetString();
12190 long wxRichTextProperties::GetPropertyLong(const wxString
& name
) const
12192 return GetProperty(name
).GetLong();
12195 bool wxRichTextProperties::GetPropertyBool(const wxString
& name
) const
12197 return GetProperty(name
).GetBool();
12200 double wxRichTextProperties::GetPropertyDouble(const wxString
& name
) const
12202 return GetProperty(name
).GetDouble();
12205 void wxRichTextProperties::SetProperty(const wxVariant
& variant
)
12207 wxASSERT(!variant
.GetName().IsEmpty());
12209 int idx
= Find(variant
.GetName());
12212 m_properties
.Add(variant
);
12214 m_properties
[idx
] = variant
;
12217 void wxRichTextProperties::SetProperty(const wxString
& name
, const wxVariant
& variant
)
12219 int idx
= Find(name
);
12220 wxVariant
var(variant
);
12224 m_properties
.Add(var
);
12226 m_properties
[idx
] = var
;
12229 void wxRichTextProperties::SetProperty(const wxString
& name
, const wxString
& value
)
12231 SetProperty(name
, wxVariant(value
, name
));
12234 void wxRichTextProperties::SetProperty(const wxString
& name
, long value
)
12236 SetProperty(name
, wxVariant(value
, name
));
12239 void wxRichTextProperties::SetProperty(const wxString
& name
, double value
)
12241 SetProperty(name
, wxVariant(value
, name
));
12244 void wxRichTextProperties::SetProperty(const wxString
& name
, bool value
)
12246 SetProperty(name
, wxVariant(value
, name
));
12249 void wxRichTextProperties::RemoveProperties(const wxRichTextProperties
& properties
)
12252 for (i
= 0; i
< properties
.GetCount(); i
++)
12254 wxString name
= properties
.GetProperties()[i
].GetName();
12255 if (HasProperty(name
))
12260 void wxRichTextProperties::MergeProperties(const wxRichTextProperties
& properties
)
12263 for (i
= 0; i
< properties
.GetCount(); i
++)
12265 SetProperty(properties
.GetProperties()[i
]);
12269 wxRichTextObject
* wxRichTextObjectAddress::GetObject(wxRichTextParagraphLayoutBox
* topLevelContainer
) const
12271 if (m_address
.GetCount() == 0)
12272 return topLevelContainer
;
12274 wxRichTextCompositeObject
* p
= topLevelContainer
;
12276 while (p
&& i
< m_address
.GetCount())
12278 int pos
= m_address
[i
];
12279 wxASSERT(pos
>= 0 && pos
< (int) p
->GetChildren().GetCount());
12280 if (pos
< 0 || pos
>= (int) p
->GetChildren().GetCount())
12283 wxRichTextObject
* p1
= p
->GetChild(pos
);
12284 if (i
== (m_address
.GetCount()-1))
12287 p
= wxDynamicCast(p1
, wxRichTextCompositeObject
);
12293 bool wxRichTextObjectAddress::Create(wxRichTextParagraphLayoutBox
* topLevelContainer
, wxRichTextObject
* obj
)
12297 if (topLevelContainer
== obj
)
12300 wxRichTextObject
* o
= obj
;
12303 wxRichTextCompositeObject
* p
= wxDynamicCast(o
->GetParent(), wxRichTextCompositeObject
);
12307 int pos
= p
->GetChildren().IndexOf(o
);
12311 m_address
.Insert(pos
, 0);
12313 if (p
== topLevelContainer
)
12322 bool wxRichTextSelection::operator==(const wxRichTextSelection
& sel
) const
12324 if (m_container
!= sel
.m_container
)
12326 if (m_ranges
.GetCount() != sel
.m_ranges
.GetCount())
12329 for (i
= 0; i
< m_ranges
.GetCount(); i
++)
12330 if (!(m_ranges
[i
] == sel
.m_ranges
[i
]))
12335 // Get the selections appropriate to the specified object, if any; returns wxRICHTEXT_NO_SELECTION if none
12336 // or none at the level of the object's container.
12337 wxRichTextRangeArray
wxRichTextSelection::GetSelectionForObject(wxRichTextObject
* obj
) const
12341 wxRichTextParagraphLayoutBox
* container
= obj
->GetParentContainer();
12343 if (container
== m_container
)
12346 container
= obj
->GetContainer();
12349 if (container
->GetParent())
12351 // If we found that our object's container is within the range of
12352 // a selection higher up, then assume the whole original object
12353 // is also selected.
12354 wxRichTextParagraphLayoutBox
* parentContainer
= container
->GetParentContainer();
12355 if (parentContainer
== m_container
)
12357 if (WithinSelection(container
->GetRange().GetStart(), m_ranges
))
12359 wxRichTextRangeArray ranges
;
12360 ranges
.Add(obj
->GetRange());
12365 container
= parentContainer
;
12374 return wxRichTextRangeArray();
12377 // Is the given position within the selection?
12378 bool wxRichTextSelection::WithinSelection(long pos
, wxRichTextObject
* obj
) const
12384 wxRichTextRangeArray selectionRanges
= GetSelectionForObject(obj
);
12385 return WithinSelection(pos
, selectionRanges
);
12389 // Is the given position within the selection range?
12390 bool wxRichTextSelection::WithinSelection(long pos
, const wxRichTextRangeArray
& ranges
)
12393 for (i
= 0; i
< ranges
.GetCount(); i
++)
12395 const wxRichTextRange
& range
= ranges
[i
];
12396 if (pos
>= range
.GetStart() && pos
<= range
.GetEnd())
12402 // Is the given range completely within the selection range?
12403 bool wxRichTextSelection::WithinSelection(const wxRichTextRange
& range
, const wxRichTextRangeArray
& ranges
)
12406 for (i
= 0; i
< ranges
.GetCount(); i
++)
12408 const wxRichTextRange
& eachRange
= ranges
[i
];
12409 if (range
.IsWithin(eachRange
))
12415 IMPLEMENT_CLASS(wxRichTextDrawingHandler
, wxObject
)
12416 IMPLEMENT_CLASS(wxRichTextDrawingContext
, wxObject
)
12418 bool wxRichTextDrawingContext::HasVirtualAttributes(wxRichTextObject
* obj
) const
12420 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
12423 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
12424 if (handler
->HasVirtualAttributes(obj
))
12427 node
= node
->GetNext();
12432 wxRichTextAttr
wxRichTextDrawingContext::GetVirtualAttributes(wxRichTextObject
* obj
) const
12434 wxRichTextAttr attr
;
12435 // We apply all handlers, so we can may combine several different attributes
12436 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
12439 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
12440 if (handler
->HasVirtualAttributes(obj
))
12442 bool success
= handler
->GetVirtualAttributes(attr
, obj
);
12446 node
= node
->GetNext();
12451 bool wxRichTextDrawingContext::ApplyVirtualAttributes(wxRichTextAttr
& attr
, wxRichTextObject
* obj
) const
12453 if (HasVirtualAttributes(obj
))
12455 wxRichTextAttr
a(GetVirtualAttributes(obj
));
12463 /// Adds a handler to the end
12464 void wxRichTextBuffer::AddDrawingHandler(wxRichTextDrawingHandler
*handler
)
12466 sm_drawingHandlers
.Append(handler
);
12469 /// Inserts a handler at the front
12470 void wxRichTextBuffer::InsertDrawingHandler(wxRichTextDrawingHandler
*handler
)
12472 sm_drawingHandlers
.Insert( handler
);
12475 /// Removes a handler
12476 bool wxRichTextBuffer::RemoveDrawingHandler(const wxString
& name
)
12478 wxRichTextDrawingHandler
*handler
= FindDrawingHandler(name
);
12481 sm_drawingHandlers
.DeleteObject(handler
);
12489 wxRichTextDrawingHandler
* wxRichTextBuffer::FindDrawingHandler(const wxString
& name
)
12491 wxList::compatibility_iterator node
= sm_drawingHandlers
.GetFirst();
12494 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
12495 if (handler
->GetName().Lower() == name
.Lower()) return handler
;
12497 node
= node
->GetNext();
12502 void wxRichTextBuffer::CleanUpDrawingHandlers()
12504 wxList::compatibility_iterator node
= sm_drawingHandlers
.GetFirst();
12507 wxRichTextDrawingHandler
* handler
= (wxRichTextDrawingHandler
*)node
->GetData();
12508 wxList::compatibility_iterator next
= node
->GetNext();
12513 sm_drawingHandlers
.Clear();