1
/////////////////////////////////////////////////////////////////////////////
2
// Name: src/richtext/richtextbuffer.cpp
3
// Purpose: Buffer for wxRichTextCtrl
4
// Author: Julian Smart
7
// RCS-ID: $Id: richtextbuffer.cpp 71828 2012-06-21 19:12:04Z JS $
8
// Copyright: (c) Julian Smart
9
// Licence: wxWindows licence
10
/////////////////////////////////////////////////////////////////////////////
12
// For compilers that support precompilation, includes "wx.h".
13
#include "wx/wxprec.h"
21
#include "wx/richtext/richtextbuffer.h"
27
#include "wx/dataobj.h"
28
#include "wx/module.h"
31
#include "wx/settings.h"
32
#include "wx/filename.h"
33
#include "wx/clipbrd.h"
34
#include "wx/wfstream.h"
35
#include "wx/mstream.h"
36
#include "wx/sstream.h"
37
#include "wx/textfile.h"
38
#include "wx/hashmap.h"
39
#include "wx/dynarray.h"
41
#include "wx/richtext/richtextctrl.h"
42
#include "wx/richtext/richtextstyles.h"
43
#include "wx/richtext/richtextimagedlg.h"
44
#include "wx/richtext/richtextsizepage.h"
45
#include "wx/richtext/richtextxml.h"
47
#include "wx/listimpl.cpp"
48
#include "wx/arrimpl.cpp"
50
WX_DEFINE_LIST(wxRichTextObjectList)
51
WX_DEFINE_LIST(wxRichTextLineList)
53
// Switch off if the platform doesn't like it for some reason
54
#define wxRICHTEXT_USE_OPTIMIZED_DRAWING 1
56
// Use GetPartialTextExtents for platforms that support it natively
57
#define wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS 1
59
const wxChar wxRichTextLineBreakChar = (wxChar) 29;
61
// Helper classes for floating layout
62
struct wxRichTextFloatRectMap
64
wxRichTextFloatRectMap(int sY, int eY, int w, wxRichTextObject* obj)
74
wxRichTextObject* anchor;
77
WX_DEFINE_SORTED_ARRAY(wxRichTextFloatRectMap*, wxRichTextFloatRectMapArray);
79
int wxRichTextFloatRectMapCmp(wxRichTextFloatRectMap* r1, wxRichTextFloatRectMap* r2)
81
return r1->startY - r2->startY;
84
class wxRichTextFloatCollector
87
wxRichTextFloatCollector(const wxRect& availableRect);
88
~wxRichTextFloatCollector();
90
// Collect the floating objects info in the given paragraph
91
void CollectFloat(wxRichTextParagraph* para);
92
void CollectFloat(wxRichTextParagraph* para, wxRichTextObject* floating);
94
// Return the last paragraph we collected
95
wxRichTextParagraph* LastParagraph();
97
// Given the start y position and the height of the line,
98
// find out how wide the line can be
99
wxRect GetAvailableRect(int startY, int endY);
101
// Given a floating box, find its fit position
102
int GetFitPosition(int direction, int start, int height) const;
103
int GetFitPosition(const wxRichTextFloatRectMapArray& array, int start, int height) const;
105
// Find the last y position
106
int GetLastRectBottom();
108
// Draw the floats inside a rect
109
void Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style);
111
// HitTest the floats
112
int HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int flags);
114
// Get floating object count
115
int GetFloatingObjectCount() const { return m_left.GetCount() + m_right.GetCount(); }
117
// Get floating objects
118
bool GetFloatingObjects(wxRichTextObjectList& objects) const;
121
bool DeleteFloat(wxRichTextObject* obj);
123
// Do we have this float already?
124
bool HasFloat(wxRichTextObject* obj);
126
bool HasFloats() const { return m_left.GetCount() >0 || m_right.GetCount() > 0; }
128
static int SearchAdjacentRect(const wxRichTextFloatRectMapArray& array, int point);
130
static int GetWidthFromFloatRect(const wxRichTextFloatRectMapArray& array, int index, int startY, int endY);
132
static void FreeFloatRectMapArray(wxRichTextFloatRectMapArray& array);
134
static void DrawFloat(const wxRichTextFloatRectMapArray& array, wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style);
136
static int HitTestFloat(const wxRichTextFloatRectMapArray& array, wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int flags);
139
wxRichTextFloatRectMapArray m_left;
140
wxRichTextFloatRectMapArray m_right;
142
wxRect m_availableRect;
143
wxRichTextParagraph* m_para;
147
bool wxRichTextFloatCollector::DeleteFloat(wxRichTextObject* obj)
150
for (i = 0; i < m_left.GetCount(); i++)
152
if (m_left[i]->anchor == obj)
158
for (i = 0; i < m_right.GetCount(); i++)
160
if (m_right[i]->anchor == obj)
169
// Do we have this float already?
170
bool wxRichTextFloatCollector::HasFloat(wxRichTextObject* obj)
173
for (i = 0; i < m_left.GetCount(); i++)
175
if (m_left[i]->anchor == obj)
180
for (i = 0; i < m_right.GetCount(); i++)
182
if (m_right[i]->anchor == obj)
190
// Get floating objects
191
bool wxRichTextFloatCollector::GetFloatingObjects(wxRichTextObjectList& objects) const
194
for (i = 0; i < m_left.GetCount(); i++)
195
objects.Append(m_left[i]->anchor);
196
for (i = 0; i < m_right.GetCount(); i++)
197
objects.Append(m_right[i]->anchor);
203
* Binary search helper function
204
* The argument point is the Y coordinate, and this fuction
205
* always return the floating rect that contain this coordinate
206
* or under this coordinate.
208
int wxRichTextFloatCollector::SearchAdjacentRect(const wxRichTextFloatRectMapArray& array, int point)
210
int end = array.GetCount() - 1;
223
int mid = (start + end) / 2;
224
if (array[mid]->startY <= point && array[mid]->endY >= point)
226
else if (array[mid]->startY > point)
231
else if (array[mid]->endY < point)
241
int wxRichTextFloatCollector::GetWidthFromFloatRect(const wxRichTextFloatRectMapArray& array, int index, int startY, int endY)
244
int len = array.GetCount();
246
wxASSERT(index >= 0 && index < len);
248
if (array[index]->startY < startY && array[index]->endY > startY)
249
ret = ret < array[index]->width ? array[index]->width : ret;
250
while (index < len && array[index]->startY <= endY)
252
ret = ret < array[index]->width ? array[index]->width : ret;
259
wxRichTextFloatCollector::wxRichTextFloatCollector(const wxRect& rect) : m_left(wxRichTextFloatRectMapCmp), m_right(wxRichTextFloatRectMapCmp)
261
m_availableRect = rect;
265
void wxRichTextFloatCollector::FreeFloatRectMapArray(wxRichTextFloatRectMapArray& array)
267
int len = array.GetCount();
268
for (int i = 0; i < len; i++)
272
wxRichTextFloatCollector::~wxRichTextFloatCollector()
274
FreeFloatRectMapArray(m_left);
275
FreeFloatRectMapArray(m_right);
278
int wxRichTextFloatCollector::GetFitPosition(const wxRichTextFloatRectMapArray& array, int start, int height) const
280
if (array.GetCount() == 0)
283
int i = SearchAdjacentRect(array, start);
285
while (i < (int) array.GetCount())
287
if (array[i]->startY - last >= height)
289
last = array[i]->endY;
296
int wxRichTextFloatCollector::GetFitPosition(int direction, int start, int height) const
298
if (direction == wxTEXT_BOX_ATTR_FLOAT_LEFT)
299
return GetFitPosition(m_left, start, height);
300
else if (direction == wxTEXT_BOX_ATTR_FLOAT_RIGHT)
301
return GetFitPosition(m_right, start, height);
304
wxASSERT("Never should be here");
309
// Adds a floating image to the float collector.
310
// The actual positioning is done by wxRichTextParagraph::LayoutFloat.
311
void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph* para, wxRichTextObject* floating)
313
int direction = floating->GetFloatDirection();
315
wxPoint pos = floating->GetPosition();
316
wxSize size = floating->GetCachedSize();
317
wxRichTextFloatRectMap *map = new wxRichTextFloatRectMap(pos.y, pos.y + size.y, size.x, floating);
320
case wxTEXT_BOX_ATTR_FLOAT_NONE:
323
case wxTEXT_BOX_ATTR_FLOAT_LEFT:
324
// Just a not-enough simple assertion
325
wxASSERT (m_left.Index(map) == wxNOT_FOUND);
328
case wxTEXT_BOX_ATTR_FLOAT_RIGHT:
329
wxASSERT (m_right.Index(map) == wxNOT_FOUND);
334
wxASSERT("Unrecognised float attribute.");
340
void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph* para)
342
wxRichTextObjectList::compatibility_iterator node = para->GetChildren().GetFirst();
345
wxRichTextObject* floating = node->GetData();
347
if (floating->IsFloating())
349
CollectFloat(para, floating);
352
node = node->GetNext();
358
wxRichTextParagraph* wxRichTextFloatCollector::LastParagraph()
363
wxRect wxRichTextFloatCollector::GetAvailableRect(int startY, int endY)
365
int widthLeft = 0, widthRight = 0;
366
if (m_left.GetCount() != 0)
368
int i = SearchAdjacentRect(m_left, startY);
369
if (i < (int) m_left.GetCount())
370
widthLeft = GetWidthFromFloatRect(m_left, i, startY, endY);
372
if (m_right.GetCount() != 0)
374
int j = SearchAdjacentRect(m_right, startY);
375
if (j < (int) m_right.GetCount())
376
widthRight = GetWidthFromFloatRect(m_right, j, startY, endY);
379
// TODO: actually we want to use the actual image positions to find the
380
// available remaining space, since the image might not be right up against
381
// the left or right edge of the container.
382
return wxRect(widthLeft + m_availableRect.x, 0, m_availableRect.width - widthLeft - widthRight, 0);
385
int wxRichTextFloatCollector::GetLastRectBottom()
388
int len = m_left.GetCount();
390
ret = ret > m_left[len-1]->endY ? ret : m_left[len-1]->endY;
392
len = m_right.GetCount();
394
ret = ret > m_right[len-1]->endY ? ret : m_right[len-1]->endY;
400
void wxRichTextFloatCollector::DrawFloat(const wxRichTextFloatRectMapArray& array, wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& WXUNUSED(range), const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
403
int end = rect.y + rect.height;
405
i = SearchAdjacentRect(array, start);
406
if (i < 0 || i >= (int) array.GetCount())
408
j = SearchAdjacentRect(array, end);
409
if (j < 0 || j >= (int) array.GetCount())
410
j = array.GetCount() - 1;
413
wxRichTextObject* obj = array[i]->anchor;
414
wxRichTextRange r = obj->GetRange();
415
obj->Draw(dc, context, r, selection, wxRect(obj->GetPosition(), obj->GetCachedSize()), descent, style);
420
void wxRichTextFloatCollector::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
422
if (m_left.GetCount() > 0)
423
DrawFloat(m_left, dc, context, range, selection, rect, descent, style);
424
if (m_right.GetCount() > 0)
425
DrawFloat(m_right, dc, context, range, selection, rect, descent, style);
428
int wxRichTextFloatCollector::HitTestFloat(const wxRichTextFloatRectMapArray& array, wxDC& WXUNUSED(dc), wxRichTextDrawingContext& WXUNUSED(context), const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int WXUNUSED(flags))
431
if (array.GetCount() == 0)
432
return wxRICHTEXT_HITTEST_NONE;
433
i = SearchAdjacentRect(array, pt.y);
434
if (i < 0 || i >= (int) array.GetCount())
435
return wxRICHTEXT_HITTEST_NONE;
436
if (!array[i]->anchor->IsShown())
437
return wxRICHTEXT_HITTEST_NONE;
439
wxPoint point = array[i]->anchor->GetPosition();
440
wxSize size = array[i]->anchor->GetCachedSize();
441
if (point.x <= pt.x && point.x + size.x >= pt.x
442
&& point.y <= pt.y && point.y + size.y >= pt.y)
444
textPosition = array[i]->anchor->GetRange().GetStart();
445
* obj = array[i]->anchor;
446
if (pt.x > (pt.x + pt.x + size.x) / 2)
447
return wxRICHTEXT_HITTEST_BEFORE;
449
return wxRICHTEXT_HITTEST_AFTER;
452
return wxRICHTEXT_HITTEST_NONE;
455
int wxRichTextFloatCollector::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int flags)
457
int ret = HitTestFloat(m_left, dc, context, pt, textPosition, obj, flags);
458
if (ret == wxRICHTEXT_HITTEST_NONE)
460
ret = HitTestFloat(m_right, dc, context, pt, textPosition, obj, flags);
465
// Helpers for efficiency
466
inline void wxCheckSetFont(wxDC& dc, const wxFont& font)
471
inline void wxCheckSetPen(wxDC& dc, const wxPen& pen)
473
const wxPen& pen1 = dc.GetPen();
474
if (pen1.IsOk() && pen.IsOk())
476
if (pen1.GetWidth() == pen.GetWidth() &&
477
pen1.GetStyle() == pen.GetStyle() &&
478
pen1.GetColour() == pen.GetColour())
484
inline void wxCheckSetBrush(wxDC& dc, const wxBrush& brush)
486
const wxBrush& brush1 = dc.GetBrush();
487
if (brush1.IsOk() && brush.IsOk())
489
if (brush1.GetStyle() == brush.GetStyle() &&
490
brush1.GetColour() == brush.GetColour())
498
* This is the base for drawable objects.
501
IMPLEMENT_CLASS(wxRichTextObject, wxObject)
503
wxRichTextObject::wxRichTextObject(wxRichTextObject* parent)
511
wxRichTextObject::~wxRichTextObject()
515
void wxRichTextObject::Dereference()
523
void wxRichTextObject::Copy(const wxRichTextObject& obj)
526
m_maxSize = obj.m_maxSize;
527
m_minSize = obj.m_minSize;
529
m_range = obj.m_range;
530
m_ownRange = obj.m_ownRange;
531
m_attributes = obj.m_attributes;
532
m_properties = obj.m_properties;
533
m_descent = obj.m_descent;
537
// Get/set the top-level container of this object.
538
wxRichTextParagraphLayoutBox* wxRichTextObject::GetContainer() const
540
const wxRichTextObject* p = this;
545
return wxDynamicCast(p, wxRichTextParagraphLayoutBox);
552
void wxRichTextObject::SetMargins(int margin)
554
SetMargins(margin, margin, margin, margin);
557
void wxRichTextObject::SetMargins(int leftMargin, int rightMargin, int topMargin, int bottomMargin)
559
GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().SetValue(leftMargin, wxTEXT_ATTR_UNITS_PIXELS);
560
GetAttributes().GetTextBoxAttr().GetMargins().GetRight().SetValue(rightMargin, wxTEXT_ATTR_UNITS_PIXELS);
561
GetAttributes().GetTextBoxAttr().GetMargins().GetTop().SetValue(topMargin, wxTEXT_ATTR_UNITS_PIXELS);
562
GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().SetValue(bottomMargin, wxTEXT_ATTR_UNITS_PIXELS);
565
int wxRichTextObject::GetLeftMargin() const
567
return GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().GetValue();
570
int wxRichTextObject::GetRightMargin() const
572
return GetAttributes().GetTextBoxAttr().GetMargins().GetRight().GetValue();
575
int wxRichTextObject::GetTopMargin() const
577
return GetAttributes().GetTextBoxAttr().GetMargins().GetTop().GetValue();
580
int wxRichTextObject::GetBottomMargin() const
582
return GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().GetValue();
585
// Calculate the available content space in the given rectangle, given the
586
// margins, border and padding specified in the object's attributes.
587
wxRect wxRichTextObject::GetAvailableContentArea(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& outerRect) const
589
wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
590
marginRect = outerRect;
591
wxRichTextAttr attr(GetAttributes());
592
context.ApplyVirtualAttributes(attr, (wxRichTextObject*) this);
593
GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
597
// Invalidate the buffer. With no argument, invalidates whole buffer.
598
void wxRichTextObject::Invalidate(const wxRichTextRange& invalidRange)
600
if (invalidRange != wxRICHTEXT_NONE)
602
// If this is a floating object, size may not be recalculated
603
// after floats have been collected in an early stage of Layout.
604
// So avoid resetting the cache for floating objects during layout.
606
SetCachedSize(wxDefaultSize);
607
SetMaxSize(wxDefaultSize);
608
SetMinSize(wxDefaultSize);
612
// Convert units in tenths of a millimetre to device units
613
int wxRichTextObject::ConvertTenthsMMToPixels(wxDC& dc, int units) const
618
scale = GetBuffer()->GetScale() / GetBuffer()->GetDimensionScale();
619
int p = ConvertTenthsMMToPixels(dc.GetPPI().x, units, scale);
624
// Convert units in tenths of a millimetre to device units
625
int wxRichTextObject::ConvertTenthsMMToPixels(int ppi, int units, double scale)
627
// There are ppi pixels in 254.1 "1/10 mm"
629
double pixels = ((double) units * (double)ppi) / 254.1;
633
// If the result is very small, make it at least one pixel in size.
634
if (pixels == 0 && units > 0)
640
// Convert units in pixels to tenths of a millimetre
641
int wxRichTextObject::ConvertPixelsToTenthsMM(wxDC& dc, int pixels) const
646
scale = GetBuffer()->GetScale();
648
return ConvertPixelsToTenthsMM(dc.GetPPI().x, p, scale);
651
int wxRichTextObject::ConvertPixelsToTenthsMM(int ppi, int pixels, double scale)
653
// There are ppi pixels in 254.1 "1/10 mm"
655
double p = double(pixels);
660
int units = int( p * 254.1 / (double) ppi );
664
// Draw the borders and background for the given rectangle and attributes.
665
// Width and height are taken to be the outer margin size, not the content.
666
bool wxRichTextObject::DrawBoxAttributes(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& attr, const wxRect& boxRect, int flags)
668
// Assume boxRect is the area around the content
669
wxRect marginRect = boxRect;
670
wxRect contentRect, borderRect, paddingRect, outlineRect;
672
GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
674
// Margin is transparent. Draw background from margin.
675
if (attr.HasBackgroundColour() || (flags & wxRICHTEXT_DRAW_SELECTED))
678
if (flags & wxRICHTEXT_DRAW_SELECTED)
680
// TODO: get selection colour from control?
681
colour = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);
684
colour = attr.GetBackgroundColour();
687
wxBrush brush(colour);
691
dc.DrawRectangle(borderRect);
694
if (flags & wxRICHTEXT_DRAW_GUIDELINES)
696
wxRichTextAttr editBorderAttr = attr;
697
// TODO: make guideline colour configurable
698
editBorderAttr.GetTextBoxAttr().GetBorder().SetColour(*wxLIGHT_GREY);
699
editBorderAttr.GetTextBoxAttr().GetBorder().SetWidth(1, wxTEXT_ATTR_UNITS_PIXELS);
700
editBorderAttr.GetTextBoxAttr().GetBorder().SetStyle(wxTEXT_BOX_ATTR_BORDER_SOLID);
702
DrawBorder(dc, buffer, editBorderAttr.GetTextBoxAttr().GetBorder(), borderRect, flags);
705
if (attr.GetTextBoxAttr().GetBorder().IsValid())
706
DrawBorder(dc, buffer, attr.GetTextBoxAttr().GetBorder(), borderRect);
708
if (attr.GetTextBoxAttr().GetOutline().IsValid())
709
DrawBorder(dc, buffer, attr.GetTextBoxAttr().GetOutline(), outlineRect);
715
bool wxRichTextObject::DrawBorder(wxDC& dc, wxRichTextBuffer* buffer, const wxTextAttrBorders& attr, const wxRect& rect, int WXUNUSED(flags))
717
int borderLeft = 0, borderRight = 0, borderTop = 0, borderBottom = 0;
718
wxTextAttrDimensionConverter converter(dc, buffer ? buffer->GetScale() : 1.0);
720
if (attr.GetLeft().IsValid() && attr.GetLeft().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
722
borderLeft = converter.GetPixels(attr.GetLeft().GetWidth());
723
wxColour col(attr.GetLeft().GetColour());
725
// If pen width is > 1, resorts to a solid rectangle.
728
int penStyle = wxSOLID;
729
if (attr.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
731
else if (attr.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
732
penStyle = wxLONG_DASH;
733
wxPen pen(col, 1, penStyle);
735
dc.DrawLine(rect.x, rect.y, rect.x, rect.y + rect.height);
738
else if (borderLeft > 1)
744
dc.DrawRectangle(rect.x, rect.y, borderLeft, rect.height);
748
if (attr.GetRight().IsValid() && attr.GetRight().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
750
borderRight = converter.GetPixels(attr.GetRight().GetWidth());
752
wxColour col(attr.GetRight().GetColour());
754
// If pen width is > 1, resorts to a solid rectangle.
755
if (borderRight == 1)
757
int penStyle = wxSOLID;
758
if (attr.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
760
else if (attr.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
761
penStyle = wxLONG_DASH;
762
wxPen pen(col, 1, penStyle);
764
dc.DrawLine(rect.x + rect.width, rect.y, rect.x + rect.width, rect.y + rect.height + 1);
767
else if (borderRight > 1)
773
dc.DrawRectangle(rect.x + rect.width - borderRight, rect.y, borderRight, rect.height);
777
if (attr.GetTop().IsValid() && attr.GetTop().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
779
borderTop = converter.GetPixels(attr.GetTop().GetWidth());
781
wxColour col(attr.GetTop().GetColour());
783
// If pen width is > 1, resorts to a solid rectangle.
786
int penStyle = wxSOLID;
787
if (attr.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
789
else if (attr.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
790
penStyle = wxLONG_DASH;
791
wxPen pen(col, 1, penStyle);
793
dc.DrawLine(rect.x, rect.y, rect.x + rect.width, rect.y);
796
else if (borderTop > 1)
802
dc.DrawRectangle(rect.x, rect.y, rect.width, borderTop);
806
if (attr.GetBottom().IsValid() && attr.GetBottom().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
808
borderBottom = converter.GetPixels(attr.GetBottom().GetWidth());
809
wxColour col(attr.GetTop().GetColour());
811
// If pen width is > 1, resorts to a solid rectangle.
812
if (borderBottom == 1)
814
int penStyle = wxSOLID;
815
if (attr.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
817
else if (attr.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
818
penStyle = wxLONG_DASH;
819
wxPen pen(col, 1, penStyle);
821
dc.DrawLine(rect.x, rect.y + rect.height, rect.x + rect.width, rect.y + rect.height);
824
else if (borderBottom > 1)
830
dc.DrawRectangle(rect.x, rect.y + rect.height - borderBottom, rect.width, borderBottom);
837
// Get the various rectangles of the box model in pixels. You can either specify contentRect (inner)
838
// or marginRect (outer), and the other must be the default rectangle (no width or height).
839
// Note that the outline doesn't affect the position of the rectangle, it's drawn in whatever space
842
// | Margin | Border | Padding | CONTENT | Padding | Border | Margin |
844
bool wxRichTextObject::GetBoxRects(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& attr, wxRect& marginRect, wxRect& borderRect, wxRect& contentRect, wxRect& paddingRect, wxRect& outlineRect)
846
int borderLeft = 0, borderRight = 0, borderTop = 0, borderBottom = 0;
847
int outlineLeft = 0, outlineRight = 0, outlineTop = 0, outlineBottom = 0;
848
int paddingLeft = 0, paddingRight = 0, paddingTop = 0, paddingBottom = 0;
849
int marginLeft = 0, marginRight = 0, marginTop = 0, marginBottom = 0;
851
wxTextAttrDimensionConverter converter(dc, buffer ? buffer->GetScale() : 1.0);
853
if (attr.GetTextBoxAttr().GetMargins().GetLeft().IsValid())
854
marginLeft = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetLeft());
855
if (attr.GetTextBoxAttr().GetMargins().GetRight().IsValid())
856
marginRight = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetRight());
857
if (attr.GetTextBoxAttr().GetMargins().GetTop().IsValid())
858
marginTop = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetTop());
859
if (attr.GetTextBoxAttr().GetMargins().GetBottom().IsValid())
860
marginBottom = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetBottom());
862
if (attr.GetTextBoxAttr().GetBorder().GetLeft().GetWidth().IsValid())
863
borderLeft = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetLeft().GetWidth());
864
if (attr.GetTextBoxAttr().GetBorder().GetRight().GetWidth().IsValid())
865
borderRight = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetRight().GetWidth());
866
if (attr.GetTextBoxAttr().GetBorder().GetTop().GetWidth().IsValid())
867
borderTop = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetTop().GetWidth());
868
if (attr.GetTextBoxAttr().GetBorder().GetBottom().GetWidth().IsValid())
869
borderBottom = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetBottom().GetWidth());
871
if (attr.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
872
paddingLeft = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetLeft());
873
if (attr.GetTextBoxAttr().GetPadding().GetRight().IsValid())
874
paddingRight = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetRight());
875
if (attr.GetTextBoxAttr().GetPadding().GetTop().IsValid())
876
paddingTop = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetTop());
877
if (attr.GetTextBoxAttr().GetPadding().GetBottom().IsValid())
878
paddingBottom = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetBottom());
880
if (attr.GetTextBoxAttr().GetOutline().GetLeft().GetWidth().IsValid())
881
outlineLeft = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetLeft().GetWidth());
882
if (attr.GetTextBoxAttr().GetOutline().GetRight().GetWidth().IsValid())
883
outlineRight = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetRight().GetWidth());
884
if (attr.GetTextBoxAttr().GetOutline().GetTop().GetWidth().IsValid())
885
outlineTop = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetTop().GetWidth());
886
if (attr.GetTextBoxAttr().GetOutline().GetBottom().GetWidth().IsValid())
887
outlineBottom = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetBottom().GetWidth());
889
int leftTotal = marginLeft + borderLeft + paddingLeft;
890
int rightTotal = marginRight + borderRight + paddingRight;
891
int topTotal = marginTop + borderTop + paddingTop;
892
int bottomTotal = marginBottom + borderBottom + paddingBottom;
894
if (marginRect != wxRect())
896
contentRect.x = marginRect.x + leftTotal;
897
contentRect.y = marginRect.y + topTotal;
898
contentRect.width = marginRect.width - (leftTotal + rightTotal);
899
contentRect.height = marginRect.height - (topTotal + bottomTotal);
903
marginRect.x = contentRect.x - leftTotal;
904
marginRect.y = contentRect.y - topTotal;
905
marginRect.width = contentRect.width + (leftTotal + rightTotal);
906
marginRect.height = contentRect.height + (topTotal + bottomTotal);
909
borderRect.x = marginRect.x + marginLeft;
910
borderRect.y = marginRect.y + marginTop;
911
borderRect.width = marginRect.width - (marginLeft + marginRight);
912
borderRect.height = marginRect.height - (marginTop + marginBottom);
914
paddingRect.x = marginRect.x + marginLeft + borderLeft;
915
paddingRect.y = marginRect.y + marginTop + borderTop;
916
paddingRect.width = marginRect.width - (marginLeft + marginRight + borderLeft + borderRight);
917
paddingRect.height = marginRect.height - (marginTop + marginBottom + borderTop + borderBottom);
919
// The outline is outside the margin and doesn't influence the overall box position or content size.
920
outlineRect.x = marginRect.x - outlineLeft;
921
outlineRect.y = marginRect.y - outlineTop;
922
outlineRect.width = marginRect.width + (outlineLeft + outlineRight);
923
outlineRect.height = marginRect.height + (outlineTop + outlineBottom);
928
// Get the total margin for the object in pixels, taking into account margin, padding and border size
929
bool wxRichTextObject::GetTotalMargin(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& attr, int& leftMargin, int& rightMargin,
930
int& topMargin, int& bottomMargin)
932
// Assume boxRect is the area around the content
933
wxRect contentRect, marginRect, borderRect, paddingRect, outlineRect;
934
marginRect = wxRect(0, 0, 1000, 1000);
936
GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
938
leftMargin = contentRect.GetLeft() - marginRect.GetLeft();
939
rightMargin = marginRect.GetRight() - contentRect.GetRight();
940
topMargin = contentRect.GetTop() - marginRect.GetTop();
941
bottomMargin = marginRect.GetBottom() - contentRect.GetBottom();
946
// Returns the rectangle which the child has available to it given restrictions specified in the
947
// child attribute, e.g. 50% width of the parent, 400 pixels, x position 20% of the parent, etc.
948
// availableContainerSpace might be a parent that the cell has to compute its width relative to.
949
// E.g. a cell that's 50% of its parent.
950
wxRect wxRichTextObject::AdjustAvailableSpace(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& WXUNUSED(parentAttr), const wxRichTextAttr& childAttr, const wxRect& availableParentSpace, const wxRect& availableContainerSpace)
952
wxRect rect = availableParentSpace;
955
scale = buffer->GetScale();
957
wxTextAttrDimensionConverter converter(dc, scale, availableContainerSpace.GetSize());
959
if (childAttr.GetTextBoxAttr().GetWidth().IsValid())
960
rect.width = converter.GetPixels(childAttr.GetTextBoxAttr().GetWidth());
962
if (childAttr.GetTextBoxAttr().GetHeight().IsValid())
963
rect.height = converter.GetPixels(childAttr.GetTextBoxAttr().GetHeight());
965
// Can specify either left or right for the position (we're assuming we can't
966
// set the left and right edges to effectively set the size. Would we want to do that?)
967
if (childAttr.GetTextBoxAttr().GetPosition().GetLeft().IsValid())
969
rect.x = rect.x + converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetLeft());
971
else if (childAttr.GetTextBoxAttr().GetPosition().GetRight().IsValid())
973
int x = converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetRight());
974
if (childAttr.GetTextBoxAttr().GetPosition().GetRight().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE)
975
rect.x = availableContainerSpace.x + availableContainerSpace.width - rect.width;
980
if (childAttr.GetTextBoxAttr().GetPosition().GetTop().IsValid())
982
rect.y = rect.y + converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetTop());
984
else if (childAttr.GetTextBoxAttr().GetPosition().GetBottom().IsValid())
986
int y = converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetBottom());
987
if (childAttr.GetTextBoxAttr().GetPosition().GetBottom().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE)
988
rect.y = availableContainerSpace.y + availableContainerSpace.height - rect.height;
993
if (rect.GetWidth() > availableParentSpace.GetWidth())
994
rect.SetWidth(availableParentSpace.GetWidth());
999
// Dump to output stream for debugging
1000
void wxRichTextObject::Dump(wxTextOutputStream& stream)
1002
stream << GetClassInfo()->GetClassName() << wxT("\n");
1003
stream << wxString::Format(wxT("Size: %d,%d. Position: %d,%d, Range: %ld,%ld"), m_size.x, m_size.y, m_pos.x, m_pos.y, m_range.GetStart(), m_range.GetEnd()) << wxT("\n");
1004
stream << wxString::Format(wxT("Text colour: %d,%d,%d."), (int) m_attributes.GetTextColour().Red(), (int) m_attributes.GetTextColour().Green(), (int) m_attributes.GetTextColour().Blue()) << wxT("\n");
1007
// Gets the containing buffer
1008
wxRichTextBuffer* wxRichTextObject::GetBuffer() const
1010
const wxRichTextObject* obj = this;
1011
while (obj && !wxDynamicCast(obj, wxRichTextBuffer))
1012
obj = obj->GetParent();
1013
return wxDynamicCast(obj, wxRichTextBuffer);
1016
// Get the absolute object position, by traversing up the child/parent hierarchy
1017
wxPoint wxRichTextObject::GetAbsolutePosition() const
1019
wxPoint pt = GetPosition();
1021
wxRichTextObject* p = GetParent();
1024
pt = pt + p->GetPosition();
1031
// Hit-testing: returns a flag indicating hit test details, plus
1032
// information about position
1033
int wxRichTextObject::HitTest(wxDC& WXUNUSED(dc), wxRichTextDrawingContext& WXUNUSED(context), const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int WXUNUSED(flags))
1036
return wxRICHTEXT_HITTEST_NONE;
1038
wxRect rect = GetRect();
1039
if (pt.x >= rect.x && pt.x < rect.x + rect.width &&
1040
pt.y >= rect.y && pt.y < rect.y + rect.height)
1043
*contextObj = GetParentContainer();
1044
textPosition = GetRange().GetStart();
1045
return wxRICHTEXT_HITTEST_ON;
1048
return wxRICHTEXT_HITTEST_NONE;
1051
// Lays out the object first with a given amount of space, and then if no width was specified in attr,
1052
// lays out the object again using the maximum ('best') size
1053
bool wxRichTextObject::LayoutToBestSize(wxDC& dc, wxRichTextDrawingContext& context, wxRichTextBuffer* buffer,
1054
const wxRichTextAttr& parentAttr, const wxRichTextAttr& attr,
1055
const wxRect& availableParentSpace, const wxRect& availableContainerSpace,
1058
wxRect availableChildRect = AdjustAvailableSpace(dc, buffer, parentAttr, attr, availableParentSpace, availableContainerSpace);
1059
wxRect originalAvailableRect = availableChildRect;
1060
Layout(dc, context, availableChildRect, availableContainerSpace, style);
1062
wxSize maxSize = GetMaxSize();
1064
// Don't ignore if maxSize.x is zero, since we need to redo the paragraph's lines
1066
if (!attr.GetTextBoxAttr().GetWidth().IsValid() && maxSize.x < availableChildRect.width)
1068
// Redo the layout with a fixed, minimum size this time.
1069
Invalidate(wxRICHTEXT_ALL);
1070
wxRichTextAttr newAttr(attr);
1071
newAttr.GetTextBoxAttr().GetWidth().SetValue(maxSize.x, wxTEXT_ATTR_UNITS_PIXELS);
1072
newAttr.GetTextBoxAttr().GetWidth().SetPosition(wxTEXT_BOX_ATTR_POSITION_ABSOLUTE);
1074
availableChildRect = AdjustAvailableSpace(dc, buffer, parentAttr, newAttr, availableParentSpace, availableContainerSpace);
1076
// If a paragraph, align the whole paragraph.
1077
// Problem with this: if we're limited by a floating object, a line may be centered
1078
// w.r.t. the smaller resulting box rather than the actual available width.
1079
if (attr.HasAlignment() && !GetContainer()->GetFloatCollector()->HasFloats()) // FIXME: aligning whole paragraph not compatible with floating objects
1081
// centering, right-justification
1082
if (attr.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE)
1084
availableChildRect.x = (originalAvailableRect.GetWidth() - availableChildRect.GetWidth())/2 + availableChildRect.x;
1086
else if (attr.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT)
1088
availableChildRect.x = availableChildRect.x + originalAvailableRect.GetWidth() - availableChildRect.GetWidth();
1092
Layout(dc, context, availableChildRect, availableContainerSpace, style);
1106
// Move the object recursively, by adding the offset from old to new
1107
void wxRichTextObject::Move(const wxPoint& pt)
1114
* wxRichTextCompositeObject
1115
* This is the base for drawable objects.
1118
IMPLEMENT_CLASS(wxRichTextCompositeObject, wxRichTextObject)
1120
wxRichTextCompositeObject::wxRichTextCompositeObject(wxRichTextObject* parent):
1121
wxRichTextObject(parent)
1125
wxRichTextCompositeObject::~wxRichTextCompositeObject()
1130
/// Get the nth child
1131
wxRichTextObject* wxRichTextCompositeObject::GetChild(size_t n) const
1133
wxASSERT ( n < m_children.GetCount() );
1135
return m_children.Item(n)->GetData();
1138
/// Append a child, returning the position
1139
size_t wxRichTextCompositeObject::AppendChild(wxRichTextObject* child)
1141
m_children.Append(child);
1142
child->SetParent(this);
1143
return m_children.GetCount() - 1;
1146
/// Insert the child in front of the given object, or at the beginning
1147
bool wxRichTextCompositeObject::InsertChild(wxRichTextObject* child, wxRichTextObject* inFrontOf)
1151
wxRichTextObjectList::compatibility_iterator node = m_children.Find(inFrontOf);
1152
m_children.Insert(node, child);
1155
m_children.Insert(child);
1156
child->SetParent(this);
1161
/// Delete the child
1162
bool wxRichTextCompositeObject::RemoveChild(wxRichTextObject* child, bool deleteChild)
1164
wxRichTextObjectList::compatibility_iterator node = m_children.Find(child);
1167
wxRichTextObject* obj = node->GetData();
1168
m_children.Erase(node);
1177
/// Delete all children
1178
bool wxRichTextCompositeObject::DeleteChildren()
1180
wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1183
wxRichTextObjectList::compatibility_iterator oldNode = node;
1185
wxRichTextObject* child = node->GetData();
1186
child->Dereference(); // Only delete if reference count is zero
1188
node = node->GetNext();
1189
m_children.Erase(oldNode);
1195
/// Get the child count
1196
size_t wxRichTextCompositeObject::GetChildCount() const
1198
return m_children.GetCount();
1202
void wxRichTextCompositeObject::Copy(const wxRichTextCompositeObject& obj)
1204
wxRichTextObject::Copy(obj);
1208
wxRichTextObjectList::compatibility_iterator node = obj.m_children.GetFirst();
1211
wxRichTextObject* child = node->GetData();
1212
wxRichTextObject* newChild = child->Clone();
1213
newChild->SetParent(this);
1214
m_children.Append(newChild);
1216
node = node->GetNext();
1220
/// Hit-testing: returns a flag indicating hit test details, plus
1221
/// information about position
1222
int wxRichTextCompositeObject::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
1225
return wxRICHTEXT_HITTEST_NONE;
1227
wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1230
wxRichTextObject* child = node->GetData();
1232
if (child->IsShown() && child->IsTopLevel() && (flags & wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS))
1234
// Just check if we hit the overall object
1235
int ret = child->wxRichTextObject::HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
1236
if (ret != wxRICHTEXT_HITTEST_NONE)
1239
else if (child->IsShown())
1241
int ret = child->HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
1242
if (ret != wxRICHTEXT_HITTEST_NONE)
1246
node = node->GetNext();
1249
return wxRICHTEXT_HITTEST_NONE;
1252
/// Finds the absolute position and row height for the given character position
1253
bool wxRichTextCompositeObject::FindPosition(wxDC& dc, wxRichTextDrawingContext& context, long index, wxPoint& pt, int* height, bool forceLineStart)
1255
wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1258
wxRichTextObject* child = node->GetData();
1260
// Don't recurse if the child is a top-level object,
1261
// such as a text box, because the character position will no longer
1262
// apply. By definition, a top-level object has its own range of
1263
// character positions.
1264
if (!child->IsTopLevel() && child->FindPosition(dc, context, index, pt, height, forceLineStart))
1267
node = node->GetNext();
1274
void wxRichTextCompositeObject::CalculateRange(long start, long& end)
1276
long current = start;
1277
long lastEnd = current;
1285
wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1288
wxRichTextObject* child = node->GetData();
1291
child->CalculateRange(current, childEnd);
1294
current = childEnd + 1;
1296
node = node->GetNext();
1301
// A top-level object always has a range of size 1,
1302
// because its children don't count at this level.
1304
m_range.SetRange(start, start);
1306
// An object with no children has zero length
1307
if (m_children.GetCount() == 0)
1309
m_ownRange.SetRange(0, lastEnd);
1315
// An object with no children has zero length
1316
if (m_children.GetCount() == 0)
1319
m_range.SetRange(start, end);
1323
/// Delete range from layout.
1324
bool wxRichTextCompositeObject::DeleteRange(const wxRichTextRange& range)
1326
wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1330
wxRichTextObject* obj = (wxRichTextObject*) node->GetData();
1331
wxRichTextObjectList::compatibility_iterator next = node->GetNext();
1333
// Delete the range in each paragraph
1335
// When a chunk has been deleted, internally the content does not
1336
// now match the ranges.
1337
// However, so long as deletion is not done on the same object twice this is OK.
1338
// If you may delete content from the same object twice, recalculate
1339
// the ranges inbetween DeleteRange calls by calling CalculateRanges, and
1340
// adjust the range you're deleting accordingly.
1342
if (!obj->GetRange().IsOutside(range))
1344
// No need to delete within a top-level object; just removing this object will do fine
1345
if (!obj->IsTopLevel())
1346
obj->DeleteRange(range);
1348
// Delete an empty object, or paragraph within this range.
1349
if (obj->IsEmpty() ||
1350
(range.GetStart() <= obj->GetRange().GetStart() && range.GetEnd() >= obj->GetRange().GetEnd()))
1352
// An empty paragraph has length 1, so won't be deleted unless the
1353
// whole range is deleted.
1354
RemoveChild(obj, true);
1364
/// Get any text in this object for the given range
1365
wxString wxRichTextCompositeObject::GetTextForRange(const wxRichTextRange& range) const
1368
wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1371
wxRichTextObject* child = node->GetData();
1372
wxRichTextRange childRange = range;
1373
if (!child->GetRange().IsOutside(range))
1375
childRange.LimitTo(child->GetRange());
1377
wxString childText = child->GetTextForRange(childRange);
1381
node = node->GetNext();
1387
/// Get the child object at the given character position
1388
wxRichTextObject* wxRichTextCompositeObject::GetChildAtPosition(long pos) const
1390
wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1393
wxRichTextObject* child = node->GetData();
1394
if (child->GetRange().GetStart() == pos)
1396
node = node->GetNext();
1401
/// Recursively merge all pieces that can be merged.
1402
bool wxRichTextCompositeObject::Defragment(const wxRichTextRange& range)
1404
wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1407
wxRichTextObject* child = node->GetData();
1408
if (range == wxRICHTEXT_ALL || !child->GetRange().IsOutside(range))
1410
wxRichTextCompositeObject* composite = wxDynamicCast(child, wxRichTextCompositeObject);
1412
composite->Defragment();
1414
if (node->GetNext())
1416
wxRichTextObject* nextChild = node->GetNext()->GetData();
1417
if (child->CanMerge(nextChild) && child->Merge(nextChild))
1419
nextChild->Dereference();
1420
m_children.Erase(node->GetNext());
1422
// Don't set node -- we'll see if we can merge again with the next
1426
node = node->GetNext();
1429
node = node->GetNext();
1432
node = node->GetNext();
1435
// Delete any remaining empty objects, but leave at least one empty object per composite object.
1436
if (GetChildCount() > 1)
1438
node = m_children.GetFirst();
1441
wxRichTextObjectList::compatibility_iterator next = node->GetNext();
1442
wxRichTextObject* child = node->GetData();
1443
if (range == wxRICHTEXT_ALL || !child->GetRange().IsOutside(range))
1445
if (child->IsEmpty())
1447
child->Dereference();
1448
m_children.Erase(node);
1453
node = node->GetNext();
1460
/// Dump to output stream for debugging
1461
void wxRichTextCompositeObject::Dump(wxTextOutputStream& stream)
1463
wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1466
wxRichTextObject* child = node->GetData();
1467
child->Dump(stream);
1468
node = node->GetNext();
1472
/// Get/set the object size for the given range. Returns false if the range
1473
/// is invalid for this object.
1474
bool wxRichTextCompositeObject::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* partialExtents) const
1476
if (!range.IsWithin(GetRange()))
1481
wxArrayInt childExtents;
1488
wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1491
wxRichTextObject* child = node->GetData();
1492
if (!child->GetRange().IsOutside(range))
1494
// Floating objects have a zero size within the paragraph.
1495
if (child->IsFloating())
1500
if (partialExtents->GetCount() > 0)
1501
lastSize = (*partialExtents)[partialExtents->GetCount()-1];
1505
partialExtents->Add(0 /* zero size */ + lastSize);
1512
wxRichTextRange rangeToUse = range;
1513
rangeToUse.LimitTo(child->GetRange());
1514
if (child->IsTopLevel())
1515
rangeToUse = child->GetOwnRange();
1517
int childDescent = 0;
1519
// At present wxRICHTEXT_HEIGHT_ONLY is only fast if we're already cached the size,
1520
// but it's only going to be used after caching has taken place.
1521
if ((flags & wxRICHTEXT_HEIGHT_ONLY) && child->GetCachedSize().y != 0)
1523
childDescent = child->GetDescent();
1524
childSize = child->GetCachedSize();
1526
sz.y = wxMax(sz.y, childSize.y);
1527
sz.x += childSize.x;
1528
descent = wxMax(descent, childDescent);
1530
else if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y), p))
1532
sz.y = wxMax(sz.y, childSize.y);
1533
sz.x += childSize.x;
1534
descent = wxMax(descent, childDescent);
1536
if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange() || child->IsTopLevel()))
1538
child->SetCachedSize(childSize);
1539
child->SetDescent(childDescent);
1545
if (partialExtents->GetCount() > 0)
1546
lastSize = (*partialExtents)[partialExtents->GetCount()-1];
1551
for (i = 0; i < childExtents.GetCount(); i++)
1553
partialExtents->Add(childExtents[i] + lastSize);
1563
node = node->GetNext();
1569
// Invalidate the buffer. With no argument, invalidates whole buffer.
1570
void wxRichTextCompositeObject::Invalidate(const wxRichTextRange& invalidRange)
1572
wxRichTextObject::Invalidate(invalidRange);
1574
wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1577
wxRichTextObject* child = node->GetData();
1578
if (invalidRange != wxRICHTEXT_ALL && invalidRange != wxRICHTEXT_NONE && child->GetRange().IsOutside(invalidRange))
1582
else if (child->IsTopLevel())
1584
if (invalidRange == wxRICHTEXT_NONE)
1585
child->Invalidate(wxRICHTEXT_NONE);
1587
child->Invalidate(wxRICHTEXT_ALL); // All children must be invalidated if within parent range
1590
child->Invalidate(invalidRange);
1591
node = node->GetNext();
1595
// Move the object recursively, by adding the offset from old to new
1596
void wxRichTextCompositeObject::Move(const wxPoint& pt)
1598
wxPoint oldPos = GetPosition();
1600
wxPoint offset = pt - oldPos;
1602
wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1605
wxRichTextObject* child = node->GetData();
1606
wxPoint childPos = child->GetPosition() + offset;
1607
child->Move(childPos);
1608
node = node->GetNext();
1614
* wxRichTextParagraphLayoutBox
1615
* This box knows how to lay out paragraphs.
1618
IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraphLayoutBox, wxRichTextCompositeObject)
1620
wxRichTextParagraphLayoutBox::wxRichTextParagraphLayoutBox(wxRichTextObject* parent):
1621
wxRichTextCompositeObject(parent)
1626
wxRichTextParagraphLayoutBox::~wxRichTextParagraphLayoutBox()
1628
if (m_floatCollector)
1630
delete m_floatCollector;
1631
m_floatCollector = NULL;
1635
/// Initialize the object.
1636
void wxRichTextParagraphLayoutBox::Init()
1640
// For now, assume is the only box and has no initial size.
1641
m_range = wxRichTextRange(0, -1);
1642
m_ownRange = wxRichTextRange(0, -1);
1644
m_invalidRange = wxRICHTEXT_ALL;
1646
m_partialParagraph = false;
1647
m_floatCollector = NULL;
1650
void wxRichTextParagraphLayoutBox::Clear()
1654
if (m_floatCollector)
1655
delete m_floatCollector;
1656
m_floatCollector = NULL;
1657
m_partialParagraph = false;
1661
void wxRichTextParagraphLayoutBox::Copy(const wxRichTextParagraphLayoutBox& obj)
1665
wxRichTextCompositeObject::Copy(obj);
1667
m_partialParagraph = obj.m_partialParagraph;
1668
m_defaultAttributes = obj.m_defaultAttributes;
1671
// Gather information about floating objects; only gather floats for those paragraphs that
1672
// will not be formatted again due to optimization, after which floats will be gathered per-paragraph
1674
bool wxRichTextParagraphLayoutBox::UpdateFloatingObjects(const wxRect& availableRect, wxRichTextObject* untilObj)
1676
if (m_floatCollector != NULL)
1677
delete m_floatCollector;
1678
m_floatCollector = new wxRichTextFloatCollector(availableRect);
1679
wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1680
// Only gather floats up to the point we'll start formatting paragraphs.
1681
while (untilObj && node && node->GetData() != untilObj)
1683
wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
1684
wxASSERT (child != NULL);
1686
m_floatCollector->CollectFloat(child);
1687
node = node->GetNext();
1693
// Returns the style sheet associated with the overall buffer.
1694
wxRichTextStyleSheet* wxRichTextParagraphLayoutBox::GetStyleSheet() const
1696
return GetBuffer() ? GetBuffer()->GetStyleSheet() : (wxRichTextStyleSheet*) NULL;
1699
// Get the number of floating objects at this level
1700
int wxRichTextParagraphLayoutBox::GetFloatingObjectCount() const
1702
if (m_floatCollector)
1703
return m_floatCollector->GetFloatingObjectCount();
1708
// Get a list of floating objects
1709
bool wxRichTextParagraphLayoutBox::GetFloatingObjects(wxRichTextObjectList& objects) const
1711
if (m_floatCollector)
1713
return m_floatCollector->GetFloatingObjects(objects);
1720
void wxRichTextParagraphLayoutBox::UpdateRanges()
1724
start = GetRange().GetStart();
1726
CalculateRange(start, end);
1730
int wxRichTextParagraphLayoutBox::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
1733
return wxRICHTEXT_HITTEST_NONE;
1735
int ret = wxRICHTEXT_HITTEST_NONE;
1736
if (m_floatCollector && (flags & wxRICHTEXT_HITTEST_NO_FLOATING_OBJECTS) == 0)
1737
ret = m_floatCollector->HitTest(dc, context, pt, textPosition, obj, flags);
1739
if (ret == wxRICHTEXT_HITTEST_NONE)
1740
return wxRichTextCompositeObject::HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
1748
/// Draw the floating objects
1749
void wxRichTextParagraphLayoutBox::DrawFloats(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
1751
if (m_floatCollector)
1752
m_floatCollector->Draw(dc, context, range, selection, rect, descent, style);
1755
void wxRichTextParagraphLayoutBox::MoveAnchoredObjectToParagraph(wxRichTextParagraph* from, wxRichTextParagraph* to, wxRichTextObject* obj)
1760
from->RemoveChild(obj);
1761
to->AppendChild(obj);
1765
bool wxRichTextParagraphLayoutBox::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
1770
wxRect thisRect(GetPosition(), GetCachedSize());
1772
wxRichTextAttr attr(GetAttributes());
1773
context.ApplyVirtualAttributes(attr, this);
1776
if (selection.IsValid() && GetParentContainer() != this && selection.WithinSelection(GetRange().GetStart(), GetParentContainer()))
1777
flags |= wxRICHTEXT_DRAW_SELECTED;
1779
// Don't draw guidelines if at top level
1780
int theseFlags = flags;
1782
theseFlags &= ~wxRICHTEXT_DRAW_GUIDELINES;
1783
DrawBoxAttributes(dc, GetBuffer(), attr, thisRect, theseFlags);
1785
DrawFloats(dc, context, range, selection, rect, descent, style);
1786
wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1789
wxRichTextObject* child = node->GetData();
1791
if (child && !child->GetRange().IsOutside(range))
1793
wxRect childRect(child->GetPosition(), child->GetCachedSize());
1794
wxRichTextRange childRange = range;
1795
if (child->IsTopLevel())
1797
childRange = child->GetOwnRange();
1800
if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) == 0) && childRect.GetTop() > rect.GetBottom())
1805
else if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) == 0) && childRect.GetBottom() < rect.GetTop())
1810
child->Draw(dc, context, childRange, selection, rect, descent, style);
1813
node = node->GetNext();
1818
/// Lay the item out
1819
bool wxRichTextParagraphLayoutBox::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style)
1821
SetPosition(rect.GetPosition());
1826
wxRect availableSpace;
1827
bool formatRect = (style & wxRICHTEXT_LAYOUT_SPECIFIED_RECT) == wxRICHTEXT_LAYOUT_SPECIFIED_RECT;
1829
wxRichTextAttr attr(GetAttributes());
1830
context.ApplyVirtualAttributes(attr, this);
1832
// If only laying out a specific area, the passed rect has a different meaning:
1833
// the visible part of the buffer. This is used in wxRichTextCtrl::OnSize,
1834
// so that during a size, only the visible part will be relaid out, or
1835
// it would take too long causing flicker. As an approximation, we assume that
1836
// everything up to the start of the visible area is laid out correctly.
1839
wxRect rect2(0, 0, rect.width, rect.height);
1840
availableSpace = GetAvailableContentArea(dc, context, rect2);
1842
// Invalidate the part of the buffer from the first visible line
1843
// to the end. If other parts of the buffer are currently invalid,
1844
// then they too will be taken into account if they are above
1845
// the visible point.
1847
wxRichTextLine* line = GetLineAtYPosition(rect.y);
1849
startPos = line->GetAbsoluteRange().GetStart();
1851
Invalidate(wxRichTextRange(startPos, GetOwnRange().GetEnd()));
1855
availableSpace = GetAvailableContentArea(dc, context, rect);
1858
// Fix the width if we're at the top level
1860
attr.GetTextBoxAttr().GetWidth().SetValue(rect.GetWidth(), wxTEXT_ATTR_UNITS_PIXELS);
1862
int leftMargin, rightMargin, topMargin, bottomMargin;
1863
wxRichTextObject::GetTotalMargin(dc, GetBuffer(), attr, leftMargin, rightMargin,
1864
topMargin, bottomMargin);
1869
// The maximum paragraph maximum width, so we can set the overall maximum width for this object
1870
int maxMaxWidth = 0;
1872
// The maximum paragraph minimum width, so we can set the overall minimum width for this object
1873
int maxMinWidth = 0;
1875
// If we have vertical alignment, we must recalculate everything.
1876
bool hasVerticalAlignment = (attr.GetTextBoxAttr().HasVerticalAlignment() &&
1877
(attr.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP));
1879
wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1881
bool layoutAll = true;
1883
// Get invalid range, rounding to paragraph start/end.
1884
wxRichTextRange invalidRange = GetInvalidRange(true);
1886
if (invalidRange == wxRICHTEXT_NONE && !formatRect)
1889
if (invalidRange == wxRICHTEXT_ALL || hasVerticalAlignment)
1891
else // If we know what range is affected, start laying out from that point on.
1892
if (invalidRange.GetStart() >= GetOwnRange().GetStart())
1894
wxRichTextParagraph* firstParagraph = GetParagraphAtPosition(invalidRange.GetStart());
1897
wxRichTextObjectList::compatibility_iterator firstNode = m_children.Find(firstParagraph);
1898
wxRichTextObjectList::compatibility_iterator previousNode;
1900
previousNode = firstNode->GetPrevious();
1905
wxRichTextParagraph* previousParagraph = wxDynamicCast(previousNode->GetData(), wxRichTextParagraph);
1906
availableSpace.y = previousParagraph->GetPosition().y + previousParagraph->GetCachedSize().y;
1909
// Now we're going to start iterating from the first affected paragraph.
1917
// Gather information about only those floating objects that will not be formatted,
1918
// after which floats will be gathered per-paragraph during layout.
1919
UpdateFloatingObjects(availableSpace, node ? node->GetData() : (wxRichTextObject*) NULL);
1921
// A way to force speedy rest-of-buffer layout (the 'else' below)
1922
bool forceQuickLayout = false;
1924
// First get the size of the paragraphs we won't be laying out
1925
wxRichTextObjectList::compatibility_iterator n = m_children.GetFirst();
1926
while (n && n != node)
1928
wxRichTextParagraph* child = wxDynamicCast(n->GetData(), wxRichTextParagraph);
1931
maxWidth = wxMax(maxWidth, child->GetCachedSize().x);
1932
maxMinWidth = wxMax(maxMinWidth, child->GetMinSize().x);
1933
maxMaxWidth = wxMax(maxMaxWidth, child->GetMaxSize().x);
1940
// Assume this box only contains paragraphs
1942
wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
1943
// Unsure if this is needed
1944
// wxCHECK_MSG( child, false, wxT("Unknown object in layout") );
1946
if (child && child->IsShown())
1948
// TODO: what if the child hasn't been laid out (e.g. involved in Undo) but still has 'old' lines
1949
if ( !forceQuickLayout &&
1951
child->GetLines().IsEmpty() ||
1952
!child->GetRange().IsOutside(invalidRange)) )
1954
// Lays out the object first with a given amount of space, and then if no width was specified in attr,
1955
// lays out the object again using the minimum size
1956
child->LayoutToBestSize(dc, context, GetBuffer(),
1957
attr, child->GetAttributes(), availableSpace, rect, style&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT);
1959
// Layout must set the cached size
1960
availableSpace.y += child->GetCachedSize().y;
1961
maxWidth = wxMax(maxWidth, child->GetCachedSize().x);
1962
maxMinWidth = wxMax(maxMinWidth, child->GetMinSize().x);
1963
maxMaxWidth = wxMax(maxMaxWidth, child->GetMaxSize().x);
1965
// If we're just formatting the visible part of the buffer,
1966
// and we're now past the bottom of the window, and we don't have any
1967
// floating objects (since they may cause wrapping to change for the rest of the
1968
// the buffer), start quick layout.
1969
if (!hasVerticalAlignment && formatRect && child->GetPosition().y > rect.GetBottom() && GetFloatingObjectCount() == 0)
1970
forceQuickLayout = true;
1974
// We're outside the immediately affected range, so now let's just
1975
// move everything up or down. This assumes that all the children have previously
1976
// been laid out and have wrapped line lists associated with them.
1977
// TODO: check all paragraphs before the affected range.
1979
int inc = availableSpace.y - child->GetPosition().y;
1983
wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
1986
if (child->GetLines().GetCount() == 0)
1988
// Lays out the object first with a given amount of space, and then if no width was specified in attr,
1989
// lays out the object again using the minimum size
1990
child->LayoutToBestSize(dc, context, GetBuffer(),
1991
attr, child->GetAttributes(), availableSpace, rect, style&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT);
1993
//child->Layout(dc, availableChildRect, style);
1996
child->Move(wxPoint(child->GetPosition().x, child->GetPosition().y + inc));
1998
availableSpace.y += child->GetCachedSize().y;
1999
maxWidth = wxMax(maxWidth, child->GetCachedSize().x);
2000
maxMinWidth = wxMax(maxMinWidth, child->GetMinSize().x);
2001
maxMaxWidth = wxMax(maxMaxWidth, child->GetMaxSize().x);
2004
node = node->GetNext();
2010
node = node->GetNext();
2013
node = m_children.GetLast();
2014
if (node && node->GetData()->IsShown())
2016
wxRichTextObject* child = node->GetData();
2017
maxHeight = child->GetPosition().y - (GetPosition().y + topMargin) + child->GetCachedSize().y;
2020
maxHeight = 0; // topMargin + bottomMargin;
2022
// Check the bottom edge of any floating object
2023
if (GetFloatCollector() && GetFloatCollector()->HasFloats())
2025
int bottom = GetFloatCollector()->GetLastRectBottom();
2026
if (bottom > maxHeight)
2030
if (attr.GetTextBoxAttr().GetSize().GetWidth().IsValid())
2032
wxRect r = AdjustAvailableSpace(dc, GetBuffer(), wxRichTextAttr() /* not used */, attr, parentRect, parentRect);
2033
int w = r.GetWidth();
2035
// Convert external to content rect
2036
w = w - leftMargin - rightMargin;
2037
maxWidth = wxMax(maxWidth, w);
2038
maxMaxWidth = wxMax(maxMaxWidth, w);
2042
// TODO: Make sure the layout box's position reflects
2043
// the position of the children, but without
2044
// breaking layout of a box within a paragraph.
2047
// TODO: (also in para layout) should set the
2048
// object's size to an absolute one if specified,
2049
// but if not specified, calculate it from content.
2051
// We need to add back the margins etc.
2053
wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
2054
contentRect = wxRect(wxPoint(0, 0), wxSize(maxWidth, maxHeight));
2055
GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
2056
SetCachedSize(marginRect.GetSize());
2059
// The maximum size is the greatest of all maximum widths for all paragraphs.
2061
wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
2062
contentRect = wxRect(wxPoint(0, 0), wxSize(maxMaxWidth, maxHeight)); // Actually max height is a lie, we can't know it
2063
GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
2064
SetMaxSize(marginRect.GetSize());
2067
// The minimum size is the greatest of all minimum widths for all paragraphs.
2069
wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
2070
contentRect = wxRect(wxPoint(0, 0), wxSize(maxMinWidth, maxHeight)); // Actually max height is a lie, we can't know it
2071
GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
2072
SetMinSize(marginRect.GetSize());
2075
if (attr.GetTextBoxAttr().HasVerticalAlignment() &&
2076
(attr.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP))
2079
int leftOverSpace = availableSpace.height - topMargin - bottomMargin - maxHeight;
2080
if (leftOverSpace > 0)
2082
if (attr.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_CENTRE)
2084
yOffset = (leftOverSpace/2);
2086
else if (attr.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_BOTTOM)
2088
yOffset = leftOverSpace;
2092
// Move all the children to vertically align the content
2093
// This doesn't take into account floating objects, unfortunately.
2096
wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2099
wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2101
child->Move(wxPoint(child->GetPosition().x, child->GetPosition().y + yOffset));
2103
node = node->GetNext();
2108
m_invalidRange = wxRICHTEXT_NONE;
2113
/// Get/set the size for the given range.
2114
bool wxRichTextParagraphLayoutBox::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* WXUNUSED(partialExtents)) const
2118
wxRichTextObjectList::compatibility_iterator startPara = wxRichTextObjectList::compatibility_iterator();
2119
wxRichTextObjectList::compatibility_iterator endPara = wxRichTextObjectList::compatibility_iterator();
2121
// First find the first paragraph whose starting position is within the range.
2122
wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2125
// child is a paragraph
2126
wxRichTextObject* child = node->GetData();
2127
const wxRichTextRange& r = child->GetRange();
2129
if (r.GetStart() <= range.GetStart() && r.GetEnd() >= range.GetStart())
2135
node = node->GetNext();
2138
// Next find the last paragraph containing part of the range
2139
node = m_children.GetFirst();
2142
// child is a paragraph
2143
wxRichTextObject* child = node->GetData();
2144
const wxRichTextRange& r = child->GetRange();
2146
if (r.GetStart() <= range.GetEnd() && r.GetEnd() >= range.GetEnd())
2152
node = node->GetNext();
2155
if (!startPara || !endPara)
2158
// Now we can add up the sizes
2159
for (node = startPara; node ; node = node->GetNext())
2161
// child is a paragraph
2162
wxRichTextObject* child = node->GetData();
2163
const wxRichTextRange& childRange = child->GetRange();
2164
wxRichTextRange rangeToFind = range;
2165
rangeToFind.LimitTo(childRange);
2167
if (child->IsTopLevel())
2168
rangeToFind = child->GetOwnRange();
2172
int childDescent = 0;
2173
child->GetRangeSize(rangeToFind, childSize, childDescent, dc, context, flags, position);
2175
descent = wxMax(childDescent, descent);
2177
sz.x = wxMax(sz.x, childSize.x);
2178
sz.y += childSize.y;
2180
if (node == endPara)
2189
/// Get the paragraph at the given position
2190
wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphAtPosition(long pos, bool caretPosition) const
2195
// First find the first paragraph whose starting position is within the range.
2196
wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2199
// child is a paragraph
2200
wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2201
// wxASSERT (child != NULL);
2205
// Return first child in buffer if position is -1
2209
if (child->GetRange().Contains(pos))
2213
node = node->GetNext();
2218
/// Get the line at the given position
2219
wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineAtPosition(long pos, bool caretPosition) const
2224
// First find the first paragraph whose starting position is within the range.
2225
wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2228
wxRichTextObject* obj = (wxRichTextObject*) node->GetData();
2229
if (obj->GetRange().Contains(pos))
2231
// child is a paragraph
2232
wxRichTextParagraph* child = wxDynamicCast(obj, wxRichTextParagraph);
2233
// wxASSERT (child != NULL);
2237
wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2240
wxRichTextLine* line = node2->GetData();
2242
wxRichTextRange range = line->GetAbsoluteRange();
2244
if (range.Contains(pos) ||
2246
// If the position is end-of-paragraph, then return the last line of
2247
// of the paragraph.
2248
((range.GetEnd() == child->GetRange().GetEnd()-1) && (pos == child->GetRange().GetEnd())))
2251
node2 = node2->GetNext();
2256
node = node->GetNext();
2259
int lineCount = GetLineCount();
2261
return GetLineForVisibleLineNumber(lineCount-1);
2266
/// Get the line at the given y pixel position, or the last line.
2267
wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineAtYPosition(int y) const
2269
wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2272
wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2273
// wxASSERT (child != NULL);
2277
wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2280
wxRichTextLine* line = node2->GetData();
2282
wxRect rect(line->GetRect());
2284
if (y <= rect.GetBottom())
2287
node2 = node2->GetNext();
2291
node = node->GetNext();
2295
int lineCount = GetLineCount();
2297
return GetLineForVisibleLineNumber(lineCount-1);
2302
/// Get the number of visible lines
2303
int wxRichTextParagraphLayoutBox::GetLineCount() const
2307
wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2310
wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2311
// wxASSERT (child != NULL);
2314
count += child->GetLines().GetCount();
2316
node = node->GetNext();
2322
/// Get the paragraph for a given line
2323
wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphForLine(wxRichTextLine* line) const
2325
return GetParagraphAtPosition(line->GetAbsoluteRange().GetStart());
2328
/// Get the line size at the given position
2329
wxSize wxRichTextParagraphLayoutBox::GetLineSizeAtPosition(long pos, bool caretPosition) const
2331
wxRichTextLine* line = GetLineAtPosition(pos, caretPosition);
2334
return line->GetSize();
2337
return wxSize(0, 0);
2341
/// Convenience function to add a paragraph of text
2342
wxRichTextRange wxRichTextParagraphLayoutBox::AddParagraph(const wxString& text, wxRichTextAttr* paraStyle)
2344
// Don't use the base style, just the default style, and the base style will
2345
// be combined at display time.
2346
// Divide into paragraph and character styles.
2348
wxRichTextAttr defaultCharStyle;
2349
wxRichTextAttr defaultParaStyle;
2351
// If the default style is a named paragraph style, don't apply any character formatting
2352
// to the initial text string.
2353
if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2355
wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2357
defaultParaStyle = def->GetStyleMergedWithBase(GetStyleSheet());
2360
wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle, defaultCharStyle);
2362
wxRichTextAttr* pStyle = paraStyle ? paraStyle : (wxRichTextAttr*) & defaultParaStyle;
2363
wxRichTextAttr* cStyle = & defaultCharStyle;
2365
wxRichTextParagraph* para = new wxRichTextParagraph(text, this, pStyle, cStyle);
2366
para->GetAttributes().GetTextBoxAttr().Reset();
2372
return para->GetRange();
2375
/// Adds multiple paragraphs, based on newlines.
2376
wxRichTextRange wxRichTextParagraphLayoutBox::AddParagraphs(const wxString& text, wxRichTextAttr* paraStyle)
2378
// Don't use the base style, just the default style, and the base style will
2379
// be combined at display time.
2380
// Divide into paragraph and character styles.
2382
wxRichTextAttr defaultCharStyle;
2383
wxRichTextAttr defaultParaStyle;
2385
// If the default style is a named paragraph style, don't apply any character formatting
2386
// to the initial text string.
2387
if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2389
wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2391
defaultParaStyle = def->GetStyleMergedWithBase(GetStyleSheet());
2394
wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle, defaultCharStyle);
2396
wxRichTextAttr* pStyle = paraStyle ? paraStyle : (wxRichTextAttr*) & defaultParaStyle;
2397
wxRichTextAttr* cStyle = & defaultCharStyle;
2399
wxRichTextParagraph* firstPara = NULL;
2400
wxRichTextParagraph* lastPara = NULL;
2402
wxRichTextRange range(-1, -1);
2405
size_t len = text.length();
2407
wxRichTextParagraph* para = new wxRichTextParagraph(wxEmptyString, this, pStyle, cStyle);
2408
para->GetAttributes().GetTextBoxAttr().Reset();
2417
wxChar ch = text[i];
2418
if (ch == wxT('\n') || ch == wxT('\r'))
2422
wxRichTextPlainText* plainText = (wxRichTextPlainText*) para->GetChildren().GetFirst()->GetData();
2423
plainText->SetText(line);
2425
para = new wxRichTextParagraph(wxEmptyString, this, pStyle, cStyle);
2426
para->GetAttributes().GetTextBoxAttr().Reset();
2431
line = wxEmptyString;
2442
wxRichTextPlainText* plainText = (wxRichTextPlainText*) para->GetChildren().GetFirst()->GetData();
2443
plainText->SetText(line);
2448
return wxRichTextRange(firstPara->GetRange().GetStart(), lastPara->GetRange().GetEnd());
2451
/// Convenience function to add an image
2452
wxRichTextRange wxRichTextParagraphLayoutBox::AddImage(const wxImage& image, wxRichTextAttr* paraStyle)
2454
// Don't use the base style, just the default style, and the base style will
2455
// be combined at display time.
2456
// Divide into paragraph and character styles.
2458
wxRichTextAttr defaultCharStyle;
2459
wxRichTextAttr defaultParaStyle;
2461
// If the default style is a named paragraph style, don't apply any character formatting
2462
// to the initial text string.
2463
if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2465
wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2467
defaultParaStyle = def->GetStyleMergedWithBase(GetStyleSheet());
2470
wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle, defaultCharStyle);
2472
wxRichTextAttr* pStyle = paraStyle ? paraStyle : (wxRichTextAttr*) & defaultParaStyle;
2473
wxRichTextAttr* cStyle = & defaultCharStyle;
2475
wxRichTextParagraph* para = new wxRichTextParagraph(this, pStyle);
2476
para->GetAttributes().GetTextBoxAttr().Reset();
2478
para->AppendChild(new wxRichTextImage(image, this, cStyle));
2482
return para->GetRange();
2486
/// Insert fragment into this box at the given position. If partialParagraph is true,
2487
/// it is assumed that the last (or only) paragraph is just a piece of data with no paragraph
2490
bool wxRichTextParagraphLayoutBox::InsertFragment(long position, wxRichTextParagraphLayoutBox& fragment)
2492
// First, find the first paragraph whose starting position is within the range.
2493
wxRichTextParagraph* para = GetParagraphAtPosition(position);
2496
wxRichTextAttr originalAttr = para->GetAttributes();
2498
wxRichTextObjectList::compatibility_iterator node = m_children.Find(para);
2500
// Now split at this position, returning the object to insert the new
2501
// ones in front of.
2502
wxRichTextObject* nextObject = para->SplitAt(position);
2504
// Special case: partial paragraph, just one paragraph. Might be a small amount of
2505
// text, for example, so let's optimize.
2507
if (fragment.GetPartialParagraph() && fragment.GetChildren().GetCount() == 1)
2509
// Add the first para to this para...
2510
wxRichTextObjectList::compatibility_iterator firstParaNode = fragment.GetChildren().GetFirst();
2514
// Iterate through the fragment paragraph inserting the content into this paragraph.
2515
wxRichTextParagraph* firstPara = wxDynamicCast(firstParaNode->GetData(), wxRichTextParagraph);
2516
wxASSERT (firstPara != NULL);
2518
wxRichTextObjectList::compatibility_iterator objectNode = firstPara->GetChildren().GetFirst();
2521
wxRichTextObject* newObj = objectNode->GetData()->Clone();
2526
para->AppendChild(newObj);
2530
// Insert before nextObject
2531
para->InsertChild(newObj, nextObject);
2534
objectNode = objectNode->GetNext();
2541
// Procedure for inserting a fragment consisting of a number of
2544
// 1. Remove and save the content that's after the insertion point, for adding
2545
// back once we've added the fragment.
2546
// 2. Add the content from the first fragment paragraph to the current
2548
// 3. Add remaining fragment paragraphs after the current paragraph.
2549
// 4. Add back the saved content from the first paragraph. If partialParagraph
2550
// is true, add it to the last paragraph added and not a new one.
2552
// 1. Remove and save objects after split point.
2553
wxList savedObjects;
2555
para->MoveToList(nextObject, savedObjects);
2557
// 2. Add the content from the 1st fragment paragraph.
2558
wxRichTextObjectList::compatibility_iterator firstParaNode = fragment.GetChildren().GetFirst();
2562
wxRichTextParagraph* firstPara = wxDynamicCast(firstParaNode->GetData(), wxRichTextParagraph);
2563
wxASSERT(firstPara != NULL);
2565
if (!(fragment.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE))
2566
para->SetAttributes(firstPara->GetAttributes());
2568
// Save empty paragraph attributes for appending later
2569
// These are character attributes deliberately set for a new paragraph. Without this,
2570
// we couldn't pass default attributes when appending a new paragraph.
2571
wxRichTextAttr emptyParagraphAttributes;
2573
wxRichTextObjectList::compatibility_iterator objectNode = firstPara->GetChildren().GetFirst();
2575
if (objectNode && firstPara->GetChildren().GetCount() == 1 && objectNode->GetData()->IsEmpty())
2576
emptyParagraphAttributes = objectNode->GetData()->GetAttributes();
2580
wxRichTextObject* newObj = objectNode->GetData()->Clone();
2583
para->AppendChild(newObj);
2585
objectNode = objectNode->GetNext();
2588
// 3. Add remaining fragment paragraphs after the current paragraph.
2589
wxRichTextObjectList::compatibility_iterator nextParagraphNode = node->GetNext();
2590
wxRichTextObject* nextParagraph = NULL;
2591
if (nextParagraphNode)
2592
nextParagraph = nextParagraphNode->GetData();
2594
wxRichTextObjectList::compatibility_iterator i = fragment.GetChildren().GetFirst()->GetNext();
2595
wxRichTextParagraph* finalPara = para;
2597
bool needExtraPara = (!i || !fragment.GetPartialParagraph());
2599
// If there was only one paragraph, we need to insert a new one.
2602
wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
2603
wxASSERT( para != NULL );
2605
finalPara = (wxRichTextParagraph*) para->Clone();
2608
InsertChild(finalPara, nextParagraph);
2610
AppendChild(finalPara);
2615
// If there was only one paragraph, or we have full paragraphs in our fragment,
2616
// we need to insert a new one.
2619
finalPara = new wxRichTextParagraph;
2622
InsertChild(finalPara, nextParagraph);
2624
AppendChild(finalPara);
2627
// 4. Add back the remaining content.
2631
finalPara->MoveFromList(savedObjects);
2633
// Ensure there's at least one object
2634
if (finalPara->GetChildCount() == 0)
2636
wxRichTextPlainText* text = new wxRichTextPlainText(wxEmptyString);
2637
text->SetAttributes(emptyParagraphAttributes);
2639
finalPara->AppendChild(text);
2643
if ((fragment.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE) && firstPara)
2644
finalPara->SetAttributes(firstPara->GetAttributes());
2645
else if (finalPara && finalPara != para)
2646
finalPara->SetAttributes(originalAttr);
2654
wxRichTextObjectList::compatibility_iterator i = fragment.GetChildren().GetFirst();
2657
wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
2658
wxASSERT( para != NULL );
2660
AppendChild(para->Clone());
2669
/// Make a copy of the fragment corresponding to the given range, putting it in 'fragment'.
2670
/// If there was an incomplete paragraph at the end, partialParagraph is set to true.
2671
bool wxRichTextParagraphLayoutBox::CopyFragment(const wxRichTextRange& range, wxRichTextParagraphLayoutBox& fragment)
2673
wxRichTextObjectList::compatibility_iterator i = GetChildren().GetFirst();
2676
wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
2677
wxASSERT( para != NULL );
2679
if (!para->GetRange().IsOutside(range))
2681
fragment.AppendChild(para->Clone());
2686
// Now top and tail the first and last paragraphs in our new fragment (which might be the same).
2687
if (!fragment.IsEmpty())
2689
wxRichTextParagraph* firstPara = wxDynamicCast(fragment.GetChildren().GetFirst()->GetData(), wxRichTextParagraph);
2690
wxASSERT( firstPara != NULL );
2692
wxRichTextParagraph* lastPara = wxDynamicCast(fragment.GetChildren().GetLast()->GetData(), wxRichTextParagraph);
2693
wxASSERT( lastPara != NULL );
2695
if (!firstPara || !lastPara)
2698
bool isFragment = (range.GetEnd() < lastPara->GetRange().GetEnd());
2700
long firstPos = firstPara->GetRange().GetStart();
2702
// Adjust for renumbering from zero
2703
wxRichTextRange topTailRange(range.GetStart() - firstPos, range.GetEnd() - firstPos);
2706
fragment.CalculateRange(0, end);
2708
// Chop off the start of the paragraph
2709
if (topTailRange.GetStart() > 0)
2711
wxRichTextRange r(0, topTailRange.GetStart()-1);
2712
firstPara->DeleteRange(r);
2714
// Make sure the numbering is correct
2715
fragment.CalculateRange(0, end);
2717
// Now, we've deleted some positions, so adjust the range
2719
topTailRange.SetStart(range.GetLength());
2720
topTailRange.SetEnd(fragment.GetOwnRange().GetEnd());
2724
topTailRange.SetStart(range.GetLength());
2725
topTailRange.SetEnd(fragment.GetOwnRange().GetEnd());
2728
if (topTailRange.GetStart() < lastPara->GetRange().GetEnd())
2730
lastPara->DeleteRange(topTailRange);
2732
// Make sure the numbering is correct
2734
fragment.CalculateRange(0, end);
2736
// We only have part of a paragraph at the end
2737
fragment.SetPartialParagraph(true);
2741
// We have a partial paragraph (don't save last new paragraph marker)
2742
// or complete paragraph
2743
fragment.SetPartialParagraph(isFragment);
2750
/// Given a position, get the number of the visible line (potentially many to a paragraph),
2751
/// starting from zero at the start of the buffer.
2752
long wxRichTextParagraphLayoutBox::GetVisibleLineNumber(long pos, bool caretPosition, bool startOfLine) const
2759
wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2762
wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2763
// wxASSERT( child != NULL );
2767
if (child->GetRange().Contains(pos))
2769
wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2772
wxRichTextLine* line = node2->GetData();
2773
wxRichTextRange lineRange = line->GetAbsoluteRange();
2775
if (lineRange.Contains(pos) || pos == lineRange.GetStart())
2777
// If the caret is displayed at the end of the previous wrapped line,
2778
// we want to return the line it's _displayed_ at (not the actual line
2779
// containing the position).
2780
if (lineRange.GetStart() == pos && !startOfLine && child->GetRange().GetStart() != pos)
2781
return lineCount - 1;
2788
node2 = node2->GetNext();
2790
// If we didn't find it in the lines, it must be
2791
// the last position of the paragraph. So return the last line.
2795
lineCount += child->GetLines().GetCount();
2798
node = node->GetNext();
2805
/// Given a line number, get the corresponding wxRichTextLine object.
2806
wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineForVisibleLineNumber(long lineNumber) const
2810
wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2813
wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2814
// wxASSERT(child != NULL);
2818
if (lineNumber < (int) (child->GetLines().GetCount() + lineCount))
2820
wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2823
wxRichTextLine* line = node2->GetData();
2825
if (lineCount == lineNumber)
2830
node2 = node2->GetNext();
2834
lineCount += child->GetLines().GetCount();
2837
node = node->GetNext();
2844
/// Delete range from layout.
2845
bool wxRichTextParagraphLayoutBox::DeleteRange(const wxRichTextRange& range)
2847
wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2849
wxRichTextParagraph* firstPara = NULL;
2852
wxRichTextParagraph* obj = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2853
// wxASSERT (obj != NULL);
2855
wxRichTextObjectList::compatibility_iterator next = node->GetNext();
2859
// Delete the range in each paragraph
2861
if (!obj->GetRange().IsOutside(range))
2863
// Deletes the content of this object within the given range
2864
obj->DeleteRange(range);
2866
wxRichTextRange thisRange = obj->GetRange();
2867
wxRichTextAttr thisAttr = obj->GetAttributes();
2869
// If the whole paragraph is within the range to delete,
2870
// delete the whole thing.
2871
if (range.GetStart() <= thisRange.GetStart() && range.GetEnd() >= thisRange.GetEnd())
2873
// Delete the whole object
2874
RemoveChild(obj, true);
2877
else if (!firstPara)
2880
// If the range includes the paragraph end, we need to join this
2881
// and the next paragraph.
2882
if (range.GetEnd() <= thisRange.GetEnd())
2884
// We need to move the objects from the next paragraph
2885
// to this paragraph
2887
wxRichTextParagraph* nextParagraph = NULL;
2888
if ((range.GetEnd() < thisRange.GetEnd()) && obj)
2889
nextParagraph = obj;
2892
// We're ending at the end of the paragraph, so merge the _next_ paragraph.
2894
nextParagraph = wxDynamicCast(next->GetData(), wxRichTextParagraph);
2897
bool applyFinalParagraphStyle = firstPara && nextParagraph && nextParagraph != firstPara;
2899
wxRichTextAttr nextParaAttr;
2900
if (applyFinalParagraphStyle)
2902
// Special case when deleting the end of a paragraph - use _this_ paragraph's style,
2903
// not the next one.
2904
if (range.GetStart() == range.GetEnd() && range.GetStart() == thisRange.GetEnd())
2905
nextParaAttr = thisAttr;
2907
nextParaAttr = nextParagraph->GetAttributes();
2910
if (firstPara && nextParagraph && firstPara != nextParagraph)
2912
// Move the objects to the previous para
2913
wxRichTextObjectList::compatibility_iterator node1 = nextParagraph->GetChildren().GetFirst();
2917
wxRichTextObject* obj1 = node1->GetData();
2919
firstPara->AppendChild(obj1);
2921
wxRichTextObjectList::compatibility_iterator next1 = node1->GetNext();
2922
nextParagraph->GetChildren().Erase(node1);
2927
// Delete the paragraph
2928
RemoveChild(nextParagraph, true);
2931
// Avoid empty paragraphs
2932
if (firstPara && firstPara->GetChildren().GetCount() == 0)
2934
wxRichTextPlainText* text = new wxRichTextPlainText(wxEmptyString);
2935
firstPara->AppendChild(text);
2938
if (applyFinalParagraphStyle)
2939
firstPara->SetAttributes(nextParaAttr);
2952
/// Get any text in this object for the given range
2953
wxString wxRichTextParagraphLayoutBox::GetTextForRange(const wxRichTextRange& range) const
2957
wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2960
wxRichTextObject* child = node->GetData();
2961
if (!child->GetRange().IsOutside(range))
2963
wxRichTextRange childRange = range;
2964
childRange.LimitTo(child->GetRange());
2966
wxString childText = child->GetTextForRange(childRange);
2970
if ((childRange.GetEnd() == child->GetRange().GetEnd()) && node->GetNext())
2975
node = node->GetNext();
2981
/// Get all the text
2982
wxString wxRichTextParagraphLayoutBox::GetText() const
2984
return GetTextForRange(GetOwnRange());
2987
/// Get the paragraph by number
2988
wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphAtLine(long paragraphNumber) const
2990
if ((size_t) paragraphNumber >= GetChildCount())
2993
return (wxRichTextParagraph*) GetChild((size_t) paragraphNumber);
2996
/// Get the length of the paragraph
2997
int wxRichTextParagraphLayoutBox::GetParagraphLength(long paragraphNumber) const
2999
wxRichTextParagraph* para = GetParagraphAtLine(paragraphNumber);
3001
return para->GetRange().GetLength() - 1; // don't include newline
3006
/// Get the text of the paragraph
3007
wxString wxRichTextParagraphLayoutBox::GetParagraphText(long paragraphNumber) const
3009
wxRichTextParagraph* para = GetParagraphAtLine(paragraphNumber);
3011
return para->GetTextForRange(para->GetRange());
3013
return wxEmptyString;
3016
/// Convert zero-based line column and paragraph number to a position.
3017
long wxRichTextParagraphLayoutBox::XYToPosition(long x, long y) const
3019
wxRichTextParagraph* para = GetParagraphAtLine(y);
3022
return para->GetRange().GetStart() + x;
3028
/// Convert zero-based position to line column and paragraph number
3029
bool wxRichTextParagraphLayoutBox::PositionToXY(long pos, long* x, long* y) const
3031
wxRichTextParagraph* para = GetParagraphAtPosition(pos);
3035
wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3038
wxRichTextObject* child = node->GetData();
3042
node = node->GetNext();
3046
*x = pos - para->GetRange().GetStart();
3054
/// Get the leaf object in a paragraph at this position.
3055
/// Given a line number, get the corresponding wxRichTextLine object.
3056
wxRichTextObject* wxRichTextParagraphLayoutBox::GetLeafObjectAtPosition(long position) const
3058
wxRichTextParagraph* para = GetParagraphAtPosition(position);
3061
wxRichTextObjectList::compatibility_iterator node = para->GetChildren().GetFirst();
3065
wxRichTextObject* child = node->GetData();
3066
if (child->GetRange().Contains(position))
3069
node = node->GetNext();
3071
if (position == para->GetRange().GetEnd() && para->GetChildCount() > 0)
3072
return para->GetChildren().GetLast()->GetData();
3077
/// Set character or paragraph text attributes: apply character styles only to immediate text nodes
3078
bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange& range, const wxRichTextAttr& style, int flags)
3080
bool characterStyle = false;
3081
bool paragraphStyle = false;
3083
if (style.IsCharacterStyle())
3084
characterStyle = true;
3085
if (style.IsParagraphStyle())
3086
paragraphStyle = true;
3088
wxRichTextBuffer* buffer = GetBuffer();
3090
bool withUndo = ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
3091
bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
3092
bool parasOnly = ((flags & wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY) != 0);
3093
bool charactersOnly = ((flags & wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY) != 0);
3094
bool resetExistingStyle = ((flags & wxRICHTEXT_SETSTYLE_RESET) != 0);
3095
bool removeStyle = ((flags & wxRICHTEXT_SETSTYLE_REMOVE) != 0);
3097
// Apply paragraph style first, if any
3098
wxRichTextAttr wholeStyle(style);
3100
if (!removeStyle && wholeStyle.HasParagraphStyleName() && buffer->GetStyleSheet())
3102
wxRichTextParagraphStyleDefinition* def = buffer->GetStyleSheet()->FindParagraphStyle(wholeStyle.GetParagraphStyleName());
3104
wxRichTextApplyStyle(wholeStyle, def->GetStyleMergedWithBase(buffer->GetStyleSheet()));
3107
// Limit the attributes to be set to the content to only character attributes.
3108
wxRichTextAttr characterAttributes(wholeStyle);
3109
characterAttributes.SetFlags(characterAttributes.GetFlags() & (wxTEXT_ATTR_CHARACTER));
3111
if (!removeStyle && characterAttributes.HasCharacterStyleName() && buffer->GetStyleSheet())
3113
wxRichTextCharacterStyleDefinition* def = buffer->GetStyleSheet()->FindCharacterStyle(characterAttributes.GetCharacterStyleName());
3115
wxRichTextApplyStyle(characterAttributes, def->GetStyleMergedWithBase(buffer->GetStyleSheet()));
3118
// If we are associated with a control, make undoable; otherwise, apply immediately
3121
bool haveControl = (buffer->GetRichTextCtrl() != NULL);
3123
wxRichTextAction* action = NULL;
3125
if (haveControl && withUndo)
3127
action = new wxRichTextAction(NULL, _("Change Style"), wxRICHTEXT_CHANGE_STYLE, buffer, this, buffer->GetRichTextCtrl());
3128
action->SetRange(range);
3129
action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
3132
wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3135
wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
3136
// wxASSERT (para != NULL);
3138
if (para && para->GetChildCount() > 0)
3140
// Stop searching if we're beyond the range of interest
3141
if (para->GetRange().GetStart() > range.GetEnd())
3144
if (!para->GetRange().IsOutside(range))
3146
// We'll be using a copy of the paragraph to make style changes,
3147
// not updating the buffer directly.
3148
wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
3150
if (haveControl && withUndo)
3152
newPara = new wxRichTextParagraph(*para);
3153
action->GetNewParagraphs().AppendChild(newPara);
3155
// Also store the old ones for Undo
3156
action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
3161
// If we're specifying paragraphs only, then we really mean character formatting
3162
// to be included in the paragraph style
3163
if ((paragraphStyle || parasOnly) && !charactersOnly)
3167
// Removes the given style from the paragraph
3168
wxRichTextRemoveStyle(newPara->GetAttributes(), style);
3170
else if (resetExistingStyle)
3171
newPara->GetAttributes() = wholeStyle;
3176
// Only apply attributes that will make a difference to the combined
3177
// style as seen on the display
3178
wxRichTextAttr combinedAttr(para->GetCombinedAttributes(true));
3179
wxRichTextApplyStyle(newPara->GetAttributes(), wholeStyle, & combinedAttr);
3182
wxRichTextApplyStyle(newPara->GetAttributes(), wholeStyle);
3186
// When applying paragraph styles dynamically, don't change the text objects' attributes
3187
// since they will computed as needed. Only apply the character styling if it's _only_
3188
// character styling. This policy is subject to change and might be put under user control.
3190
// Hm. we might well be applying a mix of paragraph and character styles, in which
3191
// case we _do_ want to apply character styles regardless of what para styles are set.
3192
// But if we're applying a paragraph style, which has some character attributes, but
3193
// we only want the paragraphs to hold this character style, then we _don't_ want to
3194
// apply the character style. So we need to be able to choose.
3196
if (!parasOnly && (characterStyle|charactersOnly) && range.GetStart() != newPara->GetRange().GetEnd())
3198
wxRichTextRange childRange(range);
3199
childRange.LimitTo(newPara->GetRange());
3201
// Find the starting position and if necessary split it so
3202
// we can start applying a different style.
3203
// TODO: check that the style actually changes or is different
3204
// from style outside of range
3205
wxRichTextObject* firstObject wxDUMMY_INITIALIZE(NULL);
3206
wxRichTextObject* lastObject wxDUMMY_INITIALIZE(NULL);
3208
if (childRange.GetStart() == newPara->GetRange().GetStart())
3209
firstObject = newPara->GetChildren().GetFirst()->GetData();
3211
firstObject = newPara->SplitAt(range.GetStart());
3213
// Increment by 1 because we're apply the style one _after_ the split point
3214
long splitPoint = childRange.GetEnd();
3215
if (splitPoint != newPara->GetRange().GetEnd())
3219
if (splitPoint == newPara->GetRange().GetEnd())
3220
lastObject = newPara->GetChildren().GetLast()->GetData();
3222
// lastObject is set as a side-effect of splitting. It's
3223
// returned as the object before the new object.
3224
(void) newPara->SplitAt(splitPoint, & lastObject);
3226
wxASSERT(firstObject != NULL);
3227
wxASSERT(lastObject != NULL);
3229
if (!firstObject || !lastObject)
3232
wxRichTextObjectList::compatibility_iterator firstNode = newPara->GetChildren().Find(firstObject);
3233
wxRichTextObjectList::compatibility_iterator lastNode = newPara->GetChildren().Find(lastObject);
3235
wxASSERT(firstNode);
3238
wxRichTextObjectList::compatibility_iterator node2 = firstNode;
3242
wxRichTextObject* child = node2->GetData();
3246
// Removes the given style from the paragraph
3247
wxRichTextRemoveStyle(child->GetAttributes(), style);
3249
else if (resetExistingStyle)
3250
child->GetAttributes() = characterAttributes;
3255
// Only apply attributes that will make a difference to the combined
3256
// style as seen on the display
3257
wxRichTextAttr combinedAttr(newPara->GetCombinedAttributes(child->GetAttributes(), true));
3258
wxRichTextApplyStyle(child->GetAttributes(), characterAttributes, & combinedAttr);
3261
wxRichTextApplyStyle(child->GetAttributes(), characterAttributes);
3264
if (node2 == lastNode)
3267
node2 = node2->GetNext();
3273
node = node->GetNext();
3276
// Do action, or delay it until end of batch.
3277
if (haveControl && withUndo)
3278
buffer->SubmitAction(action);
3283
// Just change the attributes for this single object.
3284
void wxRichTextParagraphLayoutBox::SetStyle(wxRichTextObject* obj, const wxRichTextAttr& textAttr, int flags)
3286
wxRichTextBuffer* buffer = GetBuffer();
3287
bool withUndo = flags & wxRICHTEXT_SETSTYLE_WITH_UNDO;
3288
bool resetExistingStyle = ((flags & wxRICHTEXT_SETSTYLE_RESET) != 0);
3289
bool haveControl = (buffer->GetRichTextCtrl() != NULL);
3291
wxRichTextAction *action = NULL;
3292
wxRichTextAttr newAttr = obj->GetAttributes();
3293
if (resetExistingStyle)
3296
newAttr.Apply(textAttr);
3298
if (haveControl && withUndo)
3300
action = new wxRichTextAction(NULL, _("Change Object Style"), wxRICHTEXT_CHANGE_ATTRIBUTES, buffer, obj->GetContainer(), buffer->GetRichTextCtrl());
3301
action->SetRange(obj->GetRange().FromInternal());
3302
action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
3303
action->MakeObject(obj);
3305
action->GetAttributes() = newAttr;
3308
obj->GetAttributes() = newAttr;
3310
if (haveControl && withUndo)
3311
buffer->SubmitAction(action);
3314
/// Get the text attributes for this position.
3315
bool wxRichTextParagraphLayoutBox::GetStyle(long position, wxRichTextAttr& style)
3317
return DoGetStyle(position, style, true);
3320
bool wxRichTextParagraphLayoutBox::GetUncombinedStyle(long position, wxRichTextAttr& style)
3322
return DoGetStyle(position, style, false);
3325
/// Implementation helper for GetStyle. If combineStyles is true, combine base, paragraph and
3326
/// context attributes.
3327
bool wxRichTextParagraphLayoutBox::DoGetStyle(long position, wxRichTextAttr& style, bool combineStyles)
3329
wxRichTextObject* obj wxDUMMY_INITIALIZE(NULL);
3331
if (style.IsParagraphStyle())
3333
obj = GetParagraphAtPosition(position);
3338
// Start with the base style
3339
style = GetAttributes();
3340
style.GetTextBoxAttr().Reset();
3342
// Apply the paragraph style
3343
wxRichTextApplyStyle(style, obj->GetAttributes());
3346
style = obj->GetAttributes();
3353
obj = GetLeafObjectAtPosition(position);
3358
wxRichTextParagraph* para = wxDynamicCast(obj->GetParent(), wxRichTextParagraph);
3359
style = para ? para->GetCombinedAttributes(obj->GetAttributes()) : obj->GetAttributes();
3362
style = obj->GetAttributes();
3370
static bool wxHasStyle(long flags, long style)
3372
return (flags & style) != 0;
3375
/// Combines 'style' with 'currentStyle' for the purpose of summarising the attributes of a range of
3377
bool wxRichTextParagraphLayoutBox::CollectStyle(wxRichTextAttr& currentStyle, const wxRichTextAttr& style, wxRichTextAttr& clashingAttr, wxRichTextAttr& absentAttr)
3379
currentStyle.CollectCommonAttributes(style, clashingAttr, absentAttr);
3384
/// Get the combined style for a range - if any attribute is different within the range,
3385
/// that attribute is not present within the flags.
3386
/// *** Note that this is not recursive, and so assumes that content inside a paragraph is not itself
3388
bool wxRichTextParagraphLayoutBox::GetStyleForRange(const wxRichTextRange& range, wxRichTextAttr& style)
3390
style = wxRichTextAttr();
3392
wxRichTextAttr clashingAttr;
3393
wxRichTextAttr absentAttrPara, absentAttrChar;
3395
wxRichTextObjectList::compatibility_iterator node = GetChildren().GetFirst();
3398
wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
3399
if (para && !(para->GetRange().GetStart() > range.GetEnd() || para->GetRange().GetEnd() < range.GetStart()))
3401
if (para->GetChildren().GetCount() == 0)
3403
wxRichTextAttr paraStyle = para->GetCombinedAttributes(true /* use box attributes */);
3405
CollectStyle(style, paraStyle, clashingAttr, absentAttrPara);
3409
wxRichTextRange paraRange(para->GetRange());
3410
paraRange.LimitTo(range);
3412
// First collect paragraph attributes only
3413
wxRichTextAttr paraStyle = para->GetCombinedAttributes();
3414
paraStyle.SetFlags(paraStyle.GetFlags() & wxTEXT_ATTR_PARAGRAPH);
3415
CollectStyle(style, paraStyle, clashingAttr, absentAttrPara);
3417
wxRichTextObjectList::compatibility_iterator childNode = para->GetChildren().GetFirst();
3421
wxRichTextObject* child = childNode->GetData();
3422
if (!(child->GetRange().GetStart() > range.GetEnd() || child->GetRange().GetEnd() < range.GetStart()))
3424
wxRichTextAttr childStyle = para->GetCombinedAttributes(child->GetAttributes(), true /* include box attributes */);
3426
// Now collect character attributes only
3427
childStyle.SetFlags(childStyle.GetFlags() & wxTEXT_ATTR_CHARACTER);
3429
CollectStyle(style, childStyle, clashingAttr, absentAttrChar);
3432
childNode = childNode->GetNext();
3436
node = node->GetNext();
3441
/// Set default style
3442
bool wxRichTextParagraphLayoutBox::SetDefaultStyle(const wxRichTextAttr& style)
3444
m_defaultAttributes = style;
3448
/// Test if this whole range has character attributes of the specified kind. If any
3449
/// of the attributes are different within the range, the test fails. You
3450
/// can use this to implement, for example, bold button updating. style must have
3451
/// flags indicating which attributes are of interest.
3452
bool wxRichTextParagraphLayoutBox::HasCharacterAttributes(const wxRichTextRange& range, const wxRichTextAttr& style) const
3455
int matchingCount = 0;
3457
wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3460
wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
3461
// wxASSERT (para != NULL);
3465
// Stop searching if we're beyond the range of interest
3466
if (para->GetRange().GetStart() > range.GetEnd())
3467
return foundCount == matchingCount && foundCount != 0;
3469
if (!para->GetRange().IsOutside(range))
3471
wxRichTextObjectList::compatibility_iterator node2 = para->GetChildren().GetFirst();
3475
wxRichTextObject* child = node2->GetData();
3476
// Allow for empty string if no buffer
3477
wxRichTextRange childRange = child->GetRange();
3478
if (childRange.GetLength() == 0 && GetRange().GetLength() == 1)
3479
childRange.SetEnd(childRange.GetEnd()+1);
3481
if (!childRange.IsOutside(range) && wxDynamicCast(child, wxRichTextPlainText))
3484
wxRichTextAttr textAttr = para->GetCombinedAttributes(child->GetAttributes());
3486
if (textAttr.EqPartial(style, false /* strong test - attributes must be valid in both objects */))
3490
node2 = node2->GetNext();
3495
node = node->GetNext();
3498
return foundCount == matchingCount && foundCount != 0;
3501
/// Test if this whole range has paragraph attributes of the specified kind. If any
3502
/// of the attributes are different within the range, the test fails. You
3503
/// can use this to implement, for example, centering button updating. style must have
3504
/// flags indicating which attributes are of interest.
3505
bool wxRichTextParagraphLayoutBox::HasParagraphAttributes(const wxRichTextRange& range, const wxRichTextAttr& style) const
3508
int matchingCount = 0;
3510
wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3513
wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
3514
// wxASSERT (para != NULL);
3518
// Stop searching if we're beyond the range of interest
3519
if (para->GetRange().GetStart() > range.GetEnd())
3520
return foundCount == matchingCount && foundCount != 0;
3522
if (!para->GetRange().IsOutside(range))
3524
wxRichTextAttr textAttr = GetAttributes();
3525
// Apply the paragraph style
3526
wxRichTextApplyStyle(textAttr, para->GetAttributes());
3529
if (textAttr.EqPartial(style, false /* strong test */))
3534
node = node->GetNext();
3536
return foundCount == matchingCount && foundCount != 0;
3539
void wxRichTextParagraphLayoutBox::PrepareContent(wxRichTextParagraphLayoutBox& container)
3541
wxRichTextBuffer* buffer = GetBuffer();
3542
if (buffer && buffer->GetRichTextCtrl())
3543
buffer->GetRichTextCtrl()->PrepareContent(container);
3546
/// Set character or paragraph properties
3547
bool wxRichTextParagraphLayoutBox::SetProperties(const wxRichTextRange& range, const wxRichTextProperties& properties, int flags)
3549
wxRichTextBuffer* buffer = GetBuffer();
3551
bool withUndo = ((flags & wxRICHTEXT_SETPROPERTIES_WITH_UNDO) != 0);
3552
bool parasOnly = ((flags & wxRICHTEXT_SETPROPERTIES_PARAGRAPHS_ONLY) != 0);
3553
bool charactersOnly = ((flags & wxRICHTEXT_SETPROPERTIES_CHARACTERS_ONLY) != 0);
3554
bool resetExistingProperties = ((flags & wxRICHTEXT_SETPROPERTIES_RESET) != 0);
3555
bool removeProperties = ((flags & wxRICHTEXT_SETPROPERTIES_REMOVE) != 0);
3557
// If we are associated with a control, make undoable; otherwise, apply immediately
3560
bool haveControl = (buffer->GetRichTextCtrl() != NULL);
3562
wxRichTextAction* action = NULL;
3564
if (haveControl && withUndo)
3566
action = new wxRichTextAction(NULL, _("Change Properties"), wxRICHTEXT_CHANGE_PROPERTIES, buffer, this, buffer->GetRichTextCtrl());
3567
action->SetRange(range);
3568
action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
3571
wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3574
wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
3575
// wxASSERT (para != NULL);
3577
if (para && para->GetChildCount() > 0)
3579
// Stop searching if we're beyond the range of interest
3580
if (para->GetRange().GetStart() > range.GetEnd())
3583
if (!para->GetRange().IsOutside(range))
3585
// We'll be using a copy of the paragraph to make style changes,
3586
// not updating the buffer directly.
3587
wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
3589
if (haveControl && withUndo)
3591
newPara = new wxRichTextParagraph(*para);
3592
action->GetNewParagraphs().AppendChild(newPara);
3594
// Also store the old ones for Undo
3595
action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
3602
if (removeProperties)
3604
// Removes the given style from the paragraph
3606
newPara->GetProperties().RemoveProperties(properties);
3608
else if (resetExistingProperties)
3609
newPara->GetProperties() = properties;
3611
newPara->GetProperties().MergeProperties(properties);
3614
// When applying paragraph styles dynamically, don't change the text objects' attributes
3615
// since they will computed as needed. Only apply the character styling if it's _only_
3616
// character styling. This policy is subject to change and might be put under user control.
3618
// Hm. we might well be applying a mix of paragraph and character styles, in which
3619
// case we _do_ want to apply character styles regardless of what para styles are set.
3620
// But if we're applying a paragraph style, which has some character attributes, but
3621
// we only want the paragraphs to hold this character style, then we _don't_ want to
3622
// apply the character style. So we need to be able to choose.
3624
if (!parasOnly && charactersOnly && range.GetStart() != newPara->GetRange().GetEnd())
3626
wxRichTextRange childRange(range);
3627
childRange.LimitTo(newPara->GetRange());
3629
// Find the starting position and if necessary split it so
3630
// we can start applying different properties.
3631
// TODO: check that the properties actually change or are different
3632
// from properties outside of range
3633
wxRichTextObject* firstObject wxDUMMY_INITIALIZE(NULL);
3634
wxRichTextObject* lastObject wxDUMMY_INITIALIZE(NULL);
3636
if (childRange.GetStart() == newPara->GetRange().GetStart())
3637
firstObject = newPara->GetChildren().GetFirst()->GetData();
3639
firstObject = newPara->SplitAt(range.GetStart());
3641
// Increment by 1 because we're apply the style one _after_ the split point
3642
long splitPoint = childRange.GetEnd();
3643
if (splitPoint != newPara->GetRange().GetEnd())
3647
if (splitPoint == newPara->GetRange().GetEnd())
3648
lastObject = newPara->GetChildren().GetLast()->GetData();
3650
// lastObject is set as a side-effect of splitting. It's
3651
// returned as the object before the new object.
3652
(void) newPara->SplitAt(splitPoint, & lastObject);
3654
wxASSERT(firstObject != NULL);
3655
wxASSERT(lastObject != NULL);
3657
if (!firstObject || !lastObject)
3660
wxRichTextObjectList::compatibility_iterator firstNode = newPara->GetChildren().Find(firstObject);
3661
wxRichTextObjectList::compatibility_iterator lastNode = newPara->GetChildren().Find(lastObject);
3663
wxASSERT(firstNode);
3666
wxRichTextObjectList::compatibility_iterator node2 = firstNode;
3670
wxRichTextObject* child = node2->GetData();
3672
if (removeProperties)
3674
// Removes the given properties from the paragraph
3675
child->GetProperties().RemoveProperties(properties);
3677
else if (resetExistingProperties)
3678
child->GetProperties() = properties;
3681
child->GetProperties().MergeProperties(properties);
3684
if (node2 == lastNode)
3687
node2 = node2->GetNext();
3693
node = node->GetNext();
3696
// Do action, or delay it until end of batch.
3697
if (haveControl && withUndo)
3698
buffer->SubmitAction(action);
3703
void wxRichTextParagraphLayoutBox::Reset()
3707
wxRichTextBuffer* buffer = GetBuffer();
3708
if (buffer && buffer->GetRichTextCtrl())
3710
wxRichTextEvent event(wxEVT_COMMAND_RICHTEXT_BUFFER_RESET, buffer->GetRichTextCtrl()->GetId());
3711
event.SetEventObject(buffer->GetRichTextCtrl());
3712
event.SetContainer(this);
3714
buffer->SendEvent(event, true);
3717
AddParagraph(wxEmptyString);
3719
PrepareContent(*this);
3721
InvalidateHierarchy(wxRICHTEXT_ALL);
3724
/// Invalidate the buffer. With no argument, invalidates whole buffer.
3725
void wxRichTextParagraphLayoutBox::Invalidate(const wxRichTextRange& invalidRange)
3727
wxRichTextCompositeObject::Invalidate(invalidRange);
3729
DoInvalidate(invalidRange);
3732
// Do the (in)validation for this object only
3733
void wxRichTextParagraphLayoutBox::DoInvalidate(const wxRichTextRange& invalidRange)
3735
if (invalidRange == wxRICHTEXT_ALL)
3737
m_invalidRange = wxRICHTEXT_ALL;
3739
// Already invalidating everything
3740
else if (m_invalidRange == wxRICHTEXT_ALL)
3745
if ((invalidRange.GetStart() < m_invalidRange.GetStart()) || m_invalidRange.GetStart() == -1)
3746
m_invalidRange.SetStart(invalidRange.GetStart());
3747
if (invalidRange.GetEnd() > m_invalidRange.GetEnd())
3748
m_invalidRange.SetEnd(invalidRange.GetEnd());
3752
// Do the (in)validation both up and down the hierarchy
3753
void wxRichTextParagraphLayoutBox::InvalidateHierarchy(const wxRichTextRange& invalidRange)
3755
Invalidate(invalidRange);
3757
if (invalidRange != wxRICHTEXT_NONE)
3759
// Now go up the hierarchy
3760
wxRichTextObject* thisObj = this;
3761
wxRichTextObject* p = GetParent();
3764
wxRichTextParagraphLayoutBox* l = wxDynamicCast(p, wxRichTextParagraphLayoutBox);
3766
l->DoInvalidate(thisObj->GetRange());
3774
/// Get invalid range, rounding to entire paragraphs if argument is true.
3775
wxRichTextRange wxRichTextParagraphLayoutBox::GetInvalidRange(bool wholeParagraphs) const
3777
if (m_invalidRange == wxRICHTEXT_ALL || m_invalidRange == wxRICHTEXT_NONE)
3778
return m_invalidRange;
3780
wxRichTextRange range = m_invalidRange;
3782
if (wholeParagraphs)
3784
wxRichTextParagraph* para1 = GetParagraphAtPosition(range.GetStart());
3786
range.SetStart(para1->GetRange().GetStart());
3787
// floating layout make all child should be relayout
3788
range.SetEnd(GetOwnRange().GetEnd());
3793
/// Apply the style sheet to the buffer, for example if the styles have changed.
3794
bool wxRichTextParagraphLayoutBox::ApplyStyleSheet(wxRichTextStyleSheet* styleSheet)
3796
wxASSERT(styleSheet != NULL);
3802
wxRichTextAttr attr(GetBasicStyle());
3803
if (GetBasicStyle().HasParagraphStyleName())
3805
wxRichTextParagraphStyleDefinition* paraDef = styleSheet->FindParagraphStyle(GetBasicStyle().GetParagraphStyleName());
3808
attr.Apply(paraDef->GetStyleMergedWithBase(styleSheet));
3809
SetBasicStyle(attr);
3814
if (GetBasicStyle().HasCharacterStyleName())
3816
wxRichTextCharacterStyleDefinition* charDef = styleSheet->FindCharacterStyle(GetBasicStyle().GetCharacterStyleName());
3819
attr.Apply(charDef->GetStyleMergedWithBase(styleSheet));
3820
SetBasicStyle(attr);
3825
wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3828
wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
3829
// wxASSERT (para != NULL);
3833
// Combine paragraph and list styles. If there is a list style in the original attributes,
3834
// the current indentation overrides anything else and is used to find the item indentation.
3835
// Also, for applying paragraph styles, consider having 2 modes: (1) we merge with what we have,
3836
// thereby taking into account all user changes, (2) reset the style completely (except for indentation/list
3837
// exception as above).
3838
// Problem: when changing from one list style to another, there's a danger that the level info will get lost.
3839
// So when changing a list style interactively, could retrieve level based on current style, then
3840
// set appropriate indent and apply new style.
3844
if (para->GetAttributes().HasOutlineLevel())
3845
outline = para->GetAttributes().GetOutlineLevel();
3846
if (para->GetAttributes().HasBulletNumber())
3847
num = para->GetAttributes().GetBulletNumber();
3849
if (!para->GetAttributes().GetParagraphStyleName().IsEmpty() && !para->GetAttributes().GetListStyleName().IsEmpty())
3851
int currentIndent = para->GetAttributes().GetLeftIndent();
3853
wxRichTextParagraphStyleDefinition* paraDef = styleSheet->FindParagraphStyle(para->GetAttributes().GetParagraphStyleName());
3854
wxRichTextListStyleDefinition* listDef = styleSheet->FindListStyle(para->GetAttributes().GetListStyleName());
3855
if (paraDef && !listDef)
3857
para->GetAttributes() = paraDef->GetStyleMergedWithBase(styleSheet);
3860
else if (listDef && !paraDef)
3862
// Set overall style defined for the list style definition
3863
para->GetAttributes() = listDef->GetStyleMergedWithBase(styleSheet);
3865
// Apply the style for this level
3866
wxRichTextApplyStyle(para->GetAttributes(), * listDef->GetLevelAttributes(listDef->FindLevelForIndent(currentIndent)));
3869
else if (listDef && paraDef)
3871
// Combines overall list style, style for level, and paragraph style
3872
para->GetAttributes() = listDef->CombineWithParagraphStyle(currentIndent, paraDef->GetStyleMergedWithBase(styleSheet));
3876
else if (para->GetAttributes().GetParagraphStyleName().IsEmpty() && !para->GetAttributes().GetListStyleName().IsEmpty())
3878
int currentIndent = para->GetAttributes().GetLeftIndent();
3880
wxRichTextListStyleDefinition* listDef = styleSheet->FindListStyle(para->GetAttributes().GetListStyleName());
3882
// Overall list definition style
3883
para->GetAttributes() = listDef->GetStyleMergedWithBase(styleSheet);
3885
// Style for this level
3886
wxRichTextApplyStyle(para->GetAttributes(), * listDef->GetLevelAttributes(listDef->FindLevelForIndent(currentIndent)));
3890
else if (!para->GetAttributes().GetParagraphStyleName().IsEmpty() && para->GetAttributes().GetListStyleName().IsEmpty())
3892
wxRichTextParagraphStyleDefinition* def = styleSheet->FindParagraphStyle(para->GetAttributes().GetParagraphStyleName());
3895
para->GetAttributes() = def->GetStyleMergedWithBase(styleSheet);
3901
para->GetAttributes().SetOutlineLevel(outline);
3903
para->GetAttributes().SetBulletNumber(num);
3906
node = node->GetNext();
3908
return foundCount != 0;
3912
bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
3914
wxRichTextBuffer* buffer = GetBuffer();
3915
wxRichTextStyleSheet* styleSheet = buffer->GetStyleSheet();
3917
bool withUndo = ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
3918
// bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
3919
bool specifyLevel = ((flags & wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL) != 0);
3920
bool renumber = ((flags & wxRICHTEXT_SETSTYLE_RENUMBER) != 0);
3922
// Current number, if numbering
3925
wxASSERT (!specifyLevel || (specifyLevel && (specifiedLevel >= 0)));
3927
// If we are associated with a control, make undoable; otherwise, apply immediately
3930
bool haveControl = (buffer->GetRichTextCtrl() != NULL);
3932
wxRichTextAction* action = NULL;
3934
if (haveControl && withUndo)
3936
action = new wxRichTextAction(NULL, _("Change List Style"), wxRICHTEXT_CHANGE_STYLE, buffer, this, buffer->GetRichTextCtrl());
3937
action->SetRange(range);
3938
action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
3941
wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3944
wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
3945
// wxASSERT (para != NULL);
3947
if (para && para->GetChildCount() > 0)
3949
// Stop searching if we're beyond the range of interest
3950
if (para->GetRange().GetStart() > range.GetEnd())
3953
if (!para->GetRange().IsOutside(range))
3955
// We'll be using a copy of the paragraph to make style changes,
3956
// not updating the buffer directly.
3957
wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
3959
if (haveControl && withUndo)
3961
newPara = new wxRichTextParagraph(*para);
3962
action->GetNewParagraphs().AppendChild(newPara);
3964
// Also store the old ones for Undo
3965
action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
3972
int thisIndent = newPara->GetAttributes().GetLeftIndent();
3973
int thisLevel = specifyLevel ? specifiedLevel : def->FindLevelForIndent(thisIndent);
3975
// How is numbering going to work?
3976
// If we are renumbering, or numbering for the first time, we need to keep
3977
// track of the number for each level. But we might be simply applying a different
3979
// In Word, applying a style to several paragraphs, even if at different levels,
3980
// reverts the level back to the same one. So we could do the same here.
3981
// Renumbering will need to be done when we promote/demote a paragraph.
3983
// Apply the overall list style, and item style for this level
3984
wxRichTextAttr listStyle(def->GetCombinedStyleForLevel(thisLevel, styleSheet));
3985
wxRichTextApplyStyle(newPara->GetAttributes(), listStyle);
3987
// Now we need to do numbering
3990
newPara->GetAttributes().SetBulletNumber(n);
3995
else if (!newPara->GetAttributes().GetListStyleName().IsEmpty())
3997
// if def is NULL, remove list style, applying any associated paragraph style
3998
// to restore the attributes
4000
newPara->GetAttributes().SetListStyleName(wxEmptyString);
4001
newPara->GetAttributes().SetLeftIndent(0, 0);
4002
newPara->GetAttributes().SetBulletText(wxEmptyString);
4004
// Eliminate the main list-related attributes
4005
newPara->GetAttributes().SetFlags(newPara->GetAttributes().GetFlags() & ~wxTEXT_ATTR_LEFT_INDENT & ~wxTEXT_ATTR_BULLET_STYLE & ~wxTEXT_ATTR_BULLET_NUMBER & ~wxTEXT_ATTR_BULLET_TEXT & wxTEXT_ATTR_LIST_STYLE_NAME);
4007
if (styleSheet && !newPara->GetAttributes().GetParagraphStyleName().IsEmpty())
4009
wxRichTextParagraphStyleDefinition* def = styleSheet->FindParagraphStyle(newPara->GetAttributes().GetParagraphStyleName());
4012
newPara->GetAttributes() = def->GetStyleMergedWithBase(styleSheet);
4019
node = node->GetNext();
4022
// Do action, or delay it until end of batch.
4023
if (haveControl && withUndo)
4024
buffer->SubmitAction(action);
4029
bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange& range, const wxString& defName, int flags, int startFrom, int specifiedLevel)
4031
wxRichTextBuffer* buffer = GetBuffer();
4032
if (buffer && buffer->GetStyleSheet())
4034
wxRichTextListStyleDefinition* def = buffer->GetStyleSheet()->FindListStyle(defName);
4036
return SetListStyle(range, def, flags, startFrom, specifiedLevel);
4041
/// Clear list for given range
4042
bool wxRichTextParagraphLayoutBox::ClearListStyle(const wxRichTextRange& range, int flags)
4044
return SetListStyle(range, NULL, flags);
4047
/// Number/renumber any list elements in the given range
4048
bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
4050
return DoNumberList(range, range, 0, def, flags, startFrom, specifiedLevel);
4053
/// Number/renumber any list elements in the given range. Also do promotion or demotion of items, if specified
4054
bool wxRichTextParagraphLayoutBox::DoNumberList(const wxRichTextRange& range, const wxRichTextRange& promotionRange, int promoteBy,
4055
wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
4057
wxRichTextBuffer* buffer = GetBuffer();
4058
wxRichTextStyleSheet* styleSheet = buffer->GetStyleSheet();
4060
bool withUndo = ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
4061
// bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
4063
bool specifyLevel = ((flags & wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL) != 0);
4066
bool renumber = ((flags & wxRICHTEXT_SETSTYLE_RENUMBER) != 0);
4068
// Max number of levels
4069
const int maxLevels = 10;
4071
// The level we're looking at now
4072
int currentLevel = -1;
4074
// The item number for each level
4075
int levels[maxLevels];
4078
// Reset all numbering
4079
for (i = 0; i < maxLevels; i++)
4081
if (startFrom != -1)
4082
levels[i] = startFrom-1;
4083
else if (renumber) // start again
4086
levels[i] = -1; // start from the number we found, if any
4090
wxASSERT(!specifyLevel || (specifyLevel && (specifiedLevel >= 0)));
4093
// If we are associated with a control, make undoable; otherwise, apply immediately
4096
bool haveControl = (buffer->GetRichTextCtrl() != NULL);
4098
wxRichTextAction* action = NULL;
4100
if (haveControl && withUndo)
4102
action = new wxRichTextAction(NULL, _("Renumber List"), wxRICHTEXT_CHANGE_STYLE, buffer, this, buffer->GetRichTextCtrl());
4103
action->SetRange(range);
4104
action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
4107
wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
4110
wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
4111
// wxASSERT (para != NULL);
4113
if (para && para->GetChildCount() > 0)
4115
// Stop searching if we're beyond the range of interest
4116
if (para->GetRange().GetStart() > range.GetEnd())
4119
if (!para->GetRange().IsOutside(range))
4121
// We'll be using a copy of the paragraph to make style changes,
4122
// not updating the buffer directly.
4123
wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
4125
if (haveControl && withUndo)
4127
newPara = new wxRichTextParagraph(*para);
4128
action->GetNewParagraphs().AppendChild(newPara);
4130
// Also store the old ones for Undo
4131
action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
4136
wxRichTextListStyleDefinition* defToUse = def;
4139
if (styleSheet && !newPara->GetAttributes().GetListStyleName().IsEmpty())
4140
defToUse = styleSheet->FindListStyle(newPara->GetAttributes().GetListStyleName());
4145
int thisIndent = newPara->GetAttributes().GetLeftIndent();
4146
int thisLevel = defToUse->FindLevelForIndent(thisIndent);
4148
// If we've specified a level to apply to all, change the level.
4149
if (specifiedLevel != -1)
4150
thisLevel = specifiedLevel;
4152
// Do promotion if specified
4153
if ((promoteBy != 0) && !para->GetRange().IsOutside(promotionRange))
4155
thisLevel = thisLevel - promoteBy;
4162
// Apply the overall list style, and item style for this level
4163
wxRichTextAttr listStyle(defToUse->GetCombinedStyleForLevel(thisLevel, styleSheet));
4164
wxRichTextApplyStyle(newPara->GetAttributes(), listStyle);
4166
// OK, we've (re)applied the style, now let's get the numbering right.
4168
if (currentLevel == -1)
4169
currentLevel = thisLevel;
4171
// Same level as before, do nothing except increment level's number afterwards
4172
if (currentLevel == thisLevel)
4175
// A deeper level: start renumbering all levels after current level
4176
else if (thisLevel > currentLevel)
4178
for (i = currentLevel+1; i <= thisLevel; i++)
4182
currentLevel = thisLevel;
4184
else if (thisLevel < currentLevel)
4186
currentLevel = thisLevel;
4189
// Use the current numbering if -1 and we have a bullet number already
4190
if (levels[currentLevel] == -1)
4192
if (newPara->GetAttributes().HasBulletNumber())
4193
levels[currentLevel] = newPara->GetAttributes().GetBulletNumber();
4195
levels[currentLevel] = 1;
4199
levels[currentLevel] ++;
4202
newPara->GetAttributes().SetBulletNumber(levels[currentLevel]);
4204
// Create the bullet text if an outline list
4205
if (listStyle.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE)
4208
for (i = 0; i <= currentLevel; i++)
4210
if (!text.IsEmpty())
4212
text += wxString::Format(wxT("%d"), levels[i]);
4214
newPara->GetAttributes().SetBulletText(text);
4220
node = node->GetNext();
4223
// Do action, or delay it until end of batch.
4224
if (haveControl && withUndo)
4225
buffer->SubmitAction(action);
4230
bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange& range, const wxString& defName, int flags, int startFrom, int specifiedLevel)
4232
wxRichTextBuffer* buffer = GetBuffer();
4233
if (buffer->GetStyleSheet())
4235
wxRichTextListStyleDefinition* def = NULL;
4236
if (!defName.IsEmpty())
4237
def = buffer->GetStyleSheet()->FindListStyle(defName);
4238
return NumberList(range, def, flags, startFrom, specifiedLevel);
4243
/// Promote the list items within the given range. promoteBy can be a positive or negative number, e.g. 1 or -1
4244
bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy, const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int specifiedLevel)
4247
// One strategy is to first work out the range within which renumbering must occur. Then could pass these two ranges
4248
// to NumberList with a flag indicating promotion is required within one of the ranges.
4249
// Find first and last paragraphs in range. Then for first, calculate new indentation and look back until we find
4250
// a paragraph that either has no list style, or has one that is different or whose indentation is less.
4251
// We start renumbering from the para after that different para we found. We specify that the numbering of that
4252
// list position will start from 1.
4253
// Similarly, we look after the last para in the promote range for an indentation that is less (or no list style).
4254
// We can end the renumbering at this point.
4256
// For now, only renumber within the promotion range.
4258
return DoNumberList(range, range, promoteBy, def, flags, 1, specifiedLevel);
4261
bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy, const wxRichTextRange& range, const wxString& defName, int flags, int specifiedLevel)
4263
wxRichTextBuffer* buffer = GetBuffer();
4264
if (buffer->GetStyleSheet())
4266
wxRichTextListStyleDefinition* def = NULL;
4267
if (!defName.IsEmpty())
4268
def = buffer->GetStyleSheet()->FindListStyle(defName);
4269
return PromoteList(promoteBy, range, def, flags, specifiedLevel);
4274
/// Fills in the attributes for numbering a paragraph after previousParagraph. It also finds the
4275
/// position of the paragraph that it had to start looking from.
4276
bool wxRichTextParagraphLayoutBox::FindNextParagraphNumber(wxRichTextParagraph* previousParagraph, wxRichTextAttr& attr) const
4278
if (!previousParagraph->GetAttributes().HasFlag(wxTEXT_ATTR_BULLET_STYLE) || previousParagraph->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE)
4281
wxRichTextBuffer* buffer = GetBuffer();
4282
wxRichTextStyleSheet* styleSheet = buffer->GetStyleSheet();
4283
if (styleSheet && !previousParagraph->GetAttributes().GetListStyleName().IsEmpty())
4285
wxRichTextListStyleDefinition* def = styleSheet->FindListStyle(previousParagraph->GetAttributes().GetListStyleName());
4288
// int thisIndent = previousParagraph->GetAttributes().GetLeftIndent();
4289
// int thisLevel = def->FindLevelForIndent(thisIndent);
4291
bool isOutline = (previousParagraph->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE) != 0;
4293
attr.SetFlags(previousParagraph->GetAttributes().GetFlags() & (wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_BULLET_NUMBER|wxTEXT_ATTR_BULLET_TEXT|wxTEXT_ATTR_BULLET_NAME));
4294
if (previousParagraph->GetAttributes().HasBulletName())
4295
attr.SetBulletName(previousParagraph->GetAttributes().GetBulletName());
4296
attr.SetBulletStyle(previousParagraph->GetAttributes().GetBulletStyle());
4297
attr.SetListStyleName(previousParagraph->GetAttributes().GetListStyleName());
4299
int nextNumber = previousParagraph->GetAttributes().GetBulletNumber() + 1;
4300
attr.SetBulletNumber(nextNumber);
4304
wxString text = previousParagraph->GetAttributes().GetBulletText();
4305
if (!text.IsEmpty())
4307
int pos = text.Find(wxT('.'), true);
4308
if (pos != wxNOT_FOUND)
4310
text = text.Mid(0, text.Length() - pos - 1);
4313
text = wxEmptyString;
4314
if (!text.IsEmpty())
4316
text += wxString::Format(wxT("%d"), nextNumber);
4317
attr.SetBulletText(text);
4331
* wxRichTextParagraph
4332
* This object represents a single paragraph (or in a straight text editor, a line).
4335
IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraph, wxRichTextCompositeObject)
4337
wxArrayInt wxRichTextParagraph::sm_defaultTabs;
4339
wxRichTextParagraph::wxRichTextParagraph(wxRichTextObject* parent, wxRichTextAttr* style):
4340
wxRichTextCompositeObject(parent)
4343
SetAttributes(*style);
4346
wxRichTextParagraph::wxRichTextParagraph(const wxString& text, wxRichTextObject* parent, wxRichTextAttr* paraStyle, wxRichTextAttr* charStyle):
4347
wxRichTextCompositeObject(parent)
4350
SetAttributes(*paraStyle);
4352
AppendChild(new wxRichTextPlainText(text, this, charStyle));
4355
wxRichTextParagraph::~wxRichTextParagraph()
4361
bool wxRichTextParagraph::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int WXUNUSED(descent), int style)
4366
// Currently we don't merge these attributes with the parent, but we
4367
// should consider whether we should (e.g. if we set a border colour
4368
// for all paragraphs). But generally box attributes are likely to be
4369
// different for different objects.
4370
wxRect paraRect = GetRect();
4371
wxRichTextAttr attr = GetCombinedAttributes();
4372
context.ApplyVirtualAttributes(attr, this);
4374
DrawBoxAttributes(dc, GetBuffer(), attr, paraRect);
4376
// Draw the bullet, if any
4377
if (attr.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE)
4379
if (attr.GetLeftSubIndent() != 0)
4381
int spaceBeforePara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingBefore());
4382
int leftIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftIndent());
4384
wxRichTextAttr bulletAttr(attr);
4386
// Combine with the font of the first piece of content, if one is specified
4387
if (GetChildren().GetCount() > 0)
4389
wxRichTextObject* firstObj = (wxRichTextObject*) GetChildren().GetFirst()->GetData();
4390
if (!firstObj->IsFloatable() && firstObj->GetAttributes().HasFont())
4392
wxRichTextApplyStyle(bulletAttr, firstObj->GetAttributes());
4396
// Get line height from first line, if any
4397
wxRichTextLine* line = m_cachedLines.GetFirst() ? (wxRichTextLine* ) m_cachedLines.GetFirst()->GetData() : NULL;
4400
int lineHeight wxDUMMY_INITIALIZE(0);
4403
lineHeight = line->GetSize().y;
4404
linePos = line->GetPosition() + GetPosition();
4409
if (bulletAttr.HasFont() && GetBuffer())
4410
font = GetBuffer()->GetFontTable().FindFont(bulletAttr);
4412
font = (*wxNORMAL_FONT);
4414
wxCheckSetFont(dc, font);
4416
lineHeight = dc.GetCharHeight();
4417
linePos = GetPosition();
4418
linePos.y += spaceBeforePara;
4421
wxRect bulletRect(GetPosition().x + leftIndent, linePos.y, linePos.x - (GetPosition().x + leftIndent), lineHeight);
4423
if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP)
4425
if (wxRichTextBuffer::GetRenderer())
4426
wxRichTextBuffer::GetRenderer()->DrawBitmapBullet(this, dc, bulletAttr, bulletRect);
4428
else if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_STANDARD)
4430
if (wxRichTextBuffer::GetRenderer())
4431
wxRichTextBuffer::GetRenderer()->DrawStandardBullet(this, dc, bulletAttr, bulletRect);
4435
wxString bulletText = GetBulletText();
4437
if (!bulletText.empty() && wxRichTextBuffer::GetRenderer())
4438
wxRichTextBuffer::GetRenderer()->DrawTextBullet(this, dc, bulletAttr, bulletRect, bulletText);
4443
// Draw the range for each line, one object at a time.
4445
wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
4448
wxRichTextLine* line = node->GetData();
4449
wxRichTextRange lineRange = line->GetAbsoluteRange();
4451
// Lines are specified relative to the paragraph
4453
wxPoint linePosition = line->GetPosition() + GetPosition();
4455
// Don't draw if off the screen
4456
if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) != 0) || ((linePosition.y + line->GetSize().y) >= rect.y && linePosition.y <= rect.y + rect.height))
4458
wxPoint objectPosition = linePosition;
4459
int maxDescent = line->GetDescent();
4461
// Loop through objects until we get to the one within range
4462
wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
4467
wxRichTextObject* child = node2->GetData();
4469
if (!child->IsFloating() && child->GetRange().GetLength() > 0 && !child->GetRange().IsOutside(lineRange) && !lineRange.IsOutside(range))
4471
// Draw this part of the line at the correct position
4472
wxRichTextRange objectRange(child->GetRange());
4473
objectRange.LimitTo(lineRange);
4476
if (child->IsTopLevel())
4478
objectSize = child->GetCachedSize();
4479
objectRange = child->GetOwnRange();
4483
#if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING && wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4484
if (i < (int) line->GetObjectSizes().GetCount())
4486
objectSize.x = line->GetObjectSizes()[(size_t) i];
4492
child->GetRangeSize(objectRange, objectSize, descent, dc, context, wxRICHTEXT_UNFORMATTED, objectPosition);
4496
// Use the child object's width, but the whole line's height
4497
wxRect childRect(objectPosition, wxSize(objectSize.x, line->GetSize().y));
4498
child->Draw(dc, context, objectRange, selection, childRect, maxDescent, style);
4500
objectPosition.x += objectSize.x;
4503
else if (child->GetRange().GetStart() > lineRange.GetEnd())
4504
// Can break out of inner loop now since we've passed this line's range
4507
node2 = node2->GetNext();
4511
node = node->GetNext();
4517
// Get the range width using partial extents calculated for the whole paragraph.
4518
static int wxRichTextGetRangeWidth(const wxRichTextParagraph& para, const wxRichTextRange& range, const wxArrayInt& partialExtents)
4520
wxASSERT(partialExtents.GetCount() >= (size_t) range.GetLength());
4522
if (partialExtents.GetCount() < (size_t) range.GetLength())
4525
int leftMostPos = 0;
4526
if (range.GetStart() - para.GetRange().GetStart() > 0)
4527
leftMostPos = partialExtents[range.GetStart() - para.GetRange().GetStart() - 1];
4529
int rightMostPos = partialExtents[range.GetEnd() - para.GetRange().GetStart()];
4531
int w = rightMostPos - leftMostPos;
4536
/// Lay the item out
4537
bool wxRichTextParagraph::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style)
4539
// Deal with floating objects firstly before the normal layout
4540
wxRichTextBuffer* buffer = GetBuffer();
4542
wxRichTextFloatCollector* collector = GetContainer()->GetFloatCollector();
4543
wxASSERT(collector);
4544
LayoutFloat(dc, context, rect, style, collector);
4546
wxRichTextAttr attr = GetCombinedAttributes();
4547
context.ApplyVirtualAttributes(attr, this);
4551
// Increase the size of the paragraph due to spacing
4552
int spaceBeforePara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingBefore());
4553
int spaceAfterPara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingAfter());
4554
int leftIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftIndent());
4555
int leftSubIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftSubIndent());
4556
int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
4558
int lineSpacing = 0;
4560
// Let's assume line spacing of 10 is normal, 15 is 1.5, 20 is 2, etc.
4561
if (attr.HasLineSpacing() && attr.GetLineSpacing() > 0 && attr.GetFont().IsOk())
4563
wxCheckSetFont(dc, attr.GetFont());
4564
lineSpacing = (int) (double(dc.GetCharHeight()) * (double(attr.GetLineSpacing())/10.0 - 1.0));
4567
// Start position for each line relative to the paragraph
4568
int startPositionFirstLine = leftIndent;
4569
int startPositionSubsequentLines = leftIndent + leftSubIndent;
4571
// If we have a bullet in this paragraph, the start position for the first line's text
4572
// is actually leftIndent + leftSubIndent.
4573
if (attr.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE)
4574
startPositionFirstLine = startPositionSubsequentLines;
4576
long lastEndPos = GetRange().GetStart()-1;
4577
long lastCompletedEndPos = lastEndPos;
4579
int currentWidth = 0;
4580
SetPosition(rect.GetPosition());
4582
wxPoint currentPosition(0, spaceBeforePara); // We will calculate lines relative to paragraph
4585
int maxHeight = currentPosition.y;
4590
int lineDescent = 0;
4592
wxRichTextObjectList::compatibility_iterator node;
4594
#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4596
wxArrayInt partialExtents;
4599
int paraDescent = 0;
4601
// This calculates the partial text extents
4602
GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, rect.GetPosition(), & partialExtents);
4604
node = m_children.GetFirst();
4607
wxRichTextObject* child = node->GetData();
4609
//child->SetCachedSize(wxDefaultSize);
4610
child->Layout(dc, context, rect, style);
4612
node = node->GetNext();
4618
// We may need to go back to a previous child, in which case create the new line,
4619
// find the child corresponding to the start position of the string, and
4622
wxRect availableRect;
4624
node = m_children.GetFirst();
4627
wxRichTextObject* child = node->GetData();
4629
// If floating, ignore. We already laid out floats.
4630
// Also ignore if empty object, except if we haven't got any
4632
if (child->IsFloating() || !child->IsShown() ||
4633
(child->GetRange().GetLength() == 0 && maxHeight > spaceBeforePara)
4636
node = node->GetNext();
4640
// If this is e.g. a composite text box, it will need to be laid out itself.
4641
// But if just a text fragment or image, for example, this will
4642
// do nothing. NB: won't we need to set the position after layout?
4643
// since for example if position is dependent on vertical line size, we
4644
// can't tell the position until the size is determined. So possibly introduce
4645
// another layout phase.
4647
// We may only be looking at part of a child, if we searched back for wrapping
4648
// and found a suitable point some way into the child. So get the size for the fragment
4651
long nextBreakPos = GetFirstLineBreakPosition(lastEndPos+1);
4652
long lastPosToUse = child->GetRange().GetEnd();
4653
bool lineBreakInThisObject = (nextBreakPos > -1 && nextBreakPos <= child->GetRange().GetEnd());
4655
if (lineBreakInThisObject)
4656
lastPosToUse = nextBreakPos;
4659
int childDescent = 0;
4661
int startOffset = (lineCount == 0 ? startPositionFirstLine : startPositionSubsequentLines);
4662
availableRect = wxRect(rect.x + startOffset, rect.y + currentPosition.y,
4663
rect.width - startOffset - rightIndent, rect.height);
4665
if (child->IsTopLevel())
4667
wxSize oldSize = child->GetCachedSize();
4669
child->Invalidate(wxRICHTEXT_ALL);
4670
child->SetPosition(wxPoint(0, 0));
4672
// Lays out the object first with a given amount of space, and then if no width was specified in attr,
4673
// lays out the object again using the minimum size
4674
// The position will be determined by its location in its line,
4675
// and not by the child's actual position.
4676
child->LayoutToBestSize(dc, context, buffer,
4677
attr, child->GetAttributes(), availableRect, parentRect, style);
4679
if (oldSize != child->GetCachedSize())
4681
partialExtents.Clear();
4683
// Recalculate the partial text extents since the child object changed size
4684
GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, wxPoint(0,0), & partialExtents);
4688
// Problem: we need to layout composites here for which we need the available width,
4689
// but we can't get the available width without using the float collector which
4690
// needs to know the object height.
4692
if ((nextBreakPos == -1) && (lastEndPos == child->GetRange().GetStart() - 1)) // i.e. we want to get the whole thing
4694
childSize = child->GetCachedSize();
4695
childDescent = child->GetDescent();
4699
#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4700
// Get height only, then the width using the partial extents
4701
GetRangeSize(wxRichTextRange(lastEndPos+1, lastPosToUse), childSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_HEIGHT_ONLY);
4702
childSize.x = wxRichTextGetRangeWidth(*this, wxRichTextRange(lastEndPos+1, lastPosToUse), partialExtents);
4704
GetRangeSize(wxRichTextRange(lastEndPos+1, lastPosToUse), childSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED, rect.GetPosition());
4709
int loopIterations = 0;
4711
// If there are nested objects that need to lay themselves out, we have to do this in a
4712
// loop because the height of the object may well depend on the available width.
4713
// And because of floating object positioning, the available width depends on the
4714
// height of the object and whether it will clash with the floating objects.
4715
// So, we see whether the available width changes due to the presence of floating images.
4716
// If it does, then we'll use the new restricted width to find the object height again.
4717
// If this causes another restriction in the available width, we'll try again, until
4718
// either we lose patience or the available width settles down.
4723
wxRect oldAvailableRect = availableRect;
4725
// Available width depends on the floating objects and the line height.
4726
// Note: the floating objects may be placed vertically along the two sides of
4727
// buffer, so we may have different available line widths with different
4728
// [startY, endY]. So, we can't determine how wide the available
4729
// space is until we know the exact line height.
4730
if (childDescent == 0)
4732
lineHeight = wxMax(lineHeight, childSize.y);
4733
lineDescent = maxDescent;
4734
lineAscent = maxAscent;
4738
lineDescent = wxMax(childDescent, maxDescent);
4739
lineAscent = wxMax(childSize.y-childDescent, maxAscent);
4741
lineHeight = wxMax(lineHeight, (lineDescent + lineAscent));
4742
wxRect floatAvailableRect = collector->GetAvailableRect(rect.y + currentPosition.y, rect.y + currentPosition.y + lineHeight);
4744
// Adjust availableRect to the space that is available when taking floating objects into account.
4746
if (floatAvailableRect.x + startOffset > availableRect.x)
4748
int newX = floatAvailableRect.x + startOffset;
4749
int newW = availableRect.width - (newX - availableRect.x);
4750
availableRect.x = newX;
4751
availableRect.width = newW;
4754
if (floatAvailableRect.width < availableRect.width)
4755
availableRect.width = floatAvailableRect.width;
4757
currentPosition.x = availableRect.x - rect.x;
4759
if (child->IsTopLevel() && loopIterations <= 20)
4761
if (availableRect != oldAvailableRect)
4763
wxSize oldSize = child->GetCachedSize();
4765
// Lays out the object first with a given amount of space, and then if no width was specified in attr,
4766
// lays out the object again using the minimum size
4767
child->Invalidate(wxRICHTEXT_ALL);
4768
child->LayoutToBestSize(dc, context, buffer,
4769
attr, child->GetAttributes(), availableRect, parentRect, style);
4770
childSize = child->GetCachedSize();
4771
childDescent = child->GetDescent();
4773
if (oldSize != child->GetCachedSize())
4775
partialExtents.Clear();
4777
// Recalculate the partial text extents since the child object changed size
4778
GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, wxPoint(0,0), & partialExtents);
4781
// Go around the loop finding the available rect for the given floating objects
4791
if (child->IsTopLevel())
4793
// We can move it to the correct position at this point
4794
// TODO: probably need to add margin
4795
child->Move(GetPosition() + wxPoint(currentWidth + (wxMax(leftIndent, leftIndent + leftSubIndent)), currentPosition.y));
4799
// 1) There was a line break BEFORE the natural break
4800
// 2) There was a line break AFTER the natural break
4801
// 3) It's the last line
4802
// 4) The child still fits (carry on) - 'else' clause
4804
if ((lineBreakInThisObject && (childSize.x + currentWidth <= availableRect.width))
4806
(childSize.x + currentWidth > availableRect.width)
4808
((childSize.x + currentWidth <= availableRect.width) && !node->GetNext())
4812
long wrapPosition = 0;
4813
if ((childSize.x + currentWidth <= availableRect.width) && !node->GetNext() && !lineBreakInThisObject)
4814
wrapPosition = child->GetRange().GetEnd();
4817
// Find a place to wrap. This may walk back to previous children,
4818
// for example if a word spans several objects.
4819
// Note: one object must contains only one wxTextAtrr, so the line height will not
4820
// change inside one object. Thus, we can pass the remain line width to the
4821
// FindWrapPosition function.
4822
if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos+1, child->GetRange().GetEnd()), dc, context, availableRect.width, wrapPosition, & partialExtents))
4824
// If the function failed, just cut it off at the end of this child.
4825
wrapPosition = child->GetRange().GetEnd();
4828
// FindWrapPosition can still return a value that will put us in an endless wrapping loop
4829
if (wrapPosition <= lastCompletedEndPos)
4830
wrapPosition = wxMax(lastCompletedEndPos+1,child->GetRange().GetEnd());
4832
// Line end position shouldn't be the same as the end, or greater.
4833
if (wrapPosition >= GetRange().GetEnd())
4834
wrapPosition = wxMax(0, GetRange().GetEnd()-1);
4836
// wxLogDebug(wxT("Split at %ld"), wrapPosition);
4838
// Let's find the actual size of the current line now
4840
wxRichTextRange actualRange(lastCompletedEndPos+1, wrapPosition);
4844
#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4845
if (!child->IsEmpty())
4847
// Get height only, then the width using the partial extents
4848
GetRangeSize(actualRange, actualSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_HEIGHT_ONLY);
4849
actualSize.x = wxRichTextGetRangeWidth(*this, actualRange, partialExtents);
4853
GetRangeSize(actualRange, actualSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED);
4855
currentWidth = actualSize.x;
4857
// The descent for the whole line at this point, is the correct max descent
4858
maxDescent = childDescent;
4860
maxAscent = actualSize.y-childDescent;
4862
// lineHeight is given by the height for the whole line, since it will
4863
// take into account ascend/descend.
4864
lineHeight = actualSize.y;
4866
if (lineHeight == 0 && buffer)
4868
wxFont font(buffer->GetFontTable().FindFont(attr));
4869
wxCheckSetFont(dc, font);
4870
lineHeight = dc.GetCharHeight();
4873
if (maxDescent == 0)
4876
dc.GetTextExtent(wxT("X"), & w, &h, & maxDescent);
4880
wxRichTextLine* line = AllocateLine(lineCount);
4882
// Set relative range so we won't have to change line ranges when paragraphs are moved
4883
line->SetRange(wxRichTextRange(actualRange.GetStart() - GetRange().GetStart(), actualRange.GetEnd() - GetRange().GetStart()));
4884
line->SetPosition(currentPosition);
4885
line->SetSize(wxSize(currentWidth, lineHeight));
4886
line->SetDescent(maxDescent);
4888
maxHeight = currentPosition.y + lineHeight;
4890
// Now move down a line. TODO: add margins, spacing
4891
currentPosition.y += lineHeight;
4892
currentPosition.y += lineSpacing;
4895
maxWidth = wxMax(maxWidth, currentWidth+startOffset);
4900
// TODO: account for zero-length objects
4901
// wxASSERT(wrapPosition > lastCompletedEndPos);
4903
lastEndPos = wrapPosition;
4904
lastCompletedEndPos = lastEndPos;
4908
if (wrapPosition < GetRange().GetEnd()-1)
4910
// May need to set the node back to a previous one, due to searching back in wrapping
4911
wxRichTextObject* childAfterWrapPosition = FindObjectAtPosition(wrapPosition+1);
4912
if (childAfterWrapPosition)
4913
node = m_children.Find(childAfterWrapPosition);
4915
node = node->GetNext();
4918
node = node->GetNext();
4920
// Apply paragraph styles such as alignment to the wrapped line
4921
ApplyParagraphStyle(line, attr, availableRect, dc);
4925
// We still fit, so don't add a line, and keep going
4926
currentWidth += childSize.x;
4928
if (childDescent == 0)
4930
// An object with a zero descend value wants to take up the whole
4931
// height regardless of baseline
4932
lineHeight = wxMax(lineHeight, childSize.y);
4936
maxDescent = wxMax(childDescent, maxDescent);
4937
maxAscent = wxMax(childSize.y-childDescent, maxAscent);
4940
lineHeight = wxMax(lineHeight, (maxDescent + maxAscent));
4942
maxWidth = wxMax(maxWidth, currentWidth+startOffset);
4943
lastEndPos = child->GetRange().GetEnd();
4945
node = node->GetNext();
4949
//wxASSERT(!(lastCompletedEndPos != -1 && lastCompletedEndPos < GetRange().GetEnd()-1));
4951
// Remove remaining unused line objects, if any
4952
ClearUnusedLines(lineCount);
4954
// We need to add back the margins etc.
4956
wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
4957
contentRect = wxRect(wxPoint(0, 0), wxSize(maxWidth, currentPosition.y + spaceAfterPara));
4958
GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
4959
SetCachedSize(marginRect.GetSize());
4962
// The maximum size is the length of the paragraph stretched out into a line.
4963
// So if there were a single word, or an image, or a fixed-size text box, the object could be shrunk around
4964
// this size. TODO: take into account line breaks.
4966
wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
4967
contentRect = wxRect(wxPoint(0, 0), wxSize(paraSize.x + wxMax(leftIndent, leftIndent + leftSubIndent) + rightIndent, currentPosition.y + spaceAfterPara));
4968
GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
4969
SetMaxSize(marginRect.GetSize());
4972
// Find the greatest minimum size. Currently we only look at non-text objects,
4973
// which isn't ideal but it would be slow to find the maximum word width to
4974
// use as the minimum.
4977
node = m_children.GetFirst();
4980
wxRichTextObject* child = node->GetData();
4982
// If floating, ignore. We already laid out floats.
4983
// Also ignore if empty object, except if we haven't got any
4985
if (!child->IsFloating() && child->GetRange().GetLength() != 0 && !wxDynamicCast(child, wxRichTextPlainText))
4987
if (child->GetCachedSize().x > minWidth)
4988
minWidth = child->GetMinSize().x;
4990
node = node->GetNext();
4993
wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
4994
contentRect = wxRect(wxPoint(0, 0), wxSize(minWidth, currentPosition.y + spaceAfterPara));
4995
GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
4996
SetMinSize(marginRect.GetSize());
4999
#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5000
#if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
5001
// Use the text extents to calculate the size of each fragment in each line
5002
wxRichTextLineList::compatibility_iterator lineNode = m_cachedLines.GetFirst();
5005
wxRichTextLine* line = lineNode->GetData();
5006
wxRichTextRange lineRange = line->GetAbsoluteRange();
5008
// Loop through objects until we get to the one within range
5009
wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
5013
wxRichTextObject* child = node2->GetData();
5015
if (child->GetRange().GetLength() > 0 && !child->GetRange().IsOutside(lineRange))
5017
wxRichTextRange rangeToUse = lineRange;
5018
rangeToUse.LimitTo(child->GetRange());
5020
// Find the size of the child from the text extents, and store in an array
5021
// for drawing later
5023
if (rangeToUse.GetStart() > GetRange().GetStart())
5024
left = partialExtents[(rangeToUse.GetStart()-1) - GetRange().GetStart()];
5025
int right = partialExtents[rangeToUse.GetEnd() - GetRange().GetStart()];
5026
int sz = right - left;
5027
line->GetObjectSizes().Add(sz);
5029
else if (child->GetRange().GetStart() > lineRange.GetEnd())
5030
// Can break out of inner loop now since we've passed this line's range
5033
node2 = node2->GetNext();
5036
lineNode = lineNode->GetNext();
5044
/// Apply paragraph styles, such as centering, to wrapped lines
5045
/// TODO: take into account box attributes, possibly
5046
void wxRichTextParagraph::ApplyParagraphStyle(wxRichTextLine* line, const wxRichTextAttr& attr, const wxRect& rect, wxDC& dc)
5048
if (!attr.HasAlignment())
5051
wxPoint pos = line->GetPosition();
5052
wxPoint originalPos = pos;
5053
wxSize size = line->GetSize();
5055
// centering, right-justification
5056
if (attr.HasAlignment() && attr.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE)
5058
int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
5059
pos.x = (rect.GetWidth() - rightIndent - size.x)/2 + pos.x;
5060
line->SetPosition(pos);
5062
else if (attr.HasAlignment() && attr.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT)
5064
int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
5065
pos.x = pos.x + rect.GetWidth() - size.x - rightIndent;
5066
line->SetPosition(pos);
5069
if (pos != originalPos)
5071
wxPoint inc = pos - originalPos;
5073
wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5077
wxRichTextObject* child = node->GetData();
5078
if (child->IsTopLevel() && !child->GetRange().IsOutside(line->GetAbsoluteRange()))
5079
child->Move(child->GetPosition() + inc);
5081
node = node->GetNext();
5086
/// Insert text at the given position
5087
bool wxRichTextParagraph::InsertText(long pos, const wxString& text)
5089
wxRichTextObject* childToUse = NULL;
5090
wxRichTextObjectList::compatibility_iterator nodeToUse = wxRichTextObjectList::compatibility_iterator();
5092
wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5095
wxRichTextObject* child = node->GetData();
5096
if (child->GetRange().Contains(pos) && child->GetRange().GetLength() > 0)
5103
node = node->GetNext();
5108
wxRichTextPlainText* textObject = wxDynamicCast(childToUse, wxRichTextPlainText);
5111
int posInString = pos - textObject->GetRange().GetStart();
5113
wxString newText = textObject->GetText().Mid(0, posInString) +
5114
text + textObject->GetText().Mid(posInString);
5115
textObject->SetText(newText);
5117
int textLength = text.length();
5119
textObject->SetRange(wxRichTextRange(textObject->GetRange().GetStart(),
5120
textObject->GetRange().GetEnd() + textLength));
5122
// Increment the end range of subsequent fragments in this paragraph.
5123
// We'll set the paragraph range itself at a higher level.
5125
wxRichTextObjectList::compatibility_iterator node = nodeToUse->GetNext();
5128
wxRichTextObject* child = node->GetData();
5129
child->SetRange(wxRichTextRange(textObject->GetRange().GetStart() + textLength,
5130
textObject->GetRange().GetEnd() + textLength));
5132
node = node->GetNext();
5139
// TODO: if not a text object, insert at closest position, e.g. in front of it
5145
// Don't pass parent initially to suppress auto-setting of parent range.
5146
// We'll do that at a higher level.
5147
wxRichTextPlainText* textObject = new wxRichTextPlainText(text, this);
5149
AppendChild(textObject);
5156
void wxRichTextParagraph::Copy(const wxRichTextParagraph& obj)
5158
wxRichTextCompositeObject::Copy(obj);
5161
/// Clear the cached lines
5162
void wxRichTextParagraph::ClearLines()
5164
WX_CLEAR_LIST(wxRichTextLineList, m_cachedLines);
5167
/// Get/set the object size for the given range. Returns false if the range
5168
/// is invalid for this object.
5169
bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* partialExtents) const
5171
if (!range.IsWithin(GetRange()))
5174
if (flags & wxRICHTEXT_UNFORMATTED)
5176
// Just use unformatted data, assume no line breaks
5179
wxArrayInt childExtents;
5188
int maxLineHeight = 0;
5190
wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5193
wxRichTextObject* child = node->GetData();
5194
if (!child->GetRange().IsOutside(range))
5196
// Floating objects have a zero size within the paragraph.
5197
if (child->IsFloating())
5202
if (partialExtents->GetCount() > 0)
5203
lastSize = (*partialExtents)[partialExtents->GetCount()-1];
5207
partialExtents->Add(0 /* zero size */ + lastSize);
5214
wxRichTextRange rangeToUse = range;
5215
rangeToUse.LimitTo(child->GetRange());
5216
int childDescent = 0;
5218
// At present wxRICHTEXT_HEIGHT_ONLY is only fast if we've already cached the size,
5219
// but it's only going to be used after caching has taken place.
5220
if ((flags & wxRICHTEXT_HEIGHT_ONLY) && child->GetCachedSize().y != 0)
5222
childDescent = child->GetDescent();
5223
childSize = child->GetCachedSize();
5225
if (childDescent == 0)
5227
maxLineHeight = wxMax(maxLineHeight, childSize.y);
5231
maxDescent = wxMax(maxDescent, childDescent);
5232
maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5235
maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5237
sz.y = wxMax(sz.y, maxLineHeight);
5238
sz.x += childSize.x;
5239
descent = maxDescent;
5241
else if (child->IsTopLevel())
5243
childDescent = child->GetDescent();
5244
childSize = child->GetCachedSize();
5246
if (childDescent == 0)
5248
maxLineHeight = wxMax(maxLineHeight, childSize.y);
5252
maxDescent = wxMax(maxDescent, childDescent);
5253
maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5256
maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5258
sz.y = wxMax(sz.y, maxLineHeight);
5259
sz.x += childSize.x;
5260
descent = maxDescent;
5262
// FIXME: this won't change the original values.
5263
// Should we be calling GetRangeSize above instead of using cached values?
5265
if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange()))
5267
child->SetCachedSize(childSize);
5268
child->SetDescent(childDescent);
5275
if (partialExtents->GetCount() > 0)
5276
lastSize = (*partialExtents)[partialExtents->GetCount()-1];
5280
partialExtents->Add(childSize.x + lastSize);
5283
else if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y), p))
5285
if (childDescent == 0)
5287
maxLineHeight = wxMax(maxLineHeight, childSize.y);
5291
maxDescent = wxMax(maxDescent, childDescent);
5292
maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5295
maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5297
sz.y = wxMax(sz.y, maxLineHeight);
5298
sz.x += childSize.x;
5299
descent = maxDescent;
5301
if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange()))
5303
child->SetCachedSize(childSize);
5304
child->SetDescent(childDescent);
5310
if (partialExtents->GetCount() > 0)
5311
lastSize = (*partialExtents)[partialExtents->GetCount()-1];
5316
for (i = 0; i < childExtents.GetCount(); i++)
5318
partialExtents->Add(childExtents[i] + lastSize);
5328
node = node->GetNext();
5334
// Use formatted data, with line breaks
5337
// We're going to loop through each line, and then for each line,
5338
// call GetRangeSize for the fragment that comprises that line.
5339
// Only we have to do that multiple times within the line, because
5340
// the line may be broken into pieces. For now ignore line break commands
5341
// (so we can assume that getting the unformatted size for a fragment
5342
// within a line is the actual size)
5344
wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
5347
wxRichTextLine* line = node->GetData();
5348
wxRichTextRange lineRange = line->GetAbsoluteRange();
5349
if (!lineRange.IsOutside(range))
5353
int maxLineHeight = 0;
5354
int maxLineWidth = 0;
5356
wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
5359
wxRichTextObject* child = node2->GetData();
5361
if (!child->IsFloating() && !child->GetRange().IsOutside(lineRange))
5363
wxRichTextRange rangeToUse = lineRange;
5364
rangeToUse.LimitTo(child->GetRange());
5365
if (child->IsTopLevel())
5366
rangeToUse = child->GetOwnRange();
5369
int childDescent = 0;
5370
if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y)))
5372
if (childDescent == 0)
5374
// Assume that if descent is zero, this child can occupy the full line height
5375
// and does not need space for the line's maximum descent. So we influence
5376
// the overall max line height only.
5377
maxLineHeight = wxMax(maxLineHeight, childSize.y);
5381
maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5382
maxDescent = wxMax(maxAscent, childDescent);
5384
maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5385
maxLineWidth += childSize.x;
5389
node2 = node2->GetNext();
5392
descent = wxMax(descent, maxDescent);
5394
// Increase size by a line (TODO: paragraph spacing)
5395
sz.y += maxLineHeight;
5396
sz.x = wxMax(sz.x, maxLineWidth);
5398
node = node->GetNext();
5405
/// Finds the absolute position and row height for the given character position
5406
bool wxRichTextParagraph::FindPosition(wxDC& dc, wxRichTextDrawingContext& context, long index, wxPoint& pt, int* height, bool forceLineStart)
5410
wxRichTextLine* line = ((wxRichTextParagraphLayoutBox*)GetParent())->GetLineAtPosition(0);
5412
*height = line->GetSize().y;
5414
*height = dc.GetCharHeight();
5416
// -1 means 'the start of the buffer'.
5419
pt = pt + line->GetPosition();
5424
// The final position in a paragraph is taken to mean the position
5425
// at the start of the next paragraph.
5426
if (index == GetRange().GetEnd())
5428
wxRichTextParagraphLayoutBox* parent = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
5429
wxASSERT( parent != NULL );
5431
// Find the height at the next paragraph, if any
5432
wxRichTextLine* line = parent->GetLineAtPosition(index + 1);
5435
*height = line->GetSize().y;
5436
pt = line->GetAbsolutePosition();
5440
*height = dc.GetCharHeight();
5441
int indent = ConvertTenthsMMToPixels(dc, m_attributes.GetLeftIndent());
5442
pt = wxPoint(indent, GetCachedSize().y);
5448
if (index < GetRange().GetStart() || index > GetRange().GetEnd())
5451
wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
5454
wxRichTextLine* line = node->GetData();
5455
wxRichTextRange lineRange = line->GetAbsoluteRange();
5456
if (index >= lineRange.GetStart() && index <= lineRange.GetEnd())
5458
// If this is the last point in the line, and we're forcing the
5459
// returned value to be the start of the next line, do the required
5461
if (index == lineRange.GetEnd() && forceLineStart)
5463
if (node->GetNext())
5465
wxRichTextLine* nextLine = node->GetNext()->GetData();
5466
*height = nextLine->GetSize().y;
5467
pt = nextLine->GetAbsolutePosition();
5472
pt.y = line->GetPosition().y + GetPosition().y;
5474
wxRichTextRange r(lineRange.GetStart(), index);
5478
// We find the size of the line up to this point,
5479
// then we can add this size to the line start position and
5480
// paragraph start position to find the actual position.
5482
if (GetRangeSize(r, rangeSize, descent, dc, context, wxRICHTEXT_UNFORMATTED, line->GetPosition()+ GetPosition()))
5484
pt.x = line->GetPosition().x + GetPosition().x + rangeSize.x;
5485
*height = line->GetSize().y;
5492
node = node->GetNext();
5498
/// Hit-testing: returns a flag indicating hit test details, plus
5499
/// information about position
5500
int wxRichTextParagraph::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
5503
return wxRICHTEXT_HITTEST_NONE;
5505
// If we're in the top-level container, then we can return
5506
// a suitable hit test code even if the point is outside the container area,
5507
// so that we can position the caret sensibly even if we don't
5508
// click on valid content. If we're not at the top-level, and the point
5509
// is not within this paragraph object, then we don't want to stop more
5510
// precise hit-testing from working prematurely, so return immediately.
5511
// NEW STRATEGY: use the parent boundary to test whether we're in the
5512
// right region, not the paragraph, since the paragraph may be positioned
5513
// some way in from where the user clicks.
5516
wxRichTextObject* tempObj, *tempContextObj;
5517
if (GetParent() && GetParent()->wxRichTextObject::HitTest(dc, context, pt, tmpPos, & tempObj, & tempContextObj, flags) == wxRICHTEXT_HITTEST_NONE)
5518
return wxRICHTEXT_HITTEST_NONE;
5521
wxRichTextObjectList::compatibility_iterator objNode = m_children.GetFirst();
5524
wxRichTextObject* child = objNode->GetData();
5525
// Don't recurse if we have wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS,
5526
// and also, if this seems composite but actually is marked as atomic,
5528
if (child->IsTopLevel() && ((flags & wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS) == 0) &&
5529
(! (((flags & wxRICHTEXT_HITTEST_HONOUR_ATOMIC) != 0) && child->IsAtomic())))
5532
int hitTest = child->HitTest(dc, context, pt, textPosition, obj, contextObj);
5533
if (hitTest != wxRICHTEXT_HITTEST_NONE)
5538
objNode = objNode->GetNext();
5541
wxPoint paraPos = GetPosition();
5543
wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
5546
wxRichTextLine* line = node->GetData();
5547
wxPoint linePos = paraPos + line->GetPosition();
5548
wxSize lineSize = line->GetSize();
5549
wxRichTextRange lineRange = line->GetAbsoluteRange();
5551
if (pt.y <= linePos.y + lineSize.y)
5553
if (pt.x < linePos.x)
5555
textPosition = lineRange.GetStart();
5556
*obj = FindObjectAtPosition(textPosition);
5557
*contextObj = GetContainer();
5558
return wxRICHTEXT_HITTEST_BEFORE|wxRICHTEXT_HITTEST_OUTSIDE;
5560
else if (pt.x >= (linePos.x + lineSize.x))
5562
textPosition = lineRange.GetEnd();
5563
*obj = FindObjectAtPosition(textPosition);
5564
*contextObj = GetContainer();
5565
return wxRICHTEXT_HITTEST_AFTER|wxRICHTEXT_HITTEST_OUTSIDE;
5569
#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5570
wxArrayInt partialExtents;
5575
// This calculates the partial text extents
5576
GetRangeSize(lineRange, paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED, linePos, & partialExtents);
5578
int lastX = linePos.x;
5580
for (i = 0; i < partialExtents.GetCount(); i++)
5582
int nextX = partialExtents[i] + linePos.x;
5584
if (pt.x >= lastX && pt.x <= nextX)
5586
textPosition = i + lineRange.GetStart(); // minus 1?
5588
*obj = FindObjectAtPosition(textPosition);
5589
*contextObj = GetContainer();
5591
// So now we know it's between i-1 and i.
5592
// Let's see if we can be more precise about
5593
// which side of the position it's on.
5595
int midPoint = (nextX + lastX)/2;
5596
if (pt.x >= midPoint)
5597
return wxRICHTEXT_HITTEST_AFTER;
5599
return wxRICHTEXT_HITTEST_BEFORE;
5606
int lastX = linePos.x;
5607
for (i = lineRange.GetStart(); i <= lineRange.GetEnd(); i++)
5612
wxRichTextRange rangeToUse(lineRange.GetStart(), i);
5614
GetRangeSize(rangeToUse, childSize, descent, dc, context, wxRICHTEXT_UNFORMATTED, linePos);
5616
int nextX = childSize.x + linePos.x;
5618
if (pt.x >= lastX && pt.x <= nextX)
5622
*obj = FindObjectAtPosition(textPosition);
5623
*contextObj = GetContainer();
5625
// So now we know it's between i-1 and i.
5626
// Let's see if we can be more precise about
5627
// which side of the position it's on.
5629
int midPoint = (nextX + lastX)/2;
5630
if (pt.x >= midPoint)
5631
return wxRICHTEXT_HITTEST_AFTER;
5633
return wxRICHTEXT_HITTEST_BEFORE;
5644
node = node->GetNext();
5647
return wxRICHTEXT_HITTEST_NONE;
5650
/// Split an object at this position if necessary, and return
5651
/// the previous object, or NULL if inserting at beginning.
5652
wxRichTextObject* wxRichTextParagraph::SplitAt(long pos, wxRichTextObject** previousObject)
5654
wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5657
wxRichTextObject* child = node->GetData();
5659
if (pos == child->GetRange().GetStart())
5663
if (node->GetPrevious())
5664
*previousObject = node->GetPrevious()->GetData();
5666
*previousObject = NULL;
5672
if (child->GetRange().Contains(pos))
5674
// This should create a new object, transferring part of
5675
// the content to the old object and the rest to the new object.
5676
wxRichTextObject* newObject = child->DoSplit(pos);
5678
// If we couldn't split this object, just insert in front of it.
5681
// Maybe this is an empty string, try the next one
5686
// Insert the new object after 'child'
5687
if (node->GetNext())
5688
m_children.Insert(node->GetNext(), newObject);
5690
m_children.Append(newObject);
5691
newObject->SetParent(this);
5694
*previousObject = child;
5700
node = node->GetNext();
5703
*previousObject = NULL;
5707
/// Move content to a list from obj on
5708
void wxRichTextParagraph::MoveToList(wxRichTextObject* obj, wxList& list)
5710
wxRichTextObjectList::compatibility_iterator node = m_children.Find(obj);
5713
wxRichTextObject* child = node->GetData();
5716
wxRichTextObjectList::compatibility_iterator oldNode = node;
5718
node = node->GetNext();
5720
m_children.DeleteNode(oldNode);
5724
/// Add content back from list
5725
void wxRichTextParagraph::MoveFromList(wxList& list)
5727
for (wxList::compatibility_iterator node = list.GetFirst(); node; node = node->GetNext())
5729
AppendChild((wxRichTextObject*) node->GetData());
5734
void wxRichTextParagraph::CalculateRange(long start, long& end)
5736
wxRichTextCompositeObject::CalculateRange(start, end);
5738
// Add one for end of paragraph
5741
m_range.SetRange(start, end);
5744
/// Find the object at the given position
5745
wxRichTextObject* wxRichTextParagraph::FindObjectAtPosition(long position)
5747
wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5750
wxRichTextObject* obj = node->GetData();
5751
if (obj->GetRange().Contains(position) ||
5752
obj->GetRange().GetStart() == position ||
5753
obj->GetRange().GetEnd() == position)
5756
node = node->GetNext();
5761
/// Get the plain text searching from the start or end of the range.
5762
/// The resulting string may be shorter than the range given.
5763
bool wxRichTextParagraph::GetContiguousPlainText(wxString& text, const wxRichTextRange& range, bool fromStart)
5765
text = wxEmptyString;
5769
wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5772
wxRichTextObject* obj = node->GetData();
5773
if (!obj->GetRange().IsOutside(range))
5775
wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
5778
text += textObj->GetTextForRange(range);
5786
node = node->GetNext();
5791
wxRichTextObjectList::compatibility_iterator node = m_children.GetLast();
5794
wxRichTextObject* obj = node->GetData();
5795
if (!obj->GetRange().IsOutside(range))
5797
wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
5800
text = textObj->GetTextForRange(range) + text;
5804
text = wxT(" ") + text;
5808
node = node->GetPrevious();
5815
/// Find a suitable wrap position.
5816
bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange& range, wxDC& dc, wxRichTextDrawingContext& context, int availableSpace, long& wrapPosition, wxArrayInt* partialExtents)
5818
if (range.GetLength() <= 0)
5821
// Find the first position where the line exceeds the available space.
5823
long breakPosition = range.GetEnd();
5825
#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5826
if (partialExtents && partialExtents->GetCount() >= (size_t) (GetRange().GetLength()-1)) // the final position in a paragraph is the newline
5830
if (range.GetStart() > GetRange().GetStart())
5831
widthBefore = (*partialExtents)[range.GetStart() - GetRange().GetStart() - 1];
5836
for (i = (size_t) range.GetStart(); i <= (size_t) range.GetEnd(); i++)
5838
int widthFromStartOfThisRange = (*partialExtents)[i - GetRange().GetStart()] - widthBefore;
5840
if (widthFromStartOfThisRange > availableSpace)
5842
breakPosition = i-1;
5850
// Binary chop for speed
5851
long minPos = range.GetStart();
5852
long maxPos = range.GetEnd();
5855
if (minPos == maxPos)
5858
GetRangeSize(wxRichTextRange(range.GetStart(), minPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
5860
if (sz.x > availableSpace)
5861
breakPosition = minPos - 1;
5864
else if ((maxPos - minPos) == 1)
5867
GetRangeSize(wxRichTextRange(range.GetStart(), minPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
5869
if (sz.x > availableSpace)
5870
breakPosition = minPos - 1;
5873
GetRangeSize(wxRichTextRange(range.GetStart(), maxPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
5874
if (sz.x > availableSpace)
5875
breakPosition = maxPos-1;
5881
long nextPos = minPos + ((maxPos - minPos) / 2);
5884
GetRangeSize(wxRichTextRange(range.GetStart(), nextPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
5886
if (sz.x > availableSpace)
5898
// Now we know the last position on the line.
5899
// Let's try to find a word break.
5902
if (GetContiguousPlainText(plainText, wxRichTextRange(range.GetStart(), breakPosition), false))
5904
int newLinePos = plainText.Find(wxRichTextLineBreakChar);
5905
if (newLinePos != wxNOT_FOUND)
5907
breakPosition = wxMax(0, range.GetStart() + newLinePos);
5911
int spacePos = plainText.Find(wxT(' '), true);
5912
int tabPos = plainText.Find(wxT('\t'), true);
5913
int pos = wxMax(spacePos, tabPos);
5914
if (pos != wxNOT_FOUND)
5916
int positionsFromEndOfString = plainText.length() - pos - 1;
5917
breakPosition = breakPosition - positionsFromEndOfString;
5922
wrapPosition = breakPosition;
5927
/// Get the bullet text for this paragraph.
5928
wxString wxRichTextParagraph::GetBulletText()
5930
if (GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE ||
5931
(GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP))
5932
return wxEmptyString;
5934
int number = GetAttributes().GetBulletNumber();
5937
if ((GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ARABIC) || (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE))
5939
text.Printf(wxT("%d"), number);
5941
else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_UPPER)
5943
// TODO: Unicode, and also check if number > 26
5944
text.Printf(wxT("%c"), (wxChar) (number+64));
5946
else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_LOWER)
5948
// TODO: Unicode, and also check if number > 26
5949
text.Printf(wxT("%c"), (wxChar) (number+96));
5951
else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER)
5953
text = wxRichTextDecimalToRoman(number);
5955
else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER)
5957
text = wxRichTextDecimalToRoman(number);
5960
else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL)
5962
text = GetAttributes().GetBulletText();
5965
if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE)
5967
// The outline style relies on the text being computed statically,
5968
// since it depends on other levels points (e.g. 1.2.1.1). So normally the bullet text
5969
// should be stored in the attributes; if not, just use the number for this
5970
// level, as previously computed.
5971
if (!GetAttributes().GetBulletText().IsEmpty())
5972
text = GetAttributes().GetBulletText();
5975
if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PARENTHESES)
5977
text = wxT("(") + text + wxT(")");
5979
else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_RIGHT_PARENTHESIS)
5981
text = text + wxT(")");
5984
if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PERIOD)
5992
/// Allocate or reuse a line object
5993
wxRichTextLine* wxRichTextParagraph::AllocateLine(int pos)
5995
if (pos < (int) m_cachedLines.GetCount())
5997
wxRichTextLine* line = m_cachedLines.Item(pos)->GetData();
6003
wxRichTextLine* line = new wxRichTextLine(this);
6004
m_cachedLines.Append(line);
6009
/// Clear remaining unused line objects, if any
6010
bool wxRichTextParagraph::ClearUnusedLines(int lineCount)
6012
int cachedLineCount = m_cachedLines.GetCount();
6013
if ((int) cachedLineCount > lineCount)
6015
for (int i = 0; i < (int) (cachedLineCount - lineCount); i ++)
6017
wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetLast();
6018
wxRichTextLine* line = node->GetData();
6019
m_cachedLines.Erase(node);
6026
/// Get combined attributes of the base style, paragraph style and character style. We use this to dynamically
6027
/// retrieve the actual style.
6028
wxRichTextAttr wxRichTextParagraph::GetCombinedAttributes(const wxRichTextAttr& contentStyle, bool includingBoxAttr) const
6030
wxRichTextAttr attr;
6031
wxRichTextParagraphLayoutBox* buf = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
6034
attr = buf->GetBasicStyle();
6035
if (!includingBoxAttr)
6037
attr.GetTextBoxAttr().Reset();
6038
// The background colour will be painted by the container, and we don't
6039
// want to unnecessarily overwrite the background when we're drawing text
6040
// because this may erase the guideline (which appears just under the text
6041
// if there's no padding).
6042
attr.SetFlags(attr.GetFlags() & ~wxTEXT_ATTR_BACKGROUND_COLOUR);
6044
wxRichTextApplyStyle(attr, GetAttributes());
6047
attr = GetAttributes();
6049
wxRichTextApplyStyle(attr, contentStyle);
6053
/// Get combined attributes of the base style and paragraph style.
6054
wxRichTextAttr wxRichTextParagraph::GetCombinedAttributes(bool includingBoxAttr) const
6056
wxRichTextAttr attr;
6057
wxRichTextParagraphLayoutBox* buf = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
6060
attr = buf->GetBasicStyle();
6061
if (!includingBoxAttr)
6062
attr.GetTextBoxAttr().Reset();
6063
wxRichTextApplyStyle(attr, GetAttributes());
6066
attr = GetAttributes();
6071
// Create default tabstop array
6072
void wxRichTextParagraph::InitDefaultTabs()
6074
// create a default tab list at 10 mm each.
6075
for (int i = 0; i < 20; ++i)
6077
sm_defaultTabs.Add(i*100);
6081
// Clear default tabstop array
6082
void wxRichTextParagraph::ClearDefaultTabs()
6084
sm_defaultTabs.Clear();
6087
void wxRichTextParagraph::LayoutFloat(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, int style, wxRichTextFloatCollector* floatCollector)
6089
wxRichTextObjectList::compatibility_iterator node = GetChildren().GetFirst();
6092
wxRichTextObject* anchored = node->GetData();
6093
if (anchored && anchored->IsFloating() && !floatCollector->HasFloat(anchored))
6097
anchored->GetRangeSize(anchored->GetRange(), size, descent, dc, context, style);
6100
if (anchored->GetAttributes().GetTextBoxAttr().GetTop().IsValid())
6102
offsetY = anchored->GetAttributes().GetTextBoxAttr().GetTop().GetValue();
6103
if (anchored->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
6105
offsetY = ConvertTenthsMMToPixels(dc, offsetY);
6109
int pos = floatCollector->GetFitPosition(anchored->GetAttributes().GetTextBoxAttr().GetFloatMode(), rect.y + offsetY, size.y);
6111
/* Update the offset */
6112
int newOffsetY = pos - rect.y;
6113
if (newOffsetY != offsetY)
6115
if (anchored->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
6116
newOffsetY = ConvertPixelsToTenthsMM(dc, newOffsetY);
6117
anchored->GetAttributes().GetTextBoxAttr().GetTop().SetValue(newOffsetY);
6120
if (anchored->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT)
6122
else if (anchored->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT)
6123
x = rect.x + rect.width - size.x;
6125
anchored->SetPosition(wxPoint(x, pos));
6126
anchored->SetCachedSize(size);
6127
floatCollector->CollectFloat(this, anchored);
6130
node = node->GetNext();
6134
// Get the first position from pos that has a line break character.
6135
long wxRichTextParagraph::GetFirstLineBreakPosition(long pos)
6137
wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
6140
wxRichTextObject* obj = node->GetData();
6141
if (pos >= obj->GetRange().GetStart() && pos <= obj->GetRange().GetEnd())
6143
wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
6146
long breakPos = textObj->GetFirstLineBreakPosition(pos);
6151
node = node->GetNext();
6158
* This object represents a line in a paragraph, and stores
6159
* offsets from the start of the paragraph representing the
6160
* start and end positions of the line.
6163
wxRichTextLine::wxRichTextLine(wxRichTextParagraph* parent)
6169
void wxRichTextLine::Init(wxRichTextParagraph* parent)
6172
m_range.SetRange(-1, -1);
6173
m_pos = wxPoint(0, 0);
6174
m_size = wxSize(0, 0);
6176
#if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6177
m_objectSizes.Clear();
6182
void wxRichTextLine::Copy(const wxRichTextLine& obj)
6184
m_range = obj.m_range;
6185
#if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6186
m_objectSizes = obj.m_objectSizes;
6190
/// Get the absolute object position
6191
wxPoint wxRichTextLine::GetAbsolutePosition() const
6193
return m_parent->GetPosition() + m_pos;
6196
/// Get the absolute range
6197
wxRichTextRange wxRichTextLine::GetAbsoluteRange() const
6199
wxRichTextRange range(m_range.GetStart() + m_parent->GetRange().GetStart(), 0);
6200
range.SetEnd(range.GetStart() + m_range.GetLength()-1);
6205
* wxRichTextPlainText
6206
* This object represents a single piece of text.
6209
IMPLEMENT_DYNAMIC_CLASS(wxRichTextPlainText, wxRichTextObject)
6211
wxRichTextPlainText::wxRichTextPlainText(const wxString& text, wxRichTextObject* parent, wxRichTextAttr* style):
6212
wxRichTextObject(parent)
6215
SetAttributes(*style);
6220
#define USE_KERNING_FIX 1
6222
// If insufficient tabs are defined, this is the tab width used
6223
#define WIDTH_FOR_DEFAULT_TABS 50
6226
bool wxRichTextPlainText::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int WXUNUSED(style))
6228
wxRichTextParagraph* para = wxDynamicCast(GetParent(), wxRichTextParagraph);
6229
wxASSERT (para != NULL);
6231
wxRichTextAttr textAttr(para ? para->GetCombinedAttributes(GetAttributes(), false /* no box attributes */) : GetAttributes());
6232
context.ApplyVirtualAttributes(textAttr, this);
6234
// Let's make the assumption for now that for content in a paragraph, including
6235
// text, we never have a discontinuous selection. So we only deal with a
6237
wxRichTextRange selectionRange;
6238
if (selection.IsValid())
6240
wxRichTextRangeArray selectionRanges = selection.GetSelectionForObject(this);
6241
if (selectionRanges.GetCount() > 0)
6242
selectionRange = selectionRanges[0];
6244
selectionRange = wxRICHTEXT_NO_SELECTION;
6247
selectionRange = wxRICHTEXT_NO_SELECTION;
6249
int offset = GetRange().GetStart();
6251
// Replace line break characters with spaces
6252
wxString str = m_text;
6253
wxString toRemove = wxRichTextLineBreakChar;
6254
str.Replace(toRemove, wxT(" "));
6255
if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS))
6258
long len = range.GetLength();
6259
wxString stringChunk = str.Mid(range.GetStart() - offset, (size_t) len);
6261
// Test for the optimized situations where all is selected, or none
6264
wxFont textFont(GetBuffer()->GetFontTable().FindFont(textAttr));
6265
wxCheckSetFont(dc, textFont);
6266
int charHeight = dc.GetCharHeight();
6269
if ( textFont.IsOk() )
6271
if ( textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT) )
6273
if (textFont.IsUsingSizeInPixels())
6275
double size = static_cast<double>(textFont.GetPixelSize().y) / wxSCRIPT_MUL_FACTOR;
6276
textFont.SetPixelSize(wxSize(0, static_cast<int>(size)));
6282
double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
6283
textFont.SetPointSize(static_cast<int>(size));
6287
wxCheckSetFont(dc, textFont);
6289
else if ( textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT) )
6291
if (textFont.IsUsingSizeInPixels())
6293
double size = static_cast<double>(textFont.GetPixelSize().y) / wxSCRIPT_MUL_FACTOR;
6294
textFont.SetPixelSize(wxSize(0, static_cast<int>(size)));
6296
int sub_height = static_cast<int>(static_cast<double>(charHeight) / wxSCRIPT_MUL_FACTOR);
6297
y = rect.y + (rect.height - sub_height + (descent - m_descent));
6301
double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
6302
textFont.SetPointSize(static_cast<int>(size));
6304
int sub_height = static_cast<int>(static_cast<double>(charHeight) / wxSCRIPT_MUL_FACTOR);
6305
y = rect.y + (rect.height - sub_height + (descent - m_descent));
6307
wxCheckSetFont(dc, textFont);
6312
y = rect.y + (rect.height - charHeight - (descent - m_descent));
6318
y = rect.y + (rect.height - charHeight - (descent - m_descent));
6321
// TODO: new selection code
6323
// (a) All selected.
6324
if (selectionRange.GetStart() <= range.GetStart() && selectionRange.GetEnd() >= range.GetEnd())
6326
DrawTabbedString(dc, textAttr, rect, stringChunk, x, y, true);
6328
// (b) None selected.
6329
else if (selectionRange.GetEnd() < range.GetStart() || selectionRange.GetStart() > range.GetEnd())
6331
// Draw all unselected
6332
DrawTabbedString(dc, textAttr, rect, stringChunk, x, y, false);
6336
// (c) Part selected, part not
6337
// Let's draw unselected chunk, selected chunk, then unselected chunk.
6339
dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
6341
// 1. Initial unselected chunk, if any, up until start of selection.
6342
if (selectionRange.GetStart() > range.GetStart() && selectionRange.GetStart() <= range.GetEnd())
6344
int r1 = range.GetStart();
6345
int s1 = selectionRange.GetStart()-1;
6346
int fragmentLen = s1 - r1 + 1;
6347
if (fragmentLen < 0)
6349
wxLogDebug(wxT("Mid(%d, %d"), (int)(r1 - offset), (int)fragmentLen);
6351
wxString stringFragment = str.Mid(r1 - offset, fragmentLen);
6353
DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, false);
6356
if (stringChunk.Find(wxT("\t")) == wxNOT_FOUND)
6358
// Compensate for kerning difference
6359
wxString stringFragment2(str.Mid(r1 - offset, fragmentLen+1));
6360
wxString stringFragment3(str.Mid(r1 - offset + fragmentLen, 1));
6362
wxCoord w1, h1, w2, h2, w3, h3;
6363
dc.GetTextExtent(stringFragment, & w1, & h1);
6364
dc.GetTextExtent(stringFragment2, & w2, & h2);
6365
dc.GetTextExtent(stringFragment3, & w3, & h3);
6367
int kerningDiff = (w1 + w3) - w2;
6368
x = x - kerningDiff;
6373
// 2. Selected chunk, if any.
6374
if (selectionRange.GetEnd() >= range.GetStart())
6376
int s1 = wxMax(selectionRange.GetStart(), range.GetStart());
6377
int s2 = wxMin(selectionRange.GetEnd(), range.GetEnd());
6379
int fragmentLen = s2 - s1 + 1;
6380
if (fragmentLen < 0)
6382
wxLogDebug(wxT("Mid(%d, %d"), (int)(s1 - offset), (int)fragmentLen);
6384
wxString stringFragment = str.Mid(s1 - offset, fragmentLen);
6386
DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, true);
6389
if (stringChunk.Find(wxT("\t")) == wxNOT_FOUND)
6391
// Compensate for kerning difference
6392
wxString stringFragment2(str.Mid(s1 - offset, fragmentLen+1));
6393
wxString stringFragment3(str.Mid(s1 - offset + fragmentLen, 1));
6395
wxCoord w1, h1, w2, h2, w3, h3;
6396
dc.GetTextExtent(stringFragment, & w1, & h1);
6397
dc.GetTextExtent(stringFragment2, & w2, & h2);
6398
dc.GetTextExtent(stringFragment3, & w3, & h3);
6400
int kerningDiff = (w1 + w3) - w2;
6401
x = x - kerningDiff;
6406
// 3. Remaining unselected chunk, if any
6407
if (selectionRange.GetEnd() < range.GetEnd())
6409
int s2 = wxMin(selectionRange.GetEnd()+1, range.GetEnd());
6410
int r2 = range.GetEnd();
6412
int fragmentLen = r2 - s2 + 1;
6413
if (fragmentLen < 0)
6415
wxLogDebug(wxT("Mid(%d, %d"), (int)(s2 - offset), (int)fragmentLen);
6417
wxString stringFragment = str.Mid(s2 - offset, fragmentLen);
6419
DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, false);
6426
bool wxRichTextPlainText::DrawTabbedString(wxDC& dc, const wxRichTextAttr& attr, const wxRect& rect,wxString& str, wxCoord& x, wxCoord& y, bool selected)
6428
bool hasTabs = (str.Find(wxT('\t')) != wxNOT_FOUND);
6430
wxArrayInt tabArray;
6434
if (attr.GetTabs().IsEmpty())
6435
tabArray = wxRichTextParagraph::GetDefaultTabs();
6437
tabArray = attr.GetTabs();
6438
tabCount = tabArray.GetCount();
6440
for (int i = 0; i < tabCount; ++i)
6442
int pos = tabArray[i];
6443
pos = ConvertTenthsMMToPixels(dc, pos);
6450
int nextTabPos = -1;
6456
wxColour highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
6457
wxColour highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
6459
wxCheckSetBrush(dc, wxBrush(highlightColour));
6460
wxCheckSetPen(dc, wxPen(highlightColour));
6461
dc.SetTextForeground(highlightTextColour);
6462
dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
6466
dc.SetTextForeground(attr.GetTextColour());
6468
if (attr.HasFlag(wxTEXT_ATTR_BACKGROUND_COLOUR) && attr.GetBackgroundColour().IsOk())
6470
dc.SetBackgroundMode(wxBRUSHSTYLE_SOLID);
6471
dc.SetTextBackground(attr.GetBackgroundColour());
6474
dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
6477
wxCoord x_orig = GetParent()->GetPosition().x;
6480
// the string has a tab
6481
// break up the string at the Tab
6482
wxString stringChunk = str.BeforeFirst(wxT('\t'));
6483
str = str.AfterFirst(wxT('\t'));
6484
dc.GetTextExtent(stringChunk, & w, & h);
6486
bool not_found = true;
6487
for (int i = 0; i < tabCount && not_found; ++i)
6489
nextTabPos = tabArray.Item(i) + x_orig;
6491
// Find the next tab position.
6492
// Even if we're at the end of the tab array, we must still draw the chunk.
6494
if (nextTabPos > tabPos || (i == (tabCount - 1)))
6496
if (nextTabPos <= tabPos)
6498
int defaultTabWidth = ConvertTenthsMMToPixels(dc, WIDTH_FOR_DEFAULT_TABS);
6499
nextTabPos = tabPos + defaultTabWidth;
6506
wxRect selRect(x, rect.y, w, rect.GetHeight());
6507
dc.DrawRectangle(selRect);
6509
dc.DrawText(stringChunk, x, y);
6511
if (attr.HasTextEffects() && (attr.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH))
6513
wxPen oldPen = dc.GetPen();
6514
wxCheckSetPen(dc, wxPen(attr.GetTextColour(), 1));
6515
dc.DrawLine(x, (int) (y+(h/2)+0.5), x+w, (int) (y+(h/2)+0.5));
6516
wxCheckSetPen(dc, oldPen);
6522
hasTabs = (str.Find(wxT('\t')) != wxNOT_FOUND);
6527
dc.GetTextExtent(str, & w, & h);
6530
wxRect selRect(x, rect.y, w, rect.GetHeight());
6531
dc.DrawRectangle(selRect);
6533
dc.DrawText(str, x, y);
6535
if (attr.HasTextEffects() && (attr.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH))
6537
wxPen oldPen = dc.GetPen();
6538
wxCheckSetPen(dc, wxPen(attr.GetTextColour(), 1));
6539
dc.DrawLine(x, (int) (y+(h/2)+0.5), x+w, (int) (y+(h/2)+0.5));
6540
wxCheckSetPen(dc, oldPen);
6549
/// Lay the item out
6550
bool wxRichTextPlainText::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& WXUNUSED(rect), const wxRect& WXUNUSED(parentRect), int WXUNUSED(style))
6552
// Only lay out if we haven't already cached the size
6554
GetRangeSize(GetRange(), m_size, m_descent, dc, context, 0, wxPoint(0, 0));
6556
// Eventually we want to have a reasonable estimate of minimum size.
6557
m_minSize = wxSize(0, 0);
6562
void wxRichTextPlainText::Copy(const wxRichTextPlainText& obj)
6564
wxRichTextObject::Copy(obj);
6566
m_text = obj.m_text;
6569
/// Get/set the object size for the given range. Returns false if the range
6570
/// is invalid for this object.
6571
bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int WXUNUSED(flags), wxPoint position, wxArrayInt* partialExtents) const
6573
if (!range.IsWithin(GetRange()))
6576
wxRichTextParagraph* para = wxDynamicCast(GetParent(), wxRichTextParagraph);
6577
wxASSERT (para != NULL);
6579
int relativeX = position.x - GetParent()->GetPosition().x;
6581
wxRichTextAttr textAttr(para ? para->GetCombinedAttributes(GetAttributes()) : GetAttributes());
6582
context.ApplyVirtualAttributes(textAttr, (wxRichTextObject*) this);
6584
// Always assume unformatted text, since at this level we have no knowledge
6585
// of line breaks - and we don't need it, since we'll calculate size within
6586
// formatted text by doing it in chunks according to the line ranges
6588
bool bScript(false);
6589
wxFont font(GetBuffer()->GetFontTable().FindFont(textAttr));
6592
if ( textAttr.HasTextEffects() && ( (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT)
6593
|| (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT) ) )
6595
wxFont textFont = font;
6596
if (textFont.IsUsingSizeInPixels())
6598
double size = static_cast<double>(textFont.GetPixelSize().y) / wxSCRIPT_MUL_FACTOR;
6599
textFont.SetPixelSize(wxSize(0, static_cast<int>(size)));
6603
double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
6604
textFont.SetPointSize(static_cast<int>(size));
6606
wxCheckSetFont(dc, textFont);
6611
wxCheckSetFont(dc, font);
6615
bool haveDescent = false;
6616
int startPos = range.GetStart() - GetRange().GetStart();
6617
long len = range.GetLength();
6619
wxString str(m_text);
6620
wxString toReplace = wxRichTextLineBreakChar;
6621
str.Replace(toReplace, wxT(" "));
6623
wxString stringChunk = str.Mid(startPos, (size_t) len);
6625
if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS))
6626
stringChunk.MakeUpper();
6630
if (stringChunk.Find(wxT('\t')) != wxNOT_FOUND)
6632
// the string has a tab
6633
wxArrayInt tabArray;
6634
if (textAttr.GetTabs().IsEmpty())
6635
tabArray = wxRichTextParagraph::GetDefaultTabs();
6637
tabArray = textAttr.GetTabs();
6639
int tabCount = tabArray.GetCount();
6641
for (int i = 0; i < tabCount; ++i)
6643
int pos = tabArray[i];
6644
pos = ((wxRichTextPlainText*) this)->ConvertTenthsMMToPixels(dc, pos);
6648
int nextTabPos = -1;
6650
while (stringChunk.Find(wxT('\t')) >= 0)
6652
int absoluteWidth = 0;
6654
// the string has a tab
6655
// break up the string at the Tab
6656
wxString stringFragment = stringChunk.BeforeFirst(wxT('\t'));
6657
stringChunk = stringChunk.AfterFirst(wxT('\t'));
6662
if (partialExtents->GetCount() > 0)
6663
oldWidth = (*partialExtents)[partialExtents->GetCount()-1];
6667
// Add these partial extents
6669
dc.GetPartialTextExtents(stringFragment, p);
6671
for (j = 0; j < p.GetCount(); j++)
6672
partialExtents->Add(oldWidth + p[j]);
6674
if (partialExtents->GetCount() > 0)
6675
absoluteWidth = (*partialExtents)[(*partialExtents).GetCount()-1] + relativeX;
6677
absoluteWidth = relativeX;
6681
dc.GetTextExtent(stringFragment, & w, & h);
6683
absoluteWidth = width + relativeX;
6687
bool notFound = true;
6688
for (int i = 0; i < tabCount && notFound; ++i)
6690
nextTabPos = tabArray.Item(i);
6692
// Find the next tab position.
6693
// Even if we're at the end of the tab array, we must still process the chunk.
6695
if (nextTabPos > absoluteWidth || (i == (tabCount - 1)))
6697
if (nextTabPos <= absoluteWidth)
6699
int defaultTabWidth = ((wxRichTextPlainText*) this)->ConvertTenthsMMToPixels(dc, WIDTH_FOR_DEFAULT_TABS);
6700
nextTabPos = absoluteWidth + defaultTabWidth;
6704
width = nextTabPos - relativeX;
6707
partialExtents->Add(width);
6713
if (!stringChunk.IsEmpty())
6718
if (partialExtents->GetCount() > 0)
6719
oldWidth = (*partialExtents)[partialExtents->GetCount()-1];
6723
// Add these partial extents
6725
dc.GetPartialTextExtents(stringChunk, p);
6727
for (j = 0; j < p.GetCount(); j++)
6728
partialExtents->Add(oldWidth + p[j]);
6732
dc.GetTextExtent(stringChunk, & w, & h, & descent);
6740
int charHeight = dc.GetCharHeight();
6741
if ((*partialExtents).GetCount() > 0)
6742
w = (*partialExtents)[partialExtents->GetCount()-1];
6745
size = wxSize(w, charHeight);
6749
size = wxSize(width, dc.GetCharHeight());
6753
dc.GetTextExtent(wxT("X"), & w, & h, & descent);
6761
/// Do a split, returning an object containing the second part, and setting
6762
/// the first part in 'this'.
6763
wxRichTextObject* wxRichTextPlainText::DoSplit(long pos)
6765
long index = pos - GetRange().GetStart();
6767
if (index < 0 || index >= (int) m_text.length())
6770
wxString firstPart = m_text.Mid(0, index);
6771
wxString secondPart = m_text.Mid(index);
6775
wxRichTextPlainText* newObject = new wxRichTextPlainText(secondPart);
6776
newObject->SetAttributes(GetAttributes());
6777
newObject->SetProperties(GetProperties());
6779
newObject->SetRange(wxRichTextRange(pos, GetRange().GetEnd()));
6780
GetRange().SetEnd(pos-1);
6786
void wxRichTextPlainText::CalculateRange(long start, long& end)
6788
end = start + m_text.length() - 1;
6789
m_range.SetRange(start, end);
6793
bool wxRichTextPlainText::DeleteRange(const wxRichTextRange& range)
6795
wxRichTextRange r = range;
6797
r.LimitTo(GetRange());
6799
if (r.GetStart() == GetRange().GetStart() && r.GetEnd() == GetRange().GetEnd())
6805
long startIndex = r.GetStart() - GetRange().GetStart();
6806
long len = r.GetLength();
6808
m_text = m_text.Mid(0, startIndex) + m_text.Mid(startIndex+len);
6812
/// Get text for the given range.
6813
wxString wxRichTextPlainText::GetTextForRange(const wxRichTextRange& range) const
6815
wxRichTextRange r = range;
6817
r.LimitTo(GetRange());
6819
long startIndex = r.GetStart() - GetRange().GetStart();
6820
long len = r.GetLength();
6822
return m_text.Mid(startIndex, len);
6825
/// Returns true if this object can merge itself with the given one.
6826
bool wxRichTextPlainText::CanMerge(wxRichTextObject* object) const
6828
return object->GetClassInfo() == wxCLASSINFO(wxRichTextPlainText) &&
6829
(m_text.empty() || (wxTextAttrEq(GetAttributes(), object->GetAttributes()) && m_properties == object->GetProperties()));
6832
/// Returns true if this object merged itself with the given one.
6833
/// The calling code will then delete the given object.
6834
bool wxRichTextPlainText::Merge(wxRichTextObject* object)
6836
wxRichTextPlainText* textObject = wxDynamicCast(object, wxRichTextPlainText);
6837
wxASSERT( textObject != NULL );
6841
m_text += textObject->GetText();
6842
wxRichTextApplyStyle(m_attributes, textObject->GetAttributes());
6849
/// Dump to output stream for debugging
6850
void wxRichTextPlainText::Dump(wxTextOutputStream& stream)
6852
wxRichTextObject::Dump(stream);
6853
stream << m_text << wxT("\n");
6856
/// Get the first position from pos that has a line break character.
6857
long wxRichTextPlainText::GetFirstLineBreakPosition(long pos)
6860
int len = m_text.length();
6861
int startPos = pos - m_range.GetStart();
6862
for (i = startPos; i < len; i++)
6864
wxChar ch = m_text[i];
6865
if (ch == wxRichTextLineBreakChar)
6867
return i + m_range.GetStart();
6875
* This is a kind of box, used to represent the whole buffer
6878
IMPLEMENT_DYNAMIC_CLASS(wxRichTextBuffer, wxRichTextParagraphLayoutBox)
6880
wxList wxRichTextBuffer::sm_handlers;
6881
wxList wxRichTextBuffer::sm_drawingHandlers;
6882
wxRichTextFieldTypeHashMap wxRichTextBuffer::sm_fieldTypes;
6883
wxRichTextRenderer* wxRichTextBuffer::sm_renderer = NULL;
6884
int wxRichTextBuffer::sm_bulletRightMargin = 20;
6885
float wxRichTextBuffer::sm_bulletProportion = (float) 0.3;
6888
void wxRichTextBuffer::Init()
6890
m_commandProcessor = new wxCommandProcessor;
6891
m_styleSheet = NULL;
6893
m_batchedCommandDepth = 0;
6894
m_batchedCommand = NULL;
6898
m_dimensionScale = 1.0;
6904
wxRichTextBuffer::~wxRichTextBuffer()
6906
delete m_commandProcessor;
6907
delete m_batchedCommand;
6910
ClearEventHandlers();
6913
void wxRichTextBuffer::ResetAndClearCommands()
6917
GetCommandProcessor()->ClearCommands();
6920
Invalidate(wxRICHTEXT_ALL);
6923
void wxRichTextBuffer::Copy(const wxRichTextBuffer& obj)
6925
wxRichTextParagraphLayoutBox::Copy(obj);
6927
m_styleSheet = obj.m_styleSheet;
6928
m_modified = obj.m_modified;
6929
m_batchedCommandDepth = 0;
6930
if (m_batchedCommand)
6931
delete m_batchedCommand;
6932
m_batchedCommand = NULL;
6933
m_suppressUndo = obj.m_suppressUndo;
6934
m_invalidRange = obj.m_invalidRange;
6935
m_dimensionScale = obj.m_dimensionScale;
6936
m_fontScale = obj.m_fontScale;
6939
/// Push style sheet to top of stack
6940
bool wxRichTextBuffer::PushStyleSheet(wxRichTextStyleSheet* styleSheet)
6943
styleSheet->InsertSheet(m_styleSheet);
6945
SetStyleSheet(styleSheet);
6950
/// Pop style sheet from top of stack
6951
wxRichTextStyleSheet* wxRichTextBuffer::PopStyleSheet()
6955
wxRichTextStyleSheet* oldSheet = m_styleSheet;
6956
m_styleSheet = oldSheet->GetNextSheet();
6965
/// Submit command to insert paragraphs
6966
bool wxRichTextBuffer::InsertParagraphsWithUndo(long pos, const wxRichTextParagraphLayoutBox& paragraphs, wxRichTextCtrl* ctrl, int flags)
6968
return ctrl->GetFocusObject()->InsertParagraphsWithUndo(this, pos, paragraphs, ctrl, flags);
6971
/// Submit command to insert paragraphs
6972
bool wxRichTextParagraphLayoutBox::InsertParagraphsWithUndo(wxRichTextBuffer* buffer, long pos, const wxRichTextParagraphLayoutBox& paragraphs, wxRichTextCtrl* ctrl, int WXUNUSED(flags))
6974
wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
6976
action->GetNewParagraphs() = paragraphs;
6978
action->SetPosition(pos);
6980
wxRichTextRange range = wxRichTextRange(pos, pos + paragraphs.GetOwnRange().GetEnd() - 1);
6981
if (!paragraphs.GetPartialParagraph())
6982
range.SetEnd(range.GetEnd()+1);
6984
// Set the range we'll need to delete in Undo
6985
action->SetRange(range);
6987
buffer->SubmitAction(action);
6992
/// Submit command to insert the given text
6993
bool wxRichTextBuffer::InsertTextWithUndo(long pos, const wxString& text, wxRichTextCtrl* ctrl, int flags)
6995
return ctrl->GetFocusObject()->InsertTextWithUndo(this, pos, text, ctrl, flags);
6998
/// Submit command to insert the given text
6999
bool wxRichTextParagraphLayoutBox::InsertTextWithUndo(wxRichTextBuffer* buffer, long pos, const wxString& text, wxRichTextCtrl* ctrl, int flags)
7001
wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
7003
wxRichTextAttr* p = NULL;
7004
wxRichTextAttr paraAttr;
7005
if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7007
// Get appropriate paragraph style
7008
paraAttr = GetStyleForNewParagraph(buffer, pos, false, false);
7009
if (!paraAttr.IsDefault())
7013
action->GetNewParagraphs().AddParagraphs(text, p);
7015
int length = action->GetNewParagraphs().GetOwnRange().GetLength();
7017
if (!text.empty() && text.Last() != wxT('\n'))
7019
// Don't count the newline when undoing
7021
action->GetNewParagraphs().SetPartialParagraph(true);
7023
else if (!text.empty() && text.Last() == wxT('\n'))
7026
action->SetPosition(pos);
7028
// Set the range we'll need to delete in Undo
7029
action->SetRange(wxRichTextRange(pos, pos + length - 1));
7031
buffer->SubmitAction(action);
7036
/// Submit command to insert the given text
7037
bool wxRichTextBuffer::InsertNewlineWithUndo(long pos, wxRichTextCtrl* ctrl, int flags)
7039
return ctrl->GetFocusObject()->InsertNewlineWithUndo(this, pos, ctrl, flags);
7042
/// Submit command to insert the given text
7043
bool wxRichTextParagraphLayoutBox::InsertNewlineWithUndo(wxRichTextBuffer* buffer, long pos, wxRichTextCtrl* ctrl, int flags)
7045
wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
7047
wxRichTextAttr* p = NULL;
7048
wxRichTextAttr paraAttr;
7049
if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7051
paraAttr = GetStyleForNewParagraph(buffer, pos, false, true /* look for next paragraph style */);
7052
if (!paraAttr.IsDefault())
7056
wxRichTextAttr attr(buffer->GetDefaultStyle());
7057
// Don't include box attributes such as margins
7058
attr.GetTextBoxAttr().Reset();
7060
wxRichTextParagraph* newPara = new wxRichTextParagraph(wxEmptyString, this, & attr);
7061
action->GetNewParagraphs().AppendChild(newPara);
7062
action->GetNewParagraphs().UpdateRanges();
7063
action->GetNewParagraphs().SetPartialParagraph(false);
7064
wxRichTextParagraph* para = GetParagraphAtPosition(pos, false);
7068
newPara->SetAttributes(*p);
7070
if (flags & wxRICHTEXT_INSERT_INTERACTIVE)
7072
if (para && para->GetRange().GetEnd() == pos)
7075
// Now see if we need to number the paragraph.
7076
if (newPara->GetAttributes().HasBulletNumber())
7078
wxRichTextAttr numberingAttr;
7079
if (FindNextParagraphNumber(para, numberingAttr))
7080
wxRichTextApplyStyle(newPara->GetAttributes(), (const wxRichTextAttr&) numberingAttr);
7084
action->SetPosition(pos);
7086
// Use the default character style
7087
if (!buffer->GetDefaultStyle().IsDefault() && newPara->GetChildren().GetFirst())
7089
// Check whether the default style merely reflects the paragraph/basic style,
7090
// in which case don't apply it.
7091
wxRichTextAttr defaultStyle(buffer->GetDefaultStyle());
7092
defaultStyle.GetTextBoxAttr().Reset();
7093
wxRichTextAttr toApply;
7096
wxRichTextAttr combinedAttr = para->GetCombinedAttributes(true /* include box attributes */);
7097
wxRichTextAttr newAttr;
7098
// This filters out attributes that are accounted for by the current
7099
// paragraph/basic style
7100
wxRichTextApplyStyle(toApply, defaultStyle, & combinedAttr);
7103
toApply = defaultStyle;
7105
if (!toApply.IsDefault())
7106
newPara->GetChildren().GetFirst()->GetData()->SetAttributes(toApply);
7109
// Set the range we'll need to delete in Undo
7110
action->SetRange(wxRichTextRange(pos1, pos1));
7112
buffer->SubmitAction(action);
7117
/// Submit command to insert the given image
7118
bool wxRichTextBuffer::InsertImageWithUndo(long pos, const wxRichTextImageBlock& imageBlock, wxRichTextCtrl* ctrl, int flags,
7119
const wxRichTextAttr& textAttr)
7121
return ctrl->GetFocusObject()->InsertImageWithUndo(this, pos, imageBlock, ctrl, flags, textAttr);
7124
/// Submit command to insert the given image
7125
bool wxRichTextParagraphLayoutBox::InsertImageWithUndo(wxRichTextBuffer* buffer, long pos, const wxRichTextImageBlock& imageBlock,
7126
wxRichTextCtrl* ctrl, int flags,
7127
const wxRichTextAttr& textAttr)
7129
wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Image"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
7131
wxRichTextAttr* p = NULL;
7132
wxRichTextAttr paraAttr;
7133
if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7135
paraAttr = GetStyleForNewParagraph(buffer, pos);
7136
if (!paraAttr.IsDefault())
7140
wxRichTextAttr attr(buffer->GetDefaultStyle());
7142
// Don't include box attributes such as margins
7143
attr.GetTextBoxAttr().Reset();
7145
wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
7147
newPara->SetAttributes(*p);
7149
wxRichTextImage* imageObject = new wxRichTextImage(imageBlock, newPara);
7150
newPara->AppendChild(imageObject);
7151
imageObject->SetAttributes(textAttr);
7152
action->GetNewParagraphs().AppendChild(newPara);
7153
action->GetNewParagraphs().UpdateRanges();
7155
action->GetNewParagraphs().SetPartialParagraph(true);
7157
action->SetPosition(pos);
7159
// Set the range we'll need to delete in Undo
7160
action->SetRange(wxRichTextRange(pos, pos));
7162
buffer->SubmitAction(action);
7167
// Insert an object with no change of it
7168
wxRichTextObject* wxRichTextBuffer::InsertObjectWithUndo(long pos, wxRichTextObject *object, wxRichTextCtrl* ctrl, int flags)
7170
return ctrl->GetFocusObject()->InsertObjectWithUndo(this, pos, object, ctrl, flags);
7173
// Insert an object with no change of it
7174
wxRichTextObject* wxRichTextParagraphLayoutBox::InsertObjectWithUndo(wxRichTextBuffer* buffer, long pos, wxRichTextObject *object, wxRichTextCtrl* ctrl, int flags)
7176
wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Object"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
7178
wxRichTextAttr* p = NULL;
7179
wxRichTextAttr paraAttr;
7180
if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7182
paraAttr = GetStyleForNewParagraph(buffer, pos);
7183
if (!paraAttr.IsDefault())
7187
wxRichTextAttr attr(buffer->GetDefaultStyle());
7189
// Don't include box attributes such as margins
7190
attr.GetTextBoxAttr().Reset();
7192
wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
7194
newPara->SetAttributes(*p);
7196
newPara->AppendChild(object);
7197
action->GetNewParagraphs().AppendChild(newPara);
7198
action->GetNewParagraphs().UpdateRanges();
7200
action->GetNewParagraphs().SetPartialParagraph(true);
7202
action->SetPosition(pos);
7204
// Set the range we'll need to delete in Undo
7205
action->SetRange(wxRichTextRange(pos, pos));
7207
buffer->SubmitAction(action);
7209
wxRichTextObject* obj = GetLeafObjectAtPosition(pos);
7213
wxRichTextField* wxRichTextParagraphLayoutBox::InsertFieldWithUndo(wxRichTextBuffer* buffer, long pos, const wxString& fieldType,
7214
const wxRichTextProperties& properties,
7215
wxRichTextCtrl* ctrl, int flags,
7216
const wxRichTextAttr& textAttr)
7218
wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Field"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
7220
wxRichTextAttr* p = NULL;
7221
wxRichTextAttr paraAttr;
7222
if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7224
paraAttr = GetStyleForNewParagraph(buffer, pos);
7225
if (!paraAttr.IsDefault())
7229
wxRichTextAttr attr(buffer->GetDefaultStyle());
7231
// Don't include box attributes such as margins
7232
attr.GetTextBoxAttr().Reset();
7234
wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
7236
newPara->SetAttributes(*p);
7238
wxRichTextField* fieldObject = new wxRichTextField();
7239
fieldObject->wxRichTextObject::SetProperties(properties);
7240
fieldObject->SetFieldType(fieldType);
7241
fieldObject->SetAttributes(textAttr);
7242
newPara->AppendChild(fieldObject);
7243
action->GetNewParagraphs().AppendChild(newPara);
7244
action->GetNewParagraphs().UpdateRanges();
7245
action->GetNewParagraphs().SetPartialParagraph(true);
7246
action->SetPosition(pos);
7248
// Set the range we'll need to delete in Undo
7249
action->SetRange(wxRichTextRange(pos, pos));
7251
buffer->SubmitAction(action);
7253
wxRichTextField* obj = wxDynamicCast(GetLeafObjectAtPosition(pos), wxRichTextField);
7257
/// Get the style that is appropriate for a new paragraph at this position.
7258
/// If the previous paragraph has a paragraph style name, look up the next-paragraph
7260
wxRichTextAttr wxRichTextParagraphLayoutBox::GetStyleForNewParagraph(wxRichTextBuffer* buffer, long pos, bool caretPosition, bool lookUpNewParaStyle) const
7262
wxRichTextParagraph* para = GetParagraphAtPosition(pos, caretPosition);
7265
wxRichTextAttr attr;
7266
bool foundAttributes = false;
7268
// Look for a matching paragraph style
7269
if (lookUpNewParaStyle && !para->GetAttributes().GetParagraphStyleName().IsEmpty() && buffer->GetStyleSheet())
7271
wxRichTextParagraphStyleDefinition* paraDef = buffer->GetStyleSheet()->FindParagraphStyle(para->GetAttributes().GetParagraphStyleName());
7274
// If we're not at the end of the paragraph, then we apply THIS style, and not the designated next style.
7275
if (para->GetRange().GetEnd() == pos && !paraDef->GetNextStyle().IsEmpty())
7277
wxRichTextParagraphStyleDefinition* nextParaDef = buffer->GetStyleSheet()->FindParagraphStyle(paraDef->GetNextStyle());
7280
foundAttributes = true;
7281
attr = nextParaDef->GetStyleMergedWithBase(buffer->GetStyleSheet());
7285
// If we didn't find the 'next style', use this style instead.
7286
if (!foundAttributes)
7288
foundAttributes = true;
7289
attr = paraDef->GetStyleMergedWithBase(buffer->GetStyleSheet());
7294
// Also apply list style if present
7295
if (lookUpNewParaStyle && !para->GetAttributes().GetListStyleName().IsEmpty() && buffer->GetStyleSheet())
7297
wxRichTextListStyleDefinition* listDef = buffer->GetStyleSheet()->FindListStyle(para->GetAttributes().GetListStyleName());
7300
int thisIndent = para->GetAttributes().GetLeftIndent();
7301
int thisLevel = para->GetAttributes().HasOutlineLevel() ? para->GetAttributes().GetOutlineLevel() : listDef->FindLevelForIndent(thisIndent);
7303
// Apply the overall list style, and item style for this level
7304
wxRichTextAttr listStyle(listDef->GetCombinedStyleForLevel(thisLevel, buffer->GetStyleSheet()));
7305
wxRichTextApplyStyle(attr, listStyle);
7306
attr.SetOutlineLevel(thisLevel);
7307
if (para->GetAttributes().HasBulletNumber())
7308
attr.SetBulletNumber(para->GetAttributes().GetBulletNumber());
7312
if (!foundAttributes)
7314
attr = para->GetAttributes();
7315
int flags = attr.GetFlags();
7317
// Eliminate character styles
7318
flags &= ( (~ wxTEXT_ATTR_FONT) |
7319
(~ wxTEXT_ATTR_TEXT_COLOUR) |
7320
(~ wxTEXT_ATTR_BACKGROUND_COLOUR) );
7321
attr.SetFlags(flags);
7327
return wxRichTextAttr();
7330
/// Submit command to delete this range
7331
bool wxRichTextBuffer::DeleteRangeWithUndo(const wxRichTextRange& range, wxRichTextCtrl* ctrl)
7333
return ctrl->GetFocusObject()->DeleteRangeWithUndo(range, ctrl, this);
7336
/// Submit command to delete this range
7337
bool wxRichTextParagraphLayoutBox::DeleteRangeWithUndo(const wxRichTextRange& range, wxRichTextCtrl* ctrl, wxRichTextBuffer* buffer)
7339
wxRichTextAction* action = new wxRichTextAction(NULL, _("Delete"), wxRICHTEXT_DELETE, buffer, this, ctrl);
7341
action->SetPosition(ctrl->GetCaretPosition());
7343
// Set the range to delete
7344
action->SetRange(range);
7346
// Copy the fragment that we'll need to restore in Undo
7347
CopyFragment(range, action->GetOldParagraphs());
7349
// See if we're deleting a paragraph marker, in which case we need to
7350
// make a note not to copy the attributes from the 2nd paragraph to the 1st.
7351
if (range.GetStart() == range.GetEnd())
7353
wxRichTextParagraph* para = GetParagraphAtPosition(range.GetStart());
7354
if (para && para->GetRange().GetEnd() == range.GetEnd())
7356
wxRichTextParagraph* nextPara = GetParagraphAtPosition(range.GetStart()+1);
7357
if (nextPara && nextPara != para)
7359
action->GetOldParagraphs().GetChildren().GetFirst()->GetData()->SetAttributes(nextPara->GetAttributes());
7360
action->GetOldParagraphs().GetAttributes().SetFlags(action->GetOldParagraphs().GetAttributes().GetFlags() | wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE);
7365
buffer->SubmitAction(action);
7370
/// Collapse undo/redo commands
7371
bool wxRichTextBuffer::BeginBatchUndo(const wxString& cmdName)
7373
if (m_batchedCommandDepth == 0)
7375
wxASSERT(m_batchedCommand == NULL);
7376
if (m_batchedCommand)
7378
GetCommandProcessor()->Store(m_batchedCommand);
7380
m_batchedCommand = new wxRichTextCommand(cmdName);
7383
m_batchedCommandDepth ++;
7388
/// Collapse undo/redo commands
7389
bool wxRichTextBuffer::EndBatchUndo()
7391
m_batchedCommandDepth --;
7393
wxASSERT(m_batchedCommandDepth >= 0);
7394
wxASSERT(m_batchedCommand != NULL);
7396
if (m_batchedCommandDepth == 0)
7398
GetCommandProcessor()->Store(m_batchedCommand);
7399
m_batchedCommand = NULL;
7405
/// Submit immediately, or delay according to whether collapsing is on
7406
bool wxRichTextBuffer::SubmitAction(wxRichTextAction* action)
7408
if (action && !action->GetNewParagraphs().IsEmpty())
7409
PrepareContent(action->GetNewParagraphs());
7411
if (BatchingUndo() && m_batchedCommand && !SuppressingUndo())
7413
wxRichTextCommand* cmd = new wxRichTextCommand(action->GetName());
7414
cmd->AddAction(action);
7416
cmd->GetActions().Clear();
7419
m_batchedCommand->AddAction(action);
7423
wxRichTextCommand* cmd = new wxRichTextCommand(action->GetName());
7424
cmd->AddAction(action);
7426
// Only store it if we're not suppressing undo.
7427
return GetCommandProcessor()->Submit(cmd, !SuppressingUndo());
7433
/// Begin suppressing undo/redo commands.
7434
bool wxRichTextBuffer::BeginSuppressUndo()
7441
/// End suppressing undo/redo commands.
7442
bool wxRichTextBuffer::EndSuppressUndo()
7449
/// Begin using a style
7450
bool wxRichTextBuffer::BeginStyle(const wxRichTextAttr& style)
7452
wxRichTextAttr newStyle(GetDefaultStyle());
7453
newStyle.GetTextBoxAttr().Reset();
7455
// Save the old default style
7456
m_attributeStack.Append((wxObject*) new wxRichTextAttr(newStyle));
7458
wxRichTextApplyStyle(newStyle, style);
7459
newStyle.SetFlags(style.GetFlags()|newStyle.GetFlags());
7461
SetDefaultStyle(newStyle);
7467
bool wxRichTextBuffer::EndStyle()
7469
if (!m_attributeStack.GetFirst())
7471
wxLogDebug(_("Too many EndStyle calls!"));
7475
wxList::compatibility_iterator node = m_attributeStack.GetLast();
7476
wxRichTextAttr* attr = (wxRichTextAttr*)node->GetData();
7477
m_attributeStack.Erase(node);
7479
SetDefaultStyle(*attr);
7486
bool wxRichTextBuffer::EndAllStyles()
7488
while (m_attributeStack.GetCount() != 0)
7493
/// Clear the style stack
7494
void wxRichTextBuffer::ClearStyleStack()
7496
for (wxList::compatibility_iterator node = m_attributeStack.GetFirst(); node; node = node->GetNext())
7497
delete (wxRichTextAttr*) node->GetData();
7498
m_attributeStack.Clear();
7501
/// Begin using bold
7502
bool wxRichTextBuffer::BeginBold()
7504
wxRichTextAttr attr;
7505
attr.SetFontWeight(wxFONTWEIGHT_BOLD);
7507
return BeginStyle(attr);
7510
/// Begin using italic
7511
bool wxRichTextBuffer::BeginItalic()
7513
wxRichTextAttr attr;
7514
attr.SetFontStyle(wxFONTSTYLE_ITALIC);
7516
return BeginStyle(attr);
7519
/// Begin using underline
7520
bool wxRichTextBuffer::BeginUnderline()
7522
wxRichTextAttr attr;
7523
attr.SetFontUnderlined(true);
7525
return BeginStyle(attr);
7528
/// Begin using point size
7529
bool wxRichTextBuffer::BeginFontSize(int pointSize)
7531
wxRichTextAttr attr;
7532
attr.SetFontSize(pointSize);
7534
return BeginStyle(attr);
7537
/// Begin using this font
7538
bool wxRichTextBuffer::BeginFont(const wxFont& font)
7540
wxRichTextAttr attr;
7543
return BeginStyle(attr);
7546
/// Begin using this colour
7547
bool wxRichTextBuffer::BeginTextColour(const wxColour& colour)
7549
wxRichTextAttr attr;
7550
attr.SetFlags(wxTEXT_ATTR_TEXT_COLOUR);
7551
attr.SetTextColour(colour);
7553
return BeginStyle(attr);
7556
/// Begin using alignment
7557
bool wxRichTextBuffer::BeginAlignment(wxTextAttrAlignment alignment)
7559
wxRichTextAttr attr;
7560
attr.SetFlags(wxTEXT_ATTR_ALIGNMENT);
7561
attr.SetAlignment(alignment);
7563
return BeginStyle(attr);
7566
/// Begin left indent
7567
bool wxRichTextBuffer::BeginLeftIndent(int leftIndent, int leftSubIndent)
7569
wxRichTextAttr attr;
7570
attr.SetFlags(wxTEXT_ATTR_LEFT_INDENT);
7571
attr.SetLeftIndent(leftIndent, leftSubIndent);
7573
return BeginStyle(attr);
7576
/// Begin right indent
7577
bool wxRichTextBuffer::BeginRightIndent(int rightIndent)
7579
wxRichTextAttr attr;
7580
attr.SetFlags(wxTEXT_ATTR_RIGHT_INDENT);
7581
attr.SetRightIndent(rightIndent);
7583
return BeginStyle(attr);
7586
/// Begin paragraph spacing
7587
bool wxRichTextBuffer::BeginParagraphSpacing(int before, int after)
7591
flags |= wxTEXT_ATTR_PARA_SPACING_BEFORE;
7593
flags |= wxTEXT_ATTR_PARA_SPACING_AFTER;
7595
wxRichTextAttr attr;
7596
attr.SetFlags(flags);
7597
attr.SetParagraphSpacingBefore(before);
7598
attr.SetParagraphSpacingAfter(after);
7600
return BeginStyle(attr);
7603
/// Begin line spacing
7604
bool wxRichTextBuffer::BeginLineSpacing(int lineSpacing)
7606
wxRichTextAttr attr;
7607
attr.SetFlags(wxTEXT_ATTR_LINE_SPACING);
7608
attr.SetLineSpacing(lineSpacing);
7610
return BeginStyle(attr);
7613
/// Begin numbered bullet
7614
bool wxRichTextBuffer::BeginNumberedBullet(int bulletNumber, int leftIndent, int leftSubIndent, int bulletStyle)
7616
wxRichTextAttr attr;
7617
attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
7618
attr.SetBulletStyle(bulletStyle);
7619
attr.SetBulletNumber(bulletNumber);
7620
attr.SetLeftIndent(leftIndent, leftSubIndent);
7622
return BeginStyle(attr);
7625
/// Begin symbol bullet
7626
bool wxRichTextBuffer::BeginSymbolBullet(const wxString& symbol, int leftIndent, int leftSubIndent, int bulletStyle)
7628
wxRichTextAttr attr;
7629
attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
7630
attr.SetBulletStyle(bulletStyle);
7631
attr.SetLeftIndent(leftIndent, leftSubIndent);
7632
attr.SetBulletText(symbol);
7634
return BeginStyle(attr);
7637
/// Begin standard bullet
7638
bool wxRichTextBuffer::BeginStandardBullet(const wxString& bulletName, int leftIndent, int leftSubIndent, int bulletStyle)
7640
wxRichTextAttr attr;
7641
attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
7642
attr.SetBulletStyle(bulletStyle);
7643
attr.SetLeftIndent(leftIndent, leftSubIndent);
7644
attr.SetBulletName(bulletName);
7646
return BeginStyle(attr);
7649
/// Begin named character style
7650
bool wxRichTextBuffer::BeginCharacterStyle(const wxString& characterStyle)
7652
if (GetStyleSheet())
7654
wxRichTextCharacterStyleDefinition* def = GetStyleSheet()->FindCharacterStyle(characterStyle);
7657
wxRichTextAttr attr = def->GetStyleMergedWithBase(GetStyleSheet());
7658
return BeginStyle(attr);
7664
/// Begin named paragraph style
7665
bool wxRichTextBuffer::BeginParagraphStyle(const wxString& paragraphStyle)
7667
if (GetStyleSheet())
7669
wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(paragraphStyle);
7672
wxRichTextAttr attr = def->GetStyleMergedWithBase(GetStyleSheet());
7673
return BeginStyle(attr);
7679
/// Begin named list style
7680
bool wxRichTextBuffer::BeginListStyle(const wxString& listStyle, int level, int number)
7682
if (GetStyleSheet())
7684
wxRichTextListStyleDefinition* def = GetStyleSheet()->FindListStyle(listStyle);
7687
wxRichTextAttr attr(def->GetCombinedStyleForLevel(level));
7689
attr.SetBulletNumber(number);
7691
return BeginStyle(attr);
7698
bool wxRichTextBuffer::BeginURL(const wxString& url, const wxString& characterStyle)
7700
wxRichTextAttr attr;
7702
if (!characterStyle.IsEmpty() && GetStyleSheet())
7704
wxRichTextCharacterStyleDefinition* def = GetStyleSheet()->FindCharacterStyle(characterStyle);
7707
attr = def->GetStyleMergedWithBase(GetStyleSheet());
7712
return BeginStyle(attr);
7715
/// Adds a handler to the end
7716
void wxRichTextBuffer::AddHandler(wxRichTextFileHandler *handler)
7718
sm_handlers.Append(handler);
7721
/// Inserts a handler at the front
7722
void wxRichTextBuffer::InsertHandler(wxRichTextFileHandler *handler)
7724
sm_handlers.Insert( handler );
7727
/// Removes a handler
7728
bool wxRichTextBuffer::RemoveHandler(const wxString& name)
7730
wxRichTextFileHandler *handler = FindHandler(name);
7733
sm_handlers.DeleteObject(handler);
7741
/// Finds a handler by filename or, if supplied, type
7742
wxRichTextFileHandler *wxRichTextBuffer::FindHandlerFilenameOrType(const wxString& filename,
7743
wxRichTextFileType imageType)
7745
if (imageType != wxRICHTEXT_TYPE_ANY)
7746
return FindHandler(imageType);
7747
else if (!filename.IsEmpty())
7749
wxString path, file, ext;
7750
wxFileName::SplitPath(filename, & path, & file, & ext);
7751
return FindHandler(ext, imageType);
7758
/// Finds a handler by name
7759
wxRichTextFileHandler* wxRichTextBuffer::FindHandler(const wxString& name)
7761
wxList::compatibility_iterator node = sm_handlers.GetFirst();
7764
wxRichTextFileHandler *handler = (wxRichTextFileHandler*)node->GetData();
7765
if (handler->GetName().Lower() == name.Lower()) return handler;
7767
node = node->GetNext();
7772
/// Finds a handler by extension and type
7773
wxRichTextFileHandler* wxRichTextBuffer::FindHandler(const wxString& extension, wxRichTextFileType type)
7775
wxList::compatibility_iterator node = sm_handlers.GetFirst();
7778
wxRichTextFileHandler *handler = (wxRichTextFileHandler*)node->GetData();
7779
if ( handler->GetExtension().Lower() == extension.Lower() &&
7780
(type == wxRICHTEXT_TYPE_ANY || handler->GetType() == type) )
7782
node = node->GetNext();
7787
/// Finds a handler by type
7788
wxRichTextFileHandler* wxRichTextBuffer::FindHandler(wxRichTextFileType type)
7790
wxList::compatibility_iterator node = sm_handlers.GetFirst();
7793
wxRichTextFileHandler *handler = (wxRichTextFileHandler *)node->GetData();
7794
if (handler->GetType() == type) return handler;
7795
node = node->GetNext();
7800
void wxRichTextBuffer::InitStandardHandlers()
7802
if (!FindHandler(wxRICHTEXT_TYPE_TEXT))
7803
AddHandler(new wxRichTextPlainTextHandler);
7806
void wxRichTextBuffer::CleanUpHandlers()
7808
wxList::compatibility_iterator node = sm_handlers.GetFirst();
7811
wxRichTextFileHandler* handler = (wxRichTextFileHandler*)node->GetData();
7812
wxList::compatibility_iterator next = node->GetNext();
7817
sm_handlers.Clear();
7820
wxString wxRichTextBuffer::GetExtWildcard(bool combine, bool save, wxArrayInt* types)
7827
wxList::compatibility_iterator node = GetHandlers().GetFirst();
7831
wxRichTextFileHandler* handler = (wxRichTextFileHandler*) node->GetData();
7832
if (handler->IsVisible() && ((save && handler->CanSave()) || (!save && handler->CanLoad())))
7837
wildcard += wxT(";");
7838
wildcard += wxT("*.") + handler->GetExtension();
7843
wildcard += wxT("|");
7844
wildcard += handler->GetName();
7845
wildcard += wxT(" ");
7846
wildcard += _("files");
7847
wildcard += wxT(" (*.");
7848
wildcard += handler->GetExtension();
7849
wildcard += wxT(")|*.");
7850
wildcard += handler->GetExtension();
7852
types->Add(handler->GetType());
7857
node = node->GetNext();
7861
wildcard = wxT("(") + wildcard + wxT(")|") + wildcard;
7866
bool wxRichTextBuffer::LoadFile(const wxString& filename, wxRichTextFileType type)
7868
wxRichTextFileHandler* handler = FindHandlerFilenameOrType(filename, type);
7871
SetDefaultStyle(wxRichTextAttr());
7872
handler->SetFlags(GetHandlerFlags());
7873
bool success = handler->LoadFile(this, filename);
7874
Invalidate(wxRICHTEXT_ALL);
7882
bool wxRichTextBuffer::SaveFile(const wxString& filename, wxRichTextFileType type)
7884
wxRichTextFileHandler* handler = FindHandlerFilenameOrType(filename, type);
7887
handler->SetFlags(GetHandlerFlags());
7888
return handler->SaveFile(this, filename);
7894
/// Load from a stream
7895
bool wxRichTextBuffer::LoadFile(wxInputStream& stream, wxRichTextFileType type)
7897
wxRichTextFileHandler* handler = FindHandler(type);
7900
SetDefaultStyle(wxRichTextAttr());
7901
handler->SetFlags(GetHandlerFlags());
7902
bool success = handler->LoadFile(this, stream);
7903
Invalidate(wxRICHTEXT_ALL);
7910
/// Save to a stream
7911
bool wxRichTextBuffer::SaveFile(wxOutputStream& stream, wxRichTextFileType type)
7913
wxRichTextFileHandler* handler = FindHandler(type);
7916
handler->SetFlags(GetHandlerFlags());
7917
return handler->SaveFile(this, stream);
7923
/// Copy the range to the clipboard
7924
bool wxRichTextBuffer::CopyToClipboard(const wxRichTextRange& range)
7926
bool success = false;
7927
wxRichTextParagraphLayoutBox* container = this;
7928
if (GetRichTextCtrl())
7929
container = GetRichTextCtrl()->GetFocusObject();
7931
#if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
7933
if (!wxTheClipboard->IsOpened() && wxTheClipboard->Open())
7935
wxTheClipboard->Clear();
7937
// Add composite object
7939
wxDataObjectComposite* compositeObject = new wxDataObjectComposite();
7942
wxString text = container->GetTextForRange(range);
7945
text = wxTextFile::Translate(text, wxTextFileType_Dos);
7948
compositeObject->Add(new wxTextDataObject(text), false /* not preferred */);
7951
// Add rich text buffer data object. This needs the XML handler to be present.
7953
if (FindHandler(wxRICHTEXT_TYPE_XML))
7955
wxRichTextBuffer* richTextBuf = new wxRichTextBuffer;
7956
container->CopyFragment(range, *richTextBuf);
7958
compositeObject->Add(new wxRichTextBufferDataObject(richTextBuf), true /* preferred */);
7961
if (wxTheClipboard->SetData(compositeObject))
7964
wxTheClipboard->Close();
7973
/// Paste the clipboard content to the buffer
7974
bool wxRichTextBuffer::PasteFromClipboard(long position)
7976
bool success = false;
7977
wxRichTextParagraphLayoutBox* container = this;
7978
if (GetRichTextCtrl())
7979
container = GetRichTextCtrl()->GetFocusObject();
7981
#if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
7982
if (CanPasteFromClipboard())
7984
if (wxTheClipboard->Open())
7986
if (wxTheClipboard->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())))
7988
wxRichTextBufferDataObject data;
7989
wxTheClipboard->GetData(data);
7990
wxRichTextBuffer* richTextBuffer = data.GetRichTextBuffer();
7993
container->InsertParagraphsWithUndo(this, position+1, *richTextBuffer, GetRichTextCtrl(), 0);
7994
if (GetRichTextCtrl())
7995
GetRichTextCtrl()->ShowPosition(position + richTextBuffer->GetOwnRange().GetEnd());
7996
delete richTextBuffer;
7999
else if (wxTheClipboard->IsSupported(wxDF_TEXT)
8001
|| wxTheClipboard->IsSupported(wxDF_UNICODETEXT)
8005
wxTextDataObject data;
8006
wxTheClipboard->GetData(data);
8007
wxString text(data.GetText());
8010
text2.Alloc(text.Length()+1);
8012
for (i = 0; i < text.Length(); i++)
8014
wxChar ch = text[i];
8015
if (ch != wxT('\r'))
8019
wxString text2 = text;
8021
container->InsertTextWithUndo(this, position+1, text2, GetRichTextCtrl(), wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE);
8023
if (GetRichTextCtrl())
8024
GetRichTextCtrl()->ShowPosition(position + text2.Length());
8028
else if (wxTheClipboard->IsSupported(wxDF_BITMAP))
8030
wxBitmapDataObject data;
8031
wxTheClipboard->GetData(data);
8032
wxBitmap bitmap(data.GetBitmap());
8033
wxImage image(bitmap.ConvertToImage());
8035
wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Image"), wxRICHTEXT_INSERT, this, container, GetRichTextCtrl(), false);
8037
action->GetNewParagraphs().AddImage(image);
8039
if (action->GetNewParagraphs().GetChildCount() == 1)
8040
action->GetNewParagraphs().SetPartialParagraph(true);
8042
action->SetPosition(position+1);
8044
// Set the range we'll need to delete in Undo
8045
action->SetRange(wxRichTextRange(position+1, position+1));
8047
SubmitAction(action);
8051
wxTheClipboard->Close();
8055
wxUnusedVar(position);
8060
/// Can we paste from the clipboard?
8061
bool wxRichTextBuffer::CanPasteFromClipboard() const
8063
bool canPaste = false;
8064
#if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
8065
if (!wxTheClipboard->IsOpened() && wxTheClipboard->Open())
8067
if (wxTheClipboard->IsSupported(wxDF_TEXT)
8069
|| wxTheClipboard->IsSupported(wxDF_UNICODETEXT)
8071
|| wxTheClipboard->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())) ||
8072
wxTheClipboard->IsSupported(wxDF_BITMAP))
8076
wxTheClipboard->Close();
8082
/// Dumps contents of buffer for debugging purposes
8083
void wxRichTextBuffer::Dump()
8087
wxStringOutputStream stream(& text);
8088
wxTextOutputStream textStream(stream);
8095
/// Add an event handler
8096
bool wxRichTextBuffer::AddEventHandler(wxEvtHandler* handler)
8098
m_eventHandlers.Append(handler);
8102
/// Remove an event handler
8103
bool wxRichTextBuffer::RemoveEventHandler(wxEvtHandler* handler, bool deleteHandler)
8105
wxList::compatibility_iterator node = m_eventHandlers.Find(handler);
8108
m_eventHandlers.Erase(node);
8118
/// Clear event handlers
8119
void wxRichTextBuffer::ClearEventHandlers()
8121
m_eventHandlers.Clear();
8124
/// Send event to event handlers. If sendToAll is true, will send to all event handlers,
8125
/// otherwise will stop at the first successful one.
8126
bool wxRichTextBuffer::SendEvent(wxEvent& event, bool sendToAll)
8128
bool success = false;
8129
for (wxList::compatibility_iterator node = m_eventHandlers.GetFirst(); node; node = node->GetNext())
8131
wxEvtHandler* handler = (wxEvtHandler*) node->GetData();
8132
if (handler->ProcessEvent(event))
8142
/// Set style sheet and notify of the change
8143
bool wxRichTextBuffer::SetStyleSheetAndNotify(wxRichTextStyleSheet* sheet)
8145
wxRichTextStyleSheet* oldSheet = GetStyleSheet();
8147
wxWindowID winid = wxID_ANY;
8148
if (GetRichTextCtrl())
8149
winid = GetRichTextCtrl()->GetId();
8151
wxRichTextEvent event(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACING, winid);
8152
event.SetEventObject(GetRichTextCtrl());
8153
event.SetContainer(GetRichTextCtrl()->GetFocusObject());
8154
event.SetOldStyleSheet(oldSheet);
8155
event.SetNewStyleSheet(sheet);
8158
if (SendEvent(event) && !event.IsAllowed())
8160
if (sheet != oldSheet)
8166
if (oldSheet && oldSheet != sheet)
8169
SetStyleSheet(sheet);
8171
event.SetEventType(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACED);
8172
event.SetOldStyleSheet(NULL);
8175
return SendEvent(event);
8178
/// Set renderer, deleting old one
8179
void wxRichTextBuffer::SetRenderer(wxRichTextRenderer* renderer)
8183
sm_renderer = renderer;
8186
/// Hit-testing: returns a flag indicating hit test details, plus
8187
/// information about position
8188
int wxRichTextBuffer::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
8190
int ret = wxRichTextParagraphLayoutBox::HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
8191
if (ret != wxRICHTEXT_HITTEST_NONE)
8197
textPosition = m_ownRange.GetEnd()-1;
8200
return wxRICHTEXT_HITTEST_AFTER|wxRICHTEXT_HITTEST_OUTSIDE;
8204
void wxRichTextBuffer::SetFontScale(double fontScale)
8206
m_fontScale = fontScale;
8207
m_fontTable.SetFontScale(fontScale);
8210
void wxRichTextBuffer::SetDimensionScale(double dimScale)
8212
m_dimensionScale = dimScale;
8215
bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph* paragraph, wxDC& dc, const wxRichTextAttr& bulletAttr, const wxRect& rect)
8217
if (bulletAttr.GetTextColour().IsOk())
8219
wxCheckSetPen(dc, wxPen(bulletAttr.GetTextColour()));
8220
wxCheckSetBrush(dc, wxBrush(bulletAttr.GetTextColour()));
8224
wxCheckSetPen(dc, *wxBLACK_PEN);
8225
wxCheckSetBrush(dc, *wxBLACK_BRUSH);
8229
if (bulletAttr.HasFont())
8231
font = paragraph->GetBuffer()->GetFontTable().FindFont(bulletAttr);
8234
font = (*wxNORMAL_FONT);
8236
wxCheckSetFont(dc, font);
8238
int charHeight = dc.GetCharHeight();
8240
int bulletWidth = (int) (((float) charHeight) * wxRichTextBuffer::GetBulletProportion());
8241
int bulletHeight = bulletWidth;
8245
// Calculate the top position of the character (as opposed to the whole line height)
8246
int y = rect.y + (rect.height - charHeight);
8248
// Calculate where the bullet should be positioned
8249
y = y + (charHeight+1)/2 - (bulletHeight+1)/2;
8251
// The margin between a bullet and text.
8252
int margin = paragraph->ConvertTenthsMMToPixels(dc, wxRichTextBuffer::GetBulletRightMargin());
8254
if (bulletAttr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT)
8255
x = rect.x + rect.width - bulletWidth - margin;
8256
else if (bulletAttr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE)
8257
x = x + (rect.width)/2 - bulletWidth/2;
8259
if (bulletAttr.GetBulletName() == wxT("standard/square"))
8261
dc.DrawRectangle(x, y, bulletWidth, bulletHeight);
8263
else if (bulletAttr.GetBulletName() == wxT("standard/diamond"))
8266
pts[0].x = x; pts[0].y = y + bulletHeight/2;
8267
pts[1].x = x + bulletWidth/2; pts[1].y = y;
8268
pts[2].x = x + bulletWidth; pts[2].y = y + bulletHeight/2;
8269
pts[3].x = x + bulletWidth/2; pts[3].y = y + bulletHeight;
8271
dc.DrawPolygon(4, pts);
8273
else if (bulletAttr.GetBulletName() == wxT("standard/triangle"))
8276
pts[0].x = x; pts[0].y = y;
8277
pts[1].x = x + bulletWidth; pts[1].y = y + bulletHeight/2;
8278
pts[2].x = x; pts[2].y = y + bulletHeight;
8280
dc.DrawPolygon(3, pts);
8282
else if (bulletAttr.GetBulletName() == wxT("standard/circle-outline"))
8284
wxCheckSetBrush(dc, *wxTRANSPARENT_BRUSH);
8285
dc.DrawEllipse(x, y, bulletWidth, bulletHeight);
8287
else // "standard/circle", and catch-all
8289
dc.DrawEllipse(x, y, bulletWidth, bulletHeight);
8295
bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph* paragraph, wxDC& dc, const wxRichTextAttr& attr, const wxRect& rect, const wxString& text)
8300
if ((attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL) && !attr.GetBulletFont().IsEmpty() && attr.HasFont())
8302
wxRichTextAttr fontAttr;
8303
if (attr.HasFontPixelSize())
8304
fontAttr.SetFontPixelSize(attr.GetFontSize());
8306
fontAttr.SetFontPointSize(attr.GetFontSize());
8307
fontAttr.SetFontStyle(attr.GetFontStyle());
8308
fontAttr.SetFontWeight(attr.GetFontWeight());
8309
fontAttr.SetFontUnderlined(attr.GetFontUnderlined());
8310
fontAttr.SetFontFaceName(attr.GetBulletFont());
8311
font = paragraph->GetBuffer()->GetFontTable().FindFont(fontAttr);
8313
else if (attr.HasFont())
8314
font = paragraph->GetBuffer()->GetFontTable().FindFont(attr);
8316
font = (*wxNORMAL_FONT);
8318
wxCheckSetFont(dc, font);
8320
if (attr.GetTextColour().IsOk())
8321
dc.SetTextForeground(attr.GetTextColour());
8323
dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
8325
int charHeight = dc.GetCharHeight();
8327
dc.GetTextExtent(text, & tw, & th);
8331
// Calculate the top position of the character (as opposed to the whole line height)
8332
int y = rect.y + (rect.height - charHeight);
8334
// The margin between a bullet and text.
8335
int margin = paragraph->ConvertTenthsMMToPixels(dc, wxRichTextBuffer::GetBulletRightMargin());
8337
if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT)
8338
x = (rect.x + rect.width) - tw - margin;
8339
else if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE)
8340
x = x + (rect.width)/2 - tw/2;
8342
dc.DrawText(text, x, y);
8350
bool wxRichTextStdRenderer::DrawBitmapBullet(wxRichTextParagraph* WXUNUSED(paragraph), wxDC& WXUNUSED(dc), const wxRichTextAttr& WXUNUSED(attr), const wxRect& WXUNUSED(rect))
8352
// Currently unimplemented. The intention is to store bitmaps by name in a media store associated
8353
// with the buffer. The store will allow retrieval from memory, disk or other means.
8357
/// Enumerate the standard bullet names currently supported
8358
bool wxRichTextStdRenderer::EnumerateStandardBulletNames(wxArrayString& bulletNames)
8360
bulletNames.Add(wxTRANSLATE("standard/circle"));
8361
bulletNames.Add(wxTRANSLATE("standard/circle-outline"));
8362
bulletNames.Add(wxTRANSLATE("standard/square"));
8363
bulletNames.Add(wxTRANSLATE("standard/diamond"));
8364
bulletNames.Add(wxTRANSLATE("standard/triangle"));
8373
IMPLEMENT_DYNAMIC_CLASS(wxRichTextBox, wxRichTextParagraphLayoutBox)
8375
wxRichTextBox::wxRichTextBox(wxRichTextObject* parent):
8376
wxRichTextParagraphLayoutBox(parent)
8381
bool wxRichTextBox::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
8386
// TODO: if the active object in the control, draw an indication.
8387
// We need to add the concept of active object, and not just focus object,
8388
// so we can apply commands (properties, delete, ...) to objects such as text boxes and images.
8389
// Ultimately we would like to be able to interactively resize an active object
8390
// using drag handles.
8391
return wxRichTextParagraphLayoutBox::Draw(dc, context, range, selection, rect, descent, style);
8395
void wxRichTextBox::Copy(const wxRichTextBox& obj)
8397
wxRichTextParagraphLayoutBox::Copy(obj);
8400
// Edit properties via a GUI
8401
bool wxRichTextBox::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
8403
wxRichTextObjectPropertiesDialog boxDlg(this, wxGetTopLevelParent(parent), wxID_ANY, _("Box Properties"));
8404
boxDlg.SetAttributes(GetAttributes());
8406
if (boxDlg.ShowModal() == wxID_OK)
8408
// By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
8409
// indeterminate in the object.
8410
boxDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
8421
IMPLEMENT_DYNAMIC_CLASS(wxRichTextField, wxRichTextParagraphLayoutBox)
8423
wxRichTextField::wxRichTextField(const wxString& fieldType, wxRichTextObject* parent):
8424
wxRichTextParagraphLayoutBox(parent)
8426
SetFieldType(fieldType);
8430
bool wxRichTextField::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
8435
wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8436
if (fieldType && fieldType->Draw(this, dc, context, range, selection, rect, descent, style))
8439
// Fallback; but don't draw guidelines.
8440
style &= ~wxRICHTEXT_DRAW_GUIDELINES;
8441
return wxRichTextParagraphLayoutBox::Draw(dc, context, range, selection, rect, descent, style);
8444
bool wxRichTextField::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style)
8446
wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8447
if (fieldType && fieldType->Layout(this, dc, context, rect, parentRect, style))
8451
return wxRichTextParagraphLayoutBox::Layout(dc, context, rect, parentRect, style);
8454
bool wxRichTextField::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* partialExtents) const
8456
wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8458
return fieldType->GetRangeSize((wxRichTextField*) this, range, size, descent, dc, context, flags, position, partialExtents);
8460
return wxRichTextParagraphLayoutBox::GetRangeSize(range, size, descent, dc, context, flags, position, partialExtents);
8464
void wxRichTextField::CalculateRange(long start, long& end)
8467
wxRichTextParagraphLayoutBox::CalculateRange(start, end);
8469
wxRichTextObject::CalculateRange(start, end);
8473
void wxRichTextField::Copy(const wxRichTextField& obj)
8475
wxRichTextParagraphLayoutBox::Copy(obj);
8477
UpdateField(GetBuffer());
8480
// Edit properties via a GUI
8481
bool wxRichTextField::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
8483
wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8485
return fieldType->EditProperties(this, parent, buffer);
8490
bool wxRichTextField::CanEditProperties() const
8492
wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8494
return fieldType->CanEditProperties((wxRichTextField*) this);
8499
wxString wxRichTextField::GetPropertiesMenuLabel() const
8501
wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8503
return fieldType->GetPropertiesMenuLabel((wxRichTextField*) this);
8505
return wxEmptyString;
8508
bool wxRichTextField::UpdateField(wxRichTextBuffer* buffer)
8510
wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8512
return fieldType->UpdateField(buffer, (wxRichTextField*) this);
8517
bool wxRichTextField::IsTopLevel() const
8519
wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8521
return fieldType->IsTopLevel((wxRichTextField*) this);
8526
IMPLEMENT_CLASS(wxRichTextFieldType, wxObject)
8528
IMPLEMENT_CLASS(wxRichTextFieldTypeStandard, wxRichTextFieldType)
8530
wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString& name, const wxString& label, int displayStyle)
8536
SetDisplayStyle(displayStyle);
8539
wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString& name, const wxBitmap& bitmap, int displayStyle)
8545
SetDisplayStyle(displayStyle);
8548
void wxRichTextFieldTypeStandard::Init()
8550
m_displayStyle = wxRICHTEXT_FIELD_STYLE_RECTANGLE;
8551
m_font = wxFont(6, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
8552
m_textColour = *wxWHITE;
8553
m_borderColour = *wxBLACK;
8554
m_backgroundColour = *wxBLACK;
8555
m_verticalPadding = 1;
8556
m_horizontalPadding = 3;
8557
m_horizontalMargin = 2;
8558
m_verticalMargin = 0;
8561
void wxRichTextFieldTypeStandard::Copy(const wxRichTextFieldTypeStandard& field)
8563
wxRichTextFieldType::Copy(field);
8565
m_label = field.m_label;
8566
m_displayStyle = field.m_displayStyle;
8567
m_font = field.m_font;
8568
m_textColour = field.m_textColour;
8569
m_borderColour = field.m_borderColour;
8570
m_backgroundColour = field.m_backgroundColour;
8571
m_verticalPadding = field.m_verticalPadding;
8572
m_horizontalPadding = field.m_horizontalPadding;
8573
m_horizontalMargin = field.m_horizontalMargin;
8574
m_bitmap = field.m_bitmap;
8577
bool wxRichTextFieldTypeStandard::Draw(wxRichTextField* obj, wxDC& dc, wxRichTextDrawingContext& WXUNUSED(context), const wxRichTextRange& WXUNUSED(range), const wxRichTextSelection& selection, const wxRect& rect, int descent, int WXUNUSED(style))
8579
if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_COMPOSITE)
8580
return false; // USe default composite drawing
8581
else // if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_RECTANGLE || m_displayStyle == wxRICHTEXT_FIELD_STYLE_NOBORDER)
8585
wxPen borderPen(m_borderColour, 1, wxSOLID);
8586
wxBrush backgroundBrush(m_backgroundColour);
8587
wxColour textColour(m_textColour);
8589
if (selection.WithinSelection(obj->GetRange().GetStart(), obj))
8591
wxColour highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
8592
wxColour highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
8594
borderPen = wxPen(highlightTextColour, 1, wxSOLID);
8595
backgroundBrush = wxBrush(highlightColour);
8597
wxCheckSetBrush(dc, backgroundBrush);
8598
wxCheckSetPen(dc, wxPen(highlightColour, 1, wxSOLID));
8599
dc.DrawRectangle(rect);
8602
if (m_displayStyle != wxRICHTEXT_FIELD_STYLE_NO_BORDER)
8605
// objectRect is the area where the content is drawn, after margins around it have been taken into account
8606
wxRect objectRect = wxRect(wxPoint(rect.x + m_horizontalMargin, rect.y + wxMax(0, rect.height - descent - obj->GetCachedSize().y)),
8607
wxSize(obj->GetCachedSize().x - 2*m_horizontalMargin - borderSize, obj->GetCachedSize().y));
8609
// clientArea is where the text is actually written
8610
wxRect clientArea = objectRect;
8612
if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_RECTANGLE)
8614
dc.SetPen(borderPen);
8615
dc.SetBrush(backgroundBrush);
8616
dc.DrawRoundedRectangle(objectRect, 4.0);
8618
else if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_START_TAG)
8620
int arrowLength = objectRect.height/2;
8621
clientArea.width -= (arrowLength - m_horizontalPadding);
8624
pts[0].x = objectRect.x; pts[0].y = objectRect.y;
8625
pts[1].x = objectRect.x + objectRect.width - arrowLength; pts[1].y = objectRect.y;
8626
pts[2].x = objectRect.x + objectRect.width; pts[2].y = objectRect.y + (objectRect.height/2);
8627
pts[3].x = objectRect.x + objectRect.width - arrowLength; pts[3].y = objectRect.y + objectRect.height;
8628
pts[4].x = objectRect.x; pts[4].y = objectRect.y + objectRect.height;
8629
dc.SetPen(borderPen);
8630
dc.SetBrush(backgroundBrush);
8631
dc.DrawPolygon(5, pts);
8633
else if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_END_TAG)
8635
int arrowLength = objectRect.height/2;
8636
clientArea.width -= (arrowLength - m_horizontalPadding);
8637
clientArea.x += (arrowLength - m_horizontalPadding);
8640
pts[0].x = objectRect.x + objectRect.width; pts[0].y = objectRect.y;
8641
pts[1].x = objectRect.x + arrowLength; pts[1].y = objectRect.y;
8642
pts[2].x = objectRect.x; pts[2].y = objectRect.y + (objectRect.height/2);
8643
pts[3].x = objectRect.x + arrowLength; pts[3].y = objectRect.y + objectRect.height;
8644
pts[4].x = objectRect.x + objectRect.width; pts[4].y = objectRect.y + objectRect.height;
8645
dc.SetPen(borderPen);
8646
dc.SetBrush(backgroundBrush);
8647
dc.DrawPolygon(5, pts);
8650
if (m_bitmap.IsOk())
8652
int x = clientArea.x + (clientArea.width - m_bitmap.GetWidth())/2;
8653
int y = clientArea.y + m_verticalPadding;
8654
dc.DrawBitmap(m_bitmap, x, y, true);
8656
if (selection.WithinSelection(obj->GetRange().GetStart(), obj))
8658
wxCheckSetBrush(dc, *wxBLACK_BRUSH);
8659
wxCheckSetPen(dc, *wxBLACK_PEN);
8660
dc.SetLogicalFunction(wxINVERT);
8661
dc.DrawRectangle(wxRect(x, y, m_bitmap.GetWidth(), m_bitmap.GetHeight()));
8662
dc.SetLogicalFunction(wxCOPY);
8667
wxString label(m_label);
8668
if (label.IsEmpty())
8670
int w, h, maxDescent;
8672
dc.GetTextExtent(m_label, & w, &h, & maxDescent);
8673
dc.SetTextForeground(textColour);
8675
int x = clientArea.x + (clientArea.width - w)/2;
8676
int y = clientArea.y + (clientArea.height - (h - maxDescent))/2;
8677
dc.DrawText(m_label, x, y);
8684
bool wxRichTextFieldTypeStandard::Layout(wxRichTextField* obj, wxDC& dc, wxRichTextDrawingContext& context, const wxRect& WXUNUSED(rect), const wxRect& WXUNUSED(parentRect), int style)
8686
if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_COMPOSITE)
8687
return false; // USe default composite layout
8689
wxSize size = GetSize(obj, dc, context, style);
8690
obj->SetCachedSize(size);
8691
obj->SetMinSize(size);
8692
obj->SetMaxSize(size);
8696
bool wxRichTextFieldTypeStandard::GetRangeSize(wxRichTextField* obj, const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* partialExtents) const
8698
if (IsTopLevel(obj))
8699
return obj->wxRichTextParagraphLayoutBox::GetRangeSize(range, size, descent, dc, context, flags, position);
8702
wxSize sz = GetSize(obj, dc, context, 0);
8706
if (partialExtents->GetCount() > 0)
8707
lastSize = (*partialExtents)[partialExtents->GetCount()-1];
8710
partialExtents->Add(lastSize + sz.x);
8717
wxSize wxRichTextFieldTypeStandard::GetSize(wxRichTextField* WXUNUSED(obj), wxDC& dc, wxRichTextDrawingContext& WXUNUSED(context), int WXUNUSED(style)) const
8720
int w = 0, h = 0, maxDescent = 0;
8723
if (m_bitmap.IsOk())
8725
w = m_bitmap.GetWidth();
8726
h = m_bitmap.GetHeight();
8728
sz = wxSize(w + m_horizontalMargin*2, h + m_verticalMargin*2);
8732
wxString label(m_label);
8733
if (label.IsEmpty())
8736
dc.GetTextExtent(label, & w, &h, & maxDescent);
8738
sz = wxSize(w + m_horizontalPadding*2 + m_horizontalMargin*2, h + m_verticalPadding *2 + m_verticalMargin*2);
8741
if (m_displayStyle != wxRICHTEXT_FIELD_STYLE_NO_BORDER)
8743
sz.x += borderSize*2;
8744
sz.y += borderSize*2;
8747
if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_START_TAG || m_displayStyle == wxRICHTEXT_FIELD_STYLE_END_TAG)
8749
// Add space for the arrow
8750
sz.x += (sz.y/2 - m_horizontalPadding);
8756
IMPLEMENT_DYNAMIC_CLASS(wxRichTextCell, wxRichTextBox)
8758
wxRichTextCell::wxRichTextCell(wxRichTextObject* parent):
8759
wxRichTextBox(parent)
8764
bool wxRichTextCell::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
8766
return wxRichTextBox::Draw(dc, context, range, selection, rect, descent, style);
8770
void wxRichTextCell::Copy(const wxRichTextCell& obj)
8772
wxRichTextBox::Copy(obj);
8775
// Edit properties via a GUI
8776
bool wxRichTextCell::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
8778
// We need to gather common attributes for all selected cells.
8780
wxRichTextTable* table = wxDynamicCast(GetParent(), wxRichTextTable);
8781
bool multipleCells = false;
8782
wxRichTextAttr attr;
8784
if (table && buffer && buffer->GetRichTextCtrl() && buffer->GetRichTextCtrl()->GetSelection().IsValid() &&
8785
buffer->GetRichTextCtrl()->GetSelection().GetContainer() == GetParent())
8787
wxRichTextAttr clashingAttr, absentAttr;
8788
const wxRichTextSelection& sel = buffer->GetRichTextCtrl()->GetSelection();
8790
int selectedCellCount = 0;
8791
for (i = 0; i < sel.GetCount(); i++)
8793
const wxRichTextRange& range = sel[i];
8794
wxRichTextCell* cell = table->GetCell(range.GetStart());
8797
wxRichTextAttr cellStyle = cell->GetAttributes();
8799
CollectStyle(attr, cellStyle, clashingAttr, absentAttr);
8801
selectedCellCount ++;
8804
multipleCells = selectedCellCount > 1;
8808
attr = GetAttributes();
8813
caption = _("Multiple Cell Properties");
8815
caption = _("Cell Properties");
8817
wxRichTextObjectPropertiesDialog cellDlg(this, wxGetTopLevelParent(parent), wxID_ANY, caption);
8818
cellDlg.SetAttributes(attr);
8820
wxRichTextSizePage* sizePage = wxDynamicCast(cellDlg.FindPage(wxCLASSINFO(wxRichTextSizePage)), wxRichTextSizePage);
8823
// We don't want position and floating controls for a cell.
8824
sizePage->ShowPositionControls(false);
8825
sizePage->ShowFloatingControls(false);
8828
if (cellDlg.ShowModal() == wxID_OK)
8832
const wxRichTextSelection& sel = buffer->GetRichTextCtrl()->GetSelection();
8833
// Apply the style; we interpret indeterminate attributes as 'don't touch this attribute'
8834
// since it may represent clashing attributes across multiple objects.
8835
table->SetCellStyle(sel, attr);
8838
// For a single object, indeterminate attributes set by the user should be reflected in the
8839
// actual object style, so pass the wxRICHTEXT_SETSTYLE_RESET flag to assign
8840
// the style directly instead of applying (which ignores indeterminate attributes,
8841
// leaving them as they were).
8842
cellDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
8849
WX_DEFINE_OBJARRAY(wxRichTextObjectPtrArrayArray)
8851
IMPLEMENT_DYNAMIC_CLASS(wxRichTextTable, wxRichTextBox)
8853
wxRichTextTable::wxRichTextTable(wxRichTextObject* parent): wxRichTextBox(parent)
8859
// Draws the object.
8860
bool wxRichTextTable::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
8862
return wxRichTextBox::Draw(dc, context, range, selection, rect, descent, style);
8865
WX_DECLARE_OBJARRAY(wxRect, wxRichTextRectArray);
8866
WX_DEFINE_OBJARRAY(wxRichTextRectArray);
8868
// Lays the object out. rect is the space available for layout. Often it will
8869
// be the specified overall space for this object, if trying to constrain
8870
// layout to a particular size, or it could be the total space available in the
8871
// parent. rect is the overall size, so we must subtract margins and padding.
8872
// to get the actual available space.
8873
bool wxRichTextTable::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& WXUNUSED(parentRect), int style)
8875
SetPosition(rect.GetPosition());
8877
// TODO: the meaty bit. Calculate sizes of all cells and rows. Try to use
8878
// minimum size if within alloted size, then divide up remaining size
8879
// between rows/cols.
8882
wxRichTextBuffer* buffer = GetBuffer();
8883
if (buffer) scale = buffer->GetScale();
8885
wxRect availableSpace = GetAvailableContentArea(dc, context, rect);
8886
wxTextAttrDimensionConverter converter(dc, scale, availableSpace.GetSize());
8888
wxRichTextAttr attr(GetAttributes());
8889
context.ApplyVirtualAttributes(attr, this);
8891
// If we have no fixed table size, and assuming we're not pushed for
8892
// space, then we don't have to try to stretch the table to fit the contents.
8893
bool stretchToFitTableWidth = false;
8895
int tableWidth = rect.width;
8896
if (attr.GetTextBoxAttr().GetWidth().IsValid())
8898
tableWidth = converter.GetPixels(attr.GetTextBoxAttr().GetWidth());
8900
// Fixed table width, so we do want to stretch columns out if necessary.
8901
stretchToFitTableWidth = true;
8903
// Shouldn't be able to exceed the size passed to this function
8904
tableWidth = wxMin(rect.width, tableWidth);
8907
// Get internal padding
8908
int paddingLeft = 0, paddingTop = 0;
8909
if (attr.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
8910
paddingLeft = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetLeft());
8911
if (attr.GetTextBoxAttr().GetPadding().GetTop().IsValid())
8912
paddingTop = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetTop());
8914
// Assume that left and top padding are also used for inter-cell padding.
8915
int paddingX = paddingLeft;
8916
int paddingY = paddingTop;
8918
int totalLeftMargin = 0, totalRightMargin = 0, totalTopMargin = 0, totalBottomMargin = 0;
8919
GetTotalMargin(dc, buffer, attr, totalLeftMargin, totalRightMargin, totalTopMargin, totalBottomMargin);
8921
// Internal table width - the area for content
8922
int internalTableWidth = tableWidth - totalLeftMargin - totalRightMargin;
8924
int rowCount = m_cells.GetCount();
8925
if (m_colCount == 0 || rowCount == 0)
8927
wxRect overallRect(rect.x, rect.y, totalLeftMargin + totalRightMargin, totalTopMargin + totalBottomMargin);
8928
SetCachedSize(overallRect.GetSize());
8930
// Zero content size
8931
SetMinSize(overallRect.GetSize());
8932
SetMaxSize(GetMinSize());
8936
// The final calculated widths
8937
wxArrayInt colWidths;
8938
colWidths.Add(0, m_colCount);
8940
wxArrayInt absoluteColWidths;
8941
absoluteColWidths.Add(0, m_colCount);
8943
wxArrayInt percentageColWidths;
8944
percentageColWidths.Add(0, m_colCount);
8945
// wxArrayInt percentageColWidthsSpanning(m_colCount);
8946
// These are only relevant when the first column contains spanning information.
8947
// wxArrayInt columnSpans(m_colCount); // Each contains 1 for non-spanning cell, > 1 for spanning cell.
8948
wxArrayInt maxColWidths;
8949
maxColWidths.Add(0, m_colCount);
8950
wxArrayInt minColWidths;
8951
minColWidths.Add(0, m_colCount);
8953
wxSize tableSize(tableWidth, 0);
8957
for (i = 0; i < m_colCount; i++)
8959
absoluteColWidths[i] = 0;
8960
// absoluteColWidthsSpanning[i] = 0;
8961
percentageColWidths[i] = -1;
8962
// percentageColWidthsSpanning[i] = -1;
8964
maxColWidths[i] = 0;
8965
minColWidths[i] = 0;
8966
// columnSpans[i] = 1;
8969
// (0) Determine which cells are visible according to spans
8971
// __________________
8976
// |------------------|
8977
// |__________________| 4
8979
// To calculate cell visibility:
8980
// First find all spanning cells. Build an array of span records with start x, y and end x, y.
8981
// Then for each cell, test whether we're within one of those cells, and unless we're at the start of
8982
// that cell, hide the cell.
8984
// We can also use this array to match the size of spanning cells to the grid. Or just do
8985
// this when we iterate through all cells.
8987
// 0.1: add spanning cells to an array
8988
wxRichTextRectArray rectArray;
8989
for (j = 0; j < m_rowCount; j++)
8991
for (i = 0; i < m_colCount; i++)
8993
wxRichTextBox* cell = GetCell(j, i);
8994
int colSpan = 1, rowSpan = 1;
8995
if (cell->GetProperties().HasProperty(wxT("colspan")))
8996
colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
8997
if (cell->GetProperties().HasProperty(wxT("rowspan")))
8998
rowSpan = cell->GetProperties().GetPropertyLong(wxT("rowspan"));
8999
if (colSpan > 1 || rowSpan > 1)
9001
rectArray.Add(wxRect(i, j, colSpan, rowSpan));
9005
// 0.2: find which cells are subsumed by a spanning cell
9006
for (j = 0; j < m_rowCount; j++)
9008
for (i = 0; i < m_colCount; i++)
9010
wxRichTextBox* cell = GetCell(j, i);
9011
if (rectArray.GetCount() == 0)
9017
int colSpan = 1, rowSpan = 1;
9018
if (cell->GetProperties().HasProperty(wxT("colspan")))
9019
colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
9020
if (cell->GetProperties().HasProperty(wxT("rowspan")))
9021
rowSpan = cell->GetProperties().GetPropertyLong(wxT("rowspan"));
9022
if (colSpan > 1 || rowSpan > 1)
9024
// Assume all spanning cells are shown
9030
for (k = 0; k < (int) rectArray.GetCount(); k++)
9032
if (rectArray[k].Contains(wxPoint(i, j)))
9044
// TODO: find the first spanned cell in each row that spans the most columns and doesn't
9045
// overlap with a spanned cell starting at a previous column position.
9046
// This means we need to keep an array of rects so we can check. However
9047
// it does also mean that some spans simply may not be taken into account
9048
// where there are different spans happening on different rows. In these cases,
9049
// they will simply be as wide as their constituent columns.
9051
// (1) Do an initial layout for all cells to get minimum and maximum size, and get
9052
// the absolute or percentage width of each column.
9054
for (j = 0; j < m_rowCount; j++)
9056
// First get the overall margins so we can calculate percentage widths based on
9057
// the available content space for all cells on the row
9059
int overallRowContentMargin = 0;
9060
int visibleCellCount = 0;
9062
for (i = 0; i < m_colCount; i++)
9064
wxRichTextBox* cell = GetCell(j, i);
9065
if (cell->IsShown())
9067
int cellTotalLeftMargin = 0, cellTotalRightMargin = 0, cellTotalTopMargin = 0, cellTotalBottomMargin = 0;
9068
GetTotalMargin(dc, buffer, cell->GetAttributes(), cellTotalLeftMargin, cellTotalRightMargin, cellTotalTopMargin, cellTotalBottomMargin);
9070
overallRowContentMargin += (cellTotalLeftMargin + cellTotalRightMargin);
9071
visibleCellCount ++;
9075
// Add in inter-cell padding
9076
overallRowContentMargin += ((visibleCellCount-1) * paddingX);
9078
int rowContentWidth = internalTableWidth - overallRowContentMargin;
9079
wxSize rowTableSize(rowContentWidth, 0);
9080
wxTextAttrDimensionConverter converter(dc, scale, rowTableSize);
9082
for (i = 0; i < m_colCount; i++)
9084
wxRichTextBox* cell = GetCell(j, i);
9085
if (cell->IsShown())
9088
if (cell->GetProperties().HasProperty(wxT("colspan")))
9089
colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
9091
// Lay out cell to find min/max widths
9092
cell->Invalidate(wxRICHTEXT_ALL);
9093
cell->Layout(dc, context, availableSpace, availableSpace, style);
9097
int absoluteCellWidth = -1;
9098
int percentageCellWidth = -1;
9100
// I think we need to calculate percentages from the internal table size,
9101
// minus the padding between cells which we'll need to calculate from the
9102
// (number of VISIBLE cells - 1)*paddingX. Then percentages that add up to 100%
9103
// will add up to 100%. In CSS, the width specifies the cell's content rect width,
9104
// so if we want to conform to that we'll need to add in the overall cell margins.
9105
// However, this will make it difficult to specify percentages that add up to
9106
// 100% and still fit within the table width.
9107
// Let's say two cells have 50% width. They have 10 pixels of overall margin each.
9108
// The table content rect is 500 pixels and the inter-cell padding is 20 pixels.
9109
// If we're using internal content size for the width, we would calculate the
9110
// the overall cell width for n cells as:
9111
// (500 - 20*(n-1) - overallCellMargin1 - overallCellMargin2 - ...) * percentage / 100
9112
// + thisOverallCellMargin
9113
// = 500 - 20 - 10 - 10) * 0.5 + 10 = 240 pixels overall cell width.
9114
// Adding this back, we get 240 + 240 + 20 = 500 pixels.
9116
if (cell->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9118
int w = converter.GetPixels(cell->GetAttributes().GetTextBoxAttr().GetWidth());
9119
if (cell->GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
9121
percentageCellWidth = w;
9125
absoluteCellWidth = w;
9127
// Override absolute width with minimum width if necessary
9128
if (cell->GetMinSize().x > 0 && absoluteCellWidth !=1 && cell->GetMinSize().x > absoluteCellWidth)
9129
absoluteCellWidth = cell->GetMinSize().x;
9132
if (absoluteCellWidth != -1)
9134
if (absoluteCellWidth > absoluteColWidths[i])
9135
absoluteColWidths[i] = absoluteCellWidth;
9138
if (percentageCellWidth != -1)
9140
if (percentageCellWidth > percentageColWidths[i])
9141
percentageColWidths[i] = percentageCellWidth;
9144
if (colSpan == 1 && cell->GetMinSize().x && cell->GetMinSize().x > minColWidths[i])
9145
minColWidths[i] = cell->GetMinSize().x;
9146
if (colSpan == 1 && cell->GetMaxSize().x && cell->GetMaxSize().x > maxColWidths[i])
9147
maxColWidths[i] = cell->GetMaxSize().x;
9153
// (2) Allocate initial column widths from minimum widths, absolute values and proportions
9154
// TODO: simply merge this into (1).
9155
for (i = 0; i < m_colCount; i++)
9157
if (absoluteColWidths[i] > 0)
9159
colWidths[i] = absoluteColWidths[i];
9161
else if (percentageColWidths[i] > 0)
9163
colWidths[i] = percentageColWidths[i];
9165
// This is rubbish - we calculated the absolute widths from percentages, so
9166
// we can't do it again here.
9167
//colWidths[i] = (int) (double(percentageColWidths[i]) * double(tableWidth) / 100.0 + 0.5);
9171
// (3) Process absolute or proportional widths of spanning columns,
9172
// now that we know what our fixed column widths are going to be.
9173
// Spanned cells will try to adjust columns so the span will fit.
9174
// Even existing fixed column widths can be expanded if necessary.
9175
// Actually, currently fixed columns widths aren't adjusted; instead,
9176
// the algorithm favours earlier rows and adjusts unspecified column widths
9177
// the first time only. After that, we can't know whether the column has been
9178
// specified explicitly or not. (We could make a note if necessary.)
9179
for (j = 0; j < m_rowCount; j++)
9181
// First get the overall margins so we can calculate percentage widths based on
9182
// the available content space for all cells on the row
9184
int overallRowContentMargin = 0;
9185
int visibleCellCount = 0;
9187
for (i = 0; i < m_colCount; i++)
9189
wxRichTextBox* cell = GetCell(j, i);
9190
if (cell->IsShown())
9192
int cellTotalLeftMargin = 0, cellTotalRightMargin = 0, cellTotalTopMargin = 0, cellTotalBottomMargin = 0;
9193
GetTotalMargin(dc, buffer, cell->GetAttributes(), cellTotalLeftMargin, cellTotalRightMargin, cellTotalTopMargin, cellTotalBottomMargin);
9195
overallRowContentMargin += (cellTotalLeftMargin + cellTotalRightMargin);
9196
visibleCellCount ++;
9200
// Add in inter-cell padding
9201
overallRowContentMargin += ((visibleCellCount-1) * paddingX);
9203
int rowContentWidth = internalTableWidth - overallRowContentMargin;
9204
wxSize rowTableSize(rowContentWidth, 0);
9205
wxTextAttrDimensionConverter converter(dc, scale, rowTableSize);
9207
for (i = 0; i < m_colCount; i++)
9209
wxRichTextBox* cell = GetCell(j, i);
9210
if (cell->IsShown())
9213
if (cell->GetProperties().HasProperty(wxT("colspan")))
9214
colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
9218
int spans = wxMin(colSpan, m_colCount - i);
9222
if (cell->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9224
cellWidth = converter.GetPixels(cell->GetAttributes().GetTextBoxAttr().GetWidth());
9225
// Override absolute width with minimum width if necessary
9226
if (cell->GetMinSize().x > 0 && cellWidth !=1 && cell->GetMinSize().x > cellWidth)
9227
cellWidth = cell->GetMinSize().x;
9231
// Do we want to do this? It's the only chance we get to
9232
// use the cell's min/max sizes, so we need to work out
9233
// how we're going to balance the unspecified spanning cell
9234
// width with the possibility more-constrained constituent cell widths.
9235
// Say there's a tiny bitmap giving it a max width of 10 pixels. We
9236
// don't want to constraint all the spanned columns to fit into this cell.
9237
// OK, let's say that if any of the constituent columns don't fit,
9238
// then we simply stop constraining the columns; instead, we'll just fit the spanning
9239
// cells to the columns later.
9240
cellWidth = cell->GetMinSize().x;
9241
if (cell->GetMaxSize().x > cellWidth)
9242
cellWidth = cell->GetMaxSize().x;
9245
// Subtract the padding between cells
9246
int spanningWidth = cellWidth;
9247
spanningWidth -= paddingX * (spans-1);
9249
if (spanningWidth > 0)
9251
// Now share the spanning width between columns within that span
9252
// TODO: take into account min widths of columns within the span
9253
int spanningWidthLeft = spanningWidth;
9254
int stretchColCount = 0;
9255
for (k = i; k < (i+spans); k++)
9257
if (colWidths[k] > 0) // absolute or proportional width has been specified
9258
spanningWidthLeft -= colWidths[k];
9262
// Now divide what's left between the remaining columns
9264
if (stretchColCount > 0)
9265
colShare = spanningWidthLeft / stretchColCount;
9266
int colShareRemainder = spanningWidthLeft - (colShare * stretchColCount);
9268
// If fixed-width columns are currently too big, then we'll later
9269
// stretch the spanned cell to fit.
9271
if (spanningWidthLeft > 0)
9273
for (k = i; k < (i+spans); k++)
9275
if (colWidths[k] <= 0) // absolute or proportional width has not been specified
9277
int newWidth = colShare;
9278
if (k == (i+spans-1))
9279
newWidth += colShareRemainder; // ensure all pixels are filled
9280
colWidths[k] = newWidth;
9291
// (4) Next, share any remaining space out between columns that have not yet been calculated.
9292
// TODO: take into account min widths of columns within the span
9293
int tableWidthMinusPadding = internalTableWidth - (m_colCount-1)*paddingX;
9294
int widthLeft = tableWidthMinusPadding;
9295
int stretchColCount = 0;
9296
for (i = 0; i < m_colCount; i++)
9298
// TODO: we need to take into account min widths.
9299
// Subtract min width from width left, then
9300
// add the colShare to the min width
9301
if (colWidths[i] > 0) // absolute or proportional width has been specified
9302
widthLeft -= colWidths[i];
9305
if (minColWidths[i] > 0)
9306
widthLeft -= minColWidths[i];
9312
// Now divide what's left between the remaining columns
9314
if (stretchColCount > 0)
9315
colShare = widthLeft / stretchColCount;
9316
int colShareRemainder = widthLeft - (colShare * stretchColCount);
9318
// Check we don't have enough space, in which case shrink all columns, overriding
9319
// any absolute/proportional widths
9320
// TODO: actually we would like to divide up the shrinkage according to size.
9321
// How do we calculate the proportions that will achieve this?
9322
// Could first choose an arbitrary value for stretching cells, and then calculate
9323
// factors to multiply each width by.
9324
// TODO: want to record this fact and pass to an iteration that tries e.g. min widths
9325
if (widthLeft < 0 || (stretchToFitTableWidth && (stretchColCount == 0)))
9327
colShare = tableWidthMinusPadding / m_colCount;
9328
colShareRemainder = tableWidthMinusPadding - (colShare * m_colCount);
9329
for (i = 0; i < m_colCount; i++)
9332
minColWidths[i] = 0;
9336
// We have to adjust the columns if either we need to shrink the
9337
// table to fit the parent/table width, or we explicitly set the
9338
// table width and need to stretch out the table.
9339
if (widthLeft < 0 || stretchToFitTableWidth)
9341
for (i = 0; i < m_colCount; i++)
9343
if (colWidths[i] <= 0) // absolute or proportional width has not been specified
9345
if (minColWidths[i] > 0)
9346
colWidths[i] = minColWidths[i] + colShare;
9348
colWidths[i] = colShare;
9349
if (i == (m_colCount-1))
9350
colWidths[i] += colShareRemainder; // ensure all pixels are filled
9355
// TODO: if spanned cells have no specified or max width, make them the
9356
// as big as the columns they span. Do this for all spanned cells in all
9357
// rows, of course. Size any spanned cells left over at the end - even if they
9358
// have width > 0, make sure they're limited to the appropriate column edge.
9362
Sort out confusion between content width
9363
and overall width later. For now, assume we specify overall width.
9365
So, now we've laid out the table to fit into the given space
9366
and have used specified widths and minimum widths.
9368
Now we need to consider how we will try to take maximum width into account.
9372
// (??) TODO: take max width into account
9374
// (6) Lay out all cells again with the current values
9377
int y = availableSpace.y;
9378
for (j = 0; j < m_rowCount; j++)
9380
int x = availableSpace.x; // TODO: take into account centering etc.
9381
int maxCellHeight = 0;
9382
int maxSpecifiedCellHeight = 0;
9384
wxArrayInt actualWidths;
9385
actualWidths.Add(0, m_colCount);
9387
wxTextAttrDimensionConverter converter(dc, scale);
9388
for (i = 0; i < m_colCount; i++)
9390
wxRichTextCell* cell = GetCell(j, i);
9391
if (cell->IsShown())
9393
// Get max specified cell height
9394
// Don't handle percentages for height
9395
if (cell->GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && cell->GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() != wxTEXT_ATTR_UNITS_PERCENTAGE)
9397
int h = converter.GetPixels(cell->GetAttributes().GetTextBoxAttr().GetHeight());
9398
if (h > maxSpecifiedCellHeight)
9399
maxSpecifiedCellHeight = h;
9402
if (colWidths[i] > 0) // absolute or proportional width has been specified
9405
if (cell->GetProperties().HasProperty(wxT("colspan")))
9406
colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
9408
wxRect availableCellSpace;
9410
// TODO: take into acount spans
9413
// Calculate the size of this spanning cell from its constituent columns
9415
int spans = wxMin(colSpan, m_colCount - i);
9416
for (k = i; k < spans; k++)
9422
availableCellSpace = wxRect(x, y, xx, -1);
9425
availableCellSpace = wxRect(x, y, colWidths[i], -1);
9427
// Store actual width so we can force cell to be the appropriate width on the final loop
9428
actualWidths[i] = availableCellSpace.GetWidth();
9431
cell->Invalidate(wxRICHTEXT_ALL);
9432
cell->Layout(dc, context, availableCellSpace, availableSpace, style);
9434
// TODO: use GetCachedSize().x to compute 'natural' size
9436
x += (availableCellSpace.GetWidth() + paddingX);
9437
if (cell->GetCachedSize().y > maxCellHeight)
9438
maxCellHeight = cell->GetCachedSize().y;
9443
maxCellHeight = wxMax(maxCellHeight, maxSpecifiedCellHeight);
9445
for (i = 0; i < m_colCount; i++)
9447
wxRichTextCell* cell = GetCell(j, i);
9448
if (cell->IsShown())
9450
wxRect availableCellSpace = wxRect(cell->GetPosition(), wxSize(actualWidths[i], maxCellHeight));
9451
// Lay out cell with new height
9452
cell->Invalidate(wxRICHTEXT_ALL);
9453
cell->Layout(dc, context, availableCellSpace, availableSpace, style);
9455
// Make sure the cell size really is the appropriate size,
9456
// not the calculated box size
9457
cell->SetCachedSize(wxSize(actualWidths[i], maxCellHeight));
9459
maxRight = wxMax(maxRight, cell->GetPosition().x + cell->GetCachedSize().x);
9464
if (j < (m_rowCount-1))
9468
// We need to add back the margins etc.
9470
wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
9471
contentRect = wxRect(wxPoint(0, 0), wxSize(maxRight - availableSpace.x, y - availableSpace.y));
9472
GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
9473
SetCachedSize(marginRect.GetSize());
9476
// TODO: calculate max size
9478
SetMaxSize(GetCachedSize());
9481
// TODO: calculate min size
9483
SetMinSize(GetCachedSize());
9486
// TODO: currently we use either a fixed table width or the parent's size.
9487
// We also want to be able to calculate the table width from its content,
9488
// whether using fixed column widths or cell content min/max width.
9489
// Probably need a boolean flag to say whether we need to stretch cells
9490
// to fit the table width, or to simply use min/max cell widths. The
9491
// trouble with this is that if cell widths are not specified, they
9492
// will be tiny; we could use arbitrary defaults but this seems unsatisfactory.
9493
// Anyway, ignoring that problem, we probably need to factor layout into a function
9494
// that can can calculate the maximum unconstrained layout in case table size is
9495
// not specified. Then LayoutToBestSize() can choose to use either parent size to
9496
// constrain Layout(), or the previously-calculated max size to constraint layout.
9501
// Finds the absolute position and row height for the given character position
9502
bool wxRichTextTable::FindPosition(wxDC& dc, wxRichTextDrawingContext& context, long index, wxPoint& pt, int* height, bool forceLineStart)
9504
wxRichTextCell* child = GetCell(index+1);
9507
// Find the position at the start of the child cell, since the table doesn't
9508
// have any caret position of its own.
9509
return child->FindPosition(dc, context, -1, pt, height, forceLineStart);
9515
// Get the cell at the given character position (in the range of the table).
9516
wxRichTextCell* wxRichTextTable::GetCell(long pos) const
9518
int row = 0, col = 0;
9519
if (GetCellRowColumnPosition(pos, row, col))
9521
return GetCell(row, col);
9527
// Get the row/column for a given character position
9528
bool wxRichTextTable::GetCellRowColumnPosition(long pos, int& row, int& col) const
9530
if (m_colCount == 0 || m_rowCount == 0)
9533
row = (int) (pos / m_colCount);
9534
col = pos - (row * m_colCount);
9536
wxASSERT(row < m_rowCount && col < m_colCount);
9538
if (row < m_rowCount && col < m_colCount)
9544
// Calculate range, taking row/cell ordering into account instead of relying
9545
// on list ordering.
9546
void wxRichTextTable::CalculateRange(long start, long& end)
9548
long current = start;
9549
long lastEnd = current;
9558
for (i = 0; i < m_rowCount; i++)
9560
for (j = 0; j < m_colCount; j++)
9562
wxRichTextCell* child = GetCell(i, j);
9567
child->CalculateRange(current, childEnd);
9570
current = childEnd + 1;
9575
// A top-level object always has a range of size 1,
9576
// because its children don't count at this level.
9578
m_range.SetRange(start, start);
9580
// An object with no children has zero length
9581
if (m_children.GetCount() == 0)
9583
m_ownRange.SetRange(0, lastEnd);
9586
// Gets the range size.
9587
bool wxRichTextTable::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* partialExtents) const
9589
return wxRichTextBox::GetRangeSize(range, size, descent, dc, context, flags, position, partialExtents);
9592
// Deletes content in the given range.
9593
bool wxRichTextTable::DeleteRange(const wxRichTextRange& WXUNUSED(range))
9595
// TODO: implement deletion of cells
9599
// Gets any text in this object for the given range.
9600
wxString wxRichTextTable::GetTextForRange(const wxRichTextRange& range) const
9602
return wxRichTextBox::GetTextForRange(range);
9605
// Copies this object.
9606
void wxRichTextTable::Copy(const wxRichTextTable& obj)
9608
wxRichTextBox::Copy(obj);
9612
m_rowCount = obj.m_rowCount;
9613
m_colCount = obj.m_colCount;
9615
m_cells.Add(wxRichTextObjectPtrArray(), m_rowCount);
9618
for (i = 0; i < m_rowCount; i++)
9620
wxRichTextObjectPtrArray& colArray = m_cells[i];
9621
for (j = 0; j < m_colCount; j++)
9623
wxRichTextCell* cell = wxDynamicCast(obj.GetCell(i, j)->Clone(), wxRichTextCell);
9631
void wxRichTextTable::ClearTable()
9637
bool wxRichTextTable::CreateTable(int rows, int cols)
9644
m_cells.Add(wxRichTextObjectPtrArray(), rows);
9647
for (i = 0; i < rows; i++)
9649
wxRichTextObjectPtrArray& colArray = m_cells[i];
9650
for (j = 0; j < cols; j++)
9652
wxRichTextCell* cell = new wxRichTextCell;
9654
cell->AddParagraph(wxEmptyString);
9663
wxRichTextCell* wxRichTextTable::GetCell(int row, int col) const
9665
wxASSERT(row < m_rowCount);
9666
wxASSERT(col < m_colCount);
9668
if (row < m_rowCount && col < m_colCount)
9670
wxRichTextObjectPtrArray& colArray = m_cells[row];
9671
wxRichTextObject* obj = colArray[col];
9672
return wxDynamicCast(obj, wxRichTextCell);
9678
// Returns a selection object specifying the selections between start and end character positions.
9679
// For example, a table would deduce what cells (of range length 1) are selected when dragging across the table.
9680
wxRichTextSelection wxRichTextTable::GetSelection(long start, long end) const
9682
wxRichTextSelection selection;
9683
selection.SetContainer((wxRichTextTable*) this);
9692
wxASSERT( start >= 0 && end < (m_colCount * m_rowCount));
9694
if (end >= (m_colCount * m_rowCount))
9697
// We need to find the rectangle of cells that is described by the rectangle
9698
// with start, end as the diagonal. Make sure we don't add cells that are
9699
// not currenty visible because they are overlapped by spanning cells.
9701
--------------------------
9702
| 0 | 1 | 2 | 3 | 4 |
9703
--------------------------
9704
| 5 | 6 | 7 | 8 | 9 |
9705
--------------------------
9706
| 10 | 11 | 12 | 13 | 14 |
9707
--------------------------
9708
| 15 | 16 | 17 | 18 | 19 |
9709
--------------------------
9711
Let's say we select 6 -> 18.
9713
Left and right edge cols of rectangle are 1 and 3 inclusive. Find least/greatest to find
9714
which is left and which is right.
9716
Top and bottom edge rows are 1 and 3 inclusive. Again, find least/greatest to find top and bottom.
9718
Now go through rows from 1 to 3 and only add cells that are (a) within above column range
9724
int leftCol = start - m_colCount * int(start/m_colCount);
9725
int rightCol = end - m_colCount * int(end/m_colCount);
9727
int topRow = int(start/m_colCount);
9728
int bottomRow = int(end/m_colCount);
9730
if (leftCol > rightCol)
9737
if (topRow > bottomRow)
9739
int tmp = bottomRow;
9745
for (i = topRow; i <= bottomRow; i++)
9747
for (j = leftCol; j <= rightCol; j++)
9749
wxRichTextCell* cell = GetCell(i, j);
9750
if (cell && cell->IsShown())
9751
selection.Add(cell->GetRange());
9758
// Sets the attributes for the cells specified by the selection.
9759
bool wxRichTextTable::SetCellStyle(const wxRichTextSelection& selection, const wxRichTextAttr& style, int flags)
9761
if (selection.GetContainer() != this)
9764
wxRichTextBuffer* buffer = GetBuffer();
9765
bool haveControl = (buffer && buffer->GetRichTextCtrl() != NULL);
9766
bool withUndo = haveControl && ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
9769
buffer->BeginBatchUndo(_("Set Cell Style"));
9771
wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
9774
wxRichTextCell* cell = wxDynamicCast(node->GetData(), wxRichTextCell);
9775
if (cell && selection.WithinSelection(cell->GetRange().GetStart()))
9776
SetStyle(cell, style, flags);
9777
node = node->GetNext();
9780
// Do action, or delay it until end of batch.
9782
buffer->EndBatchUndo();
9787
bool wxRichTextTable::DeleteRows(int startRow, int noRows)
9789
wxASSERT((startRow + noRows) < m_rowCount);
9790
if ((startRow + noRows) >= m_rowCount)
9794
for (i = startRow; i < (startRow+noRows); i++)
9796
wxRichTextObjectPtrArray& colArray = m_cells[startRow];
9797
for (j = 0; j < (int) colArray.GetCount(); j++)
9799
wxRichTextObject* cell = colArray[j];
9800
RemoveChild(cell, true);
9803
// Keep deleting at the same position, since we move all
9805
m_cells.RemoveAt(startRow);
9808
m_rowCount = m_rowCount - noRows;
9813
bool wxRichTextTable::DeleteColumns(int startCol, int noCols)
9815
wxASSERT((startCol + noCols) < m_colCount);
9816
if ((startCol + noCols) >= m_colCount)
9819
bool deleteRows = (noCols == m_colCount);
9822
for (i = 0; i < m_rowCount; i++)
9824
wxRichTextObjectPtrArray& colArray = m_cells[deleteRows ? 0 : i];
9825
for (j = startCol; j < (startCol+noCols); j++)
9827
wxRichTextObject* cell = colArray[j];
9828
RemoveChild(cell, true);
9832
m_cells.RemoveAt(0);
9837
m_colCount = m_colCount - noCols;
9842
bool wxRichTextTable::AddRows(int startRow, int noRows, const wxRichTextAttr& attr)
9844
wxASSERT(startRow <= m_rowCount);
9845
if (startRow > m_rowCount)
9849
for (i = 0; i < noRows; i++)
9852
if (startRow == m_rowCount)
9854
m_cells.Add(wxRichTextObjectPtrArray());
9855
idx = m_cells.GetCount() - 1;
9859
m_cells.Insert(wxRichTextObjectPtrArray(), startRow+i);
9863
wxRichTextObjectPtrArray& colArray = m_cells[idx];
9864
for (j = 0; j < m_colCount; j++)
9866
wxRichTextCell* cell = new wxRichTextCell;
9867
cell->GetAttributes() = attr;
9874
m_rowCount = m_rowCount + noRows;
9878
bool wxRichTextTable::AddColumns(int startCol, int noCols, const wxRichTextAttr& attr)
9880
wxASSERT(startCol <= m_colCount);
9881
if (startCol > m_colCount)
9885
for (i = 0; i < m_rowCount; i++)
9887
wxRichTextObjectPtrArray& colArray = m_cells[i];
9888
for (j = 0; j < noCols; j++)
9890
wxRichTextCell* cell = new wxRichTextCell;
9891
cell->GetAttributes() = attr;
9895
if (startCol == m_colCount)
9898
colArray.Insert(cell, startCol+j);
9902
m_colCount = m_colCount + noCols;
9907
// Edit properties via a GUI
9908
bool wxRichTextTable::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
9910
wxRichTextObjectPropertiesDialog boxDlg(this, wxGetTopLevelParent(parent), wxID_ANY, _("Table Properties"));
9911
boxDlg.SetAttributes(GetAttributes());
9913
if (boxDlg.ShowModal() == wxID_OK)
9915
boxDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
9923
* Module to initialise and clean up handlers
9926
class wxRichTextModule: public wxModule
9928
DECLARE_DYNAMIC_CLASS(wxRichTextModule)
9930
wxRichTextModule() {}
9933
wxRichTextBuffer::SetRenderer(new wxRichTextStdRenderer);
9934
wxRichTextBuffer::InitStandardHandlers();
9935
wxRichTextParagraph::InitDefaultTabs();
9937
wxRichTextXMLHandler::RegisterNodeName(wxT("text"), wxT("wxRichTextPlainText"));
9938
wxRichTextXMLHandler::RegisterNodeName(wxT("symbol"), wxT("wxRichTextPlainText"));
9939
wxRichTextXMLHandler::RegisterNodeName(wxT("image"), wxT("wxRichTextImage"));
9940
wxRichTextXMLHandler::RegisterNodeName(wxT("paragraph"), wxT("wxRichTextParagraph"));
9941
wxRichTextXMLHandler::RegisterNodeName(wxT("paragraphlayout"), wxT("wxRichTextParagraphLayoutBox"));
9942
wxRichTextXMLHandler::RegisterNodeName(wxT("textbox"), wxT("wxRichTextBox"));
9943
wxRichTextXMLHandler::RegisterNodeName(wxT("cell"), wxT("wxRichTextCell"));
9944
wxRichTextXMLHandler::RegisterNodeName(wxT("table"), wxT("wxRichTextTable"));
9945
wxRichTextXMLHandler::RegisterNodeName(wxT("field"), wxT("wxRichTextField"));
9951
wxRichTextBuffer::CleanUpHandlers();
9952
wxRichTextBuffer::CleanUpDrawingHandlers();
9953
wxRichTextBuffer::CleanUpFieldTypes();
9954
wxRichTextXMLHandler::ClearNodeToClassMap();
9955
wxRichTextDecimalToRoman(-1);
9956
wxRichTextParagraph::ClearDefaultTabs();
9957
wxRichTextCtrl::ClearAvailableFontNames();
9958
wxRichTextBuffer::SetRenderer(NULL);
9962
IMPLEMENT_DYNAMIC_CLASS(wxRichTextModule, wxModule)
9965
// If the richtext lib is dynamically loaded after the app has already started
9966
// (such as from wxPython) then the built-in module system will not init this
9967
// module. Provide this function to do it manually.
9968
void wxRichTextModuleInit()
9970
wxModule* module = new wxRichTextModule;
9972
wxModule::RegisterModule(module);
9977
* Commands for undo/redo
9981
wxRichTextCommand::wxRichTextCommand(const wxString& name, wxRichTextCommandId id, wxRichTextBuffer* buffer,
9982
wxRichTextParagraphLayoutBox* container, wxRichTextCtrl* ctrl, bool ignoreFirstTime): wxCommand(true, name)
9984
/* wxRichTextAction* action = */ new wxRichTextAction(this, name, id, buffer, container, ctrl, ignoreFirstTime);
9987
wxRichTextCommand::wxRichTextCommand(const wxString& name): wxCommand(true, name)
9991
wxRichTextCommand::~wxRichTextCommand()
9996
void wxRichTextCommand::AddAction(wxRichTextAction* action)
9998
if (!m_actions.Member(action))
9999
m_actions.Append(action);
10002
bool wxRichTextCommand::Do()
10004
for (wxList::compatibility_iterator node = m_actions.GetFirst(); node; node = node->GetNext())
10006
wxRichTextAction* action = (wxRichTextAction*) node->GetData();
10013
bool wxRichTextCommand::Undo()
10015
for (wxList::compatibility_iterator node = m_actions.GetLast(); node; node = node->GetPrevious())
10017
wxRichTextAction* action = (wxRichTextAction*) node->GetData();
10024
void wxRichTextCommand::ClearActions()
10026
WX_CLEAR_LIST(wxList, m_actions);
10030
* Individual action
10034
wxRichTextAction::wxRichTextAction(wxRichTextCommand* cmd, const wxString& name, wxRichTextCommandId id,
10035
wxRichTextBuffer* buffer, wxRichTextParagraphLayoutBox* container,
10036
wxRichTextCtrl* ctrl, bool ignoreFirstTime)
10040
m_containerAddress.Create(buffer, container);
10041
m_ignoreThis = ignoreFirstTime;
10046
m_newParagraphs.SetDefaultStyle(buffer->GetDefaultStyle());
10047
m_newParagraphs.SetBasicStyle(buffer->GetBasicStyle());
10049
cmd->AddAction(this);
10052
wxRichTextAction::~wxRichTextAction()
10058
// Returns the container that this action refers to, using the container address and top-level buffer.
10059
wxRichTextParagraphLayoutBox* wxRichTextAction::GetContainer() const
10061
wxRichTextParagraphLayoutBox* container = wxDynamicCast(GetContainerAddress().GetObject(m_buffer), wxRichTextParagraphLayoutBox);
10066
void wxRichTextAction::CalculateRefreshOptimizations(wxArrayInt& optimizationLineCharPositions, wxArrayInt& optimizationLineYPositions)
10068
// Store a list of line start character and y positions so we can figure out which area
10069
// we need to refresh
10071
#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10072
wxRichTextParagraphLayoutBox* container = GetContainer();
10073
wxASSERT(container != NULL);
10077
// NOTE: we're assuming that the buffer is laid out correctly at this point.
10078
// If we had several actions, which only invalidate and leave layout until the
10079
// paint handler is called, then this might not be true. So we may need to switch
10080
// optimisation on only when we're simply adding text and not simultaneously
10081
// deleting a selection, for example. Or, we make sure the buffer is laid out correctly
10082
// first, but of course this means we'll be doing it twice.
10083
if (!m_buffer->IsDirty() && m_ctrl) // can only do optimisation if the buffer is already laid out correctly
10085
wxSize clientSize = m_ctrl->GetUnscaledSize(m_ctrl->GetClientSize());
10086
wxPoint firstVisiblePt = m_ctrl->GetUnscaledPoint(m_ctrl->GetFirstVisiblePoint());
10087
int lastY = firstVisiblePt.y + clientSize.y;
10089
wxRichTextParagraph* para = container->GetParagraphAtPosition(GetRange().GetStart());
10090
wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(para);
10093
wxRichTextParagraph* child = (wxRichTextParagraph*) node->GetData();
10094
wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
10097
wxRichTextLine* line = node2->GetData();
10098
wxPoint pt = line->GetAbsolutePosition();
10099
wxRichTextRange range = line->GetAbsoluteRange();
10103
node2 = wxRichTextLineList::compatibility_iterator();
10104
node = wxRichTextObjectList::compatibility_iterator();
10106
else if (range.GetStart() > GetPosition() && pt.y >= firstVisiblePt.y)
10108
optimizationLineCharPositions.Add(range.GetStart());
10109
optimizationLineYPositions.Add(pt.y);
10113
node2 = node2->GetNext();
10117
node = node->GetNext();
10123
bool wxRichTextAction::Do()
10125
m_buffer->Modify(true);
10127
wxRichTextParagraphLayoutBox* container = GetContainer();
10128
wxASSERT(container != NULL);
10134
case wxRICHTEXT_INSERT:
10136
// Store a list of line start character and y positions so we can figure out which area
10137
// we need to refresh
10138
wxArrayInt optimizationLineCharPositions;
10139
wxArrayInt optimizationLineYPositions;
10141
#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10142
CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
10145
container->InsertFragment(GetRange().GetStart(), m_newParagraphs);
10146
container->UpdateRanges();
10148
// InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10149
// Layout() would stop prematurely at the top level.
10150
container->InvalidateHierarchy(wxRichTextRange(wxMax(0, GetRange().GetStart()-1), GetRange().GetEnd()));
10152
long newCaretPosition = GetPosition() + m_newParagraphs.GetOwnRange().GetLength();
10154
// Character position to caret position
10155
newCaretPosition --;
10157
// Don't take into account the last newline
10158
if (m_newParagraphs.GetPartialParagraph())
10159
newCaretPosition --;
10161
if (m_newParagraphs.GetChildren().GetCount() > 1)
10163
wxRichTextObject* p = (wxRichTextObject*) m_newParagraphs.GetChildren().GetLast()->GetData();
10164
if (p->GetRange().GetLength() == 1)
10165
newCaretPosition --;
10168
newCaretPosition = wxMin(newCaretPosition, (container->GetOwnRange().GetEnd()-1));
10170
UpdateAppearance(newCaretPosition, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions, true /* do */);
10172
wxRichTextEvent cmdEvent(
10173
wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED,
10174
m_ctrl ? m_ctrl->GetId() : -1);
10175
cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10176
cmdEvent.SetRange(GetRange());
10177
cmdEvent.SetPosition(GetRange().GetStart());
10178
cmdEvent.SetContainer(container);
10180
m_buffer->SendEvent(cmdEvent);
10184
case wxRICHTEXT_DELETE:
10186
wxArrayInt optimizationLineCharPositions;
10187
wxArrayInt optimizationLineYPositions;
10189
#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10190
CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
10193
container->DeleteRange(GetRange());
10194
container->UpdateRanges();
10195
// InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10196
// Layout() would stop prematurely at the top level.
10197
container->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
10199
long caretPos = GetRange().GetStart()-1;
10200
if (caretPos >= container->GetOwnRange().GetEnd())
10203
UpdateAppearance(caretPos, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions, true /* do */);
10205
wxRichTextEvent cmdEvent(
10206
wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED,
10207
m_ctrl ? m_ctrl->GetId() : -1);
10208
cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10209
cmdEvent.SetRange(GetRange());
10210
cmdEvent.SetPosition(GetRange().GetStart());
10211
cmdEvent.SetContainer(container);
10213
m_buffer->SendEvent(cmdEvent);
10217
case wxRICHTEXT_CHANGE_STYLE:
10218
case wxRICHTEXT_CHANGE_PROPERTIES:
10220
ApplyParagraphs(GetNewParagraphs());
10222
// InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10223
// Layout() would stop prematurely at the top level.
10224
container->InvalidateHierarchy(GetRange());
10226
UpdateAppearance(GetPosition());
10228
wxRichTextEvent cmdEvent(
10229
m_cmdId == wxRICHTEXT_CHANGE_STYLE ? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED : wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED,
10230
m_ctrl ? m_ctrl->GetId() : -1);
10231
cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10232
cmdEvent.SetRange(GetRange());
10233
cmdEvent.SetPosition(GetRange().GetStart());
10234
cmdEvent.SetContainer(container);
10236
m_buffer->SendEvent(cmdEvent);
10240
case wxRICHTEXT_CHANGE_ATTRIBUTES:
10242
wxRichTextObject* obj = m_objectAddress.GetObject(m_buffer); // container->GetChildAtPosition(GetRange().GetStart());
10245
wxRichTextAttr oldAttr = obj->GetAttributes();
10246
obj->GetAttributes() = m_attributes;
10247
m_attributes = oldAttr;
10250
// InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10251
// Layout() would stop prematurely at the top level.
10252
container->InvalidateHierarchy(GetRange());
10254
UpdateAppearance(GetPosition());
10256
wxRichTextEvent cmdEvent(
10257
wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED,
10258
m_ctrl ? m_ctrl->GetId() : -1);
10259
cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10260
cmdEvent.SetRange(GetRange());
10261
cmdEvent.SetPosition(GetRange().GetStart());
10262
cmdEvent.SetContainer(container);
10264
m_buffer->SendEvent(cmdEvent);
10268
case wxRICHTEXT_CHANGE_OBJECT:
10270
wxRichTextObject* obj = m_objectAddress.GetObject(m_buffer);
10271
// wxRichTextObject* obj = container->GetChildAtPosition(GetRange().GetStart());
10272
if (obj && m_object)
10274
wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(obj);
10277
wxRichTextObject* obj = node->GetData();
10278
node->SetData(m_object);
10283
// InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10284
// Layout() would stop prematurely at the top level.
10285
container->InvalidateHierarchy(GetRange());
10287
UpdateAppearance(GetPosition());
10289
// TODO: send new kind of modification event
10300
bool wxRichTextAction::Undo()
10302
m_buffer->Modify(true);
10304
wxRichTextParagraphLayoutBox* container = GetContainer();
10305
wxASSERT(container != NULL);
10311
case wxRICHTEXT_INSERT:
10313
wxArrayInt optimizationLineCharPositions;
10314
wxArrayInt optimizationLineYPositions;
10316
#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10317
CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
10320
container->DeleteRange(GetRange());
10321
container->UpdateRanges();
10323
// InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10324
// Layout() would stop prematurely at the top level.
10325
container->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
10327
long newCaretPosition = GetPosition() - 1;
10329
UpdateAppearance(newCaretPosition, true, /* send update event */ & optimizationLineCharPositions, & optimizationLineYPositions, false /* undo */);
10331
wxRichTextEvent cmdEvent(
10332
wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED,
10333
m_ctrl ? m_ctrl->GetId() : -1);
10334
cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10335
cmdEvent.SetRange(GetRange());
10336
cmdEvent.SetPosition(GetRange().GetStart());
10337
cmdEvent.SetContainer(container);
10339
m_buffer->SendEvent(cmdEvent);
10343
case wxRICHTEXT_DELETE:
10345
wxArrayInt optimizationLineCharPositions;
10346
wxArrayInt optimizationLineYPositions;
10348
#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10349
CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
10352
container->InsertFragment(GetRange().GetStart(), m_oldParagraphs);
10353
container->UpdateRanges();
10355
// InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10356
// Layout() would stop prematurely at the top level.
10357
container->InvalidateHierarchy(GetRange());
10359
UpdateAppearance(GetPosition(), true, /* send update event */ & optimizationLineCharPositions, & optimizationLineYPositions, false /* undo */);
10361
wxRichTextEvent cmdEvent(
10362
wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED,
10363
m_ctrl ? m_ctrl->GetId() : -1);
10364
cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10365
cmdEvent.SetRange(GetRange());
10366
cmdEvent.SetPosition(GetRange().GetStart());
10367
cmdEvent.SetContainer(container);
10369
m_buffer->SendEvent(cmdEvent);
10373
case wxRICHTEXT_CHANGE_STYLE:
10374
case wxRICHTEXT_CHANGE_PROPERTIES:
10376
ApplyParagraphs(GetOldParagraphs());
10377
// InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10378
// Layout() would stop prematurely at the top level.
10379
container->InvalidateHierarchy(GetRange());
10381
UpdateAppearance(GetPosition());
10383
wxRichTextEvent cmdEvent(
10384
m_cmdId == wxRICHTEXT_CHANGE_STYLE ? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED : wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED,
10385
m_ctrl ? m_ctrl->GetId() : -1);
10386
cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10387
cmdEvent.SetRange(GetRange());
10388
cmdEvent.SetPosition(GetRange().GetStart());
10389
cmdEvent.SetContainer(container);
10391
m_buffer->SendEvent(cmdEvent);
10395
case wxRICHTEXT_CHANGE_ATTRIBUTES:
10396
case wxRICHTEXT_CHANGE_OBJECT:
10407
/// Update the control appearance
10408
void wxRichTextAction::UpdateAppearance(long caretPosition, bool sendUpdateEvent, wxArrayInt* optimizationLineCharPositions, wxArrayInt* optimizationLineYPositions, bool isDoCmd)
10410
wxRichTextParagraphLayoutBox* container = GetContainer();
10411
wxASSERT(container != NULL);
10417
m_ctrl->SetFocusObject(container);
10418
m_ctrl->SetCaretPosition(caretPosition);
10420
if (!m_ctrl->IsFrozen())
10422
wxRect containerRect = container->GetRect();
10424
m_ctrl->LayoutContent();
10426
// Refresh everything if there were floating objects or the container changed size
10427
// (we can't yet optimize in these cases, since more complex interaction with other content occurs)
10428
if (container->GetFloatingObjectCount() > 0 || (container->GetParent() && containerRect != container->GetRect()))
10430
m_ctrl->Refresh(false);
10434
#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10435
// Find refresh rectangle if we are in a position to optimise refresh
10436
if ((m_cmdId == wxRICHTEXT_INSERT || m_cmdId == wxRICHTEXT_DELETE) && optimizationLineCharPositions)
10440
wxSize clientSize = m_ctrl->GetUnscaledSize(m_ctrl->GetClientSize());
10441
wxPoint firstVisiblePt = m_ctrl->GetUnscaledPoint(m_ctrl->GetFirstVisiblePoint());
10443
// Start/end positions
10445
int lastY = firstVisiblePt.y + clientSize.y;
10447
bool foundEnd = false;
10449
// position offset - how many characters were inserted
10450
int positionOffset = GetRange().GetLength();
10452
// Determine whether this is Do or Undo, and adjust positionOffset accordingly
10453
if ((m_cmdId == wxRICHTEXT_DELETE && isDoCmd) || (m_cmdId == wxRICHTEXT_INSERT && !isDoCmd))
10454
positionOffset = - positionOffset;
10456
// find the first line which is being drawn at the same position as it was
10457
// before. Since we're talking about a simple insertion, we can assume
10458
// that the rest of the window does not need to be redrawn.
10460
wxRichTextParagraph* para = container->GetParagraphAtPosition(GetPosition());
10461
// Since we support floating layout, we should redraw the whole para instead of just
10462
// the first line touching the invalid range.
10465
firstY = para->GetPosition().y;
10468
wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(para);
10471
wxRichTextParagraph* child = (wxRichTextParagraph*) node->GetData();
10472
wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
10475
wxRichTextLine* line = node2->GetData();
10476
wxPoint pt = line->GetAbsolutePosition();
10477
wxRichTextRange range = line->GetAbsoluteRange();
10479
// we want to find the first line that is in the same position
10480
// as before. This will mean we're at the end of the changed text.
10482
if (pt.y > lastY) // going past the end of the window, no more info
10484
node2 = wxRichTextLineList::compatibility_iterator();
10485
node = wxRichTextObjectList::compatibility_iterator();
10487
// Detect last line in the buffer
10488
else if (!node2->GetNext() && para->GetRange().Contains(container->GetOwnRange().GetEnd()))
10490
// If deleting text, make sure we refresh below as well as above
10491
if (positionOffset >= 0)
10494
lastY = pt.y + line->GetSize().y;
10497
node2 = wxRichTextLineList::compatibility_iterator();
10498
node = wxRichTextObjectList::compatibility_iterator();
10504
// search for this line being at the same position as before
10505
for (i = 0; i < optimizationLineCharPositions->GetCount(); i++)
10507
if (((*optimizationLineCharPositions)[i] + positionOffset == range.GetStart()) &&
10508
((*optimizationLineYPositions)[i] == pt.y))
10510
// Stop, we're now the same as we were
10515
node2 = wxRichTextLineList::compatibility_iterator();
10516
node = wxRichTextObjectList::compatibility_iterator();
10524
node2 = node2->GetNext();
10528
node = node->GetNext();
10531
firstY = wxMax(firstVisiblePt.y, firstY);
10533
lastY = firstVisiblePt.y + clientSize.y;
10535
// Convert to device coordinates
10536
wxRect rect(m_ctrl->GetPhysicalPoint(m_ctrl->GetScaledPoint(wxPoint(firstVisiblePt.x, firstY))), m_ctrl->GetScaledSize(wxSize(clientSize.x, lastY - firstY)));
10537
m_ctrl->RefreshRect(rect);
10541
m_ctrl->Refresh(false);
10543
m_ctrl->PositionCaret();
10545
// This causes styles to persist when doing programmatic
10546
// content creation except when Freeze/Thaw is used, so
10547
// disable this and check for the consequences.
10548
// m_ctrl->SetDefaultStyleToCursorStyle();
10550
if (sendUpdateEvent)
10551
wxTextCtrl::SendTextUpdatedEvent(m_ctrl);
10556
/// Replace the buffer paragraphs with the new ones.
10557
void wxRichTextAction::ApplyParagraphs(const wxRichTextParagraphLayoutBox& fragment)
10559
wxRichTextParagraphLayoutBox* container = GetContainer();
10560
wxASSERT(container != NULL);
10564
wxRichTextObjectList::compatibility_iterator node = fragment.GetChildren().GetFirst();
10567
wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
10568
wxASSERT (para != NULL);
10570
// We'll replace the existing paragraph by finding the paragraph at this position,
10571
// delete its node data, and setting a copy as the new node data.
10572
// TODO: make more efficient by simply swapping old and new paragraph objects.
10574
wxRichTextParagraph* existingPara = container->GetParagraphAtPosition(para->GetRange().GetStart());
10577
wxRichTextObjectList::compatibility_iterator bufferParaNode = container->GetChildren().Find(existingPara);
10578
if (bufferParaNode)
10580
wxRichTextParagraph* newPara = new wxRichTextParagraph(*para);
10581
newPara->SetParent(container);
10583
bufferParaNode->SetData(newPara);
10585
delete existingPara;
10589
node = node->GetNext();
10596
* This stores beginning and end positions for a range of data.
10599
WX_DEFINE_OBJARRAY(wxRichTextRangeArray);
10601
/// Limit this range to be within 'range'
10602
bool wxRichTextRange::LimitTo(const wxRichTextRange& range)
10604
if (m_start < range.m_start)
10605
m_start = range.m_start;
10607
if (m_end > range.m_end)
10608
m_end = range.m_end;
10614
* wxRichTextImage implementation
10615
* This object represents an image.
10618
IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage, wxRichTextObject)
10620
wxRichTextImage::wxRichTextImage(const wxImage& image, wxRichTextObject* parent, wxRichTextAttr* charStyle):
10621
wxRichTextObject(parent)
10624
m_imageBlock.MakeImageBlockDefaultQuality(image, wxBITMAP_TYPE_PNG);
10626
SetAttributes(*charStyle);
10629
wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock& imageBlock, wxRichTextObject* parent, wxRichTextAttr* charStyle):
10630
wxRichTextObject(parent)
10633
m_imageBlock = imageBlock;
10635
SetAttributes(*charStyle);
10638
void wxRichTextImage::Init()
10640
m_originalImageSize = wxSize(-1, -1);
10643
/// Create a cached image at the required size
10644
bool wxRichTextImage::LoadImageCache(wxDC& dc, bool resetCache)
10646
if (!m_imageBlock.IsOk())
10649
// If we have an original image size, use that to compute the cached bitmap size
10650
// instead of loading the image each time. This way we can avoid loading
10651
// the image so long as the new cached bitmap size hasn't changed.
10654
if (resetCache || m_originalImageSize == wxSize(-1, -1))
10656
m_imageCache = wxNullBitmap;
10658
m_imageBlock.Load(image);
10662
m_originalImageSize = wxSize(image.GetWidth(), image.GetHeight());
10665
int width = m_originalImageSize.GetWidth();
10666
int height = m_originalImageSize.GetHeight();
10668
int parentWidth = 0;
10669
int parentHeight = 0;
10672
int maxHeight = -1;
10674
wxRichTextBuffer* buffer = GetBuffer();
10678
if (buffer->GetRichTextCtrl())
10680
// Subtract borders
10681
sz = buffer->GetRichTextCtrl()->GetClientSize();
10683
wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
10684
marginRect = wxRect(0, 0, sz.x, sz.y);
10685
buffer->GetBoxRects(dc, buffer, buffer->GetAttributes(), marginRect, borderRect, contentRect, paddingRect, outlineRect);
10687
sz = contentRect.GetSize();
10689
// Start with a maximum width of the control size, even if not specified by the content,
10690
// to minimize the amount of picture overlapping the right-hand side
10694
sz = buffer->GetCachedSize();
10695
parentWidth = sz.GetWidth();
10696
parentHeight = sz.GetHeight();
10699
if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetWidth().GetValue() > 0)
10701
if (parentWidth > 0 && GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
10702
width = (int) ((GetAttributes().GetTextBoxAttr().GetWidth().GetValue() * parentWidth)/100.0);
10703
else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
10704
width = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetWidth().GetValue());
10705
else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
10706
width = GetAttributes().GetTextBoxAttr().GetWidth().GetValue();
10709
// Limit to max width
10711
if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() > 0)
10715
if (parentWidth > 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
10716
mw = (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() * parentWidth)/100.0);
10717
else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
10718
mw = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue());
10719
else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
10720
mw = GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue();
10722
// If we already have a smaller max width due to the constraints of the control size,
10723
// don't use the larger max width.
10724
if (mw != -1 && ((maxWidth == -1) || (mw < maxWidth)))
10728
if (maxWidth > 0 && width > maxWidth)
10731
// Preserve the aspect ratio
10732
if (width != m_originalImageSize.GetWidth())
10733
height = (int) (float(m_originalImageSize.GetHeight()) * (float(width)/float(m_originalImageSize.GetWidth())));
10735
if (GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetHeight().GetValue() > 0)
10737
if (parentHeight > 0 && GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
10738
height = (int) ((GetAttributes().GetTextBoxAttr().GetHeight().GetValue() * parentHeight)/100.0);
10739
else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
10740
height = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetHeight().GetValue());
10741
else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
10742
height = GetAttributes().GetTextBoxAttr().GetHeight().GetValue();
10744
// Preserve the aspect ratio
10745
if (height != m_originalImageSize.GetHeight())
10746
width = (int) (float(m_originalImageSize.GetWidth()) * (float(height)/float(m_originalImageSize.GetHeight())));
10749
// Limit to max height
10751
if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() > 0)
10753
if (parentHeight > 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
10754
maxHeight = (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() * parentHeight)/100.0);
10755
else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
10756
maxHeight = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue());
10757
else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
10758
maxHeight = GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue();
10761
if (maxHeight > 0 && height > maxHeight)
10763
height = maxHeight;
10765
// Preserve the aspect ratio
10766
if (height != m_originalImageSize.GetHeight())
10767
width = (int) (float(m_originalImageSize.GetWidth()) * (float(height)/float(m_originalImageSize.GetHeight())));
10770
if (m_imageCache.IsOk() && m_imageCache.GetWidth() == width && m_imageCache.GetHeight() == height)
10772
// Do nothing, we didn't need to change the image cache
10778
m_imageBlock.Load(image);
10783
if (image.GetWidth() == width && image.GetHeight() == height)
10784
m_imageCache = wxBitmap(image);
10787
// If the original width and height is small, e.g. 400 or below,
10788
// scale up and then down to improve image quality. This can make
10789
// a big difference, with not much performance hit.
10790
int upscaleThreshold = 400;
10792
if (image.GetWidth() <= upscaleThreshold || image.GetHeight() <= upscaleThreshold)
10794
img = image.Scale(image.GetWidth()*2, image.GetHeight()*2);
10795
img.Rescale(width, height, wxIMAGE_QUALITY_HIGH);
10798
img = image.Scale(width, height, wxIMAGE_QUALITY_HIGH);
10799
m_imageCache = wxBitmap(img);
10803
return m_imageCache.IsOk();
10807
bool wxRichTextImage::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& WXUNUSED(range), const wxRichTextSelection& selection, const wxRect& rect, int WXUNUSED(descent), int WXUNUSED(style))
10812
// Don't need cached size AFAIK
10813
// wxSize size = GetCachedSize();
10814
if (!LoadImageCache(dc))
10817
wxRichTextAttr attr(GetAttributes());
10818
context.ApplyVirtualAttributes(attr, this);
10820
DrawBoxAttributes(dc, GetBuffer(), attr, wxRect(rect.GetPosition(), GetCachedSize()));
10822
wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
10823
wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
10824
marginRect = rect; // outer rectangle, will calculate contentRect
10825
GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
10827
dc.DrawBitmap(m_imageCache, contentRect.x, contentRect.y, true);
10829
if (selection.WithinSelection(GetRange().GetStart(), this))
10831
wxCheckSetBrush(dc, *wxBLACK_BRUSH);
10832
wxCheckSetPen(dc, *wxBLACK_PEN);
10833
dc.SetLogicalFunction(wxINVERT);
10834
dc.DrawRectangle(contentRect);
10835
dc.SetLogicalFunction(wxCOPY);
10841
/// Lay the item out
10842
bool wxRichTextImage::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& WXUNUSED(parentRect), int WXUNUSED(style))
10844
if (!LoadImageCache(dc))
10847
wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
10848
wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
10849
contentRect = wxRect(wxPoint(0,0), imageSize);
10851
wxRichTextAttr attr(GetAttributes());
10852
context.ApplyVirtualAttributes(attr, this);
10854
GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
10856
wxSize overallSize = marginRect.GetSize();
10858
SetCachedSize(overallSize);
10859
SetMaxSize(overallSize);
10860
SetMinSize(overallSize);
10861
SetPosition(rect.GetPosition());
10866
/// Get/set the object size for the given range. Returns false if the range
10867
/// is invalid for this object.
10868
bool wxRichTextImage::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& WXUNUSED(descent), wxDC& dc, wxRichTextDrawingContext& context, int WXUNUSED(flags), wxPoint WXUNUSED(position), wxArrayInt* partialExtents) const
10870
if (!range.IsWithin(GetRange()))
10873
if (!((wxRichTextImage*)this)->LoadImageCache(dc))
10875
size.x = 0; size.y = 0;
10876
if (partialExtents)
10877
partialExtents->Add(0);
10881
wxRichTextAttr attr(GetAttributes());
10882
context.ApplyVirtualAttributes(attr, (wxRichTextObject*) this);
10884
wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
10885
wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
10886
contentRect = wxRect(wxPoint(0,0), imageSize);
10887
GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
10889
wxSize overallSize = marginRect.GetSize();
10891
if (partialExtents)
10892
partialExtents->Add(overallSize.x);
10894
size = overallSize;
10899
// Get the 'natural' size for an object. For an image, it would be the
10901
wxTextAttrSize wxRichTextImage::GetNaturalSize() const
10903
wxTextAttrSize size;
10904
if (GetImageCache().IsOk())
10906
size.SetWidth(GetImageCache().GetWidth(), wxTEXT_ATTR_UNITS_PIXELS);
10907
size.SetHeight(GetImageCache().GetHeight(), wxTEXT_ATTR_UNITS_PIXELS);
10914
void wxRichTextImage::Copy(const wxRichTextImage& obj)
10916
wxRichTextObject::Copy(obj);
10918
m_imageBlock = obj.m_imageBlock;
10919
m_originalImageSize = obj.m_originalImageSize;
10922
/// Edit properties via a GUI
10923
bool wxRichTextImage::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
10925
wxRichTextObjectPropertiesDialog imageDlg(this, wxGetTopLevelParent(parent), wxID_ANY, _("Picture Properties"));
10926
imageDlg.SetAttributes(GetAttributes());
10928
if (imageDlg.ShowModal() == wxID_OK)
10930
// By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
10931
// indeterminate in the object.
10932
imageDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
10944
/// Compare two attribute objects
10945
bool wxTextAttrEq(const wxRichTextAttr& attr1, const wxRichTextAttr& attr2)
10947
return (attr1 == attr2);
10951
bool wxRichTextTabsEq(const wxArrayInt& tabs1, const wxArrayInt& tabs2)
10953
if (tabs1.GetCount() != tabs2.GetCount())
10957
for (i = 0; i < tabs1.GetCount(); i++)
10959
if (tabs1[i] != tabs2[i])
10965
bool wxRichTextApplyStyle(wxRichTextAttr& destStyle, const wxRichTextAttr& style, wxRichTextAttr* compareWith)
10967
return destStyle.Apply(style, compareWith);
10970
// Remove attributes
10971
bool wxRichTextRemoveStyle(wxRichTextAttr& destStyle, const wxRichTextAttr& style)
10973
return destStyle.RemoveStyle(style);
10976
/// Combine two bitlists, specifying the bits of interest with separate flags.
10977
bool wxRichTextCombineBitlists(int& valueA, int valueB, int& flagsA, int flagsB)
10979
return wxRichTextAttr::CombineBitlists(valueA, valueB, flagsA, flagsB);
10982
/// Compare two bitlists
10983
bool wxRichTextBitlistsEqPartial(int valueA, int valueB, int flags)
10985
return wxRichTextAttr::BitlistsEqPartial(valueA, valueB, flags);
10988
/// Split into paragraph and character styles
10989
bool wxRichTextSplitParaCharStyles(const wxRichTextAttr& style, wxRichTextAttr& parStyle, wxRichTextAttr& charStyle)
10991
return wxRichTextAttr::SplitParaCharStyles(style, parStyle, charStyle);
10994
/// Convert a decimal to Roman numerals
10995
wxString wxRichTextDecimalToRoman(long n)
10997
static wxArrayInt decimalNumbers;
10998
static wxArrayString romanNumbers;
11003
decimalNumbers.Clear();
11004
romanNumbers.Clear();
11005
return wxEmptyString;
11008
if (decimalNumbers.GetCount() == 0)
11010
#define wxRichTextAddDecRom(n, r) decimalNumbers.Add(n); romanNumbers.Add(r);
11012
wxRichTextAddDecRom(1000, wxT("M"));
11013
wxRichTextAddDecRom(900, wxT("CM"));
11014
wxRichTextAddDecRom(500, wxT("D"));
11015
wxRichTextAddDecRom(400, wxT("CD"));
11016
wxRichTextAddDecRom(100, wxT("C"));
11017
wxRichTextAddDecRom(90, wxT("XC"));
11018
wxRichTextAddDecRom(50, wxT("L"));
11019
wxRichTextAddDecRom(40, wxT("XL"));
11020
wxRichTextAddDecRom(10, wxT("X"));
11021
wxRichTextAddDecRom(9, wxT("IX"));
11022
wxRichTextAddDecRom(5, wxT("V"));
11023
wxRichTextAddDecRom(4, wxT("IV"));
11024
wxRichTextAddDecRom(1, wxT("I"));
11030
while (n > 0 && i < 13)
11032
if (n >= decimalNumbers[i])
11034
n -= decimalNumbers[i];
11035
roman += romanNumbers[i];
11042
if (roman.IsEmpty())
11048
* wxRichTextFileHandler
11049
* Base class for file handlers
11052
IMPLEMENT_CLASS(wxRichTextFileHandler, wxObject)
11054
#if wxUSE_FFILE && wxUSE_STREAMS
11055
bool wxRichTextFileHandler::LoadFile(wxRichTextBuffer *buffer, const wxString& filename)
11057
wxFFileInputStream stream(filename);
11059
return LoadFile(buffer, stream);
11064
bool wxRichTextFileHandler::SaveFile(wxRichTextBuffer *buffer, const wxString& filename)
11066
wxFFileOutputStream stream(filename);
11068
return SaveFile(buffer, stream);
11072
#endif // wxUSE_FFILE && wxUSE_STREAMS
11074
/// Can we handle this filename (if using files)? By default, checks the extension.
11075
bool wxRichTextFileHandler::CanHandle(const wxString& filename) const
11077
wxString path, file, ext;
11078
wxFileName::SplitPath(filename, & path, & file, & ext);
11080
return (ext.Lower() == GetExtension());
11084
* wxRichTextTextHandler
11085
* Plain text handler
11088
IMPLEMENT_CLASS(wxRichTextPlainTextHandler, wxRichTextFileHandler)
11091
bool wxRichTextPlainTextHandler::DoLoadFile(wxRichTextBuffer *buffer, wxInputStream& stream)
11093
if (!stream.IsOk())
11099
while (!stream.Eof())
11101
int ch = stream.GetC();
11105
if (ch == 10 && lastCh != 13)
11108
if (ch > 0 && ch != 10)
11115
buffer->ResetAndClearCommands();
11117
buffer->AddParagraphs(str);
11118
buffer->UpdateRanges();
11123
bool wxRichTextPlainTextHandler::DoSaveFile(wxRichTextBuffer *buffer, wxOutputStream& stream)
11125
if (!stream.IsOk())
11128
wxString text = buffer->GetText();
11130
wxString newLine = wxRichTextLineBreakChar;
11131
text.Replace(newLine, wxT("\n"));
11133
wxCharBuffer buf = text.ToAscii();
11135
stream.Write((const char*) buf, text.length());
11138
#endif // wxUSE_STREAMS
11141
* Stores information about an image, in binary in-memory form
11144
wxRichTextImageBlock::wxRichTextImageBlock()
11149
wxRichTextImageBlock::wxRichTextImageBlock(const wxRichTextImageBlock& block):wxObject()
11155
wxRichTextImageBlock::~wxRichTextImageBlock()
11160
void wxRichTextImageBlock::Init()
11164
m_imageType = wxBITMAP_TYPE_INVALID;
11167
void wxRichTextImageBlock::Clear()
11171
m_imageType = wxBITMAP_TYPE_INVALID;
11175
// Load the original image into a memory block.
11176
// If the image is not a JPEG, we must convert it into a JPEG
11177
// to conserve space.
11178
// If it's not a JPEG we can make use of 'image', already scaled, so we don't have to
11179
// load the image a 2nd time.
11181
bool wxRichTextImageBlock::MakeImageBlock(const wxString& filename, wxBitmapType imageType,
11182
wxImage& image, bool convertToJPEG)
11184
m_imageType = imageType;
11186
wxString filenameToRead(filename);
11187
bool removeFile = false;
11189
if (imageType == wxBITMAP_TYPE_INVALID)
11190
return false; // Could not determine image type
11192
if ((imageType != wxBITMAP_TYPE_JPEG) && convertToJPEG)
11194
wxString tempFile =
11195
wxFileName::CreateTempFileName(_("image"));
11197
wxASSERT(!tempFile.IsEmpty());
11199
image.SaveFile(tempFile, wxBITMAP_TYPE_JPEG);
11200
filenameToRead = tempFile;
11203
m_imageType = wxBITMAP_TYPE_JPEG;
11206
if (!file.Open(filenameToRead))
11209
m_dataSize = (size_t) file.Length();
11214
m_data = ReadBlock(filenameToRead, m_dataSize);
11217
wxRemoveFile(filenameToRead);
11219
return (m_data != NULL);
11222
// Make an image block from the wxImage in the given
11224
bool wxRichTextImageBlock::MakeImageBlock(wxImage& image, wxBitmapType imageType, int quality)
11226
image.SetOption(wxT("quality"), quality);
11228
if (imageType == wxBITMAP_TYPE_INVALID)
11229
return false; // Could not determine image type
11231
return DoMakeImageBlock(image, imageType);
11234
// Uses a const wxImage for efficiency, but can't set quality (only relevant for JPEG)
11235
bool wxRichTextImageBlock::MakeImageBlockDefaultQuality(const wxImage& image, wxBitmapType imageType)
11237
if (imageType == wxBITMAP_TYPE_INVALID)
11238
return false; // Could not determine image type
11240
return DoMakeImageBlock(image, imageType);
11243
// Makes the image block
11244
bool wxRichTextImageBlock::DoMakeImageBlock(const wxImage& image, wxBitmapType imageType)
11246
wxMemoryOutputStream memStream;
11247
if (!image.SaveFile(memStream, imageType))
11252
unsigned char* block = new unsigned char[memStream.GetSize()];
11260
m_imageType = imageType;
11261
m_dataSize = memStream.GetSize();
11263
memStream.CopyTo(m_data, m_dataSize);
11265
return (m_data != NULL);
11269
bool wxRichTextImageBlock::Write(const wxString& filename)
11271
return WriteBlock(filename, m_data, m_dataSize);
11274
void wxRichTextImageBlock::Copy(const wxRichTextImageBlock& block)
11276
m_imageType = block.m_imageType;
11278
m_dataSize = block.m_dataSize;
11279
if (m_dataSize == 0)
11282
m_data = new unsigned char[m_dataSize];
11284
for (i = 0; i < m_dataSize; i++)
11285
m_data[i] = block.m_data[i];
11289
void wxRichTextImageBlock::operator=(const wxRichTextImageBlock& block)
11294
// Load a wxImage from the block
11295
bool wxRichTextImageBlock::Load(wxImage& image)
11300
// Read in the image.
11302
wxMemoryInputStream mstream(m_data, m_dataSize);
11303
bool success = image.LoadFile(mstream, GetImageType());
11305
wxString tempFile = wxFileName::CreateTempFileName(_("image"));
11306
wxASSERT(!tempFile.IsEmpty());
11308
if (!WriteBlock(tempFile, m_data, m_dataSize))
11312
success = image.LoadFile(tempFile, GetImageType());
11313
wxRemoveFile(tempFile);
11319
// Write data in hex to a stream
11320
bool wxRichTextImageBlock::WriteHex(wxOutputStream& stream)
11322
if (m_dataSize == 0)
11325
int bufSize = 100000;
11326
if (int(2*m_dataSize) < bufSize)
11327
bufSize = 2*m_dataSize;
11328
char* buf = new char[bufSize+1];
11330
int left = m_dataSize;
11335
if (left*2 > bufSize)
11337
n = bufSize; left -= (bufSize/2);
11341
n = left*2; left = 0;
11345
for (i = 0; i < (n/2); i++)
11347
wxDecToHex(m_data[j], b, b+1);
11352
stream.Write((const char*) buf, n);
11358
// Read data in hex from a stream
11359
bool wxRichTextImageBlock::ReadHex(wxInputStream& stream, int length, wxBitmapType imageType)
11361
int dataSize = length/2;
11366
// create a null terminated temporary string:
11370
m_data = new unsigned char[dataSize];
11372
for (i = 0; i < dataSize; i ++)
11374
str[0] = (char)stream.GetC();
11375
str[1] = (char)stream.GetC();
11377
m_data[i] = (unsigned char)wxHexToDec(str);
11380
m_dataSize = dataSize;
11381
m_imageType = imageType;
11386
// Allocate and read from stream as a block of memory
11387
unsigned char* wxRichTextImageBlock::ReadBlock(wxInputStream& stream, size_t size)
11389
unsigned char* block = new unsigned char[size];
11393
stream.Read(block, size);
11398
unsigned char* wxRichTextImageBlock::ReadBlock(const wxString& filename, size_t size)
11400
wxFileInputStream stream(filename);
11401
if (!stream.IsOk())
11404
return ReadBlock(stream, size);
11407
// Write memory block to stream
11408
bool wxRichTextImageBlock::WriteBlock(wxOutputStream& stream, unsigned char* block, size_t size)
11410
stream.Write((void*) block, size);
11411
return stream.IsOk();
11415
// Write memory block to file
11416
bool wxRichTextImageBlock::WriteBlock(const wxString& filename, unsigned char* block, size_t size)
11418
wxFileOutputStream outStream(filename);
11419
if (!outStream.IsOk())
11422
return WriteBlock(outStream, block, size);
11425
// Gets the extension for the block's type
11426
wxString wxRichTextImageBlock::GetExtension() const
11428
wxImageHandler* handler = wxImage::FindHandler(GetImageType());
11430
return handler->GetExtension();
11432
return wxEmptyString;
11438
* The data object for a wxRichTextBuffer
11441
const wxChar *wxRichTextBufferDataObject::ms_richTextBufferFormatId = wxT("wxShape");
11443
wxRichTextBufferDataObject::wxRichTextBufferDataObject(wxRichTextBuffer* richTextBuffer)
11445
m_richTextBuffer = richTextBuffer;
11447
// this string should uniquely identify our format, but is otherwise
11449
m_formatRichTextBuffer.SetId(GetRichTextBufferFormatId());
11451
SetFormat(m_formatRichTextBuffer);
11454
wxRichTextBufferDataObject::~wxRichTextBufferDataObject()
11456
delete m_richTextBuffer;
11459
// after a call to this function, the richTextBuffer is owned by the caller and it
11460
// is responsible for deleting it!
11461
wxRichTextBuffer* wxRichTextBufferDataObject::GetRichTextBuffer()
11463
wxRichTextBuffer* richTextBuffer = m_richTextBuffer;
11464
m_richTextBuffer = NULL;
11466
return richTextBuffer;
11469
wxDataFormat wxRichTextBufferDataObject::GetPreferredFormat(Direction WXUNUSED(dir)) const
11471
return m_formatRichTextBuffer;
11474
size_t wxRichTextBufferDataObject::GetDataSize() const
11476
if (!m_richTextBuffer)
11482
wxStringOutputStream stream(& bufXML);
11483
if (!m_richTextBuffer->SaveFile(stream, wxRICHTEXT_TYPE_XML))
11485
wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
11491
wxCharBuffer buffer = bufXML.mb_str(wxConvUTF8);
11492
return strlen(buffer) + 1;
11494
return bufXML.Length()+1;
11498
bool wxRichTextBufferDataObject::GetDataHere(void *pBuf) const
11500
if (!pBuf || !m_richTextBuffer)
11506
wxStringOutputStream stream(& bufXML);
11507
if (!m_richTextBuffer->SaveFile(stream, wxRICHTEXT_TYPE_XML))
11509
wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
11515
wxCharBuffer buffer = bufXML.mb_str(wxConvUTF8);
11516
size_t len = strlen(buffer);
11517
memcpy((char*) pBuf, (const char*) buffer, len);
11518
((char*) pBuf)[len] = 0;
11520
size_t len = bufXML.Length();
11521
memcpy((char*) pBuf, (const char*) bufXML.c_str(), len);
11522
((char*) pBuf)[len] = 0;
11528
bool wxRichTextBufferDataObject::SetData(size_t WXUNUSED(len), const void *buf)
11530
wxDELETE(m_richTextBuffer);
11532
wxString bufXML((const char*) buf, wxConvUTF8);
11534
m_richTextBuffer = new wxRichTextBuffer;
11536
wxStringInputStream stream(bufXML);
11537
if (!m_richTextBuffer->LoadFile(stream, wxRICHTEXT_TYPE_XML))
11539
wxLogError(wxT("Could not read the buffer from an XML stream.\nYou may have forgotten to add the XML file handler."));
11541
wxDELETE(m_richTextBuffer);
11553
* wxRichTextFontTable
11554
* Manages quick access to a pool of fonts for rendering rich text
11557
WX_DECLARE_STRING_HASH_MAP_WITH_DECL(wxFont, wxRichTextFontTableHashMap, class WXDLLIMPEXP_RICHTEXT);
11559
class wxRichTextFontTableData: public wxObjectRefData
11562
wxRichTextFontTableData() {}
11564
wxFont FindFont(const wxRichTextAttr& fontSpec, double fontScale);
11566
wxRichTextFontTableHashMap m_hashMap;
11569
wxFont wxRichTextFontTableData::FindFont(const wxRichTextAttr& fontSpec, double fontScale)
11571
wxString facename(fontSpec.GetFontFaceName());
11573
int fontSize = fontSpec.GetFontSize();
11574
if (fontScale != 1.0)
11575
fontSize = (int) ((double(fontSize) * fontScale) + 0.5);
11578
if (fontSpec.HasFontPixelSize() && !fontSpec.HasFontPointSize())
11582
wxString spec = wxString::Format(wxT("%d-%s-%d-%d-%d-%d-%s-%d"),
11583
fontSize, units.c_str(), fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), (int) fontSpec.GetFontUnderlined(), (int) fontSpec.GetFontStrikethrough(),
11584
facename.c_str(), (int) fontSpec.GetFontEncoding());
11586
wxRichTextFontTableHashMap::iterator entry = m_hashMap.find(spec);
11587
if ( entry == m_hashMap.end() )
11589
if (fontSpec.HasFontPixelSize() && !fontSpec.HasFontPointSize())
11591
wxFont font(wxSize(0, fontSize), wxFONTFAMILY_DEFAULT, fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), fontSpec.GetFontUnderlined(), facename);
11592
if (fontSpec.HasFontStrikethrough() && fontSpec.GetFontStrikethrough())
11593
font.SetStrikethrough(true);
11594
m_hashMap[spec] = font;
11599
wxFont font(fontSize, wxFONTFAMILY_DEFAULT, fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), fontSpec.GetFontUnderlined(), facename.c_str());
11600
if (fontSpec.HasFontStrikethrough() && fontSpec.GetFontStrikethrough())
11601
font.SetStrikethrough(true);
11603
m_hashMap[spec] = font;
11609
return entry->second;
11613
IMPLEMENT_DYNAMIC_CLASS(wxRichTextFontTable, wxObject)
11615
wxRichTextFontTable::wxRichTextFontTable()
11617
m_refData = new wxRichTextFontTableData;
11621
wxRichTextFontTable::wxRichTextFontTable(const wxRichTextFontTable& table)
11627
wxRichTextFontTable::~wxRichTextFontTable()
11632
bool wxRichTextFontTable::operator == (const wxRichTextFontTable& table) const
11634
return (m_refData == table.m_refData);
11637
void wxRichTextFontTable::operator= (const wxRichTextFontTable& table)
11640
m_fontScale = table.m_fontScale;
11643
wxFont wxRichTextFontTable::FindFont(const wxRichTextAttr& fontSpec)
11645
wxRichTextFontTableData* data = (wxRichTextFontTableData*) m_refData;
11647
return data->FindFont(fontSpec, m_fontScale);
11652
void wxRichTextFontTable::Clear()
11654
wxRichTextFontTableData* data = (wxRichTextFontTableData*) m_refData;
11656
data->m_hashMap.clear();
11659
void wxRichTextFontTable::SetFontScale(double fontScale)
11661
if (fontScale != m_fontScale)
11663
m_fontScale = fontScale;
11668
void wxTextBoxAttr::Reset()
11671
m_floatMode = wxTEXT_BOX_ATTR_FLOAT_NONE;
11672
m_clearMode = wxTEXT_BOX_ATTR_CLEAR_NONE;
11673
m_collapseMode = wxTEXT_BOX_ATTR_COLLAPSE_NONE;
11674
m_verticalAlignment = wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_NONE;
11675
m_boxStyleName = wxEmptyString;
11679
m_position.Reset();
11690
bool wxTextBoxAttr::operator== (const wxTextBoxAttr& attr) const
11693
m_flags == attr.m_flags &&
11694
m_floatMode == attr.m_floatMode &&
11695
m_clearMode == attr.m_clearMode &&
11696
m_collapseMode == attr.m_collapseMode &&
11697
m_verticalAlignment == attr.m_verticalAlignment &&
11699
m_margins == attr.m_margins &&
11700
m_padding == attr.m_padding &&
11701
m_position == attr.m_position &&
11703
m_size == attr.m_size &&
11704
m_minSize == attr.m_minSize &&
11705
m_maxSize == attr.m_maxSize &&
11707
m_border == attr.m_border &&
11708
m_outline == attr.m_outline &&
11710
m_boxStyleName == attr.m_boxStyleName
11714
// Partial equality test
11715
bool wxTextBoxAttr::EqPartial(const wxTextBoxAttr& attr, bool weakTest) const
11718
((!HasFloatMode() && attr.HasFloatMode()) ||
11719
(!HasClearMode() && attr.HasClearMode()) ||
11720
(!HasCollapseBorders() && attr.HasCollapseBorders()) ||
11721
(!HasVerticalAlignment() && attr.HasVerticalAlignment()) ||
11722
(!HasBoxStyleName() && attr.HasBoxStyleName())))
11726
if (attr.HasFloatMode() && HasFloatMode() && (GetFloatMode() != attr.GetFloatMode()))
11729
if (attr.HasClearMode() && HasClearMode() && (GetClearMode() != attr.GetClearMode()))
11732
if (attr.HasCollapseBorders() && HasCollapseBorders() && (attr.GetCollapseBorders() != GetCollapseBorders()))
11735
if (attr.HasVerticalAlignment() && HasVerticalAlignment() && (attr.GetVerticalAlignment() != GetVerticalAlignment()))
11738
if (attr.HasBoxStyleName() && HasBoxStyleName() && (attr.GetBoxStyleName() != GetBoxStyleName()))
11743
if (!m_position.EqPartial(attr.m_position, weakTest))
11748
if (!m_size.EqPartial(attr.m_size, weakTest))
11750
if (!m_minSize.EqPartial(attr.m_minSize, weakTest))
11752
if (!m_maxSize.EqPartial(attr.m_maxSize, weakTest))
11757
if (!m_margins.EqPartial(attr.m_margins, weakTest))
11762
if (!m_padding.EqPartial(attr.m_padding, weakTest))
11767
if (!GetBorder().EqPartial(attr.GetBorder(), weakTest))
11772
if (!GetOutline().EqPartial(attr.GetOutline(), weakTest))
11778
// Merges the given attributes. If compareWith
11779
// is non-NULL, then it will be used to mask out those attributes that are the same in style
11780
// and compareWith, for situations where we don't want to explicitly set inherited attributes.
11781
bool wxTextBoxAttr::Apply(const wxTextBoxAttr& attr, const wxTextBoxAttr* compareWith)
11783
if (attr.HasFloatMode())
11785
if (!(compareWith && compareWith->HasFloatMode() && compareWith->GetFloatMode() == attr.GetFloatMode()))
11786
SetFloatMode(attr.GetFloatMode());
11789
if (attr.HasClearMode())
11791
if (!(compareWith && compareWith->HasClearMode() && compareWith->GetClearMode() == attr.GetClearMode()))
11792
SetClearMode(attr.GetClearMode());
11795
if (attr.HasCollapseBorders())
11797
if (!(compareWith && compareWith->HasCollapseBorders() && compareWith->GetCollapseBorders() == attr.GetCollapseBorders()))
11798
SetCollapseBorders(attr.GetCollapseBorders());
11801
if (attr.HasVerticalAlignment())
11803
if (!(compareWith && compareWith->HasVerticalAlignment() && compareWith->GetVerticalAlignment() == attr.GetVerticalAlignment()))
11804
SetVerticalAlignment(attr.GetVerticalAlignment());
11807
if (attr.HasBoxStyleName())
11809
if (!(compareWith && compareWith->HasBoxStyleName() && compareWith->GetBoxStyleName() == attr.GetBoxStyleName()))
11810
SetBoxStyleName(attr.GetBoxStyleName());
11813
m_margins.Apply(attr.m_margins, compareWith ? (& attr.m_margins) : (const wxTextAttrDimensions*) NULL);
11814
m_padding.Apply(attr.m_padding, compareWith ? (& attr.m_padding) : (const wxTextAttrDimensions*) NULL);
11815
m_position.Apply(attr.m_position, compareWith ? (& attr.m_position) : (const wxTextAttrDimensions*) NULL);
11817
m_size.Apply(attr.m_size, compareWith ? (& attr.m_size) : (const wxTextAttrSize*) NULL);
11818
m_minSize.Apply(attr.m_minSize, compareWith ? (& attr.m_minSize) : (const wxTextAttrSize*) NULL);
11819
m_maxSize.Apply(attr.m_maxSize, compareWith ? (& attr.m_maxSize) : (const wxTextAttrSize*) NULL);
11821
m_border.Apply(attr.m_border, compareWith ? (& attr.m_border) : (const wxTextAttrBorders*) NULL);
11822
m_outline.Apply(attr.m_outline, compareWith ? (& attr.m_outline) : (const wxTextAttrBorders*) NULL);
11827
// Remove specified attributes from this object
11828
bool wxTextBoxAttr::RemoveStyle(const wxTextBoxAttr& attr)
11830
if (attr.HasFloatMode())
11831
RemoveFlag(wxTEXT_BOX_ATTR_FLOAT);
11833
if (attr.HasClearMode())
11834
RemoveFlag(wxTEXT_BOX_ATTR_CLEAR);
11836
if (attr.HasCollapseBorders())
11837
RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
11839
if (attr.HasVerticalAlignment())
11840
RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
11842
if (attr.HasBoxStyleName())
11844
SetBoxStyleName(wxEmptyString);
11845
RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
11848
m_margins.RemoveStyle(attr.m_margins);
11849
m_padding.RemoveStyle(attr.m_padding);
11850
m_position.RemoveStyle(attr.m_position);
11852
m_size.RemoveStyle(attr.m_size);
11853
m_minSize.RemoveStyle(attr.m_minSize);
11854
m_maxSize.RemoveStyle(attr.m_maxSize);
11856
m_border.RemoveStyle(attr.m_border);
11857
m_outline.RemoveStyle(attr.m_outline);
11862
// Collects the attributes that are common to a range of content, building up a note of
11863
// which attributes are absent in some objects and which clash in some objects.
11864
void wxTextBoxAttr::CollectCommonAttributes(const wxTextBoxAttr& attr, wxTextBoxAttr& clashingAttr, wxTextBoxAttr& absentAttr)
11866
if (attr.HasFloatMode())
11868
if (!clashingAttr.HasFloatMode() && !absentAttr.HasFloatMode())
11870
if (HasFloatMode())
11872
if (GetFloatMode() != attr.GetFloatMode())
11874
clashingAttr.AddFlag(wxTEXT_BOX_ATTR_FLOAT);
11875
RemoveFlag(wxTEXT_BOX_ATTR_FLOAT);
11879
SetFloatMode(attr.GetFloatMode());
11883
absentAttr.AddFlag(wxTEXT_BOX_ATTR_FLOAT);
11885
if (attr.HasClearMode())
11887
if (!clashingAttr.HasClearMode() && !absentAttr.HasClearMode())
11889
if (HasClearMode())
11891
if (GetClearMode() != attr.GetClearMode())
11893
clashingAttr.AddFlag(wxTEXT_BOX_ATTR_CLEAR);
11894
RemoveFlag(wxTEXT_BOX_ATTR_CLEAR);
11898
SetClearMode(attr.GetClearMode());
11902
absentAttr.AddFlag(wxTEXT_BOX_ATTR_CLEAR);
11904
if (attr.HasCollapseBorders())
11906
if (!clashingAttr.HasCollapseBorders() && !absentAttr.HasCollapseBorders())
11908
if (HasCollapseBorders())
11910
if (GetCollapseBorders() != attr.GetCollapseBorders())
11912
clashingAttr.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
11913
RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
11917
SetCollapseBorders(attr.GetCollapseBorders());
11921
absentAttr.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
11923
if (attr.HasVerticalAlignment())
11925
if (!clashingAttr.HasVerticalAlignment() && !absentAttr.HasVerticalAlignment())
11927
if (HasVerticalAlignment())
11929
if (GetVerticalAlignment() != attr.GetVerticalAlignment())
11931
clashingAttr.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
11932
RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
11936
SetVerticalAlignment(attr.GetVerticalAlignment());
11940
absentAttr.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
11942
if (attr.HasBoxStyleName())
11944
if (!clashingAttr.HasBoxStyleName() && !absentAttr.HasBoxStyleName())
11946
if (HasBoxStyleName())
11948
if (GetBoxStyleName() != attr.GetBoxStyleName())
11950
clashingAttr.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
11951
RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
11955
SetBoxStyleName(attr.GetBoxStyleName());
11959
absentAttr.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
11961
m_margins.CollectCommonAttributes(attr.m_margins, clashingAttr.m_margins, absentAttr.m_margins);
11962
m_padding.CollectCommonAttributes(attr.m_padding, clashingAttr.m_padding, absentAttr.m_padding);
11963
m_position.CollectCommonAttributes(attr.m_position, clashingAttr.m_position, absentAttr.m_position);
11965
m_size.CollectCommonAttributes(attr.m_size, clashingAttr.m_size, absentAttr.m_size);
11966
m_minSize.CollectCommonAttributes(attr.m_minSize, clashingAttr.m_minSize, absentAttr.m_minSize);
11967
m_maxSize.CollectCommonAttributes(attr.m_maxSize, clashingAttr.m_maxSize, absentAttr.m_maxSize);
11969
m_border.CollectCommonAttributes(attr.m_border, clashingAttr.m_border, absentAttr.m_border);
11970
m_outline.CollectCommonAttributes(attr.m_outline, clashingAttr.m_outline, absentAttr.m_outline);
11973
bool wxTextBoxAttr::IsDefault() const
11975
return GetFlags() == 0 && !m_border.IsValid() && !m_outline.IsValid() &&
11976
!m_size.IsValid() && !m_minSize.IsValid() && !m_maxSize.IsValid() &&
11977
!m_position.IsValid() && !m_padding.IsValid() && !m_margins.IsValid();
11982
void wxRichTextAttr::Copy(const wxRichTextAttr& attr)
11984
wxTextAttr::Copy(attr);
11986
m_textBoxAttr = attr.m_textBoxAttr;
11989
bool wxRichTextAttr::operator==(const wxRichTextAttr& attr) const
11991
if (!(wxTextAttr::operator==(attr)))
11994
return (m_textBoxAttr == attr.m_textBoxAttr);
11997
// Partial equality test
11998
bool wxRichTextAttr::EqPartial(const wxRichTextAttr& attr, bool weakTest) const
12000
if (!(wxTextAttr::EqPartial(attr, weakTest)))
12003
return m_textBoxAttr.EqPartial(attr.m_textBoxAttr, weakTest);
12006
// Merges the given attributes. If compareWith
12007
// is non-NULL, then it will be used to mask out those attributes that are the same in style
12008
// and compareWith, for situations where we don't want to explicitly set inherited attributes.
12009
bool wxRichTextAttr::Apply(const wxRichTextAttr& style, const wxRichTextAttr* compareWith)
12011
wxTextAttr::Apply(style, compareWith);
12013
return m_textBoxAttr.Apply(style.m_textBoxAttr, compareWith ? (& compareWith->m_textBoxAttr) : (const wxTextBoxAttr*) NULL);
12016
// Remove specified attributes from this object
12017
bool wxRichTextAttr::RemoveStyle(const wxRichTextAttr& attr)
12019
wxTextAttr::RemoveStyle(*this, attr);
12021
return m_textBoxAttr.RemoveStyle(attr.m_textBoxAttr);
12024
// Collects the attributes that are common to a range of content, building up a note of
12025
// which attributes are absent in some objects and which clash in some objects.
12026
void wxRichTextAttr::CollectCommonAttributes(const wxRichTextAttr& attr, wxRichTextAttr& clashingAttr, wxRichTextAttr& absentAttr)
12028
wxTextAttrCollectCommonAttributes(*this, attr, clashingAttr, absentAttr);
12030
m_textBoxAttr.CollectCommonAttributes(attr.m_textBoxAttr, clashingAttr.m_textBoxAttr, absentAttr.m_textBoxAttr);
12033
// Partial equality test
12034
bool wxTextAttrBorder::EqPartial(const wxTextAttrBorder& border, bool weakTest) const
12037
((!HasStyle() && border.HasStyle()) ||
12038
(!HasColour() && border.HasColour()) ||
12039
(!HasWidth() && border.HasWidth())))
12044
if (border.HasStyle() && HasStyle() && (border.GetStyle() != GetStyle()))
12047
if (border.HasColour() && HasColour() && (border.GetColourLong() != GetColourLong()))
12050
if (border.HasWidth() && HasWidth() && !(border.GetWidth() == GetWidth()))
12056
// Apply border to 'this', but not if the same as compareWith
12057
bool wxTextAttrBorder::Apply(const wxTextAttrBorder& border, const wxTextAttrBorder* compareWith)
12059
if (border.HasStyle())
12061
if (!(compareWith && (border.GetStyle() == compareWith->GetStyle())))
12062
SetStyle(border.GetStyle());
12064
if (border.HasColour())
12066
if (!(compareWith && (border.GetColourLong() == compareWith->GetColourLong())))
12067
SetColour(border.GetColourLong());
12069
if (border.HasWidth())
12071
if (!(compareWith && (border.GetWidth() == compareWith->GetWidth())))
12072
SetWidth(border.GetWidth());
12078
// Remove specified attributes from this object
12079
bool wxTextAttrBorder::RemoveStyle(const wxTextAttrBorder& attr)
12081
if (attr.HasStyle() && HasStyle())
12082
SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_STYLE);
12083
if (attr.HasColour() && HasColour())
12084
SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_COLOUR);
12085
if (attr.HasWidth() && HasWidth())
12086
m_borderWidth.Reset();
12091
// Collects the attributes that are common to a range of content, building up a note of
12092
// which attributes are absent in some objects and which clash in some objects.
12093
void wxTextAttrBorder::CollectCommonAttributes(const wxTextAttrBorder& attr, wxTextAttrBorder& clashingAttr, wxTextAttrBorder& absentAttr)
12095
if (attr.HasStyle())
12097
if (!clashingAttr.HasStyle() && !absentAttr.HasStyle())
12101
if (GetStyle() != attr.GetStyle())
12103
clashingAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE);
12104
RemoveFlag(wxTEXT_BOX_ATTR_BORDER_STYLE);
12108
SetStyle(attr.GetStyle());
12112
absentAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE);
12114
if (attr.HasColour())
12116
if (!clashingAttr.HasColour() && !absentAttr.HasColour())
12120
if (GetColour() != attr.GetColour())
12122
clashingAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
12123
RemoveFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
12127
SetColour(attr.GetColourLong());
12131
absentAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
12133
m_borderWidth.CollectCommonAttributes(attr.m_borderWidth, clashingAttr.m_borderWidth, absentAttr.m_borderWidth);
12136
// Partial equality test
12137
bool wxTextAttrBorders::EqPartial(const wxTextAttrBorders& borders, bool weakTest) const
12139
return m_left.EqPartial(borders.m_left, weakTest) && m_right.EqPartial(borders.m_right, weakTest) &&
12140
m_top.EqPartial(borders.m_top, weakTest) && m_bottom.EqPartial(borders.m_bottom, weakTest);
12143
// Apply border to 'this', but not if the same as compareWith
12144
bool wxTextAttrBorders::Apply(const wxTextAttrBorders& borders, const wxTextAttrBorders* compareWith)
12146
m_left.Apply(borders.m_left, compareWith ? (& compareWith->m_left) : (const wxTextAttrBorder*) NULL);
12147
m_right.Apply(borders.m_right, compareWith ? (& compareWith->m_right) : (const wxTextAttrBorder*) NULL);
12148
m_top.Apply(borders.m_top, compareWith ? (& compareWith->m_top) : (const wxTextAttrBorder*) NULL);
12149
m_bottom.Apply(borders.m_bottom, compareWith ? (& compareWith->m_bottom) : (const wxTextAttrBorder*) NULL);
12153
// Remove specified attributes from this object
12154
bool wxTextAttrBorders::RemoveStyle(const wxTextAttrBorders& attr)
12156
m_left.RemoveStyle(attr.m_left);
12157
m_right.RemoveStyle(attr.m_right);
12158
m_top.RemoveStyle(attr.m_top);
12159
m_bottom.RemoveStyle(attr.m_bottom);
12163
// Collects the attributes that are common to a range of content, building up a note of
12164
// which attributes are absent in some objects and which clash in some objects.
12165
void wxTextAttrBorders::CollectCommonAttributes(const wxTextAttrBorders& attr, wxTextAttrBorders& clashingAttr, wxTextAttrBorders& absentAttr)
12167
m_left.CollectCommonAttributes(attr.m_left, clashingAttr.m_left, absentAttr.m_left);
12168
m_right.CollectCommonAttributes(attr.m_right, clashingAttr.m_right, absentAttr.m_right);
12169
m_top.CollectCommonAttributes(attr.m_top, clashingAttr.m_top, absentAttr.m_top);
12170
m_bottom.CollectCommonAttributes(attr.m_bottom, clashingAttr.m_bottom, absentAttr.m_bottom);
12173
// Set style of all borders
12174
void wxTextAttrBorders::SetStyle(int style)
12176
m_left.SetStyle(style);
12177
m_right.SetStyle(style);
12178
m_top.SetStyle(style);
12179
m_bottom.SetStyle(style);
12182
// Set colour of all borders
12183
void wxTextAttrBorders::SetColour(unsigned long colour)
12185
m_left.SetColour(colour);
12186
m_right.SetColour(colour);
12187
m_top.SetColour(colour);
12188
m_bottom.SetColour(colour);
12191
void wxTextAttrBorders::SetColour(const wxColour& colour)
12193
m_left.SetColour(colour);
12194
m_right.SetColour(colour);
12195
m_top.SetColour(colour);
12196
m_bottom.SetColour(colour);
12199
// Set width of all borders
12200
void wxTextAttrBorders::SetWidth(const wxTextAttrDimension& width)
12202
m_left.SetWidth(width);
12203
m_right.SetWidth(width);
12204
m_top.SetWidth(width);
12205
m_bottom.SetWidth(width);
12208
// Partial equality test
12209
bool wxTextAttrDimension::EqPartial(const wxTextAttrDimension& dim, bool weakTest) const
12211
if (!weakTest && !IsValid() && dim.IsValid())
12214
if (dim.IsValid() && IsValid() && !((*this) == dim))
12220
bool wxTextAttrDimension::Apply(const wxTextAttrDimension& dim, const wxTextAttrDimension* compareWith)
12224
if (!(compareWith && dim == (*compareWith)))
12231
// Collects the attributes that are common to a range of content, building up a note of
12232
// which attributes are absent in some objects and which clash in some objects.
12233
void wxTextAttrDimension::CollectCommonAttributes(const wxTextAttrDimension& attr, wxTextAttrDimension& clashingAttr, wxTextAttrDimension& absentAttr)
12235
if (attr.IsValid())
12237
if (!clashingAttr.IsValid() && !absentAttr.IsValid())
12241
if (!((*this) == attr))
12243
clashingAttr.SetValid(true);
12252
absentAttr.SetValid(true);
12255
wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(wxDC& dc, double scale, const wxSize& parentSize)
12257
m_ppi = dc.GetPPI().x; m_scale = scale; m_parentSize = parentSize;
12260
wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(int ppi, double scale, const wxSize& parentSize)
12262
m_ppi = ppi; m_scale = scale; m_parentSize = parentSize;
12265
int wxTextAttrDimensionConverter::ConvertTenthsMMToPixels(int units) const
12267
return wxRichTextObject::ConvertTenthsMMToPixels(m_ppi, units, m_scale);
12270
int wxTextAttrDimensionConverter::ConvertPixelsToTenthsMM(int pixels) const
12272
return wxRichTextObject::ConvertPixelsToTenthsMM(m_ppi, pixels, m_scale);
12275
int wxTextAttrDimensionConverter::GetPixels(const wxTextAttrDimension& dim, int direction) const
12277
if (dim.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
12278
return ConvertTenthsMMToPixels(dim.GetValue());
12279
else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
12280
return dim.GetValue();
12281
else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
12283
wxASSERT(m_parentSize != wxDefaultSize);
12284
if (direction == wxHORIZONTAL)
12285
return (int) (double(m_parentSize.x) * double(dim.GetValue()) / 100.0);
12287
return (int) (double(m_parentSize.y) * double(dim.GetValue()) / 100.0);
12296
int wxTextAttrDimensionConverter::GetTenthsMM(const wxTextAttrDimension& dim) const
12298
if (dim.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
12299
return dim.GetValue();
12300
else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
12301
return ConvertPixelsToTenthsMM(dim.GetValue());
12309
// Partial equality test
12310
bool wxTextAttrDimensions::EqPartial(const wxTextAttrDimensions& dims, bool weakTest) const
12312
if (!m_left.EqPartial(dims.m_left, weakTest))
12315
if (!m_right.EqPartial(dims.m_right, weakTest))
12318
if (!m_top.EqPartial(dims.m_top, weakTest))
12321
if (!m_bottom.EqPartial(dims.m_bottom, weakTest))
12327
// Apply border to 'this', but not if the same as compareWith
12328
bool wxTextAttrDimensions::Apply(const wxTextAttrDimensions& dims, const wxTextAttrDimensions* compareWith)
12330
m_left.Apply(dims.m_left, compareWith ? (& compareWith->m_left) : (const wxTextAttrDimension*) NULL);
12331
m_right.Apply(dims.m_right, compareWith ? (& compareWith->m_right): (const wxTextAttrDimension*) NULL);
12332
m_top.Apply(dims.m_top, compareWith ? (& compareWith->m_top): (const wxTextAttrDimension*) NULL);
12333
m_bottom.Apply(dims.m_bottom, compareWith ? (& compareWith->m_bottom): (const wxTextAttrDimension*) NULL);
12338
// Remove specified attributes from this object
12339
bool wxTextAttrDimensions::RemoveStyle(const wxTextAttrDimensions& attr)
12341
if (attr.m_left.IsValid())
12343
if (attr.m_right.IsValid())
12345
if (attr.m_top.IsValid())
12347
if (attr.m_bottom.IsValid())
12353
// Collects the attributes that are common to a range of content, building up a note of
12354
// which attributes are absent in some objects and which clash in some objects.
12355
void wxTextAttrDimensions::CollectCommonAttributes(const wxTextAttrDimensions& attr, wxTextAttrDimensions& clashingAttr, wxTextAttrDimensions& absentAttr)
12357
m_left.CollectCommonAttributes(attr.m_left, clashingAttr.m_left, absentAttr.m_left);
12358
m_right.CollectCommonAttributes(attr.m_right, clashingAttr.m_right, absentAttr.m_right);
12359
m_top.CollectCommonAttributes(attr.m_top, clashingAttr.m_top, absentAttr.m_top);
12360
m_bottom.CollectCommonAttributes(attr.m_bottom, clashingAttr.m_bottom, absentAttr.m_bottom);
12363
// Partial equality test
12364
bool wxTextAttrSize::EqPartial(const wxTextAttrSize& size, bool weakTest) const
12366
if (!m_width.EqPartial(size.m_width, weakTest))
12369
if (!m_height.EqPartial(size.m_height, weakTest))
12375
// Apply border to 'this', but not if the same as compareWith
12376
bool wxTextAttrSize::Apply(const wxTextAttrSize& size, const wxTextAttrSize* compareWith)
12378
m_width.Apply(size.m_width, compareWith ? (& compareWith->m_width) : (const wxTextAttrDimension*) NULL);
12379
m_height.Apply(size.m_height, compareWith ? (& compareWith->m_height): (const wxTextAttrDimension*) NULL);
12384
// Remove specified attributes from this object
12385
bool wxTextAttrSize::RemoveStyle(const wxTextAttrSize& attr)
12387
if (attr.m_width.IsValid())
12389
if (attr.m_height.IsValid())
12395
// Collects the attributes that are common to a range of content, building up a note of
12396
// which attributes are absent in some objects and which clash in some objects.
12397
void wxTextAttrSize::CollectCommonAttributes(const wxTextAttrSize& attr, wxTextAttrSize& clashingAttr, wxTextAttrSize& absentAttr)
12399
m_width.CollectCommonAttributes(attr.m_width, clashingAttr.m_width, absentAttr.m_width);
12400
m_height.CollectCommonAttributes(attr.m_height, clashingAttr.m_height, absentAttr.m_height);
12403
// Collects the attributes that are common to a range of content, building up a note of
12404
// which attributes are absent in some objects and which clash in some objects.
12405
void wxTextAttrCollectCommonAttributes(wxTextAttr& currentStyle, const wxTextAttr& attr, wxTextAttr& clashingAttr, wxTextAttr& absentAttr)
12407
absentAttr.SetFlags(absentAttr.GetFlags() | (~attr.GetFlags() & wxTEXT_ATTR_ALL));
12408
absentAttr.SetTextEffectFlags(absentAttr.GetTextEffectFlags() | (~attr.GetTextEffectFlags() & 0xFFFF));
12410
long forbiddenFlags = clashingAttr.GetFlags()|absentAttr.GetFlags();
12412
if (attr.HasFont())
12414
// If different font size units are being used, this is a clash.
12415
if (((attr.GetFlags() & wxTEXT_ATTR_FONT_SIZE) | (currentStyle.GetFlags() & wxTEXT_ATTR_FONT_SIZE)) == wxTEXT_ATTR_FONT_SIZE)
12417
currentStyle.SetFontSize(0);
12418
currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_SIZE);
12419
clashingAttr.AddFlag(wxTEXT_ATTR_FONT_SIZE);
12423
if (attr.HasFontPointSize() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_POINT_SIZE))
12425
if (currentStyle.HasFontPointSize())
12427
if (currentStyle.GetFontSize() != attr.GetFontSize())
12429
// Clash of attr - mark as such
12430
clashingAttr.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE);
12431
currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE);
12435
currentStyle.SetFontSize(attr.GetFontSize());
12438
if (attr.HasFontPixelSize() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_PIXEL_SIZE))
12440
if (currentStyle.HasFontPixelSize())
12442
if (currentStyle.GetFontSize() != attr.GetFontSize())
12444
// Clash of attr - mark as such
12445
clashingAttr.AddFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE);
12446
currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE);
12450
currentStyle.SetFontPixelSize(attr.GetFontSize());
12454
if (attr.HasFontItalic() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_ITALIC))
12456
if (currentStyle.HasFontItalic())
12458
if (currentStyle.GetFontStyle() != attr.GetFontStyle())
12460
// Clash of attr - mark as such
12461
clashingAttr.AddFlag(wxTEXT_ATTR_FONT_ITALIC);
12462
currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC);
12466
currentStyle.SetFontStyle(attr.GetFontStyle());
12469
if (attr.HasFontFamily() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_FAMILY))
12471
if (currentStyle.HasFontFamily())
12473
if (currentStyle.GetFontFamily() != attr.GetFontFamily())
12475
// Clash of attr - mark as such
12476
clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FAMILY);
12477
currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY);
12481
currentStyle.SetFontFamily(attr.GetFontFamily());
12484
if (attr.HasFontWeight() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_WEIGHT))
12486
if (currentStyle.HasFontWeight())
12488
if (currentStyle.GetFontWeight() != attr.GetFontWeight())
12490
// Clash of attr - mark as such
12491
clashingAttr.AddFlag(wxTEXT_ATTR_FONT_WEIGHT);
12492
currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT);
12496
currentStyle.SetFontWeight(attr.GetFontWeight());
12499
if (attr.HasFontFaceName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_FACE))
12501
if (currentStyle.HasFontFaceName())
12503
wxString faceName1(currentStyle.GetFontFaceName());
12504
wxString faceName2(attr.GetFontFaceName());
12506
if (faceName1 != faceName2)
12508
// Clash of attr - mark as such
12509
clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FACE);
12510
currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FACE);
12514
currentStyle.SetFontFaceName(attr.GetFontFaceName());
12517
if (attr.HasFontUnderlined() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_UNDERLINE))
12519
if (currentStyle.HasFontUnderlined())
12521
if (currentStyle.GetFontUnderlined() != attr.GetFontUnderlined())
12523
// Clash of attr - mark as such
12524
clashingAttr.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE);
12525
currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE);
12529
currentStyle.SetFontUnderlined(attr.GetFontUnderlined());
12532
if (attr.HasFontStrikethrough() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_STRIKETHROUGH))
12534
if (currentStyle.HasFontStrikethrough())
12536
if (currentStyle.GetFontStrikethrough() != attr.GetFontStrikethrough())
12538
// Clash of attr - mark as such
12539
clashingAttr.AddFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH);
12540
currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH);
12544
currentStyle.SetFontStrikethrough(attr.GetFontStrikethrough());
12548
if (attr.HasTextColour() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_TEXT_COLOUR))
12550
if (currentStyle.HasTextColour())
12552
if (currentStyle.GetTextColour() != attr.GetTextColour())
12554
// Clash of attr - mark as such
12555
clashingAttr.AddFlag(wxTEXT_ATTR_TEXT_COLOUR);
12556
currentStyle.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR);
12560
currentStyle.SetTextColour(attr.GetTextColour());
12563
if (attr.HasBackgroundColour() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BACKGROUND_COLOUR))
12565
if (currentStyle.HasBackgroundColour())
12567
if (currentStyle.GetBackgroundColour() != attr.GetBackgroundColour())
12569
// Clash of attr - mark as such
12570
clashingAttr.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
12571
currentStyle.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
12575
currentStyle.SetBackgroundColour(attr.GetBackgroundColour());
12578
if (attr.HasAlignment() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_ALIGNMENT))
12580
if (currentStyle.HasAlignment())
12582
if (currentStyle.GetAlignment() != attr.GetAlignment())
12584
// Clash of attr - mark as such
12585
clashingAttr.AddFlag(wxTEXT_ATTR_ALIGNMENT);
12586
currentStyle.RemoveFlag(wxTEXT_ATTR_ALIGNMENT);
12590
currentStyle.SetAlignment(attr.GetAlignment());
12593
if (attr.HasTabs() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_TABS))
12595
if (currentStyle.HasTabs())
12597
if (!wxRichTextTabsEq(currentStyle.GetTabs(), attr.GetTabs()))
12599
// Clash of attr - mark as such
12600
clashingAttr.AddFlag(wxTEXT_ATTR_TABS);
12601
currentStyle.RemoveFlag(wxTEXT_ATTR_TABS);
12605
currentStyle.SetTabs(attr.GetTabs());
12608
if (attr.HasLeftIndent() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LEFT_INDENT))
12610
if (currentStyle.HasLeftIndent())
12612
if (currentStyle.GetLeftIndent() != attr.GetLeftIndent() || currentStyle.GetLeftSubIndent() != attr.GetLeftSubIndent())
12614
// Clash of attr - mark as such
12615
clashingAttr.AddFlag(wxTEXT_ATTR_LEFT_INDENT);
12616
currentStyle.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT);
12620
currentStyle.SetLeftIndent(attr.GetLeftIndent(), attr.GetLeftSubIndent());
12623
if (attr.HasRightIndent() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_RIGHT_INDENT))
12625
if (currentStyle.HasRightIndent())
12627
if (currentStyle.GetRightIndent() != attr.GetRightIndent())
12629
// Clash of attr - mark as such
12630
clashingAttr.AddFlag(wxTEXT_ATTR_RIGHT_INDENT);
12631
currentStyle.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT);
12635
currentStyle.SetRightIndent(attr.GetRightIndent());
12638
if (attr.HasParagraphSpacingAfter() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARA_SPACING_AFTER))
12640
if (currentStyle.HasParagraphSpacingAfter())
12642
if (currentStyle.GetParagraphSpacingAfter() != attr.GetParagraphSpacingAfter())
12644
// Clash of attr - mark as such
12645
clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
12646
currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
12650
currentStyle.SetParagraphSpacingAfter(attr.GetParagraphSpacingAfter());
12653
if (attr.HasParagraphSpacingBefore() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARA_SPACING_BEFORE))
12655
if (currentStyle.HasParagraphSpacingBefore())
12657
if (currentStyle.GetParagraphSpacingBefore() != attr.GetParagraphSpacingBefore())
12659
// Clash of attr - mark as such
12660
clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
12661
currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
12665
currentStyle.SetParagraphSpacingBefore(attr.GetParagraphSpacingBefore());
12668
if (attr.HasLineSpacing() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LINE_SPACING))
12670
if (currentStyle.HasLineSpacing())
12672
if (currentStyle.GetLineSpacing() != attr.GetLineSpacing())
12674
// Clash of attr - mark as such
12675
clashingAttr.AddFlag(wxTEXT_ATTR_LINE_SPACING);
12676
currentStyle.RemoveFlag(wxTEXT_ATTR_LINE_SPACING);
12680
currentStyle.SetLineSpacing(attr.GetLineSpacing());
12683
if (attr.HasCharacterStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_CHARACTER_STYLE_NAME))
12685
if (currentStyle.HasCharacterStyleName())
12687
if (currentStyle.GetCharacterStyleName() != attr.GetCharacterStyleName())
12689
// Clash of attr - mark as such
12690
clashingAttr.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
12691
currentStyle.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
12695
currentStyle.SetCharacterStyleName(attr.GetCharacterStyleName());
12698
if (attr.HasParagraphStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARAGRAPH_STYLE_NAME))
12700
if (currentStyle.HasParagraphStyleName())
12702
if (currentStyle.GetParagraphStyleName() != attr.GetParagraphStyleName())
12704
// Clash of attr - mark as such
12705
clashingAttr.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
12706
currentStyle.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
12710
currentStyle.SetParagraphStyleName(attr.GetParagraphStyleName());
12713
if (attr.HasListStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LIST_STYLE_NAME))
12715
if (currentStyle.HasListStyleName())
12717
if (currentStyle.GetListStyleName() != attr.GetListStyleName())
12719
// Clash of attr - mark as such
12720
clashingAttr.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
12721
currentStyle.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
12725
currentStyle.SetListStyleName(attr.GetListStyleName());
12728
if (attr.HasBulletStyle() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_STYLE))
12730
if (currentStyle.HasBulletStyle())
12732
if (currentStyle.GetBulletStyle() != attr.GetBulletStyle())
12734
// Clash of attr - mark as such
12735
clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_STYLE);
12736
currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE);
12740
currentStyle.SetBulletStyle(attr.GetBulletStyle());
12743
if (attr.HasBulletNumber() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_NUMBER))
12745
if (currentStyle.HasBulletNumber())
12747
if (currentStyle.GetBulletNumber() != attr.GetBulletNumber())
12749
// Clash of attr - mark as such
12750
clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NUMBER);
12751
currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER);
12755
currentStyle.SetBulletNumber(attr.GetBulletNumber());
12758
if (attr.HasBulletText() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_TEXT))
12760
if (currentStyle.HasBulletText())
12762
if (currentStyle.GetBulletText() != attr.GetBulletText())
12764
// Clash of attr - mark as such
12765
clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_TEXT);
12766
currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT);
12771
currentStyle.SetBulletText(attr.GetBulletText());
12772
currentStyle.SetBulletFont(attr.GetBulletFont());
12776
if (attr.HasBulletName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_NAME))
12778
if (currentStyle.HasBulletName())
12780
if (currentStyle.GetBulletName() != attr.GetBulletName())
12782
// Clash of attr - mark as such
12783
clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NAME);
12784
currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NAME);
12789
currentStyle.SetBulletName(attr.GetBulletName());
12793
if (attr.HasURL() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_URL))
12795
if (currentStyle.HasURL())
12797
if (currentStyle.GetURL() != attr.GetURL())
12799
// Clash of attr - mark as such
12800
clashingAttr.AddFlag(wxTEXT_ATTR_URL);
12801
currentStyle.RemoveFlag(wxTEXT_ATTR_URL);
12806
currentStyle.SetURL(attr.GetURL());
12810
if (attr.HasTextEffects() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_EFFECTS))
12812
if (currentStyle.HasTextEffects())
12814
// We need to find the bits in the new attr that are different:
12815
// just look at those bits that are specified by the new attr.
12817
// We need to remove the bits and flags that are not common between current attr
12818
// and new attr. In so doing we need to take account of the styles absent from one or more of the
12819
// previous styles.
12821
int currentRelevantTextEffects = currentStyle.GetTextEffects() & attr.GetTextEffectFlags();
12822
int newRelevantTextEffects = attr.GetTextEffects() & attr.GetTextEffectFlags();
12824
if (currentRelevantTextEffects != newRelevantTextEffects)
12826
// Find the text effects that were different, using XOR
12827
int differentEffects = currentRelevantTextEffects ^ newRelevantTextEffects;
12829
// Clash of attr - mark as such
12830
clashingAttr.SetTextEffectFlags(clashingAttr.GetTextEffectFlags() | differentEffects);
12831
currentStyle.SetTextEffectFlags(currentStyle.GetTextEffectFlags() & ~differentEffects);
12836
currentStyle.SetTextEffects(attr.GetTextEffects());
12837
currentStyle.SetTextEffectFlags(attr.GetTextEffectFlags());
12840
// Mask out the flags and values that cannot be common because they were absent in one or more objecrs
12841
// that we've looked at so far
12842
currentStyle.SetTextEffects(currentStyle.GetTextEffects() & ~absentAttr.GetTextEffectFlags());
12843
currentStyle.SetTextEffectFlags(currentStyle.GetTextEffectFlags() & ~absentAttr.GetTextEffectFlags());
12845
if (currentStyle.GetTextEffectFlags() == 0)
12846
currentStyle.RemoveFlag(wxTEXT_ATTR_EFFECTS);
12849
if (attr.HasOutlineLevel() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_OUTLINE_LEVEL))
12851
if (currentStyle.HasOutlineLevel())
12853
if (currentStyle.GetOutlineLevel() != attr.GetOutlineLevel())
12855
// Clash of attr - mark as such
12856
clashingAttr.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
12857
currentStyle.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
12861
currentStyle.SetOutlineLevel(attr.GetOutlineLevel());
12865
WX_DEFINE_OBJARRAY(wxRichTextVariantArray);
12867
IMPLEMENT_DYNAMIC_CLASS(wxRichTextProperties, wxObject)
12869
bool wxRichTextProperties::operator==(const wxRichTextProperties& props) const
12871
if (m_properties.GetCount() != props.GetCount())
12875
for (i = 0; i < m_properties.GetCount(); i++)
12877
const wxVariant& var1 = m_properties[i];
12878
int idx = props.Find(var1.GetName());
12881
const wxVariant& var2 = props.m_properties[idx];
12882
if (!(var1 == var2))
12889
wxArrayString wxRichTextProperties::GetPropertyNames() const
12893
for (i = 0; i < m_properties.GetCount(); i++)
12895
arr.Add(m_properties[i].GetName());
12900
int wxRichTextProperties::Find(const wxString& name) const
12903
for (i = 0; i < m_properties.GetCount(); i++)
12905
if (m_properties[i].GetName() == name)
12911
bool wxRichTextProperties::Remove(const wxString& name)
12913
int idx = Find(name);
12916
m_properties.RemoveAt(idx);
12923
wxVariant* wxRichTextProperties::FindOrCreateProperty(const wxString& name)
12925
int idx = Find(name);
12926
if (idx == wxNOT_FOUND)
12927
SetProperty(name, wxString());
12929
if (idx != wxNOT_FOUND)
12931
return & (*this)[idx];
12937
const wxVariant& wxRichTextProperties::GetProperty(const wxString& name) const
12939
static const wxVariant nullVariant;
12940
int idx = Find(name);
12942
return m_properties[idx];
12944
return nullVariant;
12947
wxString wxRichTextProperties::GetPropertyString(const wxString& name) const
12949
return GetProperty(name).GetString();
12952
long wxRichTextProperties::GetPropertyLong(const wxString& name) const
12954
return GetProperty(name).GetLong();
12957
bool wxRichTextProperties::GetPropertyBool(const wxString& name) const
12959
return GetProperty(name).GetBool();
12962
double wxRichTextProperties::GetPropertyDouble(const wxString& name) const
12964
return GetProperty(name).GetDouble();
12967
void wxRichTextProperties::SetProperty(const wxVariant& variant)
12969
wxASSERT(!variant.GetName().IsEmpty());
12971
int idx = Find(variant.GetName());
12974
m_properties.Add(variant);
12976
m_properties[idx] = variant;
12979
void wxRichTextProperties::SetProperty(const wxString& name, const wxVariant& variant)
12981
int idx = Find(name);
12982
wxVariant var(variant);
12986
m_properties.Add(var);
12988
m_properties[idx] = var;
12991
void wxRichTextProperties::SetProperty(const wxString& name, const wxString& value)
12993
SetProperty(name, wxVariant(value, name));
12996
void wxRichTextProperties::SetProperty(const wxString& name, long value)
12998
SetProperty(name, wxVariant(value, name));
13001
void wxRichTextProperties::SetProperty(const wxString& name, double value)
13003
SetProperty(name, wxVariant(value, name));
13006
void wxRichTextProperties::SetProperty(const wxString& name, bool value)
13008
SetProperty(name, wxVariant(value, name));
13011
void wxRichTextProperties::RemoveProperties(const wxRichTextProperties& properties)
13014
for (i = 0; i < properties.GetCount(); i++)
13016
wxString name = properties.GetProperties()[i].GetName();
13017
if (HasProperty(name))
13022
void wxRichTextProperties::MergeProperties(const wxRichTextProperties& properties)
13025
for (i = 0; i < properties.GetCount(); i++)
13027
SetProperty(properties.GetProperties()[i]);
13031
wxRichTextObject* wxRichTextObjectAddress::GetObject(wxRichTextParagraphLayoutBox* topLevelContainer) const
13033
if (m_address.GetCount() == 0)
13034
return topLevelContainer;
13036
wxRichTextCompositeObject* p = topLevelContainer;
13038
while (p && i < m_address.GetCount())
13040
int pos = m_address[i];
13041
wxASSERT(pos >= 0 && pos < (int) p->GetChildren().GetCount());
13042
if (pos < 0 || pos >= (int) p->GetChildren().GetCount())
13045
wxRichTextObject* p1 = p->GetChild(pos);
13046
if (i == (m_address.GetCount()-1))
13049
p = wxDynamicCast(p1, wxRichTextCompositeObject);
13055
bool wxRichTextObjectAddress::Create(wxRichTextParagraphLayoutBox* topLevelContainer, wxRichTextObject* obj)
13059
if (topLevelContainer == obj)
13062
wxRichTextObject* o = obj;
13065
wxRichTextCompositeObject* p = wxDynamicCast(o->GetParent(), wxRichTextCompositeObject);
13069
int pos = p->GetChildren().IndexOf(o);
13073
m_address.Insert(pos, 0);
13075
if (p == topLevelContainer)
13084
bool wxRichTextSelection::operator==(const wxRichTextSelection& sel) const
13086
if (m_container != sel.m_container)
13088
if (m_ranges.GetCount() != sel.m_ranges.GetCount())
13091
for (i = 0; i < m_ranges.GetCount(); i++)
13092
if (!(m_ranges[i] == sel.m_ranges[i]))
13097
// Get the selections appropriate to the specified object, if any; returns wxRICHTEXT_NO_SELECTION if none
13098
// or none at the level of the object's container.
13099
wxRichTextRangeArray wxRichTextSelection::GetSelectionForObject(wxRichTextObject* obj) const
13103
wxRichTextParagraphLayoutBox* container = obj->GetParentContainer();
13105
if (container == m_container)
13108
container = obj->GetContainer();
13111
if (container->GetParent())
13113
// If we found that our object's container is within the range of
13114
// a selection higher up, then assume the whole original object
13115
// is also selected.
13116
wxRichTextParagraphLayoutBox* parentContainer = container->GetParentContainer();
13117
if (parentContainer == m_container)
13119
if (WithinSelection(container->GetRange().GetStart(), m_ranges))
13121
wxRichTextRangeArray ranges;
13122
ranges.Add(obj->GetRange());
13127
container = parentContainer;
13136
return wxRichTextRangeArray();
13139
// Is the given position within the selection?
13140
bool wxRichTextSelection::WithinSelection(long pos, wxRichTextObject* obj) const
13146
wxRichTextRangeArray selectionRanges = GetSelectionForObject(obj);
13147
return WithinSelection(pos, selectionRanges);
13151
// Is the given position within the selection range?
13152
bool wxRichTextSelection::WithinSelection(long pos, const wxRichTextRangeArray& ranges)
13155
for (i = 0; i < ranges.GetCount(); i++)
13157
const wxRichTextRange& range = ranges[i];
13158
if (pos >= range.GetStart() && pos <= range.GetEnd())
13164
// Is the given range completely within the selection range?
13165
bool wxRichTextSelection::WithinSelection(const wxRichTextRange& range, const wxRichTextRangeArray& ranges)
13168
for (i = 0; i < ranges.GetCount(); i++)
13170
const wxRichTextRange& eachRange = ranges[i];
13171
if (range.IsWithin(eachRange))
13177
IMPLEMENT_CLASS(wxRichTextDrawingHandler, wxObject)
13178
IMPLEMENT_CLASS(wxRichTextDrawingContext, wxObject)
13180
bool wxRichTextDrawingContext::HasVirtualAttributes(wxRichTextObject* obj) const
13182
wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
13185
wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13186
if (handler->HasVirtualAttributes(obj))
13189
node = node->GetNext();
13194
wxRichTextAttr wxRichTextDrawingContext::GetVirtualAttributes(wxRichTextObject* obj) const
13196
wxRichTextAttr attr;
13197
// We apply all handlers, so we can may combine several different attributes
13198
wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
13201
wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13202
if (handler->HasVirtualAttributes(obj))
13204
bool success = handler->GetVirtualAttributes(attr, obj);
13206
wxUnusedVar(success);
13209
node = node->GetNext();
13214
bool wxRichTextDrawingContext::ApplyVirtualAttributes(wxRichTextAttr& attr, wxRichTextObject* obj) const
13216
if (HasVirtualAttributes(obj))
13218
wxRichTextAttr a(GetVirtualAttributes(obj));
13226
/// Adds a handler to the end
13227
void wxRichTextBuffer::AddDrawingHandler(wxRichTextDrawingHandler *handler)
13229
sm_drawingHandlers.Append(handler);
13232
/// Inserts a handler at the front
13233
void wxRichTextBuffer::InsertDrawingHandler(wxRichTextDrawingHandler *handler)
13235
sm_drawingHandlers.Insert( handler );
13238
/// Removes a handler
13239
bool wxRichTextBuffer::RemoveDrawingHandler(const wxString& name)
13241
wxRichTextDrawingHandler *handler = FindDrawingHandler(name);
13244
sm_drawingHandlers.DeleteObject(handler);
13252
wxRichTextDrawingHandler* wxRichTextBuffer::FindDrawingHandler(const wxString& name)
13254
wxList::compatibility_iterator node = sm_drawingHandlers.GetFirst();
13257
wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13258
if (handler->GetName().Lower() == name.Lower()) return handler;
13260
node = node->GetNext();
13265
void wxRichTextBuffer::CleanUpDrawingHandlers()
13267
wxList::compatibility_iterator node = sm_drawingHandlers.GetFirst();
13270
wxRichTextDrawingHandler* handler = (wxRichTextDrawingHandler*)node->GetData();
13271
wxList::compatibility_iterator next = node->GetNext();
13276
sm_drawingHandlers.Clear();
13279
void wxRichTextBuffer::AddFieldType(wxRichTextFieldType *fieldType)
13281
sm_fieldTypes[fieldType->GetName()] = fieldType;
13284
bool wxRichTextBuffer::RemoveFieldType(const wxString& name)
13286
wxRichTextFieldTypeHashMap::iterator it = sm_fieldTypes.find(name);
13287
if (it == sm_fieldTypes.end())
13291
wxRichTextFieldType* fieldType = it->second;
13292
sm_fieldTypes.erase(it);
13298
wxRichTextFieldType *wxRichTextBuffer::FindFieldType(const wxString& name)
13300
wxRichTextFieldTypeHashMap::iterator it = sm_fieldTypes.find(name);
13301
if (it == sm_fieldTypes.end())
13307
void wxRichTextBuffer::CleanUpFieldTypes()
13309
wxRichTextFieldTypeHashMap::iterator it;
13310
for( it = sm_fieldTypes.begin(); it != sm_fieldTypes.end(); ++it )
13312
wxRichTextFieldType* fieldType = it->second;
13316
sm_fieldTypes.clear();