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 // If this is a floating object, size may not be recalculated
617 // after floats have been collected in an early stage of Layout.
618 // So avoid resetting the cache for floating objects during layout.
620 SetCachedSize(wxDefaultSize
);
621 SetMaxSize(wxDefaultSize
);
622 SetMinSize(wxDefaultSize
);
626 // Convert units in tenths of a millimetre to device units
627 int wxRichTextObject::ConvertTenthsMMToPixels(wxDC
& dc
, int units
) const
632 scale
= GetBuffer()->GetScale();
633 int p
= ConvertTenthsMMToPixels(dc
.GetPPI().x
, units
, scale
);
638 // Convert units in tenths of a millimetre to device units
639 int wxRichTextObject::ConvertTenthsMMToPixels(int ppi
, int units
, double scale
)
641 // There are ppi pixels in 254.1 "1/10 mm"
643 double pixels
= ((double) units
* (double)ppi
) / 254.1;
647 // If the result is very small, make it at least one pixel in size.
648 if (pixels
== 0 && units
> 0)
654 // Convert units in pixels to tenths of a millimetre
655 int wxRichTextObject::ConvertPixelsToTenthsMM(wxDC
& dc
, int pixels
) const
660 scale
= GetBuffer()->GetScale();
662 return ConvertPixelsToTenthsMM(dc
.GetPPI().x
, p
, scale
);
665 int wxRichTextObject::ConvertPixelsToTenthsMM(int ppi
, int pixels
, double scale
)
667 // There are ppi pixels in 254.1 "1/10 mm"
669 double p
= double(pixels
);
674 int units
= int( p
* 254.1 / (double) ppi
);
678 // Draw the borders and background for the given rectangle and attributes.
679 // Width and height are taken to be the outer margin size, not the content.
680 bool wxRichTextObject::DrawBoxAttributes(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& attr
, const wxRect
& boxRect
, int flags
)
682 // Assume boxRect is the area around the content
683 wxRect marginRect
= boxRect
;
684 wxRect contentRect
, borderRect
, paddingRect
, outlineRect
;
686 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
688 // Margin is transparent. Draw background from margin.
689 if (attr
.HasBackgroundColour() || (flags
& wxRICHTEXT_DRAW_SELECTED
))
692 if (flags
& wxRICHTEXT_DRAW_SELECTED
)
694 // TODO: get selection colour from control?
695 colour
= wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
);
698 colour
= attr
.GetBackgroundColour();
701 wxBrush
brush(colour
);
705 dc
.DrawRectangle(borderRect
);
708 if (flags
& wxRICHTEXT_DRAW_GUIDELINES
)
710 wxRichTextAttr editBorderAttr
= attr
;
711 // TODO: make guideline colour configurable
712 editBorderAttr
.GetTextBoxAttr().GetBorder().SetColour(*wxLIGHT_GREY
);
713 editBorderAttr
.GetTextBoxAttr().GetBorder().SetWidth(1, wxTEXT_ATTR_UNITS_PIXELS
);
714 editBorderAttr
.GetTextBoxAttr().GetBorder().SetStyle(wxTEXT_BOX_ATTR_BORDER_SOLID
);
716 DrawBorder(dc
, buffer
, editBorderAttr
.GetTextBoxAttr().GetBorder(), borderRect
, flags
);
719 if (attr
.GetTextBoxAttr().GetBorder().IsValid())
720 DrawBorder(dc
, buffer
, attr
.GetTextBoxAttr().GetBorder(), borderRect
);
722 if (attr
.GetTextBoxAttr().GetOutline().IsValid())
723 DrawBorder(dc
, buffer
, attr
.GetTextBoxAttr().GetOutline(), outlineRect
);
729 bool wxRichTextObject::DrawBorder(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxTextAttrBorders
& attr
, const wxRect
& rect
, int WXUNUSED(flags
))
731 int borderLeft
= 0, borderRight
= 0, borderTop
= 0, borderBottom
= 0;
732 wxTextAttrDimensionConverter
converter(dc
, buffer
? buffer
->GetScale() : 1.0);
734 if (attr
.GetLeft().IsValid() && attr
.GetLeft().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
736 borderLeft
= converter
.GetPixels(attr
.GetLeft().GetWidth());
737 wxColour
col(attr
.GetLeft().GetColour());
739 // If pen width is > 1, resorts to a solid rectangle.
742 int penStyle
= wxSOLID
;
743 if (attr
.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
745 else if (attr
.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
746 penStyle
= wxLONG_DASH
;
747 wxPen
pen(col
, 1, penStyle
);
749 dc
.DrawLine(rect
.x
, rect
.y
, rect
.x
, rect
.y
+ rect
.height
);
752 else if (borderLeft
> 1)
758 dc
.DrawRectangle(rect
.x
, rect
.y
, borderLeft
, rect
.height
);
762 if (attr
.GetRight().IsValid() && attr
.GetRight().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
764 borderRight
= converter
.GetPixels(attr
.GetRight().GetWidth());
766 wxColour
col(attr
.GetRight().GetColour());
768 // If pen width is > 1, resorts to a solid rectangle.
769 if (borderRight
== 1)
771 int penStyle
= wxSOLID
;
772 if (attr
.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
774 else if (attr
.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
775 penStyle
= wxLONG_DASH
;
776 wxPen
pen(col
, 1, penStyle
);
778 dc
.DrawLine(rect
.x
+ rect
.width
, rect
.y
, rect
.x
+ rect
.width
, rect
.y
+ rect
.height
+ 1);
781 else if (borderRight
> 1)
787 dc
.DrawRectangle(rect
.x
+ rect
.width
- borderRight
, rect
.y
, borderRight
, rect
.height
);
791 if (attr
.GetTop().IsValid() && attr
.GetTop().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
793 borderTop
= converter
.GetPixels(attr
.GetTop().GetWidth());
795 wxColour
col(attr
.GetTop().GetColour());
797 // If pen width is > 1, resorts to a solid rectangle.
800 int penStyle
= wxSOLID
;
801 if (attr
.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
803 else if (attr
.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
804 penStyle
= wxLONG_DASH
;
805 wxPen
pen(col
, 1, penStyle
);
807 dc
.DrawLine(rect
.x
, rect
.y
, rect
.x
+ rect
.width
, rect
.y
);
810 else if (borderTop
> 1)
816 dc
.DrawRectangle(rect
.x
, rect
.y
, rect
.width
, borderTop
);
820 if (attr
.GetBottom().IsValid() && attr
.GetBottom().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE
)
822 borderBottom
= converter
.GetPixels(attr
.GetBottom().GetWidth());
823 wxColour
col(attr
.GetTop().GetColour());
825 // If pen width is > 1, resorts to a solid rectangle.
826 if (borderBottom
== 1)
828 int penStyle
= wxSOLID
;
829 if (attr
.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED
)
831 else if (attr
.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED
)
832 penStyle
= wxLONG_DASH
;
833 wxPen
pen(col
, 1, penStyle
);
835 dc
.DrawLine(rect
.x
, rect
.y
+ rect
.height
, rect
.x
+ rect
.width
, rect
.y
+ rect
.height
);
838 else if (borderBottom
> 1)
844 dc
.DrawRectangle(rect
.x
, rect
.y
+ rect
.height
- borderBottom
, rect
.width
, borderBottom
);
851 // Get the various rectangles of the box model in pixels. You can either specify contentRect (inner)
852 // or marginRect (outer), and the other must be the default rectangle (no width or height).
853 // Note that the outline doesn't affect the position of the rectangle, it's drawn in whatever space
856 // | Margin | Border | Padding | CONTENT | Padding | Border | Margin |
858 bool wxRichTextObject::GetBoxRects(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& attr
, wxRect
& marginRect
, wxRect
& borderRect
, wxRect
& contentRect
, wxRect
& paddingRect
, wxRect
& outlineRect
)
860 int borderLeft
= 0, borderRight
= 0, borderTop
= 0, borderBottom
= 0;
861 int outlineLeft
= 0, outlineRight
= 0, outlineTop
= 0, outlineBottom
= 0;
862 int paddingLeft
= 0, paddingRight
= 0, paddingTop
= 0, paddingBottom
= 0;
863 int marginLeft
= 0, marginRight
= 0, marginTop
= 0, marginBottom
= 0;
865 wxTextAttrDimensionConverter
converter(dc
, buffer
? buffer
->GetScale() : 1.0);
867 if (attr
.GetTextBoxAttr().GetMargins().GetLeft().IsValid())
868 marginLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetLeft());
869 if (attr
.GetTextBoxAttr().GetMargins().GetRight().IsValid())
870 marginRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetRight());
871 if (attr
.GetTextBoxAttr().GetMargins().GetTop().IsValid())
872 marginTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetTop());
873 if (attr
.GetTextBoxAttr().GetMargins().GetBottom().IsValid())
874 marginBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetMargins().GetBottom());
876 if (attr
.GetTextBoxAttr().GetBorder().GetLeft().GetWidth().IsValid())
877 borderLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetLeft().GetWidth());
878 if (attr
.GetTextBoxAttr().GetBorder().GetRight().GetWidth().IsValid())
879 borderRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetRight().GetWidth());
880 if (attr
.GetTextBoxAttr().GetBorder().GetTop().GetWidth().IsValid())
881 borderTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetTop().GetWidth());
882 if (attr
.GetTextBoxAttr().GetBorder().GetBottom().GetWidth().IsValid())
883 borderBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetBorder().GetBottom().GetWidth());
885 if (attr
.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
886 paddingLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetLeft());
887 if (attr
.GetTextBoxAttr().GetPadding().GetRight().IsValid())
888 paddingRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetRight());
889 if (attr
.GetTextBoxAttr().GetPadding().GetTop().IsValid())
890 paddingTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetTop());
891 if (attr
.GetTextBoxAttr().GetPadding().GetBottom().IsValid())
892 paddingBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetBottom());
894 if (attr
.GetTextBoxAttr().GetOutline().GetLeft().GetWidth().IsValid())
895 outlineLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetLeft().GetWidth());
896 if (attr
.GetTextBoxAttr().GetOutline().GetRight().GetWidth().IsValid())
897 outlineRight
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetRight().GetWidth());
898 if (attr
.GetTextBoxAttr().GetOutline().GetTop().GetWidth().IsValid())
899 outlineTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetTop().GetWidth());
900 if (attr
.GetTextBoxAttr().GetOutline().GetBottom().GetWidth().IsValid())
901 outlineBottom
= converter
.GetPixels(attr
.GetTextBoxAttr().GetOutline().GetBottom().GetWidth());
903 int leftTotal
= marginLeft
+ borderLeft
+ paddingLeft
;
904 int rightTotal
= marginRight
+ borderRight
+ paddingRight
;
905 int topTotal
= marginTop
+ borderTop
+ paddingTop
;
906 int bottomTotal
= marginBottom
+ borderBottom
+ paddingBottom
;
908 if (marginRect
!= wxRect())
910 contentRect
.x
= marginRect
.x
+ leftTotal
;
911 contentRect
.y
= marginRect
.y
+ topTotal
;
912 contentRect
.width
= marginRect
.width
- (leftTotal
+ rightTotal
);
913 contentRect
.height
= marginRect
.height
- (topTotal
+ bottomTotal
);
917 marginRect
.x
= contentRect
.x
- leftTotal
;
918 marginRect
.y
= contentRect
.y
- topTotal
;
919 marginRect
.width
= contentRect
.width
+ (leftTotal
+ rightTotal
);
920 marginRect
.height
= contentRect
.height
+ (topTotal
+ bottomTotal
);
923 borderRect
.x
= marginRect
.x
+ marginLeft
;
924 borderRect
.y
= marginRect
.y
+ marginTop
;
925 borderRect
.width
= marginRect
.width
- (marginLeft
+ marginRight
);
926 borderRect
.height
= marginRect
.height
- (marginTop
+ marginBottom
);
928 paddingRect
.x
= marginRect
.x
+ marginLeft
+ borderLeft
;
929 paddingRect
.y
= marginRect
.y
+ marginTop
+ borderTop
;
930 paddingRect
.width
= marginRect
.width
- (marginLeft
+ marginRight
+ borderLeft
+ borderRight
);
931 paddingRect
.height
= marginRect
.height
- (marginTop
+ marginBottom
+ borderTop
+ borderBottom
);
933 // The outline is outside the margin and doesn't influence the overall box position or content size.
934 outlineRect
.x
= marginRect
.x
- outlineLeft
;
935 outlineRect
.y
= marginRect
.y
- outlineTop
;
936 outlineRect
.width
= marginRect
.width
+ (outlineLeft
+ outlineRight
);
937 outlineRect
.height
= marginRect
.height
+ (outlineTop
+ outlineBottom
);
942 // Get the total margin for the object in pixels, taking into account margin, padding and border size
943 bool wxRichTextObject::GetTotalMargin(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& attr
, int& leftMargin
, int& rightMargin
,
944 int& topMargin
, int& bottomMargin
)
946 // Assume boxRect is the area around the content
947 wxRect contentRect
, marginRect
, borderRect
, paddingRect
, outlineRect
;
948 marginRect
= wxRect(0, 0, 1000, 1000);
950 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
952 leftMargin
= contentRect
.GetLeft() - marginRect
.GetLeft();
953 rightMargin
= marginRect
.GetRight() - contentRect
.GetRight();
954 topMargin
= contentRect
.GetTop() - marginRect
.GetTop();
955 bottomMargin
= marginRect
.GetBottom() - contentRect
.GetBottom();
960 // Returns the rectangle which the child has available to it given restrictions specified in the
961 // child attribute, e.g. 50% width of the parent, 400 pixels, x position 20% of the parent, etc.
962 // availableContainerSpace might be a parent that the cell has to compute its width relative to.
963 // E.g. a cell that's 50% of its parent.
964 wxRect
wxRichTextObject::AdjustAvailableSpace(wxDC
& dc
, wxRichTextBuffer
* buffer
, const wxRichTextAttr
& WXUNUSED(parentAttr
), const wxRichTextAttr
& childAttr
, const wxRect
& availableParentSpace
, const wxRect
& availableContainerSpace
)
966 wxRect rect
= availableParentSpace
;
969 scale
= buffer
->GetScale();
971 wxTextAttrDimensionConverter
converter(dc
, scale
, availableContainerSpace
.GetSize());
973 if (childAttr
.GetTextBoxAttr().GetWidth().IsValid())
974 rect
.width
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetWidth());
976 if (childAttr
.GetTextBoxAttr().GetHeight().IsValid())
977 rect
.height
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetHeight());
979 // Can specify either left or right for the position (we're assuming we can't
980 // set the left and right edges to effectively set the size. Would we want to do that?)
981 if (childAttr
.GetTextBoxAttr().GetPosition().GetLeft().IsValid())
983 rect
.x
= rect
.x
+ converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetLeft());
985 else if (childAttr
.GetTextBoxAttr().GetPosition().GetRight().IsValid())
987 int x
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetRight());
988 if (childAttr
.GetTextBoxAttr().GetPosition().GetRight().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE
)
989 rect
.x
= availableContainerSpace
.x
+ availableContainerSpace
.width
- rect
.width
;
994 if (childAttr
.GetTextBoxAttr().GetPosition().GetTop().IsValid())
996 rect
.y
= rect
.y
+ converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetTop());
998 else if (childAttr
.GetTextBoxAttr().GetPosition().GetBottom().IsValid())
1000 int y
= converter
.GetPixels(childAttr
.GetTextBoxAttr().GetPosition().GetBottom());
1001 if (childAttr
.GetTextBoxAttr().GetPosition().GetBottom().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE
)
1002 rect
.y
= availableContainerSpace
.y
+ availableContainerSpace
.height
- rect
.height
;
1007 if (rect
.GetWidth() > availableParentSpace
.GetWidth())
1008 rect
.SetWidth(availableParentSpace
.GetWidth());
1013 // Dump to output stream for debugging
1014 void wxRichTextObject::Dump(wxTextOutputStream
& stream
)
1016 stream
<< GetClassInfo()->GetClassName() << wxT("\n");
1017 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");
1018 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");
1021 // Gets the containing buffer
1022 wxRichTextBuffer
* wxRichTextObject::GetBuffer() const
1024 const wxRichTextObject
* obj
= this;
1025 while (obj
&& !obj
->IsKindOf(CLASSINFO(wxRichTextBuffer
)))
1026 obj
= obj
->GetParent();
1027 return wxDynamicCast(obj
, wxRichTextBuffer
);
1030 // Get the absolute object position, by traversing up the child/parent hierarchy
1031 wxPoint
wxRichTextObject::GetAbsolutePosition() const
1033 wxPoint pt
= GetPosition();
1035 wxRichTextObject
* p
= GetParent();
1038 pt
= pt
+ p
->GetPosition();
1045 // Hit-testing: returns a flag indicating hit test details, plus
1046 // information about position
1047 int wxRichTextObject::HitTest(wxDC
& WXUNUSED(dc
), wxRichTextDrawingContext
& WXUNUSED(context
), const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int WXUNUSED(flags
))
1050 return wxRICHTEXT_HITTEST_NONE
;
1052 wxRect rect
= GetRect();
1053 if (pt
.x
>= rect
.x
&& pt
.x
< rect
.x
+ rect
.width
&&
1054 pt
.y
>= rect
.y
&& pt
.y
< rect
.y
+ rect
.height
)
1057 *contextObj
= GetParentContainer();
1058 textPosition
= GetRange().GetStart();
1059 return wxRICHTEXT_HITTEST_ON
;
1062 return wxRICHTEXT_HITTEST_NONE
;
1065 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1066 // lays out the object again using the maximum ('best') size
1067 bool wxRichTextObject::LayoutToBestSize(wxDC
& dc
, wxRichTextDrawingContext
& context
, wxRichTextBuffer
* buffer
,
1068 const wxRichTextAttr
& parentAttr
, const wxRichTextAttr
& attr
,
1069 const wxRect
& availableParentSpace
, const wxRect
& availableContainerSpace
,
1072 wxRect availableChildRect
= AdjustAvailableSpace(dc
, buffer
, parentAttr
, attr
, availableParentSpace
, availableContainerSpace
);
1073 wxRect originalAvailableRect
= availableChildRect
;
1074 Layout(dc
, context
, availableChildRect
, availableContainerSpace
, style
);
1076 wxSize maxSize
= GetMaxSize();
1078 // Don't ignore if maxSize.x is zero, since we need to redo the paragraph's lines
1080 if (!attr
.GetTextBoxAttr().GetWidth().IsValid() && maxSize
.x
< availableChildRect
.width
)
1082 // Redo the layout with a fixed, minimum size this time.
1083 Invalidate(wxRICHTEXT_ALL
);
1084 wxRichTextAttr
newAttr(attr
);
1085 newAttr
.GetTextBoxAttr().GetWidth().SetValue(maxSize
.x
, wxTEXT_ATTR_UNITS_PIXELS
);
1086 newAttr
.GetTextBoxAttr().GetWidth().SetPosition(wxTEXT_BOX_ATTR_POSITION_ABSOLUTE
);
1088 availableChildRect
= AdjustAvailableSpace(dc
, buffer
, parentAttr
, newAttr
, availableParentSpace
, availableContainerSpace
);
1090 // If a paragraph, align the whole paragraph.
1091 // Problem with this: if we're limited by a floating object, a line may be centered
1092 // w.r.t. the smaller resulting box rather than the actual available width.
1093 if (attr
.HasAlignment() && !GetContainer()->GetFloatCollector()->HasFloats()) // FIXME: aligning whole paragraph not compatible with floating objects
1095 // centering, right-justification
1096 if (attr
.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE
)
1098 availableChildRect
.x
= (originalAvailableRect
.GetWidth() - availableChildRect
.GetWidth())/2 + availableChildRect
.x
;
1100 else if (attr
.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT
)
1102 availableChildRect
.x
= availableChildRect
.x
+ originalAvailableRect
.GetWidth() - availableChildRect
.GetWidth();
1106 Layout(dc
, context
, availableChildRect
, availableContainerSpace
, style
);
1120 // Move the object recursively, by adding the offset from old to new
1121 void wxRichTextObject::Move(const wxPoint
& pt
)
1128 * wxRichTextCompositeObject
1129 * This is the base for drawable objects.
1132 IMPLEMENT_CLASS(wxRichTextCompositeObject
, wxRichTextObject
)
1134 wxRichTextCompositeObject::wxRichTextCompositeObject(wxRichTextObject
* parent
):
1135 wxRichTextObject(parent
)
1139 wxRichTextCompositeObject::~wxRichTextCompositeObject()
1144 /// Get the nth child
1145 wxRichTextObject
* wxRichTextCompositeObject::GetChild(size_t n
) const
1147 wxASSERT ( n
< m_children
.GetCount() );
1149 return m_children
.Item(n
)->GetData();
1152 /// Append a child, returning the position
1153 size_t wxRichTextCompositeObject::AppendChild(wxRichTextObject
* child
)
1155 m_children
.Append(child
);
1156 child
->SetParent(this);
1157 return m_children
.GetCount() - 1;
1160 /// Insert the child in front of the given object, or at the beginning
1161 bool wxRichTextCompositeObject::InsertChild(wxRichTextObject
* child
, wxRichTextObject
* inFrontOf
)
1165 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(inFrontOf
);
1166 m_children
.Insert(node
, child
);
1169 m_children
.Insert(child
);
1170 child
->SetParent(this);
1175 /// Delete the child
1176 bool wxRichTextCompositeObject::RemoveChild(wxRichTextObject
* child
, bool deleteChild
)
1178 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(child
);
1181 wxRichTextObject
* obj
= node
->GetData();
1182 m_children
.Erase(node
);
1191 /// Delete all children
1192 bool wxRichTextCompositeObject::DeleteChildren()
1194 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1197 wxRichTextObjectList::compatibility_iterator oldNode
= node
;
1199 wxRichTextObject
* child
= node
->GetData();
1200 child
->Dereference(); // Only delete if reference count is zero
1202 node
= node
->GetNext();
1203 m_children
.Erase(oldNode
);
1209 /// Get the child count
1210 size_t wxRichTextCompositeObject::GetChildCount() const
1212 return m_children
.GetCount();
1216 void wxRichTextCompositeObject::Copy(const wxRichTextCompositeObject
& obj
)
1218 wxRichTextObject::Copy(obj
);
1222 wxRichTextObjectList::compatibility_iterator node
= obj
.m_children
.GetFirst();
1225 wxRichTextObject
* child
= node
->GetData();
1226 wxRichTextObject
* newChild
= child
->Clone();
1227 newChild
->SetParent(this);
1228 m_children
.Append(newChild
);
1230 node
= node
->GetNext();
1234 /// Hit-testing: returns a flag indicating hit test details, plus
1235 /// information about position
1236 int wxRichTextCompositeObject::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
1239 return wxRICHTEXT_HITTEST_NONE
;
1241 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1244 wxRichTextObject
* child
= node
->GetData();
1246 if (child
->IsShown() && child
->IsTopLevel() && (flags
& wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS
))
1248 // Just check if we hit the overall object
1249 int ret
= child
->wxRichTextObject::HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
1250 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
1253 else if (child
->IsShown())
1255 int ret
= child
->HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
1256 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
1260 node
= node
->GetNext();
1263 return wxRICHTEXT_HITTEST_NONE
;
1266 /// Finds the absolute position and row height for the given character position
1267 bool wxRichTextCompositeObject::FindPosition(wxDC
& dc
, wxRichTextDrawingContext
& context
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
1269 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1272 wxRichTextObject
* child
= node
->GetData();
1274 // Don't recurse if the child is a top-level object,
1275 // such as a text box, because the character position will no longer
1276 // apply. By definition, a top-level object has its own range of
1277 // character positions.
1278 if (!child
->IsTopLevel() && child
->FindPosition(dc
, context
, index
, pt
, height
, forceLineStart
))
1281 node
= node
->GetNext();
1288 void wxRichTextCompositeObject::CalculateRange(long start
, long& end
)
1290 long current
= start
;
1291 long lastEnd
= current
;
1299 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1302 wxRichTextObject
* child
= node
->GetData();
1305 child
->CalculateRange(current
, childEnd
);
1308 current
= childEnd
+ 1;
1310 node
= node
->GetNext();
1315 // A top-level object always has a range of size 1,
1316 // because its children don't count at this level.
1318 m_range
.SetRange(start
, start
);
1320 // An object with no children has zero length
1321 if (m_children
.GetCount() == 0)
1323 m_ownRange
.SetRange(0, lastEnd
);
1329 // An object with no children has zero length
1330 if (m_children
.GetCount() == 0)
1333 m_range
.SetRange(start
, end
);
1337 /// Delete range from layout.
1338 bool wxRichTextCompositeObject::DeleteRange(const wxRichTextRange
& range
)
1340 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1344 wxRichTextObject
* obj
= (wxRichTextObject
*) node
->GetData();
1345 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
1347 // Delete the range in each paragraph
1349 // When a chunk has been deleted, internally the content does not
1350 // now match the ranges.
1351 // However, so long as deletion is not done on the same object twice this is OK.
1352 // If you may delete content from the same object twice, recalculate
1353 // the ranges inbetween DeleteRange calls by calling CalculateRanges, and
1354 // adjust the range you're deleting accordingly.
1356 if (!obj
->GetRange().IsOutside(range
))
1358 // No need to delete within a top-level object; just removing this object will do fine
1359 if (!obj
->IsTopLevel())
1360 obj
->DeleteRange(range
);
1362 // Delete an empty object, or paragraph within this range.
1363 if (obj
->IsEmpty() ||
1364 (range
.GetStart() <= obj
->GetRange().GetStart() && range
.GetEnd() >= obj
->GetRange().GetEnd()))
1366 // An empty paragraph has length 1, so won't be deleted unless the
1367 // whole range is deleted.
1368 RemoveChild(obj
, true);
1378 /// Get any text in this object for the given range
1379 wxString
wxRichTextCompositeObject::GetTextForRange(const wxRichTextRange
& range
) const
1382 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1385 wxRichTextObject
* child
= node
->GetData();
1386 wxRichTextRange childRange
= range
;
1387 if (!child
->GetRange().IsOutside(range
))
1389 childRange
.LimitTo(child
->GetRange());
1391 wxString childText
= child
->GetTextForRange(childRange
);
1395 node
= node
->GetNext();
1401 /// Get the child object at the given character position
1402 wxRichTextObject
* wxRichTextCompositeObject::GetChildAtPosition(long pos
) const
1404 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1407 wxRichTextObject
* child
= node
->GetData();
1408 if (child
->GetRange().GetStart() == pos
)
1410 node
= node
->GetNext();
1415 /// Recursively merge all pieces that can be merged.
1416 bool wxRichTextCompositeObject::Defragment(const wxRichTextRange
& range
)
1418 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1421 wxRichTextObject
* child
= node
->GetData();
1422 if (range
== wxRICHTEXT_ALL
|| !child
->GetRange().IsOutside(range
))
1424 wxRichTextCompositeObject
* composite
= wxDynamicCast(child
, wxRichTextCompositeObject
);
1426 composite
->Defragment();
1428 if (node
->GetNext())
1430 wxRichTextObject
* nextChild
= node
->GetNext()->GetData();
1431 if (child
->CanMerge(nextChild
) && child
->Merge(nextChild
))
1433 nextChild
->Dereference();
1434 m_children
.Erase(node
->GetNext());
1436 // Don't set node -- we'll see if we can merge again with the next
1440 node
= node
->GetNext();
1443 node
= node
->GetNext();
1446 node
= node
->GetNext();
1449 // Delete any remaining empty objects, but leave at least one empty object per composite object.
1450 if (GetChildCount() > 1)
1452 node
= m_children
.GetFirst();
1455 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
1456 wxRichTextObject
* child
= node
->GetData();
1457 if (range
== wxRICHTEXT_ALL
|| !child
->GetRange().IsOutside(range
))
1459 if (child
->IsEmpty())
1461 child
->Dereference();
1462 m_children
.Erase(node
);
1467 node
= node
->GetNext();
1474 /// Dump to output stream for debugging
1475 void wxRichTextCompositeObject::Dump(wxTextOutputStream
& stream
)
1477 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1480 wxRichTextObject
* child
= node
->GetData();
1481 child
->Dump(stream
);
1482 node
= node
->GetNext();
1486 /// Get/set the object size for the given range. Returns false if the range
1487 /// is invalid for this object.
1488 bool wxRichTextCompositeObject::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
1490 if (!range
.IsWithin(GetRange()))
1495 wxArrayInt childExtents
;
1502 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1505 wxRichTextObject
* child
= node
->GetData();
1506 if (!child
->GetRange().IsOutside(range
))
1508 // Floating objects have a zero size within the paragraph.
1509 if (child
->IsFloating())
1514 if (partialExtents
->GetCount() > 0)
1515 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
1519 partialExtents
->Add(0 /* zero size */ + lastSize
);
1526 wxRichTextRange rangeToUse
= range
;
1527 rangeToUse
.LimitTo(child
->GetRange());
1528 if (child
->IsTopLevel())
1529 rangeToUse
= child
->GetOwnRange();
1531 int childDescent
= 0;
1533 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we're already cached the size,
1534 // but it's only going to be used after caching has taken place.
1535 if ((flags
& wxRICHTEXT_HEIGHT_ONLY
) && child
->GetCachedSize().y
!= 0)
1537 childDescent
= child
->GetDescent();
1538 childSize
= child
->GetCachedSize();
1540 sz
.y
= wxMax(sz
.y
, childSize
.y
);
1541 sz
.x
+= childSize
.x
;
1542 descent
= wxMax(descent
, childDescent
);
1544 else if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, context
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
), p
))
1546 sz
.y
= wxMax(sz
.y
, childSize
.y
);
1547 sz
.x
+= childSize
.x
;
1548 descent
= wxMax(descent
, childDescent
);
1550 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange() || child
->IsTopLevel()))
1552 child
->SetCachedSize(childSize
);
1553 child
->SetDescent(childDescent
);
1559 if (partialExtents
->GetCount() > 0)
1560 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
1565 for (i
= 0; i
< childExtents
.GetCount(); i
++)
1567 partialExtents
->Add(childExtents
[i
] + lastSize
);
1577 node
= node
->GetNext();
1583 // Invalidate the buffer. With no argument, invalidates whole buffer.
1584 void wxRichTextCompositeObject::Invalidate(const wxRichTextRange
& invalidRange
)
1586 wxRichTextObject::Invalidate(invalidRange
);
1588 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1591 wxRichTextObject
* child
= node
->GetData();
1592 if (invalidRange
!= wxRICHTEXT_ALL
&& invalidRange
!= wxRICHTEXT_NONE
&& child
->GetRange().IsOutside(invalidRange
))
1596 else if (child
->IsTopLevel())
1598 if (invalidRange
== wxRICHTEXT_NONE
)
1599 child
->Invalidate(wxRICHTEXT_NONE
);
1601 child
->Invalidate(wxRICHTEXT_ALL
); // All children must be invalidated if within parent range
1604 child
->Invalidate(invalidRange
);
1605 node
= node
->GetNext();
1609 // Move the object recursively, by adding the offset from old to new
1610 void wxRichTextCompositeObject::Move(const wxPoint
& pt
)
1612 wxPoint oldPos
= GetPosition();
1614 wxPoint offset
= pt
- oldPos
;
1616 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1619 wxRichTextObject
* child
= node
->GetData();
1620 wxPoint childPos
= child
->GetPosition() + offset
;
1621 child
->Move(childPos
);
1622 node
= node
->GetNext();
1628 * wxRichTextParagraphLayoutBox
1629 * This box knows how to lay out paragraphs.
1632 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraphLayoutBox
, wxRichTextCompositeObject
)
1634 wxRichTextParagraphLayoutBox::wxRichTextParagraphLayoutBox(wxRichTextObject
* parent
):
1635 wxRichTextCompositeObject(parent
)
1640 wxRichTextParagraphLayoutBox::~wxRichTextParagraphLayoutBox()
1642 if (m_floatCollector
)
1644 delete m_floatCollector
;
1645 m_floatCollector
= NULL
;
1649 /// Initialize the object.
1650 void wxRichTextParagraphLayoutBox::Init()
1654 // For now, assume is the only box and has no initial size.
1655 m_range
= wxRichTextRange(0, -1);
1656 m_ownRange
= wxRichTextRange(0, -1);
1658 m_invalidRange
= wxRICHTEXT_ALL
;
1660 m_partialParagraph
= false;
1661 m_floatCollector
= NULL
;
1664 void wxRichTextParagraphLayoutBox::Clear()
1668 if (m_floatCollector
)
1669 delete m_floatCollector
;
1670 m_floatCollector
= NULL
;
1671 m_partialParagraph
= false;
1675 void wxRichTextParagraphLayoutBox::Copy(const wxRichTextParagraphLayoutBox
& obj
)
1679 wxRichTextCompositeObject::Copy(obj
);
1681 m_partialParagraph
= obj
.m_partialParagraph
;
1682 m_defaultAttributes
= obj
.m_defaultAttributes
;
1685 // Gather information about floating objects; only gather floats for those paragraphs that
1686 // will not be formatted again due to optimization, after which floats will be gathered per-paragraph
1688 bool wxRichTextParagraphLayoutBox::UpdateFloatingObjects(const wxRect
& availableRect
, wxRichTextObject
* untilObj
)
1690 if (m_floatCollector
!= NULL
)
1691 delete m_floatCollector
;
1692 m_floatCollector
= new wxRichTextFloatCollector(availableRect
);
1693 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1694 // Only gather floats up to the point we'll start formatting paragraphs.
1695 while (untilObj
&& node
&& node
->GetData() != untilObj
)
1697 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
1698 wxASSERT (child
!= NULL
);
1700 m_floatCollector
->CollectFloat(child
);
1701 node
= node
->GetNext();
1707 // Returns the style sheet associated with the overall buffer.
1708 wxRichTextStyleSheet
* wxRichTextParagraphLayoutBox::GetStyleSheet() const
1710 return GetBuffer() ? GetBuffer()->GetStyleSheet() : (wxRichTextStyleSheet
*) NULL
;
1713 // Get the number of floating objects at this level
1714 int wxRichTextParagraphLayoutBox::GetFloatingObjectCount() const
1716 if (m_floatCollector
)
1717 return m_floatCollector
->GetFloatingObjectCount();
1722 // Get a list of floating objects
1723 bool wxRichTextParagraphLayoutBox::GetFloatingObjects(wxRichTextObjectList
& objects
) const
1725 if (m_floatCollector
)
1727 return m_floatCollector
->GetFloatingObjects(objects
);
1734 void wxRichTextParagraphLayoutBox::UpdateRanges()
1738 start
= GetRange().GetStart();
1740 CalculateRange(start
, end
);
1744 int wxRichTextParagraphLayoutBox::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
1747 return wxRICHTEXT_HITTEST_NONE
;
1749 int ret
= wxRICHTEXT_HITTEST_NONE
;
1750 if (m_floatCollector
&& (flags
& wxRICHTEXT_HITTEST_NO_FLOATING_OBJECTS
) == 0)
1751 ret
= m_floatCollector
->HitTest(dc
, context
, pt
, textPosition
, obj
, flags
);
1753 if (ret
== wxRICHTEXT_HITTEST_NONE
)
1754 return wxRichTextCompositeObject::HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
1762 /// Draw the floating objects
1763 void wxRichTextParagraphLayoutBox::DrawFloats(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
1765 if (m_floatCollector
)
1766 m_floatCollector
->Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
1769 void wxRichTextParagraphLayoutBox::MoveAnchoredObjectToParagraph(wxRichTextParagraph
* from
, wxRichTextParagraph
* to
, wxRichTextObject
* obj
)
1774 from
->RemoveChild(obj
);
1775 to
->AppendChild(obj
);
1779 bool wxRichTextParagraphLayoutBox::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
1784 wxRect
thisRect(GetPosition(), GetCachedSize());
1786 wxRichTextAttr
attr(GetAttributes());
1787 context
.ApplyVirtualAttributes(attr
, this);
1790 if (selection
.IsValid() && GetParentContainer() != this && selection
.WithinSelection(GetRange().GetStart(), GetParentContainer()))
1791 flags
|= wxRICHTEXT_DRAW_SELECTED
;
1793 // Don't draw guidelines if at top level
1794 int theseFlags
= flags
;
1796 theseFlags
&= ~wxRICHTEXT_DRAW_GUIDELINES
;
1797 DrawBoxAttributes(dc
, GetBuffer(), attr
, thisRect
, theseFlags
);
1799 DrawFloats(dc
, context
, range
, selection
, rect
, descent
, style
);
1800 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1803 wxRichTextObject
* child
= node
->GetData();
1805 if (child
&& !child
->GetRange().IsOutside(range
))
1807 wxRect
childRect(child
->GetPosition(), child
->GetCachedSize());
1808 wxRichTextRange childRange
= range
;
1809 if (child
->IsTopLevel())
1811 childRange
= child
->GetOwnRange();
1814 if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) == 0) && childRect
.GetTop() > rect
.GetBottom())
1819 else if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) == 0) && childRect
.GetBottom() < rect
.GetTop())
1824 child
->Draw(dc
, context
, childRange
, selection
, rect
, descent
, style
);
1827 node
= node
->GetNext();
1832 /// Lay the item out
1833 bool wxRichTextParagraphLayoutBox::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
)
1835 SetPosition(rect
.GetPosition());
1840 wxRect availableSpace
;
1841 bool formatRect
= (style
& wxRICHTEXT_LAYOUT_SPECIFIED_RECT
) == wxRICHTEXT_LAYOUT_SPECIFIED_RECT
;
1843 wxRichTextAttr
attr(GetAttributes());
1844 context
.ApplyVirtualAttributes(attr
, this);
1846 // If only laying out a specific area, the passed rect has a different meaning:
1847 // the visible part of the buffer. This is used in wxRichTextCtrl::OnSize,
1848 // so that during a size, only the visible part will be relaid out, or
1849 // it would take too long causing flicker. As an approximation, we assume that
1850 // everything up to the start of the visible area is laid out correctly.
1853 wxRect
rect2(0, 0, rect
.width
, rect
.height
);
1854 availableSpace
= GetAvailableContentArea(dc
, context
, rect2
);
1856 // Invalidate the part of the buffer from the first visible line
1857 // to the end. If other parts of the buffer are currently invalid,
1858 // then they too will be taken into account if they are above
1859 // the visible point.
1861 wxRichTextLine
* line
= GetLineAtYPosition(rect
.y
);
1863 startPos
= line
->GetAbsoluteRange().GetStart();
1865 Invalidate(wxRichTextRange(startPos
, GetOwnRange().GetEnd()));
1869 availableSpace
= GetAvailableContentArea(dc
, context
, rect
);
1872 int leftMargin
, rightMargin
, topMargin
, bottomMargin
;
1873 wxRichTextObject::GetTotalMargin(dc
, GetBuffer(), attr
, leftMargin
, rightMargin
,
1874 topMargin
, bottomMargin
);
1879 // The maximum paragraph maximum width, so we can set the overall maximum width for this object
1880 int maxMaxWidth
= 0;
1882 // The maximum paragraph minimum width, so we can set the overall minimum width for this object
1883 int maxMinWidth
= 0;
1885 // If we have vertical alignment, we must recalculate everything.
1886 bool hasVerticalAlignment
= (attr
.GetTextBoxAttr().HasVerticalAlignment() &&
1887 (attr
.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP
));
1889 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
1891 bool layoutAll
= true;
1893 // Get invalid range, rounding to paragraph start/end.
1894 wxRichTextRange invalidRange
= GetInvalidRange(true);
1896 if (invalidRange
== wxRICHTEXT_NONE
&& !formatRect
)
1899 if (invalidRange
== wxRICHTEXT_ALL
|| hasVerticalAlignment
)
1901 else // If we know what range is affected, start laying out from that point on.
1902 if (invalidRange
.GetStart() >= GetOwnRange().GetStart())
1904 wxRichTextParagraph
* firstParagraph
= GetParagraphAtPosition(invalidRange
.GetStart());
1907 wxRichTextObjectList::compatibility_iterator firstNode
= m_children
.Find(firstParagraph
);
1908 wxRichTextObjectList::compatibility_iterator previousNode
;
1910 previousNode
= firstNode
->GetPrevious();
1915 wxRichTextParagraph
* previousParagraph
= wxDynamicCast(previousNode
->GetData(), wxRichTextParagraph
);
1916 availableSpace
.y
= previousParagraph
->GetPosition().y
+ previousParagraph
->GetCachedSize().y
;
1919 // Now we're going to start iterating from the first affected paragraph.
1927 // Gather information about only those floating objects that will not be formatted,
1928 // after which floats will be gathered per-paragraph during layout.
1929 UpdateFloatingObjects(availableSpace
, node
? node
->GetData() : (wxRichTextObject
*) NULL
);
1931 // A way to force speedy rest-of-buffer layout (the 'else' below)
1932 bool forceQuickLayout
= false;
1934 // First get the size of the paragraphs we won't be laying out
1935 wxRichTextObjectList::compatibility_iterator n
= m_children
.GetFirst();
1936 while (n
&& n
!= node
)
1938 wxRichTextParagraph
* child
= wxDynamicCast(n
->GetData(), wxRichTextParagraph
);
1941 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
1942 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
1943 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
1950 // Assume this box only contains paragraphs
1952 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
1953 // Unsure if this is needed
1954 // wxCHECK_MSG( child, false, wxT("Unknown object in layout") );
1956 if (child
&& child
->IsShown())
1958 // TODO: what if the child hasn't been laid out (e.g. involved in Undo) but still has 'old' lines
1959 if ( !forceQuickLayout
&&
1961 child
->GetLines().IsEmpty() ||
1962 !child
->GetRange().IsOutside(invalidRange
)) )
1964 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1965 // lays out the object again using the minimum size
1966 child
->LayoutToBestSize(dc
, context
, GetBuffer(),
1967 attr
, child
->GetAttributes(), availableSpace
, rect
, style
&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT
);
1969 // Layout must set the cached size
1970 availableSpace
.y
+= child
->GetCachedSize().y
;
1971 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
1972 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
1973 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
1975 // If we're just formatting the visible part of the buffer,
1976 // and we're now past the bottom of the window, and we don't have any
1977 // floating objects (since they may cause wrapping to change for the rest of the
1978 // the buffer), start quick layout.
1979 if (!hasVerticalAlignment
&& formatRect
&& child
->GetPosition().y
> rect
.GetBottom() && GetFloatingObjectCount() == 0)
1980 forceQuickLayout
= true;
1984 // We're outside the immediately affected range, so now let's just
1985 // move everything up or down. This assumes that all the children have previously
1986 // been laid out and have wrapped line lists associated with them.
1987 // TODO: check all paragraphs before the affected range.
1989 int inc
= availableSpace
.y
- child
->GetPosition().y
;
1993 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
1996 if (child
->GetLines().GetCount() == 0)
1998 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1999 // lays out the object again using the minimum size
2000 child
->LayoutToBestSize(dc
, context
, GetBuffer(),
2001 attr
, child
->GetAttributes(), availableSpace
, rect
, style
&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT
);
2003 //child->Layout(dc, availableChildRect, style);
2006 child
->Move(wxPoint(child
->GetPosition().x
, child
->GetPosition().y
+ inc
));
2008 availableSpace
.y
+= child
->GetCachedSize().y
;
2009 maxWidth
= wxMax(maxWidth
, child
->GetCachedSize().x
);
2010 maxMinWidth
= wxMax(maxMinWidth
, child
->GetMinSize().x
);
2011 maxMaxWidth
= wxMax(maxMaxWidth
, child
->GetMaxSize().x
);
2014 node
= node
->GetNext();
2020 node
= node
->GetNext();
2023 node
= m_children
.GetLast();
2024 if (node
&& node
->GetData()->IsShown())
2026 wxRichTextObject
* child
= node
->GetData();
2027 maxHeight
= child
->GetPosition().y
- (GetPosition().y
+ topMargin
) + child
->GetCachedSize().y
;
2030 maxHeight
= 0; // topMargin + bottomMargin;
2032 // Check the bottom edge of any floating object
2033 if (GetFloatCollector() && GetFloatCollector()->HasFloats())
2035 int bottom
= GetFloatCollector()->GetLastRectBottom();
2036 if (bottom
> maxHeight
)
2040 if (attr
.GetTextBoxAttr().GetSize().GetWidth().IsValid())
2042 wxRect r
= AdjustAvailableSpace(dc
, GetBuffer(), wxRichTextAttr() /* not used */, attr
, parentRect
, parentRect
);
2043 int w
= r
.GetWidth();
2045 // Convert external to content rect
2046 w
= w
- leftMargin
- rightMargin
;
2047 maxWidth
= wxMax(maxWidth
, w
);
2048 maxMaxWidth
= wxMax(maxMaxWidth
, w
);
2051 // TODO: (also in para layout) should set the
2052 // object's size to an absolute one if specified,
2053 // but if not specified, calculate it from content.
2055 // We need to add back the margins etc.
2057 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2058 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxWidth
, maxHeight
));
2059 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2060 SetCachedSize(marginRect
.GetSize());
2063 // The maximum size is the greatest of all maximum widths for all paragraphs.
2065 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2066 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxMaxWidth
, maxHeight
)); // Actually max height is a lie, we can't know it
2067 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2068 SetMaxSize(marginRect
.GetSize());
2071 // The minimum size is the greatest of all minimum widths for all paragraphs.
2073 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
2074 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxMinWidth
, maxHeight
)); // Actually max height is a lie, we can't know it
2075 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
2076 SetMinSize(marginRect
.GetSize());
2079 if (attr
.GetTextBoxAttr().HasVerticalAlignment() &&
2080 (attr
.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP
))
2083 int leftOverSpace
= availableSpace
.height
- topMargin
- bottomMargin
- maxHeight
;
2084 if (leftOverSpace
> 0)
2086 if (attr
.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_CENTRE
)
2088 yOffset
= (leftOverSpace
/2);
2090 else if (attr
.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_BOTTOM
)
2092 yOffset
= leftOverSpace
;
2096 // Move all the children to vertically align the content
2097 // This doesn't take into account floating objects, unfortunately.
2100 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2103 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2105 child
->Move(wxPoint(child
->GetPosition().x
, child
->GetPosition().y
+ yOffset
));
2107 node
= node
->GetNext();
2112 m_invalidRange
= wxRICHTEXT_NONE
;
2117 /// Get/set the size for the given range.
2118 bool wxRichTextParagraphLayoutBox::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* WXUNUSED(partialExtents
)) const
2122 wxRichTextObjectList::compatibility_iterator startPara
= wxRichTextObjectList::compatibility_iterator();
2123 wxRichTextObjectList::compatibility_iterator endPara
= wxRichTextObjectList::compatibility_iterator();
2125 // First find the first paragraph whose starting position is within the range.
2126 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2129 // child is a paragraph
2130 wxRichTextObject
* child
= node
->GetData();
2131 const wxRichTextRange
& r
= child
->GetRange();
2133 if (r
.GetStart() <= range
.GetStart() && r
.GetEnd() >= range
.GetStart())
2139 node
= node
->GetNext();
2142 // Next find the last paragraph containing part of the range
2143 node
= m_children
.GetFirst();
2146 // child is a paragraph
2147 wxRichTextObject
* child
= node
->GetData();
2148 const wxRichTextRange
& r
= child
->GetRange();
2150 if (r
.GetStart() <= range
.GetEnd() && r
.GetEnd() >= range
.GetEnd())
2156 node
= node
->GetNext();
2159 if (!startPara
|| !endPara
)
2162 // Now we can add up the sizes
2163 for (node
= startPara
; node
; node
= node
->GetNext())
2165 // child is a paragraph
2166 wxRichTextObject
* child
= node
->GetData();
2167 const wxRichTextRange
& childRange
= child
->GetRange();
2168 wxRichTextRange rangeToFind
= range
;
2169 rangeToFind
.LimitTo(childRange
);
2171 if (child
->IsTopLevel())
2172 rangeToFind
= child
->GetOwnRange();
2176 int childDescent
= 0;
2177 child
->GetRangeSize(rangeToFind
, childSize
, childDescent
, dc
, context
, flags
, position
);
2179 descent
= wxMax(childDescent
, descent
);
2181 sz
.x
= wxMax(sz
.x
, childSize
.x
);
2182 sz
.y
+= childSize
.y
;
2184 if (node
== endPara
)
2193 /// Get the paragraph at the given position
2194 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphAtPosition(long pos
, bool caretPosition
) const
2199 // First find the first paragraph whose starting position is within the range.
2200 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2203 // child is a paragraph
2204 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2205 // wxASSERT (child != NULL);
2209 // Return first child in buffer if position is -1
2213 if (child
->GetRange().Contains(pos
))
2217 node
= node
->GetNext();
2222 /// Get the line at the given position
2223 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineAtPosition(long pos
, bool caretPosition
) const
2228 // First find the first paragraph whose starting position is within the range.
2229 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2232 wxRichTextObject
* obj
= (wxRichTextObject
*) node
->GetData();
2233 if (obj
->GetRange().Contains(pos
))
2235 // child is a paragraph
2236 wxRichTextParagraph
* child
= wxDynamicCast(obj
, wxRichTextParagraph
);
2237 // wxASSERT (child != NULL);
2241 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2244 wxRichTextLine
* line
= node2
->GetData();
2246 wxRichTextRange range
= line
->GetAbsoluteRange();
2248 if (range
.Contains(pos
) ||
2250 // If the position is end-of-paragraph, then return the last line of
2251 // of the paragraph.
2252 ((range
.GetEnd() == child
->GetRange().GetEnd()-1) && (pos
== child
->GetRange().GetEnd())))
2255 node2
= node2
->GetNext();
2260 node
= node
->GetNext();
2263 int lineCount
= GetLineCount();
2265 return GetLineForVisibleLineNumber(lineCount
-1);
2270 /// Get the line at the given y pixel position, or the last line.
2271 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineAtYPosition(int y
) const
2273 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2276 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2277 // wxASSERT (child != NULL);
2281 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2284 wxRichTextLine
* line
= node2
->GetData();
2286 wxRect
rect(line
->GetRect());
2288 if (y
<= rect
.GetBottom())
2291 node2
= node2
->GetNext();
2295 node
= node
->GetNext();
2299 int lineCount
= GetLineCount();
2301 return GetLineForVisibleLineNumber(lineCount
-1);
2306 /// Get the number of visible lines
2307 int wxRichTextParagraphLayoutBox::GetLineCount() const
2311 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2314 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2315 // wxASSERT (child != NULL);
2318 count
+= child
->GetLines().GetCount();
2320 node
= node
->GetNext();
2326 /// Get the paragraph for a given line
2327 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphForLine(wxRichTextLine
* line
) const
2329 return GetParagraphAtPosition(line
->GetAbsoluteRange().GetStart());
2332 /// Get the line size at the given position
2333 wxSize
wxRichTextParagraphLayoutBox::GetLineSizeAtPosition(long pos
, bool caretPosition
) const
2335 wxRichTextLine
* line
= GetLineAtPosition(pos
, caretPosition
);
2338 return line
->GetSize();
2341 return wxSize(0, 0);
2345 /// Convenience function to add a paragraph of text
2346 wxRichTextRange
wxRichTextParagraphLayoutBox::AddParagraph(const wxString
& text
, wxRichTextAttr
* paraStyle
)
2348 // Don't use the base style, just the default style, and the base style will
2349 // be combined at display time.
2350 // Divide into paragraph and character styles.
2352 wxRichTextAttr defaultCharStyle
;
2353 wxRichTextAttr defaultParaStyle
;
2355 // If the default style is a named paragraph style, don't apply any character formatting
2356 // to the initial text string.
2357 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2359 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2361 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2364 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2366 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2367 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2369 wxRichTextParagraph
* para
= new wxRichTextParagraph(text
, this, pStyle
, cStyle
);
2375 return para
->GetRange();
2378 /// Adds multiple paragraphs, based on newlines.
2379 wxRichTextRange
wxRichTextParagraphLayoutBox::AddParagraphs(const wxString
& text
, wxRichTextAttr
* paraStyle
)
2381 // Don't use the base style, just the default style, and the base style will
2382 // be combined at display time.
2383 // Divide into paragraph and character styles.
2385 wxRichTextAttr defaultCharStyle
;
2386 wxRichTextAttr defaultParaStyle
;
2388 // If the default style is a named paragraph style, don't apply any character formatting
2389 // to the initial text string.
2390 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2392 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2394 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2397 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2399 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2400 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2402 wxRichTextParagraph
* firstPara
= NULL
;
2403 wxRichTextParagraph
* lastPara
= NULL
;
2405 wxRichTextRange
range(-1, -1);
2408 size_t len
= text
.length();
2410 wxRichTextParagraph
* para
= new wxRichTextParagraph(wxEmptyString
, this, pStyle
, cStyle
);
2419 wxChar ch
= text
[i
];
2420 if (ch
== wxT('\n') || ch
== wxT('\r'))
2424 wxRichTextPlainText
* plainText
= (wxRichTextPlainText
*) para
->GetChildren().GetFirst()->GetData();
2425 plainText
->SetText(line
);
2427 para
= new wxRichTextParagraph(wxEmptyString
, this, pStyle
, cStyle
);
2432 line
= wxEmptyString
;
2443 wxRichTextPlainText
* plainText
= (wxRichTextPlainText
*) para
->GetChildren().GetFirst()->GetData();
2444 plainText
->SetText(line
);
2449 return wxRichTextRange(firstPara
->GetRange().GetStart(), lastPara
->GetRange().GetEnd());
2452 /// Convenience function to add an image
2453 wxRichTextRange
wxRichTextParagraphLayoutBox::AddImage(const wxImage
& image
, wxRichTextAttr
* paraStyle
)
2455 // Don't use the base style, just the default style, and the base style will
2456 // be combined at display time.
2457 // Divide into paragraph and character styles.
2459 wxRichTextAttr defaultCharStyle
;
2460 wxRichTextAttr defaultParaStyle
;
2462 // If the default style is a named paragraph style, don't apply any character formatting
2463 // to the initial text string.
2464 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2466 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2468 defaultParaStyle
= def
->GetStyleMergedWithBase(GetStyleSheet());
2471 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle
, defaultCharStyle
);
2473 wxRichTextAttr
* pStyle
= paraStyle
? paraStyle
: (wxRichTextAttr
*) & defaultParaStyle
;
2474 wxRichTextAttr
* cStyle
= & defaultCharStyle
;
2476 wxRichTextParagraph
* para
= new wxRichTextParagraph(this, pStyle
);
2478 para
->AppendChild(new wxRichTextImage(image
, this, cStyle
));
2482 return para
->GetRange();
2486 /// Insert fragment into this box at the given position. If partialParagraph is true,
2487 /// it is assumed that the last (or only) paragraph is just a piece of data with no paragraph
2490 bool wxRichTextParagraphLayoutBox::InsertFragment(long position
, wxRichTextParagraphLayoutBox
& fragment
)
2492 // First, find the first paragraph whose starting position is within the range.
2493 wxRichTextParagraph
* para
= GetParagraphAtPosition(position
);
2496 wxRichTextAttr originalAttr
= para
->GetAttributes();
2498 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(para
);
2500 // Now split at this position, returning the object to insert the new
2501 // ones in front of.
2502 wxRichTextObject
* nextObject
= para
->SplitAt(position
);
2504 // Special case: partial paragraph, just one paragraph. Might be a small amount of
2505 // text, for example, so let's optimize.
2507 if (fragment
.GetPartialParagraph() && fragment
.GetChildren().GetCount() == 1)
2509 // Add the first para to this para...
2510 wxRichTextObjectList::compatibility_iterator firstParaNode
= fragment
.GetChildren().GetFirst();
2514 // Iterate through the fragment paragraph inserting the content into this paragraph.
2515 wxRichTextParagraph
* firstPara
= wxDynamicCast(firstParaNode
->GetData(), wxRichTextParagraph
);
2516 wxASSERT (firstPara
!= NULL
);
2518 wxRichTextObjectList::compatibility_iterator objectNode
= firstPara
->GetChildren().GetFirst();
2521 wxRichTextObject
* newObj
= objectNode
->GetData()->Clone();
2526 para
->AppendChild(newObj
);
2530 // Insert before nextObject
2531 para
->InsertChild(newObj
, nextObject
);
2534 objectNode
= objectNode
->GetNext();
2541 // Procedure for inserting a fragment consisting of a number of
2544 // 1. Remove and save the content that's after the insertion point, for adding
2545 // back once we've added the fragment.
2546 // 2. Add the content from the first fragment paragraph to the current
2548 // 3. Add remaining fragment paragraphs after the current paragraph.
2549 // 4. Add back the saved content from the first paragraph. If partialParagraph
2550 // is true, add it to the last paragraph added and not a new one.
2552 // 1. Remove and save objects after split point.
2553 wxList savedObjects
;
2555 para
->MoveToList(nextObject
, savedObjects
);
2557 // 2. Add the content from the 1st fragment paragraph.
2558 wxRichTextObjectList::compatibility_iterator firstParaNode
= fragment
.GetChildren().GetFirst();
2562 wxRichTextParagraph
* firstPara
= wxDynamicCast(firstParaNode
->GetData(), wxRichTextParagraph
);
2563 wxASSERT(firstPara
!= NULL
);
2565 if (!(fragment
.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
))
2566 para
->SetAttributes(firstPara
->GetAttributes());
2568 // Save empty paragraph attributes for appending later
2569 // These are character attributes deliberately set for a new paragraph. Without this,
2570 // we couldn't pass default attributes when appending a new paragraph.
2571 wxRichTextAttr emptyParagraphAttributes
;
2573 wxRichTextObjectList::compatibility_iterator objectNode
= firstPara
->GetChildren().GetFirst();
2575 if (objectNode
&& firstPara
->GetChildren().GetCount() == 1 && objectNode
->GetData()->IsEmpty())
2576 emptyParagraphAttributes
= objectNode
->GetData()->GetAttributes();
2580 wxRichTextObject
* newObj
= objectNode
->GetData()->Clone();
2583 para
->AppendChild(newObj
);
2585 objectNode
= objectNode
->GetNext();
2588 // 3. Add remaining fragment paragraphs after the current paragraph.
2589 wxRichTextObjectList::compatibility_iterator nextParagraphNode
= node
->GetNext();
2590 wxRichTextObject
* nextParagraph
= NULL
;
2591 if (nextParagraphNode
)
2592 nextParagraph
= nextParagraphNode
->GetData();
2594 wxRichTextObjectList::compatibility_iterator i
= fragment
.GetChildren().GetFirst()->GetNext();
2595 wxRichTextParagraph
* finalPara
= para
;
2597 bool needExtraPara
= (!i
|| !fragment
.GetPartialParagraph());
2599 // If there was only one paragraph, we need to insert a new one.
2602 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2603 wxASSERT( para
!= NULL
);
2605 finalPara
= (wxRichTextParagraph
*) para
->Clone();
2608 InsertChild(finalPara
, nextParagraph
);
2610 AppendChild(finalPara
);
2615 // If there was only one paragraph, or we have full paragraphs in our fragment,
2616 // we need to insert a new one.
2619 finalPara
= new wxRichTextParagraph
;
2622 InsertChild(finalPara
, nextParagraph
);
2624 AppendChild(finalPara
);
2627 // 4. Add back the remaining content.
2631 finalPara
->MoveFromList(savedObjects
);
2633 // Ensure there's at least one object
2634 if (finalPara
->GetChildCount() == 0)
2636 wxRichTextPlainText
* text
= new wxRichTextPlainText(wxEmptyString
);
2637 text
->SetAttributes(emptyParagraphAttributes
);
2639 finalPara
->AppendChild(text
);
2643 if ((fragment
.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
) && firstPara
)
2644 finalPara
->SetAttributes(firstPara
->GetAttributes());
2645 else if (finalPara
&& finalPara
!= para
)
2646 finalPara
->SetAttributes(originalAttr
);
2654 wxRichTextObjectList::compatibility_iterator i
= fragment
.GetChildren().GetFirst();
2657 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2658 wxASSERT( para
!= NULL
);
2660 AppendChild(para
->Clone());
2669 /// Make a copy of the fragment corresponding to the given range, putting it in 'fragment'.
2670 /// If there was an incomplete paragraph at the end, partialParagraph is set to true.
2671 bool wxRichTextParagraphLayoutBox::CopyFragment(const wxRichTextRange
& range
, wxRichTextParagraphLayoutBox
& fragment
)
2673 wxRichTextObjectList::compatibility_iterator i
= GetChildren().GetFirst();
2676 wxRichTextParagraph
* para
= wxDynamicCast(i
->GetData(), wxRichTextParagraph
);
2677 wxASSERT( para
!= NULL
);
2679 if (!para
->GetRange().IsOutside(range
))
2681 fragment
.AppendChild(para
->Clone());
2686 // Now top and tail the first and last paragraphs in our new fragment (which might be the same).
2687 if (!fragment
.IsEmpty())
2689 wxRichTextParagraph
* firstPara
= wxDynamicCast(fragment
.GetChildren().GetFirst()->GetData(), wxRichTextParagraph
);
2690 wxASSERT( firstPara
!= NULL
);
2692 wxRichTextParagraph
* lastPara
= wxDynamicCast(fragment
.GetChildren().GetLast()->GetData(), wxRichTextParagraph
);
2693 wxASSERT( lastPara
!= NULL
);
2695 if (!firstPara
|| !lastPara
)
2698 bool isFragment
= (range
.GetEnd() < lastPara
->GetRange().GetEnd());
2700 long firstPos
= firstPara
->GetRange().GetStart();
2702 // Adjust for renumbering from zero
2703 wxRichTextRange
topTailRange(range
.GetStart() - firstPos
, range
.GetEnd() - firstPos
);
2706 fragment
.CalculateRange(0, end
);
2708 // Chop off the start of the paragraph
2709 if (topTailRange
.GetStart() > 0)
2711 wxRichTextRange
r(0, topTailRange
.GetStart()-1);
2712 firstPara
->DeleteRange(r
);
2714 // Make sure the numbering is correct
2715 fragment
.CalculateRange(0, end
);
2717 // Now, we've deleted some positions, so adjust the range
2719 topTailRange
.SetStart(range
.GetLength());
2720 topTailRange
.SetEnd(fragment
.GetOwnRange().GetEnd());
2724 topTailRange
.SetStart(range
.GetLength());
2725 topTailRange
.SetEnd(fragment
.GetOwnRange().GetEnd());
2728 if (topTailRange
.GetStart() < lastPara
->GetRange().GetEnd())
2730 lastPara
->DeleteRange(topTailRange
);
2732 // Make sure the numbering is correct
2734 fragment
.CalculateRange(0, end
);
2736 // We only have part of a paragraph at the end
2737 fragment
.SetPartialParagraph(true);
2741 // We have a partial paragraph (don't save last new paragraph marker)
2742 // or complete paragraph
2743 fragment
.SetPartialParagraph(isFragment
);
2750 /// Given a position, get the number of the visible line (potentially many to a paragraph),
2751 /// starting from zero at the start of the buffer.
2752 long wxRichTextParagraphLayoutBox::GetVisibleLineNumber(long pos
, bool caretPosition
, bool startOfLine
) const
2759 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2762 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2763 // wxASSERT( child != NULL );
2767 if (child
->GetRange().Contains(pos
))
2769 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2772 wxRichTextLine
* line
= node2
->GetData();
2773 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
2775 if (lineRange
.Contains(pos
) || pos
== lineRange
.GetStart())
2777 // If the caret is displayed at the end of the previous wrapped line,
2778 // we want to return the line it's _displayed_ at (not the actual line
2779 // containing the position).
2780 if (lineRange
.GetStart() == pos
&& !startOfLine
&& child
->GetRange().GetStart() != pos
)
2781 return lineCount
- 1;
2788 node2
= node2
->GetNext();
2790 // If we didn't find it in the lines, it must be
2791 // the last position of the paragraph. So return the last line.
2795 lineCount
+= child
->GetLines().GetCount();
2798 node
= node
->GetNext();
2805 /// Given a line number, get the corresponding wxRichTextLine object.
2806 wxRichTextLine
* wxRichTextParagraphLayoutBox::GetLineForVisibleLineNumber(long lineNumber
) const
2810 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2813 wxRichTextParagraph
* child
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2814 // wxASSERT(child != NULL);
2818 if (lineNumber
< (int) (child
->GetLines().GetCount() + lineCount
))
2820 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
2823 wxRichTextLine
* line
= node2
->GetData();
2825 if (lineCount
== lineNumber
)
2830 node2
= node2
->GetNext();
2834 lineCount
+= child
->GetLines().GetCount();
2837 node
= node
->GetNext();
2844 /// Delete range from layout.
2845 bool wxRichTextParagraphLayoutBox::DeleteRange(const wxRichTextRange
& range
)
2847 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2849 wxRichTextParagraph
* firstPara
= NULL
;
2852 wxRichTextParagraph
* obj
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
2853 // wxASSERT (obj != NULL);
2855 wxRichTextObjectList::compatibility_iterator next
= node
->GetNext();
2859 // Delete the range in each paragraph
2861 if (!obj
->GetRange().IsOutside(range
))
2863 // Deletes the content of this object within the given range
2864 obj
->DeleteRange(range
);
2866 wxRichTextRange thisRange
= obj
->GetRange();
2867 wxRichTextAttr thisAttr
= obj
->GetAttributes();
2869 // If the whole paragraph is within the range to delete,
2870 // delete the whole thing.
2871 if (range
.GetStart() <= thisRange
.GetStart() && range
.GetEnd() >= thisRange
.GetEnd())
2873 // Delete the whole object
2874 RemoveChild(obj
, true);
2877 else if (!firstPara
)
2880 // If the range includes the paragraph end, we need to join this
2881 // and the next paragraph.
2882 if (range
.GetEnd() <= thisRange
.GetEnd())
2884 // We need to move the objects from the next paragraph
2885 // to this paragraph
2887 wxRichTextParagraph
* nextParagraph
= NULL
;
2888 if ((range
.GetEnd() < thisRange
.GetEnd()) && obj
)
2889 nextParagraph
= obj
;
2892 // We're ending at the end of the paragraph, so merge the _next_ paragraph.
2894 nextParagraph
= wxDynamicCast(next
->GetData(), wxRichTextParagraph
);
2897 bool applyFinalParagraphStyle
= firstPara
&& nextParagraph
&& nextParagraph
!= firstPara
;
2899 wxRichTextAttr nextParaAttr
;
2900 if (applyFinalParagraphStyle
)
2902 // Special case when deleting the end of a paragraph - use _this_ paragraph's style,
2903 // not the next one.
2904 if (range
.GetStart() == range
.GetEnd() && range
.GetStart() == thisRange
.GetEnd())
2905 nextParaAttr
= thisAttr
;
2907 nextParaAttr
= nextParagraph
->GetAttributes();
2910 if (firstPara
&& nextParagraph
&& firstPara
!= nextParagraph
)
2912 // Move the objects to the previous para
2913 wxRichTextObjectList::compatibility_iterator node1
= nextParagraph
->GetChildren().GetFirst();
2917 wxRichTextObject
* obj1
= node1
->GetData();
2919 firstPara
->AppendChild(obj1
);
2921 wxRichTextObjectList::compatibility_iterator next1
= node1
->GetNext();
2922 nextParagraph
->GetChildren().Erase(node1
);
2927 // Delete the paragraph
2928 RemoveChild(nextParagraph
, true);
2931 // Avoid empty paragraphs
2932 if (firstPara
&& firstPara
->GetChildren().GetCount() == 0)
2934 wxRichTextPlainText
* text
= new wxRichTextPlainText(wxEmptyString
);
2935 firstPara
->AppendChild(text
);
2938 if (applyFinalParagraphStyle
)
2939 firstPara
->SetAttributes(nextParaAttr
);
2952 /// Get any text in this object for the given range
2953 wxString
wxRichTextParagraphLayoutBox::GetTextForRange(const wxRichTextRange
& range
) const
2957 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
2960 wxRichTextObject
* child
= node
->GetData();
2961 if (!child
->GetRange().IsOutside(range
))
2963 wxRichTextRange childRange
= range
;
2964 childRange
.LimitTo(child
->GetRange());
2966 wxString childText
= child
->GetTextForRange(childRange
);
2970 if ((childRange
.GetEnd() == child
->GetRange().GetEnd()) && node
->GetNext())
2975 node
= node
->GetNext();
2981 /// Get all the text
2982 wxString
wxRichTextParagraphLayoutBox::GetText() const
2984 return GetTextForRange(GetOwnRange());
2987 /// Get the paragraph by number
2988 wxRichTextParagraph
* wxRichTextParagraphLayoutBox::GetParagraphAtLine(long paragraphNumber
) const
2990 if ((size_t) paragraphNumber
>= GetChildCount())
2993 return (wxRichTextParagraph
*) GetChild((size_t) paragraphNumber
);
2996 /// Get the length of the paragraph
2997 int wxRichTextParagraphLayoutBox::GetParagraphLength(long paragraphNumber
) const
2999 wxRichTextParagraph
* para
= GetParagraphAtLine(paragraphNumber
);
3001 return para
->GetRange().GetLength() - 1; // don't include newline
3006 /// Get the text of the paragraph
3007 wxString
wxRichTextParagraphLayoutBox::GetParagraphText(long paragraphNumber
) const
3009 wxRichTextParagraph
* para
= GetParagraphAtLine(paragraphNumber
);
3011 return para
->GetTextForRange(para
->GetRange());
3013 return wxEmptyString
;
3016 /// Convert zero-based line column and paragraph number to a position.
3017 long wxRichTextParagraphLayoutBox::XYToPosition(long x
, long y
) const
3019 wxRichTextParagraph
* para
= GetParagraphAtLine(y
);
3022 return para
->GetRange().GetStart() + x
;
3028 /// Convert zero-based position to line column and paragraph number
3029 bool wxRichTextParagraphLayoutBox::PositionToXY(long pos
, long* x
, long* y
) const
3031 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
);
3035 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3038 wxRichTextObject
* child
= node
->GetData();
3042 node
= node
->GetNext();
3046 *x
= pos
- para
->GetRange().GetStart();
3054 /// Get the leaf object in a paragraph at this position.
3055 /// Given a line number, get the corresponding wxRichTextLine object.
3056 wxRichTextObject
* wxRichTextParagraphLayoutBox::GetLeafObjectAtPosition(long position
) const
3058 wxRichTextParagraph
* para
= GetParagraphAtPosition(position
);
3061 wxRichTextObjectList::compatibility_iterator node
= para
->GetChildren().GetFirst();
3065 wxRichTextObject
* child
= node
->GetData();
3066 if (child
->GetRange().Contains(position
))
3069 node
= node
->GetNext();
3071 if (position
== para
->GetRange().GetEnd() && para
->GetChildCount() > 0)
3072 return para
->GetChildren().GetLast()->GetData();
3077 /// Set character or paragraph text attributes: apply character styles only to immediate text nodes
3078 bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange
& range
, const wxRichTextAttr
& style
, int flags
)
3080 bool characterStyle
= false;
3081 bool paragraphStyle
= false;
3083 if (style
.IsCharacterStyle())
3084 characterStyle
= true;
3085 if (style
.IsParagraphStyle())
3086 paragraphStyle
= true;
3088 wxRichTextBuffer
* buffer
= GetBuffer();
3090 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
3091 bool applyMinimal
= ((flags
& wxRICHTEXT_SETSTYLE_OPTIMIZE
) != 0);
3092 bool parasOnly
= ((flags
& wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY
) != 0);
3093 bool charactersOnly
= ((flags
& wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY
) != 0);
3094 bool resetExistingStyle
= ((flags
& wxRICHTEXT_SETSTYLE_RESET
) != 0);
3095 bool removeStyle
= ((flags
& wxRICHTEXT_SETSTYLE_REMOVE
) != 0);
3097 // Apply paragraph style first, if any
3098 wxRichTextAttr
wholeStyle(style
);
3100 if (!removeStyle
&& wholeStyle
.HasParagraphStyleName() && buffer
->GetStyleSheet())
3102 wxRichTextParagraphStyleDefinition
* def
= buffer
->GetStyleSheet()->FindParagraphStyle(wholeStyle
.GetParagraphStyleName());
3104 wxRichTextApplyStyle(wholeStyle
, def
->GetStyleMergedWithBase(buffer
->GetStyleSheet()));
3107 // Limit the attributes to be set to the content to only character attributes.
3108 wxRichTextAttr
characterAttributes(wholeStyle
);
3109 characterAttributes
.SetFlags(characterAttributes
.GetFlags() & (wxTEXT_ATTR_CHARACTER
));
3111 if (!removeStyle
&& characterAttributes
.HasCharacterStyleName() && buffer
->GetStyleSheet())
3113 wxRichTextCharacterStyleDefinition
* def
= buffer
->GetStyleSheet()->FindCharacterStyle(characterAttributes
.GetCharacterStyleName());
3115 wxRichTextApplyStyle(characterAttributes
, def
->GetStyleMergedWithBase(buffer
->GetStyleSheet()));
3118 // If we are associated with a control, make undoable; otherwise, apply immediately
3121 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3123 wxRichTextAction
* action
= NULL
;
3125 if (haveControl
&& withUndo
)
3127 action
= new wxRichTextAction(NULL
, _("Change Style"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
3128 action
->SetRange(range
);
3129 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3132 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3135 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3136 // wxASSERT (para != NULL);
3138 if (para
&& para
->GetChildCount() > 0)
3140 // Stop searching if we're beyond the range of interest
3141 if (para
->GetRange().GetStart() > range
.GetEnd())
3144 if (!para
->GetRange().IsOutside(range
))
3146 // We'll be using a copy of the paragraph to make style changes,
3147 // not updating the buffer directly.
3148 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3150 if (haveControl
&& withUndo
)
3152 newPara
= new wxRichTextParagraph(*para
);
3153 action
->GetNewParagraphs().AppendChild(newPara
);
3155 // Also store the old ones for Undo
3156 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3161 // If we're specifying paragraphs only, then we really mean character formatting
3162 // to be included in the paragraph style
3163 if ((paragraphStyle
|| parasOnly
) && !charactersOnly
)
3167 // Removes the given style from the paragraph
3168 wxRichTextRemoveStyle(newPara
->GetAttributes(), style
);
3170 else if (resetExistingStyle
)
3171 newPara
->GetAttributes() = wholeStyle
;
3176 // Only apply attributes that will make a difference to the combined
3177 // style as seen on the display
3178 wxRichTextAttr
combinedAttr(para
->GetCombinedAttributes(true));
3179 wxRichTextApplyStyle(newPara
->GetAttributes(), wholeStyle
, & combinedAttr
);
3182 wxRichTextApplyStyle(newPara
->GetAttributes(), wholeStyle
);
3186 // When applying paragraph styles dynamically, don't change the text objects' attributes
3187 // since they will computed as needed. Only apply the character styling if it's _only_
3188 // character styling. This policy is subject to change and might be put under user control.
3190 // Hm. we might well be applying a mix of paragraph and character styles, in which
3191 // case we _do_ want to apply character styles regardless of what para styles are set.
3192 // But if we're applying a paragraph style, which has some character attributes, but
3193 // we only want the paragraphs to hold this character style, then we _don't_ want to
3194 // apply the character style. So we need to be able to choose.
3196 if (!parasOnly
&& (characterStyle
|charactersOnly
) && range
.GetStart() != newPara
->GetRange().GetEnd())
3198 wxRichTextRange
childRange(range
);
3199 childRange
.LimitTo(newPara
->GetRange());
3201 // Find the starting position and if necessary split it so
3202 // we can start applying a different style.
3203 // TODO: check that the style actually changes or is different
3204 // from style outside of range
3205 wxRichTextObject
* firstObject
wxDUMMY_INITIALIZE(NULL
);
3206 wxRichTextObject
* lastObject
wxDUMMY_INITIALIZE(NULL
);
3208 if (childRange
.GetStart() == newPara
->GetRange().GetStart())
3209 firstObject
= newPara
->GetChildren().GetFirst()->GetData();
3211 firstObject
= newPara
->SplitAt(range
.GetStart());
3213 // Increment by 1 because we're apply the style one _after_ the split point
3214 long splitPoint
= childRange
.GetEnd();
3215 if (splitPoint
!= newPara
->GetRange().GetEnd())
3219 if (splitPoint
== newPara
->GetRange().GetEnd())
3220 lastObject
= newPara
->GetChildren().GetLast()->GetData();
3222 // lastObject is set as a side-effect of splitting. It's
3223 // returned as the object before the new object.
3224 (void) newPara
->SplitAt(splitPoint
, & lastObject
);
3226 wxASSERT(firstObject
!= NULL
);
3227 wxASSERT(lastObject
!= NULL
);
3229 if (!firstObject
|| !lastObject
)
3232 wxRichTextObjectList::compatibility_iterator firstNode
= newPara
->GetChildren().Find(firstObject
);
3233 wxRichTextObjectList::compatibility_iterator lastNode
= newPara
->GetChildren().Find(lastObject
);
3235 wxASSERT(firstNode
);
3238 wxRichTextObjectList::compatibility_iterator node2
= firstNode
;
3242 wxRichTextObject
* child
= node2
->GetData();
3246 // Removes the given style from the paragraph
3247 wxRichTextRemoveStyle(child
->GetAttributes(), style
);
3249 else if (resetExistingStyle
)
3250 child
->GetAttributes() = characterAttributes
;
3255 // Only apply attributes that will make a difference to the combined
3256 // style as seen on the display
3257 wxRichTextAttr
combinedAttr(newPara
->GetCombinedAttributes(child
->GetAttributes(), true));
3258 wxRichTextApplyStyle(child
->GetAttributes(), characterAttributes
, & combinedAttr
);
3261 wxRichTextApplyStyle(child
->GetAttributes(), characterAttributes
);
3264 if (node2
== lastNode
)
3267 node2
= node2
->GetNext();
3273 node
= node
->GetNext();
3276 // Do action, or delay it until end of batch.
3277 if (haveControl
&& withUndo
)
3278 buffer
->SubmitAction(action
);
3283 // Just change the attributes for this single object.
3284 void wxRichTextParagraphLayoutBox::SetStyle(wxRichTextObject
* obj
, const wxRichTextAttr
& textAttr
, int flags
)
3286 wxRichTextBuffer
* buffer
= GetBuffer();
3287 bool withUndo
= flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
;
3288 bool resetExistingStyle
= ((flags
& wxRICHTEXT_SETSTYLE_RESET
) != 0);
3289 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3291 wxRichTextAction
*action
= NULL
;
3292 wxRichTextAttr newAttr
= obj
->GetAttributes();
3293 if (resetExistingStyle
)
3296 newAttr
.Apply(textAttr
);
3298 if (haveControl
&& withUndo
)
3300 action
= new wxRichTextAction(NULL
, _("Change Object Style"), wxRICHTEXT_CHANGE_ATTRIBUTES
, buffer
, obj
->GetContainer(), buffer
->GetRichTextCtrl());
3301 action
->SetRange(obj
->GetRange().FromInternal());
3302 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3303 action
->MakeObject(obj
);
3305 action
->GetAttributes() = newAttr
;
3308 obj
->GetAttributes() = newAttr
;
3310 if (haveControl
&& withUndo
)
3311 buffer
->SubmitAction(action
);
3314 /// Get the text attributes for this position.
3315 bool wxRichTextParagraphLayoutBox::GetStyle(long position
, wxRichTextAttr
& style
)
3317 return DoGetStyle(position
, style
, true);
3320 bool wxRichTextParagraphLayoutBox::GetUncombinedStyle(long position
, wxRichTextAttr
& style
)
3322 return DoGetStyle(position
, style
, false);
3325 /// Implementation helper for GetStyle. If combineStyles is true, combine base, paragraph and
3326 /// context attributes.
3327 bool wxRichTextParagraphLayoutBox::DoGetStyle(long position
, wxRichTextAttr
& style
, bool combineStyles
)
3329 wxRichTextObject
* obj
wxDUMMY_INITIALIZE(NULL
);
3331 if (style
.IsParagraphStyle())
3333 obj
= GetParagraphAtPosition(position
);
3338 // Start with the base style
3339 style
= GetAttributes();
3341 // Apply the paragraph style
3342 wxRichTextApplyStyle(style
, obj
->GetAttributes());
3345 style
= obj
->GetAttributes();
3352 obj
= GetLeafObjectAtPosition(position
);
3357 wxRichTextParagraph
* para
= wxDynamicCast(obj
->GetParent(), wxRichTextParagraph
);
3358 style
= para
? para
->GetCombinedAttributes(obj
->GetAttributes()) : obj
->GetAttributes();
3361 style
= obj
->GetAttributes();
3369 static bool wxHasStyle(long flags
, long style
)
3371 return (flags
& style
) != 0;
3374 /// Combines 'style' with 'currentStyle' for the purpose of summarising the attributes of a range of
3376 bool wxRichTextParagraphLayoutBox::CollectStyle(wxRichTextAttr
& currentStyle
, const wxRichTextAttr
& style
, wxRichTextAttr
& clashingAttr
, wxRichTextAttr
& absentAttr
)
3378 currentStyle
.CollectCommonAttributes(style
, clashingAttr
, absentAttr
);
3383 /// Get the combined style for a range - if any attribute is different within the range,
3384 /// that attribute is not present within the flags.
3385 /// *** Note that this is not recursive, and so assumes that content inside a paragraph is not itself
3387 bool wxRichTextParagraphLayoutBox::GetStyleForRange(const wxRichTextRange
& range
, wxRichTextAttr
& style
)
3389 style
= wxRichTextAttr();
3391 wxRichTextAttr clashingAttr
;
3392 wxRichTextAttr absentAttrPara
, absentAttrChar
;
3394 wxRichTextObjectList::compatibility_iterator node
= GetChildren().GetFirst();
3397 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3398 if (para
&& !(para
->GetRange().GetStart() > range
.GetEnd() || para
->GetRange().GetEnd() < range
.GetStart()))
3400 if (para
->GetChildren().GetCount() == 0)
3402 wxRichTextAttr paraStyle
= para
->GetCombinedAttributes(true /* use box attributes */);
3404 CollectStyle(style
, paraStyle
, clashingAttr
, absentAttrPara
);
3408 wxRichTextRange
paraRange(para
->GetRange());
3409 paraRange
.LimitTo(range
);
3411 // First collect paragraph attributes only
3412 wxRichTextAttr paraStyle
= para
->GetCombinedAttributes();
3413 paraStyle
.SetFlags(paraStyle
.GetFlags() & wxTEXT_ATTR_PARAGRAPH
);
3414 CollectStyle(style
, paraStyle
, clashingAttr
, absentAttrPara
);
3416 wxRichTextObjectList::compatibility_iterator childNode
= para
->GetChildren().GetFirst();
3420 wxRichTextObject
* child
= childNode
->GetData();
3421 if (!(child
->GetRange().GetStart() > range
.GetEnd() || child
->GetRange().GetEnd() < range
.GetStart()))
3423 wxRichTextAttr childStyle
= para
->GetCombinedAttributes(child
->GetAttributes(), true /* include box attributes */);
3425 // Now collect character attributes only
3426 childStyle
.SetFlags(childStyle
.GetFlags() & wxTEXT_ATTR_CHARACTER
);
3428 CollectStyle(style
, childStyle
, clashingAttr
, absentAttrChar
);
3431 childNode
= childNode
->GetNext();
3435 node
= node
->GetNext();
3440 /// Set default style
3441 bool wxRichTextParagraphLayoutBox::SetDefaultStyle(const wxRichTextAttr
& style
)
3443 m_defaultAttributes
= style
;
3447 /// Test if this whole range has character attributes of the specified kind. If any
3448 /// of the attributes are different within the range, the test fails. You
3449 /// can use this to implement, for example, bold button updating. style must have
3450 /// flags indicating which attributes are of interest.
3451 bool wxRichTextParagraphLayoutBox::HasCharacterAttributes(const wxRichTextRange
& range
, const wxRichTextAttr
& style
) const
3454 int matchingCount
= 0;
3456 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3459 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3460 // wxASSERT (para != NULL);
3464 // Stop searching if we're beyond the range of interest
3465 if (para
->GetRange().GetStart() > range
.GetEnd())
3466 return foundCount
== matchingCount
&& foundCount
!= 0;
3468 if (!para
->GetRange().IsOutside(range
))
3470 wxRichTextObjectList::compatibility_iterator node2
= para
->GetChildren().GetFirst();
3474 wxRichTextObject
* child
= node2
->GetData();
3475 // Allow for empty string if no buffer
3476 wxRichTextRange childRange
= child
->GetRange();
3477 if (childRange
.GetLength() == 0 && GetRange().GetLength() == 1)
3478 childRange
.SetEnd(childRange
.GetEnd()+1);
3480 if (!childRange
.IsOutside(range
) && child
->IsKindOf(CLASSINFO(wxRichTextPlainText
)))
3483 wxRichTextAttr textAttr
= para
->GetCombinedAttributes(child
->GetAttributes());
3485 if (wxTextAttrEqPartial(textAttr
, style
))
3489 node2
= node2
->GetNext();
3494 node
= node
->GetNext();
3497 return foundCount
== matchingCount
&& foundCount
!= 0;
3500 /// Test if this whole range has paragraph attributes of the specified kind. If any
3501 /// of the attributes are different within the range, the test fails. You
3502 /// can use this to implement, for example, centering button updating. style must have
3503 /// flags indicating which attributes are of interest.
3504 bool wxRichTextParagraphLayoutBox::HasParagraphAttributes(const wxRichTextRange
& range
, const wxRichTextAttr
& style
) const
3507 int matchingCount
= 0;
3509 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3512 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3513 // wxASSERT (para != NULL);
3517 // Stop searching if we're beyond the range of interest
3518 if (para
->GetRange().GetStart() > range
.GetEnd())
3519 return foundCount
== matchingCount
&& foundCount
!= 0;
3521 if (!para
->GetRange().IsOutside(range
))
3523 wxRichTextAttr textAttr
= GetAttributes();
3524 // Apply the paragraph style
3525 wxRichTextApplyStyle(textAttr
, para
->GetAttributes());
3528 if (wxTextAttrEqPartial(textAttr
, style
))
3533 node
= node
->GetNext();
3535 return foundCount
== matchingCount
&& foundCount
!= 0;
3538 void wxRichTextParagraphLayoutBox::PrepareContent(wxRichTextParagraphLayoutBox
& container
)
3540 wxRichTextBuffer
* buffer
= GetBuffer();
3541 if (buffer
&& buffer
->GetRichTextCtrl())
3542 buffer
->GetRichTextCtrl()->PrepareContent(container
);
3545 /// Set character or paragraph properties
3546 bool wxRichTextParagraphLayoutBox::SetProperties(const wxRichTextRange
& range
, const wxRichTextProperties
& properties
, int flags
)
3548 wxRichTextBuffer
* buffer
= GetBuffer();
3550 bool withUndo
= ((flags
& wxRICHTEXT_SETPROPERTIES_WITH_UNDO
) != 0);
3551 bool parasOnly
= ((flags
& wxRICHTEXT_SETPROPERTIES_PARAGRAPHS_ONLY
) != 0);
3552 bool charactersOnly
= ((flags
& wxRICHTEXT_SETPROPERTIES_CHARACTERS_ONLY
) != 0);
3553 bool resetExistingProperties
= ((flags
& wxRICHTEXT_SETPROPERTIES_RESET
) != 0);
3554 bool removeProperties
= ((flags
& wxRICHTEXT_SETPROPERTIES_REMOVE
) != 0);
3556 // If we are associated with a control, make undoable; otherwise, apply immediately
3559 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3561 wxRichTextAction
* action
= NULL
;
3563 if (haveControl
&& withUndo
)
3565 action
= new wxRichTextAction(NULL
, _("Change Properties"), wxRICHTEXT_CHANGE_PROPERTIES
, buffer
, this, buffer
->GetRichTextCtrl());
3566 action
->SetRange(range
);
3567 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3570 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3573 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3574 // wxASSERT (para != NULL);
3576 if (para
&& para
->GetChildCount() > 0)
3578 // Stop searching if we're beyond the range of interest
3579 if (para
->GetRange().GetStart() > range
.GetEnd())
3582 if (!para
->GetRange().IsOutside(range
))
3584 // We'll be using a copy of the paragraph to make style changes,
3585 // not updating the buffer directly.
3586 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3588 if (haveControl
&& withUndo
)
3590 newPara
= new wxRichTextParagraph(*para
);
3591 action
->GetNewParagraphs().AppendChild(newPara
);
3593 // Also store the old ones for Undo
3594 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3601 if (removeProperties
)
3603 // Removes the given style from the paragraph
3605 newPara
->GetProperties().RemoveProperties(properties
);
3607 else if (resetExistingProperties
)
3608 newPara
->GetProperties() = properties
;
3610 newPara
->GetProperties().MergeProperties(properties
);
3613 // When applying paragraph styles dynamically, don't change the text objects' attributes
3614 // since they will computed as needed. Only apply the character styling if it's _only_
3615 // character styling. This policy is subject to change and might be put under user control.
3617 // Hm. we might well be applying a mix of paragraph and character styles, in which
3618 // case we _do_ want to apply character styles regardless of what para styles are set.
3619 // But if we're applying a paragraph style, which has some character attributes, but
3620 // we only want the paragraphs to hold this character style, then we _don't_ want to
3621 // apply the character style. So we need to be able to choose.
3623 if (!parasOnly
&& charactersOnly
&& range
.GetStart() != newPara
->GetRange().GetEnd())
3625 wxRichTextRange
childRange(range
);
3626 childRange
.LimitTo(newPara
->GetRange());
3628 // Find the starting position and if necessary split it so
3629 // we can start applying different properties.
3630 // TODO: check that the properties actually change or are different
3631 // from properties outside of range
3632 wxRichTextObject
* firstObject
wxDUMMY_INITIALIZE(NULL
);
3633 wxRichTextObject
* lastObject
wxDUMMY_INITIALIZE(NULL
);
3635 if (childRange
.GetStart() == newPara
->GetRange().GetStart())
3636 firstObject
= newPara
->GetChildren().GetFirst()->GetData();
3638 firstObject
= newPara
->SplitAt(range
.GetStart());
3640 // Increment by 1 because we're apply the style one _after_ the split point
3641 long splitPoint
= childRange
.GetEnd();
3642 if (splitPoint
!= newPara
->GetRange().GetEnd())
3646 if (splitPoint
== newPara
->GetRange().GetEnd())
3647 lastObject
= newPara
->GetChildren().GetLast()->GetData();
3649 // lastObject is set as a side-effect of splitting. It's
3650 // returned as the object before the new object.
3651 (void) newPara
->SplitAt(splitPoint
, & lastObject
);
3653 wxASSERT(firstObject
!= NULL
);
3654 wxASSERT(lastObject
!= NULL
);
3656 if (!firstObject
|| !lastObject
)
3659 wxRichTextObjectList::compatibility_iterator firstNode
= newPara
->GetChildren().Find(firstObject
);
3660 wxRichTextObjectList::compatibility_iterator lastNode
= newPara
->GetChildren().Find(lastObject
);
3662 wxASSERT(firstNode
);
3665 wxRichTextObjectList::compatibility_iterator node2
= firstNode
;
3669 wxRichTextObject
* child
= node2
->GetData();
3671 if (removeProperties
)
3673 // Removes the given properties from the paragraph
3674 child
->GetProperties().RemoveProperties(properties
);
3676 else if (resetExistingProperties
)
3677 child
->GetProperties() = properties
;
3680 child
->GetProperties().MergeProperties(properties
);
3683 if (node2
== lastNode
)
3686 node2
= node2
->GetNext();
3692 node
= node
->GetNext();
3695 // Do action, or delay it until end of batch.
3696 if (haveControl
&& withUndo
)
3697 buffer
->SubmitAction(action
);
3702 void wxRichTextParagraphLayoutBox::Reset()
3706 wxRichTextBuffer
* buffer
= GetBuffer();
3707 if (buffer
&& buffer
->GetRichTextCtrl())
3709 wxRichTextEvent
event(wxEVT_COMMAND_RICHTEXT_BUFFER_RESET
, buffer
->GetRichTextCtrl()->GetId());
3710 event
.SetEventObject(buffer
->GetRichTextCtrl());
3711 event
.SetContainer(this);
3713 buffer
->SendEvent(event
, true);
3716 AddParagraph(wxEmptyString
);
3718 PrepareContent(*this);
3720 InvalidateHierarchy(wxRICHTEXT_ALL
);
3723 /// Invalidate the buffer. With no argument, invalidates whole buffer.
3724 void wxRichTextParagraphLayoutBox::Invalidate(const wxRichTextRange
& invalidRange
)
3726 wxRichTextCompositeObject::Invalidate(invalidRange
);
3728 DoInvalidate(invalidRange
);
3731 // Do the (in)validation for this object only
3732 void wxRichTextParagraphLayoutBox::DoInvalidate(const wxRichTextRange
& invalidRange
)
3734 if (invalidRange
== wxRICHTEXT_ALL
)
3736 m_invalidRange
= wxRICHTEXT_ALL
;
3738 // Already invalidating everything
3739 else if (m_invalidRange
== wxRICHTEXT_ALL
)
3744 if ((invalidRange
.GetStart() < m_invalidRange
.GetStart()) || m_invalidRange
.GetStart() == -1)
3745 m_invalidRange
.SetStart(invalidRange
.GetStart());
3746 if (invalidRange
.GetEnd() > m_invalidRange
.GetEnd())
3747 m_invalidRange
.SetEnd(invalidRange
.GetEnd());
3751 // Do the (in)validation both up and down the hierarchy
3752 void wxRichTextParagraphLayoutBox::InvalidateHierarchy(const wxRichTextRange
& invalidRange
)
3754 Invalidate(invalidRange
);
3756 if (invalidRange
!= wxRICHTEXT_NONE
)
3758 // Now go up the hierarchy
3759 wxRichTextObject
* thisObj
= this;
3760 wxRichTextObject
* p
= GetParent();
3763 wxRichTextParagraphLayoutBox
* l
= wxDynamicCast(p
, wxRichTextParagraphLayoutBox
);
3765 l
->DoInvalidate(thisObj
->GetRange());
3773 /// Get invalid range, rounding to entire paragraphs if argument is true.
3774 wxRichTextRange
wxRichTextParagraphLayoutBox::GetInvalidRange(bool wholeParagraphs
) const
3776 if (m_invalidRange
== wxRICHTEXT_ALL
|| m_invalidRange
== wxRICHTEXT_NONE
)
3777 return m_invalidRange
;
3779 wxRichTextRange range
= m_invalidRange
;
3781 if (wholeParagraphs
)
3783 wxRichTextParagraph
* para1
= GetParagraphAtPosition(range
.GetStart());
3785 range
.SetStart(para1
->GetRange().GetStart());
3786 // floating layout make all child should be relayout
3787 range
.SetEnd(GetOwnRange().GetEnd());
3792 /// Apply the style sheet to the buffer, for example if the styles have changed.
3793 bool wxRichTextParagraphLayoutBox::ApplyStyleSheet(wxRichTextStyleSheet
* styleSheet
)
3795 wxASSERT(styleSheet
!= NULL
);
3801 wxRichTextAttr
attr(GetBasicStyle());
3802 if (GetBasicStyle().HasParagraphStyleName())
3804 wxRichTextParagraphStyleDefinition
* paraDef
= styleSheet
->FindParagraphStyle(GetBasicStyle().GetParagraphStyleName());
3807 attr
.Apply(paraDef
->GetStyleMergedWithBase(styleSheet
));
3808 SetBasicStyle(attr
);
3813 if (GetBasicStyle().HasCharacterStyleName())
3815 wxRichTextCharacterStyleDefinition
* charDef
= styleSheet
->FindCharacterStyle(GetBasicStyle().GetCharacterStyleName());
3818 attr
.Apply(charDef
->GetStyleMergedWithBase(styleSheet
));
3819 SetBasicStyle(attr
);
3824 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3827 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3828 // wxASSERT (para != NULL);
3832 // Combine paragraph and list styles. If there is a list style in the original attributes,
3833 // the current indentation overrides anything else and is used to find the item indentation.
3834 // Also, for applying paragraph styles, consider having 2 modes: (1) we merge with what we have,
3835 // thereby taking into account all user changes, (2) reset the style completely (except for indentation/list
3836 // exception as above).
3837 // Problem: when changing from one list style to another, there's a danger that the level info will get lost.
3838 // So when changing a list style interactively, could retrieve level based on current style, then
3839 // set appropriate indent and apply new style.
3843 if (para
->GetAttributes().HasOutlineLevel())
3844 outline
= para
->GetAttributes().GetOutlineLevel();
3845 if (para
->GetAttributes().HasBulletNumber())
3846 num
= para
->GetAttributes().GetBulletNumber();
3848 if (!para
->GetAttributes().GetParagraphStyleName().IsEmpty() && !para
->GetAttributes().GetListStyleName().IsEmpty())
3850 int currentIndent
= para
->GetAttributes().GetLeftIndent();
3852 wxRichTextParagraphStyleDefinition
* paraDef
= styleSheet
->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
3853 wxRichTextListStyleDefinition
* listDef
= styleSheet
->FindListStyle(para
->GetAttributes().GetListStyleName());
3854 if (paraDef
&& !listDef
)
3856 para
->GetAttributes() = paraDef
->GetStyleMergedWithBase(styleSheet
);
3859 else if (listDef
&& !paraDef
)
3861 // Set overall style defined for the list style definition
3862 para
->GetAttributes() = listDef
->GetStyleMergedWithBase(styleSheet
);
3864 // Apply the style for this level
3865 wxRichTextApplyStyle(para
->GetAttributes(), * listDef
->GetLevelAttributes(listDef
->FindLevelForIndent(currentIndent
)));
3868 else if (listDef
&& paraDef
)
3870 // Combines overall list style, style for level, and paragraph style
3871 para
->GetAttributes() = listDef
->CombineWithParagraphStyle(currentIndent
, paraDef
->GetStyleMergedWithBase(styleSheet
));
3875 else if (para
->GetAttributes().GetParagraphStyleName().IsEmpty() && !para
->GetAttributes().GetListStyleName().IsEmpty())
3877 int currentIndent
= para
->GetAttributes().GetLeftIndent();
3879 wxRichTextListStyleDefinition
* listDef
= styleSheet
->FindListStyle(para
->GetAttributes().GetListStyleName());
3881 // Overall list definition style
3882 para
->GetAttributes() = listDef
->GetStyleMergedWithBase(styleSheet
);
3884 // Style for this level
3885 wxRichTextApplyStyle(para
->GetAttributes(), * listDef
->GetLevelAttributes(listDef
->FindLevelForIndent(currentIndent
)));
3889 else if (!para
->GetAttributes().GetParagraphStyleName().IsEmpty() && para
->GetAttributes().GetListStyleName().IsEmpty())
3891 wxRichTextParagraphStyleDefinition
* def
= styleSheet
->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
3894 para
->GetAttributes() = def
->GetStyleMergedWithBase(styleSheet
);
3900 para
->GetAttributes().SetOutlineLevel(outline
);
3902 para
->GetAttributes().SetBulletNumber(num
);
3905 node
= node
->GetNext();
3907 return foundCount
!= 0;
3911 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
3913 wxRichTextBuffer
* buffer
= GetBuffer();
3914 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
3916 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
3917 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
3918 bool specifyLevel
= ((flags
& wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL
) != 0);
3919 bool renumber
= ((flags
& wxRICHTEXT_SETSTYLE_RENUMBER
) != 0);
3921 // Current number, if numbering
3924 wxASSERT (!specifyLevel
|| (specifyLevel
&& (specifiedLevel
>= 0)));
3926 // If we are associated with a control, make undoable; otherwise, apply immediately
3929 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
3931 wxRichTextAction
* action
= NULL
;
3933 if (haveControl
&& withUndo
)
3935 action
= new wxRichTextAction(NULL
, _("Change List Style"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
3936 action
->SetRange(range
);
3937 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
3940 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
3943 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
3944 // wxASSERT (para != NULL);
3946 if (para
&& para
->GetChildCount() > 0)
3948 // Stop searching if we're beyond the range of interest
3949 if (para
->GetRange().GetStart() > range
.GetEnd())
3952 if (!para
->GetRange().IsOutside(range
))
3954 // We'll be using a copy of the paragraph to make style changes,
3955 // not updating the buffer directly.
3956 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
3958 if (haveControl
&& withUndo
)
3960 newPara
= new wxRichTextParagraph(*para
);
3961 action
->GetNewParagraphs().AppendChild(newPara
);
3963 // Also store the old ones for Undo
3964 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
3971 int thisIndent
= newPara
->GetAttributes().GetLeftIndent();
3972 int thisLevel
= specifyLevel
? specifiedLevel
: def
->FindLevelForIndent(thisIndent
);
3974 // How is numbering going to work?
3975 // If we are renumbering, or numbering for the first time, we need to keep
3976 // track of the number for each level. But we might be simply applying a different
3978 // In Word, applying a style to several paragraphs, even if at different levels,
3979 // reverts the level back to the same one. So we could do the same here.
3980 // Renumbering will need to be done when we promote/demote a paragraph.
3982 // Apply the overall list style, and item style for this level
3983 wxRichTextAttr
listStyle(def
->GetCombinedStyleForLevel(thisLevel
, styleSheet
));
3984 wxRichTextApplyStyle(newPara
->GetAttributes(), listStyle
);
3986 // Now we need to do numbering
3989 newPara
->GetAttributes().SetBulletNumber(n
);
3994 else if (!newPara
->GetAttributes().GetListStyleName().IsEmpty())
3996 // if def is NULL, remove list style, applying any associated paragraph style
3997 // to restore the attributes
3999 newPara
->GetAttributes().SetListStyleName(wxEmptyString
);
4000 newPara
->GetAttributes().SetLeftIndent(0, 0);
4001 newPara
->GetAttributes().SetBulletText(wxEmptyString
);
4003 // Eliminate the main list-related attributes
4004 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
);
4006 if (styleSheet
&& !newPara
->GetAttributes().GetParagraphStyleName().IsEmpty())
4008 wxRichTextParagraphStyleDefinition
* def
= styleSheet
->FindParagraphStyle(newPara
->GetAttributes().GetParagraphStyleName());
4011 newPara
->GetAttributes() = def
->GetStyleMergedWithBase(styleSheet
);
4018 node
= node
->GetNext();
4021 // Do action, or delay it until end of batch.
4022 if (haveControl
&& withUndo
)
4023 buffer
->SubmitAction(action
);
4028 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
)
4030 wxRichTextBuffer
* buffer
= GetBuffer();
4031 if (buffer
&& buffer
->GetStyleSheet())
4033 wxRichTextListStyleDefinition
* def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4035 return SetListStyle(range
, def
, flags
, startFrom
, specifiedLevel
);
4040 /// Clear list for given range
4041 bool wxRichTextParagraphLayoutBox::ClearListStyle(const wxRichTextRange
& range
, int flags
)
4043 return SetListStyle(range
, NULL
, flags
);
4046 /// Number/renumber any list elements in the given range
4047 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
4049 return DoNumberList(range
, range
, 0, def
, flags
, startFrom
, specifiedLevel
);
4052 /// Number/renumber any list elements in the given range. Also do promotion or demotion of items, if specified
4053 bool wxRichTextParagraphLayoutBox::DoNumberList(const wxRichTextRange
& range
, const wxRichTextRange
& promotionRange
, int promoteBy
,
4054 wxRichTextListStyleDefinition
* def
, int flags
, int startFrom
, int specifiedLevel
)
4056 wxRichTextBuffer
* buffer
= GetBuffer();
4057 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
4059 bool withUndo
= ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
4060 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
4062 bool specifyLevel
= ((flags
& wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL
) != 0);
4065 bool renumber
= ((flags
& wxRICHTEXT_SETSTYLE_RENUMBER
) != 0);
4067 // Max number of levels
4068 const int maxLevels
= 10;
4070 // The level we're looking at now
4071 int currentLevel
= -1;
4073 // The item number for each level
4074 int levels
[maxLevels
];
4077 // Reset all numbering
4078 for (i
= 0; i
< maxLevels
; i
++)
4080 if (startFrom
!= -1)
4081 levels
[i
] = startFrom
-1;
4082 else if (renumber
) // start again
4085 levels
[i
] = -1; // start from the number we found, if any
4089 wxASSERT(!specifyLevel
|| (specifyLevel
&& (specifiedLevel
>= 0)));
4092 // If we are associated with a control, make undoable; otherwise, apply immediately
4095 bool haveControl
= (buffer
->GetRichTextCtrl() != NULL
);
4097 wxRichTextAction
* action
= NULL
;
4099 if (haveControl
&& withUndo
)
4101 action
= new wxRichTextAction(NULL
, _("Renumber List"), wxRICHTEXT_CHANGE_STYLE
, buffer
, this, buffer
->GetRichTextCtrl());
4102 action
->SetRange(range
);
4103 action
->SetPosition(buffer
->GetRichTextCtrl()->GetCaretPosition());
4106 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
4109 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
4110 // wxASSERT (para != NULL);
4112 if (para
&& para
->GetChildCount() > 0)
4114 // Stop searching if we're beyond the range of interest
4115 if (para
->GetRange().GetStart() > range
.GetEnd())
4118 if (!para
->GetRange().IsOutside(range
))
4120 // We'll be using a copy of the paragraph to make style changes,
4121 // not updating the buffer directly.
4122 wxRichTextParagraph
* newPara
wxDUMMY_INITIALIZE(NULL
);
4124 if (haveControl
&& withUndo
)
4126 newPara
= new wxRichTextParagraph(*para
);
4127 action
->GetNewParagraphs().AppendChild(newPara
);
4129 // Also store the old ones for Undo
4130 action
->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para
));
4135 wxRichTextListStyleDefinition
* defToUse
= def
;
4138 if (styleSheet
&& !newPara
->GetAttributes().GetListStyleName().IsEmpty())
4139 defToUse
= styleSheet
->FindListStyle(newPara
->GetAttributes().GetListStyleName());
4144 int thisIndent
= newPara
->GetAttributes().GetLeftIndent();
4145 int thisLevel
= defToUse
->FindLevelForIndent(thisIndent
);
4147 // If we've specified a level to apply to all, change the level.
4148 if (specifiedLevel
!= -1)
4149 thisLevel
= specifiedLevel
;
4151 // Do promotion if specified
4152 if ((promoteBy
!= 0) && !para
->GetRange().IsOutside(promotionRange
))
4154 thisLevel
= thisLevel
- promoteBy
;
4161 // Apply the overall list style, and item style for this level
4162 wxRichTextAttr
listStyle(defToUse
->GetCombinedStyleForLevel(thisLevel
, styleSheet
));
4163 wxRichTextApplyStyle(newPara
->GetAttributes(), listStyle
);
4165 // OK, we've (re)applied the style, now let's get the numbering right.
4167 if (currentLevel
== -1)
4168 currentLevel
= thisLevel
;
4170 // Same level as before, do nothing except increment level's number afterwards
4171 if (currentLevel
== thisLevel
)
4174 // A deeper level: start renumbering all levels after current level
4175 else if (thisLevel
> currentLevel
)
4177 for (i
= currentLevel
+1; i
<= thisLevel
; i
++)
4181 currentLevel
= thisLevel
;
4183 else if (thisLevel
< currentLevel
)
4185 currentLevel
= thisLevel
;
4188 // Use the current numbering if -1 and we have a bullet number already
4189 if (levels
[currentLevel
] == -1)
4191 if (newPara
->GetAttributes().HasBulletNumber())
4192 levels
[currentLevel
] = newPara
->GetAttributes().GetBulletNumber();
4194 levels
[currentLevel
] = 1;
4198 levels
[currentLevel
] ++;
4201 newPara
->GetAttributes().SetBulletNumber(levels
[currentLevel
]);
4203 // Create the bullet text if an outline list
4204 if (listStyle
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
)
4207 for (i
= 0; i
<= currentLevel
; i
++)
4209 if (!text
.IsEmpty())
4211 text
+= wxString::Format(wxT("%d"), levels
[i
]);
4213 newPara
->GetAttributes().SetBulletText(text
);
4219 node
= node
->GetNext();
4222 // Do action, or delay it until end of batch.
4223 if (haveControl
&& withUndo
)
4224 buffer
->SubmitAction(action
);
4229 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int startFrom
, int specifiedLevel
)
4231 wxRichTextBuffer
* buffer
= GetBuffer();
4232 if (buffer
->GetStyleSheet())
4234 wxRichTextListStyleDefinition
* def
= NULL
;
4235 if (!defName
.IsEmpty())
4236 def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4237 return NumberList(range
, def
, flags
, startFrom
, specifiedLevel
);
4242 /// Promote the list items within the given range. promoteBy can be a positive or negative number, e.g. 1 or -1
4243 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy
, const wxRichTextRange
& range
, wxRichTextListStyleDefinition
* def
, int flags
, int specifiedLevel
)
4246 // One strategy is to first work out the range within which renumbering must occur. Then could pass these two ranges
4247 // to NumberList with a flag indicating promotion is required within one of the ranges.
4248 // Find first and last paragraphs in range. Then for first, calculate new indentation and look back until we find
4249 // a paragraph that either has no list style, or has one that is different or whose indentation is less.
4250 // We start renumbering from the para after that different para we found. We specify that the numbering of that
4251 // list position will start from 1.
4252 // Similarly, we look after the last para in the promote range for an indentation that is less (or no list style).
4253 // We can end the renumbering at this point.
4255 // For now, only renumber within the promotion range.
4257 return DoNumberList(range
, range
, promoteBy
, def
, flags
, 1, specifiedLevel
);
4260 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy
, const wxRichTextRange
& range
, const wxString
& defName
, int flags
, int specifiedLevel
)
4262 wxRichTextBuffer
* buffer
= GetBuffer();
4263 if (buffer
->GetStyleSheet())
4265 wxRichTextListStyleDefinition
* def
= NULL
;
4266 if (!defName
.IsEmpty())
4267 def
= buffer
->GetStyleSheet()->FindListStyle(defName
);
4268 return PromoteList(promoteBy
, range
, def
, flags
, specifiedLevel
);
4273 /// Fills in the attributes for numbering a paragraph after previousParagraph. It also finds the
4274 /// position of the paragraph that it had to start looking from.
4275 bool wxRichTextParagraphLayoutBox::FindNextParagraphNumber(wxRichTextParagraph
* previousParagraph
, wxRichTextAttr
& attr
) const
4277 if (!previousParagraph
->GetAttributes().HasFlag(wxTEXT_ATTR_BULLET_STYLE
) || previousParagraph
->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
)
4280 wxRichTextBuffer
* buffer
= GetBuffer();
4281 wxRichTextStyleSheet
* styleSheet
= buffer
->GetStyleSheet();
4282 if (styleSheet
&& !previousParagraph
->GetAttributes().GetListStyleName().IsEmpty())
4284 wxRichTextListStyleDefinition
* def
= styleSheet
->FindListStyle(previousParagraph
->GetAttributes().GetListStyleName());
4287 // int thisIndent = previousParagraph->GetAttributes().GetLeftIndent();
4288 // int thisLevel = def->FindLevelForIndent(thisIndent);
4290 bool isOutline
= (previousParagraph
->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
) != 0;
4292 attr
.SetFlags(previousParagraph
->GetAttributes().GetFlags() & (wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_BULLET_NUMBER
|wxTEXT_ATTR_BULLET_TEXT
|wxTEXT_ATTR_BULLET_NAME
));
4293 if (previousParagraph
->GetAttributes().HasBulletName())
4294 attr
.SetBulletName(previousParagraph
->GetAttributes().GetBulletName());
4295 attr
.SetBulletStyle(previousParagraph
->GetAttributes().GetBulletStyle());
4296 attr
.SetListStyleName(previousParagraph
->GetAttributes().GetListStyleName());
4298 int nextNumber
= previousParagraph
->GetAttributes().GetBulletNumber() + 1;
4299 attr
.SetBulletNumber(nextNumber
);
4303 wxString text
= previousParagraph
->GetAttributes().GetBulletText();
4304 if (!text
.IsEmpty())
4306 int pos
= text
.Find(wxT('.'), true);
4307 if (pos
!= wxNOT_FOUND
)
4309 text
= text
.Mid(0, text
.Length() - pos
- 1);
4312 text
= wxEmptyString
;
4313 if (!text
.IsEmpty())
4315 text
+= wxString::Format(wxT("%d"), nextNumber
);
4316 attr
.SetBulletText(text
);
4330 * wxRichTextParagraph
4331 * This object represents a single paragraph (or in a straight text editor, a line).
4334 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraph
, wxRichTextCompositeObject
)
4336 wxArrayInt
wxRichTextParagraph::sm_defaultTabs
;
4338 wxRichTextParagraph::wxRichTextParagraph(wxRichTextObject
* parent
, wxRichTextAttr
* style
):
4339 wxRichTextCompositeObject(parent
)
4342 SetAttributes(*style
);
4345 wxRichTextParagraph::wxRichTextParagraph(const wxString
& text
, wxRichTextObject
* parent
, wxRichTextAttr
* paraStyle
, wxRichTextAttr
* charStyle
):
4346 wxRichTextCompositeObject(parent
)
4349 SetAttributes(*paraStyle
);
4351 AppendChild(new wxRichTextPlainText(text
, this, charStyle
));
4354 wxRichTextParagraph::~wxRichTextParagraph()
4360 bool wxRichTextParagraph::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int WXUNUSED(descent
), int style
)
4365 // Currently we don't merge these attributes with the parent, but we
4366 // should consider whether we should (e.g. if we set a border colour
4367 // for all paragraphs). But generally box attributes are likely to be
4368 // different for different objects.
4369 wxRect paraRect
= GetRect();
4370 wxRichTextAttr attr
= GetCombinedAttributes();
4371 context
.ApplyVirtualAttributes(attr
, this);
4373 DrawBoxAttributes(dc
, GetBuffer(), attr
, paraRect
);
4375 // Draw the bullet, if any
4376 if (attr
.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE
)
4378 if (attr
.GetLeftSubIndent() != 0)
4380 int spaceBeforePara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingBefore());
4381 int leftIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftIndent());
4383 wxRichTextAttr
bulletAttr(attr
);
4385 // Combine with the font of the first piece of content, if one is specified
4386 if (GetChildren().GetCount() > 0)
4388 wxRichTextObject
* firstObj
= (wxRichTextObject
*) GetChildren().GetFirst()->GetData();
4389 if (!firstObj
->IsFloatable() && firstObj
->GetAttributes().HasFont())
4391 wxRichTextApplyStyle(bulletAttr
, firstObj
->GetAttributes());
4395 // Get line height from first line, if any
4396 wxRichTextLine
* line
= m_cachedLines
.GetFirst() ? (wxRichTextLine
* ) m_cachedLines
.GetFirst()->GetData() : NULL
;
4399 int lineHeight
wxDUMMY_INITIALIZE(0);
4402 lineHeight
= line
->GetSize().y
;
4403 linePos
= line
->GetPosition() + GetPosition();
4408 if (bulletAttr
.HasFont() && GetBuffer())
4409 font
= GetBuffer()->GetFontTable().FindFont(bulletAttr
);
4411 font
= (*wxNORMAL_FONT
);
4413 wxCheckSetFont(dc
, font
);
4415 lineHeight
= dc
.GetCharHeight();
4416 linePos
= GetPosition();
4417 linePos
.y
+= spaceBeforePara
;
4420 wxRect
bulletRect(GetPosition().x
+ leftIndent
, linePos
.y
, linePos
.x
- (GetPosition().x
+ leftIndent
), lineHeight
);
4422 if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
)
4424 if (wxRichTextBuffer::GetRenderer())
4425 wxRichTextBuffer::GetRenderer()->DrawBitmapBullet(this, dc
, bulletAttr
, bulletRect
);
4427 else if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_STANDARD
)
4429 if (wxRichTextBuffer::GetRenderer())
4430 wxRichTextBuffer::GetRenderer()->DrawStandardBullet(this, dc
, bulletAttr
, bulletRect
);
4434 wxString bulletText
= GetBulletText();
4436 if (!bulletText
.empty() && wxRichTextBuffer::GetRenderer())
4437 wxRichTextBuffer::GetRenderer()->DrawTextBullet(this, dc
, bulletAttr
, bulletRect
, bulletText
);
4442 // Draw the range for each line, one object at a time.
4444 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
4447 wxRichTextLine
* line
= node
->GetData();
4448 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
4450 // Lines are specified relative to the paragraph
4452 wxPoint linePosition
= line
->GetPosition() + GetPosition();
4454 // Don't draw if off the screen
4455 if (((style
& wxRICHTEXT_DRAW_IGNORE_CACHE
) != 0) || ((linePosition
.y
+ line
->GetSize().y
) >= rect
.y
&& linePosition
.y
<= rect
.y
+ rect
.height
))
4457 wxPoint objectPosition
= linePosition
;
4458 int maxDescent
= line
->GetDescent();
4460 // Loop through objects until we get to the one within range
4461 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
4466 wxRichTextObject
* child
= node2
->GetData();
4468 if (!child
->IsFloating() && child
->GetRange().GetLength() > 0 && !child
->GetRange().IsOutside(lineRange
) && !lineRange
.IsOutside(range
))
4470 // Draw this part of the line at the correct position
4471 wxRichTextRange
objectRange(child
->GetRange());
4472 objectRange
.LimitTo(lineRange
);
4475 if (child
->IsTopLevel())
4477 objectSize
= child
->GetCachedSize();
4478 objectRange
= child
->GetOwnRange();
4482 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING && wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4483 if (i
< (int) line
->GetObjectSizes().GetCount())
4485 objectSize
.x
= line
->GetObjectSizes()[(size_t) i
];
4491 child
->GetRangeSize(objectRange
, objectSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, objectPosition
);
4495 // Use the child object's width, but the whole line's height
4496 wxRect
childRect(objectPosition
, wxSize(objectSize
.x
, line
->GetSize().y
));
4497 child
->Draw(dc
, context
, objectRange
, selection
, childRect
, maxDescent
, style
);
4499 objectPosition
.x
+= objectSize
.x
;
4502 else if (child
->GetRange().GetStart() > lineRange
.GetEnd())
4503 // Can break out of inner loop now since we've passed this line's range
4506 node2
= node2
->GetNext();
4510 node
= node
->GetNext();
4516 // Get the range width using partial extents calculated for the whole paragraph.
4517 static int wxRichTextGetRangeWidth(const wxRichTextParagraph
& para
, const wxRichTextRange
& range
, const wxArrayInt
& partialExtents
)
4519 wxASSERT(partialExtents
.GetCount() >= (size_t) range
.GetLength());
4521 if (partialExtents
.GetCount() < (size_t) range
.GetLength())
4524 int leftMostPos
= 0;
4525 if (range
.GetStart() - para
.GetRange().GetStart() > 0)
4526 leftMostPos
= partialExtents
[range
.GetStart() - para
.GetRange().GetStart() - 1];
4528 int rightMostPos
= partialExtents
[range
.GetEnd() - para
.GetRange().GetStart()];
4530 int w
= rightMostPos
- leftMostPos
;
4535 /// Lay the item out
4536 bool wxRichTextParagraph::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
)
4538 // Deal with floating objects firstly before the normal layout
4539 wxRichTextBuffer
* buffer
= GetBuffer();
4541 wxRichTextFloatCollector
* collector
= GetContainer()->GetFloatCollector();
4542 wxASSERT(collector
);
4543 LayoutFloat(dc
, context
, rect
, style
, collector
);
4545 wxRichTextAttr attr
= GetCombinedAttributes();
4546 context
.ApplyVirtualAttributes(attr
, this);
4550 // Increase the size of the paragraph due to spacing
4551 int spaceBeforePara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingBefore());
4552 int spaceAfterPara
= ConvertTenthsMMToPixels(dc
, attr
.GetParagraphSpacingAfter());
4553 int leftIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftIndent());
4554 int leftSubIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetLeftSubIndent());
4555 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
4557 int lineSpacing
= 0;
4559 // Let's assume line spacing of 10 is normal, 15 is 1.5, 20 is 2, etc.
4560 if (attr
.HasLineSpacing() && attr
.GetLineSpacing() > 0 && attr
.GetFont().IsOk())
4562 wxCheckSetFont(dc
, attr
.GetFont());
4563 lineSpacing
= (int) (double(dc
.GetCharHeight()) * (double(attr
.GetLineSpacing())/10.0 - 1.0));
4566 // Start position for each line relative to the paragraph
4567 int startPositionFirstLine
= leftIndent
;
4568 int startPositionSubsequentLines
= leftIndent
+ leftSubIndent
;
4570 // If we have a bullet in this paragraph, the start position for the first line's text
4571 // is actually leftIndent + leftSubIndent.
4572 if (attr
.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE
)
4573 startPositionFirstLine
= startPositionSubsequentLines
;
4575 long lastEndPos
= GetRange().GetStart()-1;
4576 long lastCompletedEndPos
= lastEndPos
;
4578 int currentWidth
= 0;
4579 SetPosition(rect
.GetPosition());
4581 wxPoint
currentPosition(0, spaceBeforePara
); // We will calculate lines relative to paragraph
4584 int maxHeight
= currentPosition
.y
;
4589 int lineDescent
= 0;
4591 wxRichTextObjectList::compatibility_iterator node
;
4593 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4595 wxArrayInt partialExtents
;
4598 int paraDescent
= 0;
4600 // This calculates the partial text extents
4601 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, rect
.GetPosition(), & partialExtents
);
4603 node
= m_children
.GetFirst();
4606 wxRichTextObject
* child
= node
->GetData();
4608 //child->SetCachedSize(wxDefaultSize);
4609 child
->Layout(dc
, context
, rect
, style
);
4611 node
= node
->GetNext();
4617 // We may need to go back to a previous child, in which case create the new line,
4618 // find the child corresponding to the start position of the string, and
4621 wxRect availableRect
;
4623 node
= m_children
.GetFirst();
4626 wxRichTextObject
* child
= node
->GetData();
4628 // If floating, ignore. We already laid out floats.
4629 // Also ignore if empty object, except if we haven't got any
4631 if (child
->IsFloating() || !child
->IsShown() ||
4632 (child
->GetRange().GetLength() == 0 && maxHeight
> spaceBeforePara
)
4635 node
= node
->GetNext();
4639 // If this is e.g. a composite text box, it will need to be laid out itself.
4640 // But if just a text fragment or image, for example, this will
4641 // do nothing. NB: won't we need to set the position after layout?
4642 // since for example if position is dependent on vertical line size, we
4643 // can't tell the position until the size is determined. So possibly introduce
4644 // another layout phase.
4646 // We may only be looking at part of a child, if we searched back for wrapping
4647 // and found a suitable point some way into the child. So get the size for the fragment
4650 long nextBreakPos
= GetFirstLineBreakPosition(lastEndPos
+1);
4651 long lastPosToUse
= child
->GetRange().GetEnd();
4652 bool lineBreakInThisObject
= (nextBreakPos
> -1 && nextBreakPos
<= child
->GetRange().GetEnd());
4654 if (lineBreakInThisObject
)
4655 lastPosToUse
= nextBreakPos
;
4658 int childDescent
= 0;
4660 int startOffset
= (lineCount
== 0 ? startPositionFirstLine
: startPositionSubsequentLines
);
4661 availableRect
= wxRect(rect
.x
+ startOffset
, rect
.y
+ currentPosition
.y
,
4662 rect
.width
- startOffset
- rightIndent
, rect
.height
);
4664 if (child
->IsTopLevel())
4666 wxSize oldSize
= child
->GetCachedSize();
4668 child
->Invalidate(wxRICHTEXT_ALL
);
4669 child
->SetPosition(wxPoint(0, 0));
4671 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4672 // lays out the object again using the minimum size
4673 // The position will be determined by its location in its line,
4674 // and not by the child's actual position.
4675 child
->LayoutToBestSize(dc
, context
, buffer
,
4676 attr
, child
->GetAttributes(), availableRect
, parentRect
, style
);
4678 if (oldSize
!= child
->GetCachedSize())
4680 partialExtents
.Clear();
4682 // Recalculate the partial text extents since the child object changed size
4683 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), & partialExtents
);
4687 // Problem: we need to layout composites here for which we need the available width,
4688 // but we can't get the available width without using the float collector which
4689 // needs to know the object height.
4691 if ((nextBreakPos
== -1) && (lastEndPos
== child
->GetRange().GetStart() - 1)) // i.e. we want to get the whole thing
4693 childSize
= child
->GetCachedSize();
4694 childDescent
= child
->GetDescent();
4698 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4699 // Get height only, then the width using the partial extents
4700 GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
);
4701 childSize
.x
= wxRichTextGetRangeWidth(*this, wxRichTextRange(lastEndPos
+1, lastPosToUse
), partialExtents
);
4703 GetRangeSize(wxRichTextRange(lastEndPos
+1, lastPosToUse
), childSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, rect
.GetPosition());
4708 int loopIterations
= 0;
4710 // If there are nested objects that need to lay themselves out, we have to do this in a
4711 // loop because the height of the object may well depend on the available width.
4712 // And because of floating object positioning, the available width depends on the
4713 // height of the object and whether it will clash with the floating objects.
4714 // So, we see whether the available width changes due to the presence of floating images.
4715 // If it does, then we'll use the new restricted width to find the object height again.
4716 // If this causes another restriction in the available width, we'll try again, until
4717 // either we lose patience or the available width settles down.
4722 wxRect oldAvailableRect
= availableRect
;
4724 // Available width depends on the floating objects and the line height.
4725 // Note: the floating objects may be placed vertically along the two sides of
4726 // buffer, so we may have different available line widths with different
4727 // [startY, endY]. So, we can't determine how wide the available
4728 // space is until we know the exact line height.
4729 if (childDescent
== 0)
4731 lineHeight
= wxMax(lineHeight
, childSize
.y
);
4732 lineDescent
= maxDescent
;
4733 lineAscent
= maxAscent
;
4737 lineDescent
= wxMax(childDescent
, maxDescent
);
4738 lineAscent
= wxMax(childSize
.y
-childDescent
, maxAscent
);
4740 lineHeight
= wxMax(lineHeight
, (lineDescent
+ lineAscent
));
4741 wxRect floatAvailableRect
= collector
->GetAvailableRect(rect
.y
+ currentPosition
.y
, rect
.y
+ currentPosition
.y
+ lineHeight
);
4743 // Adjust availableRect to the space that is available when taking floating objects into account.
4745 if (floatAvailableRect
.x
+ startOffset
> availableRect
.x
)
4747 int newX
= floatAvailableRect
.x
+ startOffset
;
4748 int newW
= availableRect
.width
- (newX
- availableRect
.x
);
4749 availableRect
.x
= newX
;
4750 availableRect
.width
= newW
;
4753 if (floatAvailableRect
.width
< availableRect
.width
)
4754 availableRect
.width
= floatAvailableRect
.width
;
4756 currentPosition
.x
= availableRect
.x
- rect
.x
;
4758 if (child
->IsTopLevel() && loopIterations
<= 20)
4760 if (availableRect
!= oldAvailableRect
)
4762 wxSize oldSize
= child
->GetCachedSize();
4764 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4765 // lays out the object again using the minimum size
4766 child
->Invalidate(wxRICHTEXT_ALL
);
4767 child
->LayoutToBestSize(dc
, context
, buffer
,
4768 attr
, child
->GetAttributes(), availableRect
, parentRect
, style
);
4769 childSize
= child
->GetCachedSize();
4770 childDescent
= child
->GetDescent();
4772 if (oldSize
!= child
->GetCachedSize())
4774 partialExtents
.Clear();
4776 // Recalculate the partial text extents since the child object changed size
4777 GetRangeSize(GetRange(), paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_CACHE_SIZE
, wxPoint(0,0), & partialExtents
);
4780 // Go around the loop finding the available rect for the given floating objects
4790 if (child
->IsTopLevel())
4792 // We can move it to the correct position at this point
4793 child
->Move(GetPosition() + wxPoint(currentWidth
, currentPosition
.y
));
4797 // 1) There was a line break BEFORE the natural break
4798 // 2) There was a line break AFTER the natural break
4799 // 3) It's the last line
4800 // 4) The child still fits (carry on) - 'else' clause
4802 if ((lineBreakInThisObject
&& (childSize
.x
+ currentWidth
<= availableRect
.width
))
4804 (childSize
.x
+ currentWidth
> availableRect
.width
)
4806 ((childSize
.x
+ currentWidth
<= availableRect
.width
) && !node
->GetNext())
4810 long wrapPosition
= 0;
4811 if ((childSize
.x
+ currentWidth
<= availableRect
.width
) && !node
->GetNext() && !lineBreakInThisObject
)
4812 wrapPosition
= child
->GetRange().GetEnd();
4815 // Find a place to wrap. This may walk back to previous children,
4816 // for example if a word spans several objects.
4817 // Note: one object must contains only one wxTextAtrr, so the line height will not
4818 // change inside one object. Thus, we can pass the remain line width to the
4819 // FindWrapPosition function.
4820 if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos
+1, child
->GetRange().GetEnd()), dc
, context
, availableRect
.width
, wrapPosition
, & partialExtents
))
4822 // If the function failed, just cut it off at the end of this child.
4823 wrapPosition
= child
->GetRange().GetEnd();
4826 // FindWrapPosition can still return a value that will put us in an endless wrapping loop
4827 if (wrapPosition
<= lastCompletedEndPos
)
4828 wrapPosition
= wxMax(lastCompletedEndPos
+1,child
->GetRange().GetEnd());
4830 // Line end position shouldn't be the same as the end, or greater.
4831 if (wrapPosition
>= GetRange().GetEnd())
4832 wrapPosition
= GetRange().GetEnd()-1;
4834 // wxLogDebug(wxT("Split at %ld"), wrapPosition);
4836 // Let's find the actual size of the current line now
4838 wxRichTextRange
actualRange(lastCompletedEndPos
+1, wrapPosition
);
4842 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4843 if (!child
->IsEmpty())
4845 // Get height only, then the width using the partial extents
4846 GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
|wxRICHTEXT_HEIGHT_ONLY
);
4847 actualSize
.x
= wxRichTextGetRangeWidth(*this, actualRange
, partialExtents
);
4851 GetRangeSize(actualRange
, actualSize
, childDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
4853 currentWidth
= actualSize
.x
;
4855 // The descent for the whole line at this point, is the correct max descent
4856 maxDescent
= childDescent
;
4858 maxAscent
= actualSize
.y
-childDescent
;
4860 // lineHeight is given by the height for the whole line, since it will
4861 // take into account ascend/descend.
4862 lineHeight
= actualSize
.y
;
4864 if (lineHeight
== 0 && buffer
)
4866 wxFont
font(buffer
->GetFontTable().FindFont(attr
));
4867 wxCheckSetFont(dc
, font
);
4868 lineHeight
= dc
.GetCharHeight();
4871 if (maxDescent
== 0)
4874 dc
.GetTextExtent(wxT("X"), & w
, &h
, & maxDescent
);
4878 wxRichTextLine
* line
= AllocateLine(lineCount
);
4880 // Set relative range so we won't have to change line ranges when paragraphs are moved
4881 line
->SetRange(wxRichTextRange(actualRange
.GetStart() - GetRange().GetStart(), actualRange
.GetEnd() - GetRange().GetStart()));
4882 line
->SetPosition(currentPosition
);
4883 line
->SetSize(wxSize(currentWidth
, lineHeight
));
4884 line
->SetDescent(maxDescent
);
4886 maxHeight
= currentPosition
.y
+ lineHeight
;
4888 // Now move down a line. TODO: add margins, spacing
4889 currentPosition
.y
+= lineHeight
;
4890 currentPosition
.y
+= lineSpacing
;
4893 maxWidth
= wxMax(maxWidth
, currentWidth
+startOffset
);
4898 // TODO: account for zero-length objects
4899 // wxASSERT(wrapPosition > lastCompletedEndPos);
4901 lastEndPos
= wrapPosition
;
4902 lastCompletedEndPos
= lastEndPos
;
4906 if (wrapPosition
< GetRange().GetEnd()-1)
4908 // May need to set the node back to a previous one, due to searching back in wrapping
4909 wxRichTextObject
* childAfterWrapPosition
= FindObjectAtPosition(wrapPosition
+1);
4910 if (childAfterWrapPosition
)
4911 node
= m_children
.Find(childAfterWrapPosition
);
4913 node
= node
->GetNext();
4916 node
= node
->GetNext();
4918 // Apply paragraph styles such as alignment to the wrapped line
4919 ApplyParagraphStyle(line
, attr
, availableRect
, dc
);
4923 // We still fit, so don't add a line, and keep going
4924 currentWidth
+= childSize
.x
;
4926 if (childDescent
== 0)
4928 // An object with a zero descend value wants to take up the whole
4929 // height regardless of baseline
4930 lineHeight
= wxMax(lineHeight
, childSize
.y
);
4934 maxDescent
= wxMax(childDescent
, maxDescent
);
4935 maxAscent
= wxMax(childSize
.y
-childDescent
, maxAscent
);
4938 lineHeight
= wxMax(lineHeight
, (maxDescent
+ maxAscent
));
4940 maxWidth
= wxMax(maxWidth
, currentWidth
+startOffset
);
4941 lastEndPos
= child
->GetRange().GetEnd();
4943 node
= node
->GetNext();
4947 //wxASSERT(!(lastCompletedEndPos != -1 && lastCompletedEndPos < GetRange().GetEnd()-1));
4949 // Remove remaining unused line objects, if any
4950 ClearUnusedLines(lineCount
);
4952 // We need to add back the margins etc.
4954 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
4955 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxWidth
, currentPosition
.y
+ spaceAfterPara
));
4956 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
4957 SetCachedSize(marginRect
.GetSize());
4960 // The maximum size is the length of the paragraph stretched out into a line.
4961 // So if there were a single word, or an image, or a fixed-size text box, the object could be shrunk around
4962 // this size. TODO: take into account line breaks.
4964 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
4965 contentRect
= wxRect(wxPoint(0, 0), wxSize(paraSize
.x
+ wxMax(leftIndent
, leftIndent
+ leftSubIndent
) + rightIndent
, currentPosition
.y
+ spaceAfterPara
));
4966 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
4967 SetMaxSize(marginRect
.GetSize());
4970 // Find the greatest minimum size. Currently we only look at non-text objects,
4971 // which isn't ideal but it would be slow to find the maximum word width to
4972 // use as the minimum.
4975 node
= m_children
.GetFirst();
4978 wxRichTextObject
* child
= node
->GetData();
4980 // If floating, ignore. We already laid out floats.
4981 // Also ignore if empty object, except if we haven't got any
4983 if (!child
->IsFloating() && child
->GetRange().GetLength() != 0 && !child
->IsKindOf(CLASSINFO(wxRichTextPlainText
)))
4985 if (child
->GetCachedSize().x
> minWidth
)
4986 minWidth
= child
->GetMinSize().x
;
4988 node
= node
->GetNext();
4991 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
4992 contentRect
= wxRect(wxPoint(0, 0), wxSize(minWidth
, currentPosition
.y
+ spaceAfterPara
));
4993 GetBoxRects(dc
, buffer
, attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
4994 SetMinSize(marginRect
.GetSize());
4997 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4998 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
4999 // Use the text extents to calculate the size of each fragment in each line
5000 wxRichTextLineList::compatibility_iterator lineNode
= m_cachedLines
.GetFirst();
5003 wxRichTextLine
* line
= lineNode
->GetData();
5004 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5006 // Loop through objects until we get to the one within range
5007 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
5011 wxRichTextObject
* child
= node2
->GetData();
5013 if (child
->GetRange().GetLength() > 0 && !child
->GetRange().IsOutside(lineRange
))
5015 wxRichTextRange rangeToUse
= lineRange
;
5016 rangeToUse
.LimitTo(child
->GetRange());
5018 // Find the size of the child from the text extents, and store in an array
5019 // for drawing later
5021 if (rangeToUse
.GetStart() > GetRange().GetStart())
5022 left
= partialExtents
[(rangeToUse
.GetStart()-1) - GetRange().GetStart()];
5023 int right
= partialExtents
[rangeToUse
.GetEnd() - GetRange().GetStart()];
5024 int sz
= right
- left
;
5025 line
->GetObjectSizes().Add(sz
);
5027 else if (child
->GetRange().GetStart() > lineRange
.GetEnd())
5028 // Can break out of inner loop now since we've passed this line's range
5031 node2
= node2
->GetNext();
5034 lineNode
= lineNode
->GetNext();
5042 /// Apply paragraph styles, such as centering, to wrapped lines
5043 /// TODO: take into account box attributes, possibly
5044 void wxRichTextParagraph::ApplyParagraphStyle(wxRichTextLine
* line
, const wxRichTextAttr
& attr
, const wxRect
& rect
, wxDC
& dc
)
5046 if (!attr
.HasAlignment())
5049 wxPoint pos
= line
->GetPosition();
5050 wxSize size
= line
->GetSize();
5052 // centering, right-justification
5053 if (attr
.HasAlignment() && attr
.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE
)
5055 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
5056 pos
.x
= (rect
.GetWidth() - rightIndent
- size
.x
)/2 + pos
.x
;
5057 line
->SetPosition(pos
);
5059 else if (attr
.HasAlignment() && attr
.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT
)
5061 int rightIndent
= ConvertTenthsMMToPixels(dc
, attr
.GetRightIndent());
5062 pos
.x
= pos
.x
+ rect
.GetWidth() - size
.x
- rightIndent
;
5063 line
->SetPosition(pos
);
5067 /// Insert text at the given position
5068 bool wxRichTextParagraph::InsertText(long pos
, const wxString
& text
)
5070 wxRichTextObject
* childToUse
= NULL
;
5071 wxRichTextObjectList::compatibility_iterator nodeToUse
= wxRichTextObjectList::compatibility_iterator();
5073 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5076 wxRichTextObject
* child
= node
->GetData();
5077 if (child
->GetRange().Contains(pos
) && child
->GetRange().GetLength() > 0)
5084 node
= node
->GetNext();
5089 wxRichTextPlainText
* textObject
= wxDynamicCast(childToUse
, wxRichTextPlainText
);
5092 int posInString
= pos
- textObject
->GetRange().GetStart();
5094 wxString newText
= textObject
->GetText().Mid(0, posInString
) +
5095 text
+ textObject
->GetText().Mid(posInString
);
5096 textObject
->SetText(newText
);
5098 int textLength
= text
.length();
5100 textObject
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart(),
5101 textObject
->GetRange().GetEnd() + textLength
));
5103 // Increment the end range of subsequent fragments in this paragraph.
5104 // We'll set the paragraph range itself at a higher level.
5106 wxRichTextObjectList::compatibility_iterator node
= nodeToUse
->GetNext();
5109 wxRichTextObject
* child
= node
->GetData();
5110 child
->SetRange(wxRichTextRange(textObject
->GetRange().GetStart() + textLength
,
5111 textObject
->GetRange().GetEnd() + textLength
));
5113 node
= node
->GetNext();
5120 // TODO: if not a text object, insert at closest position, e.g. in front of it
5126 // Don't pass parent initially to suppress auto-setting of parent range.
5127 // We'll do that at a higher level.
5128 wxRichTextPlainText
* textObject
= new wxRichTextPlainText(text
, this);
5130 AppendChild(textObject
);
5137 void wxRichTextParagraph::Copy(const wxRichTextParagraph
& obj
)
5139 wxRichTextCompositeObject::Copy(obj
);
5142 /// Clear the cached lines
5143 void wxRichTextParagraph::ClearLines()
5145 WX_CLEAR_LIST(wxRichTextLineList
, m_cachedLines
);
5148 /// Get/set the object size for the given range. Returns false if the range
5149 /// is invalid for this object.
5150 bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
5152 if (!range
.IsWithin(GetRange()))
5155 if (flags
& wxRICHTEXT_UNFORMATTED
)
5157 // Just use unformatted data, assume no line breaks
5160 wxArrayInt childExtents
;
5169 int maxLineHeight
= 0;
5171 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5174 wxRichTextObject
* child
= node
->GetData();
5175 if (!child
->GetRange().IsOutside(range
))
5177 // Floating objects have a zero size within the paragraph.
5178 if (child
->IsFloating())
5183 if (partialExtents
->GetCount() > 0)
5184 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5188 partialExtents
->Add(0 /* zero size */ + lastSize
);
5195 wxRichTextRange rangeToUse
= range
;
5196 rangeToUse
.LimitTo(child
->GetRange());
5197 int childDescent
= 0;
5199 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we've already cached the size,
5200 // but it's only going to be used after caching has taken place.
5201 if ((flags
& wxRICHTEXT_HEIGHT_ONLY
) && child
->GetCachedSize().y
!= 0)
5203 childDescent
= child
->GetDescent();
5204 childSize
= child
->GetCachedSize();
5206 if (childDescent
== 0)
5208 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5212 maxDescent
= wxMax(maxDescent
, childDescent
);
5213 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5216 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5218 sz
.y
= wxMax(sz
.y
, maxLineHeight
);
5219 sz
.x
+= childSize
.x
;
5220 descent
= maxDescent
;
5222 else if (child
->IsTopLevel())
5224 childDescent
= child
->GetDescent();
5225 childSize
= child
->GetCachedSize();
5227 if (childDescent
== 0)
5229 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5233 maxDescent
= wxMax(maxDescent
, childDescent
);
5234 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5237 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5239 sz
.y
= wxMax(sz
.y
, maxLineHeight
);
5240 sz
.x
+= childSize
.x
;
5241 descent
= maxDescent
;
5243 // FIXME: this won't change the original values.
5244 // Should we be calling GetRangeSize above instead of using cached values?
5246 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange()))
5248 child
->SetCachedSize(childSize
);
5249 child
->SetDescent(childDescent
);
5256 if (partialExtents
->GetCount() > 0)
5257 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5261 partialExtents
->Add(childSize
.x
+ lastSize
);
5264 else if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, context
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
), p
))
5266 if (childDescent
== 0)
5268 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5272 maxDescent
= wxMax(maxDescent
, childDescent
);
5273 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5276 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5278 sz
.y
= wxMax(sz
.y
, maxLineHeight
);
5279 sz
.x
+= childSize
.x
;
5280 descent
= maxDescent
;
5282 if ((flags
& wxRICHTEXT_CACHE_SIZE
) && (rangeToUse
== child
->GetRange()))
5284 child
->SetCachedSize(childSize
);
5285 child
->SetDescent(childDescent
);
5291 if (partialExtents
->GetCount() > 0)
5292 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
5297 for (i
= 0; i
< childExtents
.GetCount(); i
++)
5299 partialExtents
->Add(childExtents
[i
] + lastSize
);
5309 node
= node
->GetNext();
5315 // Use formatted data, with line breaks
5318 // We're going to loop through each line, and then for each line,
5319 // call GetRangeSize for the fragment that comprises that line.
5320 // Only we have to do that multiple times within the line, because
5321 // the line may be broken into pieces. For now ignore line break commands
5322 // (so we can assume that getting the unformatted size for a fragment
5323 // within a line is the actual size)
5325 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5328 wxRichTextLine
* line
= node
->GetData();
5329 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5330 if (!lineRange
.IsOutside(range
))
5334 int maxLineHeight
= 0;
5335 int maxLineWidth
= 0;
5337 wxRichTextObjectList::compatibility_iterator node2
= m_children
.GetFirst();
5340 wxRichTextObject
* child
= node2
->GetData();
5342 if (!child
->IsFloating() && !child
->GetRange().IsOutside(lineRange
))
5344 wxRichTextRange rangeToUse
= lineRange
;
5345 rangeToUse
.LimitTo(child
->GetRange());
5346 if (child
->IsTopLevel())
5347 rangeToUse
= child
->GetOwnRange();
5350 int childDescent
= 0;
5351 if (child
->GetRangeSize(rangeToUse
, childSize
, childDescent
, dc
, context
, flags
, wxPoint(position
.x
+ sz
.x
, position
.y
)))
5353 if (childDescent
== 0)
5355 // Assume that if descent is zero, this child can occupy the full line height
5356 // and does not need space for the line's maximum descent. So we influence
5357 // the overall max line height only.
5358 maxLineHeight
= wxMax(maxLineHeight
, childSize
.y
);
5362 maxAscent
= wxMax(maxAscent
, (childSize
.y
- childDescent
));
5363 maxDescent
= wxMax(maxAscent
, childDescent
);
5365 maxLineHeight
= wxMax(maxLineHeight
, (maxAscent
+ maxDescent
));
5366 maxLineWidth
+= childSize
.x
;
5370 node2
= node2
->GetNext();
5373 descent
= wxMax(descent
, maxDescent
);
5375 // Increase size by a line (TODO: paragraph spacing)
5376 sz
.y
+= maxLineHeight
;
5377 sz
.x
= wxMax(sz
.x
, maxLineWidth
);
5379 node
= node
->GetNext();
5386 /// Finds the absolute position and row height for the given character position
5387 bool wxRichTextParagraph::FindPosition(wxDC
& dc
, wxRichTextDrawingContext
& context
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
5391 wxRichTextLine
* line
= ((wxRichTextParagraphLayoutBox
*)GetParent())->GetLineAtPosition(0);
5393 *height
= line
->GetSize().y
;
5395 *height
= dc
.GetCharHeight();
5397 // -1 means 'the start of the buffer'.
5400 pt
= pt
+ line
->GetPosition();
5405 // The final position in a paragraph is taken to mean the position
5406 // at the start of the next paragraph.
5407 if (index
== GetRange().GetEnd())
5409 wxRichTextParagraphLayoutBox
* parent
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
5410 wxASSERT( parent
!= NULL
);
5412 // Find the height at the next paragraph, if any
5413 wxRichTextLine
* line
= parent
->GetLineAtPosition(index
+ 1);
5416 *height
= line
->GetSize().y
;
5417 pt
= line
->GetAbsolutePosition();
5421 *height
= dc
.GetCharHeight();
5422 int indent
= ConvertTenthsMMToPixels(dc
, m_attributes
.GetLeftIndent());
5423 pt
= wxPoint(indent
, GetCachedSize().y
);
5429 if (index
< GetRange().GetStart() || index
> GetRange().GetEnd())
5432 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5435 wxRichTextLine
* line
= node
->GetData();
5436 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5437 if (index
>= lineRange
.GetStart() && index
<= lineRange
.GetEnd())
5439 // If this is the last point in the line, and we're forcing the
5440 // returned value to be the start of the next line, do the required
5442 if (index
== lineRange
.GetEnd() && forceLineStart
)
5444 if (node
->GetNext())
5446 wxRichTextLine
* nextLine
= node
->GetNext()->GetData();
5447 *height
= nextLine
->GetSize().y
;
5448 pt
= nextLine
->GetAbsolutePosition();
5453 pt
.y
= line
->GetPosition().y
+ GetPosition().y
;
5455 wxRichTextRange
r(lineRange
.GetStart(), index
);
5459 // We find the size of the line up to this point,
5460 // then we can add this size to the line start position and
5461 // paragraph start position to find the actual position.
5463 if (GetRangeSize(r
, rangeSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, line
->GetPosition()+ GetPosition()))
5465 pt
.x
= line
->GetPosition().x
+ GetPosition().x
+ rangeSize
.x
;
5466 *height
= line
->GetSize().y
;
5473 node
= node
->GetNext();
5479 /// Hit-testing: returns a flag indicating hit test details, plus
5480 /// information about position
5481 int wxRichTextParagraph::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
5484 return wxRICHTEXT_HITTEST_NONE
;
5486 // If we're in the top-level container, then we can return
5487 // a suitable hit test code even if the point is outside the container area,
5488 // so that we can position the caret sensibly even if we don't
5489 // click on valid content. If we're not at the top-level, and the point
5490 // is not within this paragraph object, then we don't want to stop more
5491 // precise hit-testing from working prematurely, so return immediately.
5492 // NEW STRATEGY: use the parent boundary to test whether we're in the
5493 // right region, not the paragraph, since the paragraph may be positioned
5494 // some way in from where the user clicks.
5497 wxRichTextObject
* tempObj
, *tempContextObj
;
5498 if (GetParent() && GetParent()->wxRichTextObject::HitTest(dc
, context
, pt
, tmpPos
, & tempObj
, & tempContextObj
, flags
) == wxRICHTEXT_HITTEST_NONE
)
5499 return wxRICHTEXT_HITTEST_NONE
;
5502 wxRichTextObjectList::compatibility_iterator objNode
= m_children
.GetFirst();
5505 wxRichTextObject
* child
= objNode
->GetData();
5506 // Don't recurse if we have wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS,
5507 // and also, if this seems composite but actually is marked as atomic,
5509 if (child
->IsTopLevel() && ((flags
& wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS
) == 0) &&
5510 (! (((flags
& wxRICHTEXT_HITTEST_HONOUR_ATOMIC
) != 0) && child
->IsAtomic())))
5513 int hitTest
= child
->HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
);
5514 if (hitTest
!= wxRICHTEXT_HITTEST_NONE
)
5519 objNode
= objNode
->GetNext();
5522 wxPoint paraPos
= GetPosition();
5524 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetFirst();
5527 wxRichTextLine
* line
= node
->GetData();
5528 wxPoint linePos
= paraPos
+ line
->GetPosition();
5529 wxSize lineSize
= line
->GetSize();
5530 wxRichTextRange lineRange
= line
->GetAbsoluteRange();
5532 if (pt
.y
<= linePos
.y
+ lineSize
.y
)
5534 if (pt
.x
< linePos
.x
)
5536 textPosition
= lineRange
.GetStart();
5537 *obj
= FindObjectAtPosition(textPosition
);
5538 *contextObj
= GetContainer();
5539 return wxRICHTEXT_HITTEST_BEFORE
|wxRICHTEXT_HITTEST_OUTSIDE
;
5541 else if (pt
.x
>= (linePos
.x
+ lineSize
.x
))
5543 textPosition
= lineRange
.GetEnd();
5544 *obj
= FindObjectAtPosition(textPosition
);
5545 *contextObj
= GetContainer();
5546 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
;
5550 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5551 wxArrayInt partialExtents
;
5556 // This calculates the partial text extents
5557 GetRangeSize(lineRange
, paraSize
, paraDescent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, linePos
, & partialExtents
);
5559 int lastX
= linePos
.x
;
5561 for (i
= 0; i
< partialExtents
.GetCount(); i
++)
5563 int nextX
= partialExtents
[i
] + linePos
.x
;
5565 if (pt
.x
>= lastX
&& pt
.x
<= nextX
)
5567 textPosition
= i
+ lineRange
.GetStart(); // minus 1?
5569 *obj
= FindObjectAtPosition(textPosition
);
5570 *contextObj
= GetContainer();
5572 // So now we know it's between i-1 and i.
5573 // Let's see if we can be more precise about
5574 // which side of the position it's on.
5576 int midPoint
= (nextX
+ lastX
)/2;
5577 if (pt
.x
>= midPoint
)
5578 return wxRICHTEXT_HITTEST_AFTER
;
5580 return wxRICHTEXT_HITTEST_BEFORE
;
5587 int lastX
= linePos
.x
;
5588 for (i
= lineRange
.GetStart(); i
<= lineRange
.GetEnd(); i
++)
5593 wxRichTextRange
rangeToUse(lineRange
.GetStart(), i
);
5595 GetRangeSize(rangeToUse
, childSize
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
, linePos
);
5597 int nextX
= childSize
.x
+ linePos
.x
;
5599 if (pt
.x
>= lastX
&& pt
.x
<= nextX
)
5603 *obj
= FindObjectAtPosition(textPosition
);
5604 *contextObj
= GetContainer();
5606 // So now we know it's between i-1 and i.
5607 // Let's see if we can be more precise about
5608 // which side of the position it's on.
5610 int midPoint
= (nextX
+ lastX
)/2;
5611 if (pt
.x
>= midPoint
)
5612 return wxRICHTEXT_HITTEST_AFTER
;
5614 return wxRICHTEXT_HITTEST_BEFORE
;
5625 node
= node
->GetNext();
5628 return wxRICHTEXT_HITTEST_NONE
;
5631 /// Split an object at this position if necessary, and return
5632 /// the previous object, or NULL if inserting at beginning.
5633 wxRichTextObject
* wxRichTextParagraph::SplitAt(long pos
, wxRichTextObject
** previousObject
)
5635 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5638 wxRichTextObject
* child
= node
->GetData();
5640 if (pos
== child
->GetRange().GetStart())
5644 if (node
->GetPrevious())
5645 *previousObject
= node
->GetPrevious()->GetData();
5647 *previousObject
= NULL
;
5653 if (child
->GetRange().Contains(pos
))
5655 // This should create a new object, transferring part of
5656 // the content to the old object and the rest to the new object.
5657 wxRichTextObject
* newObject
= child
->DoSplit(pos
);
5659 // If we couldn't split this object, just insert in front of it.
5662 // Maybe this is an empty string, try the next one
5667 // Insert the new object after 'child'
5668 if (node
->GetNext())
5669 m_children
.Insert(node
->GetNext(), newObject
);
5671 m_children
.Append(newObject
);
5672 newObject
->SetParent(this);
5675 *previousObject
= child
;
5681 node
= node
->GetNext();
5684 *previousObject
= NULL
;
5688 /// Move content to a list from obj on
5689 void wxRichTextParagraph::MoveToList(wxRichTextObject
* obj
, wxList
& list
)
5691 wxRichTextObjectList::compatibility_iterator node
= m_children
.Find(obj
);
5694 wxRichTextObject
* child
= node
->GetData();
5697 wxRichTextObjectList::compatibility_iterator oldNode
= node
;
5699 node
= node
->GetNext();
5701 m_children
.DeleteNode(oldNode
);
5705 /// Add content back from list
5706 void wxRichTextParagraph::MoveFromList(wxList
& list
)
5708 for (wxList::compatibility_iterator node
= list
.GetFirst(); node
; node
= node
->GetNext())
5710 AppendChild((wxRichTextObject
*) node
->GetData());
5715 void wxRichTextParagraph::CalculateRange(long start
, long& end
)
5717 wxRichTextCompositeObject::CalculateRange(start
, end
);
5719 // Add one for end of paragraph
5722 m_range
.SetRange(start
, end
);
5725 /// Find the object at the given position
5726 wxRichTextObject
* wxRichTextParagraph::FindObjectAtPosition(long position
)
5728 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5731 wxRichTextObject
* obj
= node
->GetData();
5732 if (obj
->GetRange().Contains(position
) ||
5733 obj
->GetRange().GetStart() == position
||
5734 obj
->GetRange().GetEnd() == position
)
5737 node
= node
->GetNext();
5742 /// Get the plain text searching from the start or end of the range.
5743 /// The resulting string may be shorter than the range given.
5744 bool wxRichTextParagraph::GetContiguousPlainText(wxString
& text
, const wxRichTextRange
& range
, bool fromStart
)
5746 text
= wxEmptyString
;
5750 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
5753 wxRichTextObject
* obj
= node
->GetData();
5754 if (!obj
->GetRange().IsOutside(range
))
5756 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5759 text
+= textObj
->GetTextForRange(range
);
5767 node
= node
->GetNext();
5772 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetLast();
5775 wxRichTextObject
* obj
= node
->GetData();
5776 if (!obj
->GetRange().IsOutside(range
))
5778 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
5781 text
= textObj
->GetTextForRange(range
) + text
;
5785 text
= wxT(" ") + text
;
5789 node
= node
->GetPrevious();
5796 /// Find a suitable wrap position.
5797 bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange
& range
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int availableSpace
, long& wrapPosition
, wxArrayInt
* partialExtents
)
5799 if (range
.GetLength() <= 0)
5802 // Find the first position where the line exceeds the available space.
5804 long breakPosition
= range
.GetEnd();
5806 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5807 if (partialExtents
&& partialExtents
->GetCount() >= (size_t) (GetRange().GetLength()-1)) // the final position in a paragraph is the newline
5811 if (range
.GetStart() > GetRange().GetStart())
5812 widthBefore
= (*partialExtents
)[range
.GetStart() - GetRange().GetStart() - 1];
5817 for (i
= (size_t) range
.GetStart(); i
<= (size_t) range
.GetEnd(); i
++)
5819 int widthFromStartOfThisRange
= (*partialExtents
)[i
- GetRange().GetStart()] - widthBefore
;
5821 if (widthFromStartOfThisRange
> availableSpace
)
5823 breakPosition
= i
-1;
5831 // Binary chop for speed
5832 long minPos
= range
.GetStart();
5833 long maxPos
= range
.GetEnd();
5836 if (minPos
== maxPos
)
5839 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
5841 if (sz
.x
> availableSpace
)
5842 breakPosition
= minPos
- 1;
5845 else if ((maxPos
- minPos
) == 1)
5848 GetRangeSize(wxRichTextRange(range
.GetStart(), minPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
5850 if (sz
.x
> availableSpace
)
5851 breakPosition
= minPos
- 1;
5854 GetRangeSize(wxRichTextRange(range
.GetStart(), maxPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
5855 if (sz
.x
> availableSpace
)
5856 breakPosition
= maxPos
-1;
5862 long nextPos
= minPos
+ ((maxPos
- minPos
) / 2);
5865 GetRangeSize(wxRichTextRange(range
.GetStart(), nextPos
), sz
, descent
, dc
, context
, wxRICHTEXT_UNFORMATTED
);
5867 if (sz
.x
> availableSpace
)
5879 // Now we know the last position on the line.
5880 // Let's try to find a word break.
5883 if (GetContiguousPlainText(plainText
, wxRichTextRange(range
.GetStart(), breakPosition
), false))
5885 int newLinePos
= plainText
.Find(wxRichTextLineBreakChar
);
5886 if (newLinePos
!= wxNOT_FOUND
)
5888 breakPosition
= wxMax(0, range
.GetStart() + newLinePos
);
5892 int spacePos
= plainText
.Find(wxT(' '), true);
5893 int tabPos
= plainText
.Find(wxT('\t'), true);
5894 int pos
= wxMax(spacePos
, tabPos
);
5895 if (pos
!= wxNOT_FOUND
)
5897 int positionsFromEndOfString
= plainText
.length() - pos
- 1;
5898 breakPosition
= breakPosition
- positionsFromEndOfString
;
5903 wrapPosition
= breakPosition
;
5908 /// Get the bullet text for this paragraph.
5909 wxString
wxRichTextParagraph::GetBulletText()
5911 if (GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE
||
5912 (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP
))
5913 return wxEmptyString
;
5915 int number
= GetAttributes().GetBulletNumber();
5918 if ((GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ARABIC
) || (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
))
5920 text
.Printf(wxT("%d"), number
);
5922 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_UPPER
)
5924 // TODO: Unicode, and also check if number > 26
5925 text
.Printf(wxT("%c"), (wxChar
) (number
+64));
5927 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_LOWER
)
5929 // TODO: Unicode, and also check if number > 26
5930 text
.Printf(wxT("%c"), (wxChar
) (number
+96));
5932 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER
)
5934 text
= wxRichTextDecimalToRoman(number
);
5936 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER
)
5938 text
= wxRichTextDecimalToRoman(number
);
5941 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
)
5943 text
= GetAttributes().GetBulletText();
5946 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE
)
5948 // The outline style relies on the text being computed statically,
5949 // since it depends on other levels points (e.g. 1.2.1.1). So normally the bullet text
5950 // should be stored in the attributes; if not, just use the number for this
5951 // level, as previously computed.
5952 if (!GetAttributes().GetBulletText().IsEmpty())
5953 text
= GetAttributes().GetBulletText();
5956 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PARENTHESES
)
5958 text
= wxT("(") + text
+ wxT(")");
5960 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_RIGHT_PARENTHESIS
)
5962 text
= text
+ wxT(")");
5965 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PERIOD
)
5973 /// Allocate or reuse a line object
5974 wxRichTextLine
* wxRichTextParagraph::AllocateLine(int pos
)
5976 if (pos
< (int) m_cachedLines
.GetCount())
5978 wxRichTextLine
* line
= m_cachedLines
.Item(pos
)->GetData();
5984 wxRichTextLine
* line
= new wxRichTextLine(this);
5985 m_cachedLines
.Append(line
);
5990 /// Clear remaining unused line objects, if any
5991 bool wxRichTextParagraph::ClearUnusedLines(int lineCount
)
5993 int cachedLineCount
= m_cachedLines
.GetCount();
5994 if ((int) cachedLineCount
> lineCount
)
5996 for (int i
= 0; i
< (int) (cachedLineCount
- lineCount
); i
++)
5998 wxRichTextLineList::compatibility_iterator node
= m_cachedLines
.GetLast();
5999 wxRichTextLine
* line
= node
->GetData();
6000 m_cachedLines
.Erase(node
);
6007 /// Get combined attributes of the base style, paragraph style and character style. We use this to dynamically
6008 /// retrieve the actual style.
6009 wxRichTextAttr
wxRichTextParagraph::GetCombinedAttributes(const wxRichTextAttr
& contentStyle
, bool includingBoxAttr
) const
6011 wxRichTextAttr attr
;
6012 wxRichTextParagraphLayoutBox
* buf
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
6015 attr
= buf
->GetBasicStyle();
6016 if (!includingBoxAttr
)
6018 attr
.GetTextBoxAttr().Reset();
6019 // The background colour will be painted by the container, and we don't
6020 // want to unnecessarily overwrite the background when we're drawing text
6021 // because this may erase the guideline (which appears just under the text
6022 // if there's no padding).
6023 attr
.SetFlags(attr
.GetFlags() & ~wxTEXT_ATTR_BACKGROUND_COLOUR
);
6025 wxRichTextApplyStyle(attr
, GetAttributes());
6028 attr
= GetAttributes();
6030 wxRichTextApplyStyle(attr
, contentStyle
);
6034 /// Get combined attributes of the base style and paragraph style.
6035 wxRichTextAttr
wxRichTextParagraph::GetCombinedAttributes(bool includingBoxAttr
) const
6037 wxRichTextAttr attr
;
6038 wxRichTextParagraphLayoutBox
* buf
= wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox
);
6041 attr
= buf
->GetBasicStyle();
6042 if (!includingBoxAttr
)
6043 attr
.GetTextBoxAttr().Reset();
6044 wxRichTextApplyStyle(attr
, GetAttributes());
6047 attr
= GetAttributes();
6052 // Create default tabstop array
6053 void wxRichTextParagraph::InitDefaultTabs()
6055 // create a default tab list at 10 mm each.
6056 for (int i
= 0; i
< 20; ++i
)
6058 sm_defaultTabs
.Add(i
*100);
6062 // Clear default tabstop array
6063 void wxRichTextParagraph::ClearDefaultTabs()
6065 sm_defaultTabs
.Clear();
6068 void wxRichTextParagraph::LayoutFloat(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, int style
, wxRichTextFloatCollector
* floatCollector
)
6070 wxRichTextObjectList::compatibility_iterator node
= GetChildren().GetFirst();
6073 wxRichTextObject
* anchored
= node
->GetData();
6074 if (anchored
&& anchored
->IsFloating() && !floatCollector
->HasFloat(anchored
))
6078 anchored
->GetRangeSize(anchored
->GetRange(), size
, descent
, dc
, context
, style
);
6081 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().IsValid())
6083 offsetY
= anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetValue();
6084 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
6086 offsetY
= ConvertTenthsMMToPixels(dc
, offsetY
);
6090 int pos
= floatCollector
->GetFitPosition(anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode(), rect
.y
+ offsetY
, size
.y
);
6092 /* Update the offset */
6093 int newOffsetY
= pos
- rect
.y
;
6094 if (newOffsetY
!= offsetY
)
6096 if (anchored
->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
6097 newOffsetY
= ConvertPixelsToTenthsMM(dc
, newOffsetY
);
6098 anchored
->GetAttributes().GetTextBoxAttr().GetTop().SetValue(newOffsetY
);
6101 if (anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT
)
6103 else if (anchored
->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT
)
6104 x
= rect
.x
+ rect
.width
- size
.x
;
6106 anchored
->SetPosition(wxPoint(x
, pos
));
6107 anchored
->SetCachedSize(size
);
6108 floatCollector
->CollectFloat(this, anchored
);
6111 node
= node
->GetNext();
6115 // Get the first position from pos that has a line break character.
6116 long wxRichTextParagraph::GetFirstLineBreakPosition(long pos
)
6118 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
6121 wxRichTextObject
* obj
= node
->GetData();
6122 if (pos
>= obj
->GetRange().GetStart() && pos
<= obj
->GetRange().GetEnd())
6124 wxRichTextPlainText
* textObj
= wxDynamicCast(obj
, wxRichTextPlainText
);
6127 long breakPos
= textObj
->GetFirstLineBreakPosition(pos
);
6132 node
= node
->GetNext();
6139 * This object represents a line in a paragraph, and stores
6140 * offsets from the start of the paragraph representing the
6141 * start and end positions of the line.
6144 wxRichTextLine::wxRichTextLine(wxRichTextParagraph
* parent
)
6150 void wxRichTextLine::Init(wxRichTextParagraph
* parent
)
6153 m_range
.SetRange(-1, -1);
6154 m_pos
= wxPoint(0, 0);
6155 m_size
= wxSize(0, 0);
6157 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6158 m_objectSizes
.Clear();
6163 void wxRichTextLine::Copy(const wxRichTextLine
& obj
)
6165 m_range
= obj
.m_range
;
6166 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6167 m_objectSizes
= obj
.m_objectSizes
;
6171 /// Get the absolute object position
6172 wxPoint
wxRichTextLine::GetAbsolutePosition() const
6174 return m_parent
->GetPosition() + m_pos
;
6177 /// Get the absolute range
6178 wxRichTextRange
wxRichTextLine::GetAbsoluteRange() const
6180 wxRichTextRange
range(m_range
.GetStart() + m_parent
->GetRange().GetStart(), 0);
6181 range
.SetEnd(range
.GetStart() + m_range
.GetLength()-1);
6186 * wxRichTextPlainText
6187 * This object represents a single piece of text.
6190 IMPLEMENT_DYNAMIC_CLASS(wxRichTextPlainText
, wxRichTextObject
)
6192 wxRichTextPlainText::wxRichTextPlainText(const wxString
& text
, wxRichTextObject
* parent
, wxRichTextAttr
* style
):
6193 wxRichTextObject(parent
)
6196 SetAttributes(*style
);
6201 #define USE_KERNING_FIX 1
6203 // If insufficient tabs are defined, this is the tab width used
6204 #define WIDTH_FOR_DEFAULT_TABS 50
6207 bool wxRichTextPlainText::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int WXUNUSED(style
))
6209 wxRichTextParagraph
* para
= wxDynamicCast(GetParent(), wxRichTextParagraph
);
6210 wxASSERT (para
!= NULL
);
6212 wxRichTextAttr
textAttr(para
? para
->GetCombinedAttributes(GetAttributes(), false /* no box attributes */) : GetAttributes());
6213 context
.ApplyVirtualAttributes(textAttr
, this);
6215 // Let's make the assumption for now that for content in a paragraph, including
6216 // text, we never have a discontinuous selection. So we only deal with a
6218 wxRichTextRange selectionRange
;
6219 if (selection
.IsValid())
6221 wxRichTextRangeArray selectionRanges
= selection
.GetSelectionForObject(this);
6222 if (selectionRanges
.GetCount() > 0)
6223 selectionRange
= selectionRanges
[0];
6225 selectionRange
= wxRICHTEXT_NO_SELECTION
;
6228 selectionRange
= wxRICHTEXT_NO_SELECTION
;
6230 int offset
= GetRange().GetStart();
6232 // Replace line break characters with spaces
6233 wxString str
= m_text
;
6234 wxString toRemove
= wxRichTextLineBreakChar
;
6235 str
.Replace(toRemove
, wxT(" "));
6236 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS
))
6239 long len
= range
.GetLength();
6240 wxString stringChunk
= str
.Mid(range
.GetStart() - offset
, (size_t) len
);
6242 // Test for the optimized situations where all is selected, or none
6245 wxFont
textFont(GetBuffer()->GetFontTable().FindFont(textAttr
));
6246 wxCheckSetFont(dc
, textFont
);
6247 int charHeight
= dc
.GetCharHeight();
6250 if ( textFont
.IsOk() )
6252 if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
) )
6254 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6255 textFont
.SetPointSize( static_cast<int>(size
) );
6258 wxCheckSetFont(dc
, textFont
);
6260 else if ( textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) )
6262 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6263 textFont
.SetPointSize( static_cast<int>(size
) );
6265 int sub_height
= static_cast<int>( static_cast<double>(charHeight
) / wxSCRIPT_MUL_FACTOR
);
6266 y
= rect
.y
+ (rect
.height
- sub_height
+ (descent
- m_descent
));
6267 wxCheckSetFont(dc
, textFont
);
6272 y
= rect
.y
+ (rect
.height
- charHeight
- (descent
- m_descent
));
6278 y
= rect
.y
+ (rect
.height
- charHeight
- (descent
- m_descent
));
6281 // TODO: new selection code
6283 // (a) All selected.
6284 if (selectionRange
.GetStart() <= range
.GetStart() && selectionRange
.GetEnd() >= range
.GetEnd())
6286 DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, true);
6288 // (b) None selected.
6289 else if (selectionRange
.GetEnd() < range
.GetStart() || selectionRange
.GetStart() > range
.GetEnd())
6291 // Draw all unselected
6292 DrawTabbedString(dc
, textAttr
, rect
, stringChunk
, x
, y
, false);
6296 // (c) Part selected, part not
6297 // Let's draw unselected chunk, selected chunk, then unselected chunk.
6299 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6301 // 1. Initial unselected chunk, if any, up until start of selection.
6302 if (selectionRange
.GetStart() > range
.GetStart() && selectionRange
.GetStart() <= range
.GetEnd())
6304 int r1
= range
.GetStart();
6305 int s1
= selectionRange
.GetStart()-1;
6306 int fragmentLen
= s1
- r1
+ 1;
6307 if (fragmentLen
< 0)
6309 wxLogDebug(wxT("Mid(%d, %d"), (int)(r1
- offset
), (int)fragmentLen
);
6311 wxString stringFragment
= str
.Mid(r1
- offset
, fragmentLen
);
6313 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false);
6316 if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
)
6318 // Compensate for kerning difference
6319 wxString
stringFragment2(str
.Mid(r1
- offset
, fragmentLen
+1));
6320 wxString
stringFragment3(str
.Mid(r1
- offset
+ fragmentLen
, 1));
6322 wxCoord w1
, h1
, w2
, h2
, w3
, h3
;
6323 dc
.GetTextExtent(stringFragment
, & w1
, & h1
);
6324 dc
.GetTextExtent(stringFragment2
, & w2
, & h2
);
6325 dc
.GetTextExtent(stringFragment3
, & w3
, & h3
);
6327 int kerningDiff
= (w1
+ w3
) - w2
;
6328 x
= x
- kerningDiff
;
6333 // 2. Selected chunk, if any.
6334 if (selectionRange
.GetEnd() >= range
.GetStart())
6336 int s1
= wxMax(selectionRange
.GetStart(), range
.GetStart());
6337 int s2
= wxMin(selectionRange
.GetEnd(), range
.GetEnd());
6339 int fragmentLen
= s2
- s1
+ 1;
6340 if (fragmentLen
< 0)
6342 wxLogDebug(wxT("Mid(%d, %d"), (int)(s1
- offset
), (int)fragmentLen
);
6344 wxString stringFragment
= str
.Mid(s1
- offset
, fragmentLen
);
6346 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, true);
6349 if (stringChunk
.Find(wxT("\t")) == wxNOT_FOUND
)
6351 // Compensate for kerning difference
6352 wxString
stringFragment2(str
.Mid(s1
- offset
, fragmentLen
+1));
6353 wxString
stringFragment3(str
.Mid(s1
- offset
+ fragmentLen
, 1));
6355 wxCoord w1
, h1
, w2
, h2
, w3
, h3
;
6356 dc
.GetTextExtent(stringFragment
, & w1
, & h1
);
6357 dc
.GetTextExtent(stringFragment2
, & w2
, & h2
);
6358 dc
.GetTextExtent(stringFragment3
, & w3
, & h3
);
6360 int kerningDiff
= (w1
+ w3
) - w2
;
6361 x
= x
- kerningDiff
;
6366 // 3. Remaining unselected chunk, if any
6367 if (selectionRange
.GetEnd() < range
.GetEnd())
6369 int s2
= wxMin(selectionRange
.GetEnd()+1, range
.GetEnd());
6370 int r2
= range
.GetEnd();
6372 int fragmentLen
= r2
- s2
+ 1;
6373 if (fragmentLen
< 0)
6375 wxLogDebug(wxT("Mid(%d, %d"), (int)(s2
- offset
), (int)fragmentLen
);
6377 wxString stringFragment
= str
.Mid(s2
- offset
, fragmentLen
);
6379 DrawTabbedString(dc
, textAttr
, rect
, stringFragment
, x
, y
, false);
6386 bool wxRichTextPlainText::DrawTabbedString(wxDC
& dc
, const wxRichTextAttr
& attr
, const wxRect
& rect
,wxString
& str
, wxCoord
& x
, wxCoord
& y
, bool selected
)
6388 bool hasTabs
= (str
.Find(wxT('\t')) != wxNOT_FOUND
);
6390 wxArrayInt tabArray
;
6394 if (attr
.GetTabs().IsEmpty())
6395 tabArray
= wxRichTextParagraph::GetDefaultTabs();
6397 tabArray
= attr
.GetTabs();
6398 tabCount
= tabArray
.GetCount();
6400 for (int i
= 0; i
< tabCount
; ++i
)
6402 int pos
= tabArray
[i
];
6403 pos
= ConvertTenthsMMToPixels(dc
, pos
);
6410 int nextTabPos
= -1;
6416 wxColour
highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
));
6417 wxColour
highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
));
6419 wxCheckSetBrush(dc
, wxBrush(highlightColour
));
6420 wxCheckSetPen(dc
, wxPen(highlightColour
));
6421 dc
.SetTextForeground(highlightTextColour
);
6422 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6426 dc
.SetTextForeground(attr
.GetTextColour());
6428 if (attr
.HasFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
) && attr
.GetBackgroundColour().IsOk())
6430 dc
.SetBackgroundMode(wxBRUSHSTYLE_SOLID
);
6431 dc
.SetTextBackground(attr
.GetBackgroundColour());
6434 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
6437 wxCoord x_orig
= GetParent()->GetPosition().x
;
6440 // the string has a tab
6441 // break up the string at the Tab
6442 wxString stringChunk
= str
.BeforeFirst(wxT('\t'));
6443 str
= str
.AfterFirst(wxT('\t'));
6444 dc
.GetTextExtent(stringChunk
, & w
, & h
);
6446 bool not_found
= true;
6447 for (int i
= 0; i
< tabCount
&& not_found
; ++i
)
6449 nextTabPos
= tabArray
.Item(i
) + x_orig
;
6451 // Find the next tab position.
6452 // Even if we're at the end of the tab array, we must still draw the chunk.
6454 if (nextTabPos
> tabPos
|| (i
== (tabCount
- 1)))
6456 if (nextTabPos
<= tabPos
)
6458 int defaultTabWidth
= ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
);
6459 nextTabPos
= tabPos
+ defaultTabWidth
;
6466 wxRect
selRect(x
, rect
.y
, w
, rect
.GetHeight());
6467 dc
.DrawRectangle(selRect
);
6469 dc
.DrawText(stringChunk
, x
, y
);
6471 if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
))
6473 wxPen oldPen
= dc
.GetPen();
6474 wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1));
6475 dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5));
6476 wxCheckSetPen(dc
, oldPen
);
6482 hasTabs
= (str
.Find(wxT('\t')) != wxNOT_FOUND
);
6487 dc
.GetTextExtent(str
, & w
, & h
);
6490 wxRect
selRect(x
, rect
.y
, w
, rect
.GetHeight());
6491 dc
.DrawRectangle(selRect
);
6493 dc
.DrawText(str
, x
, y
);
6495 if (attr
.HasTextEffects() && (attr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
))
6497 wxPen oldPen
= dc
.GetPen();
6498 wxCheckSetPen(dc
, wxPen(attr
.GetTextColour(), 1));
6499 dc
.DrawLine(x
, (int) (y
+(h
/2)+0.5), x
+w
, (int) (y
+(h
/2)+0.5));
6500 wxCheckSetPen(dc
, oldPen
);
6509 /// Lay the item out
6510 bool wxRichTextPlainText::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& WXUNUSED(rect
), const wxRect
& WXUNUSED(parentRect
), int WXUNUSED(style
))
6512 // Only lay out if we haven't already cached the size
6514 GetRangeSize(GetRange(), m_size
, m_descent
, dc
, context
, 0, wxPoint(0, 0));
6516 // Eventually we want to have a reasonable estimate of minimum size.
6517 m_minSize
= wxSize(0, 0);
6522 void wxRichTextPlainText::Copy(const wxRichTextPlainText
& obj
)
6524 wxRichTextObject::Copy(obj
);
6526 m_text
= obj
.m_text
;
6529 /// Get/set the object size for the given range. Returns false if the range
6530 /// is invalid for this object.
6531 bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int WXUNUSED(flags
), wxPoint position
, wxArrayInt
* partialExtents
) const
6533 if (!range
.IsWithin(GetRange()))
6536 wxRichTextParagraph
* para
= wxDynamicCast(GetParent(), wxRichTextParagraph
);
6537 wxASSERT (para
!= NULL
);
6539 int relativeX
= position
.x
- GetParent()->GetPosition().x
;
6541 wxRichTextAttr
textAttr(para
? para
->GetCombinedAttributes(GetAttributes()) : GetAttributes());
6542 context
.ApplyVirtualAttributes(textAttr
, (wxRichTextObject
*) this);
6544 // Always assume unformatted text, since at this level we have no knowledge
6545 // of line breaks - and we don't need it, since we'll calculate size within
6546 // formatted text by doing it in chunks according to the line ranges
6548 bool bScript(false);
6549 wxFont
font(GetBuffer()->GetFontTable().FindFont(textAttr
));
6552 if ( textAttr
.HasTextEffects() && ( (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
)
6553 || (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
) ) )
6555 wxFont textFont
= font
;
6556 double size
= static_cast<double>(textFont
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
6557 textFont
.SetPointSize( static_cast<int>(size
) );
6558 wxCheckSetFont(dc
, textFont
);
6563 wxCheckSetFont(dc
, font
);
6567 bool haveDescent
= false;
6568 int startPos
= range
.GetStart() - GetRange().GetStart();
6569 long len
= range
.GetLength();
6571 wxString
str(m_text
);
6572 wxString toReplace
= wxRichTextLineBreakChar
;
6573 str
.Replace(toReplace
, wxT(" "));
6575 wxString stringChunk
= str
.Mid(startPos
, (size_t) len
);
6577 if (textAttr
.HasTextEffects() && (textAttr
.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS
))
6578 stringChunk
.MakeUpper();
6582 if (stringChunk
.Find(wxT('\t')) != wxNOT_FOUND
)
6584 // the string has a tab
6585 wxArrayInt tabArray
;
6586 if (textAttr
.GetTabs().IsEmpty())
6587 tabArray
= wxRichTextParagraph::GetDefaultTabs();
6589 tabArray
= textAttr
.GetTabs();
6591 int tabCount
= tabArray
.GetCount();
6593 for (int i
= 0; i
< tabCount
; ++i
)
6595 int pos
= tabArray
[i
];
6596 pos
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, pos
);
6600 int nextTabPos
= -1;
6602 while (stringChunk
.Find(wxT('\t')) >= 0)
6604 int absoluteWidth
= 0;
6606 // the string has a tab
6607 // break up the string at the Tab
6608 wxString stringFragment
= stringChunk
.BeforeFirst(wxT('\t'));
6609 stringChunk
= stringChunk
.AfterFirst(wxT('\t'));
6614 if (partialExtents
->GetCount() > 0)
6615 oldWidth
= (*partialExtents
)[partialExtents
->GetCount()-1];
6619 // Add these partial extents
6621 dc
.GetPartialTextExtents(stringFragment
, p
);
6623 for (j
= 0; j
< p
.GetCount(); j
++)
6624 partialExtents
->Add(oldWidth
+ p
[j
]);
6626 if (partialExtents
->GetCount() > 0)
6627 absoluteWidth
= (*partialExtents
)[(*partialExtents
).GetCount()-1] + relativeX
;
6629 absoluteWidth
= relativeX
;
6633 dc
.GetTextExtent(stringFragment
, & w
, & h
);
6635 absoluteWidth
= width
+ relativeX
;
6639 bool notFound
= true;
6640 for (int i
= 0; i
< tabCount
&& notFound
; ++i
)
6642 nextTabPos
= tabArray
.Item(i
);
6644 // Find the next tab position.
6645 // Even if we're at the end of the tab array, we must still process the chunk.
6647 if (nextTabPos
> absoluteWidth
|| (i
== (tabCount
- 1)))
6649 if (nextTabPos
<= absoluteWidth
)
6651 int defaultTabWidth
= ((wxRichTextPlainText
*) this)->ConvertTenthsMMToPixels(dc
, WIDTH_FOR_DEFAULT_TABS
);
6652 nextTabPos
= absoluteWidth
+ defaultTabWidth
;
6656 width
= nextTabPos
- relativeX
;
6659 partialExtents
->Add(width
);
6665 if (!stringChunk
.IsEmpty())
6670 if (partialExtents
->GetCount() > 0)
6671 oldWidth
= (*partialExtents
)[partialExtents
->GetCount()-1];
6675 // Add these partial extents
6677 dc
.GetPartialTextExtents(stringChunk
, p
);
6679 for (j
= 0; j
< p
.GetCount(); j
++)
6680 partialExtents
->Add(oldWidth
+ p
[j
]);
6684 dc
.GetTextExtent(stringChunk
, & w
, & h
, & descent
);
6692 int charHeight
= dc
.GetCharHeight();
6693 if ((*partialExtents
).GetCount() > 0)
6694 w
= (*partialExtents
)[partialExtents
->GetCount()-1];
6697 size
= wxSize(w
, charHeight
);
6701 size
= wxSize(width
, dc
.GetCharHeight());
6705 dc
.GetTextExtent(wxT("X"), & w
, & h
, & descent
);
6713 /// Do a split, returning an object containing the second part, and setting
6714 /// the first part in 'this'.
6715 wxRichTextObject
* wxRichTextPlainText::DoSplit(long pos
)
6717 long index
= pos
- GetRange().GetStart();
6719 if (index
< 0 || index
>= (int) m_text
.length())
6722 wxString firstPart
= m_text
.Mid(0, index
);
6723 wxString secondPart
= m_text
.Mid(index
);
6727 wxRichTextPlainText
* newObject
= new wxRichTextPlainText(secondPart
);
6728 newObject
->SetAttributes(GetAttributes());
6729 newObject
->SetProperties(GetProperties());
6731 newObject
->SetRange(wxRichTextRange(pos
, GetRange().GetEnd()));
6732 GetRange().SetEnd(pos
-1);
6738 void wxRichTextPlainText::CalculateRange(long start
, long& end
)
6740 end
= start
+ m_text
.length() - 1;
6741 m_range
.SetRange(start
, end
);
6745 bool wxRichTextPlainText::DeleteRange(const wxRichTextRange
& range
)
6747 wxRichTextRange r
= range
;
6749 r
.LimitTo(GetRange());
6751 if (r
.GetStart() == GetRange().GetStart() && r
.GetEnd() == GetRange().GetEnd())
6757 long startIndex
= r
.GetStart() - GetRange().GetStart();
6758 long len
= r
.GetLength();
6760 m_text
= m_text
.Mid(0, startIndex
) + m_text
.Mid(startIndex
+len
);
6764 /// Get text for the given range.
6765 wxString
wxRichTextPlainText::GetTextForRange(const wxRichTextRange
& range
) const
6767 wxRichTextRange r
= range
;
6769 r
.LimitTo(GetRange());
6771 long startIndex
= r
.GetStart() - GetRange().GetStart();
6772 long len
= r
.GetLength();
6774 return m_text
.Mid(startIndex
, len
);
6777 /// Returns true if this object can merge itself with the given one.
6778 bool wxRichTextPlainText::CanMerge(wxRichTextObject
* object
) const
6780 return object
->GetClassInfo() == CLASSINFO(wxRichTextPlainText
) &&
6781 (m_text
.empty() || (wxTextAttrEq(GetAttributes(), object
->GetAttributes()) && m_properties
== object
->GetProperties()));
6784 /// Returns true if this object merged itself with the given one.
6785 /// The calling code will then delete the given object.
6786 bool wxRichTextPlainText::Merge(wxRichTextObject
* object
)
6788 wxRichTextPlainText
* textObject
= wxDynamicCast(object
, wxRichTextPlainText
);
6789 wxASSERT( textObject
!= NULL
);
6793 m_text
+= textObject
->GetText();
6794 wxRichTextApplyStyle(m_attributes
, textObject
->GetAttributes());
6801 /// Dump to output stream for debugging
6802 void wxRichTextPlainText::Dump(wxTextOutputStream
& stream
)
6804 wxRichTextObject::Dump(stream
);
6805 stream
<< m_text
<< wxT("\n");
6808 /// Get the first position from pos that has a line break character.
6809 long wxRichTextPlainText::GetFirstLineBreakPosition(long pos
)
6812 int len
= m_text
.length();
6813 int startPos
= pos
- m_range
.GetStart();
6814 for (i
= startPos
; i
< len
; i
++)
6816 wxChar ch
= m_text
[i
];
6817 if (ch
== wxRichTextLineBreakChar
)
6819 return i
+ m_range
.GetStart();
6827 * This is a kind of box, used to represent the whole buffer
6830 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBuffer
, wxRichTextParagraphLayoutBox
)
6832 wxList
wxRichTextBuffer::sm_handlers
;
6833 wxList
wxRichTextBuffer::sm_drawingHandlers
;
6834 wxRichTextFieldTypeHashMap
wxRichTextBuffer::sm_fieldTypes
;
6835 wxRichTextRenderer
* wxRichTextBuffer::sm_renderer
= NULL
;
6836 int wxRichTextBuffer::sm_bulletRightMargin
= 20;
6837 float wxRichTextBuffer::sm_bulletProportion
= (float) 0.3;
6840 void wxRichTextBuffer::Init()
6842 m_commandProcessor
= new wxCommandProcessor
;
6843 m_styleSheet
= NULL
;
6845 m_batchedCommandDepth
= 0;
6846 m_batchedCommand
= NULL
;
6854 wxRichTextBuffer::~wxRichTextBuffer()
6856 delete m_commandProcessor
;
6857 delete m_batchedCommand
;
6860 ClearEventHandlers();
6863 void wxRichTextBuffer::ResetAndClearCommands()
6867 GetCommandProcessor()->ClearCommands();
6870 Invalidate(wxRICHTEXT_ALL
);
6873 void wxRichTextBuffer::Copy(const wxRichTextBuffer
& obj
)
6875 wxRichTextParagraphLayoutBox::Copy(obj
);
6877 m_styleSheet
= obj
.m_styleSheet
;
6878 m_modified
= obj
.m_modified
;
6879 m_batchedCommandDepth
= 0;
6880 if (m_batchedCommand
)
6881 delete m_batchedCommand
;
6882 m_batchedCommand
= NULL
;
6883 m_suppressUndo
= obj
.m_suppressUndo
;
6884 m_invalidRange
= obj
.m_invalidRange
;
6887 /// Push style sheet to top of stack
6888 bool wxRichTextBuffer::PushStyleSheet(wxRichTextStyleSheet
* styleSheet
)
6891 styleSheet
->InsertSheet(m_styleSheet
);
6893 SetStyleSheet(styleSheet
);
6898 /// Pop style sheet from top of stack
6899 wxRichTextStyleSheet
* wxRichTextBuffer::PopStyleSheet()
6903 wxRichTextStyleSheet
* oldSheet
= m_styleSheet
;
6904 m_styleSheet
= oldSheet
->GetNextSheet();
6913 /// Submit command to insert paragraphs
6914 bool wxRichTextBuffer::InsertParagraphsWithUndo(long pos
, const wxRichTextParagraphLayoutBox
& paragraphs
, wxRichTextCtrl
* ctrl
, int flags
)
6916 return ctrl
->GetFocusObject()->InsertParagraphsWithUndo(this, pos
, paragraphs
, ctrl
, flags
);
6919 /// Submit command to insert paragraphs
6920 bool wxRichTextParagraphLayoutBox::InsertParagraphsWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxRichTextParagraphLayoutBox
& paragraphs
, wxRichTextCtrl
* ctrl
, int WXUNUSED(flags
))
6922 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6924 action
->GetNewParagraphs() = paragraphs
;
6926 action
->SetPosition(pos
);
6928 wxRichTextRange range
= wxRichTextRange(pos
, pos
+ paragraphs
.GetOwnRange().GetEnd() - 1);
6929 if (!paragraphs
.GetPartialParagraph())
6930 range
.SetEnd(range
.GetEnd()+1);
6932 // Set the range we'll need to delete in Undo
6933 action
->SetRange(range
);
6935 buffer
->SubmitAction(action
);
6940 /// Submit command to insert the given text
6941 bool wxRichTextBuffer::InsertTextWithUndo(long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
, int flags
)
6943 return ctrl
->GetFocusObject()->InsertTextWithUndo(this, pos
, text
, ctrl
, flags
);
6946 /// Submit command to insert the given text
6947 bool wxRichTextParagraphLayoutBox::InsertTextWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxString
& text
, wxRichTextCtrl
* ctrl
, int flags
)
6949 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6951 wxRichTextAttr
* p
= NULL
;
6952 wxRichTextAttr paraAttr
;
6953 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
6955 // Get appropriate paragraph style
6956 paraAttr
= GetStyleForNewParagraph(buffer
, pos
, false, false);
6957 if (!paraAttr
.IsDefault())
6961 action
->GetNewParagraphs().AddParagraphs(text
, p
);
6963 int length
= action
->GetNewParagraphs().GetOwnRange().GetLength();
6965 if (!text
.empty() && text
.Last() != wxT('\n'))
6967 // Don't count the newline when undoing
6969 action
->GetNewParagraphs().SetPartialParagraph(true);
6971 else if (!text
.empty() && text
.Last() == wxT('\n'))
6974 action
->SetPosition(pos
);
6976 // Set the range we'll need to delete in Undo
6977 action
->SetRange(wxRichTextRange(pos
, pos
+ length
- 1));
6979 buffer
->SubmitAction(action
);
6984 /// Submit command to insert the given text
6985 bool wxRichTextBuffer::InsertNewlineWithUndo(long pos
, wxRichTextCtrl
* ctrl
, int flags
)
6987 return ctrl
->GetFocusObject()->InsertNewlineWithUndo(this, pos
, ctrl
, flags
);
6990 /// Submit command to insert the given text
6991 bool wxRichTextParagraphLayoutBox::InsertNewlineWithUndo(wxRichTextBuffer
* buffer
, long pos
, wxRichTextCtrl
* ctrl
, int flags
)
6993 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Text"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
6995 wxRichTextAttr
* p
= NULL
;
6996 wxRichTextAttr paraAttr
;
6997 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
6999 paraAttr
= GetStyleForNewParagraph(buffer
, pos
, false, true /* look for next paragraph style */);
7000 if (!paraAttr
.IsDefault())
7004 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7006 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(wxEmptyString
, this, & attr
);
7007 action
->GetNewParagraphs().AppendChild(newPara
);
7008 action
->GetNewParagraphs().UpdateRanges();
7009 action
->GetNewParagraphs().SetPartialParagraph(false);
7010 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
, false);
7014 newPara
->SetAttributes(*p
);
7016 if (flags
& wxRICHTEXT_INSERT_INTERACTIVE
)
7018 if (para
&& para
->GetRange().GetEnd() == pos
)
7021 // Now see if we need to number the paragraph.
7022 if (newPara
->GetAttributes().HasBulletNumber())
7024 wxRichTextAttr numberingAttr
;
7025 if (FindNextParagraphNumber(para
, numberingAttr
))
7026 wxRichTextApplyStyle(newPara
->GetAttributes(), (const wxRichTextAttr
&) numberingAttr
);
7030 action
->SetPosition(pos
);
7032 // Use the default character style
7033 // Use the default character style
7034 if (!buffer
->GetDefaultStyle().IsDefault() && newPara
->GetChildren().GetFirst())
7036 // Check whether the default style merely reflects the paragraph/basic style,
7037 // in which case don't apply it.
7038 wxRichTextAttr
defaultStyle(buffer
->GetDefaultStyle());
7039 wxRichTextAttr toApply
;
7042 wxRichTextAttr combinedAttr
= para
->GetCombinedAttributes(true /* include box attributes */);
7043 wxRichTextAttr newAttr
;
7044 // This filters out attributes that are accounted for by the current
7045 // paragraph/basic style
7046 wxRichTextApplyStyle(toApply
, defaultStyle
, & combinedAttr
);
7049 toApply
= defaultStyle
;
7051 if (!toApply
.IsDefault())
7052 newPara
->GetChildren().GetFirst()->GetData()->SetAttributes(toApply
);
7055 // Set the range we'll need to delete in Undo
7056 action
->SetRange(wxRichTextRange(pos1
, pos1
));
7058 buffer
->SubmitAction(action
);
7063 /// Submit command to insert the given image
7064 bool wxRichTextBuffer::InsertImageWithUndo(long pos
, const wxRichTextImageBlock
& imageBlock
, wxRichTextCtrl
* ctrl
, int flags
,
7065 const wxRichTextAttr
& textAttr
)
7067 return ctrl
->GetFocusObject()->InsertImageWithUndo(this, pos
, imageBlock
, ctrl
, flags
, textAttr
);
7070 /// Submit command to insert the given image
7071 bool wxRichTextParagraphLayoutBox::InsertImageWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxRichTextImageBlock
& imageBlock
,
7072 wxRichTextCtrl
* ctrl
, int flags
,
7073 const wxRichTextAttr
& textAttr
)
7075 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7077 wxRichTextAttr
* p
= NULL
;
7078 wxRichTextAttr paraAttr
;
7079 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7081 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
7082 if (!paraAttr
.IsDefault())
7086 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7088 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7090 newPara
->SetAttributes(*p
);
7092 wxRichTextImage
* imageObject
= new wxRichTextImage(imageBlock
, newPara
);
7093 newPara
->AppendChild(imageObject
);
7094 imageObject
->SetAttributes(textAttr
);
7095 action
->GetNewParagraphs().AppendChild(newPara
);
7096 action
->GetNewParagraphs().UpdateRanges();
7098 action
->GetNewParagraphs().SetPartialParagraph(true);
7100 action
->SetPosition(pos
);
7102 // Set the range we'll need to delete in Undo
7103 action
->SetRange(wxRichTextRange(pos
, pos
));
7105 buffer
->SubmitAction(action
);
7110 // Insert an object with no change of it
7111 wxRichTextObject
* wxRichTextBuffer::InsertObjectWithUndo(long pos
, wxRichTextObject
*object
, wxRichTextCtrl
* ctrl
, int flags
)
7113 return ctrl
->GetFocusObject()->InsertObjectWithUndo(this, pos
, object
, ctrl
, flags
);
7116 // Insert an object with no change of it
7117 wxRichTextObject
* wxRichTextParagraphLayoutBox::InsertObjectWithUndo(wxRichTextBuffer
* buffer
, long pos
, wxRichTextObject
*object
, wxRichTextCtrl
* ctrl
, int flags
)
7119 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Object"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7121 wxRichTextAttr
* p
= NULL
;
7122 wxRichTextAttr paraAttr
;
7123 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7125 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
7126 if (!paraAttr
.IsDefault())
7130 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7132 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7134 newPara
->SetAttributes(*p
);
7136 newPara
->AppendChild(object
);
7137 action
->GetNewParagraphs().AppendChild(newPara
);
7138 action
->GetNewParagraphs().UpdateRanges();
7140 action
->GetNewParagraphs().SetPartialParagraph(true);
7142 action
->SetPosition(pos
);
7144 // Set the range we'll need to delete in Undo
7145 action
->SetRange(wxRichTextRange(pos
, pos
));
7147 buffer
->SubmitAction(action
);
7149 wxRichTextObject
* obj
= GetLeafObjectAtPosition(pos
);
7153 wxRichTextField
* wxRichTextParagraphLayoutBox::InsertFieldWithUndo(wxRichTextBuffer
* buffer
, long pos
, const wxString
& fieldType
,
7154 const wxRichTextProperties
& properties
,
7155 wxRichTextCtrl
* ctrl
, int flags
,
7156 const wxRichTextAttr
& textAttr
)
7158 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Field"), wxRICHTEXT_INSERT
, buffer
, this, ctrl
, false);
7160 wxRichTextAttr
* p
= NULL
;
7161 wxRichTextAttr paraAttr
;
7162 if (flags
& wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
)
7164 paraAttr
= GetStyleForNewParagraph(buffer
, pos
);
7165 if (!paraAttr
.IsDefault())
7169 wxRichTextAttr
attr(buffer
->GetDefaultStyle());
7171 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(this, & attr
);
7173 newPara
->SetAttributes(*p
);
7175 wxRichTextField
* fieldObject
= new wxRichTextField();
7176 fieldObject
->wxRichTextObject::SetProperties(properties
);
7177 fieldObject
->SetFieldType(fieldType
);
7178 fieldObject
->SetAttributes(textAttr
);
7179 newPara
->AppendChild(fieldObject
);
7180 action
->GetNewParagraphs().AppendChild(newPara
);
7181 action
->GetNewParagraphs().UpdateRanges();
7182 action
->GetNewParagraphs().SetPartialParagraph(true);
7183 action
->SetPosition(pos
);
7185 // Set the range we'll need to delete in Undo
7186 action
->SetRange(wxRichTextRange(pos
, pos
));
7188 buffer
->SubmitAction(action
);
7190 wxRichTextField
* obj
= wxDynamicCast(GetLeafObjectAtPosition(pos
), wxRichTextField
);
7194 /// Get the style that is appropriate for a new paragraph at this position.
7195 /// If the previous paragraph has a paragraph style name, look up the next-paragraph
7197 wxRichTextAttr
wxRichTextParagraphLayoutBox::GetStyleForNewParagraph(wxRichTextBuffer
* buffer
, long pos
, bool caretPosition
, bool lookUpNewParaStyle
) const
7199 wxRichTextParagraph
* para
= GetParagraphAtPosition(pos
, caretPosition
);
7202 wxRichTextAttr attr
;
7203 bool foundAttributes
= false;
7205 // Look for a matching paragraph style
7206 if (lookUpNewParaStyle
&& !para
->GetAttributes().GetParagraphStyleName().IsEmpty() && buffer
->GetStyleSheet())
7208 wxRichTextParagraphStyleDefinition
* paraDef
= buffer
->GetStyleSheet()->FindParagraphStyle(para
->GetAttributes().GetParagraphStyleName());
7211 // If we're not at the end of the paragraph, then we apply THIS style, and not the designated next style.
7212 if (para
->GetRange().GetEnd() == pos
&& !paraDef
->GetNextStyle().IsEmpty())
7214 wxRichTextParagraphStyleDefinition
* nextParaDef
= buffer
->GetStyleSheet()->FindParagraphStyle(paraDef
->GetNextStyle());
7217 foundAttributes
= true;
7218 attr
= nextParaDef
->GetStyleMergedWithBase(buffer
->GetStyleSheet());
7222 // If we didn't find the 'next style', use this style instead.
7223 if (!foundAttributes
)
7225 foundAttributes
= true;
7226 attr
= paraDef
->GetStyleMergedWithBase(buffer
->GetStyleSheet());
7231 // Also apply list style if present
7232 if (lookUpNewParaStyle
&& !para
->GetAttributes().GetListStyleName().IsEmpty() && buffer
->GetStyleSheet())
7234 wxRichTextListStyleDefinition
* listDef
= buffer
->GetStyleSheet()->FindListStyle(para
->GetAttributes().GetListStyleName());
7237 int thisIndent
= para
->GetAttributes().GetLeftIndent();
7238 int thisLevel
= para
->GetAttributes().HasOutlineLevel() ? para
->GetAttributes().GetOutlineLevel() : listDef
->FindLevelForIndent(thisIndent
);
7240 // Apply the overall list style, and item style for this level
7241 wxRichTextAttr
listStyle(listDef
->GetCombinedStyleForLevel(thisLevel
, buffer
->GetStyleSheet()));
7242 wxRichTextApplyStyle(attr
, listStyle
);
7243 attr
.SetOutlineLevel(thisLevel
);
7244 if (para
->GetAttributes().HasBulletNumber())
7245 attr
.SetBulletNumber(para
->GetAttributes().GetBulletNumber());
7249 if (!foundAttributes
)
7251 attr
= para
->GetAttributes();
7252 int flags
= attr
.GetFlags();
7254 // Eliminate character styles
7255 flags
&= ( (~ wxTEXT_ATTR_FONT
) |
7256 (~ wxTEXT_ATTR_TEXT_COLOUR
) |
7257 (~ wxTEXT_ATTR_BACKGROUND_COLOUR
) );
7258 attr
.SetFlags(flags
);
7264 return wxRichTextAttr();
7267 /// Submit command to delete this range
7268 bool wxRichTextBuffer::DeleteRangeWithUndo(const wxRichTextRange
& range
, wxRichTextCtrl
* ctrl
)
7270 return ctrl
->GetFocusObject()->DeleteRangeWithUndo(range
, ctrl
, this);
7273 /// Submit command to delete this range
7274 bool wxRichTextParagraphLayoutBox::DeleteRangeWithUndo(const wxRichTextRange
& range
, wxRichTextCtrl
* ctrl
, wxRichTextBuffer
* buffer
)
7276 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Delete"), wxRICHTEXT_DELETE
, buffer
, this, ctrl
);
7278 action
->SetPosition(ctrl
->GetCaretPosition());
7280 // Set the range to delete
7281 action
->SetRange(range
);
7283 // Copy the fragment that we'll need to restore in Undo
7284 CopyFragment(range
, action
->GetOldParagraphs());
7286 // See if we're deleting a paragraph marker, in which case we need to
7287 // make a note not to copy the attributes from the 2nd paragraph to the 1st.
7288 if (range
.GetStart() == range
.GetEnd())
7290 wxRichTextParagraph
* para
= GetParagraphAtPosition(range
.GetStart());
7291 if (para
&& para
->GetRange().GetEnd() == range
.GetEnd())
7293 wxRichTextParagraph
* nextPara
= GetParagraphAtPosition(range
.GetStart()+1);
7294 if (nextPara
&& nextPara
!= para
)
7296 action
->GetOldParagraphs().GetChildren().GetFirst()->GetData()->SetAttributes(nextPara
->GetAttributes());
7297 action
->GetOldParagraphs().GetAttributes().SetFlags(action
->GetOldParagraphs().GetAttributes().GetFlags() | wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE
);
7302 buffer
->SubmitAction(action
);
7307 /// Collapse undo/redo commands
7308 bool wxRichTextBuffer::BeginBatchUndo(const wxString
& cmdName
)
7310 if (m_batchedCommandDepth
== 0)
7312 wxASSERT(m_batchedCommand
== NULL
);
7313 if (m_batchedCommand
)
7315 GetCommandProcessor()->Store(m_batchedCommand
);
7317 m_batchedCommand
= new wxRichTextCommand(cmdName
);
7320 m_batchedCommandDepth
++;
7325 /// Collapse undo/redo commands
7326 bool wxRichTextBuffer::EndBatchUndo()
7328 m_batchedCommandDepth
--;
7330 wxASSERT(m_batchedCommandDepth
>= 0);
7331 wxASSERT(m_batchedCommand
!= NULL
);
7333 if (m_batchedCommandDepth
== 0)
7335 GetCommandProcessor()->Store(m_batchedCommand
);
7336 m_batchedCommand
= NULL
;
7342 /// Submit immediately, or delay according to whether collapsing is on
7343 bool wxRichTextBuffer::SubmitAction(wxRichTextAction
* action
)
7345 if (action
&& !action
->GetNewParagraphs().IsEmpty())
7346 PrepareContent(action
->GetNewParagraphs());
7348 if (BatchingUndo() && m_batchedCommand
&& !SuppressingUndo())
7350 wxRichTextCommand
* cmd
= new wxRichTextCommand(action
->GetName());
7351 cmd
->AddAction(action
);
7353 cmd
->GetActions().Clear();
7356 m_batchedCommand
->AddAction(action
);
7360 wxRichTextCommand
* cmd
= new wxRichTextCommand(action
->GetName());
7361 cmd
->AddAction(action
);
7363 // Only store it if we're not suppressing undo.
7364 return GetCommandProcessor()->Submit(cmd
, !SuppressingUndo());
7370 /// Begin suppressing undo/redo commands.
7371 bool wxRichTextBuffer::BeginSuppressUndo()
7378 /// End suppressing undo/redo commands.
7379 bool wxRichTextBuffer::EndSuppressUndo()
7386 /// Begin using a style
7387 bool wxRichTextBuffer::BeginStyle(const wxRichTextAttr
& style
)
7389 wxRichTextAttr
newStyle(GetDefaultStyle());
7391 // Save the old default style
7392 m_attributeStack
.Append((wxObject
*) new wxRichTextAttr(GetDefaultStyle()));
7394 wxRichTextApplyStyle(newStyle
, style
);
7395 newStyle
.SetFlags(style
.GetFlags()|newStyle
.GetFlags());
7397 SetDefaultStyle(newStyle
);
7403 bool wxRichTextBuffer::EndStyle()
7405 if (!m_attributeStack
.GetFirst())
7407 wxLogDebug(_("Too many EndStyle calls!"));
7411 wxList::compatibility_iterator node
= m_attributeStack
.GetLast();
7412 wxRichTextAttr
* attr
= (wxRichTextAttr
*)node
->GetData();
7413 m_attributeStack
.Erase(node
);
7415 SetDefaultStyle(*attr
);
7422 bool wxRichTextBuffer::EndAllStyles()
7424 while (m_attributeStack
.GetCount() != 0)
7429 /// Clear the style stack
7430 void wxRichTextBuffer::ClearStyleStack()
7432 for (wxList::compatibility_iterator node
= m_attributeStack
.GetFirst(); node
; node
= node
->GetNext())
7433 delete (wxRichTextAttr
*) node
->GetData();
7434 m_attributeStack
.Clear();
7437 /// Begin using bold
7438 bool wxRichTextBuffer::BeginBold()
7440 wxRichTextAttr attr
;
7441 attr
.SetFontWeight(wxFONTWEIGHT_BOLD
);
7443 return BeginStyle(attr
);
7446 /// Begin using italic
7447 bool wxRichTextBuffer::BeginItalic()
7449 wxRichTextAttr attr
;
7450 attr
.SetFontStyle(wxFONTSTYLE_ITALIC
);
7452 return BeginStyle(attr
);
7455 /// Begin using underline
7456 bool wxRichTextBuffer::BeginUnderline()
7458 wxRichTextAttr attr
;
7459 attr
.SetFontUnderlined(true);
7461 return BeginStyle(attr
);
7464 /// Begin using point size
7465 bool wxRichTextBuffer::BeginFontSize(int pointSize
)
7467 wxRichTextAttr attr
;
7468 attr
.SetFontSize(pointSize
);
7470 return BeginStyle(attr
);
7473 /// Begin using this font
7474 bool wxRichTextBuffer::BeginFont(const wxFont
& font
)
7476 wxRichTextAttr attr
;
7479 return BeginStyle(attr
);
7482 /// Begin using this colour
7483 bool wxRichTextBuffer::BeginTextColour(const wxColour
& colour
)
7485 wxRichTextAttr attr
;
7486 attr
.SetFlags(wxTEXT_ATTR_TEXT_COLOUR
);
7487 attr
.SetTextColour(colour
);
7489 return BeginStyle(attr
);
7492 /// Begin using alignment
7493 bool wxRichTextBuffer::BeginAlignment(wxTextAttrAlignment alignment
)
7495 wxRichTextAttr attr
;
7496 attr
.SetFlags(wxTEXT_ATTR_ALIGNMENT
);
7497 attr
.SetAlignment(alignment
);
7499 return BeginStyle(attr
);
7502 /// Begin left indent
7503 bool wxRichTextBuffer::BeginLeftIndent(int leftIndent
, int leftSubIndent
)
7505 wxRichTextAttr attr
;
7506 attr
.SetFlags(wxTEXT_ATTR_LEFT_INDENT
);
7507 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7509 return BeginStyle(attr
);
7512 /// Begin right indent
7513 bool wxRichTextBuffer::BeginRightIndent(int rightIndent
)
7515 wxRichTextAttr attr
;
7516 attr
.SetFlags(wxTEXT_ATTR_RIGHT_INDENT
);
7517 attr
.SetRightIndent(rightIndent
);
7519 return BeginStyle(attr
);
7522 /// Begin paragraph spacing
7523 bool wxRichTextBuffer::BeginParagraphSpacing(int before
, int after
)
7527 flags
|= wxTEXT_ATTR_PARA_SPACING_BEFORE
;
7529 flags
|= wxTEXT_ATTR_PARA_SPACING_AFTER
;
7531 wxRichTextAttr attr
;
7532 attr
.SetFlags(flags
);
7533 attr
.SetParagraphSpacingBefore(before
);
7534 attr
.SetParagraphSpacingAfter(after
);
7536 return BeginStyle(attr
);
7539 /// Begin line spacing
7540 bool wxRichTextBuffer::BeginLineSpacing(int lineSpacing
)
7542 wxRichTextAttr attr
;
7543 attr
.SetFlags(wxTEXT_ATTR_LINE_SPACING
);
7544 attr
.SetLineSpacing(lineSpacing
);
7546 return BeginStyle(attr
);
7549 /// Begin numbered bullet
7550 bool wxRichTextBuffer::BeginNumberedBullet(int bulletNumber
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7552 wxRichTextAttr attr
;
7553 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7554 attr
.SetBulletStyle(bulletStyle
);
7555 attr
.SetBulletNumber(bulletNumber
);
7556 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7558 return BeginStyle(attr
);
7561 /// Begin symbol bullet
7562 bool wxRichTextBuffer::BeginSymbolBullet(const wxString
& symbol
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7564 wxRichTextAttr attr
;
7565 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7566 attr
.SetBulletStyle(bulletStyle
);
7567 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7568 attr
.SetBulletText(symbol
);
7570 return BeginStyle(attr
);
7573 /// Begin standard bullet
7574 bool wxRichTextBuffer::BeginStandardBullet(const wxString
& bulletName
, int leftIndent
, int leftSubIndent
, int bulletStyle
)
7576 wxRichTextAttr attr
;
7577 attr
.SetFlags(wxTEXT_ATTR_BULLET_STYLE
|wxTEXT_ATTR_LEFT_INDENT
);
7578 attr
.SetBulletStyle(bulletStyle
);
7579 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
7580 attr
.SetBulletName(bulletName
);
7582 return BeginStyle(attr
);
7585 /// Begin named character style
7586 bool wxRichTextBuffer::BeginCharacterStyle(const wxString
& characterStyle
)
7588 if (GetStyleSheet())
7590 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->FindCharacterStyle(characterStyle
);
7593 wxRichTextAttr attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7594 return BeginStyle(attr
);
7600 /// Begin named paragraph style
7601 bool wxRichTextBuffer::BeginParagraphStyle(const wxString
& paragraphStyle
)
7603 if (GetStyleSheet())
7605 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->FindParagraphStyle(paragraphStyle
);
7608 wxRichTextAttr attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7609 return BeginStyle(attr
);
7615 /// Begin named list style
7616 bool wxRichTextBuffer::BeginListStyle(const wxString
& listStyle
, int level
, int number
)
7618 if (GetStyleSheet())
7620 wxRichTextListStyleDefinition
* def
= GetStyleSheet()->FindListStyle(listStyle
);
7623 wxRichTextAttr
attr(def
->GetCombinedStyleForLevel(level
));
7625 attr
.SetBulletNumber(number
);
7627 return BeginStyle(attr
);
7634 bool wxRichTextBuffer::BeginURL(const wxString
& url
, const wxString
& characterStyle
)
7636 wxRichTextAttr attr
;
7638 if (!characterStyle
.IsEmpty() && GetStyleSheet())
7640 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->FindCharacterStyle(characterStyle
);
7643 attr
= def
->GetStyleMergedWithBase(GetStyleSheet());
7648 return BeginStyle(attr
);
7651 /// Adds a handler to the end
7652 void wxRichTextBuffer::AddHandler(wxRichTextFileHandler
*handler
)
7654 sm_handlers
.Append(handler
);
7657 /// Inserts a handler at the front
7658 void wxRichTextBuffer::InsertHandler(wxRichTextFileHandler
*handler
)
7660 sm_handlers
.Insert( handler
);
7663 /// Removes a handler
7664 bool wxRichTextBuffer::RemoveHandler(const wxString
& name
)
7666 wxRichTextFileHandler
*handler
= FindHandler(name
);
7669 sm_handlers
.DeleteObject(handler
);
7677 /// Finds a handler by filename or, if supplied, type
7678 wxRichTextFileHandler
*wxRichTextBuffer::FindHandlerFilenameOrType(const wxString
& filename
,
7679 wxRichTextFileType imageType
)
7681 if (imageType
!= wxRICHTEXT_TYPE_ANY
)
7682 return FindHandler(imageType
);
7683 else if (!filename
.IsEmpty())
7685 wxString path
, file
, ext
;
7686 wxFileName::SplitPath(filename
, & path
, & file
, & ext
);
7687 return FindHandler(ext
, imageType
);
7694 /// Finds a handler by name
7695 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(const wxString
& name
)
7697 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7700 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7701 if (handler
->GetName().Lower() == name
.Lower()) return handler
;
7703 node
= node
->GetNext();
7708 /// Finds a handler by extension and type
7709 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(const wxString
& extension
, wxRichTextFileType type
)
7711 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7714 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7715 if ( handler
->GetExtension().Lower() == extension
.Lower() &&
7716 (type
== wxRICHTEXT_TYPE_ANY
|| handler
->GetType() == type
) )
7718 node
= node
->GetNext();
7723 /// Finds a handler by type
7724 wxRichTextFileHandler
* wxRichTextBuffer::FindHandler(wxRichTextFileType type
)
7726 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7729 wxRichTextFileHandler
*handler
= (wxRichTextFileHandler
*)node
->GetData();
7730 if (handler
->GetType() == type
) return handler
;
7731 node
= node
->GetNext();
7736 void wxRichTextBuffer::InitStandardHandlers()
7738 if (!FindHandler(wxRICHTEXT_TYPE_TEXT
))
7739 AddHandler(new wxRichTextPlainTextHandler
);
7742 void wxRichTextBuffer::CleanUpHandlers()
7744 wxList::compatibility_iterator node
= sm_handlers
.GetFirst();
7747 wxRichTextFileHandler
* handler
= (wxRichTextFileHandler
*)node
->GetData();
7748 wxList::compatibility_iterator next
= node
->GetNext();
7753 sm_handlers
.Clear();
7756 wxString
wxRichTextBuffer::GetExtWildcard(bool combine
, bool save
, wxArrayInt
* types
)
7763 wxList::compatibility_iterator node
= GetHandlers().GetFirst();
7767 wxRichTextFileHandler
* handler
= (wxRichTextFileHandler
*) node
->GetData();
7768 if (handler
->IsVisible() && ((save
&& handler
->CanSave()) || (!save
&& handler
->CanLoad())))
7773 wildcard
+= wxT(";");
7774 wildcard
+= wxT("*.") + handler
->GetExtension();
7779 wildcard
+= wxT("|");
7780 wildcard
+= handler
->GetName();
7781 wildcard
+= wxT(" ");
7782 wildcard
+= _("files");
7783 wildcard
+= wxT(" (*.");
7784 wildcard
+= handler
->GetExtension();
7785 wildcard
+= wxT(")|*.");
7786 wildcard
+= handler
->GetExtension();
7788 types
->Add(handler
->GetType());
7793 node
= node
->GetNext();
7797 wildcard
= wxT("(") + wildcard
+ wxT(")|") + wildcard
;
7802 bool wxRichTextBuffer::LoadFile(const wxString
& filename
, wxRichTextFileType type
)
7804 wxRichTextFileHandler
* handler
= FindHandlerFilenameOrType(filename
, type
);
7807 SetDefaultStyle(wxRichTextAttr());
7808 handler
->SetFlags(GetHandlerFlags());
7809 bool success
= handler
->LoadFile(this, filename
);
7810 Invalidate(wxRICHTEXT_ALL
);
7818 bool wxRichTextBuffer::SaveFile(const wxString
& filename
, wxRichTextFileType type
)
7820 wxRichTextFileHandler
* handler
= FindHandlerFilenameOrType(filename
, type
);
7823 handler
->SetFlags(GetHandlerFlags());
7824 return handler
->SaveFile(this, filename
);
7830 /// Load from a stream
7831 bool wxRichTextBuffer::LoadFile(wxInputStream
& stream
, wxRichTextFileType type
)
7833 wxRichTextFileHandler
* handler
= FindHandler(type
);
7836 SetDefaultStyle(wxRichTextAttr());
7837 handler
->SetFlags(GetHandlerFlags());
7838 bool success
= handler
->LoadFile(this, stream
);
7839 Invalidate(wxRICHTEXT_ALL
);
7846 /// Save to a stream
7847 bool wxRichTextBuffer::SaveFile(wxOutputStream
& stream
, wxRichTextFileType type
)
7849 wxRichTextFileHandler
* handler
= FindHandler(type
);
7852 handler
->SetFlags(GetHandlerFlags());
7853 return handler
->SaveFile(this, stream
);
7859 /// Copy the range to the clipboard
7860 bool wxRichTextBuffer::CopyToClipboard(const wxRichTextRange
& range
)
7862 bool success
= false;
7863 wxRichTextParagraphLayoutBox
* container
= this;
7864 if (GetRichTextCtrl())
7865 container
= GetRichTextCtrl()->GetFocusObject();
7867 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
7869 if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open())
7871 wxTheClipboard
->Clear();
7873 // Add composite object
7875 wxDataObjectComposite
* compositeObject
= new wxDataObjectComposite();
7878 wxString text
= container
->GetTextForRange(range
);
7881 text
= wxTextFile::Translate(text
, wxTextFileType_Dos
);
7884 compositeObject
->Add(new wxTextDataObject(text
), false /* not preferred */);
7887 // Add rich text buffer data object. This needs the XML handler to be present.
7889 if (FindHandler(wxRICHTEXT_TYPE_XML
))
7891 wxRichTextBuffer
* richTextBuf
= new wxRichTextBuffer
;
7892 container
->CopyFragment(range
, *richTextBuf
);
7894 compositeObject
->Add(new wxRichTextBufferDataObject(richTextBuf
), true /* preferred */);
7897 if (wxTheClipboard
->SetData(compositeObject
))
7900 wxTheClipboard
->Close();
7909 /// Paste the clipboard content to the buffer
7910 bool wxRichTextBuffer::PasteFromClipboard(long position
)
7912 bool success
= false;
7913 wxRichTextParagraphLayoutBox
* container
= this;
7914 if (GetRichTextCtrl())
7915 container
= GetRichTextCtrl()->GetFocusObject();
7917 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
7918 if (CanPasteFromClipboard())
7920 if (wxTheClipboard
->Open())
7922 if (wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())))
7924 wxRichTextBufferDataObject data
;
7925 wxTheClipboard
->GetData(data
);
7926 wxRichTextBuffer
* richTextBuffer
= data
.GetRichTextBuffer();
7929 container
->InsertParagraphsWithUndo(this, position
+1, *richTextBuffer
, GetRichTextCtrl(), 0);
7930 if (GetRichTextCtrl())
7931 GetRichTextCtrl()->ShowPosition(position
+ richTextBuffer
->GetOwnRange().GetEnd());
7932 delete richTextBuffer
;
7935 else if (wxTheClipboard
->IsSupported(wxDF_TEXT
)
7937 || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
)
7941 wxTextDataObject data
;
7942 wxTheClipboard
->GetData(data
);
7943 wxString
text(data
.GetText());
7946 text2
.Alloc(text
.Length()+1);
7948 for (i
= 0; i
< text
.Length(); i
++)
7950 wxChar ch
= text
[i
];
7951 if (ch
!= wxT('\r'))
7955 wxString text2
= text
;
7957 container
->InsertTextWithUndo(this, position
+1, text2
, GetRichTextCtrl(), wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE
);
7959 if (GetRichTextCtrl())
7960 GetRichTextCtrl()->ShowPosition(position
+ text2
.Length());
7964 else if (wxTheClipboard
->IsSupported(wxDF_BITMAP
))
7966 wxBitmapDataObject data
;
7967 wxTheClipboard
->GetData(data
);
7968 wxBitmap
bitmap(data
.GetBitmap());
7969 wxImage
image(bitmap
.ConvertToImage());
7971 wxRichTextAction
* action
= new wxRichTextAction(NULL
, _("Insert Image"), wxRICHTEXT_INSERT
, this, container
, GetRichTextCtrl(), false);
7973 action
->GetNewParagraphs().AddImage(image
);
7975 if (action
->GetNewParagraphs().GetChildCount() == 1)
7976 action
->GetNewParagraphs().SetPartialParagraph(true);
7978 action
->SetPosition(position
+1);
7980 // Set the range we'll need to delete in Undo
7981 action
->SetRange(wxRichTextRange(position
+1, position
+1));
7983 SubmitAction(action
);
7987 wxTheClipboard
->Close();
7991 wxUnusedVar(position
);
7996 /// Can we paste from the clipboard?
7997 bool wxRichTextBuffer::CanPasteFromClipboard() const
7999 bool canPaste
= false;
8000 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
8001 if (!wxTheClipboard
->IsOpened() && wxTheClipboard
->Open())
8003 if (wxTheClipboard
->IsSupported(wxDF_TEXT
)
8005 || wxTheClipboard
->IsSupported(wxDF_UNICODETEXT
)
8007 || wxTheClipboard
->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())) ||
8008 wxTheClipboard
->IsSupported(wxDF_BITMAP
))
8012 wxTheClipboard
->Close();
8018 /// Dumps contents of buffer for debugging purposes
8019 void wxRichTextBuffer::Dump()
8023 wxStringOutputStream
stream(& text
);
8024 wxTextOutputStream
textStream(stream
);
8031 /// Add an event handler
8032 bool wxRichTextBuffer::AddEventHandler(wxEvtHandler
* handler
)
8034 m_eventHandlers
.Append(handler
);
8038 /// Remove an event handler
8039 bool wxRichTextBuffer::RemoveEventHandler(wxEvtHandler
* handler
, bool deleteHandler
)
8041 wxList::compatibility_iterator node
= m_eventHandlers
.Find(handler
);
8044 m_eventHandlers
.Erase(node
);
8054 /// Clear event handlers
8055 void wxRichTextBuffer::ClearEventHandlers()
8057 m_eventHandlers
.Clear();
8060 /// Send event to event handlers. If sendToAll is true, will send to all event handlers,
8061 /// otherwise will stop at the first successful one.
8062 bool wxRichTextBuffer::SendEvent(wxEvent
& event
, bool sendToAll
)
8064 bool success
= false;
8065 for (wxList::compatibility_iterator node
= m_eventHandlers
.GetFirst(); node
; node
= node
->GetNext())
8067 wxEvtHandler
* handler
= (wxEvtHandler
*) node
->GetData();
8068 if (handler
->ProcessEvent(event
))
8078 /// Set style sheet and notify of the change
8079 bool wxRichTextBuffer::SetStyleSheetAndNotify(wxRichTextStyleSheet
* sheet
)
8081 wxRichTextStyleSheet
* oldSheet
= GetStyleSheet();
8083 wxWindowID winid
= wxID_ANY
;
8084 if (GetRichTextCtrl())
8085 winid
= GetRichTextCtrl()->GetId();
8087 wxRichTextEvent
event(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACING
, winid
);
8088 event
.SetEventObject(GetRichTextCtrl());
8089 event
.SetContainer(GetRichTextCtrl()->GetFocusObject());
8090 event
.SetOldStyleSheet(oldSheet
);
8091 event
.SetNewStyleSheet(sheet
);
8094 if (SendEvent(event
) && !event
.IsAllowed())
8096 if (sheet
!= oldSheet
)
8102 if (oldSheet
&& oldSheet
!= sheet
)
8105 SetStyleSheet(sheet
);
8107 event
.SetEventType(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACED
);
8108 event
.SetOldStyleSheet(NULL
);
8111 return SendEvent(event
);
8114 /// Set renderer, deleting old one
8115 void wxRichTextBuffer::SetRenderer(wxRichTextRenderer
* renderer
)
8119 sm_renderer
= renderer
;
8122 /// Hit-testing: returns a flag indicating hit test details, plus
8123 /// information about position
8124 int wxRichTextBuffer::HitTest(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxPoint
& pt
, long& textPosition
, wxRichTextObject
** obj
, wxRichTextObject
** contextObj
, int flags
)
8126 int ret
= wxRichTextParagraphLayoutBox::HitTest(dc
, context
, pt
, textPosition
, obj
, contextObj
, flags
);
8127 if (ret
!= wxRICHTEXT_HITTEST_NONE
)
8133 textPosition
= m_ownRange
.GetEnd()-1;
8136 return wxRICHTEXT_HITTEST_AFTER
|wxRICHTEXT_HITTEST_OUTSIDE
;
8140 bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxRichTextAttr
& bulletAttr
, const wxRect
& rect
)
8142 if (bulletAttr
.GetTextColour().IsOk())
8144 wxCheckSetPen(dc
, wxPen(bulletAttr
.GetTextColour()));
8145 wxCheckSetBrush(dc
, wxBrush(bulletAttr
.GetTextColour()));
8149 wxCheckSetPen(dc
, *wxBLACK_PEN
);
8150 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
8154 if (bulletAttr
.HasFont())
8156 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(bulletAttr
);
8159 font
= (*wxNORMAL_FONT
);
8161 wxCheckSetFont(dc
, font
);
8163 int charHeight
= dc
.GetCharHeight();
8165 int bulletWidth
= (int) (((float) charHeight
) * wxRichTextBuffer::GetBulletProportion());
8166 int bulletHeight
= bulletWidth
;
8170 // Calculate the top position of the character (as opposed to the whole line height)
8171 int y
= rect
.y
+ (rect
.height
- charHeight
);
8173 // Calculate where the bullet should be positioned
8174 y
= y
+ (charHeight
+1)/2 - (bulletHeight
+1)/2;
8176 // The margin between a bullet and text.
8177 int margin
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer::GetBulletRightMargin());
8179 if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
)
8180 x
= rect
.x
+ rect
.width
- bulletWidth
- margin
;
8181 else if (bulletAttr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
)
8182 x
= x
+ (rect
.width
)/2 - bulletWidth
/2;
8184 if (bulletAttr
.GetBulletName() == wxT("standard/square"))
8186 dc
.DrawRectangle(x
, y
, bulletWidth
, bulletHeight
);
8188 else if (bulletAttr
.GetBulletName() == wxT("standard/diamond"))
8191 pts
[0].x
= x
; pts
[0].y
= y
+ bulletHeight
/2;
8192 pts
[1].x
= x
+ bulletWidth
/2; pts
[1].y
= y
;
8193 pts
[2].x
= x
+ bulletWidth
; pts
[2].y
= y
+ bulletHeight
/2;
8194 pts
[3].x
= x
+ bulletWidth
/2; pts
[3].y
= y
+ bulletHeight
;
8196 dc
.DrawPolygon(4, pts
);
8198 else if (bulletAttr
.GetBulletName() == wxT("standard/triangle"))
8201 pts
[0].x
= x
; pts
[0].y
= y
;
8202 pts
[1].x
= x
+ bulletWidth
; pts
[1].y
= y
+ bulletHeight
/2;
8203 pts
[2].x
= x
; pts
[2].y
= y
+ bulletHeight
;
8205 dc
.DrawPolygon(3, pts
);
8207 else if (bulletAttr
.GetBulletName() == wxT("standard/circle-outline"))
8209 wxCheckSetBrush(dc
, *wxTRANSPARENT_BRUSH
);
8210 dc
.DrawEllipse(x
, y
, bulletWidth
, bulletHeight
);
8212 else // "standard/circle", and catch-all
8214 dc
.DrawEllipse(x
, y
, bulletWidth
, bulletHeight
);
8220 bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph
* paragraph
, wxDC
& dc
, const wxRichTextAttr
& attr
, const wxRect
& rect
, const wxString
& text
)
8225 if ((attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
) && !attr
.GetBulletFont().IsEmpty() && attr
.HasFont())
8227 wxRichTextAttr fontAttr
;
8228 fontAttr
.SetFontSize(attr
.GetFontSize());
8229 fontAttr
.SetFontStyle(attr
.GetFontStyle());
8230 fontAttr
.SetFontWeight(attr
.GetFontWeight());
8231 fontAttr
.SetFontUnderlined(attr
.GetFontUnderlined());
8232 fontAttr
.SetFontFaceName(attr
.GetBulletFont());
8233 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(fontAttr
);
8235 else if (attr
.HasFont())
8236 font
= paragraph
->GetBuffer()->GetFontTable().FindFont(attr
);
8238 font
= (*wxNORMAL_FONT
);
8240 wxCheckSetFont(dc
, font
);
8242 if (attr
.GetTextColour().IsOk())
8243 dc
.SetTextForeground(attr
.GetTextColour());
8245 dc
.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT
);
8247 int charHeight
= dc
.GetCharHeight();
8249 dc
.GetTextExtent(text
, & tw
, & th
);
8253 // Calculate the top position of the character (as opposed to the whole line height)
8254 int y
= rect
.y
+ (rect
.height
- charHeight
);
8256 // The margin between a bullet and text.
8257 int margin
= paragraph
->ConvertTenthsMMToPixels(dc
, wxRichTextBuffer::GetBulletRightMargin());
8259 if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT
)
8260 x
= (rect
.x
+ rect
.width
) - tw
- margin
;
8261 else if (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE
)
8262 x
= x
+ (rect
.width
)/2 - tw
/2;
8264 dc
.DrawText(text
, x
, y
);
8272 bool wxRichTextStdRenderer::DrawBitmapBullet(wxRichTextParagraph
* WXUNUSED(paragraph
), wxDC
& WXUNUSED(dc
), const wxRichTextAttr
& WXUNUSED(attr
), const wxRect
& WXUNUSED(rect
))
8274 // Currently unimplemented. The intention is to store bitmaps by name in a media store associated
8275 // with the buffer. The store will allow retrieval from memory, disk or other means.
8279 /// Enumerate the standard bullet names currently supported
8280 bool wxRichTextStdRenderer::EnumerateStandardBulletNames(wxArrayString
& bulletNames
)
8282 bulletNames
.Add(wxTRANSLATE("standard/circle"));
8283 bulletNames
.Add(wxTRANSLATE("standard/circle-outline"));
8284 bulletNames
.Add(wxTRANSLATE("standard/square"));
8285 bulletNames
.Add(wxTRANSLATE("standard/diamond"));
8286 bulletNames
.Add(wxTRANSLATE("standard/triangle"));
8295 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBox
, wxRichTextParagraphLayoutBox
)
8297 wxRichTextBox::wxRichTextBox(wxRichTextObject
* parent
):
8298 wxRichTextParagraphLayoutBox(parent
)
8303 bool wxRichTextBox::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8308 // TODO: if the active object in the control, draw an indication.
8309 // We need to add the concept of active object, and not just focus object,
8310 // so we can apply commands (properties, delete, ...) to objects such as text boxes and images.
8311 // Ultimately we would like to be able to interactively resize an active object
8312 // using drag handles.
8313 return wxRichTextParagraphLayoutBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8317 void wxRichTextBox::Copy(const wxRichTextBox
& obj
)
8319 wxRichTextParagraphLayoutBox::Copy(obj
);
8322 // Edit properties via a GUI
8323 bool wxRichTextBox::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8325 wxRichTextObjectPropertiesDialog
boxDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Box Properties"));
8326 boxDlg
.SetAttributes(GetAttributes());
8328 if (boxDlg
.ShowModal() == wxID_OK
)
8330 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
8331 // indeterminate in the object.
8332 boxDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
8343 IMPLEMENT_DYNAMIC_CLASS(wxRichTextField
, wxRichTextParagraphLayoutBox
)
8345 wxRichTextField::wxRichTextField(const wxString
& fieldType
, wxRichTextObject
* parent
):
8346 wxRichTextParagraphLayoutBox(parent
)
8348 SetFieldType(fieldType
);
8352 bool wxRichTextField::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8357 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8358 if (fieldType
&& fieldType
->Draw(this, dc
, context
, range
, selection
, rect
, descent
, style
))
8361 // Fallback; but don't draw guidelines.
8362 style
&= ~wxRICHTEXT_DRAW_GUIDELINES
;
8363 return wxRichTextParagraphLayoutBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8366 bool wxRichTextField::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& parentRect
, int style
)
8368 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8369 if (fieldType
&& fieldType
->Layout(this, dc
, context
, rect
, parentRect
, style
))
8373 return wxRichTextParagraphLayoutBox::Layout(dc
, context
, rect
, parentRect
, style
);
8376 bool wxRichTextField::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
8378 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8380 return fieldType
->GetRangeSize((wxRichTextField
*) this, range
, size
, descent
, dc
, context
, flags
, position
, partialExtents
);
8382 return wxRichTextParagraphLayoutBox::GetRangeSize(range
, size
, descent
, dc
, context
, flags
, position
, partialExtents
);
8386 void wxRichTextField::CalculateRange(long start
, long& end
)
8389 wxRichTextParagraphLayoutBox::CalculateRange(start
, end
);
8391 wxRichTextObject::CalculateRange(start
, end
);
8395 void wxRichTextField::Copy(const wxRichTextField
& obj
)
8397 wxRichTextParagraphLayoutBox::Copy(obj
);
8402 // Edit properties via a GUI
8403 bool wxRichTextField::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8405 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8407 return fieldType
->EditProperties(this, parent
, buffer
);
8412 bool wxRichTextField::CanEditProperties() const
8414 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8416 return fieldType
->CanEditProperties((wxRichTextField
*) this);
8421 wxString
wxRichTextField::GetPropertiesMenuLabel() const
8423 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8425 return fieldType
->GetPropertiesMenuLabel((wxRichTextField
*) this);
8427 return wxEmptyString
;
8430 bool wxRichTextField::UpdateField()
8432 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8434 return fieldType
->UpdateField((wxRichTextField
*) this);
8439 bool wxRichTextField::IsTopLevel() const
8441 wxRichTextFieldType
* fieldType
= wxRichTextBuffer::FindFieldType(GetFieldType());
8443 return fieldType
->IsTopLevel((wxRichTextField
*) this);
8448 IMPLEMENT_CLASS(wxRichTextFieldType
, wxObject
)
8450 IMPLEMENT_CLASS(wxRichTextFieldTypeStandard
, wxRichTextFieldType
)
8452 wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString
& name
, const wxString
& label
, int displayStyle
)
8458 SetDisplayStyle(displayStyle
);
8461 wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString
& name
, const wxBitmap
& bitmap
, int displayStyle
)
8467 SetDisplayStyle(displayStyle
);
8470 void wxRichTextFieldTypeStandard::Init()
8472 m_displayStyle
= wxRICHTEXT_FIELD_STYLE_RECTANGLE
;
8473 m_font
= wxFont(6, wxFONTFAMILY_SWISS
, wxFONTSTYLE_NORMAL
, wxFONTWEIGHT_NORMAL
);
8474 m_textColour
= *wxWHITE
;
8475 m_borderColour
= *wxBLACK
;
8476 m_backgroundColour
= *wxBLACK
;
8477 m_verticalPadding
= 1;
8478 m_horizontalPadding
= 3;
8479 m_horizontalMargin
= 2;
8480 m_verticalMargin
= 0;
8483 void wxRichTextFieldTypeStandard::Copy(const wxRichTextFieldTypeStandard
& field
)
8485 wxRichTextFieldType::Copy(field
);
8487 m_label
= field
.m_label
;
8488 m_displayStyle
= field
.m_displayStyle
;
8489 m_font
= field
.m_font
;
8490 m_textColour
= field
.m_textColour
;
8491 m_borderColour
= field
.m_borderColour
;
8492 m_backgroundColour
= field
.m_backgroundColour
;
8493 m_verticalPadding
= field
.m_verticalPadding
;
8494 m_horizontalPadding
= field
.m_horizontalPadding
;
8495 m_horizontalMargin
= field
.m_horizontalMargin
;
8496 m_bitmap
= field
.m_bitmap
;
8499 bool wxRichTextFieldTypeStandard::Draw(wxRichTextField
* obj
, wxDC
& dc
, wxRichTextDrawingContext
& WXUNUSED(context
), const wxRichTextRange
& WXUNUSED(range
), const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int WXUNUSED(style
))
8501 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_COMPOSITE
)
8502 return false; // USe default composite drawing
8503 else // if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_RECTANGLE || m_displayStyle == wxRICHTEXT_FIELD_STYLE_NOBORDER)
8507 wxPen
borderPen(m_borderColour
, 1, wxSOLID
);
8508 wxBrush
backgroundBrush(m_backgroundColour
);
8509 wxColour
textColour(m_textColour
);
8511 if (selection
.WithinSelection(obj
->GetRange().GetStart(), obj
))
8513 wxColour
highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
));
8514 wxColour
highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
));
8516 borderPen
= wxPen(highlightTextColour
, 1, wxSOLID
);
8517 backgroundBrush
= wxBrush(highlightColour
);
8519 wxCheckSetBrush(dc
, backgroundBrush
);
8520 wxCheckSetPen(dc
, wxPen(highlightColour
, 1, wxSOLID
));
8521 dc
.DrawRectangle(rect
);
8524 if (m_displayStyle
!= wxRICHTEXT_FIELD_STYLE_NO_BORDER
)
8527 // objectRect is the area where the content is drawn, after margins around it have been taken into account
8528 wxRect objectRect
= wxRect(wxPoint(rect
.x
+ m_horizontalMargin
, rect
.y
+ wxMax(0, rect
.height
- descent
- obj
->GetCachedSize().y
)),
8529 wxSize(obj
->GetCachedSize().x
- 2*m_horizontalMargin
- borderSize
, obj
->GetCachedSize().y
));
8531 // clientArea is where the text is actually written
8532 wxRect clientArea
= objectRect
;
8534 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_RECTANGLE
)
8536 dc
.SetPen(borderPen
);
8537 dc
.SetBrush(backgroundBrush
);
8538 dc
.DrawRoundedRectangle(objectRect
, 4.0);
8540 else if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_START_TAG
)
8542 int arrowLength
= objectRect
.height
/2;
8543 clientArea
.width
-= (arrowLength
- m_horizontalPadding
);
8546 pts
[0].x
= objectRect
.x
; pts
[0].y
= objectRect
.y
;
8547 pts
[1].x
= objectRect
.x
+ objectRect
.width
- arrowLength
; pts
[1].y
= objectRect
.y
;
8548 pts
[2].x
= objectRect
.x
+ objectRect
.width
; pts
[2].y
= objectRect
.y
+ (objectRect
.height
/2);
8549 pts
[3].x
= objectRect
.x
+ objectRect
.width
- arrowLength
; pts
[3].y
= objectRect
.y
+ objectRect
.height
;
8550 pts
[4].x
= objectRect
.x
; pts
[4].y
= objectRect
.y
+ objectRect
.height
;
8551 dc
.SetPen(borderPen
);
8552 dc
.SetBrush(backgroundBrush
);
8553 dc
.DrawPolygon(5, pts
);
8555 else if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_END_TAG
)
8557 int arrowLength
= objectRect
.height
/2;
8558 clientArea
.width
-= (arrowLength
- m_horizontalPadding
);
8559 clientArea
.x
+= (arrowLength
- m_horizontalPadding
);
8562 pts
[0].x
= objectRect
.x
+ objectRect
.width
; pts
[0].y
= objectRect
.y
;
8563 pts
[1].x
= objectRect
.x
+ arrowLength
; pts
[1].y
= objectRect
.y
;
8564 pts
[2].x
= objectRect
.x
; pts
[2].y
= objectRect
.y
+ (objectRect
.height
/2);
8565 pts
[3].x
= objectRect
.x
+ arrowLength
; pts
[3].y
= objectRect
.y
+ objectRect
.height
;
8566 pts
[4].x
= objectRect
.x
+ objectRect
.width
; pts
[4].y
= objectRect
.y
+ objectRect
.height
;
8567 dc
.SetPen(borderPen
);
8568 dc
.SetBrush(backgroundBrush
);
8569 dc
.DrawPolygon(5, pts
);
8572 if (m_bitmap
.IsOk())
8574 int x
= clientArea
.x
+ (clientArea
.width
- m_bitmap
.GetWidth())/2;
8575 int y
= clientArea
.y
+ m_verticalPadding
;
8576 dc
.DrawBitmap(m_bitmap
, x
, y
, true);
8578 if (selection
.WithinSelection(obj
->GetRange().GetStart(), obj
))
8580 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
8581 wxCheckSetPen(dc
, *wxBLACK_PEN
);
8582 dc
.SetLogicalFunction(wxINVERT
);
8583 dc
.DrawRectangle(wxRect(x
, y
, m_bitmap
.GetWidth(), m_bitmap
.GetHeight()));
8584 dc
.SetLogicalFunction(wxCOPY
);
8589 wxString
label(m_label
);
8590 if (label
.IsEmpty())
8592 int w
, h
, maxDescent
;
8594 dc
.GetTextExtent(m_label
, & w
, &h
, & maxDescent
);
8595 dc
.SetTextForeground(textColour
);
8597 int x
= clientArea
.x
+ (clientArea
.width
- w
)/2;
8598 int y
= clientArea
.y
+ (clientArea
.height
- (h
- maxDescent
))/2;
8599 dc
.DrawText(m_label
, x
, y
);
8606 bool wxRichTextFieldTypeStandard::Layout(wxRichTextField
* obj
, wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& WXUNUSED(rect
), const wxRect
& WXUNUSED(parentRect
), int style
)
8608 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_COMPOSITE
)
8609 return false; // USe default composite layout
8611 wxSize size
= GetSize(obj
, dc
, context
, style
);
8612 obj
->SetCachedSize(size
);
8613 obj
->SetMinSize(size
);
8614 obj
->SetMaxSize(size
);
8618 bool wxRichTextFieldTypeStandard::GetRangeSize(wxRichTextField
* obj
, const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
8620 if (IsTopLevel(obj
))
8621 return obj
->wxRichTextParagraphLayoutBox::GetRangeSize(range
, size
, descent
, dc
, context
, flags
, position
);
8624 wxSize sz
= GetSize(obj
, dc
, context
, 0);
8628 if (partialExtents
->GetCount() > 0)
8629 lastSize
= (*partialExtents
)[partialExtents
->GetCount()-1];
8632 partialExtents
->Add(lastSize
+ sz
.x
);
8639 wxSize
wxRichTextFieldTypeStandard::GetSize(wxRichTextField
* WXUNUSED(obj
), wxDC
& dc
, wxRichTextDrawingContext
& WXUNUSED(context
), int WXUNUSED(style
)) const
8642 int w
= 0, h
= 0, maxDescent
= 0;
8645 if (m_bitmap
.IsOk())
8647 w
= m_bitmap
.GetWidth();
8648 h
= m_bitmap
.GetHeight();
8650 sz
= wxSize(w
+ m_horizontalMargin
*2, h
+ m_verticalMargin
*2);
8654 wxString
label(m_label
);
8655 if (label
.IsEmpty())
8658 dc
.GetTextExtent(label
, & w
, &h
, & maxDescent
);
8660 sz
= wxSize(w
+ m_horizontalPadding
*2 + m_horizontalMargin
*2, h
+ m_verticalPadding
*2 + m_verticalMargin
*2);
8663 if (m_displayStyle
!= wxRICHTEXT_FIELD_STYLE_NO_BORDER
)
8665 sz
.x
+= borderSize
*2;
8666 sz
.y
+= borderSize
*2;
8669 if (m_displayStyle
== wxRICHTEXT_FIELD_STYLE_START_TAG
|| m_displayStyle
== wxRICHTEXT_FIELD_STYLE_END_TAG
)
8671 // Add space for the arrow
8672 sz
.x
+= (sz
.y
/2 - m_horizontalPadding
);
8678 IMPLEMENT_DYNAMIC_CLASS(wxRichTextCell
, wxRichTextBox
)
8680 wxRichTextCell::wxRichTextCell(wxRichTextObject
* parent
):
8681 wxRichTextBox(parent
)
8686 bool wxRichTextCell::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8688 return wxRichTextBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8692 void wxRichTextCell::Copy(const wxRichTextCell
& obj
)
8694 wxRichTextBox::Copy(obj
);
8697 // Edit properties via a GUI
8698 bool wxRichTextCell::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
8700 // We need to gather common attributes for all selected cells.
8702 wxRichTextTable
* table
= wxDynamicCast(GetParent(), wxRichTextTable
);
8703 bool multipleCells
= false;
8704 wxRichTextAttr attr
;
8706 if (table
&& buffer
&& buffer
->GetRichTextCtrl() && buffer
->GetRichTextCtrl()->GetSelection().IsValid() &&
8707 buffer
->GetRichTextCtrl()->GetSelection().GetContainer() == GetParent())
8709 wxRichTextAttr clashingAttr
, absentAttr
;
8710 const wxRichTextSelection
& sel
= buffer
->GetRichTextCtrl()->GetSelection();
8712 int selectedCellCount
= 0;
8713 for (i
= 0; i
< sel
.GetCount(); i
++)
8715 const wxRichTextRange
& range
= sel
[i
];
8716 wxRichTextCell
* cell
= table
->GetCell(range
.GetStart());
8719 wxRichTextAttr cellStyle
= cell
->GetAttributes();
8721 CollectStyle(attr
, cellStyle
, clashingAttr
, absentAttr
);
8723 selectedCellCount
++;
8726 multipleCells
= selectedCellCount
> 1;
8730 attr
= GetAttributes();
8735 caption
= _("Multiple Cell Properties");
8737 caption
= _("Cell Properties");
8739 wxRichTextObjectPropertiesDialog
cellDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, caption
);
8740 cellDlg
.SetAttributes(attr
);
8742 wxRichTextSizePage
* sizePage
= wxDynamicCast(cellDlg
.FindPage(CLASSINFO(wxRichTextSizePage
)), wxRichTextSizePage
);
8745 // We don't want position and floating controls for a cell.
8746 sizePage
->ShowPositionControls(false);
8747 sizePage
->ShowFloatingControls(false);
8750 if (cellDlg
.ShowModal() == wxID_OK
)
8754 const wxRichTextSelection
& sel
= buffer
->GetRichTextCtrl()->GetSelection();
8755 // Apply the style; we interpret indeterminate attributes as 'don't touch this attribute'
8756 // since it may represent clashing attributes across multiple objects.
8757 table
->SetCellStyle(sel
, attr
);
8760 // For a single object, indeterminate attributes set by the user should be reflected in the
8761 // actual object style, so pass the wxRICHTEXT_SETSTYLE_RESET flag to assign
8762 // the style directly instead of applying (which ignores indeterminate attributes,
8763 // leaving them as they were).
8764 cellDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
8771 WX_DEFINE_OBJARRAY(wxRichTextObjectPtrArrayArray
)
8773 IMPLEMENT_DYNAMIC_CLASS(wxRichTextTable
, wxRichTextBox
)
8775 wxRichTextTable::wxRichTextTable(wxRichTextObject
* parent
): wxRichTextBox(parent
)
8781 // Draws the object.
8782 bool wxRichTextTable::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& range
, const wxRichTextSelection
& selection
, const wxRect
& rect
, int descent
, int style
)
8784 return wxRichTextBox::Draw(dc
, context
, range
, selection
, rect
, descent
, style
);
8787 WX_DECLARE_OBJARRAY(wxRect
, wxRichTextRectArray
);
8788 WX_DEFINE_OBJARRAY(wxRichTextRectArray
);
8790 // Lays the object out. rect is the space available for layout. Often it will
8791 // be the specified overall space for this object, if trying to constrain
8792 // layout to a particular size, or it could be the total space available in the
8793 // parent. rect is the overall size, so we must subtract margins and padding.
8794 // to get the actual available space.
8795 bool wxRichTextTable::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& WXUNUSED(parentRect
), int style
)
8797 SetPosition(rect
.GetPosition());
8799 // TODO: the meaty bit. Calculate sizes of all cells and rows. Try to use
8800 // minimum size if within alloted size, then divide up remaining size
8801 // between rows/cols.
8804 wxRichTextBuffer
* buffer
= GetBuffer();
8805 if (buffer
) scale
= buffer
->GetScale();
8807 wxRect availableSpace
= GetAvailableContentArea(dc
, context
, rect
);
8808 wxTextAttrDimensionConverter
converter(dc
, scale
, availableSpace
.GetSize());
8810 wxRichTextAttr
attr(GetAttributes());
8811 context
.ApplyVirtualAttributes(attr
, this);
8813 // If we have no fixed table size, and assuming we're not pushed for
8814 // space, then we don't have to try to stretch the table to fit the contents.
8815 bool stretchToFitTableWidth
= false;
8817 int tableWidth
= rect
.width
;
8818 if (attr
.GetTextBoxAttr().GetWidth().IsValid())
8820 tableWidth
= converter
.GetPixels(attr
.GetTextBoxAttr().GetWidth());
8822 // Fixed table width, so we do want to stretch columns out if necessary.
8823 stretchToFitTableWidth
= true;
8825 // Shouldn't be able to exceed the size passed to this function
8826 tableWidth
= wxMin(rect
.width
, tableWidth
);
8829 // Get internal padding
8830 int paddingLeft
= 0, paddingTop
= 0;
8831 if (attr
.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
8832 paddingLeft
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetLeft());
8833 if (attr
.GetTextBoxAttr().GetPadding().GetTop().IsValid())
8834 paddingTop
= converter
.GetPixels(attr
.GetTextBoxAttr().GetPadding().GetTop());
8836 // Assume that left and top padding are also used for inter-cell padding.
8837 int paddingX
= paddingLeft
;
8838 int paddingY
= paddingTop
;
8840 int totalLeftMargin
= 0, totalRightMargin
= 0, totalTopMargin
= 0, totalBottomMargin
= 0;
8841 GetTotalMargin(dc
, buffer
, attr
, totalLeftMargin
, totalRightMargin
, totalTopMargin
, totalBottomMargin
);
8843 // Internal table width - the area for content
8844 int internalTableWidth
= tableWidth
- totalLeftMargin
- totalRightMargin
;
8846 int rowCount
= m_cells
.GetCount();
8847 if (m_colCount
== 0 || rowCount
== 0)
8849 wxRect
overallRect(rect
.x
, rect
.y
, totalLeftMargin
+ totalRightMargin
, totalTopMargin
+ totalBottomMargin
);
8850 SetCachedSize(overallRect
.GetSize());
8852 // Zero content size
8853 SetMinSize(overallRect
.GetSize());
8854 SetMaxSize(GetMinSize());
8858 // The final calculated widths
8859 wxArrayInt colWidths
;
8860 colWidths
.Add(0, m_colCount
);
8862 wxArrayInt absoluteColWidths
;
8863 absoluteColWidths
.Add(0, m_colCount
);
8865 wxArrayInt percentageColWidths
;
8866 percentageColWidths
.Add(0, m_colCount
);
8867 // wxArrayInt percentageColWidthsSpanning(m_colCount);
8868 // These are only relevant when the first column contains spanning information.
8869 // wxArrayInt columnSpans(m_colCount); // Each contains 1 for non-spanning cell, > 1 for spanning cell.
8870 wxArrayInt maxColWidths
;
8871 maxColWidths
.Add(0, m_colCount
);
8872 wxArrayInt minColWidths
;
8873 minColWidths
.Add(0, m_colCount
);
8875 wxSize
tableSize(tableWidth
, 0);
8879 for (i
= 0; i
< m_colCount
; i
++)
8881 absoluteColWidths
[i
] = 0;
8882 // absoluteColWidthsSpanning[i] = 0;
8883 percentageColWidths
[i
] = -1;
8884 // percentageColWidthsSpanning[i] = -1;
8886 maxColWidths
[i
] = 0;
8887 minColWidths
[i
] = 0;
8888 // columnSpans[i] = 1;
8891 // (0) Determine which cells are visible according to spans
8893 // __________________
8898 // |------------------|
8899 // |__________________| 4
8901 // To calculate cell visibility:
8902 // First find all spanning cells. Build an array of span records with start x, y and end x, y.
8903 // Then for each cell, test whether we're within one of those cells, and unless we're at the start of
8904 // that cell, hide the cell.
8906 // We can also use this array to match the size of spanning cells to the grid. Or just do
8907 // this when we iterate through all cells.
8909 // 0.1: add spanning cells to an array
8910 wxRichTextRectArray rectArray
;
8911 for (j
= 0; j
< m_rowCount
; j
++)
8913 for (i
= 0; i
< m_colCount
; i
++)
8915 wxRichTextBox
* cell
= GetCell(j
, i
);
8916 int colSpan
= 1, rowSpan
= 1;
8917 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8918 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8919 if (cell
->GetProperties().HasProperty(wxT("rowspan")))
8920 rowSpan
= cell
->GetProperties().GetPropertyLong(wxT("rowspan"));
8921 if (colSpan
> 1 || rowSpan
> 1)
8923 rectArray
.Add(wxRect(i
, j
, colSpan
, rowSpan
));
8927 // 0.2: find which cells are subsumed by a spanning cell
8928 for (j
= 0; j
< m_rowCount
; j
++)
8930 for (i
= 0; i
< m_colCount
; i
++)
8932 wxRichTextBox
* cell
= GetCell(j
, i
);
8933 if (rectArray
.GetCount() == 0)
8939 int colSpan
= 1, rowSpan
= 1;
8940 if (cell
->GetProperties().HasProperty(wxT("colspan")))
8941 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
8942 if (cell
->GetProperties().HasProperty(wxT("rowspan")))
8943 rowSpan
= cell
->GetProperties().GetPropertyLong(wxT("rowspan"));
8944 if (colSpan
> 1 || rowSpan
> 1)
8946 // Assume all spanning cells are shown
8952 for (k
= 0; k
< (int) rectArray
.GetCount(); k
++)
8954 if (rectArray
[k
].Contains(wxPoint(i
, j
)))
8966 // TODO: find the first spanned cell in each row that spans the most columns and doesn't
8967 // overlap with a spanned cell starting at a previous column position.
8968 // This means we need to keep an array of rects so we can check. However
8969 // it does also mean that some spans simply may not be taken into account
8970 // where there are different spans happening on different rows. In these cases,
8971 // they will simply be as wide as their constituent columns.
8973 // (1) Do an initial layout for all cells to get minimum and maximum size, and get
8974 // the absolute or percentage width of each column.
8976 for (j
= 0; j
< m_rowCount
; j
++)
8978 // First get the overall margins so we can calculate percentage widths based on
8979 // the available content space for all cells on the row
8981 int overallRowContentMargin
= 0;
8982 int visibleCellCount
= 0;
8984 for (i
= 0; i
< m_colCount
; i
++)
8986 wxRichTextBox
* cell
= GetCell(j
, i
);
8987 if (cell
->IsShown())
8989 int cellTotalLeftMargin
= 0, cellTotalRightMargin
= 0, cellTotalTopMargin
= 0, cellTotalBottomMargin
= 0;
8990 GetTotalMargin(dc
, buffer
, cell
->GetAttributes(), cellTotalLeftMargin
, cellTotalRightMargin
, cellTotalTopMargin
, cellTotalBottomMargin
);
8992 overallRowContentMargin
+= (cellTotalLeftMargin
+ cellTotalRightMargin
);
8993 visibleCellCount
++;
8997 // Add in inter-cell padding
8998 overallRowContentMargin
+= ((visibleCellCount
-1) * paddingX
);
9000 int rowContentWidth
= internalTableWidth
- overallRowContentMargin
;
9001 wxSize
rowTableSize(rowContentWidth
, 0);
9002 wxTextAttrDimensionConverter
converter(dc
, scale
, rowTableSize
);
9004 for (i
= 0; i
< m_colCount
; i
++)
9006 wxRichTextBox
* cell
= GetCell(j
, i
);
9007 if (cell
->IsShown())
9010 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9011 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9013 // Lay out cell to find min/max widths
9014 cell
->Invalidate(wxRICHTEXT_ALL
);
9015 cell
->Layout(dc
, context
, availableSpace
, availableSpace
, style
);
9019 int absoluteCellWidth
= -1;
9020 int percentageCellWidth
= -1;
9022 // I think we need to calculate percentages from the internal table size,
9023 // minus the padding between cells which we'll need to calculate from the
9024 // (number of VISIBLE cells - 1)*paddingX. Then percentages that add up to 100%
9025 // will add up to 100%. In CSS, the width specifies the cell's content rect width,
9026 // so if we want to conform to that we'll need to add in the overall cell margins.
9027 // However, this will make it difficult to specify percentages that add up to
9028 // 100% and still fit within the table width.
9029 // Let's say two cells have 50% width. They have 10 pixels of overall margin each.
9030 // The table content rect is 500 pixels and the inter-cell padding is 20 pixels.
9031 // If we're using internal content size for the width, we would calculate the
9032 // the overall cell width for n cells as:
9033 // (500 - 20*(n-1) - overallCellMargin1 - overallCellMargin2 - ...) * percentage / 100
9034 // + thisOverallCellMargin
9035 // = 500 - 20 - 10 - 10) * 0.5 + 10 = 240 pixels overall cell width.
9036 // Adding this back, we get 240 + 240 + 20 = 500 pixels.
9038 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9040 int w
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetWidth());
9041 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
9043 percentageCellWidth
= w
;
9047 absoluteCellWidth
= w
;
9049 // Override absolute width with minimum width if necessary
9050 if (cell
->GetMinSize().x
> 0 && absoluteCellWidth
!=1 && cell
->GetMinSize().x
> absoluteCellWidth
)
9051 absoluteCellWidth
= cell
->GetMinSize().x
;
9054 if (absoluteCellWidth
!= -1)
9056 if (absoluteCellWidth
> absoluteColWidths
[i
])
9057 absoluteColWidths
[i
] = absoluteCellWidth
;
9060 if (percentageCellWidth
!= -1)
9062 if (percentageCellWidth
> percentageColWidths
[i
])
9063 percentageColWidths
[i
] = percentageCellWidth
;
9066 if (colSpan
== 1 && cell
->GetMinSize().x
&& cell
->GetMinSize().x
> minColWidths
[i
])
9067 minColWidths
[i
] = cell
->GetMinSize().x
;
9068 if (colSpan
== 1 && cell
->GetMaxSize().x
&& cell
->GetMaxSize().x
> maxColWidths
[i
])
9069 maxColWidths
[i
] = cell
->GetMaxSize().x
;
9075 // (2) Allocate initial column widths from minimum widths, absolute values and proportions
9076 // TODO: simply merge this into (1).
9077 for (i
= 0; i
< m_colCount
; i
++)
9079 if (absoluteColWidths
[i
] > 0)
9081 colWidths
[i
] = absoluteColWidths
[i
];
9083 else if (percentageColWidths
[i
] > 0)
9085 colWidths
[i
] = percentageColWidths
[i
];
9087 // This is rubbish - we calculated the absolute widths from percentages, so
9088 // we can't do it again here.
9089 //colWidths[i] = (int) (double(percentageColWidths[i]) * double(tableWidth) / 100.0 + 0.5);
9093 // (3) Process absolute or proportional widths of spanning columns,
9094 // now that we know what our fixed column widths are going to be.
9095 // Spanned cells will try to adjust columns so the span will fit.
9096 // Even existing fixed column widths can be expanded if necessary.
9097 // Actually, currently fixed columns widths aren't adjusted; instead,
9098 // the algorithm favours earlier rows and adjusts unspecified column widths
9099 // the first time only. After that, we can't know whether the column has been
9100 // specified explicitly or not. (We could make a note if necessary.)
9101 for (j
= 0; j
< m_rowCount
; j
++)
9103 // First get the overall margins so we can calculate percentage widths based on
9104 // the available content space for all cells on the row
9106 int overallRowContentMargin
= 0;
9107 int visibleCellCount
= 0;
9109 for (i
= 0; i
< m_colCount
; i
++)
9111 wxRichTextBox
* cell
= GetCell(j
, i
);
9112 if (cell
->IsShown())
9114 int cellTotalLeftMargin
= 0, cellTotalRightMargin
= 0, cellTotalTopMargin
= 0, cellTotalBottomMargin
= 0;
9115 GetTotalMargin(dc
, buffer
, cell
->GetAttributes(), cellTotalLeftMargin
, cellTotalRightMargin
, cellTotalTopMargin
, cellTotalBottomMargin
);
9117 overallRowContentMargin
+= (cellTotalLeftMargin
+ cellTotalRightMargin
);
9118 visibleCellCount
++;
9122 // Add in inter-cell padding
9123 overallRowContentMargin
+= ((visibleCellCount
-1) * paddingX
);
9125 int rowContentWidth
= internalTableWidth
- overallRowContentMargin
;
9126 wxSize
rowTableSize(rowContentWidth
, 0);
9127 wxTextAttrDimensionConverter
converter(dc
, scale
, rowTableSize
);
9129 for (i
= 0; i
< m_colCount
; i
++)
9131 wxRichTextBox
* cell
= GetCell(j
, i
);
9132 if (cell
->IsShown())
9135 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9136 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9140 int spans
= wxMin(colSpan
, m_colCount
- i
);
9144 if (cell
->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9146 cellWidth
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetWidth());
9147 // Override absolute width with minimum width if necessary
9148 if (cell
->GetMinSize().x
> 0 && cellWidth
!=1 && cell
->GetMinSize().x
> cellWidth
)
9149 cellWidth
= cell
->GetMinSize().x
;
9153 // Do we want to do this? It's the only chance we get to
9154 // use the cell's min/max sizes, so we need to work out
9155 // how we're going to balance the unspecified spanning cell
9156 // width with the possibility more-constrained constituent cell widths.
9157 // Say there's a tiny bitmap giving it a max width of 10 pixels. We
9158 // don't want to constraint all the spanned columns to fit into this cell.
9159 // OK, let's say that if any of the constituent columns don't fit,
9160 // then we simply stop constraining the columns; instead, we'll just fit the spanning
9161 // cells to the columns later.
9162 cellWidth
= cell
->GetMinSize().x
;
9163 if (cell
->GetMaxSize().x
> cellWidth
)
9164 cellWidth
= cell
->GetMaxSize().x
;
9167 // Subtract the padding between cells
9168 int spanningWidth
= cellWidth
;
9169 spanningWidth
-= paddingX
* (spans
-1);
9171 if (spanningWidth
> 0)
9173 // Now share the spanning width between columns within that span
9174 // TODO: take into account min widths of columns within the span
9175 int spanningWidthLeft
= spanningWidth
;
9176 int stretchColCount
= 0;
9177 for (k
= i
; k
< (i
+spans
); k
++)
9179 if (colWidths
[k
] > 0) // absolute or proportional width has been specified
9180 spanningWidthLeft
-= colWidths
[k
];
9184 // Now divide what's left between the remaining columns
9186 if (stretchColCount
> 0)
9187 colShare
= spanningWidthLeft
/ stretchColCount
;
9188 int colShareRemainder
= spanningWidthLeft
- (colShare
* stretchColCount
);
9190 // If fixed-width columns are currently too big, then we'll later
9191 // stretch the spanned cell to fit.
9193 if (spanningWidthLeft
> 0)
9195 for (k
= i
; k
< (i
+spans
); k
++)
9197 if (colWidths
[k
] <= 0) // absolute or proportional width has not been specified
9199 int newWidth
= colShare
;
9200 if (k
== (i
+spans
-1))
9201 newWidth
+= colShareRemainder
; // ensure all pixels are filled
9202 colWidths
[k
] = newWidth
;
9213 // (4) Next, share any remaining space out between columns that have not yet been calculated.
9214 // TODO: take into account min widths of columns within the span
9215 int tableWidthMinusPadding
= internalTableWidth
- (m_colCount
-1)*paddingX
;
9216 int widthLeft
= tableWidthMinusPadding
;
9217 int stretchColCount
= 0;
9218 for (i
= 0; i
< m_colCount
; i
++)
9220 // TODO: we need to take into account min widths.
9221 // Subtract min width from width left, then
9222 // add the colShare to the min width
9223 if (colWidths
[i
] > 0) // absolute or proportional width has been specified
9224 widthLeft
-= colWidths
[i
];
9227 if (minColWidths
[i
] > 0)
9228 widthLeft
-= minColWidths
[i
];
9234 // Now divide what's left between the remaining columns
9236 if (stretchColCount
> 0)
9237 colShare
= widthLeft
/ stretchColCount
;
9238 int colShareRemainder
= widthLeft
- (colShare
* stretchColCount
);
9240 // Check we don't have enough space, in which case shrink all columns, overriding
9241 // any absolute/proportional widths
9242 // TODO: actually we would like to divide up the shrinkage according to size.
9243 // How do we calculate the proportions that will achieve this?
9244 // Could first choose an arbitrary value for stretching cells, and then calculate
9245 // factors to multiply each width by.
9246 // TODO: want to record this fact and pass to an iteration that tries e.g. min widths
9247 if (widthLeft
< 0 || (stretchToFitTableWidth
&& (stretchColCount
== 0)))
9249 colShare
= tableWidthMinusPadding
/ m_colCount
;
9250 colShareRemainder
= tableWidthMinusPadding
- (colShare
* m_colCount
);
9251 for (i
= 0; i
< m_colCount
; i
++)
9254 minColWidths
[i
] = 0;
9258 // We have to adjust the columns if either we need to shrink the
9259 // table to fit the parent/table width, or we explicitly set the
9260 // table width and need to stretch out the table.
9261 if (widthLeft
< 0 || stretchToFitTableWidth
)
9263 for (i
= 0; i
< m_colCount
; i
++)
9265 if (colWidths
[i
] <= 0) // absolute or proportional width has not been specified
9267 if (minColWidths
[i
] > 0)
9268 colWidths
[i
] = minColWidths
[i
] + colShare
;
9270 colWidths
[i
] = colShare
;
9271 if (i
== (m_colCount
-1))
9272 colWidths
[i
] += colShareRemainder
; // ensure all pixels are filled
9277 // TODO: if spanned cells have no specified or max width, make them the
9278 // as big as the columns they span. Do this for all spanned cells in all
9279 // rows, of course. Size any spanned cells left over at the end - even if they
9280 // have width > 0, make sure they're limited to the appropriate column edge.
9284 Sort out confusion between content width
9285 and overall width later. For now, assume we specify overall width.
9287 So, now we've laid out the table to fit into the given space
9288 and have used specified widths and minimum widths.
9290 Now we need to consider how we will try to take maximum width into account.
9294 // (??) TODO: take max width into account
9296 // (6) Lay out all cells again with the current values
9299 int y
= availableSpace
.y
;
9300 for (j
= 0; j
< m_rowCount
; j
++)
9302 int x
= availableSpace
.x
; // TODO: take into account centering etc.
9303 int maxCellHeight
= 0;
9304 int maxSpecifiedCellHeight
= 0;
9306 wxArrayInt actualWidths
;
9307 actualWidths
.Add(0, m_colCount
);
9309 wxTextAttrDimensionConverter
converter(dc
, scale
);
9310 for (i
= 0; i
< m_colCount
; i
++)
9312 wxRichTextCell
* cell
= GetCell(j
, i
);
9313 if (cell
->IsShown())
9315 // Get max specified cell height
9316 // Don't handle percentages for height
9317 if (cell
->GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && cell
->GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() != wxTEXT_ATTR_UNITS_PERCENTAGE
)
9319 int h
= converter
.GetPixels(cell
->GetAttributes().GetTextBoxAttr().GetHeight());
9320 if (h
> maxSpecifiedCellHeight
)
9321 maxSpecifiedCellHeight
= h
;
9324 if (colWidths
[i
] > 0) // absolute or proportional width has been specified
9327 if (cell
->GetProperties().HasProperty(wxT("colspan")))
9328 colSpan
= cell
->GetProperties().GetPropertyLong(wxT("colspan"));
9330 wxRect availableCellSpace
;
9332 // TODO: take into acount spans
9335 // Calculate the size of this spanning cell from its constituent columns
9337 int spans
= wxMin(colSpan
, m_colCount
- i
);
9338 for (k
= i
; k
< spans
; k
++)
9344 availableCellSpace
= wxRect(x
, y
, xx
, -1);
9347 availableCellSpace
= wxRect(x
, y
, colWidths
[i
], -1);
9349 // Store actual width so we can force cell to be the appropriate width on the final loop
9350 actualWidths
[i
] = availableCellSpace
.GetWidth();
9353 cell
->Invalidate(wxRICHTEXT_ALL
);
9354 cell
->Layout(dc
, context
, availableCellSpace
, availableSpace
, style
);
9356 // TODO: use GetCachedSize().x to compute 'natural' size
9358 x
+= (availableCellSpace
.GetWidth() + paddingX
);
9359 if (cell
->GetCachedSize().y
> maxCellHeight
)
9360 maxCellHeight
= cell
->GetCachedSize().y
;
9365 maxCellHeight
= wxMax(maxCellHeight
, maxSpecifiedCellHeight
);
9367 for (i
= 0; i
< m_colCount
; i
++)
9369 wxRichTextCell
* cell
= GetCell(j
, i
);
9370 if (cell
->IsShown())
9372 wxRect availableCellSpace
= wxRect(cell
->GetPosition(), wxSize(actualWidths
[i
], maxCellHeight
));
9373 // Lay out cell with new height
9374 cell
->Invalidate(wxRICHTEXT_ALL
);
9375 cell
->Layout(dc
, context
, availableCellSpace
, availableSpace
, style
);
9377 // Make sure the cell size really is the appropriate size,
9378 // not the calculated box size
9379 cell
->SetCachedSize(wxSize(actualWidths
[i
], maxCellHeight
));
9381 maxRight
= wxMax(maxRight
, cell
->GetPosition().x
+ cell
->GetCachedSize().x
);
9386 if (j
< (m_rowCount
-1))
9390 // We need to add back the margins etc.
9392 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
9393 contentRect
= wxRect(wxPoint(0, 0), wxSize(maxRight
- availableSpace
.x
, y
- availableSpace
.y
));
9394 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
9395 SetCachedSize(marginRect
.GetSize());
9398 // TODO: calculate max size
9400 SetMaxSize(GetCachedSize());
9403 // TODO: calculate min size
9405 SetMinSize(GetCachedSize());
9408 // TODO: currently we use either a fixed table width or the parent's size.
9409 // We also want to be able to calculate the table width from its content,
9410 // whether using fixed column widths or cell content min/max width.
9411 // Probably need a boolean flag to say whether we need to stretch cells
9412 // to fit the table width, or to simply use min/max cell widths. The
9413 // trouble with this is that if cell widths are not specified, they
9414 // will be tiny; we could use arbitrary defaults but this seems unsatisfactory.
9415 // Anyway, ignoring that problem, we probably need to factor layout into a function
9416 // that can can calculate the maximum unconstrained layout in case table size is
9417 // not specified. Then LayoutToBestSize() can choose to use either parent size to
9418 // constrain Layout(), or the previously-calculated max size to constraint layout.
9423 // Finds the absolute position and row height for the given character position
9424 bool wxRichTextTable::FindPosition(wxDC
& dc
, wxRichTextDrawingContext
& context
, long index
, wxPoint
& pt
, int* height
, bool forceLineStart
)
9426 wxRichTextCell
* child
= GetCell(index
+1);
9429 // Find the position at the start of the child cell, since the table doesn't
9430 // have any caret position of its own.
9431 return child
->FindPosition(dc
, context
, -1, pt
, height
, forceLineStart
);
9437 // Get the cell at the given character position (in the range of the table).
9438 wxRichTextCell
* wxRichTextTable::GetCell(long pos
) const
9440 int row
= 0, col
= 0;
9441 if (GetCellRowColumnPosition(pos
, row
, col
))
9443 return GetCell(row
, col
);
9449 // Get the row/column for a given character position
9450 bool wxRichTextTable::GetCellRowColumnPosition(long pos
, int& row
, int& col
) const
9452 if (m_colCount
== 0 || m_rowCount
== 0)
9455 row
= (int) (pos
/ m_colCount
);
9456 col
= pos
- (row
* m_colCount
);
9458 wxASSERT(row
< m_rowCount
&& col
< m_colCount
);
9460 if (row
< m_rowCount
&& col
< m_colCount
)
9466 // Calculate range, taking row/cell ordering into account instead of relying
9467 // on list ordering.
9468 void wxRichTextTable::CalculateRange(long start
, long& end
)
9470 long current
= start
;
9471 long lastEnd
= current
;
9480 for (i
= 0; i
< m_rowCount
; i
++)
9482 for (j
= 0; j
< m_colCount
; j
++)
9484 wxRichTextCell
* child
= GetCell(i
, j
);
9489 child
->CalculateRange(current
, childEnd
);
9492 current
= childEnd
+ 1;
9497 // A top-level object always has a range of size 1,
9498 // because its children don't count at this level.
9500 m_range
.SetRange(start
, start
);
9502 // An object with no children has zero length
9503 if (m_children
.GetCount() == 0)
9505 m_ownRange
.SetRange(0, lastEnd
);
9508 // Gets the range size.
9509 bool wxRichTextTable::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& descent
, wxDC
& dc
, wxRichTextDrawingContext
& context
, int flags
, wxPoint position
, wxArrayInt
* partialExtents
) const
9511 return wxRichTextBox::GetRangeSize(range
, size
, descent
, dc
, context
, flags
, position
, partialExtents
);
9514 // Deletes content in the given range.
9515 bool wxRichTextTable::DeleteRange(const wxRichTextRange
& WXUNUSED(range
))
9517 // TODO: implement deletion of cells
9521 // Gets any text in this object for the given range.
9522 wxString
wxRichTextTable::GetTextForRange(const wxRichTextRange
& range
) const
9524 return wxRichTextBox::GetTextForRange(range
);
9527 // Copies this object.
9528 void wxRichTextTable::Copy(const wxRichTextTable
& obj
)
9530 wxRichTextBox::Copy(obj
);
9534 m_rowCount
= obj
.m_rowCount
;
9535 m_colCount
= obj
.m_colCount
;
9537 m_cells
.Add(wxRichTextObjectPtrArray(), m_rowCount
);
9540 for (i
= 0; i
< m_rowCount
; i
++)
9542 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
9543 for (j
= 0; j
< m_colCount
; j
++)
9545 wxRichTextCell
* cell
= wxDynamicCast(obj
.GetCell(i
, j
)->Clone(), wxRichTextCell
);
9553 void wxRichTextTable::ClearTable()
9559 bool wxRichTextTable::CreateTable(int rows
, int cols
)
9566 m_cells
.Add(wxRichTextObjectPtrArray(), rows
);
9569 for (i
= 0; i
< rows
; i
++)
9571 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
9572 for (j
= 0; j
< cols
; j
++)
9574 wxRichTextCell
* cell
= new wxRichTextCell
;
9576 cell
->AddParagraph(wxEmptyString
);
9585 wxRichTextCell
* wxRichTextTable::GetCell(int row
, int col
) const
9587 wxASSERT(row
< m_rowCount
);
9588 wxASSERT(col
< m_colCount
);
9590 if (row
< m_rowCount
&& col
< m_colCount
)
9592 wxRichTextObjectPtrArray
& colArray
= m_cells
[row
];
9593 wxRichTextObject
* obj
= colArray
[col
];
9594 return wxDynamicCast(obj
, wxRichTextCell
);
9600 // Returns a selection object specifying the selections between start and end character positions.
9601 // For example, a table would deduce what cells (of range length 1) are selected when dragging across the table.
9602 wxRichTextSelection
wxRichTextTable::GetSelection(long start
, long end
) const
9604 wxRichTextSelection selection
;
9605 selection
.SetContainer((wxRichTextTable
*) this);
9614 wxASSERT( start
>= 0 && end
< (m_colCount
* m_rowCount
));
9616 if (end
>= (m_colCount
* m_rowCount
))
9619 // We need to find the rectangle of cells that is described by the rectangle
9620 // with start, end as the diagonal. Make sure we don't add cells that are
9621 // not currenty visible because they are overlapped by spanning cells.
9623 --------------------------
9624 | 0 | 1 | 2 | 3 | 4 |
9625 --------------------------
9626 | 5 | 6 | 7 | 8 | 9 |
9627 --------------------------
9628 | 10 | 11 | 12 | 13 | 14 |
9629 --------------------------
9630 | 15 | 16 | 17 | 18 | 19 |
9631 --------------------------
9633 Let's say we select 6 -> 18.
9635 Left and right edge cols of rectangle are 1 and 3 inclusive. Find least/greatest to find
9636 which is left and which is right.
9638 Top and bottom edge rows are 1 and 3 inclusive. Again, find least/greatest to find top and bottom.
9640 Now go through rows from 1 to 3 and only add cells that are (a) within above column range
9646 int leftCol
= start
- m_colCount
* int(start
/m_colCount
);
9647 int rightCol
= end
- m_colCount
* int(end
/m_colCount
);
9649 int topRow
= int(start
/m_colCount
);
9650 int bottomRow
= int(end
/m_colCount
);
9652 if (leftCol
> rightCol
)
9659 if (topRow
> bottomRow
)
9661 int tmp
= bottomRow
;
9667 for (i
= topRow
; i
<= bottomRow
; i
++)
9669 for (j
= leftCol
; j
<= rightCol
; j
++)
9671 wxRichTextCell
* cell
= GetCell(i
, j
);
9672 if (cell
&& cell
->IsShown())
9673 selection
.Add(cell
->GetRange());
9680 // Sets the attributes for the cells specified by the selection.
9681 bool wxRichTextTable::SetCellStyle(const wxRichTextSelection
& selection
, const wxRichTextAttr
& style
, int flags
)
9683 if (selection
.GetContainer() != this)
9686 wxRichTextBuffer
* buffer
= GetBuffer();
9687 bool haveControl
= (buffer
&& buffer
->GetRichTextCtrl() != NULL
);
9688 bool withUndo
= haveControl
&& ((flags
& wxRICHTEXT_SETSTYLE_WITH_UNDO
) != 0);
9691 buffer
->BeginBatchUndo(_("Set Cell Style"));
9693 wxRichTextObjectList::compatibility_iterator node
= m_children
.GetFirst();
9696 wxRichTextCell
* cell
= wxDynamicCast(node
->GetData(), wxRichTextCell
);
9697 if (cell
&& selection
.WithinSelection(cell
->GetRange().GetStart()))
9698 SetStyle(cell
, style
, flags
);
9699 node
= node
->GetNext();
9702 // Do action, or delay it until end of batch.
9704 buffer
->EndBatchUndo();
9709 bool wxRichTextTable::DeleteRows(int startRow
, int noRows
)
9711 wxASSERT((startRow
+ noRows
) < m_rowCount
);
9712 if ((startRow
+ noRows
) >= m_rowCount
)
9716 for (i
= startRow
; i
< (startRow
+noRows
); i
++)
9718 wxRichTextObjectPtrArray
& colArray
= m_cells
[startRow
];
9719 for (j
= 0; j
< (int) colArray
.GetCount(); j
++)
9721 wxRichTextObject
* cell
= colArray
[j
];
9722 RemoveChild(cell
, true);
9725 // Keep deleting at the same position, since we move all
9727 m_cells
.RemoveAt(startRow
);
9730 m_rowCount
= m_rowCount
- noRows
;
9735 bool wxRichTextTable::DeleteColumns(int startCol
, int noCols
)
9737 wxASSERT((startCol
+ noCols
) < m_colCount
);
9738 if ((startCol
+ noCols
) >= m_colCount
)
9741 bool deleteRows
= (noCols
== m_colCount
);
9744 for (i
= 0; i
< m_rowCount
; i
++)
9746 wxRichTextObjectPtrArray
& colArray
= m_cells
[deleteRows
? 0 : i
];
9747 for (j
= startCol
; j
< (startCol
+noCols
); j
++)
9749 wxRichTextObject
* cell
= colArray
[j
];
9750 RemoveChild(cell
, true);
9754 m_cells
.RemoveAt(0);
9759 m_colCount
= m_colCount
- noCols
;
9764 bool wxRichTextTable::AddRows(int startRow
, int noRows
, const wxRichTextAttr
& attr
)
9766 wxASSERT(startRow
<= m_rowCount
);
9767 if (startRow
> m_rowCount
)
9771 for (i
= 0; i
< noRows
; i
++)
9774 if (startRow
== m_rowCount
)
9776 m_cells
.Add(wxRichTextObjectPtrArray());
9777 idx
= m_cells
.GetCount() - 1;
9781 m_cells
.Insert(wxRichTextObjectPtrArray(), startRow
+i
);
9785 wxRichTextObjectPtrArray
& colArray
= m_cells
[idx
];
9786 for (j
= 0; j
< m_colCount
; j
++)
9788 wxRichTextCell
* cell
= new wxRichTextCell
;
9789 cell
->GetAttributes() = attr
;
9796 m_rowCount
= m_rowCount
+ noRows
;
9800 bool wxRichTextTable::AddColumns(int startCol
, int noCols
, const wxRichTextAttr
& attr
)
9802 wxASSERT(startCol
<= m_colCount
);
9803 if (startCol
> m_colCount
)
9807 for (i
= 0; i
< m_rowCount
; i
++)
9809 wxRichTextObjectPtrArray
& colArray
= m_cells
[i
];
9810 for (j
= 0; j
< noCols
; j
++)
9812 wxRichTextCell
* cell
= new wxRichTextCell
;
9813 cell
->GetAttributes() = attr
;
9817 if (startCol
== m_colCount
)
9820 colArray
.Insert(cell
, startCol
+j
);
9824 m_colCount
= m_colCount
+ noCols
;
9829 // Edit properties via a GUI
9830 bool wxRichTextTable::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
9832 wxRichTextObjectPropertiesDialog
boxDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Table Properties"));
9833 boxDlg
.SetAttributes(GetAttributes());
9835 if (boxDlg
.ShowModal() == wxID_OK
)
9837 boxDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
9845 * Module to initialise and clean up handlers
9848 class wxRichTextModule
: public wxModule
9850 DECLARE_DYNAMIC_CLASS(wxRichTextModule
)
9852 wxRichTextModule() {}
9855 wxRichTextBuffer::SetRenderer(new wxRichTextStdRenderer
);
9856 wxRichTextBuffer::InitStandardHandlers();
9857 wxRichTextParagraph::InitDefaultTabs();
9862 wxRichTextBuffer::CleanUpHandlers();
9863 wxRichTextBuffer::CleanUpDrawingHandlers();
9864 wxRichTextBuffer::CleanUpFieldTypes();
9865 wxRichTextDecimalToRoman(-1);
9866 wxRichTextParagraph::ClearDefaultTabs();
9867 wxRichTextCtrl::ClearAvailableFontNames();
9868 wxRichTextBuffer::SetRenderer(NULL
);
9872 IMPLEMENT_DYNAMIC_CLASS(wxRichTextModule
, wxModule
)
9875 // If the richtext lib is dynamically loaded after the app has already started
9876 // (such as from wxPython) then the built-in module system will not init this
9877 // module. Provide this function to do it manually.
9878 void wxRichTextModuleInit()
9880 wxModule
* module = new wxRichTextModule
;
9882 wxModule::RegisterModule(module);
9887 * Commands for undo/redo
9891 wxRichTextCommand::wxRichTextCommand(const wxString
& name
, wxRichTextCommandId id
, wxRichTextBuffer
* buffer
,
9892 wxRichTextParagraphLayoutBox
* container
, wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
): wxCommand(true, name
)
9894 /* wxRichTextAction* action = */ new wxRichTextAction(this, name
, id
, buffer
, container
, ctrl
, ignoreFirstTime
);
9897 wxRichTextCommand::wxRichTextCommand(const wxString
& name
): wxCommand(true, name
)
9901 wxRichTextCommand::~wxRichTextCommand()
9906 void wxRichTextCommand::AddAction(wxRichTextAction
* action
)
9908 if (!m_actions
.Member(action
))
9909 m_actions
.Append(action
);
9912 bool wxRichTextCommand::Do()
9914 for (wxList::compatibility_iterator node
= m_actions
.GetFirst(); node
; node
= node
->GetNext())
9916 wxRichTextAction
* action
= (wxRichTextAction
*) node
->GetData();
9923 bool wxRichTextCommand::Undo()
9925 for (wxList::compatibility_iterator node
= m_actions
.GetLast(); node
; node
= node
->GetPrevious())
9927 wxRichTextAction
* action
= (wxRichTextAction
*) node
->GetData();
9934 void wxRichTextCommand::ClearActions()
9936 WX_CLEAR_LIST(wxList
, m_actions
);
9944 wxRichTextAction::wxRichTextAction(wxRichTextCommand
* cmd
, const wxString
& name
, wxRichTextCommandId id
,
9945 wxRichTextBuffer
* buffer
, wxRichTextParagraphLayoutBox
* container
,
9946 wxRichTextCtrl
* ctrl
, bool ignoreFirstTime
)
9950 m_containerAddress
.Create(buffer
, container
);
9951 m_ignoreThis
= ignoreFirstTime
;
9956 m_newParagraphs
.SetDefaultStyle(buffer
->GetDefaultStyle());
9957 m_newParagraphs
.SetBasicStyle(buffer
->GetBasicStyle());
9959 cmd
->AddAction(this);
9962 wxRichTextAction::~wxRichTextAction()
9968 // Returns the container that this action refers to, using the container address and top-level buffer.
9969 wxRichTextParagraphLayoutBox
* wxRichTextAction::GetContainer() const
9971 wxRichTextParagraphLayoutBox
* container
= wxDynamicCast(GetContainerAddress().GetObject(m_buffer
), wxRichTextParagraphLayoutBox
);
9976 void wxRichTextAction::CalculateRefreshOptimizations(wxArrayInt
& optimizationLineCharPositions
, wxArrayInt
& optimizationLineYPositions
)
9978 // Store a list of line start character and y positions so we can figure out which area
9979 // we need to refresh
9981 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9982 wxRichTextParagraphLayoutBox
* container
= GetContainer();
9983 wxASSERT(container
!= NULL
);
9987 // NOTE: we're assuming that the buffer is laid out correctly at this point.
9988 // If we had several actions, which only invalidate and leave layout until the
9989 // paint handler is called, then this might not be true. So we may need to switch
9990 // optimisation on only when we're simply adding text and not simultaneously
9991 // deleting a selection, for example. Or, we make sure the buffer is laid out correctly
9992 // first, but of course this means we'll be doing it twice.
9993 if (!m_buffer
->IsDirty() && m_ctrl
) // can only do optimisation if the buffer is already laid out correctly
9995 wxSize clientSize
= m_ctrl
->GetClientSize();
9996 wxPoint firstVisiblePt
= m_ctrl
->GetFirstVisiblePoint();
9997 int lastY
= firstVisiblePt
.y
+ clientSize
.y
;
9999 wxRichTextParagraph
* para
= container
->GetParagraphAtPosition(GetRange().GetStart());
10000 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(para
);
10003 wxRichTextParagraph
* child
= (wxRichTextParagraph
*) node
->GetData();
10004 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
10007 wxRichTextLine
* line
= node2
->GetData();
10008 wxPoint pt
= line
->GetAbsolutePosition();
10009 wxRichTextRange range
= line
->GetAbsoluteRange();
10013 node2
= wxRichTextLineList::compatibility_iterator();
10014 node
= wxRichTextObjectList::compatibility_iterator();
10016 else if (range
.GetStart() > GetPosition() && pt
.y
>= firstVisiblePt
.y
)
10018 optimizationLineCharPositions
.Add(range
.GetStart());
10019 optimizationLineYPositions
.Add(pt
.y
);
10023 node2
= node2
->GetNext();
10027 node
= node
->GetNext();
10033 bool wxRichTextAction::Do()
10035 m_buffer
->Modify(true);
10037 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10038 wxASSERT(container
!= NULL
);
10044 case wxRICHTEXT_INSERT
:
10046 // Store a list of line start character and y positions so we can figure out which area
10047 // we need to refresh
10048 wxArrayInt optimizationLineCharPositions
;
10049 wxArrayInt optimizationLineYPositions
;
10051 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10052 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
10055 container
->InsertFragment(GetRange().GetStart(), m_newParagraphs
);
10056 container
->UpdateRanges();
10058 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10059 // Layout() would stop prematurely at the top level.
10060 container
->InvalidateHierarchy(wxRichTextRange(wxMax(0, GetRange().GetStart()-1), GetRange().GetEnd()));
10062 long newCaretPosition
= GetPosition() + m_newParagraphs
.GetOwnRange().GetLength();
10064 // Character position to caret position
10065 newCaretPosition
--;
10067 // Don't take into account the last newline
10068 if (m_newParagraphs
.GetPartialParagraph())
10069 newCaretPosition
--;
10071 if (m_newParagraphs
.GetChildren().GetCount() > 1)
10073 wxRichTextObject
* p
= (wxRichTextObject
*) m_newParagraphs
.GetChildren().GetLast()->GetData();
10074 if (p
->GetRange().GetLength() == 1)
10075 newCaretPosition
--;
10078 newCaretPosition
= wxMin(newCaretPosition
, (container
->GetOwnRange().GetEnd()-1));
10080 UpdateAppearance(newCaretPosition
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */);
10082 wxRichTextEvent
cmdEvent(
10083 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED
,
10084 m_ctrl
? m_ctrl
->GetId() : -1);
10085 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10086 cmdEvent
.SetRange(GetRange());
10087 cmdEvent
.SetPosition(GetRange().GetStart());
10088 cmdEvent
.SetContainer(container
);
10090 m_buffer
->SendEvent(cmdEvent
);
10094 case wxRICHTEXT_DELETE
:
10096 wxArrayInt optimizationLineCharPositions
;
10097 wxArrayInt optimizationLineYPositions
;
10099 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10100 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
10103 container
->DeleteRange(GetRange());
10104 container
->UpdateRanges();
10105 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10106 // Layout() would stop prematurely at the top level.
10107 container
->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
10109 long caretPos
= GetRange().GetStart()-1;
10110 if (caretPos
>= container
->GetOwnRange().GetEnd())
10113 UpdateAppearance(caretPos
, true /* send update event */, & optimizationLineCharPositions
, & optimizationLineYPositions
, true /* do */);
10115 wxRichTextEvent
cmdEvent(
10116 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED
,
10117 m_ctrl
? m_ctrl
->GetId() : -1);
10118 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10119 cmdEvent
.SetRange(GetRange());
10120 cmdEvent
.SetPosition(GetRange().GetStart());
10121 cmdEvent
.SetContainer(container
);
10123 m_buffer
->SendEvent(cmdEvent
);
10127 case wxRICHTEXT_CHANGE_STYLE
:
10128 case wxRICHTEXT_CHANGE_PROPERTIES
:
10130 ApplyParagraphs(GetNewParagraphs());
10132 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10133 // Layout() would stop prematurely at the top level.
10134 container
->InvalidateHierarchy(GetRange());
10136 UpdateAppearance(GetPosition());
10138 wxRichTextEvent
cmdEvent(
10139 m_cmdId
== wxRICHTEXT_CHANGE_STYLE
? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
: wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED
,
10140 m_ctrl
? m_ctrl
->GetId() : -1);
10141 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10142 cmdEvent
.SetRange(GetRange());
10143 cmdEvent
.SetPosition(GetRange().GetStart());
10144 cmdEvent
.SetContainer(container
);
10146 m_buffer
->SendEvent(cmdEvent
);
10150 case wxRICHTEXT_CHANGE_ATTRIBUTES
:
10152 wxRichTextObject
* obj
= m_objectAddress
.GetObject(m_buffer
); // container->GetChildAtPosition(GetRange().GetStart());
10155 wxRichTextAttr oldAttr
= obj
->GetAttributes();
10156 obj
->GetAttributes() = m_attributes
;
10157 m_attributes
= oldAttr
;
10160 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10161 // Layout() would stop prematurely at the top level.
10162 container
->InvalidateHierarchy(GetRange());
10164 UpdateAppearance(GetPosition());
10166 wxRichTextEvent
cmdEvent(
10167 wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
,
10168 m_ctrl
? m_ctrl
->GetId() : -1);
10169 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10170 cmdEvent
.SetRange(GetRange());
10171 cmdEvent
.SetPosition(GetRange().GetStart());
10172 cmdEvent
.SetContainer(container
);
10174 m_buffer
->SendEvent(cmdEvent
);
10178 case wxRICHTEXT_CHANGE_OBJECT
:
10180 wxRichTextObject
* obj
= m_objectAddress
.GetObject(m_buffer
);
10181 // wxRichTextObject* obj = container->GetChildAtPosition(GetRange().GetStart());
10182 if (obj
&& m_object
)
10184 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(obj
);
10187 wxRichTextObject
* obj
= node
->GetData();
10188 node
->SetData(m_object
);
10193 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10194 // Layout() would stop prematurely at the top level.
10195 container
->InvalidateHierarchy(GetRange());
10197 UpdateAppearance(GetPosition());
10199 // TODO: send new kind of modification event
10210 bool wxRichTextAction::Undo()
10212 m_buffer
->Modify(true);
10214 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10215 wxASSERT(container
!= NULL
);
10221 case wxRICHTEXT_INSERT
:
10223 wxArrayInt optimizationLineCharPositions
;
10224 wxArrayInt optimizationLineYPositions
;
10226 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10227 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
10230 container
->DeleteRange(GetRange());
10231 container
->UpdateRanges();
10233 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10234 // Layout() would stop prematurely at the top level.
10235 container
->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
10237 long newCaretPosition
= GetPosition() - 1;
10239 UpdateAppearance(newCaretPosition
, true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */);
10241 wxRichTextEvent
cmdEvent(
10242 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED
,
10243 m_ctrl
? m_ctrl
->GetId() : -1);
10244 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10245 cmdEvent
.SetRange(GetRange());
10246 cmdEvent
.SetPosition(GetRange().GetStart());
10247 cmdEvent
.SetContainer(container
);
10249 m_buffer
->SendEvent(cmdEvent
);
10253 case wxRICHTEXT_DELETE
:
10255 wxArrayInt optimizationLineCharPositions
;
10256 wxArrayInt optimizationLineYPositions
;
10258 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10259 CalculateRefreshOptimizations(optimizationLineCharPositions
, optimizationLineYPositions
);
10262 container
->InsertFragment(GetRange().GetStart(), m_oldParagraphs
);
10263 container
->UpdateRanges();
10265 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10266 // Layout() would stop prematurely at the top level.
10267 container
->InvalidateHierarchy(GetRange());
10269 UpdateAppearance(GetPosition(), true, /* send update event */ & optimizationLineCharPositions
, & optimizationLineYPositions
, false /* undo */);
10271 wxRichTextEvent
cmdEvent(
10272 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED
,
10273 m_ctrl
? m_ctrl
->GetId() : -1);
10274 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10275 cmdEvent
.SetRange(GetRange());
10276 cmdEvent
.SetPosition(GetRange().GetStart());
10277 cmdEvent
.SetContainer(container
);
10279 m_buffer
->SendEvent(cmdEvent
);
10283 case wxRICHTEXT_CHANGE_STYLE
:
10284 case wxRICHTEXT_CHANGE_PROPERTIES
:
10286 ApplyParagraphs(GetOldParagraphs());
10287 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10288 // Layout() would stop prematurely at the top level.
10289 container
->InvalidateHierarchy(GetRange());
10291 UpdateAppearance(GetPosition());
10293 wxRichTextEvent
cmdEvent(
10294 m_cmdId
== wxRICHTEXT_CHANGE_STYLE
? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED
: wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED
,
10295 m_ctrl
? m_ctrl
->GetId() : -1);
10296 cmdEvent
.SetEventObject(m_ctrl
? (wxObject
*) m_ctrl
: (wxObject
*) m_buffer
);
10297 cmdEvent
.SetRange(GetRange());
10298 cmdEvent
.SetPosition(GetRange().GetStart());
10299 cmdEvent
.SetContainer(container
);
10301 m_buffer
->SendEvent(cmdEvent
);
10305 case wxRICHTEXT_CHANGE_ATTRIBUTES
:
10306 case wxRICHTEXT_CHANGE_OBJECT
:
10317 /// Update the control appearance
10318 void wxRichTextAction::UpdateAppearance(long caretPosition
, bool sendUpdateEvent
, wxArrayInt
* optimizationLineCharPositions
, wxArrayInt
* optimizationLineYPositions
, bool isDoCmd
)
10320 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10321 wxASSERT(container
!= NULL
);
10327 m_ctrl
->SetFocusObject(container
);
10328 m_ctrl
->SetCaretPosition(caretPosition
);
10330 if (!m_ctrl
->IsFrozen())
10332 wxRect containerRect
= container
->GetRect();
10334 m_ctrl
->LayoutContent();
10336 // Refresh everything if there were floating objects or the container changed size
10337 // (we can't yet optimize in these cases, since more complex interaction with other content occurs)
10338 if (container
->GetFloatingObjectCount() > 0 || (container
->GetParent() && containerRect
!= container
->GetRect()))
10340 m_ctrl
->Refresh(false);
10344 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10345 // Find refresh rectangle if we are in a position to optimise refresh
10346 if ((m_cmdId
== wxRICHTEXT_INSERT
|| m_cmdId
== wxRICHTEXT_DELETE
) && optimizationLineCharPositions
)
10350 wxSize clientSize
= m_ctrl
->GetClientSize();
10351 wxPoint firstVisiblePt
= m_ctrl
->GetFirstVisiblePoint();
10353 // Start/end positions
10355 int lastY
= firstVisiblePt
.y
+ clientSize
.y
;
10357 bool foundEnd
= false;
10359 // position offset - how many characters were inserted
10360 int positionOffset
= GetRange().GetLength();
10362 // Determine whether this is Do or Undo, and adjust positionOffset accordingly
10363 if ((m_cmdId
== wxRICHTEXT_DELETE
&& isDoCmd
) || (m_cmdId
== wxRICHTEXT_INSERT
&& !isDoCmd
))
10364 positionOffset
= - positionOffset
;
10366 // find the first line which is being drawn at the same position as it was
10367 // before. Since we're talking about a simple insertion, we can assume
10368 // that the rest of the window does not need to be redrawn.
10370 wxRichTextParagraph
* para
= container
->GetParagraphAtPosition(GetPosition());
10371 // Since we support floating layout, we should redraw the whole para instead of just
10372 // the first line touching the invalid range.
10375 firstY
= para
->GetPosition().y
;
10378 wxRichTextObjectList::compatibility_iterator node
= container
->GetChildren().Find(para
);
10381 wxRichTextParagraph
* child
= (wxRichTextParagraph
*) node
->GetData();
10382 wxRichTextLineList::compatibility_iterator node2
= child
->GetLines().GetFirst();
10385 wxRichTextLine
* line
= node2
->GetData();
10386 wxPoint pt
= line
->GetAbsolutePosition();
10387 wxRichTextRange range
= line
->GetAbsoluteRange();
10389 // we want to find the first line that is in the same position
10390 // as before. This will mean we're at the end of the changed text.
10392 if (pt
.y
> lastY
) // going past the end of the window, no more info
10394 node2
= wxRichTextLineList::compatibility_iterator();
10395 node
= wxRichTextObjectList::compatibility_iterator();
10397 // Detect last line in the buffer
10398 else if (!node2
->GetNext() && para
->GetRange().Contains(container
->GetOwnRange().GetEnd()))
10400 // If deleting text, make sure we refresh below as well as above
10401 if (positionOffset
>= 0)
10404 lastY
= pt
.y
+ line
->GetSize().y
;
10407 node2
= wxRichTextLineList::compatibility_iterator();
10408 node
= wxRichTextObjectList::compatibility_iterator();
10414 // search for this line being at the same position as before
10415 for (i
= 0; i
< optimizationLineCharPositions
->GetCount(); i
++)
10417 if (((*optimizationLineCharPositions
)[i
] + positionOffset
== range
.GetStart()) &&
10418 ((*optimizationLineYPositions
)[i
] == pt
.y
))
10420 // Stop, we're now the same as we were
10425 node2
= wxRichTextLineList::compatibility_iterator();
10426 node
= wxRichTextObjectList::compatibility_iterator();
10434 node2
= node2
->GetNext();
10438 node
= node
->GetNext();
10441 firstY
= wxMax(firstVisiblePt
.y
, firstY
);
10443 lastY
= firstVisiblePt
.y
+ clientSize
.y
;
10445 // Convert to device coordinates
10446 wxRect
rect(m_ctrl
->GetPhysicalPoint(wxPoint(firstVisiblePt
.x
, firstY
)), wxSize(clientSize
.x
, lastY
- firstY
));
10447 m_ctrl
->RefreshRect(rect
);
10451 m_ctrl
->Refresh(false);
10453 m_ctrl
->PositionCaret();
10455 // This causes styles to persist when doing programmatic
10456 // content creation except when Freeze/Thaw is used, so
10457 // disable this and check for the consequences.
10458 // m_ctrl->SetDefaultStyleToCursorStyle();
10460 if (sendUpdateEvent
)
10461 wxTextCtrl::SendTextUpdatedEvent(m_ctrl
);
10466 /// Replace the buffer paragraphs with the new ones.
10467 void wxRichTextAction::ApplyParagraphs(const wxRichTextParagraphLayoutBox
& fragment
)
10469 wxRichTextParagraphLayoutBox
* container
= GetContainer();
10470 wxASSERT(container
!= NULL
);
10474 wxRichTextObjectList::compatibility_iterator node
= fragment
.GetChildren().GetFirst();
10477 wxRichTextParagraph
* para
= wxDynamicCast(node
->GetData(), wxRichTextParagraph
);
10478 wxASSERT (para
!= NULL
);
10480 // We'll replace the existing paragraph by finding the paragraph at this position,
10481 // delete its node data, and setting a copy as the new node data.
10482 // TODO: make more efficient by simply swapping old and new paragraph objects.
10484 wxRichTextParagraph
* existingPara
= container
->GetParagraphAtPosition(para
->GetRange().GetStart());
10487 wxRichTextObjectList::compatibility_iterator bufferParaNode
= container
->GetChildren().Find(existingPara
);
10488 if (bufferParaNode
)
10490 wxRichTextParagraph
* newPara
= new wxRichTextParagraph(*para
);
10491 newPara
->SetParent(container
);
10493 bufferParaNode
->SetData(newPara
);
10495 delete existingPara
;
10499 node
= node
->GetNext();
10506 * This stores beginning and end positions for a range of data.
10509 WX_DEFINE_OBJARRAY(wxRichTextRangeArray
);
10511 /// Limit this range to be within 'range'
10512 bool wxRichTextRange::LimitTo(const wxRichTextRange
& range
)
10514 if (m_start
< range
.m_start
)
10515 m_start
= range
.m_start
;
10517 if (m_end
> range
.m_end
)
10518 m_end
= range
.m_end
;
10524 * wxRichTextImage implementation
10525 * This object represents an image.
10528 IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage
, wxRichTextObject
)
10530 wxRichTextImage::wxRichTextImage(const wxImage
& image
, wxRichTextObject
* parent
, wxRichTextAttr
* charStyle
):
10531 wxRichTextObject(parent
)
10534 m_imageBlock
.MakeImageBlockDefaultQuality(image
, wxBITMAP_TYPE_PNG
);
10536 SetAttributes(*charStyle
);
10539 wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock
& imageBlock
, wxRichTextObject
* parent
, wxRichTextAttr
* charStyle
):
10540 wxRichTextObject(parent
)
10543 m_imageBlock
= imageBlock
;
10545 SetAttributes(*charStyle
);
10548 void wxRichTextImage::Init()
10550 m_originalImageSize
= wxSize(-1, -1);
10553 /// Create a cached image at the required size
10554 bool wxRichTextImage::LoadImageCache(wxDC
& dc
, bool resetCache
)
10556 if (!m_imageBlock
.IsOk())
10559 // If we have an original image size, use that to compute the cached bitmap size
10560 // instead of loading the image each time. This way we can avoid loading
10561 // the image so long as the new cached bitmap size hasn't changed.
10564 if (resetCache
|| m_originalImageSize
== wxSize(-1, -1))
10566 m_imageCache
= wxNullBitmap
;
10568 m_imageBlock
.Load(image
);
10572 m_originalImageSize
= wxSize(image
.GetWidth(), image
.GetHeight());
10575 int width
= m_originalImageSize
.GetWidth();
10576 int height
= m_originalImageSize
.GetHeight();
10578 int parentWidth
= 0;
10579 int parentHeight
= 0;
10582 int maxHeight
= -1;
10584 wxRichTextBuffer
* buffer
= GetBuffer();
10588 if (buffer
->GetRichTextCtrl())
10590 // Subtract borders
10591 sz
= buffer
->GetRichTextCtrl()->GetClientSize();
10593 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
10594 marginRect
= wxRect(0, 0, sz
.x
, sz
.y
);
10595 buffer
->GetBoxRects(dc
, buffer
, buffer
->GetAttributes(), marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
10597 sz
= contentRect
.GetSize();
10599 // Start with a maximum width of the control size, even if not specified by the content,
10600 // to minimize the amount of picture overlapping the right-hand side
10604 sz
= buffer
->GetCachedSize();
10605 parentWidth
= sz
.GetWidth();
10606 parentHeight
= sz
.GetHeight();
10609 if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetWidth().GetValue() > 0)
10611 if (parentWidth
> 0 && GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
10612 width
= (int) ((GetAttributes().GetTextBoxAttr().GetWidth().GetValue() * parentWidth
)/100.0);
10613 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
10614 width
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetWidth().GetValue());
10615 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
10616 width
= GetAttributes().GetTextBoxAttr().GetWidth().GetValue();
10619 // Limit to max width
10621 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() > 0)
10625 if (parentWidth
> 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
10626 mw
= (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() * parentWidth
)/100.0);
10627 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
10628 mw
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue());
10629 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
10630 mw
= GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue();
10632 // If we already have a smaller max width due to the constraints of the control size,
10633 // don't use the larger max width.
10634 if (mw
!= -1 && ((maxWidth
== -1) || (mw
< maxWidth
)))
10638 if (maxWidth
> 0 && width
> maxWidth
)
10641 // Preserve the aspect ratio
10642 if (width
!= m_originalImageSize
.GetWidth())
10643 height
= (int) (float(m_originalImageSize
.GetHeight()) * (float(width
)/float(m_originalImageSize
.GetWidth())));
10645 if (GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetHeight().GetValue() > 0)
10647 if (parentHeight
> 0 && GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
10648 height
= (int) ((GetAttributes().GetTextBoxAttr().GetHeight().GetValue() * parentHeight
)/100.0);
10649 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
10650 height
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetHeight().GetValue());
10651 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
10652 height
= GetAttributes().GetTextBoxAttr().GetHeight().GetValue();
10654 // Preserve the aspect ratio
10655 if (height
!= m_originalImageSize
.GetHeight())
10656 width
= (int) (float(m_originalImageSize
.GetWidth()) * (float(height
)/float(m_originalImageSize
.GetHeight())));
10659 // Limit to max height
10661 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() > 0)
10663 if (parentHeight
> 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
10664 maxHeight
= (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() * parentHeight
)/100.0);
10665 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
10666 maxHeight
= ConvertTenthsMMToPixels(dc
, GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue());
10667 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
10668 maxHeight
= GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue();
10671 if (maxHeight
> 0 && height
> maxHeight
)
10673 height
= maxHeight
;
10675 // Preserve the aspect ratio
10676 if (height
!= m_originalImageSize
.GetHeight())
10677 width
= (int) (float(m_originalImageSize
.GetWidth()) * (float(height
)/float(m_originalImageSize
.GetHeight())));
10680 if (m_imageCache
.IsOk() && m_imageCache
.GetWidth() == width
&& m_imageCache
.GetHeight() == height
)
10682 // Do nothing, we didn't need to change the image cache
10688 m_imageBlock
.Load(image
);
10693 if (image
.GetWidth() == width
&& image
.GetHeight() == height
)
10694 m_imageCache
= wxBitmap(image
);
10697 // If the original width and height is small, e.g. 400 or below,
10698 // scale up and then down to improve image quality. This can make
10699 // a big difference, with not much performance hit.
10700 int upscaleThreshold
= 400;
10702 if (image
.GetWidth() <= upscaleThreshold
|| image
.GetHeight() <= upscaleThreshold
)
10704 img
= image
.Scale(image
.GetWidth()*2, image
.GetHeight()*2);
10705 img
.Rescale(width
, height
, wxIMAGE_QUALITY_HIGH
);
10708 img
= image
.Scale(width
, height
, wxIMAGE_QUALITY_HIGH
);
10709 m_imageCache
= wxBitmap(img
);
10713 return m_imageCache
.IsOk();
10717 bool wxRichTextImage::Draw(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRichTextRange
& WXUNUSED(range
), const wxRichTextSelection
& selection
, const wxRect
& rect
, int WXUNUSED(descent
), int WXUNUSED(style
))
10722 // Don't need cached size AFAIK
10723 // wxSize size = GetCachedSize();
10724 if (!LoadImageCache(dc
))
10727 wxRichTextAttr
attr(GetAttributes());
10728 context
.ApplyVirtualAttributes(attr
, this);
10730 DrawBoxAttributes(dc
, GetBuffer(), attr
, wxRect(rect
.GetPosition(), GetCachedSize()));
10732 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
10733 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
10734 marginRect
= rect
; // outer rectangle, will calculate contentRect
10735 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
10737 dc
.DrawBitmap(m_imageCache
, contentRect
.x
, contentRect
.y
, true);
10739 if (selection
.WithinSelection(GetRange().GetStart(), this))
10741 wxCheckSetBrush(dc
, *wxBLACK_BRUSH
);
10742 wxCheckSetPen(dc
, *wxBLACK_PEN
);
10743 dc
.SetLogicalFunction(wxINVERT
);
10744 dc
.DrawRectangle(contentRect
);
10745 dc
.SetLogicalFunction(wxCOPY
);
10751 /// Lay the item out
10752 bool wxRichTextImage::Layout(wxDC
& dc
, wxRichTextDrawingContext
& context
, const wxRect
& rect
, const wxRect
& WXUNUSED(parentRect
), int WXUNUSED(style
))
10754 if (!LoadImageCache(dc
))
10757 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
10758 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
10759 contentRect
= wxRect(wxPoint(0,0), imageSize
);
10761 wxRichTextAttr
attr(GetAttributes());
10762 context
.ApplyVirtualAttributes(attr
, this);
10764 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
10766 wxSize overallSize
= marginRect
.GetSize();
10768 SetCachedSize(overallSize
);
10769 SetMaxSize(overallSize
);
10770 SetMinSize(overallSize
);
10771 SetPosition(rect
.GetPosition());
10776 /// Get/set the object size for the given range. Returns false if the range
10777 /// is invalid for this object.
10778 bool wxRichTextImage::GetRangeSize(const wxRichTextRange
& range
, wxSize
& size
, int& WXUNUSED(descent
), wxDC
& dc
, wxRichTextDrawingContext
& context
, int WXUNUSED(flags
), wxPoint
WXUNUSED(position
), wxArrayInt
* partialExtents
) const
10780 if (!range
.IsWithin(GetRange()))
10783 if (!((wxRichTextImage
*)this)->LoadImageCache(dc
))
10785 size
.x
= 0; size
.y
= 0;
10786 if (partialExtents
)
10787 partialExtents
->Add(0);
10791 wxRichTextAttr
attr(GetAttributes());
10792 context
.ApplyVirtualAttributes(attr
, (wxRichTextObject
*) this);
10794 wxSize
imageSize(m_imageCache
.GetWidth(), m_imageCache
.GetHeight());
10795 wxRect marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
;
10796 contentRect
= wxRect(wxPoint(0,0), imageSize
);
10797 GetBoxRects(dc
, GetBuffer(), attr
, marginRect
, borderRect
, contentRect
, paddingRect
, outlineRect
);
10799 wxSize overallSize
= marginRect
.GetSize();
10801 if (partialExtents
)
10802 partialExtents
->Add(overallSize
.x
);
10804 size
= overallSize
;
10809 // Get the 'natural' size for an object. For an image, it would be the
10811 wxTextAttrSize
wxRichTextImage::GetNaturalSize() const
10813 wxTextAttrSize size
;
10814 if (GetImageCache().IsOk())
10816 size
.SetWidth(GetImageCache().GetWidth(), wxTEXT_ATTR_UNITS_PIXELS
);
10817 size
.SetHeight(GetImageCache().GetHeight(), wxTEXT_ATTR_UNITS_PIXELS
);
10824 void wxRichTextImage::Copy(const wxRichTextImage
& obj
)
10826 wxRichTextObject::Copy(obj
);
10828 m_imageBlock
= obj
.m_imageBlock
;
10829 m_originalImageSize
= obj
.m_originalImageSize
;
10832 /// Edit properties via a GUI
10833 bool wxRichTextImage::EditProperties(wxWindow
* parent
, wxRichTextBuffer
* buffer
)
10835 wxRichTextObjectPropertiesDialog
imageDlg(this, wxGetTopLevelParent(parent
), wxID_ANY
, _("Picture Properties"));
10836 imageDlg
.SetAttributes(GetAttributes());
10838 if (imageDlg
.ShowModal() == wxID_OK
)
10840 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
10841 // indeterminate in the object.
10842 imageDlg
.ApplyStyle(buffer
->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO
|wxRICHTEXT_SETSTYLE_RESET
);
10854 /// Compare two attribute objects
10855 bool wxTextAttrEq(const wxRichTextAttr
& attr1
, const wxRichTextAttr
& attr2
)
10857 return (attr1
== attr2
);
10860 // Partial equality test taking flags into account
10861 bool wxTextAttrEqPartial(const wxRichTextAttr
& attr1
, const wxRichTextAttr
& attr2
)
10863 return attr1
.EqPartial(attr2
);
10867 bool wxRichTextTabsEq(const wxArrayInt
& tabs1
, const wxArrayInt
& tabs2
)
10869 if (tabs1
.GetCount() != tabs2
.GetCount())
10873 for (i
= 0; i
< tabs1
.GetCount(); i
++)
10875 if (tabs1
[i
] != tabs2
[i
])
10881 bool wxRichTextApplyStyle(wxRichTextAttr
& destStyle
, const wxRichTextAttr
& style
, wxRichTextAttr
* compareWith
)
10883 return destStyle
.Apply(style
, compareWith
);
10886 // Remove attributes
10887 bool wxRichTextRemoveStyle(wxRichTextAttr
& destStyle
, const wxRichTextAttr
& style
)
10889 return destStyle
.RemoveStyle(style
);
10892 /// Combine two bitlists, specifying the bits of interest with separate flags.
10893 bool wxRichTextCombineBitlists(int& valueA
, int valueB
, int& flagsA
, int flagsB
)
10895 return wxRichTextAttr::CombineBitlists(valueA
, valueB
, flagsA
, flagsB
);
10898 /// Compare two bitlists
10899 bool wxRichTextBitlistsEqPartial(int valueA
, int valueB
, int flags
)
10901 return wxRichTextAttr::BitlistsEqPartial(valueA
, valueB
, flags
);
10904 /// Split into paragraph and character styles
10905 bool wxRichTextSplitParaCharStyles(const wxRichTextAttr
& style
, wxRichTextAttr
& parStyle
, wxRichTextAttr
& charStyle
)
10907 return wxRichTextAttr::SplitParaCharStyles(style
, parStyle
, charStyle
);
10910 /// Convert a decimal to Roman numerals
10911 wxString
wxRichTextDecimalToRoman(long n
)
10913 static wxArrayInt decimalNumbers
;
10914 static wxArrayString romanNumbers
;
10919 decimalNumbers
.Clear();
10920 romanNumbers
.Clear();
10921 return wxEmptyString
;
10924 if (decimalNumbers
.GetCount() == 0)
10926 #define wxRichTextAddDecRom(n, r) decimalNumbers.Add(n); romanNumbers.Add(r);
10928 wxRichTextAddDecRom(1000, wxT("M"));
10929 wxRichTextAddDecRom(900, wxT("CM"));
10930 wxRichTextAddDecRom(500, wxT("D"));
10931 wxRichTextAddDecRom(400, wxT("CD"));
10932 wxRichTextAddDecRom(100, wxT("C"));
10933 wxRichTextAddDecRom(90, wxT("XC"));
10934 wxRichTextAddDecRom(50, wxT("L"));
10935 wxRichTextAddDecRom(40, wxT("XL"));
10936 wxRichTextAddDecRom(10, wxT("X"));
10937 wxRichTextAddDecRom(9, wxT("IX"));
10938 wxRichTextAddDecRom(5, wxT("V"));
10939 wxRichTextAddDecRom(4, wxT("IV"));
10940 wxRichTextAddDecRom(1, wxT("I"));
10946 while (n
> 0 && i
< 13)
10948 if (n
>= decimalNumbers
[i
])
10950 n
-= decimalNumbers
[i
];
10951 roman
+= romanNumbers
[i
];
10958 if (roman
.IsEmpty())
10964 * wxRichTextFileHandler
10965 * Base class for file handlers
10968 IMPLEMENT_CLASS(wxRichTextFileHandler
, wxObject
)
10970 #if wxUSE_FFILE && wxUSE_STREAMS
10971 bool wxRichTextFileHandler::LoadFile(wxRichTextBuffer
*buffer
, const wxString
& filename
)
10973 wxFFileInputStream
stream(filename
);
10975 return LoadFile(buffer
, stream
);
10980 bool wxRichTextFileHandler::SaveFile(wxRichTextBuffer
*buffer
, const wxString
& filename
)
10982 wxFFileOutputStream
stream(filename
);
10984 return SaveFile(buffer
, stream
);
10988 #endif // wxUSE_FFILE && wxUSE_STREAMS
10990 /// Can we handle this filename (if using files)? By default, checks the extension.
10991 bool wxRichTextFileHandler::CanHandle(const wxString
& filename
) const
10993 wxString path
, file
, ext
;
10994 wxFileName::SplitPath(filename
, & path
, & file
, & ext
);
10996 return (ext
.Lower() == GetExtension());
11000 * wxRichTextTextHandler
11001 * Plain text handler
11004 IMPLEMENT_CLASS(wxRichTextPlainTextHandler
, wxRichTextFileHandler
)
11007 bool wxRichTextPlainTextHandler::DoLoadFile(wxRichTextBuffer
*buffer
, wxInputStream
& stream
)
11009 if (!stream
.IsOk())
11015 while (!stream
.Eof())
11017 int ch
= stream
.GetC();
11021 if (ch
== 10 && lastCh
!= 13)
11024 if (ch
> 0 && ch
!= 10)
11031 buffer
->ResetAndClearCommands();
11033 buffer
->AddParagraphs(str
);
11034 buffer
->UpdateRanges();
11039 bool wxRichTextPlainTextHandler::DoSaveFile(wxRichTextBuffer
*buffer
, wxOutputStream
& stream
)
11041 if (!stream
.IsOk())
11044 wxString text
= buffer
->GetText();
11046 wxString newLine
= wxRichTextLineBreakChar
;
11047 text
.Replace(newLine
, wxT("\n"));
11049 wxCharBuffer buf
= text
.ToAscii();
11051 stream
.Write((const char*) buf
, text
.length());
11054 #endif // wxUSE_STREAMS
11057 * Stores information about an image, in binary in-memory form
11060 wxRichTextImageBlock::wxRichTextImageBlock()
11065 wxRichTextImageBlock::wxRichTextImageBlock(const wxRichTextImageBlock
& block
):wxObject()
11071 wxRichTextImageBlock::~wxRichTextImageBlock()
11076 void wxRichTextImageBlock::Init()
11080 m_imageType
= wxBITMAP_TYPE_INVALID
;
11083 void wxRichTextImageBlock::Clear()
11087 m_imageType
= wxBITMAP_TYPE_INVALID
;
11091 // Load the original image into a memory block.
11092 // If the image is not a JPEG, we must convert it into a JPEG
11093 // to conserve space.
11094 // If it's not a JPEG we can make use of 'image', already scaled, so we don't have to
11095 // load the image a 2nd time.
11097 bool wxRichTextImageBlock::MakeImageBlock(const wxString
& filename
, wxBitmapType imageType
,
11098 wxImage
& image
, bool convertToJPEG
)
11100 m_imageType
= imageType
;
11102 wxString
filenameToRead(filename
);
11103 bool removeFile
= false;
11105 if (imageType
== wxBITMAP_TYPE_INVALID
)
11106 return false; // Could not determine image type
11108 if ((imageType
!= wxBITMAP_TYPE_JPEG
) && convertToJPEG
)
11110 wxString tempFile
=
11111 wxFileName::CreateTempFileName(_("image"));
11113 wxASSERT(!tempFile
.IsEmpty());
11115 image
.SaveFile(tempFile
, wxBITMAP_TYPE_JPEG
);
11116 filenameToRead
= tempFile
;
11119 m_imageType
= wxBITMAP_TYPE_JPEG
;
11122 if (!file
.Open(filenameToRead
))
11125 m_dataSize
= (size_t) file
.Length();
11130 m_data
= ReadBlock(filenameToRead
, m_dataSize
);
11133 wxRemoveFile(filenameToRead
);
11135 return (m_data
!= NULL
);
11138 // Make an image block from the wxImage in the given
11140 bool wxRichTextImageBlock::MakeImageBlock(wxImage
& image
, wxBitmapType imageType
, int quality
)
11142 image
.SetOption(wxT("quality"), quality
);
11144 if (imageType
== wxBITMAP_TYPE_INVALID
)
11145 return false; // Could not determine image type
11147 return DoMakeImageBlock(image
, imageType
);
11150 // Uses a const wxImage for efficiency, but can't set quality (only relevant for JPEG)
11151 bool wxRichTextImageBlock::MakeImageBlockDefaultQuality(const wxImage
& image
, wxBitmapType imageType
)
11153 if (imageType
== wxBITMAP_TYPE_INVALID
)
11154 return false; // Could not determine image type
11156 return DoMakeImageBlock(image
, imageType
);
11159 // Makes the image block
11160 bool wxRichTextImageBlock::DoMakeImageBlock(const wxImage
& image
, wxBitmapType imageType
)
11162 wxMemoryOutputStream memStream
;
11163 if (!image
.SaveFile(memStream
, imageType
))
11168 unsigned char* block
= new unsigned char[memStream
.GetSize()];
11176 m_imageType
= imageType
;
11177 m_dataSize
= memStream
.GetSize();
11179 memStream
.CopyTo(m_data
, m_dataSize
);
11181 return (m_data
!= NULL
);
11185 bool wxRichTextImageBlock::Write(const wxString
& filename
)
11187 return WriteBlock(filename
, m_data
, m_dataSize
);
11190 void wxRichTextImageBlock::Copy(const wxRichTextImageBlock
& block
)
11192 m_imageType
= block
.m_imageType
;
11194 m_dataSize
= block
.m_dataSize
;
11195 if (m_dataSize
== 0)
11198 m_data
= new unsigned char[m_dataSize
];
11200 for (i
= 0; i
< m_dataSize
; i
++)
11201 m_data
[i
] = block
.m_data
[i
];
11205 void wxRichTextImageBlock::operator=(const wxRichTextImageBlock
& block
)
11210 // Load a wxImage from the block
11211 bool wxRichTextImageBlock::Load(wxImage
& image
)
11216 // Read in the image.
11218 wxMemoryInputStream
mstream(m_data
, m_dataSize
);
11219 bool success
= image
.LoadFile(mstream
, GetImageType());
11221 wxString tempFile
= wxFileName::CreateTempFileName(_("image"));
11222 wxASSERT(!tempFile
.IsEmpty());
11224 if (!WriteBlock(tempFile
, m_data
, m_dataSize
))
11228 success
= image
.LoadFile(tempFile
, GetImageType());
11229 wxRemoveFile(tempFile
);
11235 // Write data in hex to a stream
11236 bool wxRichTextImageBlock::WriteHex(wxOutputStream
& stream
)
11238 if (m_dataSize
== 0)
11241 int bufSize
= 100000;
11242 if (int(2*m_dataSize
) < bufSize
)
11243 bufSize
= 2*m_dataSize
;
11244 char* buf
= new char[bufSize
+1];
11246 int left
= m_dataSize
;
11251 if (left
*2 > bufSize
)
11253 n
= bufSize
; left
-= (bufSize
/2);
11257 n
= left
*2; left
= 0;
11261 for (i
= 0; i
< (n
/2); i
++)
11263 wxDecToHex(m_data
[j
], b
, b
+1);
11268 stream
.Write((const char*) buf
, n
);
11274 // Read data in hex from a stream
11275 bool wxRichTextImageBlock::ReadHex(wxInputStream
& stream
, int length
, wxBitmapType imageType
)
11277 int dataSize
= length
/2;
11282 // create a null terminated temporary string:
11286 m_data
= new unsigned char[dataSize
];
11288 for (i
= 0; i
< dataSize
; i
++)
11290 str
[0] = (char)stream
.GetC();
11291 str
[1] = (char)stream
.GetC();
11293 m_data
[i
] = (unsigned char)wxHexToDec(str
);
11296 m_dataSize
= dataSize
;
11297 m_imageType
= imageType
;
11302 // Allocate and read from stream as a block of memory
11303 unsigned char* wxRichTextImageBlock::ReadBlock(wxInputStream
& stream
, size_t size
)
11305 unsigned char* block
= new unsigned char[size
];
11309 stream
.Read(block
, size
);
11314 unsigned char* wxRichTextImageBlock::ReadBlock(const wxString
& filename
, size_t size
)
11316 wxFileInputStream
stream(filename
);
11317 if (!stream
.IsOk())
11320 return ReadBlock(stream
, size
);
11323 // Write memory block to stream
11324 bool wxRichTextImageBlock::WriteBlock(wxOutputStream
& stream
, unsigned char* block
, size_t size
)
11326 stream
.Write((void*) block
, size
);
11327 return stream
.IsOk();
11331 // Write memory block to file
11332 bool wxRichTextImageBlock::WriteBlock(const wxString
& filename
, unsigned char* block
, size_t size
)
11334 wxFileOutputStream
outStream(filename
);
11335 if (!outStream
.IsOk())
11338 return WriteBlock(outStream
, block
, size
);
11341 // Gets the extension for the block's type
11342 wxString
wxRichTextImageBlock::GetExtension() const
11344 wxImageHandler
* handler
= wxImage::FindHandler(GetImageType());
11346 return handler
->GetExtension();
11348 return wxEmptyString
;
11354 * The data object for a wxRichTextBuffer
11357 const wxChar
*wxRichTextBufferDataObject::ms_richTextBufferFormatId
= wxT("wxShape");
11359 wxRichTextBufferDataObject::wxRichTextBufferDataObject(wxRichTextBuffer
* richTextBuffer
)
11361 m_richTextBuffer
= richTextBuffer
;
11363 // this string should uniquely identify our format, but is otherwise
11365 m_formatRichTextBuffer
.SetId(GetRichTextBufferFormatId());
11367 SetFormat(m_formatRichTextBuffer
);
11370 wxRichTextBufferDataObject::~wxRichTextBufferDataObject()
11372 delete m_richTextBuffer
;
11375 // after a call to this function, the richTextBuffer is owned by the caller and it
11376 // is responsible for deleting it!
11377 wxRichTextBuffer
* wxRichTextBufferDataObject::GetRichTextBuffer()
11379 wxRichTextBuffer
* richTextBuffer
= m_richTextBuffer
;
11380 m_richTextBuffer
= NULL
;
11382 return richTextBuffer
;
11385 wxDataFormat
wxRichTextBufferDataObject::GetPreferredFormat(Direction
WXUNUSED(dir
)) const
11387 return m_formatRichTextBuffer
;
11390 size_t wxRichTextBufferDataObject::GetDataSize() const
11392 if (!m_richTextBuffer
)
11398 wxStringOutputStream
stream(& bufXML
);
11399 if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
))
11401 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
11407 wxCharBuffer buffer
= bufXML
.mb_str(wxConvUTF8
);
11408 return strlen(buffer
) + 1;
11410 return bufXML
.Length()+1;
11414 bool wxRichTextBufferDataObject::GetDataHere(void *pBuf
) const
11416 if (!pBuf
|| !m_richTextBuffer
)
11422 wxStringOutputStream
stream(& bufXML
);
11423 if (!m_richTextBuffer
->SaveFile(stream
, wxRICHTEXT_TYPE_XML
))
11425 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
11431 wxCharBuffer buffer
= bufXML
.mb_str(wxConvUTF8
);
11432 size_t len
= strlen(buffer
);
11433 memcpy((char*) pBuf
, (const char*) buffer
, len
);
11434 ((char*) pBuf
)[len
] = 0;
11436 size_t len
= bufXML
.Length();
11437 memcpy((char*) pBuf
, (const char*) bufXML
.c_str(), len
);
11438 ((char*) pBuf
)[len
] = 0;
11444 bool wxRichTextBufferDataObject::SetData(size_t WXUNUSED(len
), const void *buf
)
11446 wxDELETE(m_richTextBuffer
);
11448 wxString
bufXML((const char*) buf
, wxConvUTF8
);
11450 m_richTextBuffer
= new wxRichTextBuffer
;
11452 wxStringInputStream
stream(bufXML
);
11453 if (!m_richTextBuffer
->LoadFile(stream
, wxRICHTEXT_TYPE_XML
))
11455 wxLogError(wxT("Could not read the buffer from an XML stream.\nYou may have forgotten to add the XML file handler."));
11457 wxDELETE(m_richTextBuffer
);
11469 * wxRichTextFontTable
11470 * Manages quick access to a pool of fonts for rendering rich text
11473 WX_DECLARE_STRING_HASH_MAP_WITH_DECL(wxFont
, wxRichTextFontTableHashMap
, class WXDLLIMPEXP_RICHTEXT
);
11475 class wxRichTextFontTableData
: public wxObjectRefData
11478 wxRichTextFontTableData() {}
11480 wxFont
FindFont(const wxRichTextAttr
& fontSpec
);
11482 wxRichTextFontTableHashMap m_hashMap
;
11485 wxFont
wxRichTextFontTableData::FindFont(const wxRichTextAttr
& fontSpec
)
11487 wxString
facename(fontSpec
.GetFontFaceName());
11488 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()));
11489 wxRichTextFontTableHashMap::iterator entry
= m_hashMap
.find(spec
);
11491 if ( entry
== m_hashMap
.end() )
11493 wxFont
font(fontSpec
.GetFontSize(), wxDEFAULT
, fontSpec
.GetFontStyle(), fontSpec
.GetFontWeight(), fontSpec
.GetFontUnderlined(), facename
.c_str());
11494 m_hashMap
[spec
] = font
;
11499 return entry
->second
;
11503 IMPLEMENT_DYNAMIC_CLASS(wxRichTextFontTable
, wxObject
)
11505 wxRichTextFontTable::wxRichTextFontTable()
11507 m_refData
= new wxRichTextFontTableData
;
11510 wxRichTextFontTable::wxRichTextFontTable(const wxRichTextFontTable
& table
)
11516 wxRichTextFontTable::~wxRichTextFontTable()
11521 bool wxRichTextFontTable::operator == (const wxRichTextFontTable
& table
) const
11523 return (m_refData
== table
.m_refData
);
11526 void wxRichTextFontTable::operator= (const wxRichTextFontTable
& table
)
11531 wxFont
wxRichTextFontTable::FindFont(const wxRichTextAttr
& fontSpec
)
11533 wxRichTextFontTableData
* data
= (wxRichTextFontTableData
*) m_refData
;
11535 return data
->FindFont(fontSpec
);
11540 void wxRichTextFontTable::Clear()
11542 wxRichTextFontTableData
* data
= (wxRichTextFontTableData
*) m_refData
;
11544 data
->m_hashMap
.clear();
11549 void wxTextBoxAttr::Reset()
11552 m_floatMode
= wxTEXT_BOX_ATTR_FLOAT_NONE
;
11553 m_clearMode
= wxTEXT_BOX_ATTR_CLEAR_NONE
;
11554 m_collapseMode
= wxTEXT_BOX_ATTR_COLLAPSE_NONE
;
11555 m_verticalAlignment
= wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_NONE
;
11556 m_boxStyleName
= wxEmptyString
;
11560 m_position
.Reset();
11571 bool wxTextBoxAttr::operator== (const wxTextBoxAttr
& attr
) const
11574 m_flags
== attr
.m_flags
&&
11575 m_floatMode
== attr
.m_floatMode
&&
11576 m_clearMode
== attr
.m_clearMode
&&
11577 m_collapseMode
== attr
.m_collapseMode
&&
11578 m_verticalAlignment
== attr
.m_verticalAlignment
&&
11580 m_margins
== attr
.m_margins
&&
11581 m_padding
== attr
.m_padding
&&
11582 m_position
== attr
.m_position
&&
11584 m_size
== attr
.m_size
&&
11585 m_minSize
== attr
.m_minSize
&&
11586 m_maxSize
== attr
.m_maxSize
&&
11588 m_border
== attr
.m_border
&&
11589 m_outline
== attr
.m_outline
&&
11591 m_boxStyleName
== attr
.m_boxStyleName
11595 // Partial equality test
11596 bool wxTextBoxAttr::EqPartial(const wxTextBoxAttr
& attr
) const
11598 if (attr
.HasFloatMode() && HasFloatMode() && (GetFloatMode() != attr
.GetFloatMode()))
11601 if (attr
.HasClearMode() && HasClearMode() && (GetClearMode() != attr
.GetClearMode()))
11604 if (attr
.HasCollapseBorders() && HasCollapseBorders() && (attr
.GetCollapseBorders() != GetCollapseBorders()))
11607 if (attr
.HasVerticalAlignment() && HasVerticalAlignment() && (attr
.GetVerticalAlignment() != GetVerticalAlignment()))
11610 if (attr
.HasBoxStyleName() && HasBoxStyleName() && (attr
.GetBoxStyleName() != GetBoxStyleName()))
11615 if (!m_position
.EqPartial(attr
.m_position
))
11620 if (!m_size
.EqPartial(attr
.m_size
))
11622 if (!m_minSize
.EqPartial(attr
.m_minSize
))
11624 if (!m_maxSize
.EqPartial(attr
.m_maxSize
))
11629 if (!m_margins
.EqPartial(attr
.m_margins
))
11634 if (!m_padding
.EqPartial(attr
.m_padding
))
11639 if (!GetBorder().EqPartial(attr
.GetBorder()))
11644 if (!GetOutline().EqPartial(attr
.GetOutline()))
11650 // Merges the given attributes. If compareWith
11651 // is non-NULL, then it will be used to mask out those attributes that are the same in style
11652 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
11653 bool wxTextBoxAttr::Apply(const wxTextBoxAttr
& attr
, const wxTextBoxAttr
* compareWith
)
11655 if (attr
.HasFloatMode())
11657 if (!(compareWith
&& compareWith
->HasFloatMode() && compareWith
->GetFloatMode() == attr
.GetFloatMode()))
11658 SetFloatMode(attr
.GetFloatMode());
11661 if (attr
.HasClearMode())
11663 if (!(compareWith
&& compareWith
->HasClearMode() && compareWith
->GetClearMode() == attr
.GetClearMode()))
11664 SetClearMode(attr
.GetClearMode());
11667 if (attr
.HasCollapseBorders())
11669 if (!(compareWith
&& compareWith
->HasCollapseBorders() && compareWith
->GetCollapseBorders() == attr
.GetCollapseBorders()))
11670 SetCollapseBorders(attr
.GetCollapseBorders());
11673 if (attr
.HasVerticalAlignment())
11675 if (!(compareWith
&& compareWith
->HasVerticalAlignment() && compareWith
->GetVerticalAlignment() == attr
.GetVerticalAlignment()))
11676 SetVerticalAlignment(attr
.GetVerticalAlignment());
11679 if (attr
.HasBoxStyleName())
11681 if (!(compareWith
&& compareWith
->HasBoxStyleName() && compareWith
->GetBoxStyleName() == attr
.GetBoxStyleName()))
11682 SetBoxStyleName(attr
.GetBoxStyleName());
11685 m_margins
.Apply(attr
.m_margins
, compareWith
? (& attr
.m_margins
) : (const wxTextAttrDimensions
*) NULL
);
11686 m_padding
.Apply(attr
.m_padding
, compareWith
? (& attr
.m_padding
) : (const wxTextAttrDimensions
*) NULL
);
11687 m_position
.Apply(attr
.m_position
, compareWith
? (& attr
.m_position
) : (const wxTextAttrDimensions
*) NULL
);
11689 m_size
.Apply(attr
.m_size
, compareWith
? (& attr
.m_size
) : (const wxTextAttrSize
*) NULL
);
11690 m_minSize
.Apply(attr
.m_minSize
, compareWith
? (& attr
.m_minSize
) : (const wxTextAttrSize
*) NULL
);
11691 m_maxSize
.Apply(attr
.m_maxSize
, compareWith
? (& attr
.m_maxSize
) : (const wxTextAttrSize
*) NULL
);
11693 m_border
.Apply(attr
.m_border
, compareWith
? (& attr
.m_border
) : (const wxTextAttrBorders
*) NULL
);
11694 m_outline
.Apply(attr
.m_outline
, compareWith
? (& attr
.m_outline
) : (const wxTextAttrBorders
*) NULL
);
11699 // Remove specified attributes from this object
11700 bool wxTextBoxAttr::RemoveStyle(const wxTextBoxAttr
& attr
)
11702 if (attr
.HasFloatMode())
11703 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT
);
11705 if (attr
.HasClearMode())
11706 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR
);
11708 if (attr
.HasCollapseBorders())
11709 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
11711 if (attr
.HasVerticalAlignment())
11712 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
11714 if (attr
.HasBoxStyleName())
11716 SetBoxStyleName(wxEmptyString
);
11717 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
11720 m_margins
.RemoveStyle(attr
.m_margins
);
11721 m_padding
.RemoveStyle(attr
.m_padding
);
11722 m_position
.RemoveStyle(attr
.m_position
);
11724 m_size
.RemoveStyle(attr
.m_size
);
11725 m_minSize
.RemoveStyle(attr
.m_minSize
);
11726 m_maxSize
.RemoveStyle(attr
.m_maxSize
);
11728 m_border
.RemoveStyle(attr
.m_border
);
11729 m_outline
.RemoveStyle(attr
.m_outline
);
11734 // Collects the attributes that are common to a range of content, building up a note of
11735 // which attributes are absent in some objects and which clash in some objects.
11736 void wxTextBoxAttr::CollectCommonAttributes(const wxTextBoxAttr
& attr
, wxTextBoxAttr
& clashingAttr
, wxTextBoxAttr
& absentAttr
)
11738 if (attr
.HasFloatMode())
11740 if (!clashingAttr
.HasFloatMode() && !absentAttr
.HasFloatMode())
11742 if (HasFloatMode())
11744 if (GetFloatMode() != attr
.GetFloatMode())
11746 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_FLOAT
);
11747 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT
);
11751 SetFloatMode(attr
.GetFloatMode());
11755 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_FLOAT
);
11757 if (attr
.HasClearMode())
11759 if (!clashingAttr
.HasClearMode() && !absentAttr
.HasClearMode())
11761 if (HasClearMode())
11763 if (GetClearMode() != attr
.GetClearMode())
11765 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_CLEAR
);
11766 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR
);
11770 SetClearMode(attr
.GetClearMode());
11774 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_CLEAR
);
11776 if (attr
.HasCollapseBorders())
11778 if (!clashingAttr
.HasCollapseBorders() && !absentAttr
.HasCollapseBorders())
11780 if (HasCollapseBorders())
11782 if (GetCollapseBorders() != attr
.GetCollapseBorders())
11784 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
11785 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
11789 SetCollapseBorders(attr
.GetCollapseBorders());
11793 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS
);
11795 if (attr
.HasVerticalAlignment())
11797 if (!clashingAttr
.HasVerticalAlignment() && !absentAttr
.HasVerticalAlignment())
11799 if (HasVerticalAlignment())
11801 if (GetVerticalAlignment() != attr
.GetVerticalAlignment())
11803 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
11804 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
11808 SetVerticalAlignment(attr
.GetVerticalAlignment());
11812 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT
);
11814 if (attr
.HasBoxStyleName())
11816 if (!clashingAttr
.HasBoxStyleName() && !absentAttr
.HasBoxStyleName())
11818 if (HasBoxStyleName())
11820 if (GetBoxStyleName() != attr
.GetBoxStyleName())
11822 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
11823 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
11827 SetBoxStyleName(attr
.GetBoxStyleName());
11831 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME
);
11833 m_margins
.CollectCommonAttributes(attr
.m_margins
, clashingAttr
.m_margins
, absentAttr
.m_margins
);
11834 m_padding
.CollectCommonAttributes(attr
.m_padding
, clashingAttr
.m_padding
, absentAttr
.m_padding
);
11835 m_position
.CollectCommonAttributes(attr
.m_position
, clashingAttr
.m_position
, absentAttr
.m_position
);
11837 m_size
.CollectCommonAttributes(attr
.m_size
, clashingAttr
.m_size
, absentAttr
.m_size
);
11838 m_minSize
.CollectCommonAttributes(attr
.m_minSize
, clashingAttr
.m_minSize
, absentAttr
.m_minSize
);
11839 m_maxSize
.CollectCommonAttributes(attr
.m_maxSize
, clashingAttr
.m_maxSize
, absentAttr
.m_maxSize
);
11841 m_border
.CollectCommonAttributes(attr
.m_border
, clashingAttr
.m_border
, absentAttr
.m_border
);
11842 m_outline
.CollectCommonAttributes(attr
.m_outline
, clashingAttr
.m_outline
, absentAttr
.m_outline
);
11845 bool wxTextBoxAttr::IsDefault() const
11847 return GetFlags() == 0 && !m_border
.IsValid() && !m_outline
.IsValid() &&
11848 !m_size
.IsValid() && !m_minSize
.IsValid() && !m_maxSize
.IsValid() &&
11849 !m_position
.IsValid() && !m_padding
.IsValid() && !m_margins
.IsValid();
11854 void wxRichTextAttr::Copy(const wxRichTextAttr
& attr
)
11856 wxTextAttr::Copy(attr
);
11858 m_textBoxAttr
= attr
.m_textBoxAttr
;
11861 bool wxRichTextAttr::operator==(const wxRichTextAttr
& attr
) const
11863 if (!(wxTextAttr::operator==(attr
)))
11866 return (m_textBoxAttr
== attr
.m_textBoxAttr
);
11869 // Partial equality test taking comparison object into account
11870 bool wxRichTextAttr::EqPartial(const wxRichTextAttr
& attr
) const
11872 if (!(wxTextAttr::EqPartial(attr
)))
11875 return m_textBoxAttr
.EqPartial(attr
.m_textBoxAttr
);
11878 // Merges the given attributes. If compareWith
11879 // is non-NULL, then it will be used to mask out those attributes that are the same in style
11880 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
11881 bool wxRichTextAttr::Apply(const wxRichTextAttr
& style
, const wxRichTextAttr
* compareWith
)
11883 wxTextAttr::Apply(style
, compareWith
);
11885 return m_textBoxAttr
.Apply(style
.m_textBoxAttr
, compareWith
? (& compareWith
->m_textBoxAttr
) : (const wxTextBoxAttr
*) NULL
);
11888 // Remove specified attributes from this object
11889 bool wxRichTextAttr::RemoveStyle(const wxRichTextAttr
& attr
)
11891 wxTextAttr::RemoveStyle(*this, attr
);
11893 return m_textBoxAttr
.RemoveStyle(attr
.m_textBoxAttr
);
11896 // Collects the attributes that are common to a range of content, building up a note of
11897 // which attributes are absent in some objects and which clash in some objects.
11898 void wxRichTextAttr::CollectCommonAttributes(const wxRichTextAttr
& attr
, wxRichTextAttr
& clashingAttr
, wxRichTextAttr
& absentAttr
)
11900 wxTextAttrCollectCommonAttributes(*this, attr
, clashingAttr
, absentAttr
);
11902 m_textBoxAttr
.CollectCommonAttributes(attr
.m_textBoxAttr
, clashingAttr
.m_textBoxAttr
, absentAttr
.m_textBoxAttr
);
11905 // Partial equality test
11906 bool wxTextAttrBorder::EqPartial(const wxTextAttrBorder
& border
) const
11908 if (border
.HasStyle() && !HasStyle() && (border
.GetStyle() != GetStyle()))
11911 if (border
.HasColour() && !HasColour() && (border
.GetColourLong() != GetColourLong()))
11914 if (border
.HasWidth() && !HasWidth() && !(border
.GetWidth() == GetWidth()))
11920 // Apply border to 'this', but not if the same as compareWith
11921 bool wxTextAttrBorder::Apply(const wxTextAttrBorder
& border
, const wxTextAttrBorder
* compareWith
)
11923 if (border
.HasStyle())
11925 if (!(compareWith
&& (border
.GetStyle() == compareWith
->GetStyle())))
11926 SetStyle(border
.GetStyle());
11928 if (border
.HasColour())
11930 if (!(compareWith
&& (border
.GetColourLong() == compareWith
->GetColourLong())))
11931 SetColour(border
.GetColourLong());
11933 if (border
.HasWidth())
11935 if (!(compareWith
&& (border
.GetWidth() == compareWith
->GetWidth())))
11936 SetWidth(border
.GetWidth());
11942 // Remove specified attributes from this object
11943 bool wxTextAttrBorder::RemoveStyle(const wxTextAttrBorder
& attr
)
11945 if (attr
.HasStyle() && HasStyle())
11946 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_STYLE
);
11947 if (attr
.HasColour() && HasColour())
11948 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_COLOUR
);
11949 if (attr
.HasWidth() && HasWidth())
11950 m_borderWidth
.Reset();
11955 // Collects the attributes that are common to a range of content, building up a note of
11956 // which attributes are absent in some objects and which clash in some objects.
11957 void wxTextAttrBorder::CollectCommonAttributes(const wxTextAttrBorder
& attr
, wxTextAttrBorder
& clashingAttr
, wxTextAttrBorder
& absentAttr
)
11959 if (attr
.HasStyle())
11961 if (!clashingAttr
.HasStyle() && !absentAttr
.HasStyle())
11965 if (GetStyle() != attr
.GetStyle())
11967 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
11968 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
11972 SetStyle(attr
.GetStyle());
11976 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE
);
11978 if (attr
.HasColour())
11980 if (!clashingAttr
.HasColour() && !absentAttr
.HasColour())
11984 if (GetColour() != attr
.GetColour())
11986 clashingAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
11987 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
11991 SetColour(attr
.GetColourLong());
11995 absentAttr
.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR
);
11997 m_borderWidth
.CollectCommonAttributes(attr
.m_borderWidth
, clashingAttr
.m_borderWidth
, absentAttr
.m_borderWidth
);
12000 // Partial equality test
12001 bool wxTextAttrBorders::EqPartial(const wxTextAttrBorders
& borders
) const
12003 return m_left
.EqPartial(borders
.m_left
) && m_right
.EqPartial(borders
.m_right
) &&
12004 m_top
.EqPartial(borders
.m_top
) && m_bottom
.EqPartial(borders
.m_bottom
);
12007 // Apply border to 'this', but not if the same as compareWith
12008 bool wxTextAttrBorders::Apply(const wxTextAttrBorders
& borders
, const wxTextAttrBorders
* compareWith
)
12010 m_left
.Apply(borders
.m_left
, compareWith
? (& compareWith
->m_left
) : (const wxTextAttrBorder
*) NULL
);
12011 m_right
.Apply(borders
.m_right
, compareWith
? (& compareWith
->m_right
) : (const wxTextAttrBorder
*) NULL
);
12012 m_top
.Apply(borders
.m_top
, compareWith
? (& compareWith
->m_top
) : (const wxTextAttrBorder
*) NULL
);
12013 m_bottom
.Apply(borders
.m_bottom
, compareWith
? (& compareWith
->m_bottom
) : (const wxTextAttrBorder
*) NULL
);
12017 // Remove specified attributes from this object
12018 bool wxTextAttrBorders::RemoveStyle(const wxTextAttrBorders
& attr
)
12020 m_left
.RemoveStyle(attr
.m_left
);
12021 m_right
.RemoveStyle(attr
.m_right
);
12022 m_top
.RemoveStyle(attr
.m_top
);
12023 m_bottom
.RemoveStyle(attr
.m_bottom
);
12027 // Collects the attributes that are common to a range of content, building up a note of
12028 // which attributes are absent in some objects and which clash in some objects.
12029 void wxTextAttrBorders::CollectCommonAttributes(const wxTextAttrBorders
& attr
, wxTextAttrBorders
& clashingAttr
, wxTextAttrBorders
& absentAttr
)
12031 m_left
.CollectCommonAttributes(attr
.m_left
, clashingAttr
.m_left
, absentAttr
.m_left
);
12032 m_right
.CollectCommonAttributes(attr
.m_right
, clashingAttr
.m_right
, absentAttr
.m_right
);
12033 m_top
.CollectCommonAttributes(attr
.m_top
, clashingAttr
.m_top
, absentAttr
.m_top
);
12034 m_bottom
.CollectCommonAttributes(attr
.m_bottom
, clashingAttr
.m_bottom
, absentAttr
.m_bottom
);
12037 // Set style of all borders
12038 void wxTextAttrBorders::SetStyle(int style
)
12040 m_left
.SetStyle(style
);
12041 m_right
.SetStyle(style
);
12042 m_top
.SetStyle(style
);
12043 m_bottom
.SetStyle(style
);
12046 // Set colour of all borders
12047 void wxTextAttrBorders::SetColour(unsigned long colour
)
12049 m_left
.SetColour(colour
);
12050 m_right
.SetColour(colour
);
12051 m_top
.SetColour(colour
);
12052 m_bottom
.SetColour(colour
);
12055 void wxTextAttrBorders::SetColour(const wxColour
& colour
)
12057 m_left
.SetColour(colour
);
12058 m_right
.SetColour(colour
);
12059 m_top
.SetColour(colour
);
12060 m_bottom
.SetColour(colour
);
12063 // Set width of all borders
12064 void wxTextAttrBorders::SetWidth(const wxTextAttrDimension
& width
)
12066 m_left
.SetWidth(width
);
12067 m_right
.SetWidth(width
);
12068 m_top
.SetWidth(width
);
12069 m_bottom
.SetWidth(width
);
12072 // Partial equality test
12073 bool wxTextAttrDimension::EqPartial(const wxTextAttrDimension
& dim
) const
12075 if (dim
.IsValid() && IsValid() && !((*this) == dim
))
12081 bool wxTextAttrDimension::Apply(const wxTextAttrDimension
& dim
, const wxTextAttrDimension
* compareWith
)
12085 if (!(compareWith
&& dim
== (*compareWith
)))
12092 // Collects the attributes that are common to a range of content, building up a note of
12093 // which attributes are absent in some objects and which clash in some objects.
12094 void wxTextAttrDimension::CollectCommonAttributes(const wxTextAttrDimension
& attr
, wxTextAttrDimension
& clashingAttr
, wxTextAttrDimension
& absentAttr
)
12096 if (attr
.IsValid())
12098 if (!clashingAttr
.IsValid() && !absentAttr
.IsValid())
12102 if (!((*this) == attr
))
12104 clashingAttr
.SetValid(true);
12113 absentAttr
.SetValid(true);
12116 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(wxDC
& dc
, double scale
, const wxSize
& parentSize
)
12118 m_ppi
= dc
.GetPPI().x
; m_scale
= scale
; m_parentSize
= parentSize
;
12121 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(int ppi
, double scale
, const wxSize
& parentSize
)
12123 m_ppi
= ppi
; m_scale
= scale
; m_parentSize
= parentSize
;
12126 int wxTextAttrDimensionConverter::ConvertTenthsMMToPixels(int units
) const
12128 return wxRichTextObject::ConvertTenthsMMToPixels(m_ppi
, units
, m_scale
);
12131 int wxTextAttrDimensionConverter::ConvertPixelsToTenthsMM(int pixels
) const
12133 return wxRichTextObject::ConvertPixelsToTenthsMM(m_ppi
, pixels
, m_scale
);
12136 int wxTextAttrDimensionConverter::GetPixels(const wxTextAttrDimension
& dim
, int direction
) const
12138 if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
12139 return ConvertTenthsMMToPixels(dim
.GetValue());
12140 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
12141 return dim
.GetValue();
12142 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE
)
12144 wxASSERT(m_parentSize
!= wxDefaultSize
);
12145 if (direction
== wxHORIZONTAL
)
12146 return (int) (double(m_parentSize
.x
) * double(dim
.GetValue()) / 100.0);
12148 return (int) (double(m_parentSize
.y
) * double(dim
.GetValue()) / 100.0);
12157 int wxTextAttrDimensionConverter::GetTenthsMM(const wxTextAttrDimension
& dim
) const
12159 if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM
)
12160 return dim
.GetValue();
12161 else if (dim
.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS
)
12162 return ConvertPixelsToTenthsMM(dim
.GetValue());
12170 // Partial equality test
12171 bool wxTextAttrDimensions::EqPartial(const wxTextAttrDimensions
& dims
) const
12173 if (!m_left
.EqPartial(dims
.m_left
))
12176 if (!m_right
.EqPartial(dims
.m_right
))
12179 if (!m_top
.EqPartial(dims
.m_top
))
12182 if (!m_bottom
.EqPartial(dims
.m_bottom
))
12188 // Apply border to 'this', but not if the same as compareWith
12189 bool wxTextAttrDimensions::Apply(const wxTextAttrDimensions
& dims
, const wxTextAttrDimensions
* compareWith
)
12191 m_left
.Apply(dims
.m_left
, compareWith
? (& compareWith
->m_left
) : (const wxTextAttrDimension
*) NULL
);
12192 m_right
.Apply(dims
.m_right
, compareWith
? (& compareWith
->m_right
): (const wxTextAttrDimension
*) NULL
);
12193 m_top
.Apply(dims
.m_top
, compareWith
? (& compareWith
->m_top
): (const wxTextAttrDimension
*) NULL
);
12194 m_bottom
.Apply(dims
.m_bottom
, compareWith
? (& compareWith
->m_bottom
): (const wxTextAttrDimension
*) NULL
);
12199 // Remove specified attributes from this object
12200 bool wxTextAttrDimensions::RemoveStyle(const wxTextAttrDimensions
& attr
)
12202 if (attr
.m_left
.IsValid())
12204 if (attr
.m_right
.IsValid())
12206 if (attr
.m_top
.IsValid())
12208 if (attr
.m_bottom
.IsValid())
12214 // Collects the attributes that are common to a range of content, building up a note of
12215 // which attributes are absent in some objects and which clash in some objects.
12216 void wxTextAttrDimensions::CollectCommonAttributes(const wxTextAttrDimensions
& attr
, wxTextAttrDimensions
& clashingAttr
, wxTextAttrDimensions
& absentAttr
)
12218 m_left
.CollectCommonAttributes(attr
.m_left
, clashingAttr
.m_left
, absentAttr
.m_left
);
12219 m_right
.CollectCommonAttributes(attr
.m_right
, clashingAttr
.m_right
, absentAttr
.m_right
);
12220 m_top
.CollectCommonAttributes(attr
.m_top
, clashingAttr
.m_top
, absentAttr
.m_top
);
12221 m_bottom
.CollectCommonAttributes(attr
.m_bottom
, clashingAttr
.m_bottom
, absentAttr
.m_bottom
);
12224 // Partial equality test
12225 bool wxTextAttrSize::EqPartial(const wxTextAttrSize
& size
) const
12227 if (!m_width
.EqPartial(size
.m_width
))
12230 if (!m_height
.EqPartial(size
.m_height
))
12236 // Apply border to 'this', but not if the same as compareWith
12237 bool wxTextAttrSize::Apply(const wxTextAttrSize
& size
, const wxTextAttrSize
* compareWith
)
12239 m_width
.Apply(size
.m_width
, compareWith
? (& compareWith
->m_width
) : (const wxTextAttrDimension
*) NULL
);
12240 m_height
.Apply(size
.m_height
, compareWith
? (& compareWith
->m_height
): (const wxTextAttrDimension
*) NULL
);
12245 // Remove specified attributes from this object
12246 bool wxTextAttrSize::RemoveStyle(const wxTextAttrSize
& attr
)
12248 if (attr
.m_width
.IsValid())
12250 if (attr
.m_height
.IsValid())
12256 // Collects the attributes that are common to a range of content, building up a note of
12257 // which attributes are absent in some objects and which clash in some objects.
12258 void wxTextAttrSize::CollectCommonAttributes(const wxTextAttrSize
& attr
, wxTextAttrSize
& clashingAttr
, wxTextAttrSize
& absentAttr
)
12260 m_width
.CollectCommonAttributes(attr
.m_width
, clashingAttr
.m_width
, absentAttr
.m_width
);
12261 m_height
.CollectCommonAttributes(attr
.m_height
, clashingAttr
.m_height
, absentAttr
.m_height
);
12264 // Collects the attributes that are common to a range of content, building up a note of
12265 // which attributes are absent in some objects and which clash in some objects.
12266 void wxTextAttrCollectCommonAttributes(wxTextAttr
& currentStyle
, const wxTextAttr
& attr
, wxTextAttr
& clashingAttr
, wxTextAttr
& absentAttr
)
12268 absentAttr
.SetFlags(absentAttr
.GetFlags() | (~attr
.GetFlags() & wxTEXT_ATTR_ALL
));
12269 absentAttr
.SetTextEffectFlags(absentAttr
.GetTextEffectFlags() | (~attr
.GetTextEffectFlags() & 0xFFFF));
12271 long forbiddenFlags
= clashingAttr
.GetFlags()|absentAttr
.GetFlags();
12273 if (attr
.HasFont())
12275 if (attr
.HasFontSize() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_SIZE
))
12277 if (currentStyle
.HasFontSize())
12279 if (currentStyle
.GetFontSize() != attr
.GetFontSize())
12281 // Clash of attr - mark as such
12282 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_SIZE
);
12283 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_SIZE
);
12287 currentStyle
.SetFontSize(attr
.GetFontSize());
12290 if (attr
.HasFontItalic() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_ITALIC
))
12292 if (currentStyle
.HasFontItalic())
12294 if (currentStyle
.GetFontStyle() != attr
.GetFontStyle())
12296 // Clash of attr - mark as such
12297 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_ITALIC
);
12298 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC
);
12302 currentStyle
.SetFontStyle(attr
.GetFontStyle());
12305 if (attr
.HasFontFamily() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_FAMILY
))
12307 if (currentStyle
.HasFontFamily())
12309 if (currentStyle
.GetFontFamily() != attr
.GetFontFamily())
12311 // Clash of attr - mark as such
12312 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FAMILY
);
12313 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY
);
12317 currentStyle
.SetFontFamily(attr
.GetFontFamily());
12320 if (attr
.HasFontWeight() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_WEIGHT
))
12322 if (currentStyle
.HasFontWeight())
12324 if (currentStyle
.GetFontWeight() != attr
.GetFontWeight())
12326 // Clash of attr - mark as such
12327 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_WEIGHT
);
12328 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT
);
12332 currentStyle
.SetFontWeight(attr
.GetFontWeight());
12335 if (attr
.HasFontFaceName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_FACE
))
12337 if (currentStyle
.HasFontFaceName())
12339 wxString
faceName1(currentStyle
.GetFontFaceName());
12340 wxString
faceName2(attr
.GetFontFaceName());
12342 if (faceName1
!= faceName2
)
12344 // Clash of attr - mark as such
12345 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_FACE
);
12346 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_FACE
);
12350 currentStyle
.SetFontFaceName(attr
.GetFontFaceName());
12353 if (attr
.HasFontUnderlined() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_FONT_UNDERLINE
))
12355 if (currentStyle
.HasFontUnderlined())
12357 if (currentStyle
.GetFontUnderlined() != attr
.GetFontUnderlined())
12359 // Clash of attr - mark as such
12360 clashingAttr
.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
12361 currentStyle
.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE
);
12365 currentStyle
.SetFontUnderlined(attr
.GetFontUnderlined());
12369 if (attr
.HasTextColour() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_TEXT_COLOUR
))
12371 if (currentStyle
.HasTextColour())
12373 if (currentStyle
.GetTextColour() != attr
.GetTextColour())
12375 // Clash of attr - mark as such
12376 clashingAttr
.AddFlag(wxTEXT_ATTR_TEXT_COLOUR
);
12377 currentStyle
.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR
);
12381 currentStyle
.SetTextColour(attr
.GetTextColour());
12384 if (attr
.HasBackgroundColour() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BACKGROUND_COLOUR
))
12386 if (currentStyle
.HasBackgroundColour())
12388 if (currentStyle
.GetBackgroundColour() != attr
.GetBackgroundColour())
12390 // Clash of attr - mark as such
12391 clashingAttr
.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
12392 currentStyle
.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR
);
12396 currentStyle
.SetBackgroundColour(attr
.GetBackgroundColour());
12399 if (attr
.HasAlignment() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_ALIGNMENT
))
12401 if (currentStyle
.HasAlignment())
12403 if (currentStyle
.GetAlignment() != attr
.GetAlignment())
12405 // Clash of attr - mark as such
12406 clashingAttr
.AddFlag(wxTEXT_ATTR_ALIGNMENT
);
12407 currentStyle
.RemoveFlag(wxTEXT_ATTR_ALIGNMENT
);
12411 currentStyle
.SetAlignment(attr
.GetAlignment());
12414 if (attr
.HasTabs() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_TABS
))
12416 if (currentStyle
.HasTabs())
12418 if (!wxRichTextTabsEq(currentStyle
.GetTabs(), attr
.GetTabs()))
12420 // Clash of attr - mark as such
12421 clashingAttr
.AddFlag(wxTEXT_ATTR_TABS
);
12422 currentStyle
.RemoveFlag(wxTEXT_ATTR_TABS
);
12426 currentStyle
.SetTabs(attr
.GetTabs());
12429 if (attr
.HasLeftIndent() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LEFT_INDENT
))
12431 if (currentStyle
.HasLeftIndent())
12433 if (currentStyle
.GetLeftIndent() != attr
.GetLeftIndent() || currentStyle
.GetLeftSubIndent() != attr
.GetLeftSubIndent())
12435 // Clash of attr - mark as such
12436 clashingAttr
.AddFlag(wxTEXT_ATTR_LEFT_INDENT
);
12437 currentStyle
.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT
);
12441 currentStyle
.SetLeftIndent(attr
.GetLeftIndent(), attr
.GetLeftSubIndent());
12444 if (attr
.HasRightIndent() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_RIGHT_INDENT
))
12446 if (currentStyle
.HasRightIndent())
12448 if (currentStyle
.GetRightIndent() != attr
.GetRightIndent())
12450 // Clash of attr - mark as such
12451 clashingAttr
.AddFlag(wxTEXT_ATTR_RIGHT_INDENT
);
12452 currentStyle
.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT
);
12456 currentStyle
.SetRightIndent(attr
.GetRightIndent());
12459 if (attr
.HasParagraphSpacingAfter() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARA_SPACING_AFTER
))
12461 if (currentStyle
.HasParagraphSpacingAfter())
12463 if (currentStyle
.GetParagraphSpacingAfter() != attr
.GetParagraphSpacingAfter())
12465 // Clash of attr - mark as such
12466 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
12467 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER
);
12471 currentStyle
.SetParagraphSpacingAfter(attr
.GetParagraphSpacingAfter());
12474 if (attr
.HasParagraphSpacingBefore() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARA_SPACING_BEFORE
))
12476 if (currentStyle
.HasParagraphSpacingBefore())
12478 if (currentStyle
.GetParagraphSpacingBefore() != attr
.GetParagraphSpacingBefore())
12480 // Clash of attr - mark as such
12481 clashingAttr
.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
12482 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE
);
12486 currentStyle
.SetParagraphSpacingBefore(attr
.GetParagraphSpacingBefore());
12489 if (attr
.HasLineSpacing() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LINE_SPACING
))
12491 if (currentStyle
.HasLineSpacing())
12493 if (currentStyle
.GetLineSpacing() != attr
.GetLineSpacing())
12495 // Clash of attr - mark as such
12496 clashingAttr
.AddFlag(wxTEXT_ATTR_LINE_SPACING
);
12497 currentStyle
.RemoveFlag(wxTEXT_ATTR_LINE_SPACING
);
12501 currentStyle
.SetLineSpacing(attr
.GetLineSpacing());
12504 if (attr
.HasCharacterStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_CHARACTER_STYLE_NAME
))
12506 if (currentStyle
.HasCharacterStyleName())
12508 if (currentStyle
.GetCharacterStyleName() != attr
.GetCharacterStyleName())
12510 // Clash of attr - mark as such
12511 clashingAttr
.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
12512 currentStyle
.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME
);
12516 currentStyle
.SetCharacterStyleName(attr
.GetCharacterStyleName());
12519 if (attr
.HasParagraphStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
))
12521 if (currentStyle
.HasParagraphStyleName())
12523 if (currentStyle
.GetParagraphStyleName() != attr
.GetParagraphStyleName())
12525 // Clash of attr - mark as such
12526 clashingAttr
.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
12527 currentStyle
.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME
);
12531 currentStyle
.SetParagraphStyleName(attr
.GetParagraphStyleName());
12534 if (attr
.HasListStyleName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_LIST_STYLE_NAME
))
12536 if (currentStyle
.HasListStyleName())
12538 if (currentStyle
.GetListStyleName() != attr
.GetListStyleName())
12540 // Clash of attr - mark as such
12541 clashingAttr
.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
12542 currentStyle
.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME
);
12546 currentStyle
.SetListStyleName(attr
.GetListStyleName());
12549 if (attr
.HasBulletStyle() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_STYLE
))
12551 if (currentStyle
.HasBulletStyle())
12553 if (currentStyle
.GetBulletStyle() != attr
.GetBulletStyle())
12555 // Clash of attr - mark as such
12556 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_STYLE
);
12557 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE
);
12561 currentStyle
.SetBulletStyle(attr
.GetBulletStyle());
12564 if (attr
.HasBulletNumber() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_NUMBER
))
12566 if (currentStyle
.HasBulletNumber())
12568 if (currentStyle
.GetBulletNumber() != attr
.GetBulletNumber())
12570 // Clash of attr - mark as such
12571 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NUMBER
);
12572 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER
);
12576 currentStyle
.SetBulletNumber(attr
.GetBulletNumber());
12579 if (attr
.HasBulletText() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_TEXT
))
12581 if (currentStyle
.HasBulletText())
12583 if (currentStyle
.GetBulletText() != attr
.GetBulletText())
12585 // Clash of attr - mark as such
12586 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_TEXT
);
12587 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT
);
12592 currentStyle
.SetBulletText(attr
.GetBulletText());
12593 currentStyle
.SetBulletFont(attr
.GetBulletFont());
12597 if (attr
.HasBulletName() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_BULLET_NAME
))
12599 if (currentStyle
.HasBulletName())
12601 if (currentStyle
.GetBulletName() != attr
.GetBulletName())
12603 // Clash of attr - mark as such
12604 clashingAttr
.AddFlag(wxTEXT_ATTR_BULLET_NAME
);
12605 currentStyle
.RemoveFlag(wxTEXT_ATTR_BULLET_NAME
);
12610 currentStyle
.SetBulletName(attr
.GetBulletName());
12614 if (attr
.HasURL() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_URL
))
12616 if (currentStyle
.HasURL())
12618 if (currentStyle
.GetURL() != attr
.GetURL())
12620 // Clash of attr - mark as such
12621 clashingAttr
.AddFlag(wxTEXT_ATTR_URL
);
12622 currentStyle
.RemoveFlag(wxTEXT_ATTR_URL
);
12627 currentStyle
.SetURL(attr
.GetURL());
12631 if (attr
.HasTextEffects() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_EFFECTS
))
12633 if (currentStyle
.HasTextEffects())
12635 // We need to find the bits in the new attr that are different:
12636 // just look at those bits that are specified by the new attr.
12638 // We need to remove the bits and flags that are not common between current attr
12639 // and new attr. In so doing we need to take account of the styles absent from one or more of the
12640 // previous styles.
12642 int currentRelevantTextEffects
= currentStyle
.GetTextEffects() & attr
.GetTextEffectFlags();
12643 int newRelevantTextEffects
= attr
.GetTextEffects() & attr
.GetTextEffectFlags();
12645 if (currentRelevantTextEffects
!= newRelevantTextEffects
)
12647 // Find the text effects that were different, using XOR
12648 int differentEffects
= currentRelevantTextEffects
^ newRelevantTextEffects
;
12650 // Clash of attr - mark as such
12651 clashingAttr
.SetTextEffectFlags(clashingAttr
.GetTextEffectFlags() | differentEffects
);
12652 currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~differentEffects
);
12657 currentStyle
.SetTextEffects(attr
.GetTextEffects());
12658 currentStyle
.SetTextEffectFlags(attr
.GetTextEffectFlags());
12661 // Mask out the flags and values that cannot be common because they were absent in one or more objecrs
12662 // that we've looked at so far
12663 currentStyle
.SetTextEffects(currentStyle
.GetTextEffects() & ~absentAttr
.GetTextEffectFlags());
12664 currentStyle
.SetTextEffectFlags(currentStyle
.GetTextEffectFlags() & ~absentAttr
.GetTextEffectFlags());
12666 if (currentStyle
.GetTextEffectFlags() == 0)
12667 currentStyle
.RemoveFlag(wxTEXT_ATTR_EFFECTS
);
12670 if (attr
.HasOutlineLevel() && !wxHasStyle(forbiddenFlags
, wxTEXT_ATTR_OUTLINE_LEVEL
))
12672 if (currentStyle
.HasOutlineLevel())
12674 if (currentStyle
.GetOutlineLevel() != attr
.GetOutlineLevel())
12676 // Clash of attr - mark as such
12677 clashingAttr
.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
12678 currentStyle
.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL
);
12682 currentStyle
.SetOutlineLevel(attr
.GetOutlineLevel());
12686 WX_DEFINE_OBJARRAY(wxRichTextVariantArray
);
12688 IMPLEMENT_DYNAMIC_CLASS(wxRichTextProperties
, wxObject
)
12690 bool wxRichTextProperties::operator==(const wxRichTextProperties
& props
) const
12692 if (m_properties
.GetCount() != props
.GetCount())
12696 for (i
= 0; i
< m_properties
.GetCount(); i
++)
12698 const wxVariant
& var1
= m_properties
[i
];
12699 int idx
= props
.Find(var1
.GetName());
12702 const wxVariant
& var2
= props
.m_properties
[idx
];
12703 if (!(var1
== var2
))
12710 wxArrayString
wxRichTextProperties::GetPropertyNames() const
12714 for (i
= 0; i
< m_properties
.GetCount(); i
++)
12716 arr
.Add(m_properties
[i
].GetName());
12721 int wxRichTextProperties::Find(const wxString
& name
) const
12724 for (i
= 0; i
< m_properties
.GetCount(); i
++)
12726 if (m_properties
[i
].GetName() == name
)
12732 bool wxRichTextProperties::Remove(const wxString
& name
)
12734 int idx
= Find(name
);
12737 m_properties
.RemoveAt(idx
);
12744 wxVariant
* wxRichTextProperties::FindOrCreateProperty(const wxString
& name
)
12746 int idx
= Find(name
);
12747 if (idx
== wxNOT_FOUND
)
12748 SetProperty(name
, wxString());
12750 if (idx
!= wxNOT_FOUND
)
12752 return & (*this)[idx
];
12758 const wxVariant
& wxRichTextProperties::GetProperty(const wxString
& name
) const
12760 static const wxVariant nullVariant
;
12761 int idx
= Find(name
);
12763 return m_properties
[idx
];
12765 return nullVariant
;
12768 wxString
wxRichTextProperties::GetPropertyString(const wxString
& name
) const
12770 return GetProperty(name
).GetString();
12773 long wxRichTextProperties::GetPropertyLong(const wxString
& name
) const
12775 return GetProperty(name
).GetLong();
12778 bool wxRichTextProperties::GetPropertyBool(const wxString
& name
) const
12780 return GetProperty(name
).GetBool();
12783 double wxRichTextProperties::GetPropertyDouble(const wxString
& name
) const
12785 return GetProperty(name
).GetDouble();
12788 void wxRichTextProperties::SetProperty(const wxVariant
& variant
)
12790 wxASSERT(!variant
.GetName().IsEmpty());
12792 int idx
= Find(variant
.GetName());
12795 m_properties
.Add(variant
);
12797 m_properties
[idx
] = variant
;
12800 void wxRichTextProperties::SetProperty(const wxString
& name
, const wxVariant
& variant
)
12802 int idx
= Find(name
);
12803 wxVariant
var(variant
);
12807 m_properties
.Add(var
);
12809 m_properties
[idx
] = var
;
12812 void wxRichTextProperties::SetProperty(const wxString
& name
, const wxString
& value
)
12814 SetProperty(name
, wxVariant(value
, name
));
12817 void wxRichTextProperties::SetProperty(const wxString
& name
, long value
)
12819 SetProperty(name
, wxVariant(value
, name
));
12822 void wxRichTextProperties::SetProperty(const wxString
& name
, double value
)
12824 SetProperty(name
, wxVariant(value
, name
));
12827 void wxRichTextProperties::SetProperty(const wxString
& name
, bool value
)
12829 SetProperty(name
, wxVariant(value
, name
));
12832 void wxRichTextProperties::RemoveProperties(const wxRichTextProperties
& properties
)
12835 for (i
= 0; i
< properties
.GetCount(); i
++)
12837 wxString name
= properties
.GetProperties()[i
].GetName();
12838 if (HasProperty(name
))
12843 void wxRichTextProperties::MergeProperties(const wxRichTextProperties
& properties
)
12846 for (i
= 0; i
< properties
.GetCount(); i
++)
12848 SetProperty(properties
.GetProperties()[i
]);
12852 wxRichTextObject
* wxRichTextObjectAddress::GetObject(wxRichTextParagraphLayoutBox
* topLevelContainer
) const
12854 if (m_address
.GetCount() == 0)
12855 return topLevelContainer
;
12857 wxRichTextCompositeObject
* p
= topLevelContainer
;
12859 while (p
&& i
< m_address
.GetCount())
12861 int pos
= m_address
[i
];
12862 wxASSERT(pos
>= 0 && pos
< (int) p
->GetChildren().GetCount());
12863 if (pos
< 0 || pos
>= (int) p
->GetChildren().GetCount())
12866 wxRichTextObject
* p1
= p
->GetChild(pos
);
12867 if (i
== (m_address
.GetCount()-1))
12870 p
= wxDynamicCast(p1
, wxRichTextCompositeObject
);
12876 bool wxRichTextObjectAddress::Create(wxRichTextParagraphLayoutBox
* topLevelContainer
, wxRichTextObject
* obj
)
12880 if (topLevelContainer
== obj
)
12883 wxRichTextObject
* o
= obj
;
12886 wxRichTextCompositeObject
* p
= wxDynamicCast(o
->GetParent(), wxRichTextCompositeObject
);
12890 int pos
= p
->GetChildren().IndexOf(o
);
12894 m_address
.Insert(pos
, 0);
12896 if (p
== topLevelContainer
)
12905 bool wxRichTextSelection::operator==(const wxRichTextSelection
& sel
) const
12907 if (m_container
!= sel
.m_container
)
12909 if (m_ranges
.GetCount() != sel
.m_ranges
.GetCount())
12912 for (i
= 0; i
< m_ranges
.GetCount(); i
++)
12913 if (!(m_ranges
[i
] == sel
.m_ranges
[i
]))
12918 // Get the selections appropriate to the specified object, if any; returns wxRICHTEXT_NO_SELECTION if none
12919 // or none at the level of the object's container.
12920 wxRichTextRangeArray
wxRichTextSelection::GetSelectionForObject(wxRichTextObject
* obj
) const
12924 wxRichTextParagraphLayoutBox
* container
= obj
->GetParentContainer();
12926 if (container
== m_container
)
12929 container
= obj
->GetContainer();
12932 if (container
->GetParent())
12934 // If we found that our object's container is within the range of
12935 // a selection higher up, then assume the whole original object
12936 // is also selected.
12937 wxRichTextParagraphLayoutBox
* parentContainer
= container
->GetParentContainer();
12938 if (parentContainer
== m_container
)
12940 if (WithinSelection(container
->GetRange().GetStart(), m_ranges
))
12942 wxRichTextRangeArray ranges
;
12943 ranges
.Add(obj
->GetRange());
12948 container
= parentContainer
;
12957 return wxRichTextRangeArray();
12960 // Is the given position within the selection?
12961 bool wxRichTextSelection::WithinSelection(long pos
, wxRichTextObject
* obj
) const
12967 wxRichTextRangeArray selectionRanges
= GetSelectionForObject(obj
);
12968 return WithinSelection(pos
, selectionRanges
);
12972 // Is the given position within the selection range?
12973 bool wxRichTextSelection::WithinSelection(long pos
, const wxRichTextRangeArray
& ranges
)
12976 for (i
= 0; i
< ranges
.GetCount(); i
++)
12978 const wxRichTextRange
& range
= ranges
[i
];
12979 if (pos
>= range
.GetStart() && pos
<= range
.GetEnd())
12985 // Is the given range completely within the selection range?
12986 bool wxRichTextSelection::WithinSelection(const wxRichTextRange
& range
, const wxRichTextRangeArray
& ranges
)
12989 for (i
= 0; i
< ranges
.GetCount(); i
++)
12991 const wxRichTextRange
& eachRange
= ranges
[i
];
12992 if (range
.IsWithin(eachRange
))
12998 IMPLEMENT_CLASS(wxRichTextDrawingHandler
, wxObject
)
12999 IMPLEMENT_CLASS(wxRichTextDrawingContext
, wxObject
)
13001 bool wxRichTextDrawingContext::HasVirtualAttributes(wxRichTextObject
* obj
) const
13003 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
13006 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13007 if (handler
->HasVirtualAttributes(obj
))
13010 node
= node
->GetNext();
13015 wxRichTextAttr
wxRichTextDrawingContext::GetVirtualAttributes(wxRichTextObject
* obj
) const
13017 wxRichTextAttr attr
;
13018 // We apply all handlers, so we can may combine several different attributes
13019 wxList::compatibility_iterator node
= m_buffer
->GetDrawingHandlers().GetFirst();
13022 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13023 if (handler
->HasVirtualAttributes(obj
))
13025 bool success
= handler
->GetVirtualAttributes(attr
, obj
);
13027 wxUnusedVar(success
);
13030 node
= node
->GetNext();
13035 bool wxRichTextDrawingContext::ApplyVirtualAttributes(wxRichTextAttr
& attr
, wxRichTextObject
* obj
) const
13037 if (HasVirtualAttributes(obj
))
13039 wxRichTextAttr
a(GetVirtualAttributes(obj
));
13047 /// Adds a handler to the end
13048 void wxRichTextBuffer::AddDrawingHandler(wxRichTextDrawingHandler
*handler
)
13050 sm_drawingHandlers
.Append(handler
);
13053 /// Inserts a handler at the front
13054 void wxRichTextBuffer::InsertDrawingHandler(wxRichTextDrawingHandler
*handler
)
13056 sm_drawingHandlers
.Insert( handler
);
13059 /// Removes a handler
13060 bool wxRichTextBuffer::RemoveDrawingHandler(const wxString
& name
)
13062 wxRichTextDrawingHandler
*handler
= FindDrawingHandler(name
);
13065 sm_drawingHandlers
.DeleteObject(handler
);
13073 wxRichTextDrawingHandler
* wxRichTextBuffer::FindDrawingHandler(const wxString
& name
)
13075 wxList::compatibility_iterator node
= sm_drawingHandlers
.GetFirst();
13078 wxRichTextDrawingHandler
*handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13079 if (handler
->GetName().Lower() == name
.Lower()) return handler
;
13081 node
= node
->GetNext();
13086 void wxRichTextBuffer::CleanUpDrawingHandlers()
13088 wxList::compatibility_iterator node
= sm_drawingHandlers
.GetFirst();
13091 wxRichTextDrawingHandler
* handler
= (wxRichTextDrawingHandler
*)node
->GetData();
13092 wxList::compatibility_iterator next
= node
->GetNext();
13097 sm_drawingHandlers
.Clear();
13100 void wxRichTextBuffer::AddFieldType(wxRichTextFieldType
*fieldType
)
13102 sm_fieldTypes
[fieldType
->GetName()] = fieldType
;
13105 bool wxRichTextBuffer::RemoveFieldType(const wxString
& name
)
13107 wxRichTextFieldTypeHashMap::iterator it
= sm_fieldTypes
.find(name
);
13108 if (it
== sm_fieldTypes
.end())
13112 wxRichTextFieldType
* fieldType
= it
->second
;
13113 sm_fieldTypes
.erase(it
);
13119 wxRichTextFieldType
*wxRichTextBuffer::FindFieldType(const wxString
& name
)
13121 wxRichTextFieldTypeHashMap::iterator it
= sm_fieldTypes
.find(name
);
13122 if (it
== sm_fieldTypes
.end())
13128 void wxRichTextBuffer::CleanUpFieldTypes()
13130 wxRichTextFieldTypeHashMap::iterator it
;
13131 for( it
= sm_fieldTypes
.begin(); it
!= sm_fieldTypes
.end(); ++it
)
13133 wxRichTextFieldType
* fieldType
= it
->second
;
13137 sm_fieldTypes
.clear();