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 /// Set character or paragraph properties
3528 bool wxRichTextParagraphLayoutBox::SetProperties(const wxRichTextRange
& range
, const wxRichTextProperties
& properties
, int flags
)
3530 wxRichTextBuffer
* buffer
= GetBuffer();
3532 bool withUndo
= ((flags
& wxRICHTEXT_SETPROPERTIES_WITH_UNDO
) != 0);
3533 bool parasOnly
= ((flags
& wxRICHTEXT_SETPROPERTIES_PARAGRAPHS_ONLY
) != 0);
3534 bool charactersOnly
= ((flags
& wxRICHTEXT_SETPROPERTIES_CHARACTERS_ONLY
) != 0);
3535 bool resetExistingProperties
= ((flags
& wxRICHTEXT_SETPROPERTIES_RESET
) != 0);
3536 bool removeProperties
= ((flags
& wxRICHTEXT_SETPROPERTIES_REMOVE
) != 0);
3538 // If we are associated with a control, make undoable; otherwise, apply immediately
3541 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3543 wxRichTextAction
* action
= NULL
;
3545 if (haveControl
&& withUndo
)
3547 action
= new wxRichTextAction(NULL
, _("Change Properties"), wxRICHTEXT_CHANGE_PROPERTIES
, buffer
, this, buffer
->GetRichTextCtrl());
3548 action
->SetRange(range
);
3549 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3552 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3555 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3556 // wxASSERT (para != NULL);
3558 if (para
&& para
->GetChildCount() > 0)
3560 // Stop searching if we're beyond the range of interest
3561 if (para
->GetRange().GetStart() > range
.GetEnd())
3564 if (!para
->GetRange().IsOutside(range
))
3566 // We'll be using a copy of the paragraph to make style changes,
3567 // not updating the buffer directly.
3568 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3570 if (haveControl
&& withUndo
)
3572 newPara
= new wxRichTextParagraph(*para
);
3573 action
->GetNewParagraphs().AppendChild(newPara
);
3575 // Also store the old ones for Undo
3576 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3583 if (removeProperties
)
3585 // Removes the given style from the paragraph
3587 newPara
->GetProperties().RemoveProperties(properties
);
3589 else if (resetExistingProperties
)
3590 newPara
->GetProperties() = properties
;
3592 newPara
->GetProperties().MergeProperties(properties
);
3595 // When applying paragraph styles dynamically, don't change the text objects' attributes
3596 // since they will computed as needed. Only apply the character styling if it's _only_
3597 // character styling. This policy is subject to change and might be put under user control.
3599 // Hm. we might well be applying a mix of paragraph and character styles, in which
3600 // case we _do_ want to apply character styles regardless of what para styles are set.
3601 // But if we're applying a paragraph style, which has some character attributes, but
3602 // we only want the paragraphs to hold this character style, then we _don't_ want to
3603 // apply the character style. So we need to be able to choose.
3605 if (!parasOnly
&& charactersOnly
&& range
.GetStart() != newPara
->GetRange().GetEnd())
3607 wxRichTextRange
childRange(range
);
3608 childRange
.LimitTo(newPara
->GetRange());
3610 // Find the starting position and if necessary split it so
3611 // we can start applying different properties.
3612 // TODO: check that the properties actually change or are different
3613 // from properties outside of range
3614 wxRichTextObject
* firstObject
wxDUMMY_INITIALIZE(NULL
);
3615 wxRichTextObject
* lastObject
wxDUMMY_INITIALIZE(NULL
);
3617 if (childRange
.GetStart() == newPara
->GetRange().GetStart())
3618 firstObject
= newPara
->GetChildren().GetFirst()->GetData();
3620 firstObject
= newPara
->SplitAt(range
.GetStart());
3622 // Increment by 1 because we're apply the style one _after_ the split point
3623 long splitPoint
= childRange
.GetEnd();
3624 if (splitPoint
!= newPara
->GetRange().GetEnd())
3628 if (splitPoint
== newPara
->GetRange().GetEnd())
3629 lastObject
= newPara
->GetChildren().GetLast()->GetData();
3631 // lastObject is set as a side-effect of splitting. It's
3632 // returned as the object before the new object.
3633 (void) newPara
->SplitAt(splitPoint
, & lastObject
);
3635 wxASSERT(firstObject
!= NULL
);
3636 wxASSERT(lastObject
!= NULL
);
3638 if (!firstObject
|| !lastObject
)
3641 wxRichTextObjectList::compatibility_iterator firstNode
= newPara
->GetChildren().Find(firstObject
);
3642 wxRichTextObjectList::compatibility_iterator lastNode
= newPara
->GetChildren().Find(lastObject
);
3644 wxASSERT(firstNode
);
3647 wxRichTextObjectList::compatibility_iterator node2
= firstNode
;
3651 wxRichTextObject
* child
= node2
->GetData();
3653 if (removeProperties
)
3655 // Removes the given properties from the paragraph
3656 child
->GetProperties().RemoveProperties(properties
);
3658 else if (resetExistingProperties
)
3659 child
->GetProperties() = properties
;
3662 child
->GetProperties().MergeProperties(properties
);
3665 if (node2
== lastNode
)
3668 node2
= node2
->GetNext();
3674 node
= node
->GetNext();
3677 // Do action, or delay it until end of batch.
3678 if (haveControl
&& withUndo
)
3679 buffer
->SubmitAction(action
);
3684 void wxRichTextParagraphLayoutBox::Reset()
3688 wxRichTextBuffer
* buffer
= GetBuffer();
3689 if (buffer
&& buffer
->GetRichTextCtrl())
3691 wxRichTextEvent
event(wxEVT_COMMAND_RICHTEXT_BUFFER_RESET
, buffer
->GetRichTextCtrl()->GetId());
3692 event
.SetEventObject(buffer
->GetRichTextCtrl());
3693 event
.SetContainer(this);
3695 buffer
->SendEvent(event
, true);
3698 AddParagraph(wxEmptyString
);
3700 InvalidateHierarchy(wxRICHTEXT_ALL
);
3703 /// Invalidate the buffer. With no argument, invalidates whole buffer.
3704 void wxRichTextParagraphLayoutBox::Invalidate(const wxRichTextRange
& invalidRange
)
3706 wxRichTextCompositeObject::Invalidate(invalidRange
);
3708 DoInvalidate(invalidRange
);
3711 // Do the (in)validation for this object only
3712 void wxRichTextParagraphLayoutBox::DoInvalidate(const wxRichTextRange
& invalidRange
)
3714 if (invalidRange
== wxRICHTEXT_ALL
)
3716 m_invalidRange
= wxRICHTEXT_ALL
;
3718 // Already invalidating everything
3719 else if (m_invalidRange
== wxRICHTEXT_ALL
)
3724 if ((invalidRange
.GetStart() < m_invalidRange
.GetStart()) || m_invalidRange
.GetStart() == -1)
3725 m_invalidRange
.SetStart(invalidRange
.GetStart());
3726 if (invalidRange
.GetEnd() > m_invalidRange
.GetEnd())
3727 m_invalidRange
.SetEnd(invalidRange
.GetEnd());
3731 // Do the (in)validation both up and down the hierarchy
3732 void wxRichTextParagraphLayoutBox::InvalidateHierarchy(const wxRichTextRange
& invalidRange
)
3734 Invalidate(invalidRange
);
3736 if (invalidRange
!= wxRICHTEXT_NONE
)
3738 // Now go up the hierarchy
3739 wxRichTextObject
* thisObj
= this;
3740 wxRichTextObject
* p
= GetParent();
3743 wxRichTextParagraphLayoutBox
* l
= wxDynamicCast(p
, wxRichTextParagraphLayoutBox
);
3745 l
->DoInvalidate(thisObj
->GetRange());
3753 /// Get invalid range, rounding to entire paragraphs if argument is true.
3754 wxRichTextRange
wxRichTextParagraphLayoutBox::GetInvalidRange(bool wholeParagraphs
) const
3756 if (m_invalidRange
== wxRICHTEXT_ALL
|| m_invalidRange
== wxRICHTEXT_NONE
)
3757 return m_invalidRange
;
3759 wxRichTextRange range
= m_invalidRange
;
3761 if (wholeParagraphs
)
3763 wxRichTextParagraph
* para1
= GetParagraphAtPosition(range
.GetStart());
3765 range
.SetStart(para1
->GetRange().GetStart());
3766 // floating layout make all child should be relayout
3767 range
.SetEnd(GetOwnRange().GetEnd());
3772 /// Apply the style sheet to the buffer, for example if the styles have changed.
3773 bool wxRichTextParagraphLayoutBox::ApplyStyleSheet(wxRichTextStyleSheet
* styleSheet
)
3775 wxASSERT(styleSheet
!= NULL
);
3781 wxRichTextAttr
attr(GetBasicStyle());
3782 if (GetBasicStyle().HasParagraphStyleName())
3784 wxRichTextParagraphStyleDefinition
* paraDef
= styleSheet
->FindParagraphStyle(GetBasicStyle().GetParagraphStyleName());
3787 attr
.Apply(paraDef
->GetStyleMergedWithBase(styleSheet
));
3788 SetBasicStyle(attr
);
3793 if (GetBasicStyle().HasCharacterStyleName())
3795 wxRichTextCharacterStyleDefinition
* charDef
= styleSheet
->FindCharacterStyle(GetBasicStyle().GetCharacterStyleName());
3798 attr
.Apply(charDef
->GetStyleMergedWithBase(styleSheet
));
3799 SetBasicStyle(attr
);
3804 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3807 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3808 // wxASSERT (para != NULL);
3812 // Combine paragraph and list styles. If there is a list style in the original attributes,
3813 // the current indentation overrides anything else and is used to find the item indentation.
3814 // Also, for applying paragraph styles, consider having 2 modes: (1) we merge with what we have,
3815 // thereby taking into account all user changes, (2) reset the style completely (except for indentation/list
3816 // exception as above).
3817 // Problem: when changing from one list style to another, there's a danger that the level info will get lost.
3818 // So when changing a list style interactively, could retrieve level based on current style, then
3819 // set appropriate indent and apply new style.
3823 if (para
->GetAttributes().HasOutlineLevel())
3824 outline
= para
->GetAttributes().GetOutlineLevel();
3825 if (para
->GetAttributes().HasBulletNumber())
3826 num
= para
->GetAttributes().GetBulletNumber();
3828 if (!para
->GetAttributes().GetParagraphStyleName().IsEmpty() && !para
->GetAttributes().GetListStyleName().IsEmpty())
3830 int currentIndent
= para
->GetAttributes().GetLeftIndent();
3832 wxRichTextParagraphStyleDefinition
* paraDef
= styleSheet
->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
3833 wxRichTextListStyleDefinition
* listDef
= styleSheet
->FindListStyle(para
->GetAttributes().GetListStyleName());
3834 if (paraDef
&& !listDef
)
3836 para
->GetAttributes() = paraDef
->GetStyleMergedWithBase(styleSheet
);
3839 else if (listDef
&& !paraDef
)
3841 // Set overall style defined for the list style definition
3842 para
->GetAttributes() = listDef
->GetStyleMergedWithBase(styleSheet
);
3844 // Apply the style for this level
3845 wxRichTextApplyStyle(para
->GetAttributes(), * listDef
->GetLevelAttributes(listDef
->FindLevelForIndent(currentIndent
)));
3848 else if (listDef
&& paraDef
)
3850 // Combines overall list style, style for level, and paragraph style
3851 para
->GetAttributes() = listDef
->CombineWithParagraphStyle(currentIndent
, paraDef
->GetStyleMergedWithBase(styleSheet
));
3855 else if (para
->GetAttributes().GetParagraphStyleName().IsEmpty() && !para
->GetAttributes().GetListStyleName().IsEmpty())
3857 int currentIndent
= para
->GetAttributes().GetLeftIndent();
3859 wxRichTextListStyleDefinition
* listDef
= styleSheet
->FindListStyle(para
->GetAttributes().GetListStyleName());
3861 // Overall list definition style
3862 para
->GetAttributes() = listDef
->GetStyleMergedWithBase(styleSheet
);
3864 // Style for this level
3865 wxRichTextApplyStyle(para
->GetAttributes(), * listDef
->GetLevelAttributes(listDef
->FindLevelForIndent(currentIndent
)));
3869 else if (!para
->GetAttributes().GetParagraphStyleName().IsEmpty() && para
->GetAttributes().GetListStyleName().IsEmpty())
3871 wxRichTextParagraphStyleDefinition
* def
= styleSheet
->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
3874 para
->GetAttributes() = def
->GetStyleMergedWithBase(styleSheet
);
3880 para
->GetAttributes().SetOutlineLevel(outline
);
3882 para
->GetAttributes().SetBulletNumber(num
);
3885 node
= node
->GetNext();
3887 return foundCount
!= 0;
3891 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
3893 wxRichTextBuffer
* buffer
= GetBuffer();
3894 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
3896 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
3897 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
3898 bool specifyLevel
= ((flags
& wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL
) != 0);
3899 bool renumber
= ((flags
& wxRICHTEXT_SETSTYLE_RENUMBER
) != 0);
3901 // Current number, if numbering
3904 wxASSERT (!specifyLevel
|| (specifyLevel
&& (specifiedLevel
>= 0)));
3906 // If we are associated with a control, make undoable; otherwise, apply immediately
3909 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3911 wxRichTextAction
* action
= NULL
;
3913 if (haveControl
&& withUndo
)
3915 action
= new wxRichTextAction(NULL
, _("Change List Style"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
3916 action
->SetRange(range
);
3917 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3920 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3923 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3924 // wxASSERT (para != NULL);
3926 if (para
&& para
->GetChildCount() > 0)
3928 // Stop searching if we're beyond the range of interest
3929 if (para
->GetRange().GetStart() > range
.GetEnd())
3932 if (!para
->GetRange().IsOutside(range
))
3934 // We'll be using a copy of the paragraph to make style changes,
3935 // not updating the buffer directly.
3936 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3938 if (haveControl
&& withUndo
)
3940 newPara
= new wxRichTextParagraph(*para
);
3941 action
->GetNewParagraphs().AppendChild(newPara
);
3943 // Also store the old ones for Undo
3944 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3951 int thisIndent
= newPara
->GetAttributes().GetLeftIndent();
3952 int thisLevel
= specifyLevel
? specifiedLevel
: def
->FindLevelForIndent(thisIndent
);
3954 // How is numbering going to work?
3955 // If we are renumbering, or numbering for the first time, we need to keep
3956 // track of the number for each level. But we might be simply applying a different
3958 // In Word, applying a style to several paragraphs, even if at different levels,
3959 // reverts the level back to the same one. So we could do the same here.
3960 // Renumbering will need to be done when we promote/demote a paragraph.
3962 // Apply the overall list style, and item style for this level
3963 wxRichTextAttr
listStyle(def
->GetCombinedStyleForLevel(thisLevel
, styleSheet
));
3964 wxRichTextApplyStyle(newPara
->GetAttributes(), listStyle
);
3966 // Now we need to do numbering
3969 newPara
->GetAttributes().SetBulletNumber(n
);
3974 else if (!newPara
->GetAttributes().GetListStyleName().IsEmpty())
3976 // if def is NULL, remove list style, applying any associated paragraph style
3977 // to restore the attributes
3979 newPara
->GetAttributes().SetListStyleName(wxEmptyString
);
3980 newPara
->GetAttributes().SetLeftIndent(0, 0);
3981 newPara
->GetAttributes().SetBulletText(wxEmptyString
);
3983 // Eliminate the main list-related attributes
3984 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
);
3986 if (styleSheet
&& !newPara
->GetAttributes().GetParagraphStyleName().IsEmpty())
3988 wxRichTextParagraphStyleDefinition
* def
= styleSheet
->FindParagraphStyle(newPara
->GetAttributes().GetParagraphStyleName());
3991 newPara
->GetAttributes() = def
->GetStyleMergedWithBase(styleSheet
);
3998 node
= node
->GetNext();
4001 // Do action, or delay it until end of batch.
4002 if (haveControl
&& withUndo
)
4003 buffer
->SubmitAction(action
);
4008 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
)
4010 wxRichTextBuffer
* buffer
= GetBuffer();
4011 if (buffer
&& buffer
->GetStyleSheet())
4013 wxRichTextListStyleDefinition
* def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4015 return SetListStyle(range
, def
, flags
, startFrom
, specifiedLevel
);
4020 /// Clear list for given range
4021 bool wxRichTextParagraphLayoutBox::ClearListStyle(const wxRichTextRange
& range
, int flags
)
4023 return SetListStyle(range
, NULL
, flags
);
4026 /// Number/renumber any list elements in the given range
4027 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
4029 return DoNumberList(range
, range
, 0, def
, flags
, startFrom
, specifiedLevel
);
4032 /// Number/renumber any list elements in the given range. Also do promotion or demotion of items, if specified
4033 bool wxRichTextParagraphLayoutBox::DoNumberList(const wxRichTextRange
& range
, const wxRichTextRange
& promotionRange
, int promoteBy
,
4034 wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
4036 wxRichTextBuffer
* buffer
= GetBuffer();
4037 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
4039 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
4040 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
4042 bool specifyLevel
= ((flags
& wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL
) != 0);
4045 bool renumber
= ((flags
& wxRICHTEXT_SETSTYLE_RENUMBER
) != 0);
4047 // Max number of levels
4048 const int maxLevels
= 10;
4050 // The level we're looking at now
4051 int currentLevel
= -1;
4053 // The item number for each level
4054 int levels
[maxLevels
];
4057 // Reset all numbering
4058 for (i
= 0; i
< maxLevels
; i
++)
4060 if (startFrom
!= -1)
4061 levels
[i
] = startFrom
-1;
4062 else if (renumber
) // start again
4065 levels
[i
] = -1; // start from the number we found, if any
4069 wxASSERT(!specifyLevel
|| (specifyLevel
&& (specifiedLevel
>= 0)));
4072 // If we are associated with a control, make undoable; otherwise, apply immediately
4075 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
4077 wxRichTextAction
* action
= NULL
;
4079 if (haveControl
&& withUndo
)
4081 action
= new wxRichTextAction(NULL
, _("Renumber List"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
4082 action
->SetRange(range
);
4083 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
4086 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
4089 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
4090 // wxASSERT (para != NULL);
4092 if (para
&& para
->GetChildCount() > 0)
4094 // Stop searching if we're beyond the range of interest
4095 if (para
->GetRange().GetStart() > range
.GetEnd())
4098 if (!para
->GetRange().IsOutside(range
))
4100 // We'll be using a copy of the paragraph to make style changes,
4101 // not updating the buffer directly.
4102 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
4104 if (haveControl
&& withUndo
)
4106 newPara
= new wxRichTextParagraph(*para
);
4107 action
->GetNewParagraphs().AppendChild(newPara
);
4109 // Also store the old ones for Undo
4110 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
4115 wxRichTextListStyleDefinition
* defToUse
= def
;
4118 if (styleSheet
&& !newPara
->GetAttributes().GetListStyleName().IsEmpty())
4119 defToUse
= styleSheet
->FindListStyle(newPara
->GetAttributes().GetListStyleName());
4124 int thisIndent
= newPara
->GetAttributes().GetLeftIndent();
4125 int thisLevel
= defToUse
->FindLevelForIndent(thisIndent
);
4127 // If we've specified a level to apply to all, change the level.
4128 if (specifiedLevel
!= -1)
4129 thisLevel
= specifiedLevel
;
4131 // Do promotion if specified
4132 if ((promoteBy
!= 0) && !para
->GetRange().IsOutside(promotionRange
))
4134 thisLevel
= thisLevel
- promoteBy
;
4141 // Apply the overall list style, and item style for this level
4142 wxRichTextAttr
listStyle(defToUse
->GetCombinedStyleForLevel(thisLevel
, styleSheet
));
4143 wxRichTextApplyStyle(newPara
->GetAttributes(), listStyle
);
4145 // OK, we've (re)applied the style, now let's get the numbering right.
4147 if (currentLevel
== -1)
4148 currentLevel
= thisLevel
;
4150 // Same level as before, do nothing except increment level's number afterwards
4151 if (currentLevel
== thisLevel
)
4154 // A deeper level: start renumbering all levels after current level
4155 else if (thisLevel
> currentLevel
)
4157 for (i
= currentLevel
+1; i
<= thisLevel
; i
++)
4161 currentLevel
= thisLevel
;
4163 else if (thisLevel
< currentLevel
)
4165 currentLevel
= thisLevel
;
4168 // Use the current numbering if -1 and we have a bullet number already
4169 if (levels
[currentLevel
] == -1)
4171 if (newPara
->GetAttributes().HasBulletNumber())
4172 levels
[currentLevel
] = newPara
->GetAttributes().GetBulletNumber();
4174 levels
[currentLevel
] = 1;
4178 levels
[currentLevel
] ++;
4181 newPara
->GetAttributes().SetBulletNumber(levels
[currentLevel
]);
4183 // Create the bullet text if an outline list
4184 if (listStyle
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
)
4187 for (i
= 0; i
<= currentLevel
; i
++)
4189 if (!text
.IsEmpty())
4191 text
+= wxString::Format(wxT("%d"), levels
[i
]);
4193 newPara
->GetAttributes().SetBulletText(text
);
4199 node
= node
->GetNext();
4202 // Do action, or delay it until end of batch.
4203 if (haveControl
&& withUndo
)
4204 buffer
->SubmitAction(action
);
4209 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
)
4211 wxRichTextBuffer
* buffer
= GetBuffer();
4212 if (buffer
->GetStyleSheet())
4214 wxRichTextListStyleDefinition
* def
= NULL
;
4215 if (!defName
.IsEmpty())
4216 def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4217 return NumberList(range
, def
, flags
, startFrom
, specifiedLevel
);
4222 /// Promote the list items within the given range. promoteBy can be a positive or negative number, e.g. 1 or -1
4223 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy
, const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int specifiedLevel
)
4226 // One strategy is to first work out the range within which renumbering must occur. Then could pass these two ranges
4227 // to NumberList with a flag indicating promotion is required within one of the ranges.
4228 // Find first and last paragraphs in range. Then for first, calculate new indentation and look back until we find
4229 // a paragraph that either has no list style, or has one that is different or whose indentation is less.
4230 // We start renumbering from the para after that different para we found. We specify that the numbering of that
4231 // list position will start from 1.
4232 // Similarly, we look after the last para in the promote range for an indentation that is less (or no list style).
4233 // We can end the renumbering at this point.
4235 // For now, only renumber within the promotion range.
4237 return DoNumberList(range
, range
, promoteBy
, def
, flags
, 1, specifiedLevel
);
4240 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy
, const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int specifiedLevel
)
4242 wxRichTextBuffer
* buffer
= GetBuffer();
4243 if (buffer
->GetStyleSheet())
4245 wxRichTextListStyleDefinition
* def
= NULL
;
4246 if (!defName
.IsEmpty())
4247 def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4248 return PromoteList(promoteBy
, range
, def
, flags
, specifiedLevel
);
4253 /// Fills in the attributes for numbering a paragraph after previousParagraph. It also finds the
4254 /// position of the paragraph that it had to start looking from.
4255 bool wxRichTextParagraphLayoutBox::FindNextParagraphNumber(wxRichTextParagraph
* previousParagraph
, wxRichTextAttr
& attr
) const
4257 if (!previousParagraph
->GetAttributes().HasFlag(wxTEXT_ATTR_BULLET_STYLE
) || previousParagraph
->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
)
4260 wxRichTextBuffer
* buffer
= GetBuffer();
4261 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
4262 if (styleSheet
&& !previousParagraph
->GetAttributes().GetListStyleName().IsEmpty())
4264 wxRichTextListStyleDefinition
* def
= styleSheet
->FindListStyle(previousParagraph
->GetAttributes().GetListStyleName());
4267 // int thisIndent = previousParagraph->GetAttributes().GetLeftIndent();
4268 // int thisLevel = def->FindLevelForIndent(thisIndent);
4270 bool isOutline
= (previousParagraph
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
) != 0;
4272 attr
.SetFlags(previousParagraph
->GetAttributes().GetFlags() & (wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_BULLET_NUMBER
|wxTEXT_ATTR_BULLET_TEXT
|wxTEXT_ATTR_BULLET_NAME
));
4273 if (previousParagraph
->GetAttributes().HasBulletName())
4274 attr
.SetBulletName(previousParagraph
->GetAttributes().GetBulletName());
4275 attr
.SetBulletStyle(previousParagraph
->GetAttributes().GetBulletStyle());
4276 attr
.SetListStyleName(previousParagraph
->GetAttributes().GetListStyleName());
4278 int nextNumber
= previousParagraph
->GetAttributes().GetBulletNumber() + 1;
4279 attr
.SetBulletNumber(nextNumber
);
4283 wxString text
= previousParagraph
->GetAttributes().GetBulletText();
4284 if (!text
.IsEmpty())
4286 int pos
= text
.Find(wxT('.'), true);
4287 if (pos
!= wxNOT_FOUND
)
4289 text
= text
.Mid(0, text
.Length() - pos
- 1);
4292 text
= wxEmptyString
;
4293 if (!text
.IsEmpty())
4295 text
+= wxString::Format(wxT("%d"), nextNumber
);
4296 attr
.SetBulletText(text
);
4310 * wxRichTextParagraph
4311 * This object represents a single paragraph (or in a straight text editor, a line).
4314 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraph
, wxRichTextCompositeObject
)
4316 wxArrayInt
wxRichTextParagraph::sm_defaultTabs
;
4318 wxRichTextParagraph::wxRichTextParagraph(wxRichTextObject
* parent
, wxRichTextAttr
* style
):
4319 wxRichTextCompositeObject(parent
)
4322 SetAttributes(*style
);
4325 wxRichTextParagraph::wxRichTextParagraph(const wxString
& text
, wxRichTextObject
* parent
, wxRichTextAttr
* paraStyle
, wxRichTextAttr
* charStyle
):
4326 wxRichTextCompositeObject(parent
)
4329 SetAttributes(*paraStyle
);
4331 AppendChild(new wxRichTextPlainText(text
, this, charStyle
));
4334 wxRichTextParagraph::~wxRichTextParagraph()
4340 bool wxRichTextParagraph::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int WXUNUSED(descent
), int style
)
4345 // Currently we don't merge these attributes with the parent, but we
4346 // should consider whether we should (e.g. if we set a border colour
4347 // for all paragraphs). But generally box attributes are likely to be
4348 // different for different objects.
4349 wxRect paraRect
= GetRect();
4350 wxRichTextAttr attr
= GetCombinedAttributes();
4351 context
.ApplyVirtualAttributes(attr
, this);
4353 DrawBoxAttributes(dc
, GetBuffer(), attr
, paraRect
);
4355 // Draw the bullet, if any
4356 if (attr
.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE
)
4358 if (attr
.GetLeftSubIndent() != 0)
4360 int spaceBeforePara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingBefore());
4361 int leftIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftIndent());
4363 wxRichTextAttr
bulletAttr(attr
);
4365 // Combine with the font of the first piece of content, if one is specified
4366 if (GetChildren().GetCount() > 0)
4368 wxRichTextObject
* firstObj
= (wxRichTextObject
*) GetChildren().GetFirst()->GetData();
4369 if (!firstObj
->IsFloatable() && firstObj
->GetAttributes().HasFont())
4371 wxRichTextApplyStyle(bulletAttr
, firstObj
->GetAttributes());
4375 // Get line height from first line, if any
4376 wxRichTextLine
* line
= m_cachedLines
.GetFirst() ? (wxRichTextLine
* ) m_cachedLines
.GetFirst()->GetData() : NULL
;
4379 int lineHeight
wxDUMMY_INITIALIZE(0);
4382 lineHeight
= line
->GetSize().y
;
4383 linePos
= line
->GetPosition() + GetPosition();
4388 if (bulletAttr
.HasFont() && GetBuffer())
4389 font
= GetBuffer()->GetFontTable().FindFont(bulletAttr
);
4391 font
= (*wxNORMAL_FONT
);
4393 wxCheckSetFont(dc
, font
);
4395 lineHeight
= dc
.GetCharHeight();
4396 linePos
= GetPosition();
4397 linePos
.y
+= spaceBeforePara
;
4400 wxRect
bulletRect(GetPosition().x
+ leftIndent
, linePos
.y
, linePos
.x
- (GetPosition().x
+ leftIndent
), lineHeight
);
4402 if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
)
4404 if (wxRichTextBuffer::GetRenderer())
4405 wxRichTextBuffer::GetRenderer()->DrawBitmapBullet(this, dc
, bulletAttr
, bulletRect
);
4407 else if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_STANDARD
)
4409 if (wxRichTextBuffer::GetRenderer())
4410 wxRichTextBuffer::GetRenderer()->DrawStandardBullet(this, dc
, bulletAttr
, bulletRect
);
4414 wxString bulletText
= GetBulletText();
4416 if (!bulletText
.empty() && wxRichTextBuffer::GetRenderer())
4417 wxRichTextBuffer::GetRenderer()->DrawTextBullet(this, dc
, bulletAttr
, bulletRect
, bulletText
);
4422 // Draw the range for each line, one object at a time.
4424 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
4427 wxRichTextLine
* line
= node
->GetData();
4428 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
4430 // Lines are specified relative to the paragraph
4432 wxPoint linePosition
= line
->GetPosition() + GetPosition();
4434 // Don't draw if off the screen
4435 if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) != 0) || ((linePosition
.y
+ line
->GetSize().y
) >= rect
.y
&& linePosition
.y
<= rect
.y
+ rect
.height
))
4437 wxPoint objectPosition
= linePosition
;
4438 int maxDescent
= line
->GetDescent();
4440 // Loop through objects until we get to the one within range
4441 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
4446 wxRichTextObject
* child
= node2
->GetData();
4448 if (!child
->IsFloating() && child
->GetRange().GetLength() > 0 && !child
->GetRange().IsOutside(lineRange
) && !lineRange
.IsOutside(range
))
4450 // Draw this part of the line at the correct position
4451 wxRichTextRange
objectRange(child
->GetRange());
4452 objectRange
.LimitTo(lineRange
);
4455 if (child
->IsTopLevel())
4457 objectSize
= child
->GetCachedSize();
4458 objectRange
= child
->GetOwnRange();
4462 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING && wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4463 if (i
< (int) line
->GetObjectSizes().GetCount())
4465 objectSize
.x
= line
->GetObjectSizes()[(size_t) i
];
4471 child
->GetRangeSize(objectRange
, objectSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, objectPosition
);
4475 // Use the child object's width, but the whole line's height
4476 wxRect
childRect(objectPosition
, wxSize(objectSize
.x
, line
->GetSize().y
));
4477 child
->Draw(dc
, context
, objectRange
, selection
, childRect
, maxDescent
, style
);
4479 objectPosition
.x
+= objectSize
.x
;
4482 else if (child
->GetRange().GetStart() > lineRange
.GetEnd())
4483 // Can break out of inner loop now since we've passed this line's range
4486 node2
= node2
->GetNext();
4490 node
= node
->GetNext();
4496 // Get the range width using partial extents calculated for the whole paragraph.
4497 static int wxRichTextGetRangeWidth(const wxRichTextParagraph
& para
, const wxRichTextRange
& range
, const wxArrayInt
& partialExtents
)
4499 wxASSERT(partialExtents
.GetCount() >= (size_t) range
.GetLength());
4501 if (partialExtents
.GetCount() < (size_t) range
.GetLength())
4504 int leftMostPos
= 0;
4505 if (range
.GetStart() - para
.GetRange().GetStart() > 0)
4506 leftMostPos
= partialExtents
[range
.GetStart() - para
.GetRange().GetStart() - 1];
4508 int rightMostPos
= partialExtents
[range
.GetEnd() - para
.GetRange().GetStart()];
4510 int w
= rightMostPos
- leftMostPos
;
4515 /// Lay the item out
4516 bool wxRichTextParagraph::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
)
4518 // Deal with floating objects firstly before the normal layout
4519 wxRichTextBuffer
* buffer
= GetBuffer();
4521 wxRichTextFloatCollector
* collector
= GetContainer()->GetFloatCollector();
4522 wxASSERT(collector
);
4523 LayoutFloat(dc
, context
, rect
, style
, collector
);
4525 wxRichTextAttr attr
= GetCombinedAttributes();
4526 context
.ApplyVirtualAttributes(attr
, this);
4530 // Increase the size of the paragraph due to spacing
4531 int spaceBeforePara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingBefore());
4532 int spaceAfterPara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingAfter());
4533 int leftIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftIndent());
4534 int leftSubIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftSubIndent());
4535 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
4537 int lineSpacing
= 0;
4539 // Let's assume line spacing of 10 is normal, 15 is 1.5, 20 is 2, etc.
4540 if (attr
.HasLineSpacing() && attr
.GetLineSpacing() > 0 && attr
.GetFont().IsOk())
4542 wxCheckSetFont(dc
, attr
.GetFont());
4543 lineSpacing
= (int) (double(dc
.GetCharHeight()) * (double(attr
.GetLineSpacing())/10.0 - 1.0));
4546 // Start position for each line relative to the paragraph
4547 int startPositionFirstLine
= leftIndent
;
4548 int startPositionSubsequentLines
= leftIndent
+ leftSubIndent
;
4550 // If we have a bullet in this paragraph, the start position for the first line's text
4551 // is actually leftIndent + leftSubIndent.
4552 if (attr
.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE
)
4553 startPositionFirstLine
= startPositionSubsequentLines
;
4555 long lastEndPos
= GetRange().GetStart()-1;
4556 long lastCompletedEndPos
= lastEndPos
;
4558 int currentWidth
= 0;
4559 SetPosition(rect
.GetPosition());
4561 wxPoint
currentPosition(0, spaceBeforePara
); // We will calculate lines relative to paragraph
4564 int maxHeight
= currentPosition
.y
;
4569 int lineDescent
= 0;
4571 wxRichTextObjectList::compatibility_iterator node
;
4573 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4575 wxArrayInt partialExtents
;
4578 int paraDescent
= 0;
4580 // This calculates the partial text extents
4581 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, rect
.GetPosition(), & partialExtents
);
4583 node
= m_children
.GetFirst();
4586 wxRichTextObject
* child
= node
->GetData();
4588 //child->SetCachedSize(wxDefaultSize);
4589 child
->Layout(dc
, context
, rect
, style
);
4591 node
= node
->GetNext();
4598 // We may need to go back to a previous child, in which case create the new line,
4599 // find the child corresponding to the start position of the string, and
4602 wxRect availableRect
;
4604 node
= m_children
.GetFirst();
4607 wxRichTextObject
* child
= node
->GetData();
4609 // If floating, ignore. We already laid out floats.
4610 // Also ignore if empty object, except if we haven't got any
4612 if (child
->IsFloating() || !child
->IsShown() ||
4613 (child
->GetRange().GetLength() == 0 && maxHeight
> spaceBeforePara
)
4616 node
= node
->GetNext();
4620 // If this is e.g. a composite text box, it will need to be laid out itself.
4621 // But if just a text fragment or image, for example, this will
4622 // do nothing. NB: won't we need to set the position after layout?
4623 // since for example if position is dependent on vertical line size, we
4624 // can't tell the position until the size is determined. So possibly introduce
4625 // another layout phase.
4627 // We may only be looking at part of a child, if we searched back for wrapping
4628 // and found a suitable point some way into the child. So get the size for the fragment
4631 long nextBreakPos
= GetFirstLineBreakPosition(lastEndPos
+1);
4632 long lastPosToUse
= child
->GetRange().GetEnd();
4633 bool lineBreakInThisObject
= (nextBreakPos
> -1 && nextBreakPos
<= child
->GetRange().GetEnd());
4635 if (lineBreakInThisObject
)
4636 lastPosToUse
= nextBreakPos
;
4639 int childDescent
= 0;
4641 int startOffset
= (lineCount
== 0 ? startPositionFirstLine
: startPositionSubsequentLines
);
4642 availableRect
= wxRect(rect
.x
+ startOffset
, rect
.y
+ currentPosition
.y
,
4643 rect
.width
- startOffset
- rightIndent
, rect
.height
);
4645 if (child
->IsTopLevel())
4647 wxSize oldSize
= child
->GetCachedSize();
4649 child
->Invalidate(wxRICHTEXT_ALL
);
4650 child
->SetPosition(wxPoint(0, 0));
4652 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4653 // lays out the object again using the minimum size
4654 // The position will be determined by its location in its line,
4655 // and not by the child's actual position.
4656 child
->LayoutToBestSize(dc
, context
, buffer
,
4657 attr
, child
->GetAttributes(), availableRect
, parentRect
, style
);
4659 if (oldSize
!= child
->GetCachedSize())
4661 partialExtents
.Clear();
4663 // Recalculate the partial text extents since the child object changed size
4664 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), & partialExtents
);
4668 // Problem: we need to layout composites here for which we need the available width,
4669 // but we can't get the available width without using the float collector which
4670 // needs to know the object height.
4672 if ((nextBreakPos
== -1) && (lastEndPos
== child
->GetRange().GetStart() - 1)) // i.e. we want to get the whole thing
4674 childSize
= child
->GetCachedSize();
4675 childDescent
= child
->GetDescent();
4679 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4680 // Get height only, then the width using the partial extents
4681 GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
);
4682 childSize
.x
= wxRichTextGetRangeWidth(*this, wxRichTextRange(lastEndPos
+1, lastPosToUse
), partialExtents
);
4684 GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, rect
.GetPosition());
4689 int loopIterations
= 0;
4691 // If there are nested objects that need to lay themselves out, we have to do this in a
4692 // loop because the height of the object may well depend on the available width.
4693 // And because of floating object positioning, the available width depends on the
4694 // height of the object and whether it will clash with the floating objects.
4695 // So, we see whether the available width changes due to the presence of floating images.
4696 // If it does, then we'll use the new restricted width to find the object height again.
4697 // If this causes another restriction in the available width, we'll try again, until
4698 // either we lose patience or the available width settles down.
4703 wxRect oldAvailableRect
= availableRect
;
4705 // Available width depends on the floating objects and the line height.
4706 // Note: the floating objects may be placed vertically along the two side of
4707 // buffer, so we may have different available line widths with different
4708 // [startY, endY]. So, we can't determine how wide the available
4709 // space is until we know the exact line height.
4710 lineDescent
= wxMax(childDescent
, maxDescent
);
4711 lineAscent
= wxMax(childSize
.y
-childDescent
, maxAscent
);
4712 lineHeight
= lineDescent
+ lineAscent
;
4713 wxRect floatAvailableRect
= collector
->GetAvailableRect(rect
.y
+ currentPosition
.y
, rect
.y
+ currentPosition
.y
+ lineHeight
);
4715 // Adjust availableRect to the space that is available when taking floating objects into account.
4717 if (floatAvailableRect
.x
+ startOffset
> availableRect
.x
)
4719 int newX
= floatAvailableRect
.x
+ startOffset
;
4720 int newW
= availableRect
.width
- (newX
- availableRect
.x
);
4721 availableRect
.x
= newX
;
4722 availableRect
.width
= newW
;
4725 if (floatAvailableRect
.width
< availableRect
.width
)
4726 availableRect
.width
= floatAvailableRect
.width
;
4728 currentPosition
.x
= availableRect
.x
- rect
.x
;
4730 if (child
->IsTopLevel() && loopIterations
<= 20)
4732 if (availableRect
!= oldAvailableRect
)
4734 wxSize oldSize
= child
->GetCachedSize();
4736 //child->SetCachedSize(wxDefaultSize);
4737 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4738 // lays out the object again using the minimum size
4739 child
->Invalidate(wxRICHTEXT_ALL
);
4740 child
->LayoutToBestSize(dc
, context
, buffer
,
4741 attr
, child
->GetAttributes(), availableRect
, parentRect
, style
);
4742 childSize
= child
->GetCachedSize();
4743 childDescent
= child
->GetDescent();
4744 //child->SetPosition(availableRect.GetPosition());
4746 if (oldSize
!= child
->GetCachedSize())
4748 partialExtents
.Clear();
4750 // Recalculate the partial text extents since the child object changed size
4751 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), & partialExtents
);
4754 // Go around the loop finding the available rect for the given floating objects
4765 // 1) There was a line break BEFORE the natural break
4766 // 2) There was a line break AFTER the natural break
4767 // 3) It's the last line
4768 // 4) The child still fits (carry on) - 'else' clause
4770 if ((lineBreakInThisObject
&& (childSize
.x
+ currentWidth
<= availableRect
.width
))
4772 (childSize
.x
+ currentWidth
> availableRect
.width
)
4774 ((childSize
.x
+ currentWidth
<= availableRect
.width
) && !node
->GetNext())
4778 if (child
->IsTopLevel())
4780 // We can move it to the correct position at this point
4781 child
->Move(GetPosition() + wxPoint(currentWidth
, currentPosition
.y
));
4784 long wrapPosition
= 0;
4785 if ((childSize
.x
+ currentWidth
<= availableRect
.width
) && !node
->GetNext() && !lineBreakInThisObject
)
4786 wrapPosition
= child
->GetRange().GetEnd();
4789 // Find a place to wrap. This may walk back to previous children,
4790 // for example if a word spans several objects.
4791 // Note: one object must contains only one wxTextAtrr, so the line height will not
4792 // change inside one object. Thus, we can pass the remain line width to the
4793 // FindWrapPosition function.
4794 if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos
+1, child
->GetRange().GetEnd()), dc
, context
, availableRect
.width
, wrapPosition
, & partialExtents
))
4796 // If the function failed, just cut it off at the end of this child.
4797 wrapPosition
= child
->GetRange().GetEnd();
4800 // FindWrapPosition can still return a value that will put us in an endless wrapping loop
4801 if (wrapPosition
<= lastCompletedEndPos
)
4802 wrapPosition
= wxMax(lastCompletedEndPos
+1,child
->GetRange().GetEnd());
4804 // Line end position shouldn't be the same as the end, or greater.
4805 if (wrapPosition
>= GetRange().GetEnd())
4806 wrapPosition
= GetRange().GetEnd()-1;
4808 // wxLogDebug(wxT("Split at %ld"), wrapPosition);
4810 // Let's find the actual size of the current line now
4812 wxRichTextRange
actualRange(lastCompletedEndPos
+1, wrapPosition
);
4814 /// Use previous descent, not the wrapping descent we just found, since this may be too big
4815 /// for the fragment we're about to add.
4816 childDescent
= maxDescent
;
4818 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4819 if (!child
->IsEmpty())
4821 // Get height only, then the width using the partial extents
4822 GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
);
4823 actualSize
.x
= wxRichTextGetRangeWidth(*this, actualRange
, partialExtents
);
4827 GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
4829 currentWidth
= actualSize
.x
;
4830 maxDescent
= wxMax(childDescent
, maxDescent
);
4831 maxAscent
= wxMax(actualSize
.y
-childDescent
, maxAscent
);
4832 lineHeight
= maxDescent
+ maxAscent
;
4834 if (lineHeight
== 0 && buffer
)
4836 wxFont
font(buffer
->GetFontTable().FindFont(attr
));
4837 wxCheckSetFont(dc
, font
);
4838 lineHeight
= dc
.GetCharHeight();
4841 if (maxDescent
== 0)
4844 dc
.GetTextExtent(wxT("X"), & w
, &h
, & maxDescent
);
4848 wxRichTextLine
* line
= AllocateLine(lineCount
);
4850 // Set relative range so we won't have to change line ranges when paragraphs are moved
4851 line
->SetRange(wxRichTextRange(actualRange
.GetStart() - GetRange().GetStart(), actualRange
.GetEnd() - GetRange().GetStart()));
4852 line
->SetPosition(currentPosition
);
4853 line
->SetSize(wxSize(currentWidth
, lineHeight
));
4854 line
->SetDescent(maxDescent
);
4856 maxHeight
= currentPosition
.y
+ lineHeight
;
4858 // Now move down a line. TODO: add margins, spacing
4859 currentPosition
.y
+= lineHeight
;
4860 currentPosition
.y
+= lineSpacing
;
4863 maxWidth
= wxMax(maxWidth
, currentWidth
+startOffset
);
4868 // TODO: account for zero-length objects, such as fields
4869 // wxASSERT(wrapPosition > lastCompletedEndPos);
4871 lastEndPos
= wrapPosition
;
4872 lastCompletedEndPos
= lastEndPos
;
4876 if (wrapPosition
< GetRange().GetEnd()-1)
4878 // May need to set the node back to a previous one, due to searching back in wrapping
4879 wxRichTextObject
* childAfterWrapPosition
= FindObjectAtPosition(wrapPosition
+1);
4880 if (childAfterWrapPosition
)
4881 node
= m_children
.Find(childAfterWrapPosition
);
4883 node
= node
->GetNext();
4886 node
= node
->GetNext();
4888 // Apply paragraph styles such as alignment to the wrapped line
4889 ApplyParagraphStyle(line
, attr
, availableRect
, dc
);
4893 // We still fit, so don't add a line, and keep going
4894 currentWidth
+= childSize
.x
;
4895 maxDescent
= wxMax(childDescent
, maxDescent
);
4896 maxAscent
= wxMax(childSize
.y
-childDescent
, maxAscent
);
4897 lineHeight
= maxDescent
+ maxAscent
;
4899 maxWidth
= wxMax(maxWidth
, currentWidth
+startOffset
);
4900 lastEndPos
= child
->GetRange().GetEnd();
4902 node
= node
->GetNext();
4906 //wxASSERT(!(lastCompletedEndPos != -1 && lastCompletedEndPos < GetRange().GetEnd()-1));
4908 // Remove remaining unused line objects, if any
4909 ClearUnusedLines(lineCount
);
4911 // We need to add back the margins etc.
4913 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
4914 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxWidth
, currentPosition
.y
+ spaceAfterPara
));
4915 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
4916 SetCachedSize(marginRect
.GetSize());
4919 // The maximum size is the length of the paragraph stretched out into a line.
4920 // So if there were a single word, or an image, or a fixed-size text box, the object could be shrunk around
4921 // this size. TODO: take into account line breaks.
4923 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
4924 contentRect
= wxRect(wxPoint(0, 0), wxSize(paraSize
.x
+ wxMax(leftIndent
, leftIndent
+ leftSubIndent
) + rightIndent
, currentPosition
.y
+ spaceAfterPara
));
4925 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
4926 SetMaxSize(marginRect
.GetSize());
4929 // Find the greatest minimum size. Currently we only look at non-text objects,
4930 // which isn't ideal but it would be slow to find the maximum word width to
4931 // use as the minimum.
4934 node
= m_children
.GetFirst();
4937 wxRichTextObject
* child
= node
->GetData();
4939 // If floating, ignore. We already laid out floats.
4940 // Also ignore if empty object, except if we haven't got any
4942 if (!child
->IsFloating() && child
->GetRange().GetLength() != 0 && !child
->IsKindOf(CLASSINFO(wxRichTextPlainText
)))
4944 if (child
->GetCachedSize().x
> minWidth
)
4945 minWidth
= child
->GetMinSize().x
;
4947 node
= node
->GetNext();
4950 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
4951 contentRect
= wxRect(wxPoint(0, 0), wxSize(minWidth
, currentPosition
.y
+ spaceAfterPara
));
4952 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
4953 SetMinSize(marginRect
.GetSize());
4957 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4958 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
4959 // Use the text extents to calculate the size of each fragment in each line
4960 wxRichTextLineList::compatibility_iterator lineNode
= m_cachedLines
.GetFirst();
4963 wxRichTextLine
* line
= lineNode
->GetData();
4964 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
4966 // Loop through objects until we get to the one within range
4967 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
4971 wxRichTextObject
* child
= node2
->GetData();
4973 if (child
->GetRange().GetLength() > 0 && !child
->GetRange().IsOutside(lineRange
))
4975 wxRichTextRange rangeToUse
= lineRange
;
4976 rangeToUse
.LimitTo(child
->GetRange());
4978 // Find the size of the child from the text extents, and store in an array
4979 // for drawing later
4981 if (rangeToUse
.GetStart() > GetRange().GetStart())
4982 left
= partialExtents
[(rangeToUse
.GetStart()-1) - GetRange().GetStart()];
4983 int right
= partialExtents
[rangeToUse
.GetEnd() - GetRange().GetStart()];
4984 int sz
= right
- left
;
4985 line
->GetObjectSizes().Add(sz
);
4987 else if (child
->GetRange().GetStart() > lineRange
.GetEnd())
4988 // Can break out of inner loop now since we've passed this line's range
4991 node2
= node2
->GetNext();
4994 lineNode
= lineNode
->GetNext();
5002 /// Apply paragraph styles, such as centering, to wrapped lines
5003 /// TODO: take into account box attributes, possibly
5004 void wxRichTextParagraph::ApplyParagraphStyle(wxRichTextLine
* line
, const wxRichTextAttr
& attr
, const wxRect
& rect
, wxDC
& dc
)
5006 if (!attr
.HasAlignment())
5009 wxPoint pos
= line
->GetPosition();
5010 wxSize size
= line
->GetSize();
5012 // centering, right-justification
5013 if (attr
.HasAlignment() && attr
.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE
)
5015 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
5016 pos
.x
= (rect
.GetWidth() - rightIndent
- size
.x
)/2 + pos
.x
;
5017 line
->SetPosition(pos
);
5019 else if (attr
.HasAlignment() && attr
.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT
)
5021 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
5022 pos
.x
= pos
.x
+ rect
.GetWidth() - size
.x
- rightIndent
;
5023 line
->SetPosition(pos
);
5027 /// Insert text at the given position
5028 bool wxRichTextParagraph::InsertText(long pos
, const wxString
& text
)
5030 wxRichTextObject
* childToUse
= NULL
;
5031 wxRichTextObjectList::compatibility_iterator nodeToUse
= wxRichTextObjectList::compatibility_iterator();
5033 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5036 wxRichTextObject
* child
= node
->GetData();
5037 if (child
->GetRange().Contains(pos
) && child
->GetRange().GetLength() > 0)
5044 node
= node
->GetNext();
5049 wxRichTextPlainText
* textObject
= wxDynamicCast(childToUse
, wxRichTextPlainText
);
5052 int posInString
= pos
- textObject
->GetRange().GetStart();
5054 wxString newText
= textObject
->GetText().Mid(0, posInString
) +
5055 text
+ textObject
->GetText().Mid(posInString
);
5056 textObject
->SetText(newText
);
5058 int textLength
= text
.length();
5060 textObject
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart(),
5061 textObject
->GetRange().GetEnd() + textLength
));
5063 // Increment the end range of subsequent fragments in this paragraph.
5064 // We'll set the paragraph range itself at a higher level.
5066 wxRichTextObjectList::compatibility_iterator node
= nodeToUse
->GetNext();
5069 wxRichTextObject
* child
= node
->GetData();
5070 child
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart() + textLength
,
5071 textObject
->GetRange().GetEnd() + textLength
));
5073 node
= node
->GetNext();
5080 // TODO: if not a text object, insert at closest position, e.g. in front of it
5086 // Don't pass parent initially to suppress auto-setting of parent range.
5087 // We'll do that at a higher level.
5088 wxRichTextPlainText
* textObject
= new wxRichTextPlainText(text
, this);
5090 AppendChild(textObject
);
5097 void wxRichTextParagraph::Copy(const wxRichTextParagraph
& obj
)
5099 wxRichTextCompositeObject::Copy(obj
);
5102 /// Clear the cached lines
5103 void wxRichTextParagraph::ClearLines()
5105 WX_CLEAR_LIST(wxRichTextLineList
, m_cachedLines
);
5108 /// Get/set the object size for the given range. Returns false if the range
5109 /// is invalid for this object.
5110 bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
5112 if (!range
.IsWithin(GetRange()))
5115 if (flags
& wxRICHTEXT_UNFORMATTED
)
5117 // Just use unformatted data, assume no line breaks
5118 // TODO: take into account line breaks
5122 wxArrayInt childExtents
;
5129 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5133 wxRichTextObject
* child
= node
->GetData();
5134 if (!child
->GetRange().IsOutside(range
))
5136 // Floating objects have a zero size within the paragraph.
5137 if (child
->IsFloating())
5142 if (partialExtents
->GetCount() > 0)
5143 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5147 partialExtents
->Add(0 /* zero size */ + lastSize
);
5154 wxRichTextRange rangeToUse
= range
;
5155 rangeToUse
.LimitTo(child
->GetRange());
5157 if (child
->IsTopLevel())
5158 rangeToUse
= child
->GetOwnRange();
5160 int childDescent
= 0;
5162 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we're already cached the size,
5163 // but it's only going to be used after caching has taken place.
5164 if ((flags
& wxRICHTEXT_HEIGHT_ONLY
) && child
->GetCachedSize().y
!= 0)
5166 childDescent
= child
->GetDescent();
5167 childSize
= child
->GetCachedSize();
5169 sz
.y
= wxMax(sz
.y
, childSize
.y
);
5170 sz
.x
+= childSize
.x
;
5171 descent
= wxMax(descent
, childDescent
);
5173 else if (child
->IsTopLevel())
5175 childDescent
= child
->GetDescent();
5176 childSize
= child
->GetCachedSize();
5178 sz
.y
= wxMax(sz
.y
, childSize
.y
);
5179 sz
.x
+= childSize
.x
;
5180 descent
= wxMax(descent
, childDescent
);
5181 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange()))
5183 child
->SetCachedSize(childSize
);
5184 child
->SetDescent(childDescent
);
5190 if (partialExtents
->GetCount() > 0)
5191 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5195 partialExtents
->Add(childSize
.x
+ lastSize
);
5198 else if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, context
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
), p
))
5200 sz
.y
= wxMax(sz
.y
, childSize
.y
);
5201 sz
.x
+= childSize
.x
;
5202 descent
= wxMax(descent
, childDescent
);
5204 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange()))
5206 child
->SetCachedSize(childSize
);
5207 child
->SetDescent(childDescent
);
5213 if (partialExtents
->GetCount() > 0)
5214 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5219 for (i
= 0; i
< childExtents
.GetCount(); i
++)
5221 partialExtents
->Add(childExtents
[i
] + lastSize
);
5231 node
= node
->GetNext();
5237 // Use formatted data, with line breaks
5240 // We're going to loop through each line, and then for each line,
5241 // call GetRangeSize for the fragment that comprises that line.
5242 // Only we have to do that multiple times within the line, because
5243 // the line may be broken into pieces. For now ignore line break commands
5244 // (so we can assume that getting the unformatted size for a fragment
5245 // within a line is the actual size)
5247 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5250 wxRichTextLine
* line
= node
->GetData();
5251 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5252 if (!lineRange
.IsOutside(range
))
5256 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
5259 wxRichTextObject
* child
= node2
->GetData();
5261 if (!child
->IsFloating() && !child
->GetRange().IsOutside(lineRange
))
5263 wxRichTextRange rangeToUse
= lineRange
;
5264 rangeToUse
.LimitTo(child
->GetRange());
5265 if (child
->IsTopLevel())
5266 rangeToUse
= child
->GetOwnRange();
5269 int childDescent
= 0;
5270 if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, context
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
)))
5272 lineSize
.y
= wxMax(lineSize
.y
, childSize
.y
);
5273 lineSize
.x
+= childSize
.x
;
5275 descent
= wxMax(descent
, childDescent
);
5278 node2
= node2
->GetNext();
5281 // Increase size by a line (TODO: paragraph spacing)
5283 sz
.x
= wxMax(sz
.x
, lineSize
.x
);
5285 node
= node
->GetNext();
5292 /// Finds the absolute position and row height for the given character position
5293 bool wxRichTextParagraph::FindPosition(wxDC
& dc
, wxRichTextDrawingContext
& context
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
5297 wxRichTextLine
* line
= ((wxRichTextParagraphLayoutBox
*)GetParent())->GetLineAtPosition(0);
5299 *height
= line
->GetSize().y
;
5301 *height
= dc
.GetCharHeight();
5303 // -1 means 'the start of the buffer'.
5306 pt
= pt
+ line
->GetPosition();
5311 // The final position in a paragraph is taken to mean the position
5312 // at the start of the next paragraph.
5313 if (index
== GetRange().GetEnd())
5315 wxRichTextParagraphLayoutBox
* parent
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
5316 wxASSERT( parent
!= NULL
);
5318 // Find the height at the next paragraph, if any
5319 wxRichTextLine
* line
= parent
->GetLineAtPosition(index
+ 1);
5322 *height
= line
->GetSize().y
;
5323 pt
= line
->GetAbsolutePosition();
5327 *height
= dc
.GetCharHeight();
5328 int indent
= ConvertTenthsMMToPixels(dc
, m_attributes
.GetLeftIndent());
5329 pt
= wxPoint(indent
, GetCachedSize().y
);
5335 if (index
< GetRange().GetStart() || index
> GetRange().GetEnd())
5338 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5341 wxRichTextLine
* line
= node
->GetData();
5342 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5343 if (index
>= lineRange
.GetStart() && index
<= lineRange
.GetEnd())
5345 // If this is the last point in the line, and we're forcing the
5346 // returned value to be the start of the next line, do the required
5348 if (index
== lineRange
.GetEnd() && forceLineStart
)
5350 if (node
->GetNext())
5352 wxRichTextLine
* nextLine
= node
->GetNext()->GetData();
5353 *height
= nextLine
->GetSize().y
;
5354 pt
= nextLine
->GetAbsolutePosition();
5359 pt
.y
= line
->GetPosition().y
+ GetPosition().y
;
5361 wxRichTextRange
r(lineRange
.GetStart(), index
);
5365 // We find the size of the line up to this point,
5366 // then we can add this size to the line start position and
5367 // paragraph start position to find the actual position.
5369 if (GetRangeSize(r
, rangeSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, line
->GetPosition()+ GetPosition()))
5371 pt
.x
= line
->GetPosition().x
+ GetPosition().x
+ rangeSize
.x
;
5372 *height
= line
->GetSize().y
;
5379 node
= node
->GetNext();
5385 /// Hit-testing: returns a flag indicating hit test details, plus
5386 /// information about position
5387 int wxRichTextParagraph::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
5390 return wxRICHTEXT_HITTEST_NONE
;
5392 // If we're in the top-level container, then we can return
5393 // a suitable hit test code even if the point is outside the container area,
5394 // so that we can position the caret sensibly even if we don't
5395 // click on valid content. If we're not at the top-level, and the point
5396 // is not within this paragraph object, then we don't want to stop more
5397 // precise hit-testing from working prematurely, so return immediately.
5398 // NEW STRATEGY: use the parent boundary to test whether we're in the
5399 // right region, not the paragraph, since the paragraph may be positioned
5400 // some way in from where the user clicks.
5403 wxRichTextObject
* tempObj
, *tempContextObj
;
5404 if (GetParent() && GetParent()->wxRichTextObject::HitTest(dc
, context
, pt
, tmpPos
, & tempObj
, & tempContextObj
, flags
) == wxRICHTEXT_HITTEST_NONE
)
5405 return wxRICHTEXT_HITTEST_NONE
;
5408 wxRichTextObjectList::compatibility_iterator objNode
= m_children
.GetFirst();
5411 wxRichTextObject
* child
= objNode
->GetData();
5412 if (child
->IsTopLevel() && ((flags
& wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS
) == 0))
5415 int hitTest
= child
->HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
);
5416 if (hitTest
!= wxRICHTEXT_HITTEST_NONE
)
5421 objNode
= objNode
->GetNext();
5424 wxPoint paraPos
= GetPosition();
5426 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5429 wxRichTextLine
* line
= node
->GetData();
5430 wxPoint linePos
= paraPos
+ line
->GetPosition();
5431 wxSize lineSize
= line
->GetSize();
5432 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5434 if (pt
.y
<= linePos
.y
+ lineSize
.y
)
5436 if (pt
.x
< linePos
.x
)
5438 textPosition
= lineRange
.GetStart();
5439 *obj
= FindObjectAtPosition(textPosition
);
5440 *contextObj
= GetContainer();
5441 return wxRICHTEXT_HITTEST_BEFORE
|wxRICHTEXT_HITTEST_OUTSIDE
;
5443 else if (pt
.x
>= (linePos
.x
+ lineSize
.x
))
5445 textPosition
= lineRange
.GetEnd();
5446 *obj
= FindObjectAtPosition(textPosition
);
5447 *contextObj
= GetContainer();
5448 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
;
5452 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5453 wxArrayInt partialExtents
;
5458 // This calculates the partial text extents
5459 GetRangeSize(lineRange
, paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, linePos
, & partialExtents
);
5461 int lastX
= linePos
.x
;
5463 for (i
= 0; i
< partialExtents
.GetCount(); i
++)
5465 int nextX
= partialExtents
[i
] + linePos
.x
;
5467 if (pt
.x
>= lastX
&& pt
.x
<= nextX
)
5469 textPosition
= i
+ lineRange
.GetStart(); // minus 1?
5471 *obj
= FindObjectAtPosition(textPosition
);
5472 *contextObj
= GetContainer();
5474 // So now we know it's between i-1 and i.
5475 // Let's see if we can be more precise about
5476 // which side of the position it's on.
5478 int midPoint
= (nextX
+ lastX
)/2;
5479 if (pt
.x
>= midPoint
)
5480 return wxRICHTEXT_HITTEST_AFTER
;
5482 return wxRICHTEXT_HITTEST_BEFORE
;
5489 int lastX
= linePos
.x
;
5490 for (i
= lineRange
.GetStart(); i
<= lineRange
.GetEnd(); i
++)
5495 wxRichTextRange
rangeToUse(lineRange
.GetStart(), i
);
5497 GetRangeSize(rangeToUse
, childSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, linePos
);
5499 int nextX
= childSize
.x
+ linePos
.x
;
5501 if (pt
.x
>= lastX
&& pt
.x
<= nextX
)
5505 *obj
= FindObjectAtPosition(textPosition
);
5506 *contextObj
= GetContainer();
5508 // So now we know it's between i-1 and i.
5509 // Let's see if we can be more precise about
5510 // which side of the position it's on.
5512 int midPoint
= (nextX
+ lastX
)/2;
5513 if (pt
.x
>= midPoint
)
5514 return wxRICHTEXT_HITTEST_AFTER
;
5516 return wxRICHTEXT_HITTEST_BEFORE
;
5527 node
= node
->GetNext();
5530 return wxRICHTEXT_HITTEST_NONE
;
5533 /// Split an object at this position if necessary, and return
5534 /// the previous object, or NULL if inserting at beginning.
5535 wxRichTextObject
* wxRichTextParagraph::SplitAt(long pos
, wxRichTextObject
** previousObject
)
5537 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5540 wxRichTextObject
* child
= node
->GetData();
5542 if (pos
== child
->GetRange().GetStart())
5546 if (node
->GetPrevious())
5547 *previousObject
= node
->GetPrevious()->GetData();
5549 *previousObject
= NULL
;
5555 if (child
->GetRange().Contains(pos
))
5557 // This should create a new object, transferring part of
5558 // the content to the old object and the rest to the new object.
5559 wxRichTextObject
* newObject
= child
->DoSplit(pos
);
5561 // If we couldn't split this object, just insert in front of it.
5564 // Maybe this is an empty string, try the next one
5569 // Insert the new object after 'child'
5570 if (node
->GetNext())
5571 m_children
.Insert(node
->GetNext(), newObject
);
5573 m_children
.Append(newObject
);
5574 newObject
->SetParent(this);
5577 *previousObject
= child
;
5583 node
= node
->GetNext();
5586 *previousObject
= NULL
;
5590 /// Move content to a list from obj on
5591 void wxRichTextParagraph::MoveToList(wxRichTextObject
* obj
, wxList
& list
)
5593 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(obj
);
5596 wxRichTextObject
* child
= node
->GetData();
5599 wxRichTextObjectList::compatibility_iterator oldNode
= node
;
5601 node
= node
->GetNext();
5603 m_children
.DeleteNode(oldNode
);
5607 /// Add content back from list
5608 void wxRichTextParagraph::MoveFromList(wxList
& list
)
5610 for (wxList::compatibility_iterator node
= list
.GetFirst(); node
; node
= node
->GetNext())
5612 AppendChild((wxRichTextObject
*) node
->GetData());
5617 void wxRichTextParagraph::CalculateRange(long start
, long& end
)
5619 wxRichTextCompositeObject::CalculateRange(start
, end
);
5621 // Add one for end of paragraph
5624 m_range
.SetRange(start
, end
);
5627 /// Find the object at the given position
5628 wxRichTextObject
* wxRichTextParagraph::FindObjectAtPosition(long position
)
5630 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5633 wxRichTextObject
* obj
= node
->GetData();
5634 if (obj
->GetRange().Contains(position
) ||
5635 obj
->GetRange().GetStart() == position
||
5636 obj
->GetRange().GetEnd() == position
)
5639 node
= node
->GetNext();
5644 /// Get the plain text searching from the start or end of the range.
5645 /// The resulting string may be shorter than the range given.
5646 bool wxRichTextParagraph::GetContiguousPlainText(wxString
& text
, const wxRichTextRange
& range
, bool fromStart
)
5648 text
= wxEmptyString
;
5652 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5655 wxRichTextObject
* obj
= node
->GetData();
5656 if (!obj
->GetRange().IsOutside(range
))
5658 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5661 text
+= textObj
->GetTextForRange(range
);
5669 node
= node
->GetNext();
5674 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetLast();
5677 wxRichTextObject
* obj
= node
->GetData();
5678 if (!obj
->GetRange().IsOutside(range
))
5680 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5683 text
= textObj
->GetTextForRange(range
) + text
;
5687 text
= wxT(" ") + text
;
5691 node
= node
->GetPrevious();
5698 /// Find a suitable wrap position.
5699 bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange
& range
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int availableSpace
, long& wrapPosition
, wxArrayInt
* partialExtents
)
5701 if (range
.GetLength() <= 0)
5704 // Find the first position where the line exceeds the available space.
5706 long breakPosition
= range
.GetEnd();
5708 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5709 if (partialExtents
&& partialExtents
->GetCount() >= (size_t) (GetRange().GetLength()-1)) // the final position in a paragraph is the newline
5713 if (range
.GetStart() > GetRange().GetStart())
5714 widthBefore
= (*partialExtents
)[range
.GetStart() - GetRange().GetStart() - 1];
5719 for (i
= (size_t) range
.GetStart(); i
<= (size_t) range
.GetEnd(); i
++)
5721 int widthFromStartOfThisRange
= (*partialExtents
)[i
- GetRange().GetStart()] - widthBefore
;
5723 if (widthFromStartOfThisRange
> availableSpace
)
5725 breakPosition
= i
-1;
5733 // Binary chop for speed
5734 long minPos
= range
.GetStart();
5735 long maxPos
= range
.GetEnd();
5738 if (minPos
== maxPos
)
5741 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
5743 if (sz
.x
> availableSpace
)
5744 breakPosition
= minPos
- 1;
5747 else if ((maxPos
- minPos
) == 1)
5750 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
5752 if (sz
.x
> availableSpace
)
5753 breakPosition
= minPos
- 1;
5756 GetRangeSize(wxRichTextRange(range
.GetStart(), maxPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
5757 if (sz
.x
> availableSpace
)
5758 breakPosition
= maxPos
-1;
5764 long nextPos
= minPos
+ ((maxPos
- minPos
) / 2);
5767 GetRangeSize(wxRichTextRange(range
.GetStart(), nextPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
5769 if (sz
.x
> availableSpace
)
5781 // Now we know the last position on the line.
5782 // Let's try to find a word break.
5785 if (GetContiguousPlainText(plainText
, wxRichTextRange(range
.GetStart(), breakPosition
), false))
5787 int newLinePos
= plainText
.Find(wxRichTextLineBreakChar
);
5788 if (newLinePos
!= wxNOT_FOUND
)
5790 breakPosition
= wxMax(0, range
.GetStart() + newLinePos
);
5794 int spacePos
= plainText
.Find(wxT(' '), true);
5795 int tabPos
= plainText
.Find(wxT('\t'), true);
5796 int pos
= wxMax(spacePos
, tabPos
);
5797 if (pos
!= wxNOT_FOUND
)
5799 int positionsFromEndOfString
= plainText
.length() - pos
- 1;
5800 breakPosition
= breakPosition
- positionsFromEndOfString
;
5805 wrapPosition
= breakPosition
;
5810 /// Get the bullet text for this paragraph.
5811 wxString
wxRichTextParagraph::GetBulletText()
5813 if (GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
||
5814 (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
))
5815 return wxEmptyString
;
5817 int number
= GetAttributes().GetBulletNumber();
5820 if ((GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ARABIC
) || (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
))
5822 text
.Printf(wxT("%d"), number
);
5824 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_UPPER
)
5826 // TODO: Unicode, and also check if number > 26
5827 text
.Printf(wxT("%c"), (wxChar
) (number
+64));
5829 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_LOWER
)
5831 // TODO: Unicode, and also check if number > 26
5832 text
.Printf(wxT("%c"), (wxChar
) (number
+96));
5834 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER
)
5836 text
= wxRichTextDecimalToRoman(number
);
5838 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER
)
5840 text
= wxRichTextDecimalToRoman(number
);
5843 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
)
5845 text
= GetAttributes().GetBulletText();
5848 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
)
5850 // The outline style relies on the text being computed statically,
5851 // since it depends on other levels points (e.g. 1.2.1.1). So normally the bullet text
5852 // should be stored in the attributes; if not, just use the number for this
5853 // level, as previously computed.
5854 if (!GetAttributes().GetBulletText().IsEmpty())
5855 text
= GetAttributes().GetBulletText();
5858 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PARENTHESES
)
5860 text
= wxT("(") + text
+ wxT(")");
5862 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_RIGHT_PARENTHESIS
)
5864 text
= text
+ wxT(")");
5867 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PERIOD
)
5875 /// Allocate or reuse a line object
5876 wxRichTextLine
* wxRichTextParagraph::AllocateLine(int pos
)
5878 if (pos
< (int) m_cachedLines
.GetCount())
5880 wxRichTextLine
* line
= m_cachedLines
.Item(pos
)->GetData();
5886 wxRichTextLine
* line
= new wxRichTextLine(this);
5887 m_cachedLines
.Append(line
);
5892 /// Clear remaining unused line objects, if any
5893 bool wxRichTextParagraph::ClearUnusedLines(int lineCount
)
5895 int cachedLineCount
= m_cachedLines
.GetCount();
5896 if ((int) cachedLineCount
> lineCount
)
5898 for (int i
= 0; i
< (int) (cachedLineCount
- lineCount
); i
++)
5900 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetLast();
5901 wxRichTextLine
* line
= node
->GetData();
5902 m_cachedLines
.Erase(node
);
5909 /// Get combined attributes of the base style, paragraph style and character style. We use this to dynamically
5910 /// retrieve the actual style.
5911 wxRichTextAttr
wxRichTextParagraph::GetCombinedAttributes(const wxRichTextAttr
& contentStyle
, bool includingBoxAttr
) const
5913 wxRichTextAttr attr
;
5914 wxRichTextParagraphLayoutBox
* buf
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
5917 attr
= buf
->GetBasicStyle();
5918 if (!includingBoxAttr
)
5920 attr
.GetTextBoxAttr().Reset();
5921 // The background colour will be painted by the container, and we don't
5922 // want to unnecessarily overwrite the background when we're drawing text
5923 // because this may erase the guideline (which appears just under the text
5924 // if there's no padding).
5925 attr
.SetFlags(attr
.GetFlags() & ~wxTEXT_ATTR_BACKGROUND_COLOUR
);
5927 wxRichTextApplyStyle(attr
, GetAttributes());
5930 attr
= GetAttributes();
5932 wxRichTextApplyStyle(attr
, contentStyle
);
5936 /// Get combined attributes of the base style and paragraph style.
5937 wxRichTextAttr
wxRichTextParagraph::GetCombinedAttributes(bool includingBoxAttr
) const
5939 wxRichTextAttr attr
;
5940 wxRichTextParagraphLayoutBox
* buf
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
5943 attr
= buf
->GetBasicStyle();
5944 if (!includingBoxAttr
)
5945 attr
.GetTextBoxAttr().Reset();
5946 wxRichTextApplyStyle(attr
, GetAttributes());
5949 attr
= GetAttributes();
5954 // Create default tabstop array
5955 void wxRichTextParagraph::InitDefaultTabs()
5957 // create a default tab list at 10 mm each.
5958 for (int i
= 0; i
< 20; ++i
)
5960 sm_defaultTabs
.Add(i
*100);
5964 // Clear default tabstop array
5965 void wxRichTextParagraph::ClearDefaultTabs()
5967 sm_defaultTabs
.Clear();
5970 void wxRichTextParagraph::LayoutFloat(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, int style
, wxRichTextFloatCollector
* floatCollector
)
5972 wxRichTextObjectList::compatibility_iterator node
= GetChildren().GetFirst();
5975 wxRichTextObject
* anchored
= node
->GetData();
5976 if (anchored
&& anchored
->IsFloating() && !floatCollector
->HasFloat(anchored
))
5980 anchored
->GetRangeSize(anchored
->GetRange(), size
, descent
, dc
, context
, style
);
5983 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().IsValid())
5985 offsetY
= anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetValue();
5986 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
5988 offsetY
= ConvertTenthsMMToPixels(dc
, offsetY
);
5992 int pos
= floatCollector
->GetFitPosition(anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode(), rect
.y
+ offsetY
, size
.y
);
5994 /* Update the offset */
5995 int newOffsetY
= pos
- rect
.y
;
5996 if (newOffsetY
!= offsetY
)
5998 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
5999 newOffsetY
= ConvertPixelsToTenthsMM(dc
, newOffsetY
);
6000 anchored
->GetAttributes().GetTextBoxAttr().GetTop().SetValue(newOffsetY
);
6003 if (anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT
)
6005 else if (anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT
)
6006 x
= rect
.x
+ rect
.width
- size
.x
;
6008 anchored
->SetPosition(wxPoint(x
, pos
));
6009 anchored
->SetCachedSize(size
);
6010 floatCollector
->CollectFloat(this, anchored
);
6013 node
= node
->GetNext();
6017 // Get the first position from pos that has a line break character.
6018 long wxRichTextParagraph::GetFirstLineBreakPosition(long pos
)
6020 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
6023 wxRichTextObject
* obj
= node
->GetData();
6024 if (pos
>= obj
->GetRange().GetStart() && pos
<= obj
->GetRange().GetEnd())
6026 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
6029 long breakPos
= textObj
->GetFirstLineBreakPosition(pos
);
6034 node
= node
->GetNext();
6041 * This object represents a line in a paragraph, and stores
6042 * offsets from the start of the paragraph representing the
6043 * start and end positions of the line.
6046 wxRichTextLine::wxRichTextLine(wxRichTextParagraph
* parent
)
6052 void wxRichTextLine::Init(wxRichTextParagraph
* parent
)
6055 m_range
.SetRange(-1, -1);
6056 m_pos
= wxPoint(0, 0);
6057 m_size
= wxSize(0, 0);
6059 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6060 m_objectSizes
.Clear();
6065 void wxRichTextLine::Copy(const wxRichTextLine
& obj
)
6067 m_range
= obj
.m_range
;
6068 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6069 m_objectSizes
= obj
.m_objectSizes
;
6073 /// Get the absolute object position
6074 wxPoint
wxRichTextLine::GetAbsolutePosition() const
6076 return m_parent
->GetPosition() + m_pos
;
6079 /// Get the absolute range
6080 wxRichTextRange
wxRichTextLine::GetAbsoluteRange() const
6082 wxRichTextRange
range(m_range
.GetStart() + m_parent
->GetRange().GetStart(), 0);
6083 range
.SetEnd(range
.GetStart() + m_range
.GetLength()-1);
6088 * wxRichTextPlainText
6089 * This object represents a single piece of text.
6092 IMPLEMENT_DYNAMIC_CLASS(wxRichTextPlainText
, wxRichTextObject
)
6094 wxRichTextPlainText::wxRichTextPlainText(const wxString
& text
, wxRichTextObject
* parent
, wxRichTextAttr
* style
):
6095 wxRichTextObject(parent
)
6098 SetAttributes(*style
);
6103 #define USE_KERNING_FIX 1
6105 // If insufficient tabs are defined, this is the tab width used
6106 #define WIDTH_FOR_DEFAULT_TABS 50
6109 bool wxRichTextPlainText::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int WXUNUSED(style
))
6111 wxRichTextParagraph
* para
= wxDynamicCast(GetParent(), wxRichTextParagraph
);
6112 wxASSERT (para
!= NULL
);
6114 wxRichTextAttr
textAttr(para
? para
->GetCombinedAttributes(GetAttributes(), false /* no box attributes */) : GetAttributes());
6115 context
.ApplyVirtualAttributes(textAttr
, this);
6117 // Let's make the assumption for now that for content in a paragraph, including
6118 // text, we never have a discontinuous selection. So we only deal with a
6120 wxRichTextRange selectionRange
;
6121 if (selection
.IsValid())
6123 wxRichTextRangeArray selectionRanges
= selection
.GetSelectionForObject(this);
6124 if (selectionRanges
.GetCount() > 0)
6125 selectionRange
= selectionRanges
[0];
6127 selectionRange
= wxRICHTEXT_NO_SELECTION
;
6130 selectionRange
= wxRICHTEXT_NO_SELECTION
;
6132 int offset
= GetRange().GetStart();
6134 // Replace line break characters with spaces
6135 wxString str
= m_text
;
6136 wxString toRemove
= wxRichTextLineBreakChar
;
6137 str
.Replace(toRemove
, wxT(" "));
6138 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS
))
6141 long len
= range
.GetLength();
6142 wxString stringChunk
= str
.Mid(range
.GetStart() - offset
, (size_t) len
);
6144 // Test for the optimized situations where all is selected, or none
6147 wxFont
textFont(GetBuffer()->GetFontTable().FindFont(textAttr
));
6148 wxCheckSetFont(dc
, textFont
);
6149 int charHeight
= dc
.GetCharHeight();
6152 if ( textFont
.IsOk() )
6154 if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
) )
6156 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6157 textFont
.SetPointSize( static_cast<int>(size
) );
6160 wxCheckSetFont(dc
, textFont
);
6162 else if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) )
6164 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6165 textFont
.SetPointSize( static_cast<int>(size
) );
6167 int sub_height
= static_cast<int>( static_cast<double>(charHeight
) / wxSCRIPT_MUL_FACTOR
);
6168 y
= rect
.y
+ (rect
.height
- sub_height
+ (descent
- m_descent
));
6169 wxCheckSetFont(dc
, textFont
);
6174 y
= rect
.y
+ (rect
.height
- charHeight
- (descent
- m_descent
));
6180 y
= rect
.y
+ (rect
.height
- charHeight
- (descent
- m_descent
));
6183 // TODO: new selection code
6185 // (a) All selected.
6186 if (selectionRange
.GetStart() <= range
.GetStart() && selectionRange
.GetEnd() >= range
.GetEnd())
6188 DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, true);
6190 // (b) None selected.
6191 else if (selectionRange
.GetEnd() < range
.GetStart() || selectionRange
.GetStart() > range
.GetEnd())
6193 // Draw all unselected
6194 DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, false);
6198 // (c) Part selected, part not
6199 // Let's draw unselected chunk, selected chunk, then unselected chunk.
6201 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6203 // 1. Initial unselected chunk, if any, up until start of selection.
6204 if (selectionRange
.GetStart() > range
.GetStart() && selectionRange
.GetStart() <= range
.GetEnd())
6206 int r1
= range
.GetStart();
6207 int s1
= selectionRange
.GetStart()-1;
6208 int fragmentLen
= s1
- r1
+ 1;
6209 if (fragmentLen
< 0)
6211 wxLogDebug(wxT("Mid(%d, %d"), (int)(r1
- offset
), (int)fragmentLen
);
6213 wxString stringFragment
= str
.Mid(r1
- offset
, fragmentLen
);
6215 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false);
6218 if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
)
6220 // Compensate for kerning difference
6221 wxString
stringFragment2(str
.Mid(r1
- offset
, fragmentLen
+1));
6222 wxString
stringFragment3(str
.Mid(r1
- offset
+ fragmentLen
, 1));
6224 wxCoord w1
, h1
, w2
, h2
, w3
, h3
;
6225 dc
.GetTextExtent(stringFragment
, & w1
, & h1
);
6226 dc
.GetTextExtent(stringFragment2
, & w2
, & h2
);
6227 dc
.GetTextExtent(stringFragment3
, & w3
, & h3
);
6229 int kerningDiff
= (w1
+ w3
) - w2
;
6230 x
= x
- kerningDiff
;
6235 // 2. Selected chunk, if any.
6236 if (selectionRange
.GetEnd() >= range
.GetStart())
6238 int s1
= wxMax(selectionRange
.GetStart(), range
.GetStart());
6239 int s2
= wxMin(selectionRange
.GetEnd(), range
.GetEnd());
6241 int fragmentLen
= s2
- s1
+ 1;
6242 if (fragmentLen
< 0)
6244 wxLogDebug(wxT("Mid(%d, %d"), (int)(s1
- offset
), (int)fragmentLen
);
6246 wxString stringFragment
= str
.Mid(s1
- offset
, fragmentLen
);
6248 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, true);
6251 if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
)
6253 // Compensate for kerning difference
6254 wxString
stringFragment2(str
.Mid(s1
- offset
, fragmentLen
+1));
6255 wxString
stringFragment3(str
.Mid(s1
- offset
+ fragmentLen
, 1));
6257 wxCoord w1
, h1
, w2
, h2
, w3
, h3
;
6258 dc
.GetTextExtent(stringFragment
, & w1
, & h1
);
6259 dc
.GetTextExtent(stringFragment2
, & w2
, & h2
);
6260 dc
.GetTextExtent(stringFragment3
, & w3
, & h3
);
6262 int kerningDiff
= (w1
+ w3
) - w2
;
6263 x
= x
- kerningDiff
;
6268 // 3. Remaining unselected chunk, if any
6269 if (selectionRange
.GetEnd() < range
.GetEnd())
6271 int s2
= wxMin(selectionRange
.GetEnd()+1, range
.GetEnd());
6272 int r2
= range
.GetEnd();
6274 int fragmentLen
= r2
- s2
+ 1;
6275 if (fragmentLen
< 0)
6277 wxLogDebug(wxT("Mid(%d, %d"), (int)(s2
- offset
), (int)fragmentLen
);
6279 wxString stringFragment
= str
.Mid(s2
- offset
, fragmentLen
);
6281 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false);
6288 bool wxRichTextPlainText::DrawTabbedString(wxDC
& dc
, const wxRichTextAttr
& attr
, const wxRect
& rect
,wxString
& str
, wxCoord
& x
, wxCoord
& y
, bool selected
)
6290 bool hasTabs
= (str
.Find(wxT('\t')) != wxNOT_FOUND
);
6292 wxArrayInt tabArray
;
6296 if (attr
.GetTabs().IsEmpty())
6297 tabArray
= wxRichTextParagraph::GetDefaultTabs();
6299 tabArray
= attr
.GetTabs();
6300 tabCount
= tabArray
.GetCount();
6302 for (int i
= 0; i
< tabCount
; ++i
)
6304 int pos
= tabArray
[i
];
6305 pos
= ConvertTenthsMMToPixels(dc
, pos
);
6312 int nextTabPos
= -1;
6318 wxColour
highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
));
6319 wxColour
highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
));
6321 wxCheckSetBrush(dc
, wxBrush(highlightColour
));
6322 wxCheckSetPen(dc
, wxPen(highlightColour
));
6323 dc
.SetTextForeground(highlightTextColour
);
6324 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6328 dc
.SetTextForeground(attr
.GetTextColour());
6330 if (attr
.HasFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
) && attr
.GetBackgroundColour().IsOk())
6332 dc
.SetBackgroundMode(wxBRUSHSTYLE_SOLID
);
6333 dc
.SetTextBackground(attr
.GetBackgroundColour());
6336 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6339 wxCoord x_orig
= GetParent()->GetPosition().x
;
6342 // the string has a tab
6343 // break up the string at the Tab
6344 wxString stringChunk
= str
.BeforeFirst(wxT('\t'));
6345 str
= str
.AfterFirst(wxT('\t'));
6346 dc
.GetTextExtent(stringChunk
, & w
, & h
);
6348 bool not_found
= true;
6349 for (int i
= 0; i
< tabCount
&& not_found
; ++i
)
6351 nextTabPos
= tabArray
.Item(i
) + x_orig
;
6353 // Find the next tab position.
6354 // Even if we're at the end of the tab array, we must still draw the chunk.
6356 if (nextTabPos
> tabPos
|| (i
== (tabCount
- 1)))
6358 if (nextTabPos
<= tabPos
)
6360 int defaultTabWidth
= ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
);
6361 nextTabPos
= tabPos
+ defaultTabWidth
;
6368 wxRect
selRect(x
, rect
.y
, w
, rect
.GetHeight());
6369 dc
.DrawRectangle(selRect
);
6371 dc
.DrawText(stringChunk
, x
, y
);
6373 if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
))
6375 wxPen oldPen
= dc
.GetPen();
6376 wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1));
6377 dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5));
6378 wxCheckSetPen(dc
, oldPen
);
6384 hasTabs
= (str
.Find(wxT('\t')) != wxNOT_FOUND
);
6389 dc
.GetTextExtent(str
, & w
, & h
);
6392 wxRect
selRect(x
, rect
.y
, w
, rect
.GetHeight());
6393 dc
.DrawRectangle(selRect
);
6395 dc
.DrawText(str
, x
, y
);
6397 if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
))
6399 wxPen oldPen
= dc
.GetPen();
6400 wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1));
6401 dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5));
6402 wxCheckSetPen(dc
, oldPen
);
6411 /// Lay the item out
6412 bool wxRichTextPlainText::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& WXUNUSED(rect
), const wxRect
& WXUNUSED(parentRect
), int WXUNUSED(style
))
6414 // Only lay out if we haven't already cached the size
6416 GetRangeSize(GetRange(), m_size
, m_descent
, dc
, context
, 0, wxPoint(0, 0));
6418 // Eventually we want to have a reasonable estimate of minimum size.
6419 m_minSize
= wxSize(0, 0);
6424 void wxRichTextPlainText::Copy(const wxRichTextPlainText
& obj
)
6426 wxRichTextObject::Copy(obj
);
6428 m_text
= obj
.m_text
;
6431 /// Get/set the object size for the given range. Returns false if the range
6432 /// is invalid for this object.
6433 bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int WXUNUSED(flags
), wxPoint position
, wxArrayInt
* partialExtents
) const
6435 if (!range
.IsWithin(GetRange()))
6438 wxRichTextParagraph
* para
= wxDynamicCast(GetParent(), wxRichTextParagraph
);
6439 wxASSERT (para
!= NULL
);
6441 int relativeX
= position
.x
- GetParent()->GetPosition().x
;
6443 wxRichTextAttr
textAttr(para
? para
->GetCombinedAttributes(GetAttributes()) : GetAttributes());
6444 context
.ApplyVirtualAttributes(textAttr
, (wxRichTextObject
*) this);
6446 // Always assume unformatted text, since at this level we have no knowledge
6447 // of line breaks - and we don't need it, since we'll calculate size within
6448 // formatted text by doing it in chunks according to the line ranges
6450 bool bScript(false);
6451 wxFont
font(GetBuffer()->GetFontTable().FindFont(textAttr
));
6454 if ( textAttr
.HasTextEffects() && ( (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
)
6455 || (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) ) )
6457 wxFont textFont
= font
;
6458 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6459 textFont
.SetPointSize( static_cast<int>(size
) );
6460 wxCheckSetFont(dc
, textFont
);
6465 wxCheckSetFont(dc
, font
);
6469 bool haveDescent
= false;
6470 int startPos
= range
.GetStart() - GetRange().GetStart();
6471 long len
= range
.GetLength();
6473 wxString
str(m_text
);
6474 wxString toReplace
= wxRichTextLineBreakChar
;
6475 str
.Replace(toReplace
, wxT(" "));
6477 wxString stringChunk
= str
.Mid(startPos
, (size_t) len
);
6479 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS
))
6480 stringChunk
.MakeUpper();
6484 if (stringChunk
.Find(wxT('\t')) != wxNOT_FOUND
)
6486 // the string has a tab
6487 wxArrayInt tabArray
;
6488 if (textAttr
.GetTabs().IsEmpty())
6489 tabArray
= wxRichTextParagraph::GetDefaultTabs();
6491 tabArray
= textAttr
.GetTabs();
6493 int tabCount
= tabArray
.GetCount();
6495 for (int i
= 0; i
< tabCount
; ++i
)
6497 int pos
= tabArray
[i
];
6498 pos
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, pos
);
6502 int nextTabPos
= -1;
6504 while (stringChunk
.Find(wxT('\t')) >= 0)
6506 int absoluteWidth
= 0;
6508 // the string has a tab
6509 // break up the string at the Tab
6510 wxString stringFragment
= stringChunk
.BeforeFirst(wxT('\t'));
6511 stringChunk
= stringChunk
.AfterFirst(wxT('\t'));
6516 if (partialExtents
->GetCount() > 0)
6517 oldWidth
= (*partialExtents
)[partialExtents
->GetCount()-1];
6521 // Add these partial extents
6523 dc
.GetPartialTextExtents(stringFragment
, p
);
6525 for (j
= 0; j
< p
.GetCount(); j
++)
6526 partialExtents
->Add(oldWidth
+ p
[j
]);
6528 if (partialExtents
->GetCount() > 0)
6529 absoluteWidth
= (*partialExtents
)[(*partialExtents
).GetCount()-1] + relativeX
;
6531 absoluteWidth
= relativeX
;
6535 dc
.GetTextExtent(stringFragment
, & w
, & h
);
6537 absoluteWidth
= width
+ relativeX
;
6541 bool notFound
= true;
6542 for (int i
= 0; i
< tabCount
&& notFound
; ++i
)
6544 nextTabPos
= tabArray
.Item(i
);
6546 // Find the next tab position.
6547 // Even if we're at the end of the tab array, we must still process the chunk.
6549 if (nextTabPos
> absoluteWidth
|| (i
== (tabCount
- 1)))
6551 if (nextTabPos
<= absoluteWidth
)
6553 int defaultTabWidth
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
);
6554 nextTabPos
= absoluteWidth
+ defaultTabWidth
;
6558 width
= nextTabPos
- relativeX
;
6561 partialExtents
->Add(width
);
6567 if (!stringChunk
.IsEmpty())
6572 if (partialExtents
->GetCount() > 0)
6573 oldWidth
= (*partialExtents
)[partialExtents
->GetCount()-1];
6577 // Add these partial extents
6579 dc
.GetPartialTextExtents(stringChunk
, p
);
6581 for (j
= 0; j
< p
.GetCount(); j
++)
6582 partialExtents
->Add(oldWidth
+ p
[j
]);
6586 dc
.GetTextExtent(stringChunk
, & w
, & h
, & descent
);
6594 int charHeight
= dc
.GetCharHeight();
6595 if ((*partialExtents
).GetCount() > 0)
6596 w
= (*partialExtents
)[partialExtents
->GetCount()-1];
6599 size
= wxSize(w
, charHeight
);
6603 size
= wxSize(width
, dc
.GetCharHeight());
6607 dc
.GetTextExtent(wxT("X"), & w
, & h
, & descent
);
6615 /// Do a split, returning an object containing the second part, and setting
6616 /// the first part in 'this'.
6617 wxRichTextObject
* wxRichTextPlainText::DoSplit(long pos
)
6619 long index
= pos
- GetRange().GetStart();
6621 if (index
< 0 || index
>= (int) m_text
.length())
6624 wxString firstPart
= m_text
.Mid(0, index
);
6625 wxString secondPart
= m_text
.Mid(index
);
6629 wxRichTextPlainText
* newObject
= new wxRichTextPlainText(secondPart
);
6630 newObject
->SetAttributes(GetAttributes());
6631 newObject
->SetProperties(GetProperties());
6633 newObject
->SetRange(wxRichTextRange(pos
, GetRange().GetEnd()));
6634 GetRange().SetEnd(pos
-1);
6640 void wxRichTextPlainText::CalculateRange(long start
, long& end
)
6642 end
= start
+ m_text
.length() - 1;
6643 m_range
.SetRange(start
, end
);
6647 bool wxRichTextPlainText::DeleteRange(const wxRichTextRange
& range
)
6649 wxRichTextRange r
= range
;
6651 r
.LimitTo(GetRange());
6653 if (r
.GetStart() == GetRange().GetStart() && r
.GetEnd() == GetRange().GetEnd())
6659 long startIndex
= r
.GetStart() - GetRange().GetStart();
6660 long len
= r
.GetLength();
6662 m_text
= m_text
.Mid(0, startIndex
) + m_text
.Mid(startIndex
+len
);
6666 /// Get text for the given range.
6667 wxString
wxRichTextPlainText::GetTextForRange(const wxRichTextRange
& range
) const
6669 wxRichTextRange r
= range
;
6671 r
.LimitTo(GetRange());
6673 long startIndex
= r
.GetStart() - GetRange().GetStart();
6674 long len
= r
.GetLength();
6676 return m_text
.Mid(startIndex
, len
);
6679 /// Returns true if this object can merge itself with the given one.
6680 bool wxRichTextPlainText::CanMerge(wxRichTextObject
* object
) const
6682 return object
->GetClassInfo() == CLASSINFO(wxRichTextPlainText
) &&
6683 (m_text
.empty() || (wxTextAttrEq(GetAttributes(), object
->GetAttributes()) && m_properties
== object
->GetProperties()));
6686 /// Returns true if this object merged itself with the given one.
6687 /// The calling code will then delete the given object.
6688 bool wxRichTextPlainText::Merge(wxRichTextObject
* object
)
6690 wxRichTextPlainText
* textObject
= wxDynamicCast(object
, wxRichTextPlainText
);
6691 wxASSERT( textObject
!= NULL
);
6695 m_text
+= textObject
->GetText();
6696 wxRichTextApplyStyle(m_attributes
, textObject
->GetAttributes());
6703 /// Dump to output stream for debugging
6704 void wxRichTextPlainText::Dump(wxTextOutputStream
& stream
)
6706 wxRichTextObject::Dump(stream
);
6707 stream
<< m_text
<< wxT("\n");
6710 /// Get the first position from pos that has a line break character.
6711 long wxRichTextPlainText::GetFirstLineBreakPosition(long pos
)
6714 int len
= m_text
.length();
6715 int startPos
= pos
- m_range
.GetStart();
6716 for (i
= startPos
; i
< len
; i
++)
6718 wxChar ch
= m_text
[i
];
6719 if (ch
== wxRichTextLineBreakChar
)
6721 return i
+ m_range
.GetStart();
6729 * This is a kind of box, used to represent the whole buffer
6732 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBuffer
, wxRichTextParagraphLayoutBox
)
6734 wxList
wxRichTextBuffer::sm_handlers
;
6735 wxList
wxRichTextBuffer::sm_drawingHandlers
;
6736 wxRichTextRenderer
* wxRichTextBuffer::sm_renderer
= NULL
;
6737 int wxRichTextBuffer::sm_bulletRightMargin
= 20;
6738 float wxRichTextBuffer::sm_bulletProportion
= (float) 0.3;
6741 void wxRichTextBuffer::Init()
6743 m_commandProcessor
= new wxCommandProcessor
;
6744 m_styleSheet
= NULL
;
6746 m_batchedCommandDepth
= 0;
6747 m_batchedCommand
= NULL
;
6754 wxRichTextBuffer::~wxRichTextBuffer()
6756 delete m_commandProcessor
;
6757 delete m_batchedCommand
;
6760 ClearEventHandlers();
6763 void wxRichTextBuffer::ResetAndClearCommands()
6767 GetCommandProcessor()->ClearCommands();
6770 Invalidate(wxRICHTEXT_ALL
);
6773 void wxRichTextBuffer::Copy(const wxRichTextBuffer
& obj
)
6775 wxRichTextParagraphLayoutBox::Copy(obj
);
6777 m_styleSheet
= obj
.m_styleSheet
;
6778 m_modified
= obj
.m_modified
;
6779 m_batchedCommandDepth
= 0;
6780 if (m_batchedCommand
)
6781 delete m_batchedCommand
;
6782 m_batchedCommand
= NULL
;
6783 m_suppressUndo
= obj
.m_suppressUndo
;
6784 m_invalidRange
= obj
.m_invalidRange
;
6787 /// Push style sheet to top of stack
6788 bool wxRichTextBuffer::PushStyleSheet(wxRichTextStyleSheet
* styleSheet
)
6791 styleSheet
->InsertSheet(m_styleSheet
);
6793 SetStyleSheet(styleSheet
);
6798 /// Pop style sheet from top of stack
6799 wxRichTextStyleSheet
* wxRichTextBuffer::PopStyleSheet()
6803 wxRichTextStyleSheet
* oldSheet
= m_styleSheet
;
6804 m_styleSheet
= oldSheet
->GetNextSheet();
6813 /// Submit command to insert paragraphs
6814 bool wxRichTextBuffer::InsertParagraphsWithUndo(long pos
, const wxRichTextParagraphLayoutBox
& paragraphs
, wxRichTextCtrl
* ctrl
, int flags
)
6816 return ctrl
->GetFocusObject()->InsertParagraphsWithUndo(this, pos
, paragraphs
, ctrl
, flags
);
6819 /// Submit command to insert paragraphs
6820 bool wxRichTextParagraphLayoutBox::InsertParagraphsWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxRichTextParagraphLayoutBox
& paragraphs
, wxRichTextCtrl
* ctrl
, int WXUNUSED(flags
))
6822 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6824 action
->GetNewParagraphs() = paragraphs
;
6826 action
->SetPosition(pos
);
6828 wxRichTextRange range
= wxRichTextRange(pos
, pos
+ paragraphs
.GetOwnRange().GetEnd() - 1);
6829 if (!paragraphs
.GetPartialParagraph())
6830 range
.SetEnd(range
.GetEnd()+1);
6832 // Set the range we'll need to delete in Undo
6833 action
->SetRange(range
);
6835 buffer
->SubmitAction(action
);
6840 /// Submit command to insert the given text
6841 bool wxRichTextBuffer::InsertTextWithUndo(long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
, int flags
)
6843 return ctrl
->GetFocusObject()->InsertTextWithUndo(this, pos
, text
, ctrl
, flags
);
6846 /// Submit command to insert the given text
6847 bool wxRichTextParagraphLayoutBox::InsertTextWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
, int flags
)
6849 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6851 wxRichTextAttr
* p
= NULL
;
6852 wxRichTextAttr paraAttr
;
6853 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
6855 // Get appropriate paragraph style
6856 paraAttr
= GetStyleForNewParagraph(buffer
, pos
, false, false);
6857 if (!paraAttr
.IsDefault())
6861 action
->GetNewParagraphs().AddParagraphs(text
, p
);
6863 int length
= action
->GetNewParagraphs().GetOwnRange().GetLength();
6865 if (!text
.empty() && text
.Last() != wxT('\n'))
6867 // Don't count the newline when undoing
6869 action
->GetNewParagraphs().SetPartialParagraph(true);
6871 else if (!text
.empty() && text
.Last() == wxT('\n'))
6874 action
->SetPosition(pos
);
6876 // Set the range we'll need to delete in Undo
6877 action
->SetRange(wxRichTextRange(pos
, pos
+ length
- 1));
6879 buffer
->SubmitAction(action
);
6884 /// Submit command to insert the given text
6885 bool wxRichTextBuffer::InsertNewlineWithUndo(long pos
, wxRichTextCtrl
* ctrl
, int flags
)
6887 return ctrl
->GetFocusObject()->InsertNewlineWithUndo(this, pos
, ctrl
, flags
);
6890 /// Submit command to insert the given text
6891 bool wxRichTextParagraphLayoutBox::InsertNewlineWithUndo(wxRichTextBuffer
* buffer
, long pos
, wxRichTextCtrl
* ctrl
, int flags
)
6893 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6895 wxRichTextAttr
* p
= NULL
;
6896 wxRichTextAttr paraAttr
;
6897 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
6899 paraAttr
= GetStyleForNewParagraph(buffer
, pos
, false, true /* look for next paragraph style */);
6900 if (!paraAttr
.IsDefault())
6904 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
6906 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(wxEmptyString
, this, & attr
);
6907 action
->GetNewParagraphs().AppendChild(newPara
);
6908 action
->GetNewParagraphs().UpdateRanges();
6909 action
->GetNewParagraphs().SetPartialParagraph(false);
6910 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
, false);
6914 newPara
->SetAttributes(*p
);
6916 if (flags
& wxRICHTEXT_INSERT_INTERACTIVE
)
6918 if (para
&& para
->GetRange().GetEnd() == pos
)
6921 // Now see if we need to number the paragraph.
6922 if (newPara
->GetAttributes().HasBulletNumber())
6924 wxRichTextAttr numberingAttr
;
6925 if (FindNextParagraphNumber(para
, numberingAttr
))
6926 wxRichTextApplyStyle(newPara
->GetAttributes(), (const wxRichTextAttr
&) numberingAttr
);
6930 action
->SetPosition(pos
);
6932 // Use the default character style
6933 // Use the default character style
6934 if (!buffer
->GetDefaultStyle().IsDefault() && newPara
->GetChildren().GetFirst())
6936 // Check whether the default style merely reflects the paragraph/basic style,
6937 // in which case don't apply it.
6938 wxRichTextAttr
defaultStyle(buffer
->GetDefaultStyle());
6939 wxRichTextAttr toApply
;
6942 wxRichTextAttr combinedAttr
= para
->GetCombinedAttributes(true /* include box attributes */);
6943 wxRichTextAttr newAttr
;
6944 // This filters out attributes that are accounted for by the current
6945 // paragraph/basic style
6946 wxRichTextApplyStyle(toApply
, defaultStyle
, & combinedAttr
);
6949 toApply
= defaultStyle
;
6951 if (!toApply
.IsDefault())
6952 newPara
->GetChildren().GetFirst()->GetData()->SetAttributes(toApply
);
6955 // Set the range we'll need to delete in Undo
6956 action
->SetRange(wxRichTextRange(pos1
, pos1
));
6958 buffer
->SubmitAction(action
);
6963 /// Submit command to insert the given image
6964 bool wxRichTextBuffer::InsertImageWithUndo(long pos
, const wxRichTextImageBlock
& imageBlock
, wxRichTextCtrl
* ctrl
, int flags
,
6965 const wxRichTextAttr
& textAttr
)
6967 return ctrl
->GetFocusObject()->InsertImageWithUndo(this, pos
, imageBlock
, ctrl
, flags
, textAttr
);
6970 /// Submit command to insert the given image
6971 bool wxRichTextParagraphLayoutBox::InsertImageWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxRichTextImageBlock
& imageBlock
,
6972 wxRichTextCtrl
* ctrl
, int flags
,
6973 const wxRichTextAttr
& textAttr
)
6975 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6977 wxRichTextAttr
* p
= NULL
;
6978 wxRichTextAttr paraAttr
;
6979 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
6981 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
6982 if (!paraAttr
.IsDefault())
6986 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
6988 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
6990 newPara
->SetAttributes(*p
);
6992 wxRichTextImage
* imageObject
= new wxRichTextImage(imageBlock
, newPara
);
6993 newPara
->AppendChild(imageObject
);
6994 imageObject
->SetAttributes(textAttr
);
6995 action
->GetNewParagraphs().AppendChild(newPara
);
6996 action
->GetNewParagraphs().UpdateRanges();
6998 action
->GetNewParagraphs().SetPartialParagraph(true);
7000 action
->SetPosition(pos
);
7002 // Set the range we'll need to delete in Undo
7003 action
->SetRange(wxRichTextRange(pos
, pos
));
7005 buffer
->SubmitAction(action
);
7010 // Insert an object with no change of it
7011 wxRichTextObject
* wxRichTextBuffer::InsertObjectWithUndo(long pos
, wxRichTextObject
*object
, wxRichTextCtrl
* ctrl
, int flags
)
7013 return ctrl
->GetFocusObject()->InsertObjectWithUndo(this, pos
, object
, ctrl
, flags
);
7016 // Insert an object with no change of it
7017 wxRichTextObject
* wxRichTextParagraphLayoutBox::InsertObjectWithUndo(wxRichTextBuffer
* buffer
, long pos
, wxRichTextObject
*object
, wxRichTextCtrl
* ctrl
, int flags
)
7019 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Object"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7021 wxRichTextAttr
* p
= NULL
;
7022 wxRichTextAttr paraAttr
;
7023 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7025 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
7026 if (!paraAttr
.IsDefault())
7030 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7032 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7034 newPara
->SetAttributes(*p
);
7036 newPara
->AppendChild(object
);
7037 action
->GetNewParagraphs().AppendChild(newPara
);
7038 action
->GetNewParagraphs().UpdateRanges();
7040 action
->GetNewParagraphs().SetPartialParagraph(true);
7042 action
->SetPosition(pos
);
7044 // Set the range we'll need to delete in Undo
7045 action
->SetRange(wxRichTextRange(pos
, pos
));
7047 buffer
->SubmitAction(action
);
7049 wxRichTextObject
* obj
= GetLeafObjectAtPosition(pos
);
7053 /// Get the style that is appropriate for a new paragraph at this position.
7054 /// If the previous paragraph has a paragraph style name, look up the next-paragraph
7056 wxRichTextAttr
wxRichTextParagraphLayoutBox::GetStyleForNewParagraph(wxRichTextBuffer
* buffer
, long pos
, bool caretPosition
, bool lookUpNewParaStyle
) const
7058 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
, caretPosition
);
7061 wxRichTextAttr attr
;
7062 bool foundAttributes
= false;
7064 // Look for a matching paragraph style
7065 if (lookUpNewParaStyle
&& !para
->GetAttributes().GetParagraphStyleName().IsEmpty() && buffer
->GetStyleSheet())
7067 wxRichTextParagraphStyleDefinition
* paraDef
= buffer
->GetStyleSheet()->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
7070 // If we're not at the end of the paragraph, then we apply THIS style, and not the designated next style.
7071 if (para
->GetRange().GetEnd() == pos
&& !paraDef
->GetNextStyle().IsEmpty())
7073 wxRichTextParagraphStyleDefinition
* nextParaDef
= buffer
->GetStyleSheet()->FindParagraphStyle(paraDef
->GetNextStyle());
7076 foundAttributes
= true;
7077 attr
= nextParaDef
->GetStyleMergedWithBase(buffer
->GetStyleSheet());
7081 // If we didn't find the 'next style', use this style instead.
7082 if (!foundAttributes
)
7084 foundAttributes
= true;
7085 attr
= paraDef
->GetStyleMergedWithBase(buffer
->GetStyleSheet());
7090 // Also apply list style if present
7091 if (lookUpNewParaStyle
&& !para
->GetAttributes().GetListStyleName().IsEmpty() && buffer
->GetStyleSheet())
7093 wxRichTextListStyleDefinition
* listDef
= buffer
->GetStyleSheet()->FindListStyle(para
->GetAttributes().GetListStyleName());
7096 int thisIndent
= para
->GetAttributes().GetLeftIndent();
7097 int thisLevel
= para
->GetAttributes().HasOutlineLevel() ? para
->GetAttributes().GetOutlineLevel() : listDef
->FindLevelForIndent(thisIndent
);
7099 // Apply the overall list style, and item style for this level
7100 wxRichTextAttr
listStyle(listDef
->GetCombinedStyleForLevel(thisLevel
, buffer
->GetStyleSheet()));
7101 wxRichTextApplyStyle(attr
, listStyle
);
7102 attr
.SetOutlineLevel(thisLevel
);
7103 if (para
->GetAttributes().HasBulletNumber())
7104 attr
.SetBulletNumber(para
->GetAttributes().GetBulletNumber());
7108 if (!foundAttributes
)
7110 attr
= para
->GetAttributes();
7111 int flags
= attr
.GetFlags();
7113 // Eliminate character styles
7114 flags
&= ( (~ wxTEXT_ATTR_FONT
) |
7115 (~ wxTEXT_ATTR_TEXT_COLOUR
) |
7116 (~ wxTEXT_ATTR_BACKGROUND_COLOUR
) );
7117 attr
.SetFlags(flags
);
7123 return wxRichTextAttr();
7126 /// Submit command to delete this range
7127 bool wxRichTextBuffer::DeleteRangeWithUndo(const wxRichTextRange
& range
, wxRichTextCtrl
* ctrl
)
7129 return ctrl
->GetFocusObject()->DeleteRangeWithUndo(range
, ctrl
, this);
7132 /// Submit command to delete this range
7133 bool wxRichTextParagraphLayoutBox::DeleteRangeWithUndo(const wxRichTextRange
& range
, wxRichTextCtrl
* ctrl
, wxRichTextBuffer
* buffer
)
7135 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Delete"), wxRICHTEXT_DELETE
, buffer
, this, ctrl
);
7137 action
->SetPosition(ctrl
->GetCaretPosition());
7139 // Set the range to delete
7140 action
->SetRange(range
);
7142 // Copy the fragment that we'll need to restore in Undo
7143 CopyFragment(range
, action
->GetOldParagraphs());
7145 // See if we're deleting a paragraph marker, in which case we need to
7146 // make a note not to copy the attributes from the 2nd paragraph to the 1st.
7147 if (range
.GetStart() == range
.GetEnd())
7149 wxRichTextParagraph
* para
= GetParagraphAtPosition(range
.GetStart());
7150 if (para
&& para
->GetRange().GetEnd() == range
.GetEnd())
7152 wxRichTextParagraph
* nextPara
= GetParagraphAtPosition(range
.GetStart()+1);
7153 if (nextPara
&& nextPara
!= para
)
7155 action
->GetOldParagraphs().GetChildren().GetFirst()->GetData()->SetAttributes(nextPara
->GetAttributes());
7156 action
->GetOldParagraphs().GetAttributes().SetFlags(action
->GetOldParagraphs().GetAttributes().GetFlags() | wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
);
7161 buffer
->SubmitAction(action
);
7166 /// Collapse undo/redo commands
7167 bool wxRichTextBuffer::BeginBatchUndo(const wxString
& cmdName
)
7169 if (m_batchedCommandDepth
== 0)
7171 wxASSERT(m_batchedCommand
== NULL
);
7172 if (m_batchedCommand
)
7174 GetCommandProcessor()->Store(m_batchedCommand
);
7176 m_batchedCommand
= new wxRichTextCommand(cmdName
);
7179 m_batchedCommandDepth
++;
7184 /// Collapse undo/redo commands
7185 bool wxRichTextBuffer::EndBatchUndo()
7187 m_batchedCommandDepth
--;
7189 wxASSERT(m_batchedCommandDepth
>= 0);
7190 wxASSERT(m_batchedCommand
!= NULL
);
7192 if (m_batchedCommandDepth
== 0)
7194 GetCommandProcessor()->Store(m_batchedCommand
);
7195 m_batchedCommand
= NULL
;
7201 /// Submit immediately, or delay according to whether collapsing is on
7202 bool wxRichTextBuffer::SubmitAction(wxRichTextAction
* action
)
7204 if (BatchingUndo() && m_batchedCommand
&& !SuppressingUndo())
7206 wxRichTextCommand
* cmd
= new wxRichTextCommand(action
->GetName());
7207 cmd
->AddAction(action
);
7209 cmd
->GetActions().Clear();
7212 m_batchedCommand
->AddAction(action
);
7216 wxRichTextCommand
* cmd
= new wxRichTextCommand(action
->GetName());
7217 cmd
->AddAction(action
);
7219 // Only store it if we're not suppressing undo.
7220 return GetCommandProcessor()->Submit(cmd
, !SuppressingUndo());
7226 /// Begin suppressing undo/redo commands.
7227 bool wxRichTextBuffer::BeginSuppressUndo()
7234 /// End suppressing undo/redo commands.
7235 bool wxRichTextBuffer::EndSuppressUndo()
7242 /// Begin using a style
7243 bool wxRichTextBuffer::BeginStyle(const wxRichTextAttr
& style
)
7245 wxRichTextAttr
newStyle(GetDefaultStyle());
7247 // Save the old default style
7248 m_attributeStack
.Append((wxObject
*) new wxRichTextAttr(GetDefaultStyle()));
7250 wxRichTextApplyStyle(newStyle
, style
);
7251 newStyle
.SetFlags(style
.GetFlags()|newStyle
.GetFlags());
7253 SetDefaultStyle(newStyle
);
7259 bool wxRichTextBuffer::EndStyle()
7261 if (!m_attributeStack
.GetFirst())
7263 wxLogDebug(_("Too many EndStyle calls!"));
7267 wxList::compatibility_iterator node
= m_attributeStack
.GetLast();
7268 wxRichTextAttr
* attr
= (wxRichTextAttr
*)node
->GetData();
7269 m_attributeStack
.Erase(node
);
7271 SetDefaultStyle(*attr
);
7278 bool wxRichTextBuffer::EndAllStyles()
7280 while (m_attributeStack
.GetCount() != 0)
7285 /// Clear the style stack
7286 void wxRichTextBuffer::ClearStyleStack()
7288 for (wxList::compatibility_iterator node
= m_attributeStack
.GetFirst(); node
; node
= node
->GetNext())
7289 delete (wxRichTextAttr
*) node
->GetData();
7290 m_attributeStack
.Clear();
7293 /// Begin using bold
7294 bool wxRichTextBuffer::BeginBold()
7296 wxRichTextAttr attr
;
7297 attr
.SetFontWeight(wxFONTWEIGHT_BOLD
);
7299 return BeginStyle(attr
);
7302 /// Begin using italic
7303 bool wxRichTextBuffer::BeginItalic()
7305 wxRichTextAttr attr
;
7306 attr
.SetFontStyle(wxFONTSTYLE_ITALIC
);
7308 return BeginStyle(attr
);
7311 /// Begin using underline
7312 bool wxRichTextBuffer::BeginUnderline()
7314 wxRichTextAttr attr
;
7315 attr
.SetFontUnderlined(true);
7317 return BeginStyle(attr
);
7320 /// Begin using point size
7321 bool wxRichTextBuffer::BeginFontSize(int pointSize
)
7323 wxRichTextAttr attr
;
7324 attr
.SetFontSize(pointSize
);
7326 return BeginStyle(attr
);
7329 /// Begin using this font
7330 bool wxRichTextBuffer::BeginFont(const wxFont
& font
)
7332 wxRichTextAttr attr
;
7335 return BeginStyle(attr
);
7338 /// Begin using this colour
7339 bool wxRichTextBuffer::BeginTextColour(const wxColour
& colour
)
7341 wxRichTextAttr attr
;
7342 attr
.SetFlags(wxTEXT_ATTR_TEXT_COLOUR
);
7343 attr
.SetTextColour(colour
);
7345 return BeginStyle(attr
);
7348 /// Begin using alignment
7349 bool wxRichTextBuffer::BeginAlignment(wxTextAttrAlignment alignment
)
7351 wxRichTextAttr attr
;
7352 attr
.SetFlags(wxTEXT_ATTR_ALIGNMENT
);
7353 attr
.SetAlignment(alignment
);
7355 return BeginStyle(attr
);
7358 /// Begin left indent
7359 bool wxRichTextBuffer::BeginLeftIndent(int leftIndent
, int leftSubIndent
)
7361 wxRichTextAttr attr
;
7362 attr
.SetFlags(wxTEXT_ATTR_LEFT_INDENT
);
7363 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7365 return BeginStyle(attr
);
7368 /// Begin right indent
7369 bool wxRichTextBuffer::BeginRightIndent(int rightIndent
)
7371 wxRichTextAttr attr
;
7372 attr
.SetFlags(wxTEXT_ATTR_RIGHT_INDENT
);
7373 attr
.SetRightIndent(rightIndent
);
7375 return BeginStyle(attr
);
7378 /// Begin paragraph spacing
7379 bool wxRichTextBuffer::BeginParagraphSpacing(int before
, int after
)
7383 flags
|= wxTEXT_ATTR_PARA_SPACING_BEFORE
;
7385 flags
|= wxTEXT_ATTR_PARA_SPACING_AFTER
;
7387 wxRichTextAttr attr
;
7388 attr
.SetFlags(flags
);
7389 attr
.SetParagraphSpacingBefore(before
);
7390 attr
.SetParagraphSpacingAfter(after
);
7392 return BeginStyle(attr
);
7395 /// Begin line spacing
7396 bool wxRichTextBuffer::BeginLineSpacing(int lineSpacing
)
7398 wxRichTextAttr attr
;
7399 attr
.SetFlags(wxTEXT_ATTR_LINE_SPACING
);
7400 attr
.SetLineSpacing(lineSpacing
);
7402 return BeginStyle(attr
);
7405 /// Begin numbered bullet
7406 bool wxRichTextBuffer::BeginNumberedBullet(int bulletNumber
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7408 wxRichTextAttr attr
;
7409 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7410 attr
.SetBulletStyle(bulletStyle
);
7411 attr
.SetBulletNumber(bulletNumber
);
7412 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7414 return BeginStyle(attr
);
7417 /// Begin symbol bullet
7418 bool wxRichTextBuffer::BeginSymbolBullet(const wxString
& symbol
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7420 wxRichTextAttr attr
;
7421 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7422 attr
.SetBulletStyle(bulletStyle
);
7423 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7424 attr
.SetBulletText(symbol
);
7426 return BeginStyle(attr
);
7429 /// Begin standard bullet
7430 bool wxRichTextBuffer::BeginStandardBullet(const wxString
& bulletName
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7432 wxRichTextAttr attr
;
7433 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7434 attr
.SetBulletStyle(bulletStyle
);
7435 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7436 attr
.SetBulletName(bulletName
);
7438 return BeginStyle(attr
);
7441 /// Begin named character style
7442 bool wxRichTextBuffer::BeginCharacterStyle(const wxString
& characterStyle
)
7444 if (GetStyleSheet())
7446 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->FindCharacterStyle(characterStyle
);
7449 wxRichTextAttr attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7450 return BeginStyle(attr
);
7456 /// Begin named paragraph style
7457 bool wxRichTextBuffer::BeginParagraphStyle(const wxString
& paragraphStyle
)
7459 if (GetStyleSheet())
7461 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(paragraphStyle
);
7464 wxRichTextAttr attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7465 return BeginStyle(attr
);
7471 /// Begin named list style
7472 bool wxRichTextBuffer::BeginListStyle(const wxString
& listStyle
, int level
, int number
)
7474 if (GetStyleSheet())
7476 wxRichTextListStyleDefinition
* def
= GetStyleSheet()->FindListStyle(listStyle
);
7479 wxRichTextAttr
attr(def
->GetCombinedStyleForLevel(level
));
7481 attr
.SetBulletNumber(number
);
7483 return BeginStyle(attr
);
7490 bool wxRichTextBuffer::BeginURL(const wxString
& url
, const wxString
& characterStyle
)
7492 wxRichTextAttr attr
;
7494 if (!characterStyle
.IsEmpty() && GetStyleSheet())
7496 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->FindCharacterStyle(characterStyle
);
7499 attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7504 return BeginStyle(attr
);
7507 /// Adds a handler to the end
7508 void wxRichTextBuffer::AddHandler(wxRichTextFileHandler
*handler
)
7510 sm_handlers
.Append(handler
);
7513 /// Inserts a handler at the front
7514 void wxRichTextBuffer::InsertHandler(wxRichTextFileHandler
*handler
)
7516 sm_handlers
.Insert( handler
);
7519 /// Removes a handler
7520 bool wxRichTextBuffer::RemoveHandler(const wxString
& name
)
7522 wxRichTextFileHandler
*handler
= FindHandler(name
);
7525 sm_handlers
.DeleteObject(handler
);
7533 /// Finds a handler by filename or, if supplied, type
7534 wxRichTextFileHandler
*wxRichTextBuffer::FindHandlerFilenameOrType(const wxString
& filename
,
7535 wxRichTextFileType imageType
)
7537 if (imageType
!= wxRICHTEXT_TYPE_ANY
)
7538 return FindHandler(imageType
);
7539 else if (!filename
.IsEmpty())
7541 wxString path
, file
, ext
;
7542 wxFileName::SplitPath(filename
, & path
, & file
, & ext
);
7543 return FindHandler(ext
, imageType
);
7550 /// Finds a handler by name
7551 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(const wxString
& name
)
7553 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7556 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7557 if (handler
->GetName().Lower() == name
.Lower()) return handler
;
7559 node
= node
->GetNext();
7564 /// Finds a handler by extension and type
7565 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(const wxString
& extension
, wxRichTextFileType type
)
7567 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7570 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7571 if ( handler
->GetExtension().Lower() == extension
.Lower() &&
7572 (type
== wxRICHTEXT_TYPE_ANY
|| handler
->GetType() == type
) )
7574 node
= node
->GetNext();
7579 /// Finds a handler by type
7580 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(wxRichTextFileType type
)
7582 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7585 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7586 if (handler
->GetType() == type
) return handler
;
7587 node
= node
->GetNext();
7592 void wxRichTextBuffer::InitStandardHandlers()
7594 if (!FindHandler(wxRICHTEXT_TYPE_TEXT
))
7595 AddHandler(new wxRichTextPlainTextHandler
);
7598 void wxRichTextBuffer::CleanUpHandlers()
7600 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7603 wxRichTextFileHandler
* handler
= (wxRichTextFileHandler
*)node
->GetData();
7604 wxList::compatibility_iterator next
= node
->GetNext();
7609 sm_handlers
.Clear();
7612 wxString
wxRichTextBuffer::GetExtWildcard(bool combine
, bool save
, wxArrayInt
* types
)
7619 wxList::compatibility_iterator node
= GetHandlers().GetFirst();
7623 wxRichTextFileHandler
* handler
= (wxRichTextFileHandler
*) node
->GetData();
7624 if (handler
->IsVisible() && ((save
&& handler
->CanSave()) || (!save
&& handler
->CanLoad())))
7629 wildcard
+= wxT(";");
7630 wildcard
+= wxT("*.") + handler
->GetExtension();
7635 wildcard
+= wxT("|");
7636 wildcard
+= handler
->GetName();
7637 wildcard
+= wxT(" ");
7638 wildcard
+= _("files");
7639 wildcard
+= wxT(" (*.");
7640 wildcard
+= handler
->GetExtension();
7641 wildcard
+= wxT(")|*.");
7642 wildcard
+= handler
->GetExtension();
7644 types
->Add(handler
->GetType());
7649 node
= node
->GetNext();
7653 wildcard
= wxT("(") + wildcard
+ wxT(")|") + wildcard
;
7658 bool wxRichTextBuffer::LoadFile(const wxString
& filename
, wxRichTextFileType type
)
7660 wxRichTextFileHandler
* handler
= FindHandlerFilenameOrType(filename
, type
);
7663 SetDefaultStyle(wxRichTextAttr());
7664 handler
->SetFlags(GetHandlerFlags());
7665 bool success
= handler
->LoadFile(this, filename
);
7666 Invalidate(wxRICHTEXT_ALL
);
7674 bool wxRichTextBuffer::SaveFile(const wxString
& filename
, wxRichTextFileType type
)
7676 wxRichTextFileHandler
* handler
= FindHandlerFilenameOrType(filename
, type
);
7679 handler
->SetFlags(GetHandlerFlags());
7680 return handler
->SaveFile(this, filename
);
7686 /// Load from a stream
7687 bool wxRichTextBuffer::LoadFile(wxInputStream
& stream
, wxRichTextFileType type
)
7689 wxRichTextFileHandler
* handler
= FindHandler(type
);
7692 SetDefaultStyle(wxRichTextAttr());
7693 handler
->SetFlags(GetHandlerFlags());
7694 bool success
= handler
->LoadFile(this, stream
);
7695 Invalidate(wxRICHTEXT_ALL
);
7702 /// Save to a stream
7703 bool wxRichTextBuffer::SaveFile(wxOutputStream
& stream
, wxRichTextFileType type
)
7705 wxRichTextFileHandler
* handler
= FindHandler(type
);
7708 handler
->SetFlags(GetHandlerFlags());
7709 return handler
->SaveFile(this, stream
);
7715 /// Copy the range to the clipboard
7716 bool wxRichTextBuffer::CopyToClipboard(const wxRichTextRange
& range
)
7718 bool success
= false;
7719 wxRichTextParagraphLayoutBox
* container
= this;
7720 if (GetRichTextCtrl())
7721 container
= GetRichTextCtrl()->GetFocusObject();
7723 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
7725 if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open())
7727 wxTheClipboard
->Clear();
7729 // Add composite object
7731 wxDataObjectComposite
* compositeObject
= new wxDataObjectComposite();
7734 wxString text
= container
->GetTextForRange(range
);
7737 text
= wxTextFile::Translate(text
, wxTextFileType_Dos
);
7740 compositeObject
->Add(new wxTextDataObject(text
), false /* not preferred */);
7743 // Add rich text buffer data object. This needs the XML handler to be present.
7745 if (FindHandler(wxRICHTEXT_TYPE_XML
))
7747 wxRichTextBuffer
* richTextBuf
= new wxRichTextBuffer
;
7748 container
->CopyFragment(range
, *richTextBuf
);
7750 compositeObject
->Add(new wxRichTextBufferDataObject(richTextBuf
), true /* preferred */);
7753 if (wxTheClipboard
->SetData(compositeObject
))
7756 wxTheClipboard
->Close();
7765 /// Paste the clipboard content to the buffer
7766 bool wxRichTextBuffer::PasteFromClipboard(long position
)
7768 bool success
= false;
7769 wxRichTextParagraphLayoutBox
* container
= this;
7770 if (GetRichTextCtrl())
7771 container
= GetRichTextCtrl()->GetFocusObject();
7773 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
7774 if (CanPasteFromClipboard())
7776 if (wxTheClipboard
->Open())
7778 if (wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())))
7780 wxRichTextBufferDataObject data
;
7781 wxTheClipboard
->GetData(data
);
7782 wxRichTextBuffer
* richTextBuffer
= data
.GetRichTextBuffer();
7785 container
->InsertParagraphsWithUndo(this, position
+1, *richTextBuffer
, GetRichTextCtrl(), 0);
7786 if (GetRichTextCtrl())
7787 GetRichTextCtrl()->ShowPosition(position
+ richTextBuffer
->GetOwnRange().GetEnd());
7788 delete richTextBuffer
;
7791 else if (wxTheClipboard
->IsSupported(wxDF_TEXT
)
7793 || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
)
7797 wxTextDataObject data
;
7798 wxTheClipboard
->GetData(data
);
7799 wxString
text(data
.GetText());
7802 text2
.Alloc(text
.Length()+1);
7804 for (i
= 0; i
< text
.Length(); i
++)
7806 wxChar ch
= text
[i
];
7807 if (ch
!= wxT('\r'))
7811 wxString text2
= text
;
7813 container
->InsertTextWithUndo(this, position
+1, text2
, GetRichTextCtrl(), wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
);
7815 if (GetRichTextCtrl())
7816 GetRichTextCtrl()->ShowPosition(position
+ text2
.Length());
7820 else if (wxTheClipboard
->IsSupported(wxDF_BITMAP
))
7822 wxBitmapDataObject data
;
7823 wxTheClipboard
->GetData(data
);
7824 wxBitmap
bitmap(data
.GetBitmap());
7825 wxImage
image(bitmap
.ConvertToImage());
7827 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, this, container
, GetRichTextCtrl(), false);
7829 action
->GetNewParagraphs().AddImage(image
);
7831 if (action
->GetNewParagraphs().GetChildCount() == 1)
7832 action
->GetNewParagraphs().SetPartialParagraph(true);
7834 action
->SetPosition(position
+1);
7836 // Set the range we'll need to delete in Undo
7837 action
->SetRange(wxRichTextRange(position
+1, position
+1));
7839 SubmitAction(action
);
7843 wxTheClipboard
->Close();
7847 wxUnusedVar(position
);
7852 /// Can we paste from the clipboard?
7853 bool wxRichTextBuffer::CanPasteFromClipboard() const
7855 bool canPaste
= false;
7856 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
7857 if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open())
7859 if (wxTheClipboard
->IsSupported(wxDF_TEXT
)
7861 || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
)
7863 || wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())) ||
7864 wxTheClipboard
->IsSupported(wxDF_BITMAP
))
7868 wxTheClipboard
->Close();
7874 /// Dumps contents of buffer for debugging purposes
7875 void wxRichTextBuffer::Dump()
7879 wxStringOutputStream
stream(& text
);
7880 wxTextOutputStream
textStream(stream
);
7887 /// Add an event handler
7888 bool wxRichTextBuffer::AddEventHandler(wxEvtHandler
* handler
)
7890 m_eventHandlers
.Append(handler
);
7894 /// Remove an event handler
7895 bool wxRichTextBuffer::RemoveEventHandler(wxEvtHandler
* handler
, bool deleteHandler
)
7897 wxList::compatibility_iterator node
= m_eventHandlers
.Find(handler
);
7900 m_eventHandlers
.Erase(node
);
7910 /// Clear event handlers
7911 void wxRichTextBuffer::ClearEventHandlers()
7913 m_eventHandlers
.Clear();
7916 /// Send event to event handlers. If sendToAll is true, will send to all event handlers,
7917 /// otherwise will stop at the first successful one.
7918 bool wxRichTextBuffer::SendEvent(wxEvent
& event
, bool sendToAll
)
7920 bool success
= false;
7921 for (wxList::compatibility_iterator node
= m_eventHandlers
.GetFirst(); node
; node
= node
->GetNext())
7923 wxEvtHandler
* handler
= (wxEvtHandler
*) node
->GetData();
7924 if (handler
->ProcessEvent(event
))
7934 /// Set style sheet and notify of the change
7935 bool wxRichTextBuffer::SetStyleSheetAndNotify(wxRichTextStyleSheet
* sheet
)
7937 wxRichTextStyleSheet
* oldSheet
= GetStyleSheet();
7939 wxWindowID winid
= wxID_ANY
;
7940 if (GetRichTextCtrl())
7941 winid
= GetRichTextCtrl()->GetId();
7943 wxRichTextEvent
event(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACING
, winid
);
7944 event
.SetEventObject(GetRichTextCtrl());
7945 event
.SetContainer(GetRichTextCtrl()->GetFocusObject());
7946 event
.SetOldStyleSheet(oldSheet
);
7947 event
.SetNewStyleSheet(sheet
);
7950 if (SendEvent(event
) && !event
.IsAllowed())
7952 if (sheet
!= oldSheet
)
7958 if (oldSheet
&& oldSheet
!= sheet
)
7961 SetStyleSheet(sheet
);
7963 event
.SetEventType(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACED
);
7964 event
.SetOldStyleSheet(NULL
);
7967 return SendEvent(event
);
7970 /// Set renderer, deleting old one
7971 void wxRichTextBuffer::SetRenderer(wxRichTextRenderer
* renderer
)
7975 sm_renderer
= renderer
;
7978 /// Hit-testing: returns a flag indicating hit test details, plus
7979 /// information about position
7980 int wxRichTextBuffer::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
7982 int ret
= wxRichTextParagraphLayoutBox::HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
7983 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
7989 textPosition
= m_ownRange
.GetEnd()-1;
7992 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
;
7996 bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxRichTextAttr
& bulletAttr
, const wxRect
& rect
)
7998 if (bulletAttr
.GetTextColour().IsOk())
8000 wxCheckSetPen(dc
, wxPen(bulletAttr
.GetTextColour()));
8001 wxCheckSetBrush(dc
, wxBrush(bulletAttr
.GetTextColour()));
8005 wxCheckSetPen(dc
, *wxBLACK_PEN
);
8006 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
8010 if (bulletAttr
.HasFont())
8012 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(bulletAttr
);
8015 font
= (*wxNORMAL_FONT
);
8017 wxCheckSetFont(dc
, font
);
8019 int charHeight
= dc
.GetCharHeight();
8021 int bulletWidth
= (int) (((float) charHeight
) * wxRichTextBuffer::GetBulletProportion());
8022 int bulletHeight
= bulletWidth
;
8026 // Calculate the top position of the character (as opposed to the whole line height)
8027 int y
= rect
.y
+ (rect
.height
- charHeight
);
8029 // Calculate where the bullet should be positioned
8030 y
= y
+ (charHeight
+1)/2 - (bulletHeight
+1)/2;
8032 // The margin between a bullet and text.
8033 int margin
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer::GetBulletRightMargin());
8035 if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
)
8036 x
= rect
.x
+ rect
.width
- bulletWidth
- margin
;
8037 else if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
)
8038 x
= x
+ (rect
.width
)/2 - bulletWidth
/2;
8040 if (bulletAttr
.GetBulletName() == wxT("standard/square"))
8042 dc
.DrawRectangle(x
, y
, bulletWidth
, bulletHeight
);
8044 else if (bulletAttr
.GetBulletName() == wxT("standard/diamond"))
8047 pts
[0].x
= x
; pts
[0].y
= y
+ bulletHeight
/2;
8048 pts
[1].x
= x
+ bulletWidth
/2; pts
[1].y
= y
;
8049 pts
[2].x
= x
+ bulletWidth
; pts
[2].y
= y
+ bulletHeight
/2;
8050 pts
[3].x
= x
+ bulletWidth
/2; pts
[3].y
= y
+ bulletHeight
;
8052 dc
.DrawPolygon(4, pts
);
8054 else if (bulletAttr
.GetBulletName() == wxT("standard/triangle"))
8057 pts
[0].x
= x
; pts
[0].y
= y
;
8058 pts
[1].x
= x
+ bulletWidth
; pts
[1].y
= y
+ bulletHeight
/2;
8059 pts
[2].x
= x
; pts
[2].y
= y
+ bulletHeight
;
8061 dc
.DrawPolygon(3, pts
);
8063 else if (bulletAttr
.GetBulletName() == wxT("standard/circle-outline"))
8065 wxCheckSetBrush(dc
, *wxTRANSPARENT_BRUSH
);
8066 dc
.DrawEllipse(x
, y
, bulletWidth
, bulletHeight
);
8068 else // "standard/circle", and catch-all
8070 dc
.DrawEllipse(x
, y
, bulletWidth
, bulletHeight
);
8076 bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxRichTextAttr
& attr
, const wxRect
& rect
, const wxString
& text
)
8081 if ((attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
) && !attr
.GetBulletFont().IsEmpty() && attr
.HasFont())
8083 wxRichTextAttr fontAttr
;
8084 fontAttr
.SetFontSize(attr
.GetFontSize());
8085 fontAttr
.SetFontStyle(attr
.GetFontStyle());
8086 fontAttr
.SetFontWeight(attr
.GetFontWeight());
8087 fontAttr
.SetFontUnderlined(attr
.GetFontUnderlined());
8088 fontAttr
.SetFontFaceName(attr
.GetBulletFont());
8089 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(fontAttr
);
8091 else if (attr
.HasFont())
8092 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(attr
);
8094 font
= (*wxNORMAL_FONT
);
8096 wxCheckSetFont(dc
, font
);
8098 if (attr
.GetTextColour().IsOk())
8099 dc
.SetTextForeground(attr
.GetTextColour());
8101 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
8103 int charHeight
= dc
.GetCharHeight();
8105 dc
.GetTextExtent(text
, & tw
, & th
);
8109 // Calculate the top position of the character (as opposed to the whole line height)
8110 int y
= rect
.y
+ (rect
.height
- charHeight
);
8112 // The margin between a bullet and text.
8113 int margin
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer::GetBulletRightMargin());
8115 if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
)
8116 x
= (rect
.x
+ rect
.width
) - tw
- margin
;
8117 else if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
)
8118 x
= x
+ (rect
.width
)/2 - tw
/2;
8120 dc
.DrawText(text
, x
, y
);
8128 bool wxRichTextStdRenderer::DrawBitmapBullet(wxRichTextParagraph
* WXUNUSED(paragraph
), wxDC
& WXUNUSED(dc
), const wxRichTextAttr
& WXUNUSED(attr
), const wxRect
& WXUNUSED(rect
))
8130 // Currently unimplemented. The intention is to store bitmaps by name in a media store associated
8131 // with the buffer. The store will allow retrieval from memory, disk or other means.
8135 /// Enumerate the standard bullet names currently supported
8136 bool wxRichTextStdRenderer::EnumerateStandardBulletNames(wxArrayString
& bulletNames
)
8138 bulletNames
.Add(wxTRANSLATE("standard/circle"));
8139 bulletNames
.Add(wxTRANSLATE("standard/circle-outline"));
8140 bulletNames
.Add(wxTRANSLATE("standard/square"));
8141 bulletNames
.Add(wxTRANSLATE("standard/diamond"));
8142 bulletNames
.Add(wxTRANSLATE("standard/triangle"));
8151 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBox
, wxRichTextParagraphLayoutBox
)
8153 wxRichTextBox::wxRichTextBox(wxRichTextObject
* parent
):
8154 wxRichTextParagraphLayoutBox(parent
)
8159 bool wxRichTextBox::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8164 // TODO: if the active object in the control, draw an indication.
8165 // We need to add the concept of active object, and not just focus object,
8166 // so we can apply commands (properties, delete, ...) to objects such as text boxes and images.
8167 // Ultimately we would like to be able to interactively resize an active object
8168 // using drag handles.
8169 return wxRichTextParagraphLayoutBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8173 void wxRichTextBox::Copy(const wxRichTextBox
& obj
)
8175 wxRichTextParagraphLayoutBox::Copy(obj
);
8178 // Edit properties via a GUI
8179 bool wxRichTextBox::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8181 wxRichTextObjectPropertiesDialog
boxDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Box Properties"));
8182 boxDlg
.SetAttributes(GetAttributes());
8184 if (boxDlg
.ShowModal() == wxID_OK
)
8186 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
8187 // indeterminate in the object.
8188 boxDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
8195 IMPLEMENT_DYNAMIC_CLASS(wxRichTextCell
, wxRichTextBox
)
8197 wxRichTextCell::wxRichTextCell(wxRichTextObject
* parent
):
8198 wxRichTextBox(parent
)
8203 bool wxRichTextCell::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8205 return wxRichTextBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8209 void wxRichTextCell::Copy(const wxRichTextCell
& obj
)
8211 wxRichTextBox::Copy(obj
);
8214 // Edit properties via a GUI
8215 bool wxRichTextCell::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8217 // We need to gather common attributes for all selected cells.
8219 wxRichTextTable
* table
= wxDynamicCast(GetParent(), wxRichTextTable
);
8220 bool multipleCells
= false;
8221 wxRichTextAttr attr
;
8223 if (table
&& buffer
&& buffer
->GetRichTextCtrl() && buffer
->GetRichTextCtrl()->GetSelection().IsValid() &&
8224 buffer
->GetRichTextCtrl()->GetSelection().GetContainer() == GetParent())
8226 wxRichTextAttr clashingAttr
, absentAttr
;
8227 const wxRichTextSelection
& sel
= buffer
->GetRichTextCtrl()->GetSelection();
8229 int selectedCellCount
= 0;
8230 for (i
= 0; i
< sel
.GetCount(); i
++)
8232 const wxRichTextRange
& range
= sel
[i
];
8233 wxRichTextCell
* cell
= table
->GetCell(range
.GetStart());
8236 wxRichTextAttr cellStyle
= cell
->GetAttributes();
8238 CollectStyle(attr
, cellStyle
, clashingAttr
, absentAttr
);
8240 selectedCellCount
++;
8243 multipleCells
= selectedCellCount
> 1;
8247 attr
= GetAttributes();
8252 caption
= _("Multiple Cell Properties");
8254 caption
= _("Cell Properties");
8256 wxRichTextObjectPropertiesDialog
cellDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, caption
);
8257 cellDlg
.SetAttributes(attr
);
8259 wxRichTextSizePage
* sizePage
= wxDynamicCast(cellDlg
.FindPage(CLASSINFO(wxRichTextSizePage
)), wxRichTextSizePage
);
8262 // We don't want position and floating controls for a cell.
8263 sizePage
->ShowPositionControls(false);
8264 sizePage
->ShowFloatingControls(false);
8267 if (cellDlg
.ShowModal() == wxID_OK
)
8271 const wxRichTextSelection
& sel
= buffer
->GetRichTextCtrl()->GetSelection();
8272 // Apply the style; we interpret indeterminate attributes as 'don't touch this attribute'
8273 // since it may represent clashing attributes across multiple objects.
8274 table
->SetCellStyle(sel
, attr
);
8277 // For a single object, indeterminate attributes set by the user should be reflected in the
8278 // actual object style, so pass the wxRICHTEXT_SETSTYLE_RESET flag to assign
8279 // the style directly instead of applying (which ignores indeterminate attributes,
8280 // leaving them as they were).
8281 cellDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
8288 WX_DEFINE_OBJARRAY(wxRichTextObjectPtrArrayArray
)
8290 IMPLEMENT_DYNAMIC_CLASS(wxRichTextTable
, wxRichTextBox
)
8292 wxRichTextTable::wxRichTextTable(wxRichTextObject
* parent
): wxRichTextBox(parent
)
8298 // Draws the object.
8299 bool wxRichTextTable::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8301 return wxRichTextBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8304 WX_DECLARE_OBJARRAY(wxRect
, wxRichTextRectArray
);
8305 WX_DEFINE_OBJARRAY(wxRichTextRectArray
);
8307 // Lays the object out. rect is the space available for layout. Often it will
8308 // be the specified overall space for this object, if trying to constrain
8309 // layout to a particular size, or it could be the total space available in the
8310 // parent. rect is the overall size, so we must subtract margins and padding.
8311 // to get the actual available space.
8312 bool wxRichTextTable::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& WXUNUSED(parentRect
), int style
)
8314 SetPosition(rect
.GetPosition());
8316 // TODO: the meaty bit. Calculate sizes of all cells and rows. Try to use
8317 // minimum size if within alloted size, then divide up remaining size
8318 // between rows/cols.
8321 wxRichTextBuffer
* buffer
= GetBuffer();
8322 if (buffer
) scale
= buffer
->GetScale();
8324 wxRect availableSpace
= GetAvailableContentArea(dc
, context
, rect
);
8325 wxTextAttrDimensionConverter
converter(dc
, scale
, availableSpace
.GetSize());
8327 wxRichTextAttr
attr(GetAttributes());
8328 context
.ApplyVirtualAttributes(attr
, this);
8330 // If we have no fixed table size, and assuming we're not pushed for
8331 // space, then we don't have to try to stretch the table to fit the contents.
8332 bool stretchToFitTableWidth
= false;
8334 int tableWidth
= rect
.width
;
8335 if (attr
.GetTextBoxAttr().GetWidth().IsValid())
8337 tableWidth
= converter
.GetPixels(attr
.GetTextBoxAttr().GetWidth());
8339 // Fixed table width, so we do want to stretch columns out if necessary.
8340 stretchToFitTableWidth
= true;
8342 // Shouldn't be able to exceed the size passed to this function
8343 tableWidth
= wxMin(rect
.width
, tableWidth
);
8346 // Get internal padding
8347 int paddingLeft
= 0, paddingTop
= 0;
8348 if (attr
.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
8349 paddingLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetLeft());
8350 if (attr
.GetTextBoxAttr().GetPadding().GetTop().IsValid())
8351 paddingTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetTop());
8353 // Assume that left and top padding are also used for inter-cell padding.
8354 int paddingX
= paddingLeft
;
8355 int paddingY
= paddingTop
;
8357 int totalLeftMargin
= 0, totalRightMargin
= 0, totalTopMargin
= 0, totalBottomMargin
= 0;
8358 GetTotalMargin(dc
, buffer
, attr
, totalLeftMargin
, totalRightMargin
, totalTopMargin
, totalBottomMargin
);
8360 // Internal table width - the area for content
8361 int internalTableWidth
= tableWidth
- totalLeftMargin
- totalRightMargin
;
8363 int rowCount
= m_cells
.GetCount();
8364 if (m_colCount
== 0 || rowCount
== 0)
8366 wxRect
overallRect(rect
.x
, rect
.y
, totalLeftMargin
+ totalRightMargin
, totalTopMargin
+ totalBottomMargin
);
8367 SetCachedSize(overallRect
.GetSize());
8369 // Zero content size
8370 SetMinSize(overallRect
.GetSize());
8371 SetMaxSize(GetMinSize());
8375 // The final calculated widths
8376 wxArrayInt colWidths
;
8377 colWidths
.Add(0, m_colCount
);
8379 wxArrayInt absoluteColWidths
;
8380 absoluteColWidths
.Add(0, m_colCount
);
8381 // wxArrayInt absoluteColWidthsSpanning(m_colCount);
8382 wxArrayInt percentageColWidths
;
8383 percentageColWidths
.Add(0, m_colCount
);
8384 // wxArrayInt percentageColWidthsSpanning(m_colCount);
8385 // These are only relevant when the first column contains spanning information.
8386 // wxArrayInt columnSpans(m_colCount); // Each contains 1 for non-spanning cell, > 1 for spanning cell.
8387 wxArrayInt maxColWidths
;
8388 maxColWidths
.Add(0, m_colCount
);
8389 wxArrayInt minColWidths
;
8390 minColWidths
.Add(0, m_colCount
);
8392 wxSize
tableSize(tableWidth
, 0);
8396 for (i
= 0; i
< m_colCount
; i
++)
8398 absoluteColWidths
[i
] = 0;
8399 // absoluteColWidthsSpanning[i] = 0;
8400 percentageColWidths
[i
] = -1;
8401 // percentageColWidthsSpanning[i] = -1;
8403 maxColWidths
[i
] = 0;
8404 minColWidths
[i
] = 0;
8405 // columnSpans[i] = 1;
8408 // (0) Determine which cells are visible according to spans
8410 // __________________
8415 // |------------------|
8416 // |__________________| 4
8418 // To calculate cell visibility:
8419 // First find all spanning cells. Build an array of span records with start x, y and end x, y.
8420 // Then for each cell, test whether we're within one of those cells, and unless we're at the start of
8421 // that cell, hide the cell.
8423 // We can also use this array to match the size of spanning cells to the grid. Or just do
8424 // this when we iterate through all cells.
8426 // 0.1: add spanning cells to an array
8427 wxRichTextRectArray rectArray
;
8428 for (j
= 0; j
< m_rowCount
; j
++)
8430 for (i
= 0; i
< m_colCount
; i
++)
8432 wxRichTextBox
* cell
= GetCell(j
, i
);
8433 int colSpan
= 1, rowSpan
= 1;
8434 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8435 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8436 if (cell
->GetProperties().HasProperty(wxT("rowspan")))
8437 rowSpan
= cell
->GetProperties().GetPropertyLong(wxT("rowspan"));
8438 if (colSpan
> 1 || rowSpan
> 1)
8440 rectArray
.Add(wxRect(i
, j
, colSpan
, rowSpan
));
8444 // 0.2: find which cells are subsumed by a spanning cell
8445 for (j
= 0; j
< m_rowCount
; j
++)
8447 for (i
= 0; i
< m_colCount
; i
++)
8449 wxRichTextBox
* cell
= GetCell(j
, i
);
8450 if (rectArray
.GetCount() == 0)
8456 int colSpan
= 1, rowSpan
= 1;
8457 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8458 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8459 if (cell
->GetProperties().HasProperty(wxT("rowspan")))
8460 rowSpan
= cell
->GetProperties().GetPropertyLong(wxT("rowspan"));
8461 if (colSpan
> 1 || rowSpan
> 1)
8463 // Assume all spanning cells are shown
8469 for (k
= 0; k
< (int) rectArray
.GetCount(); k
++)
8471 if (rectArray
[k
].Contains(wxPoint(i
, j
)))
8483 // TODO: find the first spanned cell in each row that spans the most columns and doesn't
8484 // overlap with a spanned cell starting at a previous column position.
8485 // This means we need to keep an array of rects so we can check. However
8486 // it does also mean that some spans simply may not be taken into account
8487 // where there are different spans happening on different rows. In these cases,
8488 // they will simply be as wide as their constituent columns.
8490 // (1) Do an initial layout for all cells to get minimum and maximum size, and get
8491 // the absolute or percentage width of each column.
8493 for (j
= 0; j
< m_rowCount
; j
++)
8495 // First get the overall margins so we can calculate percentage widths based on
8496 // the available content space for all cells on the row
8498 int overallRowContentMargin
= 0;
8499 int visibleCellCount
= 0;
8501 for (i
= 0; i
< m_colCount
; i
++)
8503 wxRichTextBox
* cell
= GetCell(j
, i
);
8504 if (cell
->IsShown())
8506 int cellTotalLeftMargin
= 0, cellTotalRightMargin
= 0, cellTotalTopMargin
= 0, cellTotalBottomMargin
= 0;
8507 GetTotalMargin(dc
, buffer
, cell
->GetAttributes(), cellTotalLeftMargin
, cellTotalRightMargin
, cellTotalTopMargin
, cellTotalBottomMargin
);
8509 overallRowContentMargin
+= (cellTotalLeftMargin
+ cellTotalRightMargin
);
8510 visibleCellCount
++;
8514 // Add in inter-cell padding
8515 overallRowContentMargin
+= ((visibleCellCount
-1) * paddingX
);
8517 int rowContentWidth
= internalTableWidth
- overallRowContentMargin
;
8518 wxSize
rowTableSize(rowContentWidth
, 0);
8519 wxTextAttrDimensionConverter
converter(dc
, scale
, rowTableSize
);
8521 for (i
= 0; i
< m_colCount
; i
++)
8523 wxRichTextBox
* cell
= GetCell(j
, i
);
8524 if (cell
->IsShown())
8527 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8528 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8530 // Lay out cell to find min/max widths
8531 cell
->Invalidate(wxRICHTEXT_ALL
);
8532 cell
->Layout(dc
, context
, availableSpace
, availableSpace
, style
);
8536 int absoluteCellWidth
= -1;
8537 int percentageCellWidth
= -1;
8539 // I think we need to calculate percentages from the internal table size,
8540 // minus the padding between cells which we'll need to calculate from the
8541 // (number of VISIBLE cells - 1)*paddingX. Then percentages that add up to 100%
8542 // will add up to 100%. In CSS, the width specifies the cell's content rect width,
8543 // so if we want to conform to that we'll need to add in the overall cell margins.
8544 // However, this will make it difficult to specify percentages that add up to
8545 // 100% and still fit within the table width.
8546 // Let's say two cells have 50% width. They have 10 pixels of overall margin each.
8547 // The table content rect is 500 pixels and the inter-cell padding is 20 pixels.
8548 // If we're using internal content size for the width, we would calculate the
8549 // the overall cell width for n cells as:
8550 // (500 - 20*(n-1) - overallCellMargin1 - overallCellMargin2 - ...) * percentage / 100
8551 // + thisOverallCellMargin
8552 // = 500 - 20 - 10 - 10) * 0.5 + 10 = 240 pixels overall cell width.
8553 // Adding this back, we get 240 + 240 + 20 = 500 pixels.
8555 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
8557 int w
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetWidth());
8558 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
8560 percentageCellWidth
= w
;
8564 absoluteCellWidth
= w
;
8566 // Override absolute width with minimum width if necessary
8567 if (cell
->GetMinSize().x
> 0 && absoluteCellWidth
!=1 && cell
->GetMinSize().x
> absoluteCellWidth
)
8568 absoluteCellWidth
= cell
->GetMinSize().x
;
8571 if (absoluteCellWidth
!= -1)
8573 if (absoluteCellWidth
> absoluteColWidths
[i
])
8574 absoluteColWidths
[i
] = absoluteCellWidth
;
8577 if (percentageCellWidth
!= -1)
8579 if (percentageCellWidth
> percentageColWidths
[i
])
8580 percentageColWidths
[i
] = percentageCellWidth
;
8583 if (colSpan
== 1 && cell
->GetMinSize().x
&& cell
->GetMinSize().x
> minColWidths
[i
])
8584 minColWidths
[i
] = cell
->GetMinSize().x
;
8585 if (colSpan
== 1 && cell
->GetMaxSize().x
&& cell
->GetMaxSize().x
> maxColWidths
[i
])
8586 maxColWidths
[i
] = cell
->GetMaxSize().x
;
8592 // (2) Allocate initial column widths from minimum widths, absolute values and proportions
8593 // TODO: simply merge this into (1).
8594 for (i
= 0; i
< m_colCount
; i
++)
8596 if (absoluteColWidths
[i
] > 0)
8598 colWidths
[i
] = absoluteColWidths
[i
];
8600 else if (percentageColWidths
[i
] > 0)
8602 colWidths
[i
] = percentageColWidths
[i
];
8604 // This is rubbish - we calculated the absolute widths from percentages, so
8605 // we can't do it again here.
8606 //colWidths[i] = (int) (double(percentageColWidths[i]) * double(tableWidth) / 100.0 + 0.5);
8610 // (3) Process absolute or proportional widths of spanning columns,
8611 // now that we know what our fixed column widths are going to be.
8612 // Spanned cells will try to adjust columns so the span will fit.
8613 // Even existing fixed column widths can be expanded if necessary.
8614 // Actually, currently fixed columns widths aren't adjusted; instead,
8615 // the algorithm favours earlier rows and adjusts unspecified column widths
8616 // the first time only. After that, we can't know whether the column has been
8617 // specified explicitly or not. (We could make a note if necessary.)
8618 for (j
= 0; j
< m_rowCount
; j
++)
8620 // First get the overall margins so we can calculate percentage widths based on
8621 // the available content space for all cells on the row
8623 int overallRowContentMargin
= 0;
8624 int visibleCellCount
= 0;
8626 for (i
= 0; i
< m_colCount
; i
++)
8628 wxRichTextBox
* cell
= GetCell(j
, i
);
8629 if (cell
->IsShown())
8631 int cellTotalLeftMargin
= 0, cellTotalRightMargin
= 0, cellTotalTopMargin
= 0, cellTotalBottomMargin
= 0;
8632 GetTotalMargin(dc
, buffer
, cell
->GetAttributes(), cellTotalLeftMargin
, cellTotalRightMargin
, cellTotalTopMargin
, cellTotalBottomMargin
);
8634 overallRowContentMargin
+= (cellTotalLeftMargin
+ cellTotalRightMargin
);
8635 visibleCellCount
++;
8639 // Add in inter-cell padding
8640 overallRowContentMargin
+= ((visibleCellCount
-1) * paddingX
);
8642 int rowContentWidth
= internalTableWidth
- overallRowContentMargin
;
8643 wxSize
rowTableSize(rowContentWidth
, 0);
8644 wxTextAttrDimensionConverter
converter(dc
, scale
, rowTableSize
);
8646 for (i
= 0; i
< m_colCount
; i
++)
8648 wxRichTextBox
* cell
= GetCell(j
, i
);
8649 if (cell
->IsShown())
8652 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8653 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8657 int spans
= wxMin(colSpan
, m_colCount
- i
);
8661 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
8663 cellWidth
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetWidth());
8664 // Override absolute width with minimum width if necessary
8665 if (cell
->GetMinSize().x
> 0 && cellWidth
!=1 && cell
->GetMinSize().x
> cellWidth
)
8666 cellWidth
= cell
->GetMinSize().x
;
8670 // Do we want to do this? It's the only chance we get to
8671 // use the cell's min/max sizes, so we need to work out
8672 // how we're going to balance the unspecified spanning cell
8673 // width with the possibility more-constrained constituent cell widths.
8674 // Say there's a tiny bitmap giving it a max width of 10 pixels. We
8675 // don't want to constraint all the spanned columns to fit into this cell.
8676 // OK, let's say that if any of the constituent columns don't fit,
8677 // then we simply stop constraining the columns; instead, we'll just fit the spanning
8678 // cells to the columns later.
8679 cellWidth
= cell
->GetMinSize().x
;
8680 if (cell
->GetMaxSize().x
> cellWidth
)
8681 cellWidth
= cell
->GetMaxSize().x
;
8684 // Subtract the padding between cells
8685 int spanningWidth
= cellWidth
;
8686 spanningWidth
-= paddingX
* (spans
-1);
8688 if (spanningWidth
> 0)
8690 // Now share the spanning width between columns within that span
8691 // TODO: take into account min widths of columns within the span
8692 int spanningWidthLeft
= spanningWidth
;
8693 int stretchColCount
= 0;
8694 for (k
= i
; k
< (i
+spans
); k
++)
8696 if (colWidths
[k
] > 0) // absolute or proportional width has been specified
8697 spanningWidthLeft
-= colWidths
[k
];
8701 // Now divide what's left between the remaining columns
8703 if (stretchColCount
> 0)
8704 colShare
= spanningWidthLeft
/ stretchColCount
;
8705 int colShareRemainder
= spanningWidthLeft
- (colShare
* stretchColCount
);
8707 // If fixed-width columns are currently too big, then we'll later
8708 // stretch the spanned cell to fit.
8710 if (spanningWidthLeft
> 0)
8712 for (k
= i
; k
< (i
+spans
); k
++)
8714 if (colWidths
[k
] <= 0) // absolute or proportional width has not been specified
8716 int newWidth
= colShare
;
8717 if (k
== (i
+spans
-1))
8718 newWidth
+= colShareRemainder
; // ensure all pixels are filled
8719 colWidths
[k
] = newWidth
;
8730 // (4) Next, share any remaining space out between columns that have not yet been calculated.
8731 // TODO: take into account min widths of columns within the span
8732 int tableWidthMinusPadding
= internalTableWidth
- (m_colCount
-1)*paddingX
;
8733 int widthLeft
= tableWidthMinusPadding
;
8734 int stretchColCount
= 0;
8735 for (i
= 0; i
< m_colCount
; i
++)
8737 // TODO: we need to take into account min widths.
8738 // Subtract min width from width left, then
8739 // add the colShare to the min width
8740 if (colWidths
[i
] > 0) // absolute or proportional width has been specified
8741 widthLeft
-= colWidths
[i
];
8744 if (minColWidths
[i
] > 0)
8745 widthLeft
-= minColWidths
[i
];
8751 // Now divide what's left between the remaining columns
8753 if (stretchColCount
> 0)
8754 colShare
= widthLeft
/ stretchColCount
;
8755 int colShareRemainder
= widthLeft
- (colShare
* stretchColCount
);
8757 // Check we don't have enough space, in which case shrink all columns, overriding
8758 // any absolute/proportional widths
8759 // TODO: actually we would like to divide up the shrinkage according to size.
8760 // How do we calculate the proportions that will achieve this?
8761 // Could first choose an arbitrary value for stretching cells, and then calculate
8762 // factors to multiply each width by.
8763 // TODO: want to record this fact and pass to an iteration that tries e.g. min widths
8764 if (widthLeft
< 0 || (stretchToFitTableWidth
&& (stretchColCount
== 0)))
8766 colShare
= tableWidthMinusPadding
/ m_colCount
;
8767 colShareRemainder
= tableWidthMinusPadding
- (colShare
* m_colCount
);
8768 for (i
= 0; i
< m_colCount
; i
++)
8771 minColWidths
[i
] = 0;
8775 // We have to adjust the columns if either we need to shrink the
8776 // table to fit the parent/table width, or we explicitly set the
8777 // table width and need to stretch out the table.
8778 if (widthLeft
< 0 || stretchToFitTableWidth
)
8780 for (i
= 0; i
< m_colCount
; i
++)
8782 if (colWidths
[i
] <= 0) // absolute or proportional width has not been specified
8784 if (minColWidths
[i
] > 0)
8785 colWidths
[i
] = minColWidths
[i
] + colShare
;
8787 colWidths
[i
] = colShare
;
8788 if (i
== (m_colCount
-1))
8789 colWidths
[i
] += colShareRemainder
; // ensure all pixels are filled
8794 // TODO: if spanned cells have no specified or max width, make them the
8795 // as big as the columns they span. Do this for all spanned cells in all
8796 // rows, of course. Size any spanned cells left over at the end - even if they
8797 // have width > 0, make sure they're limited to the appropriate column edge.
8801 Sort out confusion between content width
8802 and overall width later. For now, assume we specify overall width.
8804 So, now we've laid out the table to fit into the given space
8805 and have used specified widths and minimum widths.
8807 Now we need to consider how we will try to take maximum width into account.
8811 // (??) TODO: take max width into account
8813 // (6) Lay out all cells again with the current values
8816 int y
= availableSpace
.y
;
8817 for (j
= 0; j
< m_rowCount
; j
++)
8819 int x
= availableSpace
.x
; // TODO: take into account centering etc.
8820 int maxCellHeight
= 0;
8821 int maxSpecifiedCellHeight
= 0;
8823 wxArrayInt actualWidths
;
8824 actualWidths
.Add(0, m_colCount
);
8826 wxTextAttrDimensionConverter
converter(dc
, scale
);
8827 for (i
= 0; i
< m_colCount
; i
++)
8829 wxRichTextCell
* cell
= GetCell(j
, i
);
8830 if (cell
->IsShown())
8832 // Get max specified cell height
8833 // Don't handle percentages for height
8834 if (cell
->GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && cell
->GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() != wxTEXT_ATTR_UNITS_PERCENTAGE
)
8836 int h
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetHeight());
8837 if (h
> maxSpecifiedCellHeight
)
8838 maxSpecifiedCellHeight
= h
;
8841 if (colWidths
[i
] > 0) // absolute or proportional width has been specified
8844 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8845 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8847 wxRect availableCellSpace
;
8849 // TODO: take into acount spans
8852 // Calculate the size of this spanning cell from its constituent columns
8854 int spans
= wxMin(colSpan
, m_colCount
- i
);
8855 for (k
= i
; k
< spans
; k
++)
8861 availableCellSpace
= wxRect(x
, y
, xx
, -1);
8864 availableCellSpace
= wxRect(x
, y
, colWidths
[i
], -1);
8866 // Store actual width so we can force cell to be the appropriate width on the final loop
8867 actualWidths
[i
] = availableCellSpace
.GetWidth();
8870 cell
->Invalidate(wxRICHTEXT_ALL
);
8871 cell
->Layout(dc
, context
, availableCellSpace
, availableSpace
, style
);
8873 // TODO: use GetCachedSize().x to compute 'natural' size
8875 x
+= (availableCellSpace
.GetWidth() + paddingX
);
8876 if (cell
->GetCachedSize().y
> maxCellHeight
)
8877 maxCellHeight
= cell
->GetCachedSize().y
;
8882 maxCellHeight
= wxMax(maxCellHeight
, maxSpecifiedCellHeight
);
8884 for (i
= 0; i
< m_colCount
; i
++)
8886 wxRichTextCell
* cell
= GetCell(j
, i
);
8887 if (cell
->IsShown())
8889 wxRect availableCellSpace
= wxRect(cell
->GetPosition(), wxSize(actualWidths
[i
], maxCellHeight
));
8890 // Lay out cell with new height
8891 cell
->Invalidate(wxRICHTEXT_ALL
);
8892 cell
->Layout(dc
, context
, availableCellSpace
, availableSpace
, style
);
8894 // Make sure the cell size really is the appropriate size,
8895 // not the calculated box size
8896 cell
->SetCachedSize(wxSize(actualWidths
[i
], maxCellHeight
));
8898 maxRight
= wxMax(maxRight
, cell
->GetPosition().x
+ cell
->GetCachedSize().x
);
8903 if (j
< (m_rowCount
-1))
8907 // We need to add back the margins etc.
8909 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
8910 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxRight
- availableSpace
.x
, y
- availableSpace
.y
));
8911 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
8912 SetCachedSize(marginRect
.GetSize());
8915 // TODO: calculate max size
8917 SetMaxSize(GetCachedSize());
8920 // TODO: calculate min size
8922 SetMinSize(GetCachedSize());
8925 // TODO: currently we use either a fixed table width or the parent's size.
8926 // We also want to be able to calculate the table width from its content,
8927 // whether using fixed column widths or cell content min/max width.
8928 // Probably need a boolean flag to say whether we need to stretch cells
8929 // to fit the table width, or to simply use min/max cell widths. The
8930 // trouble with this is that if cell widths are not specified, they
8931 // will be tiny; we could use arbitrary defaults but this seems unsatisfactory.
8932 // Anyway, ignoring that problem, we probably need to factor layout into a function
8933 // that can can calculate the maximum unconstrained layout in case table size is
8934 // not specified. Then LayoutToBestSize() can choose to use either parent size to
8935 // constrain Layout(), or the previously-calculated max size to constraint layout.
8940 // Finds the absolute position and row height for the given character position
8941 bool wxRichTextTable::FindPosition(wxDC
& dc
, wxRichTextDrawingContext
& context
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
8943 wxRichTextCell
* child
= GetCell(index
+1);
8946 // Find the position at the start of the child cell, since the table doesn't
8947 // have any caret position of its own.
8948 return child
->FindPosition(dc
, context
, -1, pt
, height
, forceLineStart
);
8954 // Get the cell at the given character position (in the range of the table).
8955 wxRichTextCell
* wxRichTextTable::GetCell(long pos
) const
8957 int row
= 0, col
= 0;
8958 if (GetCellRowColumnPosition(pos
, row
, col
))
8960 return GetCell(row
, col
);
8966 // Get the row/column for a given character position
8967 bool wxRichTextTable::GetCellRowColumnPosition(long pos
, int& row
, int& col
) const
8969 if (m_colCount
== 0 || m_rowCount
== 0)
8972 row
= (int) (pos
/ m_colCount
);
8973 col
= pos
- (row
* m_colCount
);
8975 wxASSERT(row
< m_rowCount
&& col
< m_colCount
);
8977 if (row
< m_rowCount
&& col
< m_colCount
)
8983 // Calculate range, taking row/cell ordering into account instead of relying
8984 // on list ordering.
8985 void wxRichTextTable::CalculateRange(long start
, long& end
)
8987 long current
= start
;
8988 long lastEnd
= current
;
8997 for (i
= 0; i
< m_rowCount
; i
++)
8999 for (j
= 0; j
< m_colCount
; j
++)
9001 wxRichTextCell
* child
= GetCell(i
, j
);
9006 child
->CalculateRange(current
, childEnd
);
9009 current
= childEnd
+ 1;
9014 // A top-level object always has a range of size 1,
9015 // because its children don't count at this level.
9017 m_range
.SetRange(start
, start
);
9019 // An object with no children has zero length
9020 if (m_children
.GetCount() == 0)
9022 m_ownRange
.SetRange(0, lastEnd
);
9025 // Gets the range size.
9026 bool wxRichTextTable::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
9028 return wxRichTextBox::GetRangeSize(range
, size
, descent
, dc
, context
, flags
, position
, partialExtents
);
9031 // Deletes content in the given range.
9032 bool wxRichTextTable::DeleteRange(const wxRichTextRange
& WXUNUSED(range
))
9034 // TODO: implement deletion of cells
9038 // Gets any text in this object for the given range.
9039 wxString
wxRichTextTable::GetTextForRange(const wxRichTextRange
& range
) const
9041 return wxRichTextBox::GetTextForRange(range
);
9044 // Copies this object.
9045 void wxRichTextTable::Copy(const wxRichTextTable
& obj
)
9047 wxRichTextBox::Copy(obj
);
9051 m_rowCount
= obj
.m_rowCount
;
9052 m_colCount
= obj
.m_colCount
;
9054 m_cells
.Add(wxRichTextObjectPtrArray(), m_rowCount
);
9057 for (i
= 0; i
< m_rowCount
; i
++)
9059 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
9060 for (j
= 0; j
< m_colCount
; j
++)
9062 wxRichTextCell
* cell
= wxDynamicCast(obj
.GetCell(i
, j
)->Clone(), wxRichTextCell
);
9070 void wxRichTextTable::ClearTable()
9076 bool wxRichTextTable::CreateTable(int rows
, int cols
)
9083 m_cells
.Add(wxRichTextObjectPtrArray(), rows
);
9086 for (i
= 0; i
< rows
; i
++)
9088 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
9089 for (j
= 0; j
< cols
; j
++)
9091 wxRichTextCell
* cell
= new wxRichTextCell
;
9093 cell
->AddParagraph(wxEmptyString
);
9102 wxRichTextCell
* wxRichTextTable::GetCell(int row
, int col
) const
9104 wxASSERT(row
< m_rowCount
);
9105 wxASSERT(col
< m_colCount
);
9107 if (row
< m_rowCount
&& col
< m_colCount
)
9109 wxRichTextObjectPtrArray
& colArray
= m_cells
[row
];
9110 wxRichTextObject
* obj
= colArray
[col
];
9111 return wxDynamicCast(obj
, wxRichTextCell
);
9117 // Returns a selection object specifying the selections between start and end character positions.
9118 // For example, a table would deduce what cells (of range length 1) are selected when dragging across the table.
9119 wxRichTextSelection
wxRichTextTable::GetSelection(long start
, long end
) const
9121 wxRichTextSelection selection
;
9122 selection
.SetContainer((wxRichTextTable
*) this);
9131 wxASSERT( start
>= 0 && end
< (m_colCount
* m_rowCount
));
9133 if (end
>= (m_colCount
* m_rowCount
))
9136 // We need to find the rectangle of cells that is described by the rectangle
9137 // with start, end as the diagonal. Make sure we don't add cells that are
9138 // not currenty visible because they are overlapped by spanning cells.
9140 --------------------------
9141 | 0 | 1 | 2 | 3 | 4 |
9142 --------------------------
9143 | 5 | 6 | 7 | 8 | 9 |
9144 --------------------------
9145 | 10 | 11 | 12 | 13 | 14 |
9146 --------------------------
9147 | 15 | 16 | 17 | 18 | 19 |
9148 --------------------------
9150 Let's say we select 6 -> 18.
9152 Left and right edge cols of rectangle are 1 and 3 inclusive. Find least/greatest to find
9153 which is left and which is right.
9155 Top and bottom edge rows are 1 and 3 inclusive. Again, find least/greatest to find top and bottom.
9157 Now go through rows from 1 to 3 and only add cells that are (a) within above column range
9163 int leftCol
= start
- m_colCount
* int(start
/m_colCount
);
9164 int rightCol
= end
- m_colCount
* int(end
/m_colCount
);
9166 int topRow
= int(start
/m_colCount
);
9167 int bottomRow
= int(end
/m_colCount
);
9169 if (leftCol
> rightCol
)
9176 if (topRow
> bottomRow
)
9178 int tmp
= bottomRow
;
9184 for (i
= topRow
; i
<= bottomRow
; i
++)
9186 for (j
= leftCol
; j
<= rightCol
; j
++)
9188 wxRichTextCell
* cell
= GetCell(i
, j
);
9189 if (cell
&& cell
->IsShown())
9190 selection
.Add(cell
->GetRange());
9197 // Sets the attributes for the cells specified by the selection.
9198 bool wxRichTextTable::SetCellStyle(const wxRichTextSelection
& selection
, const wxRichTextAttr
& style
, int flags
)
9200 if (selection
.GetContainer() != this)
9203 wxRichTextBuffer
* buffer
= GetBuffer();
9204 bool haveControl
= (buffer
&& buffer
->GetRichTextCtrl() != NULL
);
9205 bool withUndo
= haveControl
&& ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
9208 buffer
->BeginBatchUndo(_("Set Cell Style"));
9210 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
9213 wxRichTextCell
* cell
= wxDynamicCast(node
->GetData(), wxRichTextCell
);
9214 if (cell
&& selection
.WithinSelection(cell
->GetRange().GetStart()))
9215 SetStyle(cell
, style
, flags
);
9216 node
= node
->GetNext();
9219 // Do action, or delay it until end of batch.
9221 buffer
->EndBatchUndo();
9226 bool wxRichTextTable::DeleteRows(int startRow
, int noRows
)
9228 wxASSERT((startRow
+ noRows
) < m_rowCount
);
9229 if ((startRow
+ noRows
) >= m_rowCount
)
9233 for (i
= startRow
; i
< (startRow
+noRows
); i
++)
9235 wxRichTextObjectPtrArray
& colArray
= m_cells
[startRow
];
9236 for (j
= 0; j
< (int) colArray
.GetCount(); j
++)
9238 wxRichTextObject
* cell
= colArray
[j
];
9239 RemoveChild(cell
, true);
9242 // Keep deleting at the same position, since we move all
9244 m_cells
.RemoveAt(startRow
);
9247 m_rowCount
= m_rowCount
- noRows
;
9252 bool wxRichTextTable::DeleteColumns(int startCol
, int noCols
)
9254 wxASSERT((startCol
+ noCols
) < m_colCount
);
9255 if ((startCol
+ noCols
) >= m_colCount
)
9258 bool deleteRows
= (noCols
== m_colCount
);
9261 for (i
= 0; i
< m_rowCount
; i
++)
9263 wxRichTextObjectPtrArray
& colArray
= m_cells
[deleteRows
? 0 : i
];
9264 for (j
= startCol
; j
< (startCol
+noCols
); j
++)
9266 wxRichTextObject
* cell
= colArray
[j
];
9267 RemoveChild(cell
, true);
9271 m_cells
.RemoveAt(0);
9276 m_colCount
= m_colCount
- noCols
;
9281 bool wxRichTextTable::AddRows(int startRow
, int noRows
, const wxRichTextAttr
& attr
)
9283 wxASSERT(startRow
<= m_rowCount
);
9284 if (startRow
> m_rowCount
)
9288 for (i
= 0; i
< noRows
; i
++)
9291 if (startRow
== m_rowCount
)
9293 m_cells
.Add(wxRichTextObjectPtrArray());
9294 idx
= m_cells
.GetCount() - 1;
9298 m_cells
.Insert(wxRichTextObjectPtrArray(), startRow
+i
);
9302 wxRichTextObjectPtrArray
& colArray
= m_cells
[idx
];
9303 for (j
= 0; j
< m_colCount
; j
++)
9305 wxRichTextCell
* cell
= new wxRichTextCell
;
9306 cell
->GetAttributes() = attr
;
9313 m_rowCount
= m_rowCount
+ noRows
;
9317 bool wxRichTextTable::AddColumns(int startCol
, int noCols
, const wxRichTextAttr
& attr
)
9319 wxASSERT(startCol
<= m_colCount
);
9320 if (startCol
> m_colCount
)
9324 for (i
= 0; i
< m_rowCount
; i
++)
9326 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
9327 for (j
= 0; j
< noCols
; j
++)
9329 wxRichTextCell
* cell
= new wxRichTextCell
;
9330 cell
->GetAttributes() = attr
;
9334 if (startCol
== m_colCount
)
9337 colArray
.Insert(cell
, startCol
+j
);
9341 m_colCount
= m_colCount
+ noCols
;
9346 // Edit properties via a GUI
9347 bool wxRichTextTable::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
9349 wxRichTextObjectPropertiesDialog
boxDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Table Properties"));
9350 boxDlg
.SetAttributes(GetAttributes());
9352 if (boxDlg
.ShowModal() == wxID_OK
)
9354 boxDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
9362 * Module to initialise and clean up handlers
9365 class wxRichTextModule
: public wxModule
9367 DECLARE_DYNAMIC_CLASS(wxRichTextModule
)
9369 wxRichTextModule() {}
9372 wxRichTextBuffer::SetRenderer(new wxRichTextStdRenderer
);
9373 wxRichTextBuffer::InitStandardHandlers();
9374 wxRichTextParagraph::InitDefaultTabs();
9379 wxRichTextBuffer::CleanUpHandlers();
9380 wxRichTextBuffer::CleanUpDrawingHandlers();
9381 wxRichTextDecimalToRoman(-1);
9382 wxRichTextParagraph::ClearDefaultTabs();
9383 wxRichTextCtrl::ClearAvailableFontNames();
9384 wxRichTextBuffer::SetRenderer(NULL
);
9388 IMPLEMENT_DYNAMIC_CLASS(wxRichTextModule
, wxModule
)
9391 // If the richtext lib is dynamically loaded after the app has already started
9392 // (such as from wxPython) then the built-in module system will not init this
9393 // module. Provide this function to do it manually.
9394 void wxRichTextModuleInit()
9396 wxModule
* module = new wxRichTextModule
;
9398 wxModule::RegisterModule(module);
9403 * Commands for undo/redo
9407 wxRichTextCommand::wxRichTextCommand(const wxString
& name
, wxRichTextCommandId id
, wxRichTextBuffer
* buffer
,
9408 wxRichTextParagraphLayoutBox
* container
, wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
): wxCommand(true, name
)
9410 /* wxRichTextAction* action = */ new wxRichTextAction(this, name
, id
, buffer
, container
, ctrl
, ignoreFirstTime
);
9413 wxRichTextCommand::wxRichTextCommand(const wxString
& name
): wxCommand(true, name
)
9417 wxRichTextCommand::~wxRichTextCommand()
9422 void wxRichTextCommand::AddAction(wxRichTextAction
* action
)
9424 if (!m_actions
.Member(action
))
9425 m_actions
.Append(action
);
9428 bool wxRichTextCommand::Do()
9430 for (wxList::compatibility_iterator node
= m_actions
.GetFirst(); node
; node
= node
->GetNext())
9432 wxRichTextAction
* action
= (wxRichTextAction
*) node
->GetData();
9439 bool wxRichTextCommand::Undo()
9441 for (wxList::compatibility_iterator node
= m_actions
.GetLast(); node
; node
= node
->GetPrevious())
9443 wxRichTextAction
* action
= (wxRichTextAction
*) node
->GetData();
9450 void wxRichTextCommand::ClearActions()
9452 WX_CLEAR_LIST(wxList
, m_actions
);
9460 wxRichTextAction::wxRichTextAction(wxRichTextCommand
* cmd
, const wxString
& name
, wxRichTextCommandId id
,
9461 wxRichTextBuffer
* buffer
, wxRichTextParagraphLayoutBox
* container
,
9462 wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
)
9466 m_containerAddress
.Create(buffer
, container
);
9467 m_ignoreThis
= ignoreFirstTime
;
9472 m_newParagraphs
.SetDefaultStyle(buffer
->GetDefaultStyle());
9473 m_newParagraphs
.SetBasicStyle(buffer
->GetBasicStyle());
9475 cmd
->AddAction(this);
9478 wxRichTextAction::~wxRichTextAction()
9484 // Returns the container that this action refers to, using the container address and top-level buffer.
9485 wxRichTextParagraphLayoutBox
* wxRichTextAction::GetContainer() const
9487 wxRichTextParagraphLayoutBox
* container
= wxDynamicCast(GetContainerAddress().GetObject(m_buffer
), wxRichTextParagraphLayoutBox
);
9492 void wxRichTextAction::CalculateRefreshOptimizations(wxArrayInt
& optimizationLineCharPositions
, wxArrayInt
& optimizationLineYPositions
)
9494 // Store a list of line start character and y positions so we can figure out which area
9495 // we need to refresh
9497 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9498 wxRichTextParagraphLayoutBox
* container
= GetContainer();
9499 wxASSERT(container
!= NULL
);
9503 // NOTE: we're assuming that the buffer is laid out correctly at this point.
9504 // If we had several actions, which only invalidate and leave layout until the
9505 // paint handler is called, then this might not be true. So we may need to switch
9506 // optimisation on only when we're simply adding text and not simultaneously
9507 // deleting a selection, for example. Or, we make sure the buffer is laid out correctly
9508 // first, but of course this means we'll be doing it twice.
9509 if (!m_buffer
->IsDirty() && m_ctrl
) // can only do optimisation if the buffer is already laid out correctly
9511 wxSize clientSize
= m_ctrl
->GetClientSize();
9512 wxPoint firstVisiblePt
= m_ctrl
->GetFirstVisiblePoint();
9513 int lastY
= firstVisiblePt
.y
+ clientSize
.y
;
9515 wxRichTextParagraph
* para
= container
->GetParagraphAtPosition(GetRange().GetStart());
9516 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(para
);
9519 wxRichTextParagraph
* child
= (wxRichTextParagraph
*) node
->GetData();
9520 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
9523 wxRichTextLine
* line
= node2
->GetData();
9524 wxPoint pt
= line
->GetAbsolutePosition();
9525 wxRichTextRange range
= line
->GetAbsoluteRange();
9529 node2
= wxRichTextLineList::compatibility_iterator();
9530 node
= wxRichTextObjectList::compatibility_iterator();
9532 else if (range
.GetStart() > GetPosition() && pt
.y
>= firstVisiblePt
.y
)
9534 optimizationLineCharPositions
.Add(range
.GetStart());
9535 optimizationLineYPositions
.Add(pt
.y
);
9539 node2
= node2
->GetNext();
9543 node
= node
->GetNext();
9549 bool wxRichTextAction::Do()
9551 m_buffer
->Modify(true);
9553 wxRichTextParagraphLayoutBox
* container
= GetContainer();
9554 wxASSERT(container
!= NULL
);
9560 case wxRICHTEXT_INSERT
:
9562 // Store a list of line start character and y positions so we can figure out which area
9563 // we need to refresh
9564 wxArrayInt optimizationLineCharPositions
;
9565 wxArrayInt optimizationLineYPositions
;
9567 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9568 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
9571 container
->InsertFragment(GetRange().GetStart(), m_newParagraphs
);
9572 container
->UpdateRanges();
9574 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9575 // Layout() would stop prematurely at the top level.
9576 container
->InvalidateHierarchy(wxRichTextRange(wxMax(0, GetRange().GetStart()-1), GetRange().GetEnd()));
9578 long newCaretPosition
= GetPosition() + m_newParagraphs
.GetOwnRange().GetLength();
9580 // Character position to caret position
9581 newCaretPosition
--;
9583 // Don't take into account the last newline
9584 if (m_newParagraphs
.GetPartialParagraph())
9585 newCaretPosition
--;
9587 if (m_newParagraphs
.GetChildren().GetCount() > 1)
9589 wxRichTextObject
* p
= (wxRichTextObject
*) m_newParagraphs
.GetChildren().GetLast()->GetData();
9590 if (p
->GetRange().GetLength() == 1)
9591 newCaretPosition
--;
9594 newCaretPosition
= wxMin(newCaretPosition
, (container
->GetOwnRange().GetEnd()-1));
9596 UpdateAppearance(newCaretPosition
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */);
9598 wxRichTextEvent
cmdEvent(
9599 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED
,
9600 m_ctrl
? m_ctrl
->GetId() : -1);
9601 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9602 cmdEvent
.SetRange(GetRange());
9603 cmdEvent
.SetPosition(GetRange().GetStart());
9604 cmdEvent
.SetContainer(container
);
9606 m_buffer
->SendEvent(cmdEvent
);
9610 case wxRICHTEXT_DELETE
:
9612 wxArrayInt optimizationLineCharPositions
;
9613 wxArrayInt optimizationLineYPositions
;
9615 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9616 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
9619 container
->DeleteRange(GetRange());
9620 container
->UpdateRanges();
9621 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9622 // Layout() would stop prematurely at the top level.
9623 container
->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
9625 long caretPos
= GetRange().GetStart()-1;
9626 if (caretPos
>= container
->GetOwnRange().GetEnd())
9629 UpdateAppearance(caretPos
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */);
9631 wxRichTextEvent
cmdEvent(
9632 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED
,
9633 m_ctrl
? m_ctrl
->GetId() : -1);
9634 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9635 cmdEvent
.SetRange(GetRange());
9636 cmdEvent
.SetPosition(GetRange().GetStart());
9637 cmdEvent
.SetContainer(container
);
9639 m_buffer
->SendEvent(cmdEvent
);
9643 case wxRICHTEXT_CHANGE_STYLE
:
9644 case wxRICHTEXT_CHANGE_PROPERTIES
:
9646 ApplyParagraphs(GetNewParagraphs());
9648 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9649 // Layout() would stop prematurely at the top level.
9650 container
->InvalidateHierarchy(GetRange());
9652 UpdateAppearance(GetPosition());
9654 wxRichTextEvent
cmdEvent(
9655 m_cmdId
== wxRICHTEXT_CHANGE_STYLE
? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
: wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED
,
9656 m_ctrl
? m_ctrl
->GetId() : -1);
9657 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9658 cmdEvent
.SetRange(GetRange());
9659 cmdEvent
.SetPosition(GetRange().GetStart());
9660 cmdEvent
.SetContainer(container
);
9662 m_buffer
->SendEvent(cmdEvent
);
9666 case wxRICHTEXT_CHANGE_ATTRIBUTES
:
9668 wxRichTextObject
* obj
= m_objectAddress
.GetObject(m_buffer
); // container->GetChildAtPosition(GetRange().GetStart());
9671 wxRichTextAttr oldAttr
= obj
->GetAttributes();
9672 obj
->GetAttributes() = m_attributes
;
9673 m_attributes
= oldAttr
;
9676 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9677 // Layout() would stop prematurely at the top level.
9678 container
->InvalidateHierarchy(GetRange());
9680 UpdateAppearance(GetPosition());
9682 wxRichTextEvent
cmdEvent(
9683 wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
,
9684 m_ctrl
? m_ctrl
->GetId() : -1);
9685 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9686 cmdEvent
.SetRange(GetRange());
9687 cmdEvent
.SetPosition(GetRange().GetStart());
9688 cmdEvent
.SetContainer(container
);
9690 m_buffer
->SendEvent(cmdEvent
);
9694 case wxRICHTEXT_CHANGE_OBJECT
:
9696 wxRichTextObject
* obj
= m_objectAddress
.GetObject(m_buffer
);
9697 // wxRichTextObject* obj = container->GetChildAtPosition(GetRange().GetStart());
9698 if (obj
&& m_object
)
9700 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(obj
);
9703 wxRichTextObject
* obj
= node
->GetData();
9704 node
->SetData(m_object
);
9709 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9710 // Layout() would stop prematurely at the top level.
9711 container
->InvalidateHierarchy(GetRange());
9713 UpdateAppearance(GetPosition());
9715 // TODO: send new kind of modification event
9726 bool wxRichTextAction::Undo()
9728 m_buffer
->Modify(true);
9730 wxRichTextParagraphLayoutBox
* container
= GetContainer();
9731 wxASSERT(container
!= NULL
);
9737 case wxRICHTEXT_INSERT
:
9739 wxArrayInt optimizationLineCharPositions
;
9740 wxArrayInt optimizationLineYPositions
;
9742 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9743 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
9746 container
->DeleteRange(GetRange());
9747 container
->UpdateRanges();
9748 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9749 // Layout() would stop prematurely at the top level.
9750 container
->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
9752 long newCaretPosition
= GetPosition() - 1;
9754 UpdateAppearance(newCaretPosition
, true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */);
9756 wxRichTextEvent
cmdEvent(
9757 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED
,
9758 m_ctrl
? m_ctrl
->GetId() : -1);
9759 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9760 cmdEvent
.SetRange(GetRange());
9761 cmdEvent
.SetPosition(GetRange().GetStart());
9762 cmdEvent
.SetContainer(container
);
9764 m_buffer
->SendEvent(cmdEvent
);
9768 case wxRICHTEXT_DELETE
:
9770 wxArrayInt optimizationLineCharPositions
;
9771 wxArrayInt optimizationLineYPositions
;
9773 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9774 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
9777 container
->InsertFragment(GetRange().GetStart(), m_oldParagraphs
);
9778 container
->UpdateRanges();
9779 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9780 // Layout() would stop prematurely at the top level.
9781 container
->InvalidateHierarchy(GetRange());
9783 UpdateAppearance(GetPosition(), true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */);
9785 wxRichTextEvent
cmdEvent(
9786 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED
,
9787 m_ctrl
? m_ctrl
->GetId() : -1);
9788 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9789 cmdEvent
.SetRange(GetRange());
9790 cmdEvent
.SetPosition(GetRange().GetStart());
9791 cmdEvent
.SetContainer(container
);
9793 m_buffer
->SendEvent(cmdEvent
);
9797 case wxRICHTEXT_CHANGE_STYLE
:
9798 case wxRICHTEXT_CHANGE_PROPERTIES
:
9800 ApplyParagraphs(GetOldParagraphs());
9801 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9802 // Layout() would stop prematurely at the top level.
9803 container
->InvalidateHierarchy(GetRange());
9805 UpdateAppearance(GetPosition());
9807 wxRichTextEvent
cmdEvent(
9808 m_cmdId
== wxRICHTEXT_CHANGE_STYLE
? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
: wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED
,
9809 m_ctrl
? m_ctrl
->GetId() : -1);
9810 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
9811 cmdEvent
.SetRange(GetRange());
9812 cmdEvent
.SetPosition(GetRange().GetStart());
9813 cmdEvent
.SetContainer(container
);
9815 m_buffer
->SendEvent(cmdEvent
);
9819 case wxRICHTEXT_CHANGE_ATTRIBUTES
:
9820 case wxRICHTEXT_CHANGE_OBJECT
:
9831 /// Update the control appearance
9832 void wxRichTextAction::UpdateAppearance(long caretPosition
, bool sendUpdateEvent
, wxArrayInt
* optimizationLineCharPositions
, wxArrayInt
* optimizationLineYPositions
, bool isDoCmd
)
9834 wxRichTextParagraphLayoutBox
* container
= GetContainer();
9835 wxASSERT(container
!= NULL
);
9841 m_ctrl
->SetFocusObject(container
);
9842 m_ctrl
->SetCaretPosition(caretPosition
);
9844 if (!m_ctrl
->IsFrozen())
9846 wxRect containerRect
= container
->GetRect();
9848 m_ctrl
->LayoutContent();
9850 // Refresh everything if there were floating objects or the container changed size
9851 // (we can't yet optimize in these cases, since more complex interaction with other content occurs)
9852 if (container
->GetFloatingObjectCount() > 0 || (container
->GetParent() && containerRect
!= container
->GetRect()))
9854 m_ctrl
->Refresh(false);
9858 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9859 // Find refresh rectangle if we are in a position to optimise refresh
9860 if ((m_cmdId
== wxRICHTEXT_INSERT
|| m_cmdId
== wxRICHTEXT_DELETE
) && optimizationLineCharPositions
)
9864 wxSize clientSize
= m_ctrl
->GetClientSize();
9865 wxPoint firstVisiblePt
= m_ctrl
->GetFirstVisiblePoint();
9867 // Start/end positions
9869 int lastY
= firstVisiblePt
.y
+ clientSize
.y
;
9871 bool foundEnd
= false;
9873 // position offset - how many characters were inserted
9874 int positionOffset
= GetRange().GetLength();
9876 // Determine whether this is Do or Undo, and adjust positionOffset accordingly
9877 if ((m_cmdId
== wxRICHTEXT_DELETE
&& isDoCmd
) || (m_cmdId
== wxRICHTEXT_INSERT
&& !isDoCmd
))
9878 positionOffset
= - positionOffset
;
9880 // find the first line which is being drawn at the same position as it was
9881 // before. Since we're talking about a simple insertion, we can assume
9882 // that the rest of the window does not need to be redrawn.
9884 wxRichTextParagraph
* para
= container
->GetParagraphAtPosition(GetPosition());
9885 // Since we support floating layout, we should redraw the whole para instead of just
9886 // the first line touching the invalid range.
9889 firstY
= para
->GetPosition().y
;
9892 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(para
);
9895 wxRichTextParagraph
* child
= (wxRichTextParagraph
*) node
->GetData();
9896 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
9899 wxRichTextLine
* line
= node2
->GetData();
9900 wxPoint pt
= line
->GetAbsolutePosition();
9901 wxRichTextRange range
= line
->GetAbsoluteRange();
9903 // we want to find the first line that is in the same position
9904 // as before. This will mean we're at the end of the changed text.
9906 if (pt
.y
> lastY
) // going past the end of the window, no more info
9908 node2
= wxRichTextLineList::compatibility_iterator();
9909 node
= wxRichTextObjectList::compatibility_iterator();
9911 // Detect last line in the buffer
9912 else if (!node2
->GetNext() && para
->GetRange().Contains(container
->GetOwnRange().GetEnd()))
9914 // If deleting text, make sure we refresh below as well as above
9915 if (positionOffset
>= 0)
9918 lastY
= pt
.y
+ line
->GetSize().y
;
9921 node2
= wxRichTextLineList::compatibility_iterator();
9922 node
= wxRichTextObjectList::compatibility_iterator();
9928 // search for this line being at the same position as before
9929 for (i
= 0; i
< optimizationLineCharPositions
->GetCount(); i
++)
9931 if (((*optimizationLineCharPositions
)[i
] + positionOffset
== range
.GetStart()) &&
9932 ((*optimizationLineYPositions
)[i
] == pt
.y
))
9934 // Stop, we're now the same as we were
9939 node2
= wxRichTextLineList::compatibility_iterator();
9940 node
= wxRichTextObjectList::compatibility_iterator();
9948 node2
= node2
->GetNext();
9952 node
= node
->GetNext();
9955 firstY
= wxMax(firstVisiblePt
.y
, firstY
);
9957 lastY
= firstVisiblePt
.y
+ clientSize
.y
;
9959 // Convert to device coordinates
9960 wxRect
rect(m_ctrl
->GetPhysicalPoint(wxPoint(firstVisiblePt
.x
, firstY
)), wxSize(clientSize
.x
, lastY
- firstY
));
9961 m_ctrl
->RefreshRect(rect
);
9965 m_ctrl
->Refresh(false);
9967 m_ctrl
->PositionCaret();
9969 // This causes styles to persist when doing programmatic
9970 // content creation except when Freeze/Thaw is used, so
9971 // disable this and check for the consequences.
9972 // m_ctrl->SetDefaultStyleToCursorStyle();
9974 if (sendUpdateEvent
)
9975 wxTextCtrl::SendTextUpdatedEvent(m_ctrl
);
9980 /// Replace the buffer paragraphs with the new ones.
9981 void wxRichTextAction::ApplyParagraphs(const wxRichTextParagraphLayoutBox
& fragment
)
9983 wxRichTextParagraphLayoutBox
* container
= GetContainer();
9984 wxASSERT(container
!= NULL
);
9988 wxRichTextObjectList::compatibility_iterator node
= fragment
.GetChildren().GetFirst();
9991 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
9992 wxASSERT (para
!= NULL
);
9994 // We'll replace the existing paragraph by finding the paragraph at this position,
9995 // delete its node data, and setting a copy as the new node data.
9996 // TODO: make more efficient by simply swapping old and new paragraph objects.
9998 wxRichTextParagraph
* existingPara
= container
->GetParagraphAtPosition(para
->GetRange().GetStart());
10001 wxRichTextObjectList::compatibility_iterator bufferParaNode
= container
->GetChildren().Find(existingPara
);
10002 if (bufferParaNode
)
10004 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(*para
);
10005 newPara
->SetParent(container
);
10007 bufferParaNode
->SetData(newPara
);
10009 delete existingPara
;
10013 node
= node
->GetNext();
10020 * This stores beginning and end positions for a range of data.
10023 WX_DEFINE_OBJARRAY(wxRichTextRangeArray
);
10025 /// Limit this range to be within 'range'
10026 bool wxRichTextRange::LimitTo(const wxRichTextRange
& range
)
10028 if (m_start
< range
.m_start
)
10029 m_start
= range
.m_start
;
10031 if (m_end
> range
.m_end
)
10032 m_end
= range
.m_end
;
10038 * wxRichTextImage implementation
10039 * This object represents an image.
10042 IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage
, wxRichTextObject
)
10044 wxRichTextImage::wxRichTextImage(const wxImage
& image
, wxRichTextObject
* parent
, wxRichTextAttr
* charStyle
):
10045 wxRichTextObject(parent
)
10047 m_imageBlock
.MakeImageBlockDefaultQuality(image
, wxBITMAP_TYPE_PNG
);
10049 SetAttributes(*charStyle
);
10052 wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock
& imageBlock
, wxRichTextObject
* parent
, wxRichTextAttr
* charStyle
):
10053 wxRichTextObject(parent
)
10055 m_imageBlock
= imageBlock
;
10057 SetAttributes(*charStyle
);
10060 /// Create a cached image at the required size
10061 bool wxRichTextImage::LoadImageCache(wxDC
& dc
, bool resetCache
)
10063 if (resetCache
|| !m_imageCache
.IsOk() /* || m_imageCache.GetWidth() != size.x || m_imageCache.GetHeight() != size.y */)
10065 if (!m_imageBlock
.IsOk())
10069 m_imageBlock
.Load(image
);
10073 int width
= image
.GetWidth();
10074 int height
= image
.GetHeight();
10076 if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetWidth().GetValue() > 0)
10078 if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
10079 width
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetWidth().GetValue());
10081 width
= GetAttributes().GetTextBoxAttr().GetWidth().GetValue();
10083 if (GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetHeight().GetValue() > 0)
10085 if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
10086 height
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetHeight().GetValue());
10088 height
= GetAttributes().GetTextBoxAttr().GetHeight().GetValue();
10091 if (image
.GetWidth() == width
&& image
.GetHeight() == height
)
10092 m_imageCache
= wxBitmap(image
);
10095 // If the original width and height is small, e.g. 400 or below,
10096 // scale up and then down to improve image quality. This can make
10097 // a big difference, with not much performance hit.
10098 int upscaleThreshold
= 400;
10100 if (image
.GetWidth() <= upscaleThreshold
|| image
.GetHeight() <= upscaleThreshold
)
10102 img
= image
.Scale(image
.GetWidth()*2, image
.GetHeight()*2);
10103 img
.Rescale(width
, height
, wxIMAGE_QUALITY_HIGH
);
10106 img
= image
.Scale(width
, height
, wxIMAGE_QUALITY_HIGH
);
10107 m_imageCache
= wxBitmap(img
);
10111 return m_imageCache
.IsOk();
10115 bool wxRichTextImage::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int WXUNUSED(descent
), int WXUNUSED(style
))
10120 // Don't need cached size AFAIK
10121 // wxSize size = GetCachedSize();
10122 if (!LoadImageCache(dc
))
10125 wxRichTextAttr
attr(GetAttributes());
10126 context
.ApplyVirtualAttributes(attr
, this);
10128 DrawBoxAttributes(dc
, GetBuffer(), attr
, wxRect(rect
.GetPosition(), GetCachedSize()));
10131 int y
= rect
.y
+ (rect
.height
- m_imageCache
.GetHeight());
10133 dc
.DrawBitmap(m_imageCache
, rect
.x
, y
, true);
10136 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
10137 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
10138 marginRect
= rect
; // outer rectangle, will calculate contentRect
10139 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
10141 dc
.DrawBitmap(m_imageCache
, contentRect
.x
, contentRect
.y
, true);
10143 if (selection
.WithinSelection(range
.GetStart(), this))
10145 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
10146 wxCheckSetPen(dc
, *wxBLACK_PEN
);
10147 dc
.SetLogicalFunction(wxINVERT
);
10148 dc
.DrawRectangle(contentRect
);
10149 dc
.SetLogicalFunction(wxCOPY
);
10155 /// Lay the item out
10156 bool wxRichTextImage::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& WXUNUSED(parentRect
), int WXUNUSED(style
))
10158 if (!LoadImageCache(dc
))
10161 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
10162 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
10163 contentRect
= wxRect(wxPoint(0,0), imageSize
);
10165 wxRichTextAttr
attr(GetAttributes());
10166 context
.ApplyVirtualAttributes(attr
, this);
10168 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
10170 wxSize overallSize
= marginRect
.GetSize();
10172 SetCachedSize(overallSize
);
10173 SetMaxSize(overallSize
);
10174 SetMinSize(overallSize
);
10175 SetPosition(rect
.GetPosition());
10180 /// Get/set the object size for the given range. Returns false if the range
10181 /// is invalid for this object.
10182 bool wxRichTextImage::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& WXUNUSED(descent
), wxDC
& dc
, wxRichTextDrawingContext
& context
, int WXUNUSED(flags
), wxPoint
WXUNUSED(position
), wxArrayInt
* partialExtents
) const
10184 if (!range
.IsWithin(GetRange()))
10187 if (!((wxRichTextImage
*)this)->LoadImageCache(dc
))
10189 size
.x
= 0; size
.y
= 0;
10190 if (partialExtents
)
10191 partialExtents
->Add(0);
10195 wxRichTextAttr
attr(GetAttributes());
10196 context
.ApplyVirtualAttributes(attr
, (wxRichTextObject
*) this);
10198 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
10199 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
10200 contentRect
= wxRect(wxPoint(0,0), imageSize
);
10201 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
10203 wxSize overallSize
= marginRect
.GetSize();
10205 if (partialExtents
)
10206 partialExtents
->Add(overallSize
.x
);
10208 size
= overallSize
;
10213 // Get the 'natural' size for an object. For an image, it would be the
10215 wxTextAttrSize
wxRichTextImage::GetNaturalSize() const
10217 wxTextAttrSize size
;
10218 if (GetImageCache().IsOk())
10220 size
.SetWidth(GetImageCache().GetWidth(), wxTEXT_ATTR_UNITS_PIXELS
);
10221 size
.SetHeight(GetImageCache().GetHeight(), wxTEXT_ATTR_UNITS_PIXELS
);
10228 void wxRichTextImage::Copy(const wxRichTextImage
& obj
)
10230 wxRichTextObject::Copy(obj
);
10232 m_imageBlock
= obj
.m_imageBlock
;
10235 /// Edit properties via a GUI
10236 bool wxRichTextImage::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
10238 wxRichTextObjectPropertiesDialog
imageDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Picture Properties"));
10239 imageDlg
.SetAttributes(GetAttributes());
10241 if (imageDlg
.ShowModal() == wxID_OK
)
10243 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
10244 // indeterminate in the object.
10245 imageDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
10257 /// Compare two attribute objects
10258 bool wxTextAttrEq(const wxRichTextAttr
& attr1
, const wxRichTextAttr
& attr2
)
10260 return (attr1
== attr2
);
10263 // Partial equality test taking flags into account
10264 bool wxTextAttrEqPartial(const wxRichTextAttr
& attr1
, const wxRichTextAttr
& attr2
)
10266 return attr1
.EqPartial(attr2
);
10270 bool wxRichTextTabsEq(const wxArrayInt
& tabs1
, const wxArrayInt
& tabs2
)
10272 if (tabs1
.GetCount() != tabs2
.GetCount())
10276 for (i
= 0; i
< tabs1
.GetCount(); i
++)
10278 if (tabs1
[i
] != tabs2
[i
])
10284 bool wxRichTextApplyStyle(wxRichTextAttr
& destStyle
, const wxRichTextAttr
& style
, wxRichTextAttr
* compareWith
)
10286 return destStyle
.Apply(style
, compareWith
);
10289 // Remove attributes
10290 bool wxRichTextRemoveStyle(wxRichTextAttr
& destStyle
, const wxRichTextAttr
& style
)
10292 return destStyle
.RemoveStyle(style
);
10295 /// Combine two bitlists, specifying the bits of interest with separate flags.
10296 bool wxRichTextCombineBitlists(int& valueA
, int valueB
, int& flagsA
, int flagsB
)
10298 return wxRichTextAttr::CombineBitlists(valueA
, valueB
, flagsA
, flagsB
);
10301 /// Compare two bitlists
10302 bool wxRichTextBitlistsEqPartial(int valueA
, int valueB
, int flags
)
10304 return wxRichTextAttr::BitlistsEqPartial(valueA
, valueB
, flags
);
10307 /// Split into paragraph and character styles
10308 bool wxRichTextSplitParaCharStyles(const wxRichTextAttr
& style
, wxRichTextAttr
& parStyle
, wxRichTextAttr
& charStyle
)
10310 return wxRichTextAttr::SplitParaCharStyles(style
, parStyle
, charStyle
);
10313 /// Convert a decimal to Roman numerals
10314 wxString
wxRichTextDecimalToRoman(long n
)
10316 static wxArrayInt decimalNumbers
;
10317 static wxArrayString romanNumbers
;
10322 decimalNumbers
.Clear();
10323 romanNumbers
.Clear();
10324 return wxEmptyString
;
10327 if (decimalNumbers
.GetCount() == 0)
10329 #define wxRichTextAddDecRom(n, r) decimalNumbers.Add(n); romanNumbers.Add(r);
10331 wxRichTextAddDecRom(1000, wxT("M"));
10332 wxRichTextAddDecRom(900, wxT("CM"));
10333 wxRichTextAddDecRom(500, wxT("D"));
10334 wxRichTextAddDecRom(400, wxT("CD"));
10335 wxRichTextAddDecRom(100, wxT("C"));
10336 wxRichTextAddDecRom(90, wxT("XC"));
10337 wxRichTextAddDecRom(50, wxT("L"));
10338 wxRichTextAddDecRom(40, wxT("XL"));
10339 wxRichTextAddDecRom(10, wxT("X"));
10340 wxRichTextAddDecRom(9, wxT("IX"));
10341 wxRichTextAddDecRom(5, wxT("V"));
10342 wxRichTextAddDecRom(4, wxT("IV"));
10343 wxRichTextAddDecRom(1, wxT("I"));
10349 while (n
> 0 && i
< 13)
10351 if (n
>= decimalNumbers
[i
])
10353 n
-= decimalNumbers
[i
];
10354 roman
+= romanNumbers
[i
];
10361 if (roman
.IsEmpty())
10367 * wxRichTextFileHandler
10368 * Base class for file handlers
10371 IMPLEMENT_CLASS(wxRichTextFileHandler
, wxObject
)
10373 #if wxUSE_FFILE && wxUSE_STREAMS
10374 bool wxRichTextFileHandler::LoadFile(wxRichTextBuffer
*buffer
, const wxString
& filename
)
10376 wxFFileInputStream
stream(filename
);
10378 return LoadFile(buffer
, stream
);
10383 bool wxRichTextFileHandler::SaveFile(wxRichTextBuffer
*buffer
, const wxString
& filename
)
10385 wxFFileOutputStream
stream(filename
);
10387 return SaveFile(buffer
, stream
);
10391 #endif // wxUSE_FFILE && wxUSE_STREAMS
10393 /// Can we handle this filename (if using files)? By default, checks the extension.
10394 bool wxRichTextFileHandler::CanHandle(const wxString
& filename
) const
10396 wxString path
, file
, ext
;
10397 wxFileName::SplitPath(filename
, & path
, & file
, & ext
);
10399 return (ext
.Lower() == GetExtension());
10403 * wxRichTextTextHandler
10404 * Plain text handler
10407 IMPLEMENT_CLASS(wxRichTextPlainTextHandler
, wxRichTextFileHandler
)
10410 bool wxRichTextPlainTextHandler::DoLoadFile(wxRichTextBuffer
*buffer
, wxInputStream
& stream
)
10412 if (!stream
.IsOk())
10418 while (!stream
.Eof())
10420 int ch
= stream
.GetC();
10424 if (ch
== 10 && lastCh
!= 13)
10427 if (ch
> 0 && ch
!= 10)
10434 buffer
->ResetAndClearCommands();
10436 buffer
->AddParagraphs(str
);
10437 buffer
->UpdateRanges();
10442 bool wxRichTextPlainTextHandler::DoSaveFile(wxRichTextBuffer
*buffer
, wxOutputStream
& stream
)
10444 if (!stream
.IsOk())
10447 wxString text
= buffer
->GetText();
10449 wxString newLine
= wxRichTextLineBreakChar
;
10450 text
.Replace(newLine
, wxT("\n"));
10452 wxCharBuffer buf
= text
.ToAscii();
10454 stream
.Write((const char*) buf
, text
.length());
10457 #endif // wxUSE_STREAMS
10460 * Stores information about an image, in binary in-memory form
10463 wxRichTextImageBlock::wxRichTextImageBlock()
10468 wxRichTextImageBlock::wxRichTextImageBlock(const wxRichTextImageBlock
& block
):wxObject()
10474 wxRichTextImageBlock::~wxRichTextImageBlock()
10479 void wxRichTextImageBlock::Init()
10483 m_imageType
= wxBITMAP_TYPE_INVALID
;
10486 void wxRichTextImageBlock::Clear()
10490 m_imageType
= wxBITMAP_TYPE_INVALID
;
10494 // Load the original image into a memory block.
10495 // If the image is not a JPEG, we must convert it into a JPEG
10496 // to conserve space.
10497 // If it's not a JPEG we can make use of 'image', already scaled, so we don't have to
10498 // load the image a 2nd time.
10500 bool wxRichTextImageBlock::MakeImageBlock(const wxString
& filename
, wxBitmapType imageType
,
10501 wxImage
& image
, bool convertToJPEG
)
10503 m_imageType
= imageType
;
10505 wxString
filenameToRead(filename
);
10506 bool removeFile
= false;
10508 if (imageType
== wxBITMAP_TYPE_INVALID
)
10509 return false; // Could not determine image type
10511 if ((imageType
!= wxBITMAP_TYPE_JPEG
) && convertToJPEG
)
10513 wxString tempFile
=
10514 wxFileName::CreateTempFileName(_("image"));
10516 wxASSERT(!tempFile
.IsEmpty());
10518 image
.SaveFile(tempFile
, wxBITMAP_TYPE_JPEG
);
10519 filenameToRead
= tempFile
;
10522 m_imageType
= wxBITMAP_TYPE_JPEG
;
10525 if (!file
.Open(filenameToRead
))
10528 m_dataSize
= (size_t) file
.Length();
10533 m_data
= ReadBlock(filenameToRead
, m_dataSize
);
10536 wxRemoveFile(filenameToRead
);
10538 return (m_data
!= NULL
);
10541 // Make an image block from the wxImage in the given
10543 bool wxRichTextImageBlock::MakeImageBlock(wxImage
& image
, wxBitmapType imageType
, int quality
)
10545 image
.SetOption(wxT("quality"), quality
);
10547 if (imageType
== wxBITMAP_TYPE_INVALID
)
10548 return false; // Could not determine image type
10550 return DoMakeImageBlock(image
, imageType
);
10553 // Uses a const wxImage for efficiency, but can't set quality (only relevant for JPEG)
10554 bool wxRichTextImageBlock::MakeImageBlockDefaultQuality(const wxImage
& image
, wxBitmapType imageType
)
10556 if (imageType
== wxBITMAP_TYPE_INVALID
)
10557 return false; // Could not determine image type
10559 return DoMakeImageBlock(image
, imageType
);
10562 // Makes the image block
10563 bool wxRichTextImageBlock::DoMakeImageBlock(const wxImage
& image
, wxBitmapType imageType
)
10565 wxMemoryOutputStream memStream
;
10566 if (!image
.SaveFile(memStream
, imageType
))
10571 unsigned char* block
= new unsigned char[memStream
.GetSize()];
10579 m_imageType
= imageType
;
10580 m_dataSize
= memStream
.GetSize();
10582 memStream
.CopyTo(m_data
, m_dataSize
);
10584 return (m_data
!= NULL
);
10588 bool wxRichTextImageBlock::Write(const wxString
& filename
)
10590 return WriteBlock(filename
, m_data
, m_dataSize
);
10593 void wxRichTextImageBlock::Copy(const wxRichTextImageBlock
& block
)
10595 m_imageType
= block
.m_imageType
;
10597 m_dataSize
= block
.m_dataSize
;
10598 if (m_dataSize
== 0)
10601 m_data
= new unsigned char[m_dataSize
];
10603 for (i
= 0; i
< m_dataSize
; i
++)
10604 m_data
[i
] = block
.m_data
[i
];
10608 void wxRichTextImageBlock::operator=(const wxRichTextImageBlock
& block
)
10613 // Load a wxImage from the block
10614 bool wxRichTextImageBlock::Load(wxImage
& image
)
10619 // Read in the image.
10621 wxMemoryInputStream
mstream(m_data
, m_dataSize
);
10622 bool success
= image
.LoadFile(mstream
, GetImageType());
10624 wxString tempFile
= wxFileName::CreateTempFileName(_("image"));
10625 wxASSERT(!tempFile
.IsEmpty());
10627 if (!WriteBlock(tempFile
, m_data
, m_dataSize
))
10631 success
= image
.LoadFile(tempFile
, GetImageType());
10632 wxRemoveFile(tempFile
);
10638 // Write data in hex to a stream
10639 bool wxRichTextImageBlock::WriteHex(wxOutputStream
& stream
)
10641 if (m_dataSize
== 0)
10644 int bufSize
= 100000;
10645 if (int(2*m_dataSize
) < bufSize
)
10646 bufSize
= 2*m_dataSize
;
10647 char* buf
= new char[bufSize
+1];
10649 int left
= m_dataSize
;
10654 if (left
*2 > bufSize
)
10656 n
= bufSize
; left
-= (bufSize
/2);
10660 n
= left
*2; left
= 0;
10664 for (i
= 0; i
< (n
/2); i
++)
10666 wxDecToHex(m_data
[j
], b
, b
+1);
10671 stream
.Write((const char*) buf
, n
);
10677 // Read data in hex from a stream
10678 bool wxRichTextImageBlock::ReadHex(wxInputStream
& stream
, int length
, wxBitmapType imageType
)
10680 int dataSize
= length
/2;
10685 // create a null terminated temporary string:
10689 m_data
= new unsigned char[dataSize
];
10691 for (i
= 0; i
< dataSize
; i
++)
10693 str
[0] = (char)stream
.GetC();
10694 str
[1] = (char)stream
.GetC();
10696 m_data
[i
] = (unsigned char)wxHexToDec(str
);
10699 m_dataSize
= dataSize
;
10700 m_imageType
= imageType
;
10705 // Allocate and read from stream as a block of memory
10706 unsigned char* wxRichTextImageBlock::ReadBlock(wxInputStream
& stream
, size_t size
)
10708 unsigned char* block
= new unsigned char[size
];
10712 stream
.Read(block
, size
);
10717 unsigned char* wxRichTextImageBlock::ReadBlock(const wxString
& filename
, size_t size
)
10719 wxFileInputStream
stream(filename
);
10720 if (!stream
.IsOk())
10723 return ReadBlock(stream
, size
);
10726 // Write memory block to stream
10727 bool wxRichTextImageBlock::WriteBlock(wxOutputStream
& stream
, unsigned char* block
, size_t size
)
10729 stream
.Write((void*) block
, size
);
10730 return stream
.IsOk();
10734 // Write memory block to file
10735 bool wxRichTextImageBlock::WriteBlock(const wxString
& filename
, unsigned char* block
, size_t size
)
10737 wxFileOutputStream
outStream(filename
);
10738 if (!outStream
.IsOk())
10741 return WriteBlock(outStream
, block
, size
);
10744 // Gets the extension for the block's type
10745 wxString
wxRichTextImageBlock::GetExtension() const
10747 wxImageHandler
* handler
= wxImage::FindHandler(GetImageType());
10749 return handler
->GetExtension();
10751 return wxEmptyString
;
10757 * The data object for a wxRichTextBuffer
10760 const wxChar
*wxRichTextBufferDataObject::ms_richTextBufferFormatId
= wxT("wxShape");
10762 wxRichTextBufferDataObject::wxRichTextBufferDataObject(wxRichTextBuffer
* richTextBuffer
)
10764 m_richTextBuffer
= richTextBuffer
;
10766 // this string should uniquely identify our format, but is otherwise
10768 m_formatRichTextBuffer
.SetId(GetRichTextBufferFormatId());
10770 SetFormat(m_formatRichTextBuffer
);
10773 wxRichTextBufferDataObject::~wxRichTextBufferDataObject()
10775 delete m_richTextBuffer
;
10778 // after a call to this function, the richTextBuffer is owned by the caller and it
10779 // is responsible for deleting it!
10780 wxRichTextBuffer
* wxRichTextBufferDataObject::GetRichTextBuffer()
10782 wxRichTextBuffer
* richTextBuffer
= m_richTextBuffer
;
10783 m_richTextBuffer
= NULL
;
10785 return richTextBuffer
;
10788 wxDataFormat
wxRichTextBufferDataObject::GetPreferredFormat(Direction
WXUNUSED(dir
)) const
10790 return m_formatRichTextBuffer
;
10793 size_t wxRichTextBufferDataObject::GetDataSize() const
10795 if (!m_richTextBuffer
)
10801 wxStringOutputStream
stream(& bufXML
);
10802 if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
))
10804 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
10810 wxCharBuffer buffer
= bufXML
.mb_str(wxConvUTF8
);
10811 return strlen(buffer
) + 1;
10813 return bufXML
.Length()+1;
10817 bool wxRichTextBufferDataObject::GetDataHere(void *pBuf
) const
10819 if (!pBuf
|| !m_richTextBuffer
)
10825 wxStringOutputStream
stream(& bufXML
);
10826 if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
))
10828 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
10834 wxCharBuffer buffer
= bufXML
.mb_str(wxConvUTF8
);
10835 size_t len
= strlen(buffer
);
10836 memcpy((char*) pBuf
, (const char*) buffer
, len
);
10837 ((char*) pBuf
)[len
] = 0;
10839 size_t len
= bufXML
.Length();
10840 memcpy((char*) pBuf
, (const char*) bufXML
.c_str(), len
);
10841 ((char*) pBuf
)[len
] = 0;
10847 bool wxRichTextBufferDataObject::SetData(size_t WXUNUSED(len
), const void *buf
)
10849 wxDELETE(m_richTextBuffer
);
10851 wxString
bufXML((const char*) buf
, wxConvUTF8
);
10853 m_richTextBuffer
= new wxRichTextBuffer
;
10855 wxStringInputStream
stream(bufXML
);
10856 if (!m_richTextBuffer
->LoadFile(stream
, wxRICHTEXT_TYPE_XML
))
10858 wxLogError(wxT("Could not read the buffer from an XML stream.\nYou may have forgotten to add the XML file handler."));
10860 wxDELETE(m_richTextBuffer
);
10872 * wxRichTextFontTable
10873 * Manages quick access to a pool of fonts for rendering rich text
10876 WX_DECLARE_STRING_HASH_MAP_WITH_DECL(wxFont
, wxRichTextFontTableHashMap
, class WXDLLIMPEXP_RICHTEXT
);
10878 class wxRichTextFontTableData
: public wxObjectRefData
10881 wxRichTextFontTableData() {}
10883 wxFont
FindFont(const wxRichTextAttr
& fontSpec
);
10885 wxRichTextFontTableHashMap m_hashMap
;
10888 wxFont
wxRichTextFontTableData::FindFont(const wxRichTextAttr
& fontSpec
)
10890 wxString
facename(fontSpec
.GetFontFaceName());
10891 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()));
10892 wxRichTextFontTableHashMap::iterator entry
= m_hashMap
.find(spec
);
10894 if ( entry
== m_hashMap
.end() )
10896 wxFont
font(fontSpec
.GetFontSize(), wxDEFAULT
, fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), fontSpec
.GetFontUnderlined(), facename
.c_str());
10897 m_hashMap
[spec
] = font
;
10902 return entry
->second
;
10906 IMPLEMENT_DYNAMIC_CLASS(wxRichTextFontTable
, wxObject
)
10908 wxRichTextFontTable::wxRichTextFontTable()
10910 m_refData
= new wxRichTextFontTableData
;
10913 wxRichTextFontTable::wxRichTextFontTable(const wxRichTextFontTable
& table
)
10919 wxRichTextFontTable::~wxRichTextFontTable()
10924 bool wxRichTextFontTable::operator == (const wxRichTextFontTable
& table
) const
10926 return (m_refData
== table
.m_refData
);
10929 void wxRichTextFontTable::operator= (const wxRichTextFontTable
& table
)
10934 wxFont
wxRichTextFontTable::FindFont(const wxRichTextAttr
& fontSpec
)
10936 wxRichTextFontTableData
* data
= (wxRichTextFontTableData
*) m_refData
;
10938 return data
->FindFont(fontSpec
);
10943 void wxRichTextFontTable::Clear()
10945 wxRichTextFontTableData
* data
= (wxRichTextFontTableData
*) m_refData
;
10947 data
->m_hashMap
.clear();
10953 void wxTextBoxAttr::Reset()
10956 m_floatMode
= wxTEXT_BOX_ATTR_FLOAT_NONE
;
10957 m_clearMode
= wxTEXT_BOX_ATTR_CLEAR_NONE
;
10958 m_collapseMode
= wxTEXT_BOX_ATTR_COLLAPSE_NONE
;
10959 m_verticalAlignment
= wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_NONE
;
10960 m_boxStyleName
= wxEmptyString
;
10964 m_position
.Reset();
10975 bool wxTextBoxAttr::operator== (const wxTextBoxAttr
& attr
) const
10978 m_flags
== attr
.m_flags
&&
10979 m_floatMode
== attr
.m_floatMode
&&
10980 m_clearMode
== attr
.m_clearMode
&&
10981 m_collapseMode
== attr
.m_collapseMode
&&
10982 m_verticalAlignment
== attr
.m_verticalAlignment
&&
10984 m_margins
== attr
.m_margins
&&
10985 m_padding
== attr
.m_padding
&&
10986 m_position
== attr
.m_position
&&
10988 m_size
== attr
.m_size
&&
10989 m_minSize
== attr
.m_minSize
&&
10990 m_maxSize
== attr
.m_maxSize
&&
10992 m_border
== attr
.m_border
&&
10993 m_outline
== attr
.m_outline
&&
10995 m_boxStyleName
== attr
.m_boxStyleName
10999 // Partial equality test
11000 bool wxTextBoxAttr::EqPartial(const wxTextBoxAttr
& attr
) const
11002 if (attr
.HasFloatMode() && HasFloatMode() && (GetFloatMode() != attr
.GetFloatMode()))
11005 if (attr
.HasClearMode() && HasClearMode() && (GetClearMode() != attr
.GetClearMode()))
11008 if (attr
.HasCollapseBorders() && HasCollapseBorders() && (attr
.GetCollapseBorders() != GetCollapseBorders()))
11011 if (attr
.HasVerticalAlignment() && HasVerticalAlignment() && (attr
.GetVerticalAlignment() != GetVerticalAlignment()))
11014 if (attr
.HasBoxStyleName() && HasBoxStyleName() && (attr
.GetBoxStyleName() != GetBoxStyleName()))
11019 if (!m_position
.EqPartial(attr
.m_position
))
11024 if (!m_size
.EqPartial(attr
.m_size
))
11026 if (!m_minSize
.EqPartial(attr
.m_minSize
))
11028 if (!m_maxSize
.EqPartial(attr
.m_maxSize
))
11033 if (!m_margins
.EqPartial(attr
.m_margins
))
11038 if (!m_padding
.EqPartial(attr
.m_padding
))
11043 if (!GetBorder().EqPartial(attr
.GetBorder()))
11048 if (!GetOutline().EqPartial(attr
.GetOutline()))
11054 // Merges the given attributes. If compareWith
11055 // is non-NULL, then it will be used to mask out those attributes that are the same in style
11056 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
11057 bool wxTextBoxAttr::Apply(const wxTextBoxAttr
& attr
, const wxTextBoxAttr
* compareWith
)
11059 if (attr
.HasFloatMode())
11061 if (!(compareWith
&& compareWith
->HasFloatMode() && compareWith
->GetFloatMode() == attr
.GetFloatMode()))
11062 SetFloatMode(attr
.GetFloatMode());
11065 if (attr
.HasClearMode())
11067 if (!(compareWith
&& compareWith
->HasClearMode() && compareWith
->GetClearMode() == attr
.GetClearMode()))
11068 SetClearMode(attr
.GetClearMode());
11071 if (attr
.HasCollapseBorders())
11073 if (!(compareWith
&& compareWith
->HasCollapseBorders() && compareWith
->GetCollapseBorders() == attr
.GetCollapseBorders()))
11074 SetCollapseBorders(attr
.GetCollapseBorders());
11077 if (attr
.HasVerticalAlignment())
11079 if (!(compareWith
&& compareWith
->HasVerticalAlignment() && compareWith
->GetVerticalAlignment() == attr
.GetVerticalAlignment()))
11080 SetVerticalAlignment(attr
.GetVerticalAlignment());
11083 if (attr
.HasBoxStyleName())
11085 if (!(compareWith
&& compareWith
->HasBoxStyleName() && compareWith
->GetBoxStyleName() == attr
.GetBoxStyleName()))
11086 SetBoxStyleName(attr
.GetBoxStyleName());
11089 m_margins
.Apply(attr
.m_margins
, compareWith
? (& attr
.m_margins
) : (const wxTextAttrDimensions
*) NULL
);
11090 m_padding
.Apply(attr
.m_padding
, compareWith
? (& attr
.m_padding
) : (const wxTextAttrDimensions
*) NULL
);
11091 m_position
.Apply(attr
.m_position
, compareWith
? (& attr
.m_position
) : (const wxTextAttrDimensions
*) NULL
);
11093 m_size
.Apply(attr
.m_size
, compareWith
? (& attr
.m_size
) : (const wxTextAttrSize
*) NULL
);
11094 m_minSize
.Apply(attr
.m_minSize
, compareWith
? (& attr
.m_minSize
) : (const wxTextAttrSize
*) NULL
);
11095 m_maxSize
.Apply(attr
.m_maxSize
, compareWith
? (& attr
.m_maxSize
) : (const wxTextAttrSize
*) NULL
);
11097 m_border
.Apply(attr
.m_border
, compareWith
? (& attr
.m_border
) : (const wxTextAttrBorders
*) NULL
);
11098 m_outline
.Apply(attr
.m_outline
, compareWith
? (& attr
.m_outline
) : (const wxTextAttrBorders
*) NULL
);
11103 // Remove specified attributes from this object
11104 bool wxTextBoxAttr::RemoveStyle(const wxTextBoxAttr
& attr
)
11106 if (attr
.HasFloatMode())
11107 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT
);
11109 if (attr
.HasClearMode())
11110 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR
);
11112 if (attr
.HasCollapseBorders())
11113 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
11115 if (attr
.HasVerticalAlignment())
11116 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
11118 if (attr
.HasBoxStyleName())
11120 SetBoxStyleName(wxEmptyString
);
11121 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
11124 m_margins
.RemoveStyle(attr
.m_margins
);
11125 m_padding
.RemoveStyle(attr
.m_padding
);
11126 m_position
.RemoveStyle(attr
.m_position
);
11128 m_size
.RemoveStyle(attr
.m_size
);
11129 m_minSize
.RemoveStyle(attr
.m_minSize
);
11130 m_maxSize
.RemoveStyle(attr
.m_maxSize
);
11132 m_border
.RemoveStyle(attr
.m_border
);
11133 m_outline
.RemoveStyle(attr
.m_outline
);
11138 // Collects the attributes that are common to a range of content, building up a note of
11139 // which attributes are absent in some objects and which clash in some objects.
11140 void wxTextBoxAttr::CollectCommonAttributes(const wxTextBoxAttr
& attr
, wxTextBoxAttr
& clashingAttr
, wxTextBoxAttr
& absentAttr
)
11142 if (attr
.HasFloatMode())
11144 if (!clashingAttr
.HasFloatMode() && !absentAttr
.HasFloatMode())
11146 if (HasFloatMode())
11148 if (GetFloatMode() != attr
.GetFloatMode())
11150 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_FLOAT
);
11151 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT
);
11155 SetFloatMode(attr
.GetFloatMode());
11159 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_FLOAT
);
11161 if (attr
.HasClearMode())
11163 if (!clashingAttr
.HasClearMode() && !absentAttr
.HasClearMode())
11165 if (HasClearMode())
11167 if (GetClearMode() != attr
.GetClearMode())
11169 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_CLEAR
);
11170 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR
);
11174 SetClearMode(attr
.GetClearMode());
11178 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_CLEAR
);
11180 if (attr
.HasCollapseBorders())
11182 if (!clashingAttr
.HasCollapseBorders() && !absentAttr
.HasCollapseBorders())
11184 if (HasCollapseBorders())
11186 if (GetCollapseBorders() != attr
.GetCollapseBorders())
11188 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
11189 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
11193 SetCollapseBorders(attr
.GetCollapseBorders());
11197 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
11199 if (attr
.HasVerticalAlignment())
11201 if (!clashingAttr
.HasVerticalAlignment() && !absentAttr
.HasVerticalAlignment())
11203 if (HasVerticalAlignment())
11205 if (GetVerticalAlignment() != attr
.GetVerticalAlignment())
11207 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
11208 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
11212 SetVerticalAlignment(attr
.GetVerticalAlignment());
11216 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
11218 if (attr
.HasBoxStyleName())
11220 if (!clashingAttr
.HasBoxStyleName() && !absentAttr
.HasBoxStyleName())
11222 if (HasBoxStyleName())
11224 if (GetBoxStyleName() != attr
.GetBoxStyleName())
11226 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
11227 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
11231 SetBoxStyleName(attr
.GetBoxStyleName());
11235 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
11237 m_margins
.CollectCommonAttributes(attr
.m_margins
, clashingAttr
.m_margins
, absentAttr
.m_margins
);
11238 m_padding
.CollectCommonAttributes(attr
.m_padding
, clashingAttr
.m_padding
, absentAttr
.m_padding
);
11239 m_position
.CollectCommonAttributes(attr
.m_position
, clashingAttr
.m_position
, absentAttr
.m_position
);
11241 m_size
.CollectCommonAttributes(attr
.m_size
, clashingAttr
.m_size
, absentAttr
.m_size
);
11242 m_minSize
.CollectCommonAttributes(attr
.m_minSize
, clashingAttr
.m_minSize
, absentAttr
.m_minSize
);
11243 m_maxSize
.CollectCommonAttributes(attr
.m_maxSize
, clashingAttr
.m_maxSize
, absentAttr
.m_maxSize
);
11245 m_border
.CollectCommonAttributes(attr
.m_border
, clashingAttr
.m_border
, absentAttr
.m_border
);
11246 m_outline
.CollectCommonAttributes(attr
.m_outline
, clashingAttr
.m_outline
, absentAttr
.m_outline
);
11249 bool wxTextBoxAttr::IsDefault() const
11251 return GetFlags() == 0 && !m_border
.IsValid() && !m_outline
.IsValid() &&
11252 !m_size
.IsValid() && !m_minSize
.IsValid() && !m_maxSize
.IsValid() &&
11253 !m_position
.IsValid() && !m_padding
.IsValid() && !m_margins
.IsValid();
11258 void wxRichTextAttr::Copy(const wxRichTextAttr
& attr
)
11260 wxTextAttr::Copy(attr
);
11262 m_textBoxAttr
= attr
.m_textBoxAttr
;
11265 bool wxRichTextAttr::operator==(const wxRichTextAttr
& attr
) const
11267 if (!(wxTextAttr::operator==(attr
)))
11270 return (m_textBoxAttr
== attr
.m_textBoxAttr
);
11273 // Partial equality test taking comparison object into account
11274 bool wxRichTextAttr::EqPartial(const wxRichTextAttr
& attr
) const
11276 if (!(wxTextAttr::EqPartial(attr
)))
11279 return m_textBoxAttr
.EqPartial(attr
.m_textBoxAttr
);
11282 // Merges the given attributes. If compareWith
11283 // is non-NULL, then it will be used to mask out those attributes that are the same in style
11284 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
11285 bool wxRichTextAttr::Apply(const wxRichTextAttr
& style
, const wxRichTextAttr
* compareWith
)
11287 wxTextAttr::Apply(style
, compareWith
);
11289 return m_textBoxAttr
.Apply(style
.m_textBoxAttr
, compareWith
? (& compareWith
->m_textBoxAttr
) : (const wxTextBoxAttr
*) NULL
);
11292 // Remove specified attributes from this object
11293 bool wxRichTextAttr::RemoveStyle(const wxRichTextAttr
& attr
)
11295 wxTextAttr::RemoveStyle(*this, attr
);
11297 return m_textBoxAttr
.RemoveStyle(attr
.m_textBoxAttr
);
11300 // Collects the attributes that are common to a range of content, building up a note of
11301 // which attributes are absent in some objects and which clash in some objects.
11302 void wxRichTextAttr::CollectCommonAttributes(const wxRichTextAttr
& attr
, wxRichTextAttr
& clashingAttr
, wxRichTextAttr
& absentAttr
)
11304 wxTextAttrCollectCommonAttributes(*this, attr
, clashingAttr
, absentAttr
);
11306 m_textBoxAttr
.CollectCommonAttributes(attr
.m_textBoxAttr
, clashingAttr
.m_textBoxAttr
, absentAttr
.m_textBoxAttr
);
11309 // Partial equality test
11310 bool wxTextAttrBorder::EqPartial(const wxTextAttrBorder
& border
) const
11312 if (border
.HasStyle() && !HasStyle() && (border
.GetStyle() != GetStyle()))
11315 if (border
.HasColour() && !HasColour() && (border
.GetColourLong() != GetColourLong()))
11318 if (border
.HasWidth() && !HasWidth() && !(border
.GetWidth() == GetWidth()))
11324 // Apply border to 'this', but not if the same as compareWith
11325 bool wxTextAttrBorder::Apply(const wxTextAttrBorder
& border
, const wxTextAttrBorder
* compareWith
)
11327 if (border
.HasStyle())
11329 if (!(compareWith
&& (border
.GetStyle() == compareWith
->GetStyle())))
11330 SetStyle(border
.GetStyle());
11332 if (border
.HasColour())
11334 if (!(compareWith
&& (border
.GetColourLong() == compareWith
->GetColourLong())))
11335 SetColour(border
.GetColourLong());
11337 if (border
.HasWidth())
11339 if (!(compareWith
&& (border
.GetWidth() == compareWith
->GetWidth())))
11340 SetWidth(border
.GetWidth());
11346 // Remove specified attributes from this object
11347 bool wxTextAttrBorder::RemoveStyle(const wxTextAttrBorder
& attr
)
11349 if (attr
.HasStyle() && HasStyle())
11350 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_STYLE
);
11351 if (attr
.HasColour() && HasColour())
11352 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_COLOUR
);
11353 if (attr
.HasWidth() && HasWidth())
11354 m_borderWidth
.Reset();
11359 // Collects the attributes that are common to a range of content, building up a note of
11360 // which attributes are absent in some objects and which clash in some objects.
11361 void wxTextAttrBorder::CollectCommonAttributes(const wxTextAttrBorder
& attr
, wxTextAttrBorder
& clashingAttr
, wxTextAttrBorder
& absentAttr
)
11363 if (attr
.HasStyle())
11365 if (!clashingAttr
.HasStyle() && !absentAttr
.HasStyle())
11369 if (GetStyle() != attr
.GetStyle())
11371 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
11372 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
11376 SetStyle(attr
.GetStyle());
11380 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
11382 if (attr
.HasColour())
11384 if (!clashingAttr
.HasColour() && !absentAttr
.HasColour())
11388 if (GetColour() != attr
.GetColour())
11390 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
11391 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
11395 SetColour(attr
.GetColourLong());
11399 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
11401 m_borderWidth
.CollectCommonAttributes(attr
.m_borderWidth
, clashingAttr
.m_borderWidth
, absentAttr
.m_borderWidth
);
11404 // Partial equality test
11405 bool wxTextAttrBorders::EqPartial(const wxTextAttrBorders
& borders
) const
11407 return m_left
.EqPartial(borders
.m_left
) && m_right
.EqPartial(borders
.m_right
) &&
11408 m_top
.EqPartial(borders
.m_top
) && m_bottom
.EqPartial(borders
.m_bottom
);
11411 // Apply border to 'this', but not if the same as compareWith
11412 bool wxTextAttrBorders::Apply(const wxTextAttrBorders
& borders
, const wxTextAttrBorders
* compareWith
)
11414 m_left
.Apply(borders
.m_left
, compareWith
? (& compareWith
->m_left
) : (const wxTextAttrBorder
*) NULL
);
11415 m_right
.Apply(borders
.m_right
, compareWith
? (& compareWith
->m_right
) : (const wxTextAttrBorder
*) NULL
);
11416 m_top
.Apply(borders
.m_top
, compareWith
? (& compareWith
->m_top
) : (const wxTextAttrBorder
*) NULL
);
11417 m_bottom
.Apply(borders
.m_bottom
, compareWith
? (& compareWith
->m_bottom
) : (const wxTextAttrBorder
*) NULL
);
11421 // Remove specified attributes from this object
11422 bool wxTextAttrBorders::RemoveStyle(const wxTextAttrBorders
& attr
)
11424 m_left
.RemoveStyle(attr
.m_left
);
11425 m_right
.RemoveStyle(attr
.m_right
);
11426 m_top
.RemoveStyle(attr
.m_top
);
11427 m_bottom
.RemoveStyle(attr
.m_bottom
);
11431 // Collects the attributes that are common to a range of content, building up a note of
11432 // which attributes are absent in some objects and which clash in some objects.
11433 void wxTextAttrBorders::CollectCommonAttributes(const wxTextAttrBorders
& attr
, wxTextAttrBorders
& clashingAttr
, wxTextAttrBorders
& absentAttr
)
11435 m_left
.CollectCommonAttributes(attr
.m_left
, clashingAttr
.m_left
, absentAttr
.m_left
);
11436 m_right
.CollectCommonAttributes(attr
.m_right
, clashingAttr
.m_right
, absentAttr
.m_right
);
11437 m_top
.CollectCommonAttributes(attr
.m_top
, clashingAttr
.m_top
, absentAttr
.m_top
);
11438 m_bottom
.CollectCommonAttributes(attr
.m_bottom
, clashingAttr
.m_bottom
, absentAttr
.m_bottom
);
11441 // Set style of all borders
11442 void wxTextAttrBorders::SetStyle(int style
)
11444 m_left
.SetStyle(style
);
11445 m_right
.SetStyle(style
);
11446 m_top
.SetStyle(style
);
11447 m_bottom
.SetStyle(style
);
11450 // Set colour of all borders
11451 void wxTextAttrBorders::SetColour(unsigned long colour
)
11453 m_left
.SetColour(colour
);
11454 m_right
.SetColour(colour
);
11455 m_top
.SetColour(colour
);
11456 m_bottom
.SetColour(colour
);
11459 void wxTextAttrBorders::SetColour(const wxColour
& colour
)
11461 m_left
.SetColour(colour
);
11462 m_right
.SetColour(colour
);
11463 m_top
.SetColour(colour
);
11464 m_bottom
.SetColour(colour
);
11467 // Set width of all borders
11468 void wxTextAttrBorders::SetWidth(const wxTextAttrDimension
& width
)
11470 m_left
.SetWidth(width
);
11471 m_right
.SetWidth(width
);
11472 m_top
.SetWidth(width
);
11473 m_bottom
.SetWidth(width
);
11476 // Partial equality test
11477 bool wxTextAttrDimension::EqPartial(const wxTextAttrDimension
& dim
) const
11479 if (dim
.IsValid() && IsValid() && !((*this) == dim
))
11485 bool wxTextAttrDimension::Apply(const wxTextAttrDimension
& dim
, const wxTextAttrDimension
* compareWith
)
11489 if (!(compareWith
&& dim
== (*compareWith
)))
11496 // Collects the attributes that are common to a range of content, building up a note of
11497 // which attributes are absent in some objects and which clash in some objects.
11498 void wxTextAttrDimension::CollectCommonAttributes(const wxTextAttrDimension
& attr
, wxTextAttrDimension
& clashingAttr
, wxTextAttrDimension
& absentAttr
)
11500 if (attr
.IsValid())
11502 if (!clashingAttr
.IsValid() && !absentAttr
.IsValid())
11506 if (!((*this) == attr
))
11508 clashingAttr
.SetValid(true);
11517 absentAttr
.SetValid(true);
11520 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(wxDC
& dc
, double scale
, const wxSize
& parentSize
)
11522 m_ppi
= dc
.GetPPI().x
; m_scale
= scale
; m_parentSize
= parentSize
;
11525 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(int ppi
, double scale
, const wxSize
& parentSize
)
11527 m_ppi
= ppi
; m_scale
= scale
; m_parentSize
= parentSize
;
11530 int wxTextAttrDimensionConverter::ConvertTenthsMMToPixels(int units
) const
11532 return wxRichTextObject::ConvertTenthsMMToPixels(m_ppi
, units
, m_scale
);
11535 int wxTextAttrDimensionConverter::ConvertPixelsToTenthsMM(int pixels
) const
11537 return wxRichTextObject::ConvertPixelsToTenthsMM(m_ppi
, pixels
, m_scale
);
11540 int wxTextAttrDimensionConverter::GetPixels(const wxTextAttrDimension
& dim
, int direction
) const
11542 if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
11543 return ConvertTenthsMMToPixels(dim
.GetValue());
11544 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
11545 return dim
.GetValue();
11546 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
11548 wxASSERT(m_parentSize
!= wxDefaultSize
);
11549 if (direction
== wxHORIZONTAL
)
11550 return (int) (double(m_parentSize
.x
) * double(dim
.GetValue()) / 100.0);
11552 return (int) (double(m_parentSize
.y
) * double(dim
.GetValue()) / 100.0);
11561 int wxTextAttrDimensionConverter::GetTenthsMM(const wxTextAttrDimension
& dim
) const
11563 if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
11564 return dim
.GetValue();
11565 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
11566 return ConvertPixelsToTenthsMM(dim
.GetValue());
11574 // Partial equality test
11575 bool wxTextAttrDimensions::EqPartial(const wxTextAttrDimensions
& dims
) const
11577 if (!m_left
.EqPartial(dims
.m_left
))
11580 if (!m_right
.EqPartial(dims
.m_right
))
11583 if (!m_top
.EqPartial(dims
.m_top
))
11586 if (!m_bottom
.EqPartial(dims
.m_bottom
))
11592 // Apply border to 'this', but not if the same as compareWith
11593 bool wxTextAttrDimensions::Apply(const wxTextAttrDimensions
& dims
, const wxTextAttrDimensions
* compareWith
)
11595 m_left
.Apply(dims
.m_left
, compareWith
? (& compareWith
->m_left
) : (const wxTextAttrDimension
*) NULL
);
11596 m_right
.Apply(dims
.m_right
, compareWith
? (& compareWith
->m_right
): (const wxTextAttrDimension
*) NULL
);
11597 m_top
.Apply(dims
.m_top
, compareWith
? (& compareWith
->m_top
): (const wxTextAttrDimension
*) NULL
);
11598 m_bottom
.Apply(dims
.m_bottom
, compareWith
? (& compareWith
->m_bottom
): (const wxTextAttrDimension
*) NULL
);
11603 // Remove specified attributes from this object
11604 bool wxTextAttrDimensions::RemoveStyle(const wxTextAttrDimensions
& attr
)
11606 if (attr
.m_left
.IsValid())
11608 if (attr
.m_right
.IsValid())
11610 if (attr
.m_top
.IsValid())
11612 if (attr
.m_bottom
.IsValid())
11618 // Collects the attributes that are common to a range of content, building up a note of
11619 // which attributes are absent in some objects and which clash in some objects.
11620 void wxTextAttrDimensions::CollectCommonAttributes(const wxTextAttrDimensions
& attr
, wxTextAttrDimensions
& clashingAttr
, wxTextAttrDimensions
& absentAttr
)
11622 m_left
.CollectCommonAttributes(attr
.m_left
, clashingAttr
.m_left
, absentAttr
.m_left
);
11623 m_right
.CollectCommonAttributes(attr
.m_right
, clashingAttr
.m_right
, absentAttr
.m_right
);
11624 m_top
.CollectCommonAttributes(attr
.m_top
, clashingAttr
.m_top
, absentAttr
.m_top
);
11625 m_bottom
.CollectCommonAttributes(attr
.m_bottom
, clashingAttr
.m_bottom
, absentAttr
.m_bottom
);
11628 // Partial equality test
11629 bool wxTextAttrSize::EqPartial(const wxTextAttrSize
& size
) const
11631 if (!m_width
.EqPartial(size
.m_width
))
11634 if (!m_height
.EqPartial(size
.m_height
))
11640 // Apply border to 'this', but not if the same as compareWith
11641 bool wxTextAttrSize::Apply(const wxTextAttrSize
& size
, const wxTextAttrSize
* compareWith
)
11643 m_width
.Apply(size
.m_width
, compareWith
? (& compareWith
->m_width
) : (const wxTextAttrDimension
*) NULL
);
11644 m_height
.Apply(size
.m_height
, compareWith
? (& compareWith
->m_height
): (const wxTextAttrDimension
*) NULL
);
11649 // Remove specified attributes from this object
11650 bool wxTextAttrSize::RemoveStyle(const wxTextAttrSize
& attr
)
11652 if (attr
.m_width
.IsValid())
11654 if (attr
.m_height
.IsValid())
11660 // Collects the attributes that are common to a range of content, building up a note of
11661 // which attributes are absent in some objects and which clash in some objects.
11662 void wxTextAttrSize::CollectCommonAttributes(const wxTextAttrSize
& attr
, wxTextAttrSize
& clashingAttr
, wxTextAttrSize
& absentAttr
)
11664 m_width
.CollectCommonAttributes(attr
.m_width
, clashingAttr
.m_width
, absentAttr
.m_width
);
11665 m_height
.CollectCommonAttributes(attr
.m_height
, clashingAttr
.m_height
, absentAttr
.m_height
);
11668 // Collects the attributes that are common to a range of content, building up a note of
11669 // which attributes are absent in some objects and which clash in some objects.
11670 void wxTextAttrCollectCommonAttributes(wxTextAttr
& currentStyle
, const wxTextAttr
& attr
, wxTextAttr
& clashingAttr
, wxTextAttr
& absentAttr
)
11672 absentAttr
.SetFlags(absentAttr
.GetFlags() | (~attr
.GetFlags() & wxTEXT_ATTR_ALL
));
11673 absentAttr
.SetTextEffectFlags(absentAttr
.GetTextEffectFlags() | (~attr
.GetTextEffectFlags() & 0xFFFF));
11675 long forbiddenFlags
= clashingAttr
.GetFlags()|absentAttr
.GetFlags();
11677 if (attr
.HasFont())
11679 if (attr
.HasFontSize() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_SIZE
))
11681 if (currentStyle
.HasFontSize())
11683 if (currentStyle
.GetFontSize() != attr
.GetFontSize())
11685 // Clash of attr - mark as such
11686 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_SIZE
);
11687 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_SIZE
);
11691 currentStyle
.SetFontSize(attr
.GetFontSize());
11694 if (attr
.HasFontItalic() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_ITALIC
))
11696 if (currentStyle
.HasFontItalic())
11698 if (currentStyle
.GetFontStyle() != attr
.GetFontStyle())
11700 // Clash of attr - mark as such
11701 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_ITALIC
);
11702 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC
);
11706 currentStyle
.SetFontStyle(attr
.GetFontStyle());
11709 if (attr
.HasFontFamily() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_FAMILY
))
11711 if (currentStyle
.HasFontFamily())
11713 if (currentStyle
.GetFontFamily() != attr
.GetFontFamily())
11715 // Clash of attr - mark as such
11716 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FAMILY
);
11717 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY
);
11721 currentStyle
.SetFontFamily(attr
.GetFontFamily());
11724 if (attr
.HasFontWeight() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_WEIGHT
))
11726 if (currentStyle
.HasFontWeight())
11728 if (currentStyle
.GetFontWeight() != attr
.GetFontWeight())
11730 // Clash of attr - mark as such
11731 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_WEIGHT
);
11732 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT
);
11736 currentStyle
.SetFontWeight(attr
.GetFontWeight());
11739 if (attr
.HasFontFaceName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_FACE
))
11741 if (currentStyle
.HasFontFaceName())
11743 wxString
faceName1(currentStyle
.GetFontFaceName());
11744 wxString
faceName2(attr
.GetFontFaceName());
11746 if (faceName1
!= faceName2
)
11748 // Clash of attr - mark as such
11749 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FACE
);
11750 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FACE
);
11754 currentStyle
.SetFontFaceName(attr
.GetFontFaceName());
11757 if (attr
.HasFontUnderlined() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_UNDERLINE
))
11759 if (currentStyle
.HasFontUnderlined())
11761 if (currentStyle
.GetFontUnderlined() != attr
.GetFontUnderlined())
11763 // Clash of attr - mark as such
11764 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
11765 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
11769 currentStyle
.SetFontUnderlined(attr
.GetFontUnderlined());
11773 if (attr
.HasTextColour() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_TEXT_COLOUR
))
11775 if (currentStyle
.HasTextColour())
11777 if (currentStyle
.GetTextColour() != attr
.GetTextColour())
11779 // Clash of attr - mark as such
11780 clashingAttr
.AddFlag(wxTEXT_ATTR_TEXT_COLOUR
);
11781 currentStyle
.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR
);
11785 currentStyle
.SetTextColour(attr
.GetTextColour());
11788 if (attr
.HasBackgroundColour() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BACKGROUND_COLOUR
))
11790 if (currentStyle
.HasBackgroundColour())
11792 if (currentStyle
.GetBackgroundColour() != attr
.GetBackgroundColour())
11794 // Clash of attr - mark as such
11795 clashingAttr
.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
11796 currentStyle
.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
11800 currentStyle
.SetBackgroundColour(attr
.GetBackgroundColour());
11803 if (attr
.HasAlignment() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_ALIGNMENT
))
11805 if (currentStyle
.HasAlignment())
11807 if (currentStyle
.GetAlignment() != attr
.GetAlignment())
11809 // Clash of attr - mark as such
11810 clashingAttr
.AddFlag(wxTEXT_ATTR_ALIGNMENT
);
11811 currentStyle
.RemoveFlag(wxTEXT_ATTR_ALIGNMENT
);
11815 currentStyle
.SetAlignment(attr
.GetAlignment());
11818 if (attr
.HasTabs() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_TABS
))
11820 if (currentStyle
.HasTabs())
11822 if (!wxRichTextTabsEq(currentStyle
.GetTabs(), attr
.GetTabs()))
11824 // Clash of attr - mark as such
11825 clashingAttr
.AddFlag(wxTEXT_ATTR_TABS
);
11826 currentStyle
.RemoveFlag(wxTEXT_ATTR_TABS
);
11830 currentStyle
.SetTabs(attr
.GetTabs());
11833 if (attr
.HasLeftIndent() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LEFT_INDENT
))
11835 if (currentStyle
.HasLeftIndent())
11837 if (currentStyle
.GetLeftIndent() != attr
.GetLeftIndent() || currentStyle
.GetLeftSubIndent() != attr
.GetLeftSubIndent())
11839 // Clash of attr - mark as such
11840 clashingAttr
.AddFlag(wxTEXT_ATTR_LEFT_INDENT
);
11841 currentStyle
.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT
);
11845 currentStyle
.SetLeftIndent(attr
.GetLeftIndent(), attr
.GetLeftSubIndent());
11848 if (attr
.HasRightIndent() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_RIGHT_INDENT
))
11850 if (currentStyle
.HasRightIndent())
11852 if (currentStyle
.GetRightIndent() != attr
.GetRightIndent())
11854 // Clash of attr - mark as such
11855 clashingAttr
.AddFlag(wxTEXT_ATTR_RIGHT_INDENT
);
11856 currentStyle
.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT
);
11860 currentStyle
.SetRightIndent(attr
.GetRightIndent());
11863 if (attr
.HasParagraphSpacingAfter() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARA_SPACING_AFTER
))
11865 if (currentStyle
.HasParagraphSpacingAfter())
11867 if (currentStyle
.GetParagraphSpacingAfter() != attr
.GetParagraphSpacingAfter())
11869 // Clash of attr - mark as such
11870 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
11871 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
11875 currentStyle
.SetParagraphSpacingAfter(attr
.GetParagraphSpacingAfter());
11878 if (attr
.HasParagraphSpacingBefore() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARA_SPACING_BEFORE
))
11880 if (currentStyle
.HasParagraphSpacingBefore())
11882 if (currentStyle
.GetParagraphSpacingBefore() != attr
.GetParagraphSpacingBefore())
11884 // Clash of attr - mark as such
11885 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
11886 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
11890 currentStyle
.SetParagraphSpacingBefore(attr
.GetParagraphSpacingBefore());
11893 if (attr
.HasLineSpacing() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LINE_SPACING
))
11895 if (currentStyle
.HasLineSpacing())
11897 if (currentStyle
.GetLineSpacing() != attr
.GetLineSpacing())
11899 // Clash of attr - mark as such
11900 clashingAttr
.AddFlag(wxTEXT_ATTR_LINE_SPACING
);
11901 currentStyle
.RemoveFlag(wxTEXT_ATTR_LINE_SPACING
);
11905 currentStyle
.SetLineSpacing(attr
.GetLineSpacing());
11908 if (attr
.HasCharacterStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_CHARACTER_STYLE_NAME
))
11910 if (currentStyle
.HasCharacterStyleName())
11912 if (currentStyle
.GetCharacterStyleName() != attr
.GetCharacterStyleName())
11914 // Clash of attr - mark as such
11915 clashingAttr
.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
11916 currentStyle
.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
11920 currentStyle
.SetCharacterStyleName(attr
.GetCharacterStyleName());
11923 if (attr
.HasParagraphStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
))
11925 if (currentStyle
.HasParagraphStyleName())
11927 if (currentStyle
.GetParagraphStyleName() != attr
.GetParagraphStyleName())
11929 // Clash of attr - mark as such
11930 clashingAttr
.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
11931 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
11935 currentStyle
.SetParagraphStyleName(attr
.GetParagraphStyleName());
11938 if (attr
.HasListStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LIST_STYLE_NAME
))
11940 if (currentStyle
.HasListStyleName())
11942 if (currentStyle
.GetListStyleName() != attr
.GetListStyleName())
11944 // Clash of attr - mark as such
11945 clashingAttr
.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
11946 currentStyle
.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
11950 currentStyle
.SetListStyleName(attr
.GetListStyleName());
11953 if (attr
.HasBulletStyle() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_STYLE
))
11955 if (currentStyle
.HasBulletStyle())
11957 if (currentStyle
.GetBulletStyle() != attr
.GetBulletStyle())
11959 // Clash of attr - mark as such
11960 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_STYLE
);
11961 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE
);
11965 currentStyle
.SetBulletStyle(attr
.GetBulletStyle());
11968 if (attr
.HasBulletNumber() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_NUMBER
))
11970 if (currentStyle
.HasBulletNumber())
11972 if (currentStyle
.GetBulletNumber() != attr
.GetBulletNumber())
11974 // Clash of attr - mark as such
11975 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NUMBER
);
11976 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER
);
11980 currentStyle
.SetBulletNumber(attr
.GetBulletNumber());
11983 if (attr
.HasBulletText() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_TEXT
))
11985 if (currentStyle
.HasBulletText())
11987 if (currentStyle
.GetBulletText() != attr
.GetBulletText())
11989 // Clash of attr - mark as such
11990 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_TEXT
);
11991 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT
);
11996 currentStyle
.SetBulletText(attr
.GetBulletText());
11997 currentStyle
.SetBulletFont(attr
.GetBulletFont());
12001 if (attr
.HasBulletName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_NAME
))
12003 if (currentStyle
.HasBulletName())
12005 if (currentStyle
.GetBulletName() != attr
.GetBulletName())
12007 // Clash of attr - mark as such
12008 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NAME
);
12009 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NAME
);
12014 currentStyle
.SetBulletName(attr
.GetBulletName());
12018 if (attr
.HasURL() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_URL
))
12020 if (currentStyle
.HasURL())
12022 if (currentStyle
.GetURL() != attr
.GetURL())
12024 // Clash of attr - mark as such
12025 clashingAttr
.AddFlag(wxTEXT_ATTR_URL
);
12026 currentStyle
.RemoveFlag(wxTEXT_ATTR_URL
);
12031 currentStyle
.SetURL(attr
.GetURL());
12035 if (attr
.HasTextEffects() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_EFFECTS
))
12037 if (currentStyle
.HasTextEffects())
12039 // We need to find the bits in the new attr that are different:
12040 // just look at those bits that are specified by the new attr.
12042 // We need to remove the bits and flags that are not common between current attr
12043 // and new attr. In so doing we need to take account of the styles absent from one or more of the
12044 // previous styles.
12046 int currentRelevantTextEffects
= currentStyle
.GetTextEffects() & attr
.GetTextEffectFlags();
12047 int newRelevantTextEffects
= attr
.GetTextEffects() & attr
.GetTextEffectFlags();
12049 if (currentRelevantTextEffects
!= newRelevantTextEffects
)
12051 // Find the text effects that were different, using XOR
12052 int differentEffects
= currentRelevantTextEffects
^ newRelevantTextEffects
;
12054 // Clash of attr - mark as such
12055 clashingAttr
.SetTextEffectFlags(clashingAttr
.GetTextEffectFlags() | differentEffects
);
12056 currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~differentEffects
);
12061 currentStyle
.SetTextEffects(attr
.GetTextEffects());
12062 currentStyle
.SetTextEffectFlags(attr
.GetTextEffectFlags());
12065 // Mask out the flags and values that cannot be common because they were absent in one or more objecrs
12066 // that we've looked at so far
12067 currentStyle
.SetTextEffects(currentStyle
.GetTextEffects() & ~absentAttr
.GetTextEffectFlags());
12068 currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~absentAttr
.GetTextEffectFlags());
12070 if (currentStyle
.GetTextEffectFlags() == 0)
12071 currentStyle
.RemoveFlag(wxTEXT_ATTR_EFFECTS
);
12074 if (attr
.HasOutlineLevel() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_OUTLINE_LEVEL
))
12076 if (currentStyle
.HasOutlineLevel())
12078 if (currentStyle
.GetOutlineLevel() != attr
.GetOutlineLevel())
12080 // Clash of attr - mark as such
12081 clashingAttr
.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
12082 currentStyle
.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
12086 currentStyle
.SetOutlineLevel(attr
.GetOutlineLevel());
12090 WX_DEFINE_OBJARRAY(wxRichTextVariantArray
);
12092 IMPLEMENT_DYNAMIC_CLASS(wxRichTextProperties
, wxObject
)
12094 bool wxRichTextProperties::operator==(const wxRichTextProperties
& props
) const
12096 if (m_properties
.GetCount() != props
.GetCount())
12100 for (i
= 0; i
< m_properties
.GetCount(); i
++)
12102 const wxVariant
& var1
= m_properties
[i
];
12103 int idx
= props
.Find(var1
.GetName());
12106 const wxVariant
& var2
= props
.m_properties
[idx
];
12107 if (!(var1
== var2
))
12114 wxArrayString
wxRichTextProperties::GetPropertyNames() const
12118 for (i
= 0; i
< m_properties
.GetCount(); i
++)
12120 arr
.Add(m_properties
[i
].GetName());
12125 int wxRichTextProperties::Find(const wxString
& name
) const
12128 for (i
= 0; i
< m_properties
.GetCount(); i
++)
12130 if (m_properties
[i
].GetName() == name
)
12136 bool wxRichTextProperties::Remove(const wxString
& name
)
12138 int idx
= Find(name
);
12141 m_properties
.RemoveAt(idx
);
12148 wxVariant
* wxRichTextProperties::FindOrCreateProperty(const wxString
& name
)
12150 int idx
= Find(name
);
12151 if (idx
== wxNOT_FOUND
)
12152 SetProperty(name
, wxString());
12154 if (idx
!= wxNOT_FOUND
)
12156 return & (*this)[idx
];
12162 const wxVariant
& wxRichTextProperties::GetProperty(const wxString
& name
) const
12164 static const wxVariant nullVariant
;
12165 int idx
= Find(name
);
12167 return m_properties
[idx
];
12169 return nullVariant
;
12172 wxString
wxRichTextProperties::GetPropertyString(const wxString
& name
) const
12174 return GetProperty(name
).GetString();
12177 long wxRichTextProperties::GetPropertyLong(const wxString
& name
) const
12179 return GetProperty(name
).GetLong();
12182 bool wxRichTextProperties::GetPropertyBool(const wxString
& name
) const
12184 return GetProperty(name
).GetBool();
12187 double wxRichTextProperties::GetPropertyDouble(const wxString
& name
) const
12189 return GetProperty(name
).GetDouble();
12192 void wxRichTextProperties::SetProperty(const wxVariant
& variant
)
12194 wxASSERT(!variant
.GetName().IsEmpty());
12196 int idx
= Find(variant
.GetName());
12199 m_properties
.Add(variant
);
12201 m_properties
[idx
] = variant
;
12204 void wxRichTextProperties::SetProperty(const wxString
& name
, const wxVariant
& variant
)
12206 int idx
= Find(name
);
12207 wxVariant
var(variant
);
12211 m_properties
.Add(var
);
12213 m_properties
[idx
] = var
;
12216 void wxRichTextProperties::SetProperty(const wxString
& name
, const wxString
& value
)
12218 SetProperty(name
, wxVariant(value
, name
));
12221 void wxRichTextProperties::SetProperty(const wxString
& name
, long value
)
12223 SetProperty(name
, wxVariant(value
, name
));
12226 void wxRichTextProperties::SetProperty(const wxString
& name
, double value
)
12228 SetProperty(name
, wxVariant(value
, name
));
12231 void wxRichTextProperties::SetProperty(const wxString
& name
, bool value
)
12233 SetProperty(name
, wxVariant(value
, name
));
12236 void wxRichTextProperties::RemoveProperties(const wxRichTextProperties
& properties
)
12239 for (i
= 0; i
< properties
.GetCount(); i
++)
12241 wxString name
= properties
.GetProperties()[i
].GetName();
12242 if (HasProperty(name
))
12247 void wxRichTextProperties::MergeProperties(const wxRichTextProperties
& properties
)
12250 for (i
= 0; i
< properties
.GetCount(); i
++)
12252 SetProperty(properties
.GetProperties()[i
]);
12256 wxRichTextObject
* wxRichTextObjectAddress::GetObject(wxRichTextParagraphLayoutBox
* topLevelContainer
) const
12258 if (m_address
.GetCount() == 0)
12259 return topLevelContainer
;
12261 wxRichTextCompositeObject
* p
= topLevelContainer
;
12263 while (p
&& i
< m_address
.GetCount())
12265 int pos
= m_address
[i
];
12266 wxASSERT(pos
>= 0 && pos
< (int) p
->GetChildren().GetCount());
12267 if (pos
< 0 || pos
>= (int) p
->GetChildren().GetCount())
12270 wxRichTextObject
* p1
= p
->GetChild(pos
);
12271 if (i
== (m_address
.GetCount()-1))
12274 p
= wxDynamicCast(p1
, wxRichTextCompositeObject
);
12280 bool wxRichTextObjectAddress::Create(wxRichTextParagraphLayoutBox
* topLevelContainer
, wxRichTextObject
* obj
)
12284 if (topLevelContainer
== obj
)
12287 wxRichTextObject
* o
= obj
;
12290 wxRichTextCompositeObject
* p
= wxDynamicCast(o
->GetParent(), wxRichTextCompositeObject
);
12294 int pos
= p
->GetChildren().IndexOf(o
);
12298 m_address
.Insert(pos
, 0);
12300 if (p
== topLevelContainer
)
12309 bool wxRichTextSelection::operator==(const wxRichTextSelection
& sel
) const
12311 if (m_container
!= sel
.m_container
)
12313 if (m_ranges
.GetCount() != sel
.m_ranges
.GetCount())
12316 for (i
= 0; i
< m_ranges
.GetCount(); i
++)
12317 if (!(m_ranges
[i
] == sel
.m_ranges
[i
]))
12322 // Get the selections appropriate to the specified object, if any; returns wxRICHTEXT_NO_SELECTION if none
12323 // or none at the level of the object's container.
12324 wxRichTextRangeArray
wxRichTextSelection::GetSelectionForObject(wxRichTextObject
* obj
) const
12328 wxRichTextParagraphLayoutBox
* container
= obj
->GetParentContainer();
12330 if (container
== m_container
)
12333 container
= obj
->GetContainer();
12336 if (container
->GetParent())
12338 // If we found that our object's container is within the range of
12339 // a selection higher up, then assume the whole original object
12340 // is also selected.
12341 wxRichTextParagraphLayoutBox
* parentContainer
= container
->GetParentContainer();
12342 if (parentContainer
== m_container
)
12344 if (WithinSelection(container
->GetRange().GetStart(), m_ranges
))
12346 wxRichTextRangeArray ranges
;
12347 ranges
.Add(obj
->GetRange());
12352 container
= parentContainer
;
12361 return wxRichTextRangeArray();
12364 // Is the given position within the selection?
12365 bool wxRichTextSelection::WithinSelection(long pos
, wxRichTextObject
* obj
) const
12371 wxRichTextRangeArray selectionRanges
= GetSelectionForObject(obj
);
12372 return WithinSelection(pos
, selectionRanges
);
12376 // Is the given position within the selection range?
12377 bool wxRichTextSelection::WithinSelection(long pos
, const wxRichTextRangeArray
& ranges
)
12380 for (i
= 0; i
< ranges
.GetCount(); i
++)
12382 const wxRichTextRange
& range
= ranges
[i
];
12383 if (pos
>= range
.GetStart() && pos
<= range
.GetEnd())
12389 // Is the given range completely within the selection range?
12390 bool wxRichTextSelection::WithinSelection(const wxRichTextRange
& range
, const wxRichTextRangeArray
& ranges
)
12393 for (i
= 0; i
< ranges
.GetCount(); i
++)
12395 const wxRichTextRange
& eachRange
= ranges
[i
];
12396 if (range
.IsWithin(eachRange
))
12402 IMPLEMENT_CLASS(wxRichTextDrawingHandler
, wxObject
)
12403 IMPLEMENT_CLASS(wxRichTextDrawingContext
, wxObject
)
12405 bool wxRichTextDrawingContext::HasVirtualAttributes(wxRichTextObject
* obj
) const
12407 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
12410 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
12411 if (handler
->HasVirtualAttributes(obj
))
12414 node
= node
->GetNext();
12419 wxRichTextAttr
wxRichTextDrawingContext::GetVirtualAttributes(wxRichTextObject
* obj
) const
12421 wxRichTextAttr attr
;
12422 // We apply all handlers, so we can may combine several different attributes
12423 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
12426 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
12427 if (handler
->HasVirtualAttributes(obj
))
12429 bool success
= handler
->GetVirtualAttributes(attr
, obj
);
12433 node
= node
->GetNext();
12438 bool wxRichTextDrawingContext::ApplyVirtualAttributes(wxRichTextAttr
& attr
, wxRichTextObject
* obj
) const
12440 if (HasVirtualAttributes(obj
))
12442 wxRichTextAttr
a(GetVirtualAttributes(obj
));
12450 /// Adds a handler to the end
12451 void wxRichTextBuffer::AddDrawingHandler(wxRichTextDrawingHandler
*handler
)
12453 sm_drawingHandlers
.Append(handler
);
12456 /// Inserts a handler at the front
12457 void wxRichTextBuffer::InsertDrawingHandler(wxRichTextDrawingHandler
*handler
)
12459 sm_drawingHandlers
.Insert( handler
);
12462 /// Removes a handler
12463 bool wxRichTextBuffer::RemoveDrawingHandler(const wxString
& name
)
12465 wxRichTextDrawingHandler
*handler
= FindDrawingHandler(name
);
12468 sm_drawingHandlers
.DeleteObject(handler
);
12476 wxRichTextDrawingHandler
* wxRichTextBuffer::FindDrawingHandler(const wxString
& name
)
12478 wxList::compatibility_iterator node
= sm_drawingHandlers
.GetFirst();
12481 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
12482 if (handler
->GetName().Lower() == name
.Lower()) return handler
;
12484 node
= node
->GetNext();
12489 void wxRichTextBuffer::CleanUpDrawingHandlers()
12491 wxList::compatibility_iterator node
= sm_drawingHandlers
.GetFirst();
12494 wxRichTextDrawingHandler
* handler
= (wxRichTextDrawingHandler
*)node
->GetData();
12495 wxList::compatibility_iterator next
= node
->GetNext();
12500 sm_drawingHandlers
.Clear();