]> git.saurik.com Git - wxWidgets.git/blame - src/richtext/richtextbuffer.cpp
Add wxAnyScrollHelperBase to reduce code duplication in wxVarScrollHelperBase.
[wxWidgets.git] / src / richtext / richtextbuffer.cpp
CommitLineData
5d7836c4 1/////////////////////////////////////////////////////////////////////////////
61399247 2// Name: src/richtext/richtextbuffer.cpp
5d7836c4
JS
3// Purpose: Buffer for wxRichTextCtrl
4// Author: Julian Smart
7fe8059f 5// Modified by:
5d7836c4 6// Created: 2005-09-30
5d7836c4
JS
7// Copyright: (c) Julian Smart
8// Licence: wxWindows licence
9/////////////////////////////////////////////////////////////////////////////
2be72ac2 10
5d7836c4
JS
11// For compilers that support precompilation, includes "wx.h".
12#include "wx/wxprec.h"
13
14#ifdef __BORLANDC__
61399247 15 #pragma hdrstop
5d7836c4
JS
16#endif
17
b01ca8b6
JS
18#if wxUSE_RICHTEXT
19
20#include "wx/richtext/richtextbuffer.h"
21
5d7836c4 22#ifndef WX_PRECOMP
61399247
WS
23 #include "wx/dc.h"
24 #include "wx/intl.h"
7947a48a 25 #include "wx/log.h"
28f92d74 26 #include "wx/dataobj.h"
02761f6c 27 #include "wx/module.h"
5d7836c4
JS
28#endif
29
0ec6da02 30#include "wx/settings.h"
5d7836c4
JS
31#include "wx/filename.h"
32#include "wx/clipbrd.h"
33#include "wx/wfstream.h"
5d7836c4
JS
34#include "wx/mstream.h"
35#include "wx/sstream.h"
0ca07313 36#include "wx/textfile.h"
44cc96a8 37#include "wx/hashmap.h"
cdaed652 38#include "wx/dynarray.h"
5d7836c4 39
5d7836c4
JS
40#include "wx/richtext/richtextctrl.h"
41#include "wx/richtext/richtextstyles.h"
cdaed652 42#include "wx/richtext/richtextimagedlg.h"
603f702b 43#include "wx/richtext/richtextsizepage.h"
1aca9fcd 44#include "wx/richtext/richtextxml.h"
5d7836c4
JS
45
46#include "wx/listimpl.cpp"
bec80f4f 47#include "wx/arrimpl.cpp"
5d7836c4 48
412e0d47
DS
49WX_DEFINE_LIST(wxRichTextObjectList)
50WX_DEFINE_LIST(wxRichTextLineList)
5d7836c4 51
ea160b2e
JS
52// Switch off if the platform doesn't like it for some reason
53#define wxRICHTEXT_USE_OPTIMIZED_DRAWING 1
54
31778480
JS
55// Use GetPartialTextExtents for platforms that support it natively
56#define wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS 1
57
ff76711f
JS
58const wxChar wxRichTextLineBreakChar = (wxChar) 29;
59
cdaed652
VZ
60// Helper classes for floating layout
61struct wxRichTextFloatRectMap
62{
63 wxRichTextFloatRectMap(int sY, int eY, int w, wxRichTextObject* obj)
64 {
65 startY = sY;
66 endY = eY;
67 width = w;
68 anchor = obj;
69 }
70
71 int startY, endY;
72 int width;
73 wxRichTextObject* anchor;
74};
75
76WX_DEFINE_SORTED_ARRAY(wxRichTextFloatRectMap*, wxRichTextFloatRectMapArray);
77
78int wxRichTextFloatRectMapCmp(wxRichTextFloatRectMap* r1, wxRichTextFloatRectMap* r2)
79{
80 return r1->startY - r2->startY;
81}
82
83class wxRichTextFloatCollector
84{
85public:
603f702b 86 wxRichTextFloatCollector(const wxRect& availableRect);
cdaed652
VZ
87 ~wxRichTextFloatCollector();
88
89 // Collect the floating objects info in the given paragraph
90 void CollectFloat(wxRichTextParagraph* para);
91 void CollectFloat(wxRichTextParagraph* para, wxRichTextObject* floating);
92
93 // Return the last paragraph we collected
94 wxRichTextParagraph* LastParagraph();
95
96 // Given the start y position and the height of the line,
97 // find out how wide the line can be
98 wxRect GetAvailableRect(int startY, int endY);
99
100 // Given a floating box, find its fit position
101 int GetFitPosition(int direction, int start, int height) const;
102 int GetFitPosition(const wxRichTextFloatRectMapArray& array, int start, int height) const;
103
104 // Find the last y position
105 int GetLastRectBottom();
106
107 // Draw the floats inside a rect
8db2e3ef 108 void Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style);
cdaed652
VZ
109
110 // HitTest the floats
8db2e3ef 111 int HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int flags);
603f702b
JS
112
113 // Get floating object count
114 int GetFloatingObjectCount() const { return m_left.GetCount() + m_right.GetCount(); }
115
116 // Get floating objects
117 bool GetFloatingObjects(wxRichTextObjectList& objects) const;
cdaed652 118
07d4142f
JS
119 // Delete a float
120 bool DeleteFloat(wxRichTextObject* obj);
121
122 // Do we have this float already?
123 bool HasFloat(wxRichTextObject* obj);
124
125 bool HasFloats() const { return m_left.GetCount() >0 || m_right.GetCount() > 0; }
126
cdaed652
VZ
127 static int SearchAdjacentRect(const wxRichTextFloatRectMapArray& array, int point);
128
129 static int GetWidthFromFloatRect(const wxRichTextFloatRectMapArray& array, int index, int startY, int endY);
ce00f59b 130
cdaed652
VZ
131 static void FreeFloatRectMapArray(wxRichTextFloatRectMapArray& array);
132
8db2e3ef 133 static void DrawFloat(const wxRichTextFloatRectMapArray& array, wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style);
cdaed652 134
8db2e3ef 135 static int HitTestFloat(const wxRichTextFloatRectMapArray& array, wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int flags);
ce00f59b 136
cdaed652
VZ
137private:
138 wxRichTextFloatRectMapArray m_left;
139 wxRichTextFloatRectMapArray m_right;
603f702b
JS
140 //int m_width;
141 wxRect m_availableRect;
cdaed652
VZ
142 wxRichTextParagraph* m_para;
143};
144
07d4142f
JS
145// Delete a float
146bool wxRichTextFloatCollector::DeleteFloat(wxRichTextObject* obj)
147{
148 size_t i;
149 for (i = 0; i < m_left.GetCount(); i++)
150 {
151 if (m_left[i]->anchor == obj)
152 {
153 m_left.RemoveAt(i);
154 return true;
155 }
156 }
157 for (i = 0; i < m_right.GetCount(); i++)
158 {
159 if (m_right[i]->anchor == obj)
160 {
161 m_right.RemoveAt(i);
162 return true;
163 }
164 }
165 return false;
166}
167
168// Do we have this float already?
169bool wxRichTextFloatCollector::HasFloat(wxRichTextObject* obj)
170{
171 size_t i;
172 for (i = 0; i < m_left.GetCount(); i++)
173 {
174 if (m_left[i]->anchor == obj)
175 {
176 return true;
177 }
178 }
179 for (i = 0; i < m_right.GetCount(); i++)
180 {
181 if (m_right[i]->anchor == obj)
182 {
183 return true;
184 }
185 }
186 return false;
187}
188
603f702b
JS
189// Get floating objects
190bool wxRichTextFloatCollector::GetFloatingObjects(wxRichTextObjectList& objects) const
191{
192 size_t i;
193 for (i = 0; i < m_left.GetCount(); i++)
194 objects.Append(m_left[i]->anchor);
195 for (i = 0; i < m_right.GetCount(); i++)
196 objects.Append(m_right[i]->anchor);
197 return true;
198}
199
200
cdaed652
VZ
201/*
202 * Binary search helper function
203 * The argument point is the Y coordinate, and this fuction
204 * always return the floating rect that contain this coordinate
205 * or under this coordinate.
206 */
207int wxRichTextFloatCollector::SearchAdjacentRect(const wxRichTextFloatRectMapArray& array, int point)
208{
209 int end = array.GetCount() - 1;
210 int start = 0;
211 int ret = 0;
212
213 wxASSERT(end >= 0);
214
215 while (true)
216 {
217 if (start > end)
218 {
219 break;
220 }
221
222 int mid = (start + end) / 2;
223 if (array[mid]->startY <= point && array[mid]->endY >= point)
224 return mid;
225 else if (array[mid]->startY > point)
226 {
227 end = mid - 1;
228 ret = mid;
229 }
230 else if (array[mid]->endY < point)
231 {
232 start = mid + 1;
233 ret = start;
234 }
235 }
236
237 return ret;
238}
239
240int wxRichTextFloatCollector::GetWidthFromFloatRect(const wxRichTextFloatRectMapArray& array, int index, int startY, int endY)
241{
242 int ret = 0;
243 int len = array.GetCount();
244
245 wxASSERT(index >= 0 && index < len);
246
247 if (array[index]->startY < startY && array[index]->endY > startY)
248 ret = ret < array[index]->width ? array[index]->width : ret;
249 while (index < len && array[index]->startY <= endY)
250 {
251 ret = ret < array[index]->width ? array[index]->width : ret;
252 index++;
253 }
254
255 return ret;
256}
257
603f702b 258wxRichTextFloatCollector::wxRichTextFloatCollector(const wxRect& rect) : m_left(wxRichTextFloatRectMapCmp), m_right(wxRichTextFloatRectMapCmp)
cdaed652 259{
603f702b 260 m_availableRect = rect;
cdaed652
VZ
261 m_para = NULL;
262}
263
264void wxRichTextFloatCollector::FreeFloatRectMapArray(wxRichTextFloatRectMapArray& array)
265{
266 int len = array.GetCount();
267 for (int i = 0; i < len; i++)
268 delete array[i];
269}
270
271wxRichTextFloatCollector::~wxRichTextFloatCollector()
272{
273 FreeFloatRectMapArray(m_left);
274 FreeFloatRectMapArray(m_right);
275}
276
277int wxRichTextFloatCollector::GetFitPosition(const wxRichTextFloatRectMapArray& array, int start, int height) const
278{
279 if (array.GetCount() == 0)
280 return start;
281
603f702b 282 int i = SearchAdjacentRect(array, start);
cdaed652 283 int last = start;
603f702b 284 while (i < (int) array.GetCount())
cdaed652
VZ
285 {
286 if (array[i]->startY - last >= height)
287 return last + 1;
288 last = array[i]->endY;
289 i++;
290 }
291
292 return last + 1;
293}
294
295int wxRichTextFloatCollector::GetFitPosition(int direction, int start, int height) const
296{
24777478 297 if (direction == wxTEXT_BOX_ATTR_FLOAT_LEFT)
cdaed652 298 return GetFitPosition(m_left, start, height);
24777478 299 else if (direction == wxTEXT_BOX_ATTR_FLOAT_RIGHT)
cdaed652
VZ
300 return GetFitPosition(m_right, start, height);
301 else
302 {
303 wxASSERT("Never should be here");
304 return start;
305 }
306}
307
603f702b
JS
308// Adds a floating image to the float collector.
309// The actual positioning is done by wxRichTextParagraph::LayoutFloat.
cdaed652
VZ
310void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph* para, wxRichTextObject* floating)
311{
603f702b 312 int direction = floating->GetFloatDirection();
24777478 313
603f702b
JS
314 wxPoint pos = floating->GetPosition();
315 wxSize size = floating->GetCachedSize();
316 wxRichTextFloatRectMap *map = new wxRichTextFloatRectMap(pos.y, pos.y + size.y, size.x, floating);
317 switch (direction)
318 {
319 case wxTEXT_BOX_ATTR_FLOAT_NONE:
320 delete map;
321 break;
322 case wxTEXT_BOX_ATTR_FLOAT_LEFT:
323 // Just a not-enough simple assertion
324 wxASSERT (m_left.Index(map) == wxNOT_FOUND);
325 m_left.Add(map);
326 break;
327 case wxTEXT_BOX_ATTR_FLOAT_RIGHT:
328 wxASSERT (m_right.Index(map) == wxNOT_FOUND);
329 m_right.Add(map);
330 break;
331 default:
332 delete map;
333 wxASSERT("Unrecognised float attribute.");
334 }
cdaed652 335
603f702b 336 m_para = para;
cdaed652
VZ
337}
338
339void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph* para)
340{
341 wxRichTextObjectList::compatibility_iterator node = para->GetChildren().GetFirst();
342 while (node)
343 {
344 wxRichTextObject* floating = node->GetData();
ce00f59b 345
cdaed652
VZ
346 if (floating->IsFloating())
347 {
bec80f4f 348 CollectFloat(para, floating);
cdaed652 349 }
ce00f59b 350
cdaed652
VZ
351 node = node->GetNext();
352 }
353
354 m_para = para;
355}
356
357wxRichTextParagraph* wxRichTextFloatCollector::LastParagraph()
358{
359 return m_para;
360}
361
362wxRect wxRichTextFloatCollector::GetAvailableRect(int startY, int endY)
363{
364 int widthLeft = 0, widthRight = 0;
365 if (m_left.GetCount() != 0)
366 {
603f702b
JS
367 int i = SearchAdjacentRect(m_left, startY);
368 if (i < (int) m_left.GetCount())
cdaed652
VZ
369 widthLeft = GetWidthFromFloatRect(m_left, i, startY, endY);
370 }
371 if (m_right.GetCount() != 0)
372 {
603f702b
JS
373 int j = SearchAdjacentRect(m_right, startY);
374 if (j < (int) m_right.GetCount())
cdaed652
VZ
375 widthRight = GetWidthFromFloatRect(m_right, j, startY, endY);
376 }
377
603f702b
JS
378 // TODO: actually we want to use the actual image positions to find the
379 // available remaining space, since the image might not be right up against
380 // the left or right edge of the container.
381 return wxRect(widthLeft + m_availableRect.x, 0, m_availableRect.width - widthLeft - widthRight, 0);
cdaed652
VZ
382}
383
384int wxRichTextFloatCollector::GetLastRectBottom()
385{
386 int ret = 0;
387 int len = m_left.GetCount();
388 if (len) {
389 ret = ret > m_left[len-1]->endY ? ret : m_left[len-1]->endY;
390 }
391 len = m_right.GetCount();
392 if (len) {
393 ret = ret > m_right[len-1]->endY ? ret : m_right[len-1]->endY;
394 }
395
396 return ret;
397}
398
8db2e3ef 399void wxRichTextFloatCollector::DrawFloat(const wxRichTextFloatRectMapArray& array, wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& WXUNUSED(range), const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
cdaed652
VZ
400{
401 int start = rect.y;
402 int end = rect.y + rect.height;
603f702b 403 int i, j;
cdaed652 404 i = SearchAdjacentRect(array, start);
603f702b 405 if (i < 0 || i >= (int) array.GetCount())
cdaed652
VZ
406 return;
407 j = SearchAdjacentRect(array, end);
603f702b 408 if (j < 0 || j >= (int) array.GetCount())
cdaed652
VZ
409 j = array.GetCount() - 1;
410 while (i <= j)
411 {
412 wxRichTextObject* obj = array[i]->anchor;
413 wxRichTextRange r = obj->GetRange();
8db2e3ef 414 obj->Draw(dc, context, r, selection, wxRect(obj->GetPosition(), obj->GetCachedSize()), descent, style);
cdaed652
VZ
415 i++;
416 }
417}
ecb5fbf1 418
8db2e3ef 419void wxRichTextFloatCollector::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
cdaed652
VZ
420{
421 if (m_left.GetCount() > 0)
8db2e3ef 422 DrawFloat(m_left, dc, context, range, selection, rect, descent, style);
cdaed652 423 if (m_right.GetCount() > 0)
8db2e3ef 424 DrawFloat(m_right, dc, context, range, selection, rect, descent, style);
cdaed652
VZ
425}
426
8db2e3ef 427int wxRichTextFloatCollector::HitTestFloat(const wxRichTextFloatRectMapArray& array, wxDC& WXUNUSED(dc), wxRichTextDrawingContext& WXUNUSED(context), const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int WXUNUSED(flags))
cdaed652 428{
603f702b 429 int i;
cdaed652
VZ
430 if (array.GetCount() == 0)
431 return wxRICHTEXT_HITTEST_NONE;
432 i = SearchAdjacentRect(array, pt.y);
603f702b 433 if (i < 0 || i >= (int) array.GetCount())
cdaed652 434 return wxRICHTEXT_HITTEST_NONE;
603f702b
JS
435 if (!array[i]->anchor->IsShown())
436 return wxRICHTEXT_HITTEST_NONE;
437
cdaed652
VZ
438 wxPoint point = array[i]->anchor->GetPosition();
439 wxSize size = array[i]->anchor->GetCachedSize();
440 if (point.x <= pt.x && point.x + size.x >= pt.x
441 && point.y <= pt.y && point.y + size.y >= pt.y)
442 {
443 textPosition = array[i]->anchor->GetRange().GetStart();
603f702b 444 * obj = array[i]->anchor;
cdaed652
VZ
445 if (pt.x > (pt.x + pt.x + size.x) / 2)
446 return wxRICHTEXT_HITTEST_BEFORE;
447 else
448 return wxRICHTEXT_HITTEST_AFTER;
449 }
ce00f59b 450
cdaed652
VZ
451 return wxRICHTEXT_HITTEST_NONE;
452}
453
8db2e3ef 454int wxRichTextFloatCollector::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int flags)
cdaed652 455{
8db2e3ef 456 int ret = HitTestFloat(m_left, dc, context, pt, textPosition, obj, flags);
cdaed652
VZ
457 if (ret == wxRICHTEXT_HITTEST_NONE)
458 {
8db2e3ef 459 ret = HitTestFloat(m_right, dc, context, pt, textPosition, obj, flags);
cdaed652
VZ
460 }
461 return ret;
462}
463
ce00f59b 464// Helpers for efficiency
ecb5fbf1
JS
465inline void wxCheckSetFont(wxDC& dc, const wxFont& font)
466{
ecb5fbf1
JS
467 dc.SetFont(font);
468}
469
470inline void wxCheckSetPen(wxDC& dc, const wxPen& pen)
471{
472 const wxPen& pen1 = dc.GetPen();
473 if (pen1.IsOk() && pen.IsOk())
474 {
475 if (pen1.GetWidth() == pen.GetWidth() &&
476 pen1.GetStyle() == pen.GetStyle() &&
477 pen1.GetColour() == pen.GetColour())
478 return;
479 }
480 dc.SetPen(pen);
481}
482
483inline void wxCheckSetBrush(wxDC& dc, const wxBrush& brush)
484{
485 const wxBrush& brush1 = dc.GetBrush();
486 if (brush1.IsOk() && brush.IsOk())
487 {
488 if (brush1.GetStyle() == brush.GetStyle() &&
489 brush1.GetColour() == brush.GetColour())
490 return;
491 }
492 dc.SetBrush(brush);
493}
494
5d7836c4
JS
495/*!
496 * wxRichTextObject
497 * This is the base for drawable objects.
498 */
499
500IMPLEMENT_CLASS(wxRichTextObject, wxObject)
501
502wxRichTextObject::wxRichTextObject(wxRichTextObject* parent)
503{
5d7836c4
JS
504 m_refCount = 1;
505 m_parent = parent;
5d7836c4 506 m_descent = 0;
603f702b 507 m_show = true;
5d7836c4
JS
508}
509
510wxRichTextObject::~wxRichTextObject()
511{
512}
513
514void wxRichTextObject::Dereference()
515{
516 m_refCount --;
517 if (m_refCount <= 0)
518 delete this;
519}
520
521/// Copy
522void wxRichTextObject::Copy(const wxRichTextObject& obj)
523{
524 m_size = obj.m_size;
603f702b
JS
525 m_maxSize = obj.m_maxSize;
526 m_minSize = obj.m_minSize;
5d7836c4 527 m_pos = obj.m_pos;
5d7836c4 528 m_range = obj.m_range;
603f702b 529 m_ownRange = obj.m_ownRange;
5d7836c4 530 m_attributes = obj.m_attributes;
bec80f4f 531 m_properties = obj.m_properties;
5d7836c4 532 m_descent = obj.m_descent;
603f702b
JS
533 m_show = obj.m_show;
534}
535
536// Get/set the top-level container of this object.
537wxRichTextParagraphLayoutBox* wxRichTextObject::GetContainer() const
538{
539 const wxRichTextObject* p = this;
540 while (p)
541 {
542 if (p->IsTopLevel())
543 {
544 return wxDynamicCast(p, wxRichTextParagraphLayoutBox);
545 }
546 p = p->GetParent();
547 }
548 return NULL;
5d7836c4
JS
549}
550
551void wxRichTextObject::SetMargins(int margin)
552{
603f702b 553 SetMargins(margin, margin, margin, margin);
5d7836c4
JS
554}
555
556void wxRichTextObject::SetMargins(int leftMargin, int rightMargin, int topMargin, int bottomMargin)
557{
603f702b
JS
558 GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().SetValue(leftMargin, wxTEXT_ATTR_UNITS_PIXELS);
559 GetAttributes().GetTextBoxAttr().GetMargins().GetRight().SetValue(rightMargin, wxTEXT_ATTR_UNITS_PIXELS);
560 GetAttributes().GetTextBoxAttr().GetMargins().GetTop().SetValue(topMargin, wxTEXT_ATTR_UNITS_PIXELS);
561 GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().SetValue(bottomMargin, wxTEXT_ATTR_UNITS_PIXELS);
562}
563
564int wxRichTextObject::GetLeftMargin() const
565{
566 return GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().GetValue();
567}
568
569int wxRichTextObject::GetRightMargin() const
570{
571 return GetAttributes().GetTextBoxAttr().GetMargins().GetRight().GetValue();
572}
573
574int wxRichTextObject::GetTopMargin() const
575{
576 return GetAttributes().GetTextBoxAttr().GetMargins().GetTop().GetValue();
577}
578
579int wxRichTextObject::GetBottomMargin() const
580{
581 return GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().GetValue();
582}
583
584// Calculate the available content space in the given rectangle, given the
585// margins, border and padding specified in the object's attributes.
8db2e3ef 586wxRect wxRichTextObject::GetAvailableContentArea(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& outerRect) const
603f702b
JS
587{
588 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
589 marginRect = outerRect;
8db2e3ef
JS
590 wxRichTextAttr attr(GetAttributes());
591 context.ApplyVirtualAttributes(attr, (wxRichTextObject*) this);
592 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
593 return contentRect;
594}
595
596// Invalidate the buffer. With no argument, invalidates whole buffer.
597void wxRichTextObject::Invalidate(const wxRichTextRange& invalidRange)
598{
599 if (invalidRange != wxRICHTEXT_NONE)
600 {
23698b12
JS
601 // If this is a floating object, size may not be recalculated
602 // after floats have been collected in an early stage of Layout.
603 // So avoid resetting the cache for floating objects during layout.
e12b91a3 604 if (!IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode())
23698b12 605 SetCachedSize(wxDefaultSize);
603f702b
JS
606 SetMaxSize(wxDefaultSize);
607 SetMinSize(wxDefaultSize);
608 }
5d7836c4
JS
609}
610
44219ff0 611// Convert units in tenths of a millimetre to device units
cdaed652 612int wxRichTextObject::ConvertTenthsMMToPixels(wxDC& dc, int units) const
5d7836c4 613{
44219ff0 614 // Unscale
bec80f4f
JS
615 double scale = 1.0;
616 if (GetBuffer())
32423dd8 617 scale = GetBuffer()->GetScale() / GetBuffer()->GetDimensionScale();
bec80f4f
JS
618 int p = ConvertTenthsMMToPixels(dc.GetPPI().x, units, scale);
619
44219ff0
JS
620 return p;
621}
622
623// Convert units in tenths of a millimetre to device units
bec80f4f 624int wxRichTextObject::ConvertTenthsMMToPixels(int ppi, int units, double scale)
44219ff0 625{
5d7836c4
JS
626 // There are ppi pixels in 254.1 "1/10 mm"
627
628 double pixels = ((double) units * (double)ppi) / 254.1;
bec80f4f
JS
629 if (scale != 1.0)
630 pixels /= scale;
5d7836c4 631
603f702b
JS
632 // If the result is very small, make it at least one pixel in size.
633 if (pixels == 0 && units > 0)
634 pixels = 1;
635
5d7836c4
JS
636 return (int) pixels;
637}
638
24777478
JS
639// Convert units in pixels to tenths of a millimetre
640int wxRichTextObject::ConvertPixelsToTenthsMM(wxDC& dc, int pixels) const
641{
642 int p = pixels;
bec80f4f
JS
643 double scale = 1.0;
644 if (GetBuffer())
645 scale = GetBuffer()->GetScale();
646
647 return ConvertPixelsToTenthsMM(dc.GetPPI().x, p, scale);
24777478
JS
648}
649
bec80f4f 650int wxRichTextObject::ConvertPixelsToTenthsMM(int ppi, int pixels, double scale)
24777478
JS
651{
652 // There are ppi pixels in 254.1 "1/10 mm"
bec80f4f
JS
653
654 double p = double(pixels);
655
656 if (scale != 1.0)
657 p *= scale;
658
659 int units = int( p * 254.1 / (double) ppi );
24777478
JS
660 return units;
661}
662
bec80f4f 663// Draw the borders and background for the given rectangle and attributes.
603f702b
JS
664// Width and height are taken to be the outer margin size, not the content.
665bool wxRichTextObject::DrawBoxAttributes(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& attr, const wxRect& boxRect, int flags)
bec80f4f
JS
666{
667 // Assume boxRect is the area around the content
603f702b
JS
668 wxRect marginRect = boxRect;
669 wxRect contentRect, borderRect, paddingRect, outlineRect;
bec80f4f 670
603f702b 671 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
bec80f4f
JS
672
673 // Margin is transparent. Draw background from margin.
603f702b 674 if (attr.HasBackgroundColour() || (flags & wxRICHTEXT_DRAW_SELECTED))
bec80f4f 675 {
603f702b
JS
676 wxColour colour;
677 if (flags & wxRICHTEXT_DRAW_SELECTED)
678 {
679 // TODO: get selection colour from control?
680 colour = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);
681 }
682 else
683 colour = attr.GetBackgroundColour();
684
685 wxPen pen(colour);
686 wxBrush brush(colour);
bec80f4f
JS
687
688 dc.SetPen(pen);
689 dc.SetBrush(brush);
37e7b783 690 dc.DrawRectangle(borderRect);
bec80f4f
JS
691 }
692
603f702b
JS
693 if (flags & wxRICHTEXT_DRAW_GUIDELINES)
694 {
695 wxRichTextAttr editBorderAttr = attr;
696 // TODO: make guideline colour configurable
697 editBorderAttr.GetTextBoxAttr().GetBorder().SetColour(*wxLIGHT_GREY);
698 editBorderAttr.GetTextBoxAttr().GetBorder().SetWidth(1, wxTEXT_ATTR_UNITS_PIXELS);
699 editBorderAttr.GetTextBoxAttr().GetBorder().SetStyle(wxTEXT_BOX_ATTR_BORDER_SOLID);
700
701 DrawBorder(dc, buffer, editBorderAttr.GetTextBoxAttr().GetBorder(), borderRect, flags);
702 }
703
704 if (attr.GetTextBoxAttr().GetBorder().IsValid())
705 DrawBorder(dc, buffer, attr.GetTextBoxAttr().GetBorder(), borderRect);
bec80f4f 706
603f702b
JS
707 if (attr.GetTextBoxAttr().GetOutline().IsValid())
708 DrawBorder(dc, buffer, attr.GetTextBoxAttr().GetOutline(), outlineRect);
bec80f4f
JS
709
710 return true;
711}
712
713// Draw a border
603f702b 714bool wxRichTextObject::DrawBorder(wxDC& dc, wxRichTextBuffer* buffer, const wxTextAttrBorders& attr, const wxRect& rect, int WXUNUSED(flags))
bec80f4f
JS
715{
716 int borderLeft = 0, borderRight = 0, borderTop = 0, borderBottom = 0;
603f702b 717 wxTextAttrDimensionConverter converter(dc, buffer ? buffer->GetScale() : 1.0);
bec80f4f 718
603f702b 719 if (attr.GetLeft().IsValid() && attr.GetLeft().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
bec80f4f
JS
720 {
721 borderLeft = converter.GetPixels(attr.GetLeft().GetWidth());
722 wxColour col(attr.GetLeft().GetColour());
723
724 // If pen width is > 1, resorts to a solid rectangle.
725 if (borderLeft == 1)
726 {
727 int penStyle = wxSOLID;
728 if (attr.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
729 penStyle = wxDOT;
730 else if (attr.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
731 penStyle = wxLONG_DASH;
603f702b 732 wxPen pen(col, 1, penStyle);
bec80f4f
JS
733 dc.SetPen(pen);
734 dc.DrawLine(rect.x, rect.y, rect.x, rect.y + rect.height);
735
736 }
737 else if (borderLeft > 1)
738 {
739 wxPen pen(col);
740 wxBrush brush(col);
741 dc.SetPen(pen);
742 dc.SetBrush(brush);
743 dc.DrawRectangle(rect.x, rect.y, borderLeft, rect.height);
744 }
745 }
746
603f702b 747 if (attr.GetRight().IsValid() && attr.GetRight().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
bec80f4f
JS
748 {
749 borderRight = converter.GetPixels(attr.GetRight().GetWidth());
750
751 wxColour col(attr.GetRight().GetColour());
752
753 // If pen width is > 1, resorts to a solid rectangle.
754 if (borderRight == 1)
755 {
756 int penStyle = wxSOLID;
757 if (attr.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
758 penStyle = wxDOT;
759 else if (attr.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
760 penStyle = wxLONG_DASH;
603f702b 761 wxPen pen(col, 1, penStyle);
bec80f4f 762 dc.SetPen(pen);
603f702b 763 dc.DrawLine(rect.x + rect.width, rect.y, rect.x + rect.width, rect.y + rect.height + 1);
bec80f4f
JS
764
765 }
766 else if (borderRight > 1)
767 {
768 wxPen pen(col);
769 wxBrush brush(col);
770 dc.SetPen(pen);
771 dc.SetBrush(brush);
63af79de 772 dc.DrawRectangle(rect.x + rect.width - borderRight, rect.y, borderRight, rect.height);
bec80f4f
JS
773 }
774 }
775
603f702b 776 if (attr.GetTop().IsValid() && attr.GetTop().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
bec80f4f
JS
777 {
778 borderTop = converter.GetPixels(attr.GetTop().GetWidth());
779
780 wxColour col(attr.GetTop().GetColour());
781
782 // If pen width is > 1, resorts to a solid rectangle.
783 if (borderTop == 1)
784 {
785 int penStyle = wxSOLID;
786 if (attr.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
787 penStyle = wxDOT;
788 else if (attr.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
789 penStyle = wxLONG_DASH;
603f702b 790 wxPen pen(col, 1, penStyle);
bec80f4f
JS
791 dc.SetPen(pen);
792 dc.DrawLine(rect.x, rect.y, rect.x + rect.width, rect.y);
793
794 }
795 else if (borderTop > 1)
796 {
797 wxPen pen(col);
798 wxBrush brush(col);
799 dc.SetPen(pen);
800 dc.SetBrush(brush);
801 dc.DrawRectangle(rect.x, rect.y, rect.width, borderTop);
802 }
803 }
804
603f702b 805 if (attr.GetBottom().IsValid() && attr.GetBottom().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
bec80f4f
JS
806 {
807 borderBottom = converter.GetPixels(attr.GetBottom().GetWidth());
914a4e23 808 wxColour col(attr.GetBottom().GetColour());
bec80f4f
JS
809
810 // If pen width is > 1, resorts to a solid rectangle.
811 if (borderBottom == 1)
812 {
813 int penStyle = wxSOLID;
814 if (attr.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
815 penStyle = wxDOT;
816 else if (attr.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
817 penStyle = wxLONG_DASH;
603f702b 818 wxPen pen(col, 1, penStyle);
bec80f4f
JS
819 dc.SetPen(pen);
820 dc.DrawLine(rect.x, rect.y + rect.height, rect.x + rect.width, rect.y + rect.height);
821
822 }
823 else if (borderBottom > 1)
824 {
825 wxPen pen(col);
826 wxBrush brush(col);
827 dc.SetPen(pen);
828 dc.SetBrush(brush);
63af79de 829 dc.DrawRectangle(rect.x, rect.y + rect.height - borderBottom, rect.width, borderBottom);
bec80f4f
JS
830 }
831 }
832
833 return true;
834}
835
836// Get the various rectangles of the box model in pixels. You can either specify contentRect (inner)
837// or marginRect (outer), and the other must be the default rectangle (no width or height).
838// Note that the outline doesn't affect the position of the rectangle, it's drawn in whatever space
839// is available.
840//
841// | Margin | Border | Padding | CONTENT | Padding | Border | Margin |
842
603f702b 843bool wxRichTextObject::GetBoxRects(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& attr, wxRect& marginRect, wxRect& borderRect, wxRect& contentRect, wxRect& paddingRect, wxRect& outlineRect)
bec80f4f
JS
844{
845 int borderLeft = 0, borderRight = 0, borderTop = 0, borderBottom = 0;
846 int outlineLeft = 0, outlineRight = 0, outlineTop = 0, outlineBottom = 0;
847 int paddingLeft = 0, paddingRight = 0, paddingTop = 0, paddingBottom = 0;
848 int marginLeft = 0, marginRight = 0, marginTop = 0, marginBottom = 0;
849
603f702b 850 wxTextAttrDimensionConverter converter(dc, buffer ? buffer->GetScale() : 1.0);
bec80f4f 851
603f702b 852 if (attr.GetTextBoxAttr().GetMargins().GetLeft().IsValid())
bec80f4f 853 marginLeft = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetLeft());
603f702b 854 if (attr.GetTextBoxAttr().GetMargins().GetRight().IsValid())
bec80f4f 855 marginRight = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetRight());
603f702b 856 if (attr.GetTextBoxAttr().GetMargins().GetTop().IsValid())
bec80f4f 857 marginTop = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetTop());
83c6ae8e 858 if (attr.GetTextBoxAttr().GetMargins().GetBottom().IsValid())
bec80f4f
JS
859 marginBottom = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetBottom());
860
603f702b 861 if (attr.GetTextBoxAttr().GetBorder().GetLeft().GetWidth().IsValid())
bec80f4f 862 borderLeft = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetLeft().GetWidth());
603f702b 863 if (attr.GetTextBoxAttr().GetBorder().GetRight().GetWidth().IsValid())
bec80f4f 864 borderRight = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetRight().GetWidth());
603f702b 865 if (attr.GetTextBoxAttr().GetBorder().GetTop().GetWidth().IsValid())
bec80f4f 866 borderTop = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetTop().GetWidth());
83c6ae8e 867 if (attr.GetTextBoxAttr().GetBorder().GetBottom().GetWidth().IsValid())
bec80f4f
JS
868 borderBottom = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetBottom().GetWidth());
869
603f702b 870 if (attr.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
bec80f4f 871 paddingLeft = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetLeft());
603f702b 872 if (attr.GetTextBoxAttr().GetPadding().GetRight().IsValid())
bec80f4f 873 paddingRight = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetRight());
603f702b 874 if (attr.GetTextBoxAttr().GetPadding().GetTop().IsValid())
bec80f4f 875 paddingTop = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetTop());
603f702b 876 if (attr.GetTextBoxAttr().GetPadding().GetBottom().IsValid())
bec80f4f
JS
877 paddingBottom = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetBottom());
878
603f702b 879 if (attr.GetTextBoxAttr().GetOutline().GetLeft().GetWidth().IsValid())
bec80f4f 880 outlineLeft = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetLeft().GetWidth());
603f702b 881 if (attr.GetTextBoxAttr().GetOutline().GetRight().GetWidth().IsValid())
bec80f4f 882 outlineRight = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetRight().GetWidth());
603f702b 883 if (attr.GetTextBoxAttr().GetOutline().GetTop().GetWidth().IsValid())
bec80f4f 884 outlineTop = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetTop().GetWidth());
603f702b 885 if (attr.GetTextBoxAttr().GetOutline().GetBottom().GetWidth().IsValid())
bec80f4f
JS
886 outlineBottom = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetBottom().GetWidth());
887
888 int leftTotal = marginLeft + borderLeft + paddingLeft;
889 int rightTotal = marginRight + borderRight + paddingRight;
890 int topTotal = marginTop + borderTop + paddingTop;
891 int bottomTotal = marginBottom + borderBottom + paddingBottom;
892
893 if (marginRect != wxRect())
894 {
895 contentRect.x = marginRect.x + leftTotal;
896 contentRect.y = marginRect.y + topTotal;
897 contentRect.width = marginRect.width - (leftTotal + rightTotal);
898 contentRect.height = marginRect.height - (topTotal + bottomTotal);
899 }
900 else
901 {
902 marginRect.x = contentRect.x - leftTotal;
903 marginRect.y = contentRect.y - topTotal;
904 marginRect.width = contentRect.width + (leftTotal + rightTotal);
905 marginRect.height = contentRect.height + (topTotal + bottomTotal);
906 }
907
908 borderRect.x = marginRect.x + marginLeft;
909 borderRect.y = marginRect.y + marginTop;
910 borderRect.width = marginRect.width - (marginLeft + marginRight);
911 borderRect.height = marginRect.height - (marginTop + marginBottom);
912
913 paddingRect.x = marginRect.x + marginLeft + borderLeft;
914 paddingRect.y = marginRect.y + marginTop + borderTop;
915 paddingRect.width = marginRect.width - (marginLeft + marginRight + borderLeft + borderRight);
916 paddingRect.height = marginRect.height - (marginTop + marginBottom + borderTop + borderBottom);
917
918 // The outline is outside the margin and doesn't influence the overall box position or content size.
919 outlineRect.x = marginRect.x - outlineLeft;
920 outlineRect.y = marginRect.y - outlineTop;
921 outlineRect.width = marginRect.width + (outlineLeft + outlineRight);
922 outlineRect.height = marginRect.height + (outlineTop + outlineBottom);
923
924 return true;
925}
926
603f702b
JS
927// Get the total margin for the object in pixels, taking into account margin, padding and border size
928bool wxRichTextObject::GetTotalMargin(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& attr, int& leftMargin, int& rightMargin,
929 int& topMargin, int& bottomMargin)
930{
931 // Assume boxRect is the area around the content
932 wxRect contentRect, marginRect, borderRect, paddingRect, outlineRect;
933 marginRect = wxRect(0, 0, 1000, 1000);
bec80f4f 934
603f702b
JS
935 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
936
937 leftMargin = contentRect.GetLeft() - marginRect.GetLeft();
938 rightMargin = marginRect.GetRight() - contentRect.GetRight();
939 topMargin = contentRect.GetTop() - marginRect.GetTop();
940 bottomMargin = marginRect.GetBottom() - contentRect.GetBottom();
941
942 return true;
943}
944
945// Returns the rectangle which the child has available to it given restrictions specified in the
946// child attribute, e.g. 50% width of the parent, 400 pixels, x position 20% of the parent, etc.
bb7bbd12
JS
947// availableContainerSpace might be a parent that the cell has to compute its width relative to.
948// E.g. a cell that's 50% of its parent.
949wxRect wxRichTextObject::AdjustAvailableSpace(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& WXUNUSED(parentAttr), const wxRichTextAttr& childAttr, const wxRect& availableParentSpace, const wxRect& availableContainerSpace)
603f702b
JS
950{
951 wxRect rect = availableParentSpace;
952 double scale = 1.0;
953 if (buffer)
954 scale = buffer->GetScale();
955
bb7bbd12 956 wxTextAttrDimensionConverter converter(dc, scale, availableContainerSpace.GetSize());
603f702b
JS
957
958 if (childAttr.GetTextBoxAttr().GetWidth().IsValid())
959 rect.width = converter.GetPixels(childAttr.GetTextBoxAttr().GetWidth());
960
961 if (childAttr.GetTextBoxAttr().GetHeight().IsValid())
962 rect.height = converter.GetPixels(childAttr.GetTextBoxAttr().GetHeight());
963
964 // Can specify either left or right for the position (we're assuming we can't
965 // set the left and right edges to effectively set the size. Would we want to do that?)
966 if (childAttr.GetTextBoxAttr().GetPosition().GetLeft().IsValid())
967 {
968 rect.x = rect.x + converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetLeft());
969 }
970 else if (childAttr.GetTextBoxAttr().GetPosition().GetRight().IsValid())
971 {
972 int x = converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetRight());
973 if (childAttr.GetTextBoxAttr().GetPosition().GetRight().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE)
bb7bbd12 974 rect.x = availableContainerSpace.x + availableContainerSpace.width - rect.width;
603f702b
JS
975 else
976 rect.x += x;
977 }
978
979 if (childAttr.GetTextBoxAttr().GetPosition().GetTop().IsValid())
980 {
981 rect.y = rect.y + converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetTop());
982 }
983 else if (childAttr.GetTextBoxAttr().GetPosition().GetBottom().IsValid())
984 {
985 int y = converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetBottom());
986 if (childAttr.GetTextBoxAttr().GetPosition().GetBottom().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE)
bb7bbd12 987 rect.y = availableContainerSpace.y + availableContainerSpace.height - rect.height;
603f702b
JS
988 else
989 rect.y += y;
990 }
991
bb7bbd12
JS
992 if (rect.GetWidth() > availableParentSpace.GetWidth())
993 rect.SetWidth(availableParentSpace.GetWidth());
994
603f702b
JS
995 return rect;
996}
997
998// Dump to output stream for debugging
5d7836c4
JS
999void wxRichTextObject::Dump(wxTextOutputStream& stream)
1000{
1001 stream << GetClassInfo()->GetClassName() << wxT("\n");
1002 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");
1003 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");
1004}
1005
603f702b 1006// Gets the containing buffer
44219ff0
JS
1007wxRichTextBuffer* wxRichTextObject::GetBuffer() const
1008{
1009 const wxRichTextObject* obj = this;
345c78ca 1010 while (obj && !wxDynamicCast(obj, wxRichTextBuffer))
44219ff0
JS
1011 obj = obj->GetParent();
1012 return wxDynamicCast(obj, wxRichTextBuffer);
1013}
5d7836c4 1014
603f702b
JS
1015// Get the absolute object position, by traversing up the child/parent hierarchy
1016wxPoint wxRichTextObject::GetAbsolutePosition() const
1017{
1018 wxPoint pt = GetPosition();
1019
1020 wxRichTextObject* p = GetParent();
1021 while (p)
1022 {
1023 pt = pt + p->GetPosition();
1024 p = p->GetParent();
1025 }
1026
1027 return pt;
1028}
1029
1030// Hit-testing: returns a flag indicating hit test details, plus
1031// information about position
8db2e3ef 1032int wxRichTextObject::HitTest(wxDC& WXUNUSED(dc), wxRichTextDrawingContext& WXUNUSED(context), const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int WXUNUSED(flags))
603f702b
JS
1033{
1034 if (!IsShown())
1035 return wxRICHTEXT_HITTEST_NONE;
1036
1037 wxRect rect = GetRect();
1038 if (pt.x >= rect.x && pt.x < rect.x + rect.width &&
1039 pt.y >= rect.y && pt.y < rect.y + rect.height)
1040 {
1041 *obj = this;
1042 *contextObj = GetParentContainer();
1043 textPosition = GetRange().GetStart();
1044 return wxRICHTEXT_HITTEST_ON;
1045 }
1046 else
1047 return wxRICHTEXT_HITTEST_NONE;
1048}
1049
1050// Lays out the object first with a given amount of space, and then if no width was specified in attr,
1051// lays out the object again using the maximum ('best') size
8db2e3ef 1052bool wxRichTextObject::LayoutToBestSize(wxDC& dc, wxRichTextDrawingContext& context, wxRichTextBuffer* buffer,
bb7bbd12
JS
1053 const wxRichTextAttr& parentAttr, const wxRichTextAttr& attr,
1054 const wxRect& availableParentSpace, const wxRect& availableContainerSpace,
603f702b
JS
1055 int style)
1056{
bb7bbd12 1057 wxRect availableChildRect = AdjustAvailableSpace(dc, buffer, parentAttr, attr, availableParentSpace, availableContainerSpace);
603f702b 1058 wxRect originalAvailableRect = availableChildRect;
8db2e3ef 1059 Layout(dc, context, availableChildRect, availableContainerSpace, style);
603f702b
JS
1060
1061 wxSize maxSize = GetMaxSize();
1062
1063 // Don't ignore if maxSize.x is zero, since we need to redo the paragraph's lines
1064 // on this basis
bb7bbd12 1065 if (!attr.GetTextBoxAttr().GetWidth().IsValid() && maxSize.x < availableChildRect.width)
603f702b
JS
1066 {
1067 // Redo the layout with a fixed, minimum size this time.
1068 Invalidate(wxRICHTEXT_ALL);
1069 wxRichTextAttr newAttr(attr);
1070 newAttr.GetTextBoxAttr().GetWidth().SetValue(maxSize.x, wxTEXT_ATTR_UNITS_PIXELS);
1071 newAttr.GetTextBoxAttr().GetWidth().SetPosition(wxTEXT_BOX_ATTR_POSITION_ABSOLUTE);
1072
bb7bbd12 1073 availableChildRect = AdjustAvailableSpace(dc, buffer, parentAttr, newAttr, availableParentSpace, availableContainerSpace);
603f702b
JS
1074
1075 // If a paragraph, align the whole paragraph.
1076 // Problem with this: if we're limited by a floating object, a line may be centered
1077 // w.r.t. the smaller resulting box rather than the actual available width.
e12b91a3
JS
1078 // FIXME: aligning whole paragraph not compatible with floating objects
1079 if (attr.HasAlignment() && (!wxRichTextBuffer::GetFloatingLayoutMode() || (GetContainer()->GetFloatCollector() && !GetContainer()->GetFloatCollector()->HasFloats())))
603f702b
JS
1080 {
1081 // centering, right-justification
8db2e3ef 1082 if (attr.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE)
603f702b
JS
1083 {
1084 availableChildRect.x = (originalAvailableRect.GetWidth() - availableChildRect.GetWidth())/2 + availableChildRect.x;
1085 }
8db2e3ef 1086 else if (attr.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT)
603f702b
JS
1087 {
1088 availableChildRect.x = availableChildRect.x + originalAvailableRect.GetWidth() - availableChildRect.GetWidth();
1089 }
1090 }
1091
8db2e3ef 1092 Layout(dc, context, availableChildRect, availableContainerSpace, style);
603f702b
JS
1093 }
1094
1095 /*
1096 __________________
1097 | ____________ |
1098 | | | |
1099
1100
1101 */
1102
1103 return true;
1104}
1105
1106// Move the object recursively, by adding the offset from old to new
1107void wxRichTextObject::Move(const wxPoint& pt)
1108{
1109 SetPosition(pt);
1110}
1111
1112
5d7836c4
JS
1113/*!
1114 * wxRichTextCompositeObject
1115 * This is the base for drawable objects.
1116 */
1117
1118IMPLEMENT_CLASS(wxRichTextCompositeObject, wxRichTextObject)
1119
1120wxRichTextCompositeObject::wxRichTextCompositeObject(wxRichTextObject* parent):
1121 wxRichTextObject(parent)
1122{
1123}
1124
1125wxRichTextCompositeObject::~wxRichTextCompositeObject()
1126{
1127 DeleteChildren();
1128}
1129
1130/// Get the nth child
1131wxRichTextObject* wxRichTextCompositeObject::GetChild(size_t n) const
1132{
1133 wxASSERT ( n < m_children.GetCount() );
1134
1135 return m_children.Item(n)->GetData();
1136}
1137
1138/// Append a child, returning the position
1139size_t wxRichTextCompositeObject::AppendChild(wxRichTextObject* child)
1140{
1141 m_children.Append(child);
1142 child->SetParent(this);
1143 return m_children.GetCount() - 1;
1144}
1145
1146/// Insert the child in front of the given object, or at the beginning
1147bool wxRichTextCompositeObject::InsertChild(wxRichTextObject* child, wxRichTextObject* inFrontOf)
1148{
1149 if (inFrontOf)
1150 {
1151 wxRichTextObjectList::compatibility_iterator node = m_children.Find(inFrontOf);
1152 m_children.Insert(node, child);
1153 }
1154 else
1155 m_children.Insert(child);
1156 child->SetParent(this);
1157
1158 return true;
1159}
1160
1161/// Delete the child
1162bool wxRichTextCompositeObject::RemoveChild(wxRichTextObject* child, bool deleteChild)
1163{
1164 wxRichTextObjectList::compatibility_iterator node = m_children.Find(child);
1165 if (node)
1166 {
efbf6735
JS
1167 wxRichTextObject* obj = node->GetData();
1168 m_children.Erase(node);
5d7836c4 1169 if (deleteChild)
efbf6735 1170 delete obj;
5d7836c4
JS
1171
1172 return true;
1173 }
1174 return false;
1175}
1176
1177/// Delete all children
1178bool wxRichTextCompositeObject::DeleteChildren()
1179{
1180 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1181 while (node)
1182 {
1183 wxRichTextObjectList::compatibility_iterator oldNode = node;
1184
1185 wxRichTextObject* child = node->GetData();
1186 child->Dereference(); // Only delete if reference count is zero
1187
1188 node = node->GetNext();
efbf6735 1189 m_children.Erase(oldNode);
5d7836c4
JS
1190 }
1191
1192 return true;
1193}
1194
1195/// Get the child count
1196size_t wxRichTextCompositeObject::GetChildCount() const
1197{
1198 return m_children.GetCount();
1199}
1200
1201/// Copy
1202void wxRichTextCompositeObject::Copy(const wxRichTextCompositeObject& obj)
1203{
1204 wxRichTextObject::Copy(obj);
1205
1206 DeleteChildren();
1207
1208 wxRichTextObjectList::compatibility_iterator node = obj.m_children.GetFirst();
1209 while (node)
1210 {
1211 wxRichTextObject* child = node->GetData();
fe5aa22c
JS
1212 wxRichTextObject* newChild = child->Clone();
1213 newChild->SetParent(this);
1214 m_children.Append(newChild);
5d7836c4
JS
1215
1216 node = node->GetNext();
1217 }
1218}
1219
1220/// Hit-testing: returns a flag indicating hit test details, plus
1221/// information about position
8db2e3ef 1222int wxRichTextCompositeObject::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
5d7836c4 1223{
603f702b
JS
1224 if (!IsShown())
1225 return wxRICHTEXT_HITTEST_NONE;
1226
5d7836c4
JS
1227 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1228 while (node)
1229 {
1230 wxRichTextObject* child = node->GetData();
1231
603f702b
JS
1232 if (child->IsShown() && child->IsTopLevel() && (flags & wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS))
1233 {
1234 // Just check if we hit the overall object
8db2e3ef 1235 int ret = child->wxRichTextObject::HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
603f702b
JS
1236 if (ret != wxRICHTEXT_HITTEST_NONE)
1237 return ret;
1238 }
1239 else if (child->IsShown())
1240 {
8db2e3ef 1241 int ret = child->HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
603f702b
JS
1242 if (ret != wxRICHTEXT_HITTEST_NONE)
1243 return ret;
1244 }
5d7836c4
JS
1245
1246 node = node->GetNext();
1247 }
1248
603f702b 1249 return wxRICHTEXT_HITTEST_NONE;
5d7836c4
JS
1250}
1251
1252/// Finds the absolute position and row height for the given character position
8db2e3ef 1253bool wxRichTextCompositeObject::FindPosition(wxDC& dc, wxRichTextDrawingContext& context, long index, wxPoint& pt, int* height, bool forceLineStart)
5d7836c4
JS
1254{
1255 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1256 while (node)
1257 {
1258 wxRichTextObject* child = node->GetData();
1259
603f702b
JS
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.
8db2e3ef 1264 if (!child->IsTopLevel() && child->FindPosition(dc, context, index, pt, height, forceLineStart))
5d7836c4
JS
1265 return true;
1266
1267 node = node->GetNext();
1268 }
1269
1270 return false;
1271}
1272
1273/// Calculate range
1274void wxRichTextCompositeObject::CalculateRange(long start, long& end)
1275{
1276 long current = start;
1277 long lastEnd = current;
1278
603f702b
JS
1279 if (IsTopLevel())
1280 {
1281 current = 0;
1282 lastEnd = 0;
1283 }
1284
5d7836c4
JS
1285 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1286 while (node)
1287 {
1288 wxRichTextObject* child = node->GetData();
1289 long childEnd = 0;
1290
1291 child->CalculateRange(current, childEnd);
1292 lastEnd = childEnd;
1293
1294 current = childEnd + 1;
1295
1296 node = node->GetNext();
1297 }
1298
603f702b
JS
1299 if (IsTopLevel())
1300 {
1301 // A top-level object always has a range of size 1,
1302 // because its children don't count at this level.
1303 end = start;
1304 m_range.SetRange(start, start);
5d7836c4 1305
603f702b
JS
1306 // An object with no children has zero length
1307 if (m_children.GetCount() == 0)
1308 lastEnd --;
1309 m_ownRange.SetRange(0, lastEnd);
1310 }
1311 else
1312 {
1313 end = lastEnd;
5d7836c4 1314
603f702b
JS
1315 // An object with no children has zero length
1316 if (m_children.GetCount() == 0)
1317 end --;
1318
1319 m_range.SetRange(start, end);
1320 }
5d7836c4
JS
1321}
1322
1323/// Delete range from layout.
1324bool wxRichTextCompositeObject::DeleteRange(const wxRichTextRange& range)
1325{
1326 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
7fe8059f 1327
5d7836c4
JS
1328 while (node)
1329 {
1330 wxRichTextObject* obj = (wxRichTextObject*) node->GetData();
1331 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
7fe8059f 1332
5d7836c4
JS
1333 // Delete the range in each paragraph
1334
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.
7fe8059f 1341
5d7836c4
JS
1342 if (!obj->GetRange().IsOutside(range))
1343 {
603f702b
JS
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);
5d7836c4
JS
1347
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()))
1351 {
1352 // An empty paragraph has length 1, so won't be deleted unless the
1353 // whole range is deleted.
7fe8059f 1354 RemoveChild(obj, true);
5d7836c4
JS
1355 }
1356 }
7fe8059f 1357
5d7836c4
JS
1358 node = next;
1359 }
7fe8059f 1360
5d7836c4
JS
1361 return true;
1362}
1363
1364/// Get any text in this object for the given range
1365wxString wxRichTextCompositeObject::GetTextForRange(const wxRichTextRange& range) const
1366{
1367 wxString text;
1368 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1369 while (node)
1370 {
1371 wxRichTextObject* child = node->GetData();
1372 wxRichTextRange childRange = range;
1373 if (!child->GetRange().IsOutside(range))
1374 {
1375 childRange.LimitTo(child->GetRange());
7fe8059f 1376
5d7836c4 1377 wxString childText = child->GetTextForRange(childRange);
7fe8059f 1378
5d7836c4
JS
1379 text += childText;
1380 }
1381 node = node->GetNext();
1382 }
1383
1384 return text;
1385}
1386
603f702b
JS
1387/// Get the child object at the given character position
1388wxRichTextObject* wxRichTextCompositeObject::GetChildAtPosition(long pos) const
1389{
1390 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1391 while (node)
1392 {
1393 wxRichTextObject* child = node->GetData();
1394 if (child->GetRange().GetStart() == pos)
1395 return child;
1396 node = node->GetNext();
1397 }
1398 return NULL;
1399}
1400
5d7836c4 1401/// Recursively merge all pieces that can be merged.
f7667b84 1402bool wxRichTextCompositeObject::Defragment(wxRichTextDrawingContext& context, const wxRichTextRange& range)
5d7836c4
JS
1403{
1404 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1405 while (node)
1406 {
1407 wxRichTextObject* child = node->GetData();
5cb0b827 1408 if (range == wxRICHTEXT_ALL || !child->GetRange().IsOutside(range))
5d7836c4 1409 {
109bfc88
JS
1410 wxRichTextCompositeObject* composite = wxDynamicCast(child, wxRichTextCompositeObject);
1411 if (composite)
f7667b84 1412 composite->Defragment(context);
109bfc88 1413
f7667b84
JS
1414 // Optimization: if there are no virtual attributes, we won't need to
1415 // to split objects in order to paint individually attributed chunks.
1416 // So only merge in this case.
1417 if (!context.GetVirtualAttributesEnabled())
5d7836c4 1418 {
f7667b84 1419 if (node->GetNext())
109bfc88 1420 {
f7667b84
JS
1421 wxRichTextObject* nextChild = node->GetNext()->GetData();
1422 if (child->CanMerge(nextChild, context) && child->Merge(nextChild, context))
1423 {
1424 nextChild->Dereference();
1425 m_children.Erase(node->GetNext());
1426 }
1427 else
1428 node = node->GetNext();
109bfc88
JS
1429 }
1430 else
1431 node = node->GetNext();
5d7836c4
JS
1432 }
1433 else
f7667b84
JS
1434 {
1435 // If we might have virtual attributes, we first see if we have to split
1436 // objects so that they may be painted with potential virtual attributes,
1437 // since text objects can only draw or measure with a single attributes object
1438 // at a time.
1439 wxRichTextObject* childAfterSplit = child;
1440 if (child->CanSplit(context))
1441 {
1442 childAfterSplit = child->Split(context);
1443 node = m_children.Find(childAfterSplit);
1444 }
1445
1446 if (node->GetNext())
1447 {
1448 wxRichTextObject* nextChild = node->GetNext()->GetData();
f7667b84
JS
1449
1450 // First split child and nextChild so we have smaller fragments to merge.
1451 // Then Merge only has to test per-object virtual attributes
1452 // because for an object with all the same sub-object attributes,
1453 // then any general virtual attributes should be merged with sub-objects by
1454 // the implementation.
1455
1456 wxRichTextObject* nextChildAfterSplit = nextChild;
1457
1458 if (nextChildAfterSplit->CanSplit(context))
1459 nextChildAfterSplit = nextChild->Split(context);
1460
1461 bool splitNextChild = nextChild != nextChildAfterSplit;
1462
1463 // See if we can merge this new fragment with (perhaps the first part of) the next object.
1464 // Note that we use nextChild because if we had split nextChild, the first object always
1465 // remains (and further parts are appended). However we must use childAfterSplit since
1466 // it's the last part of a possibly split child.
1467
1468 if (childAfterSplit->CanMerge(nextChild, context) && childAfterSplit->Merge(nextChild, context))
1469 {
1470 nextChild->Dereference();
1471 m_children.Erase(node->GetNext());
1472
1473 // Don't set node -- we'll see if we can merge again with the next
1474 // child. UNLESS we split this or the next child, in which case we know we have to
1475 // move on to the end of the next child.
1476 if (splitNextChild)
1477 node = m_children.Find(nextChildAfterSplit);
1478 }
1479 else
1480 {
1481 if (splitNextChild)
1482 node = m_children.Find(nextChildAfterSplit); // start from the last object in the split
1483 else
1484 node = node->GetNext();
1485 }
1486 }
1487 else
1488 node = node->GetNext();
1489 }
5d7836c4
JS
1490 }
1491 else
1492 node = node->GetNext();
1493 }
1494
bec80f4f
JS
1495 // Delete any remaining empty objects, but leave at least one empty object per composite object.
1496 if (GetChildCount() > 1)
5d7836c4 1497 {
bec80f4f
JS
1498 node = m_children.GetFirst();
1499 while (node)
1500 {
1501 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
1502 wxRichTextObject* child = node->GetData();
1503 if (range == wxRICHTEXT_ALL || !child->GetRange().IsOutside(range))
1504 {
1505 if (child->IsEmpty())
1506 {
1507 child->Dereference();
1508 m_children.Erase(node);
1509 }
1510 node = next;
1511 }
1512 else
1513 node = node->GetNext();
1514 }
5d7836c4 1515 }
5d7836c4 1516
5d7836c4
JS
1517 return true;
1518}
1519
bec80f4f
JS
1520/// Dump to output stream for debugging
1521void wxRichTextCompositeObject::Dump(wxTextOutputStream& stream)
5d7836c4
JS
1522{
1523 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1524 while (node)
1525 {
1526 wxRichTextObject* child = node->GetData();
bec80f4f 1527 child->Dump(stream);
5d7836c4
JS
1528 node = node->GetNext();
1529 }
5d7836c4
JS
1530}
1531
603f702b
JS
1532/// Get/set the object size for the given range. Returns false if the range
1533/// is invalid for this object.
914a4e23 1534bool wxRichTextCompositeObject::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, const wxPoint& position, const wxSize& parentSize, wxArrayInt* partialExtents) const
603f702b
JS
1535{
1536 if (!range.IsWithin(GetRange()))
1537 return false;
5d7836c4 1538
603f702b 1539 wxSize sz;
5d7836c4 1540
603f702b
JS
1541 wxArrayInt childExtents;
1542 wxArrayInt* p;
1543 if (partialExtents)
1544 p = & childExtents;
1545 else
1546 p = NULL;
5d7836c4 1547
603f702b
JS
1548 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1549 while (node)
cdaed652 1550 {
603f702b
JS
1551 wxRichTextObject* child = node->GetData();
1552 if (!child->GetRange().IsOutside(range))
1553 {
1554 // Floating objects have a zero size within the paragraph.
e12b91a3 1555 if (child->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode())
603f702b
JS
1556 {
1557 if (partialExtents)
1558 {
1559 int lastSize;
1560 if (partialExtents->GetCount() > 0)
1561 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
1562 else
1563 lastSize = 0;
cdaed652 1564
603f702b
JS
1565 partialExtents->Add(0 /* zero size */ + lastSize);
1566 }
1567 }
1568 else
1569 {
1570 wxSize childSize;
5d7836c4 1571
603f702b
JS
1572 wxRichTextRange rangeToUse = range;
1573 rangeToUse.LimitTo(child->GetRange());
1574 if (child->IsTopLevel())
1575 rangeToUse = child->GetOwnRange();
5d7836c4 1576
603f702b 1577 int childDescent = 0;
cdaed652 1578
603f702b
JS
1579 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we're already cached the size,
1580 // but it's only going to be used after caching has taken place.
1581 if ((flags & wxRICHTEXT_HEIGHT_ONLY) && child->GetCachedSize().y != 0)
1582 {
1583 childDescent = child->GetDescent();
1584 childSize = child->GetCachedSize();
bec80f4f 1585
603f702b
JS
1586 sz.y = wxMax(sz.y, childSize.y);
1587 sz.x += childSize.x;
1588 descent = wxMax(descent, childDescent);
1589 }
914a4e23 1590 else if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y), parentSize, p))
603f702b
JS
1591 {
1592 sz.y = wxMax(sz.y, childSize.y);
1593 sz.x += childSize.x;
1594 descent = wxMax(descent, childDescent);
bec80f4f 1595
603f702b
JS
1596 if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange() || child->IsTopLevel()))
1597 {
1598 child->SetCachedSize(childSize);
1599 child->SetDescent(childDescent);
1600 }
bec80f4f 1601
603f702b
JS
1602 if (partialExtents)
1603 {
1604 int lastSize;
1605 if (partialExtents->GetCount() > 0)
1606 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
1607 else
1608 lastSize = 0;
bec80f4f 1609
603f702b
JS
1610 size_t i;
1611 for (i = 0; i < childExtents.GetCount(); i++)
1612 {
1613 partialExtents->Add(childExtents[i] + lastSize);
1614 }
1615 }
1616 }
1617 }
1618
1619 if (p)
1620 p->Clear();
1621 }
1622
1623 node = node->GetNext();
1624 }
1625 size = sz;
1626 return true;
1627}
1628
1629// Invalidate the buffer. With no argument, invalidates whole buffer.
1630void wxRichTextCompositeObject::Invalidate(const wxRichTextRange& invalidRange)
1631{
1632 wxRichTextObject::Invalidate(invalidRange);
1633
1634 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1635 while (node)
1636 {
1637 wxRichTextObject* child = node->GetData();
1638 if (invalidRange != wxRICHTEXT_ALL && invalidRange != wxRICHTEXT_NONE && child->GetRange().IsOutside(invalidRange))
1639 {
1640 // Skip
1641 }
1642 else if (child->IsTopLevel())
1643 {
e12b91a3 1644 if (wxRichTextBuffer::GetFloatingLayoutMode() && child->IsFloating() && GetBuffer()->GetFloatCollector() && GetBuffer()->GetFloatCollector()->HasFloat(child))
c4168888
JS
1645 {
1646 // Don't invalidate subhierarchy if we've already been laid out
1647 }
603f702b 1648 else
c4168888
JS
1649 {
1650 if (invalidRange == wxRICHTEXT_NONE)
1651 child->Invalidate(wxRICHTEXT_NONE);
1652 else
1653 child->Invalidate(wxRICHTEXT_ALL); // All children must be invalidated if within parent range
1654 }
603f702b
JS
1655 }
1656 else
1657 child->Invalidate(invalidRange);
1658 node = node->GetNext();
1659 }
1660}
1661
1662// Move the object recursively, by adding the offset from old to new
1663void wxRichTextCompositeObject::Move(const wxPoint& pt)
1664{
1665 wxPoint oldPos = GetPosition();
1666 SetPosition(pt);
1667 wxPoint offset = pt - oldPos;
1668
1669 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1670 while (node)
1671 {
1672 wxRichTextObject* child = node->GetData();
1673 wxPoint childPos = child->GetPosition() + offset;
1674 child->Move(childPos);
1675 node = node->GetNext();
1676 }
1677}
1678
1679
1680/*!
1681 * wxRichTextParagraphLayoutBox
1682 * This box knows how to lay out paragraphs.
1683 */
1684
1685IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraphLayoutBox, wxRichTextCompositeObject)
1686
1687wxRichTextParagraphLayoutBox::wxRichTextParagraphLayoutBox(wxRichTextObject* parent):
1688 wxRichTextCompositeObject(parent)
1689{
1690 Init();
1691}
1692
1693wxRichTextParagraphLayoutBox::~wxRichTextParagraphLayoutBox()
1694{
1695 if (m_floatCollector)
1696 {
1697 delete m_floatCollector;
1698 m_floatCollector = NULL;
1699 }
1700}
1701
1702/// Initialize the object.
1703void wxRichTextParagraphLayoutBox::Init()
1704{
1705 m_ctrl = NULL;
1706
1707 // For now, assume is the only box and has no initial size.
1708 m_range = wxRichTextRange(0, -1);
1709 m_ownRange = wxRichTextRange(0, -1);
1710
1711 m_invalidRange = wxRICHTEXT_ALL;
1712
603f702b
JS
1713 m_partialParagraph = false;
1714 m_floatCollector = NULL;
1715}
1716
1717void wxRichTextParagraphLayoutBox::Clear()
1718{
1719 DeleteChildren();
1720
1721 if (m_floatCollector)
1722 delete m_floatCollector;
1723 m_floatCollector = NULL;
1724 m_partialParagraph = false;
1725}
1726
1727/// Copy
1728void wxRichTextParagraphLayoutBox::Copy(const wxRichTextParagraphLayoutBox& obj)
1729{
1730 Clear();
1731
1732 wxRichTextCompositeObject::Copy(obj);
1733
1734 m_partialParagraph = obj.m_partialParagraph;
1735 m_defaultAttributes = obj.m_defaultAttributes;
bec80f4f
JS
1736}
1737
07d4142f
JS
1738// Gather information about floating objects; only gather floats for those paragraphs that
1739// will not be formatted again due to optimization, after which floats will be gathered per-paragraph
1740// during layout.
603f702b 1741bool wxRichTextParagraphLayoutBox::UpdateFloatingObjects(const wxRect& availableRect, wxRichTextObject* untilObj)
cdaed652
VZ
1742{
1743 if (m_floatCollector != NULL)
1744 delete m_floatCollector;
603f702b 1745 m_floatCollector = new wxRichTextFloatCollector(availableRect);
cdaed652 1746 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
07d4142f
JS
1747 // Only gather floats up to the point we'll start formatting paragraphs.
1748 while (untilObj && node && node->GetData() != untilObj)
cdaed652
VZ
1749 {
1750 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
1751 wxASSERT (child != NULL);
1752 if (child)
1753 m_floatCollector->CollectFloat(child);
1754 node = node->GetNext();
1755 }
ce00f59b 1756
cdaed652
VZ
1757 return true;
1758}
1759
603f702b
JS
1760// Returns the style sheet associated with the overall buffer.
1761wxRichTextStyleSheet* wxRichTextParagraphLayoutBox::GetStyleSheet() const
1762{
1763 return GetBuffer() ? GetBuffer()->GetStyleSheet() : (wxRichTextStyleSheet*) NULL;
1764}
1765
1766// Get the number of floating objects at this level
1767int wxRichTextParagraphLayoutBox::GetFloatingObjectCount() const
1768{
1769 if (m_floatCollector)
1770 return m_floatCollector->GetFloatingObjectCount();
1771 else
1772 return 0;
1773}
1774
1775// Get a list of floating objects
1776bool wxRichTextParagraphLayoutBox::GetFloatingObjects(wxRichTextObjectList& objects) const
1777{
1778 if (m_floatCollector)
1779 {
1780 return m_floatCollector->GetFloatingObjects(objects);
1781 }
1782 else
1783 return false;
1784}
1785
1786// Calculate ranges
1787void wxRichTextParagraphLayoutBox::UpdateRanges()
1788{
1789 long start = 0;
1790 if (GetParent())
1791 start = GetRange().GetStart();
1792 long end;
1793 CalculateRange(start, end);
1794}
1795
cdaed652 1796// HitTest
8db2e3ef 1797int wxRichTextParagraphLayoutBox::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
cdaed652 1798{
603f702b
JS
1799 if (!IsShown())
1800 return wxRICHTEXT_HITTEST_NONE;
1801
cdaed652 1802 int ret = wxRICHTEXT_HITTEST_NONE;
e12b91a3 1803 if (wxRichTextBuffer::GetFloatingLayoutMode() && m_floatCollector && (flags & wxRICHTEXT_HITTEST_NO_FLOATING_OBJECTS) == 0)
8db2e3ef 1804 ret = m_floatCollector->HitTest(dc, context, pt, textPosition, obj, flags);
ce00f59b 1805
cdaed652 1806 if (ret == wxRICHTEXT_HITTEST_NONE)
8db2e3ef 1807 return wxRichTextCompositeObject::HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
cdaed652 1808 else
603f702b
JS
1809 {
1810 *contextObj = this;
cdaed652 1811 return ret;
603f702b 1812 }
cdaed652
VZ
1813}
1814
1815/// Draw the floating objects
8db2e3ef 1816void wxRichTextParagraphLayoutBox::DrawFloats(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
cdaed652 1817{
e12b91a3 1818 if (wxRichTextBuffer::GetFloatingLayoutMode() && m_floatCollector)
8db2e3ef 1819 m_floatCollector->Draw(dc, context, range, selection, rect, descent, style);
cdaed652
VZ
1820}
1821
bec80f4f 1822void wxRichTextParagraphLayoutBox::MoveAnchoredObjectToParagraph(wxRichTextParagraph* from, wxRichTextParagraph* to, wxRichTextObject* obj)
cdaed652
VZ
1823{
1824 if (from == to)
1825 return;
1826
1827 from->RemoveChild(obj);
1828 to->AppendChild(obj);
5d7836c4
JS
1829}
1830
1831/// Draw the item
8db2e3ef 1832bool wxRichTextParagraphLayoutBox::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
5d7836c4 1833{
603f702b
JS
1834 if (!IsShown())
1835 return true;
1836
1837 wxRect thisRect(GetPosition(), GetCachedSize());
1838
8db2e3ef
JS
1839 wxRichTextAttr attr(GetAttributes());
1840 context.ApplyVirtualAttributes(attr, this);
1841
603f702b
JS
1842 int flags = style;
1843 if (selection.IsValid() && GetParentContainer() != this && selection.WithinSelection(GetRange().GetStart(), GetParentContainer()))
1844 flags |= wxRICHTEXT_DRAW_SELECTED;
1845
1846 // Don't draw guidelines if at top level
1847 int theseFlags = flags;
1848 if (!GetParent())
1849 theseFlags &= ~wxRICHTEXT_DRAW_GUIDELINES;
8db2e3ef 1850 DrawBoxAttributes(dc, GetBuffer(), attr, thisRect, theseFlags);
603f702b 1851
e12b91a3
JS
1852 if (wxRichTextBuffer::GetFloatingLayoutMode())
1853 DrawFloats(dc, context, range, selection, rect, descent, style);
1854
5d7836c4
JS
1855 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1856 while (node)
1857 {
603f702b 1858 wxRichTextObject* child = node->GetData();
7fe8059f 1859
5d7836c4
JS
1860 if (child && !child->GetRange().IsOutside(range))
1861 {
1862 wxRect childRect(child->GetPosition(), child->GetCachedSize());
603f702b
JS
1863 wxRichTextRange childRange = range;
1864 if (child->IsTopLevel())
1865 {
1866 childRange = child->GetOwnRange();
1867 }
7fe8059f 1868
ea160b2e
JS
1869 if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) == 0) && childRect.GetTop() > rect.GetBottom())
1870 {
1871 // Stop drawing
1872 break;
1873 }
1874 else if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) == 0) && childRect.GetBottom() < rect.GetTop())
011b3dcb
JS
1875 {
1876 // Skip
1877 }
1878 else
8db2e3ef 1879 child->Draw(dc, context, childRange, selection, rect, descent, style);
5d7836c4
JS
1880 }
1881
1882 node = node->GetNext();
1883 }
1884 return true;
1885}
1886
1887/// Lay the item out
8db2e3ef 1888bool wxRichTextParagraphLayoutBox::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style)
5d7836c4 1889{
603f702b
JS
1890 SetPosition(rect.GetPosition());
1891
1892 if (!IsShown())
1893 return true;
1894
4d551ad5
JS
1895 wxRect availableSpace;
1896 bool formatRect = (style & wxRICHTEXT_LAYOUT_SPECIFIED_RECT) == wxRICHTEXT_LAYOUT_SPECIFIED_RECT;
1897
8db2e3ef
JS
1898 wxRichTextAttr attr(GetAttributes());
1899 context.ApplyVirtualAttributes(attr, this);
1900
4d551ad5 1901 // If only laying out a specific area, the passed rect has a different meaning:
44219ff0
JS
1902 // the visible part of the buffer. This is used in wxRichTextCtrl::OnSize,
1903 // so that during a size, only the visible part will be relaid out, or
1904 // it would take too long causing flicker. As an approximation, we assume that
1905 // everything up to the start of the visible area is laid out correctly.
4d551ad5
JS
1906 if (formatRect)
1907 {
603f702b 1908 wxRect rect2(0, 0, rect.width, rect.height);
8db2e3ef 1909 availableSpace = GetAvailableContentArea(dc, context, rect2);
4d551ad5
JS
1910
1911 // Invalidate the part of the buffer from the first visible line
1912 // to the end. If other parts of the buffer are currently invalid,
1913 // then they too will be taken into account if they are above
1914 // the visible point.
1915 long startPos = 0;
1916 wxRichTextLine* line = GetLineAtYPosition(rect.y);
1917 if (line)
1918 startPos = line->GetAbsoluteRange().GetStart();
1919
603f702b 1920 Invalidate(wxRichTextRange(startPos, GetOwnRange().GetEnd()));
4d551ad5
JS
1921 }
1922 else
603f702b 1923 {
8db2e3ef 1924 availableSpace = GetAvailableContentArea(dc, context, rect);
603f702b
JS
1925 }
1926
d157d142
JS
1927 // Fix the width if we're at the top level
1928 if (!GetParent())
1929 attr.GetTextBoxAttr().GetWidth().SetValue(rect.GetWidth(), wxTEXT_ATTR_UNITS_PIXELS);
1930
603f702b 1931 int leftMargin, rightMargin, topMargin, bottomMargin;
8db2e3ef 1932 wxRichTextObject::GetTotalMargin(dc, GetBuffer(), attr, leftMargin, rightMargin,
603f702b 1933 topMargin, bottomMargin);
5d7836c4
JS
1934
1935 int maxWidth = 0;
603f702b
JS
1936 int maxHeight = 0;
1937
1938 // The maximum paragraph maximum width, so we can set the overall maximum width for this object
1939 int maxMaxWidth = 0;
1940
1941 // The maximum paragraph minimum width, so we can set the overall minimum width for this object
1942 int maxMinWidth = 0;
1943
1944 // If we have vertical alignment, we must recalculate everything.
8db2e3ef
JS
1945 bool hasVerticalAlignment = (attr.GetTextBoxAttr().HasVerticalAlignment() &&
1946 (attr.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP));
7fe8059f 1947
5d7836c4 1948 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
39a1c2f2 1949
38113684 1950 bool layoutAll = true;
1e967276 1951
38113684
JS
1952 // Get invalid range, rounding to paragraph start/end.
1953 wxRichTextRange invalidRange = GetInvalidRange(true);
1954
4d551ad5 1955 if (invalidRange == wxRICHTEXT_NONE && !formatRect)
1e967276
JS
1956 return true;
1957
603f702b 1958 if (invalidRange == wxRICHTEXT_ALL || hasVerticalAlignment)
1e967276 1959 layoutAll = true;
38113684 1960 else // If we know what range is affected, start laying out from that point on.
603f702b 1961 if (invalidRange.GetStart() >= GetOwnRange().GetStart())
2c375f42 1962 {
38113684 1963 wxRichTextParagraph* firstParagraph = GetParagraphAtPosition(invalidRange.GetStart());
2c375f42
JS
1964 if (firstParagraph)
1965 {
1966 wxRichTextObjectList::compatibility_iterator firstNode = m_children.Find(firstParagraph);
0cc70962
VZ
1967 wxRichTextObjectList::compatibility_iterator previousNode;
1968 if ( firstNode )
1969 previousNode = firstNode->GetPrevious();
9b4af7b7 1970 if (firstNode)
2c375f42 1971 {
9b4af7b7
JS
1972 if (previousNode)
1973 {
1974 wxRichTextParagraph* previousParagraph = wxDynamicCast(previousNode->GetData(), wxRichTextParagraph);
1975 availableSpace.y = previousParagraph->GetPosition().y + previousParagraph->GetCachedSize().y;
1976 }
7fe8059f 1977
2c375f42
JS
1978 // Now we're going to start iterating from the first affected paragraph.
1979 node = firstNode;
1e967276
JS
1980
1981 layoutAll = false;
2c375f42
JS
1982 }
1983 }
1984 }
1985
07d4142f
JS
1986 // Gather information about only those floating objects that will not be formatted,
1987 // after which floats will be gathered per-paragraph during layout.
e12b91a3
JS
1988 if (wxRichTextBuffer::GetFloatingLayoutMode())
1989 UpdateFloatingObjects(availableSpace, node ? node->GetData() : (wxRichTextObject*) NULL);
cdaed652 1990
4d551ad5
JS
1991 // A way to force speedy rest-of-buffer layout (the 'else' below)
1992 bool forceQuickLayout = false;
39a1c2f2 1993
d3f6b1b5
JS
1994 // First get the size of the paragraphs we won't be laying out
1995 wxRichTextObjectList::compatibility_iterator n = m_children.GetFirst();
1996 while (n && n != node)
1997 {
1998 wxRichTextParagraph* child = wxDynamicCast(n->GetData(), wxRichTextParagraph);
1999 if (child)
2000 {
2001 maxWidth = wxMax(maxWidth, child->GetCachedSize().x);
2002 maxMinWidth = wxMax(maxMinWidth, child->GetMinSize().x);
2003 maxMaxWidth = wxMax(maxMaxWidth, child->GetMaxSize().x);
2004 }
2005 n = n->GetNext();
2006 }
2007
5d7836c4
JS
2008 while (node)
2009 {
2010 // Assume this box only contains paragraphs
2011
2012 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
706465df
JS
2013 // Unsure if this is needed
2014 // wxCHECK_MSG( child, false, wxT("Unknown object in layout") );
7fe8059f 2015
603f702b 2016 if (child && child->IsShown())
2c375f42 2017 {
603f702b
JS
2018 // TODO: what if the child hasn't been laid out (e.g. involved in Undo) but still has 'old' lines
2019 if ( !forceQuickLayout &&
2020 (layoutAll ||
2021 child->GetLines().IsEmpty() ||
2022 !child->GetRange().IsOutside(invalidRange)) )
2023 {
2024 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
2025 // lays out the object again using the minimum size
8db2e3ef
JS
2026 child->LayoutToBestSize(dc, context, GetBuffer(),
2027 attr, child->GetAttributes(), availableSpace, rect, style&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT);
603f702b
JS
2028
2029 // Layout must set the cached size
2030 availableSpace.y += child->GetCachedSize().y;
2031 maxWidth = wxMax(maxWidth, child->GetCachedSize().x);
2032 maxMinWidth = wxMax(maxMinWidth, child->GetMinSize().x);
2033 maxMaxWidth = wxMax(maxMaxWidth, child->GetMaxSize().x);
2034
2035 // If we're just formatting the visible part of the buffer,
2036 // and we're now past the bottom of the window, and we don't have any
2037 // floating objects (since they may cause wrapping to change for the rest of the
2038 // the buffer), start quick layout.
2039 if (!hasVerticalAlignment && formatRect && child->GetPosition().y > rect.GetBottom() && GetFloatingObjectCount() == 0)
2040 forceQuickLayout = true;
2041 }
2042 else
2043 {
2044 // We're outside the immediately affected range, so now let's just
2045 // move everything up or down. This assumes that all the children have previously
2046 // been laid out and have wrapped line lists associated with them.
2047 // TODO: check all paragraphs before the affected range.
2048
2049 int inc = availableSpace.y - child->GetPosition().y;
2050
2051 while (node)
2052 {
2053 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2054 if (child)
2055 {
2056 if (child->GetLines().GetCount() == 0)
2057 {
2058 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
2059 // lays out the object again using the minimum size
8db2e3ef
JS
2060 child->LayoutToBestSize(dc, context, GetBuffer(),
2061 attr, child->GetAttributes(), availableSpace, rect, style&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT);
603f702b
JS
2062
2063 //child->Layout(dc, availableChildRect, style);
2064 }
2065 else
2066 child->Move(wxPoint(child->GetPosition().x, child->GetPosition().y + inc));
5d7836c4 2067
603f702b
JS
2068 availableSpace.y += child->GetCachedSize().y;
2069 maxWidth = wxMax(maxWidth, child->GetCachedSize().x);
2070 maxMinWidth = wxMax(maxMinWidth, child->GetMinSize().x);
2071 maxMaxWidth = wxMax(maxMaxWidth, child->GetMaxSize().x);
2072 }
4d551ad5 2073
603f702b
JS
2074 node = node->GetNext();
2075 }
2076 break;
2077 }
2c375f42 2078 }
7fe8059f 2079
603f702b
JS
2080 node = node->GetNext();
2081 }
2082
2083 node = m_children.GetLast();
2084 if (node && node->GetData()->IsShown())
2085 {
2086 wxRichTextObject* child = node->GetData();
603f702b
JS
2087 maxHeight = child->GetPosition().y - (GetPosition().y + topMargin) + child->GetCachedSize().y;
2088 }
2089 else
2090 maxHeight = 0; // topMargin + bottomMargin;
2091
23698b12 2092 // Check the bottom edge of any floating object
e12b91a3 2093 if (wxRichTextBuffer::GetFloatingLayoutMode() && GetFloatCollector() && GetFloatCollector()->HasFloats())
23698b12
JS
2094 {
2095 int bottom = GetFloatCollector()->GetLastRectBottom();
2096 if (bottom > maxHeight)
2097 maxHeight = bottom;
2098 }
2099
8db2e3ef 2100 if (attr.GetTextBoxAttr().GetSize().GetWidth().IsValid())
bb7bbd12 2101 {
8db2e3ef 2102 wxRect r = AdjustAvailableSpace(dc, GetBuffer(), wxRichTextAttr() /* not used */, attr, parentRect, parentRect);
bb7bbd12
JS
2103 int w = r.GetWidth();
2104
2105 // Convert external to content rect
2106 w = w - leftMargin - rightMargin;
2107 maxWidth = wxMax(maxWidth, w);
2108 maxMaxWidth = wxMax(maxMaxWidth, w);
2109 }
32423dd8
JS
2110 else
2111 {
2112 // TODO: Make sure the layout box's position reflects
2113 // the position of the children, but without
2114 // breaking layout of a box within a paragraph.
2115 }
bb7bbd12 2116
603f702b
JS
2117 // TODO: (also in para layout) should set the
2118 // object's size to an absolute one if specified,
2119 // but if not specified, calculate it from content.
2120
2121 // We need to add back the margins etc.
2122 {
2123 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
2124 contentRect = wxRect(wxPoint(0, 0), wxSize(maxWidth, maxHeight));
8db2e3ef 2125 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
2126 SetCachedSize(marginRect.GetSize());
2127 }
2128
2129 // The maximum size is the greatest of all maximum widths for all paragraphs.
2130 {
2131 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
2132 contentRect = wxRect(wxPoint(0, 0), wxSize(maxMaxWidth, maxHeight)); // Actually max height is a lie, we can't know it
8db2e3ef 2133 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
2134 SetMaxSize(marginRect.GetSize());
2135 }
2136
2137 // The minimum size is the greatest of all minimum widths for all paragraphs.
2138 {
2139 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
2140 contentRect = wxRect(wxPoint(0, 0), wxSize(maxMinWidth, maxHeight)); // Actually max height is a lie, we can't know it
8db2e3ef 2141 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
2142 SetMinSize(marginRect.GetSize());
2143 }
2144
8db2e3ef
JS
2145 if (attr.GetTextBoxAttr().HasVerticalAlignment() &&
2146 (attr.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP))
603f702b
JS
2147 {
2148 int yOffset = 0;
2149 int leftOverSpace = availableSpace.height - topMargin - bottomMargin - maxHeight;
2150 if (leftOverSpace > 0)
2151 {
8db2e3ef 2152 if (attr.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_CENTRE)
603f702b
JS
2153 {
2154 yOffset = (leftOverSpace/2);
2155 }
8db2e3ef 2156 else if (attr.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_BOTTOM)
603f702b
JS
2157 {
2158 yOffset = leftOverSpace;
2159 }
2160 }
7fe8059f 2161
603f702b
JS
2162 // Move all the children to vertically align the content
2163 // This doesn't take into account floating objects, unfortunately.
2164 if (yOffset != 0)
2165 {
2166 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2c375f42
JS
2167 while (node)
2168 {
2169 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2170 if (child)
603f702b 2171 child->Move(wxPoint(child->GetPosition().x, child->GetPosition().y + yOffset));
7fe8059f
WS
2172
2173 node = node->GetNext();
2c375f42 2174 }
2c375f42 2175 }
5d7836c4
JS
2176 }
2177
1e967276 2178 m_invalidRange = wxRICHTEXT_NONE;
5d7836c4
JS
2179
2180 return true;
2181}
2182
5d7836c4 2183/// Get/set the size for the given range.
914a4e23 2184bool wxRichTextParagraphLayoutBox::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, const wxPoint& position, const wxSize& parentSize, wxArrayInt* WXUNUSED(partialExtents)) const
5d7836c4
JS
2185{
2186 wxSize sz;
2187
09f14108
JS
2188 wxRichTextObjectList::compatibility_iterator startPara = wxRichTextObjectList::compatibility_iterator();
2189 wxRichTextObjectList::compatibility_iterator endPara = wxRichTextObjectList::compatibility_iterator();
5d7836c4
JS
2190
2191 // First find the first paragraph whose starting position is within the range.
2192 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2193 while (node)
2194 {
2195 // child is a paragraph
2196 wxRichTextObject* child = node->GetData();
2197 const wxRichTextRange& r = child->GetRange();
2198
2199 if (r.GetStart() <= range.GetStart() && r.GetEnd() >= range.GetStart())
2200 {
2201 startPara = node;
2202 break;
2203 }
2204
2205 node = node->GetNext();
2206 }
2207
2208 // Next find the last paragraph containing part of the range
2209 node = m_children.GetFirst();
2210 while (node)
2211 {
2212 // child is a paragraph
2213 wxRichTextObject* child = node->GetData();
2214 const wxRichTextRange& r = child->GetRange();
2215
2216 if (r.GetStart() <= range.GetEnd() && r.GetEnd() >= range.GetEnd())
2217 {
2218 endPara = node;
2219 break;
2220 }
2221
2222 node = node->GetNext();
2223 }
2224
2225 if (!startPara || !endPara)
2226 return false;
2227
2228 // Now we can add up the sizes
2229 for (node = startPara; node ; node = node->GetNext())
2230 {
2231 // child is a paragraph
2232 wxRichTextObject* child = node->GetData();
2233 const wxRichTextRange& childRange = child->GetRange();
2234 wxRichTextRange rangeToFind = range;
2235 rangeToFind.LimitTo(childRange);
2236
603f702b
JS
2237 if (child->IsTopLevel())
2238 rangeToFind = child->GetOwnRange();
2239
5d7836c4
JS
2240 wxSize childSize;
2241
2242 int childDescent = 0;
914a4e23 2243 child->GetRangeSize(rangeToFind, childSize, childDescent, dc, context, flags, position, parentSize);
5d7836c4
JS
2244
2245 descent = wxMax(childDescent, descent);
2246
2247 sz.x = wxMax(sz.x, childSize.x);
2248 sz.y += childSize.y;
2249
2250 if (node == endPara)
2251 break;
2252 }
2253
2254 size = sz;
2255
2256 return true;
2257}
2258
2259/// Get the paragraph at the given position
2260wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphAtPosition(long pos, bool caretPosition) const
2261{
2262 if (caretPosition)
2263 pos ++;
2264
2265 // First find the first paragraph whose starting position is within the range.
2266 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2267 while (node)
2268 {
2269 // child is a paragraph
2270 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 2271 // wxASSERT (child != NULL);
5d7836c4 2272
603f702b
JS
2273 if (child)
2274 {
2275 // Return first child in buffer if position is -1
2276 // if (pos == -1)
2277 // return child;
5d7836c4 2278
603f702b
JS
2279 if (child->GetRange().Contains(pos))
2280 return child;
2281 }
5d7836c4
JS
2282
2283 node = node->GetNext();
2284 }
2285 return NULL;
2286}
2287
2288/// Get the line at the given position
2289wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineAtPosition(long pos, bool caretPosition) const
2290{
2291 if (caretPosition)
2292 pos ++;
2293
2294 // First find the first paragraph whose starting position is within the range.
2295 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2296 while (node)
2297 {
7051fa41
JS
2298 wxRichTextObject* obj = (wxRichTextObject*) node->GetData();
2299 if (obj->GetRange().Contains(pos))
5d7836c4 2300 {
7051fa41
JS
2301 // child is a paragraph
2302 wxRichTextParagraph* child = wxDynamicCast(obj, wxRichTextParagraph);
603f702b 2303 // wxASSERT (child != NULL);
7051fa41 2304
603f702b 2305 if (child)
7051fa41 2306 {
603f702b
JS
2307 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2308 while (node2)
2309 {
2310 wxRichTextLine* line = node2->GetData();
5d7836c4 2311
603f702b 2312 wxRichTextRange range = line->GetAbsoluteRange();
1e967276 2313
603f702b 2314 if (range.Contains(pos) ||
5d7836c4 2315
603f702b
JS
2316 // If the position is end-of-paragraph, then return the last line of
2317 // of the paragraph.
2318 ((range.GetEnd() == child->GetRange().GetEnd()-1) && (pos == child->GetRange().GetEnd())))
2319 return line;
5d7836c4 2320
603f702b
JS
2321 node2 = node2->GetNext();
2322 }
7051fa41 2323 }
7fe8059f 2324 }
5d7836c4
JS
2325
2326 node = node->GetNext();
2327 }
2328
2329 int lineCount = GetLineCount();
2330 if (lineCount > 0)
2331 return GetLineForVisibleLineNumber(lineCount-1);
2332 else
2333 return NULL;
2334}
2335
2336/// Get the line at the given y pixel position, or the last line.
2337wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineAtYPosition(int y) const
2338{
2339 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2340 while (node)
2341 {
2342 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 2343 // wxASSERT (child != NULL);
5d7836c4 2344
603f702b 2345 if (child)
5d7836c4 2346 {
603f702b
JS
2347 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2348 while (node2)
2349 {
2350 wxRichTextLine* line = node2->GetData();
5d7836c4 2351
603f702b 2352 wxRect rect(line->GetRect());
5d7836c4 2353
603f702b
JS
2354 if (y <= rect.GetBottom())
2355 return line;
5d7836c4 2356
603f702b
JS
2357 node2 = node2->GetNext();
2358 }
7fe8059f 2359 }
5d7836c4
JS
2360
2361 node = node->GetNext();
2362 }
2363
2364 // Return last line
2365 int lineCount = GetLineCount();
2366 if (lineCount > 0)
2367 return GetLineForVisibleLineNumber(lineCount-1);
2368 else
2369 return NULL;
2370}
2371
2372/// Get the number of visible lines
2373int wxRichTextParagraphLayoutBox::GetLineCount() const
2374{
2375 int count = 0;
2376
2377 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2378 while (node)
2379 {
2380 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b
JS
2381 // wxASSERT (child != NULL);
2382
2383 if (child)
2384 count += child->GetLines().GetCount();
5d7836c4 2385
5d7836c4
JS
2386 node = node->GetNext();
2387 }
2388 return count;
2389}
2390
2391
2392/// Get the paragraph for a given line
2393wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphForLine(wxRichTextLine* line) const
2394{
1e967276 2395 return GetParagraphAtPosition(line->GetAbsoluteRange().GetStart());
5d7836c4
JS
2396}
2397
2398/// Get the line size at the given position
2399wxSize wxRichTextParagraphLayoutBox::GetLineSizeAtPosition(long pos, bool caretPosition) const
2400{
2401 wxRichTextLine* line = GetLineAtPosition(pos, caretPosition);
2402 if (line)
2403 {
2404 return line->GetSize();
2405 }
2406 else
2407 return wxSize(0, 0);
2408}
2409
2410
2411/// Convenience function to add a paragraph of text
24777478 2412wxRichTextRange wxRichTextParagraphLayoutBox::AddParagraph(const wxString& text, wxRichTextAttr* paraStyle)
5d7836c4 2413{
fe5aa22c 2414 // Don't use the base style, just the default style, and the base style will
4f32b3cf
JS
2415 // be combined at display time.
2416 // Divide into paragraph and character styles.
3e541562 2417
24777478
JS
2418 wxRichTextAttr defaultCharStyle;
2419 wxRichTextAttr defaultParaStyle;
4f32b3cf 2420
5607c890
JS
2421 // If the default style is a named paragraph style, don't apply any character formatting
2422 // to the initial text string.
2423 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2424 {
2425 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2426 if (def)
2427 defaultParaStyle = def->GetStyleMergedWithBase(GetStyleSheet());
2428 }
2429 else
2430 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle, defaultCharStyle);
2431
24777478
JS
2432 wxRichTextAttr* pStyle = paraStyle ? paraStyle : (wxRichTextAttr*) & defaultParaStyle;
2433 wxRichTextAttr* cStyle = & defaultCharStyle;
4f32b3cf
JS
2434
2435 wxRichTextParagraph* para = new wxRichTextParagraph(text, this, pStyle, cStyle);
32423dd8 2436 para->GetAttributes().GetTextBoxAttr().Reset();
5d7836c4
JS
2437
2438 AppendChild(para);
2439
2440 UpdateRanges();
5d7836c4
JS
2441
2442 return para->GetRange();
2443}
2444
2445/// Adds multiple paragraphs, based on newlines.
24777478 2446wxRichTextRange wxRichTextParagraphLayoutBox::AddParagraphs(const wxString& text, wxRichTextAttr* paraStyle)
5d7836c4 2447{
fe5aa22c 2448 // Don't use the base style, just the default style, and the base style will
4f32b3cf
JS
2449 // be combined at display time.
2450 // Divide into paragraph and character styles.
3e541562 2451
24777478
JS
2452 wxRichTextAttr defaultCharStyle;
2453 wxRichTextAttr defaultParaStyle;
5607c890
JS
2454
2455 // If the default style is a named paragraph style, don't apply any character formatting
2456 // to the initial text string.
2457 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2458 {
2459 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2460 if (def)
2461 defaultParaStyle = def->GetStyleMergedWithBase(GetStyleSheet());
2462 }
2463 else
2464 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle, defaultCharStyle);
5d7836c4 2465
24777478
JS
2466 wxRichTextAttr* pStyle = paraStyle ? paraStyle : (wxRichTextAttr*) & defaultParaStyle;
2467 wxRichTextAttr* cStyle = & defaultCharStyle;
4f32b3cf 2468
5d7836c4
JS
2469 wxRichTextParagraph* firstPara = NULL;
2470 wxRichTextParagraph* lastPara = NULL;
2471
2472 wxRichTextRange range(-1, -1);
0ca07313 2473
5d7836c4 2474 size_t i = 0;
28f92d74 2475 size_t len = text.length();
5d7836c4 2476 wxString line;
4f32b3cf 2477 wxRichTextParagraph* para = new wxRichTextParagraph(wxEmptyString, this, pStyle, cStyle);
32423dd8 2478 para->GetAttributes().GetTextBoxAttr().Reset();
0ca07313
JS
2479
2480 AppendChild(para);
2481
2482 firstPara = para;
2483 lastPara = para;
2484
5d7836c4
JS
2485 while (i < len)
2486 {
2487 wxChar ch = text[i];
2488 if (ch == wxT('\n') || ch == wxT('\r'))
2489 {
99404ab0
JS
2490 if (i != (len-1))
2491 {
2492 wxRichTextPlainText* plainText = (wxRichTextPlainText*) para->GetChildren().GetFirst()->GetData();
2493 plainText->SetText(line);
0ca07313 2494
99404ab0 2495 para = new wxRichTextParagraph(wxEmptyString, this, pStyle, cStyle);
32423dd8 2496 para->GetAttributes().GetTextBoxAttr().Reset();
5d7836c4 2497
99404ab0 2498 AppendChild(para);
0ca07313 2499
99404ab0
JS
2500 lastPara = para;
2501 line = wxEmptyString;
2502 }
5d7836c4
JS
2503 }
2504 else
2505 line += ch;
2506
2507 i ++;
2508 }
0ca07313 2509
7fe8059f 2510 if (!line.empty())
5d7836c4 2511 {
0ca07313
JS
2512 wxRichTextPlainText* plainText = (wxRichTextPlainText*) para->GetChildren().GetFirst()->GetData();
2513 plainText->SetText(line);
5d7836c4
JS
2514 }
2515
5d7836c4 2516 UpdateRanges();
0ca07313 2517
0ca07313 2518 return wxRichTextRange(firstPara->GetRange().GetStart(), lastPara->GetRange().GetEnd());
5d7836c4
JS
2519}
2520
2521/// Convenience function to add an image
24777478 2522wxRichTextRange wxRichTextParagraphLayoutBox::AddImage(const wxImage& image, wxRichTextAttr* paraStyle)
5d7836c4 2523{
fe5aa22c 2524 // Don't use the base style, just the default style, and the base style will
4f32b3cf
JS
2525 // be combined at display time.
2526 // Divide into paragraph and character styles.
3e541562 2527
24777478
JS
2528 wxRichTextAttr defaultCharStyle;
2529 wxRichTextAttr defaultParaStyle;
5607c890
JS
2530
2531 // If the default style is a named paragraph style, don't apply any character formatting
2532 // to the initial text string.
2533 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2534 {
2535 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2536 if (def)
2537 defaultParaStyle = def->GetStyleMergedWithBase(GetStyleSheet());
2538 }
2539 else
2540 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle, defaultCharStyle);
5d7836c4 2541
24777478
JS
2542 wxRichTextAttr* pStyle = paraStyle ? paraStyle : (wxRichTextAttr*) & defaultParaStyle;
2543 wxRichTextAttr* cStyle = & defaultCharStyle;
5d7836c4 2544
4f32b3cf 2545 wxRichTextParagraph* para = new wxRichTextParagraph(this, pStyle);
32423dd8 2546 para->GetAttributes().GetTextBoxAttr().Reset();
4f32b3cf
JS
2547 AppendChild(para);
2548 para->AppendChild(new wxRichTextImage(image, this, cStyle));
fe5aa22c 2549
5d7836c4 2550 UpdateRanges();
5d7836c4
JS
2551
2552 return para->GetRange();
2553}
2554
2555
2556/// Insert fragment into this box at the given position. If partialParagraph is true,
2557/// it is assumed that the last (or only) paragraph is just a piece of data with no paragraph
2558/// marker.
5d7836c4 2559
0ca07313 2560bool wxRichTextParagraphLayoutBox::InsertFragment(long position, wxRichTextParagraphLayoutBox& fragment)
5d7836c4 2561{
5d7836c4
JS
2562 // First, find the first paragraph whose starting position is within the range.
2563 wxRichTextParagraph* para = GetParagraphAtPosition(position);
2564 if (para)
2565 {
24777478 2566 wxRichTextAttr originalAttr = para->GetAttributes();
99404ab0 2567
5d7836c4
JS
2568 wxRichTextObjectList::compatibility_iterator node = m_children.Find(para);
2569
2570 // Now split at this position, returning the object to insert the new
2571 // ones in front of.
2572 wxRichTextObject* nextObject = para->SplitAt(position);
2573
2574 // Special case: partial paragraph, just one paragraph. Might be a small amount of
2575 // text, for example, so let's optimize.
2576
2577 if (fragment.GetPartialParagraph() && fragment.GetChildren().GetCount() == 1)
2578 {
2579 // Add the first para to this para...
2580 wxRichTextObjectList::compatibility_iterator firstParaNode = fragment.GetChildren().GetFirst();
2581 if (!firstParaNode)
2582 return false;
2583
2584 // Iterate through the fragment paragraph inserting the content into this paragraph.
2585 wxRichTextParagraph* firstPara = wxDynamicCast(firstParaNode->GetData(), wxRichTextParagraph);
2586 wxASSERT (firstPara != NULL);
2587
2588 wxRichTextObjectList::compatibility_iterator objectNode = firstPara->GetChildren().GetFirst();
2589 while (objectNode)
2590 {
2591 wxRichTextObject* newObj = objectNode->GetData()->Clone();
7fe8059f 2592
5d7836c4
JS
2593 if (!nextObject)
2594 {
2595 // Append
2596 para->AppendChild(newObj);
2597 }
2598 else
2599 {
2600 // Insert before nextObject
2601 para->InsertChild(newObj, nextObject);
2602 }
7fe8059f 2603
5d7836c4
JS
2604 objectNode = objectNode->GetNext();
2605 }
2606
2607 return true;
2608 }
2609 else
2610 {
2611 // Procedure for inserting a fragment consisting of a number of
2612 // paragraphs:
2613 //
2614 // 1. Remove and save the content that's after the insertion point, for adding
2615 // back once we've added the fragment.
2616 // 2. Add the content from the first fragment paragraph to the current
2617 // paragraph.
2618 // 3. Add remaining fragment paragraphs after the current paragraph.
2619 // 4. Add back the saved content from the first paragraph. If partialParagraph
2620 // is true, add it to the last paragraph added and not a new one.
2621
2622 // 1. Remove and save objects after split point.
2623 wxList savedObjects;
2624 if (nextObject)
2625 para->MoveToList(nextObject, savedObjects);
2626
2627 // 2. Add the content from the 1st fragment paragraph.
2628 wxRichTextObjectList::compatibility_iterator firstParaNode = fragment.GetChildren().GetFirst();
2629 if (!firstParaNode)
2630 return false;
2631
2632 wxRichTextParagraph* firstPara = wxDynamicCast(firstParaNode->GetData(), wxRichTextParagraph);
2633 wxASSERT(firstPara != NULL);
2634
6c0ea513
JS
2635 if (!(fragment.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE))
2636 para->SetAttributes(firstPara->GetAttributes());
99404ab0
JS
2637
2638 // Save empty paragraph attributes for appending later
2639 // These are character attributes deliberately set for a new paragraph. Without this,
2640 // we couldn't pass default attributes when appending a new paragraph.
24777478 2641 wxRichTextAttr emptyParagraphAttributes;
99404ab0 2642
5d7836c4 2643 wxRichTextObjectList::compatibility_iterator objectNode = firstPara->GetChildren().GetFirst();
99404ab0
JS
2644
2645 if (objectNode && firstPara->GetChildren().GetCount() == 1 && objectNode->GetData()->IsEmpty())
2646 emptyParagraphAttributes = objectNode->GetData()->GetAttributes();
2647
5d7836c4
JS
2648 while (objectNode)
2649 {
c025e094 2650 wxRichTextObject* newObj = objectNode->GetData()->Clone();
7fe8059f 2651
c025e094
JS
2652 // Append
2653 para->AppendChild(newObj);
7fe8059f 2654
5d7836c4
JS
2655 objectNode = objectNode->GetNext();
2656 }
2657
2658 // 3. Add remaining fragment paragraphs after the current paragraph.
2659 wxRichTextObjectList::compatibility_iterator nextParagraphNode = node->GetNext();
2660 wxRichTextObject* nextParagraph = NULL;
2661 if (nextParagraphNode)
2662 nextParagraph = nextParagraphNode->GetData();
2663
2664 wxRichTextObjectList::compatibility_iterator i = fragment.GetChildren().GetFirst()->GetNext();
2665 wxRichTextParagraph* finalPara = para;
2666
99404ab0
JS
2667 bool needExtraPara = (!i || !fragment.GetPartialParagraph());
2668
5d7836c4 2669 // If there was only one paragraph, we need to insert a new one.
99404ab0 2670 while (i)
5d7836c4 2671 {
99404ab0
JS
2672 wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
2673 wxASSERT( para != NULL );
5d7836c4 2674
99404ab0 2675 finalPara = (wxRichTextParagraph*) para->Clone();
5d7836c4
JS
2676
2677 if (nextParagraph)
2678 InsertChild(finalPara, nextParagraph);
2679 else
7fe8059f 2680 AppendChild(finalPara);
99404ab0
JS
2681
2682 i = i->GetNext();
5d7836c4 2683 }
5d7836c4 2684
99404ab0
JS
2685 // If there was only one paragraph, or we have full paragraphs in our fragment,
2686 // we need to insert a new one.
2687 if (needExtraPara)
2688 {
2689 finalPara = new wxRichTextParagraph;
5d7836c4
JS
2690
2691 if (nextParagraph)
2692 InsertChild(finalPara, nextParagraph);
2693 else
2694 AppendChild(finalPara);
5d7836c4
JS
2695 }
2696
2697 // 4. Add back the remaining content.
2698 if (finalPara)
2699 {
c025e094
JS
2700 if (nextObject)
2701 finalPara->MoveFromList(savedObjects);
5d7836c4
JS
2702
2703 // Ensure there's at least one object
2704 if (finalPara->GetChildCount() == 0)
2705 {
7fe8059f 2706 wxRichTextPlainText* text = new wxRichTextPlainText(wxEmptyString);
99404ab0 2707 text->SetAttributes(emptyParagraphAttributes);
5d7836c4
JS
2708
2709 finalPara->AppendChild(text);
2710 }
2711 }
2712
6c0ea513
JS
2713 if ((fragment.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE) && firstPara)
2714 finalPara->SetAttributes(firstPara->GetAttributes());
2715 else if (finalPara && finalPara != para)
99404ab0
JS
2716 finalPara->SetAttributes(originalAttr);
2717
5d7836c4
JS
2718 return true;
2719 }
2720 }
2721 else
2722 {
2723 // Append
2724 wxRichTextObjectList::compatibility_iterator i = fragment.GetChildren().GetFirst();
2725 while (i)
2726 {
2727 wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
2728 wxASSERT( para != NULL );
7fe8059f 2729
5d7836c4 2730 AppendChild(para->Clone());
7fe8059f 2731
5d7836c4
JS
2732 i = i->GetNext();
2733 }
2734
2735 return true;
2736 }
5d7836c4
JS
2737}
2738
2739/// Make a copy of the fragment corresponding to the given range, putting it in 'fragment'.
2740/// If there was an incomplete paragraph at the end, partialParagraph is set to true.
0ca07313 2741bool wxRichTextParagraphLayoutBox::CopyFragment(const wxRichTextRange& range, wxRichTextParagraphLayoutBox& fragment)
5d7836c4
JS
2742{
2743 wxRichTextObjectList::compatibility_iterator i = GetChildren().GetFirst();
2744 while (i)
2745 {
2746 wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
2747 wxASSERT( para != NULL );
2748
2749 if (!para->GetRange().IsOutside(range))
2750 {
2751 fragment.AppendChild(para->Clone());
7fe8059f 2752 }
5d7836c4
JS
2753 i = i->GetNext();
2754 }
2755
2756 // Now top and tail the first and last paragraphs in our new fragment (which might be the same).
2757 if (!fragment.IsEmpty())
2758 {
5d7836c4
JS
2759 wxRichTextParagraph* firstPara = wxDynamicCast(fragment.GetChildren().GetFirst()->GetData(), wxRichTextParagraph);
2760 wxASSERT( firstPara != NULL );
2761
0e190fa2
JS
2762 wxRichTextParagraph* lastPara = wxDynamicCast(fragment.GetChildren().GetLast()->GetData(), wxRichTextParagraph);
2763 wxASSERT( lastPara != NULL );
2764
2765 if (!firstPara || !lastPara)
2766 return false;
2767
2768 bool isFragment = (range.GetEnd() < lastPara->GetRange().GetEnd());
2769
2770 long firstPos = firstPara->GetRange().GetStart();
2771
2772 // Adjust for renumbering from zero
2773 wxRichTextRange topTailRange(range.GetStart() - firstPos, range.GetEnd() - firstPos);
2774
2775 long end;
2776 fragment.CalculateRange(0, end);
2777
5d7836c4 2778 // Chop off the start of the paragraph
0e190fa2 2779 if (topTailRange.GetStart() > 0)
5d7836c4 2780 {
0e190fa2 2781 wxRichTextRange r(0, topTailRange.GetStart()-1);
5d7836c4
JS
2782 firstPara->DeleteRange(r);
2783
2784 // Make sure the numbering is correct
0e190fa2 2785 fragment.CalculateRange(0, end);
5d7836c4
JS
2786
2787 // Now, we've deleted some positions, so adjust the range
2788 // accordingly.
0e190fa2
JS
2789 topTailRange.SetStart(range.GetLength());
2790 topTailRange.SetEnd(fragment.GetOwnRange().GetEnd());
2791 }
2792 else
2793 {
2794 topTailRange.SetStart(range.GetLength());
2795 topTailRange.SetEnd(fragment.GetOwnRange().GetEnd());
5d7836c4
JS
2796 }
2797
61e6149e 2798 if (topTailRange.GetStart() < lastPara->GetRange().GetEnd())
5d7836c4 2799 {
0e190fa2 2800 lastPara->DeleteRange(topTailRange);
5d7836c4
JS
2801
2802 // Make sure the numbering is correct
2803 long end;
0e190fa2 2804 fragment.CalculateRange(0, end);
5d7836c4
JS
2805
2806 // We only have part of a paragraph at the end
2807 fragment.SetPartialParagraph(true);
2808 }
2809 else
2810 {
0e190fa2
JS
2811 // We have a partial paragraph (don't save last new paragraph marker)
2812 // or complete paragraph
2813 fragment.SetPartialParagraph(isFragment);
5d7836c4
JS
2814 }
2815 }
2816
2817 return true;
2818}
2819
2820/// Given a position, get the number of the visible line (potentially many to a paragraph),
2821/// starting from zero at the start of the buffer.
2822long wxRichTextParagraphLayoutBox::GetVisibleLineNumber(long pos, bool caretPosition, bool startOfLine) const
2823{
2824 if (caretPosition)
2825 pos ++;
2826
2827 int lineCount = 0;
2828
2829 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2830 while (node)
2831 {
2832 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 2833 // wxASSERT( child != NULL );
5d7836c4 2834
603f702b 2835 if (child)
5d7836c4 2836 {
603f702b 2837 if (child->GetRange().Contains(pos))
5d7836c4 2838 {
603f702b
JS
2839 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2840 while (node2)
5d7836c4 2841 {
603f702b
JS
2842 wxRichTextLine* line = node2->GetData();
2843 wxRichTextRange lineRange = line->GetAbsoluteRange();
5d7836c4 2844
603f702b
JS
2845 if (lineRange.Contains(pos) || pos == lineRange.GetStart())
2846 {
2847 // If the caret is displayed at the end of the previous wrapped line,
2848 // we want to return the line it's _displayed_ at (not the actual line
2849 // containing the position).
2850 if (lineRange.GetStart() == pos && !startOfLine && child->GetRange().GetStart() != pos)
2851 return lineCount - 1;
2852 else
2853 return lineCount;
2854 }
7fe8059f 2855
603f702b
JS
2856 lineCount ++;
2857
2858 node2 = node2->GetNext();
2859 }
2860 // If we didn't find it in the lines, it must be
2861 // the last position of the paragraph. So return the last line.
2862 return lineCount-1;
5d7836c4 2863 }
603f702b
JS
2864 else
2865 lineCount += child->GetLines().GetCount();
5d7836c4 2866 }
5d7836c4
JS
2867
2868 node = node->GetNext();
2869 }
2870
2871 // Not found
2872 return -1;
2873}
2874
2875/// Given a line number, get the corresponding wxRichTextLine object.
2876wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineForVisibleLineNumber(long lineNumber) const
2877{
2878 int lineCount = 0;
2879
2880 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2881 while (node)
2882 {
2883 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 2884 // wxASSERT(child != NULL);
5d7836c4 2885
603f702b 2886 if (child)
5d7836c4 2887 {
603f702b 2888 if (lineNumber < (int) (child->GetLines().GetCount() + lineCount))
5d7836c4 2889 {
603f702b
JS
2890 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2891 while (node2)
2892 {
2893 wxRichTextLine* line = node2->GetData();
7fe8059f 2894
603f702b
JS
2895 if (lineCount == lineNumber)
2896 return line;
5d7836c4 2897
603f702b 2898 lineCount ++;
7fe8059f 2899
603f702b
JS
2900 node2 = node2->GetNext();
2901 }
7fe8059f 2902 }
603f702b
JS
2903 else
2904 lineCount += child->GetLines().GetCount();
5d7836c4 2905 }
5d7836c4
JS
2906
2907 node = node->GetNext();
2908 }
2909
2910 // Didn't find it
2911 return NULL;
2912}
2913
2914/// Delete range from layout.
2915bool wxRichTextParagraphLayoutBox::DeleteRange(const wxRichTextRange& range)
2916{
2917 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
7fe8059f 2918
99404ab0 2919 wxRichTextParagraph* firstPara = NULL;
5d7836c4
JS
2920 while (node)
2921 {
2922 wxRichTextParagraph* obj = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 2923 // wxASSERT (obj != NULL);
5d7836c4
JS
2924
2925 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
7fe8059f 2926
603f702b 2927 if (obj)
5d7836c4 2928 {
603f702b 2929 // Delete the range in each paragraph
99404ab0 2930
603f702b 2931 if (!obj->GetRange().IsOutside(range))
5d7836c4 2932 {
603f702b
JS
2933 // Deletes the content of this object within the given range
2934 obj->DeleteRange(range);
99404ab0 2935
603f702b
JS
2936 wxRichTextRange thisRange = obj->GetRange();
2937 wxRichTextAttr thisAttr = obj->GetAttributes();
5d7836c4 2938
603f702b
JS
2939 // If the whole paragraph is within the range to delete,
2940 // delete the whole thing.
2941 if (range.GetStart() <= thisRange.GetStart() && range.GetEnd() >= thisRange.GetEnd())
5d7836c4 2942 {
603f702b
JS
2943 // Delete the whole object
2944 RemoveChild(obj, true);
2945 obj = NULL;
99404ab0 2946 }
603f702b
JS
2947 else if (!firstPara)
2948 firstPara = obj;
5d7836c4 2949
603f702b
JS
2950 // If the range includes the paragraph end, we need to join this
2951 // and the next paragraph.
2952 if (range.GetEnd() <= thisRange.GetEnd())
6c0ea513 2953 {
603f702b
JS
2954 // We need to move the objects from the next paragraph
2955 // to this paragraph
2956
2957 wxRichTextParagraph* nextParagraph = NULL;
2958 if ((range.GetEnd() < thisRange.GetEnd()) && obj)
2959 nextParagraph = obj;
6c0ea513 2960 else
603f702b
JS
2961 {
2962 // We're ending at the end of the paragraph, so merge the _next_ paragraph.
2963 if (next)
2964 nextParagraph = wxDynamicCast(next->GetData(), wxRichTextParagraph);
2965 }
5d7836c4 2966
603f702b
JS
2967 bool applyFinalParagraphStyle = firstPara && nextParagraph && nextParagraph != firstPara;
2968
2969 wxRichTextAttr nextParaAttr;
2970 if (applyFinalParagraphStyle)
2971 {
2972 // Special case when deleting the end of a paragraph - use _this_ paragraph's style,
2973 // not the next one.
2974 if (range.GetStart() == range.GetEnd() && range.GetStart() == thisRange.GetEnd())
2975 nextParaAttr = thisAttr;
2976 else
2977 nextParaAttr = nextParagraph->GetAttributes();
2978 }
5d7836c4 2979
603f702b 2980 if (firstPara && nextParagraph && firstPara != nextParagraph)
99404ab0 2981 {
603f702b
JS
2982 // Move the objects to the previous para
2983 wxRichTextObjectList::compatibility_iterator node1 = nextParagraph->GetChildren().GetFirst();
5d7836c4 2984
603f702b
JS
2985 while (node1)
2986 {
2987 wxRichTextObject* obj1 = node1->GetData();
5d7836c4 2988
603f702b 2989 firstPara->AppendChild(obj1);
5d7836c4 2990
603f702b
JS
2991 wxRichTextObjectList::compatibility_iterator next1 = node1->GetNext();
2992 nextParagraph->GetChildren().Erase(node1);
99404ab0 2993
603f702b
JS
2994 node1 = next1;
2995 }
5d7836c4 2996
603f702b
JS
2997 // Delete the paragraph
2998 RemoveChild(nextParagraph, true);
2999 }
fa01bfdd 3000
603f702b
JS
3001 // Avoid empty paragraphs
3002 if (firstPara && firstPara->GetChildren().GetCount() == 0)
3003 {
3004 wxRichTextPlainText* text = new wxRichTextPlainText(wxEmptyString);
3005 firstPara->AppendChild(text);
3006 }
99404ab0 3007
603f702b
JS
3008 if (applyFinalParagraphStyle)
3009 firstPara->SetAttributes(nextParaAttr);
3010
3011 return true;
3012 }
5d7836c4
JS
3013 }
3014 }
7fe8059f 3015
5d7836c4
JS
3016 node = next;
3017 }
7fe8059f 3018
5d7836c4
JS
3019 return true;
3020}
3021
3022/// Get any text in this object for the given range
3023wxString wxRichTextParagraphLayoutBox::GetTextForRange(const wxRichTextRange& range) const
3024{
3025 int lineCount = 0;
3026 wxString text;
3027 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3028 while (node)
3029 {
3030 wxRichTextObject* child = node->GetData();
3031 if (!child->GetRange().IsOutside(range))
3032 {
5d7836c4
JS
3033 wxRichTextRange childRange = range;
3034 childRange.LimitTo(child->GetRange());
7fe8059f 3035
5d7836c4 3036 wxString childText = child->GetTextForRange(childRange);
7fe8059f 3037
5d7836c4
JS
3038 text += childText;
3039
1a75935d 3040 if ((childRange.GetEnd() == child->GetRange().GetEnd()) && node->GetNext())
fe5aa22c
JS
3041 text += wxT("\n");
3042
5d7836c4
JS
3043 lineCount ++;
3044 }
3045 node = node->GetNext();
3046 }
3047
3048 return text;
3049}
3050
3051/// Get all the text
3052wxString wxRichTextParagraphLayoutBox::GetText() const
3053{
c99f1b0f 3054 return GetTextForRange(GetOwnRange());
5d7836c4
JS
3055}
3056
3057/// Get the paragraph by number
3058wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphAtLine(long paragraphNumber) const
3059{
27e20452 3060 if ((size_t) paragraphNumber >= GetChildCount())
5d7836c4
JS
3061 return NULL;
3062
3063 return (wxRichTextParagraph*) GetChild((size_t) paragraphNumber);
3064}
3065
3066/// Get the length of the paragraph
3067int wxRichTextParagraphLayoutBox::GetParagraphLength(long paragraphNumber) const
3068{
3069 wxRichTextParagraph* para = GetParagraphAtLine(paragraphNumber);
3070 if (para)
3071 return para->GetRange().GetLength() - 1; // don't include newline
3072 else
3073 return 0;
3074}
3075
3076/// Get the text of the paragraph
3077wxString wxRichTextParagraphLayoutBox::GetParagraphText(long paragraphNumber) const
3078{
3079 wxRichTextParagraph* para = GetParagraphAtLine(paragraphNumber);
3080 if (para)
3081 return para->GetTextForRange(para->GetRange());
3082 else
3083 return wxEmptyString;
3084}
3085
3086/// Convert zero-based line column and paragraph number to a position.
3087long wxRichTextParagraphLayoutBox::XYToPosition(long x, long y) const
3088{
3089 wxRichTextParagraph* para = GetParagraphAtLine(y);
3090 if (para)
3091 {
3092 return para->GetRange().GetStart() + x;
3093 }
3094 else
3095 return -1;
3096}
3097
3098/// Convert zero-based position to line column and paragraph number
3099bool wxRichTextParagraphLayoutBox::PositionToXY(long pos, long* x, long* y) const
3100{
3101 wxRichTextParagraph* para = GetParagraphAtPosition(pos);
3102 if (para)
3103 {
3104 int count = 0;
3105 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3106 while (node)
3107 {
3108 wxRichTextObject* child = node->GetData();
3109 if (child == para)
3110 break;
3111 count ++;
3112 node = node->GetNext();
3113 }
3114
3115 *y = count;
3116 *x = pos - para->GetRange().GetStart();
3117
3118 return true;
3119 }
3120 else
3121 return false;
3122}
3123
3124/// Get the leaf object in a paragraph at this position.
3125/// Given a line number, get the corresponding wxRichTextLine object.
3126wxRichTextObject* wxRichTextParagraphLayoutBox::GetLeafObjectAtPosition(long position) const
3127{
3128 wxRichTextParagraph* para = GetParagraphAtPosition(position);
3129 if (para)
3130 {
3131 wxRichTextObjectList::compatibility_iterator node = para->GetChildren().GetFirst();
7fe8059f 3132
5d7836c4
JS
3133 while (node)
3134 {
3135 wxRichTextObject* child = node->GetData();
3136 if (child->GetRange().Contains(position))
3137 return child;
7fe8059f 3138
5d7836c4
JS
3139 node = node->GetNext();
3140 }
3141 if (position == para->GetRange().GetEnd() && para->GetChildCount() > 0)
3142 return para->GetChildren().GetLast()->GetData();
3143 }
3144 return NULL;
3145}
3146
3147/// Set character or paragraph text attributes: apply character styles only to immediate text nodes
24777478 3148bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange& range, const wxRichTextAttr& style, int flags)
5d7836c4
JS
3149{
3150 bool characterStyle = false;
3151 bool paragraphStyle = false;
3152
3153 if (style.IsCharacterStyle())
3154 characterStyle = true;
3155 if (style.IsParagraphStyle())
3156 paragraphStyle = true;
3157
603f702b
JS
3158 wxRichTextBuffer* buffer = GetBuffer();
3159
59509217
JS
3160 bool withUndo = ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
3161 bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
3162 bool parasOnly = ((flags & wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY) != 0);
3163 bool charactersOnly = ((flags & wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY) != 0);
523d2f14 3164 bool resetExistingStyle = ((flags & wxRICHTEXT_SETSTYLE_RESET) != 0);
aeb6ebe2 3165 bool removeStyle = ((flags & wxRICHTEXT_SETSTYLE_REMOVE) != 0);
523d2f14
JS
3166
3167 // Apply paragraph style first, if any
24777478 3168 wxRichTextAttr wholeStyle(style);
523d2f14 3169
603f702b 3170 if (!removeStyle && wholeStyle.HasParagraphStyleName() && buffer->GetStyleSheet())
523d2f14 3171 {
603f702b 3172 wxRichTextParagraphStyleDefinition* def = buffer->GetStyleSheet()->FindParagraphStyle(wholeStyle.GetParagraphStyleName());
523d2f14 3173 if (def)
603f702b 3174 wxRichTextApplyStyle(wholeStyle, def->GetStyleMergedWithBase(buffer->GetStyleSheet()));
523d2f14 3175 }
59509217
JS
3176
3177 // Limit the attributes to be set to the content to only character attributes.
24777478 3178 wxRichTextAttr characterAttributes(wholeStyle);
59509217
JS
3179 characterAttributes.SetFlags(characterAttributes.GetFlags() & (wxTEXT_ATTR_CHARACTER));
3180
603f702b 3181 if (!removeStyle && characterAttributes.HasCharacterStyleName() && buffer->GetStyleSheet())
523d2f14 3182 {
603f702b 3183 wxRichTextCharacterStyleDefinition* def = buffer->GetStyleSheet()->FindCharacterStyle(characterAttributes.GetCharacterStyleName());
523d2f14 3184 if (def)
603f702b 3185 wxRichTextApplyStyle(characterAttributes, def->GetStyleMergedWithBase(buffer->GetStyleSheet()));
523d2f14
JS
3186 }
3187
5d7836c4
JS
3188 // If we are associated with a control, make undoable; otherwise, apply immediately
3189 // to the data.
3190
603f702b 3191 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
5d7836c4
JS
3192
3193 wxRichTextAction* action = NULL;
7fe8059f 3194
5d7836c4
JS
3195 if (haveControl && withUndo)
3196 {
603f702b 3197 action = new wxRichTextAction(NULL, _("Change Style"), wxRICHTEXT_CHANGE_STYLE, buffer, this, buffer->GetRichTextCtrl());
5d7836c4 3198 action->SetRange(range);
603f702b 3199 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
5d7836c4
JS
3200 }
3201
3202 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3203 while (node)
3204 {
3205 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 3206 // wxASSERT (para != NULL);
5d7836c4
JS
3207
3208 if (para && para->GetChildCount() > 0)
3209 {
3210 // Stop searching if we're beyond the range of interest
3211 if (para->GetRange().GetStart() > range.GetEnd())
3212 break;
3213
3214 if (!para->GetRange().IsOutside(range))
3215 {
3216 // We'll be using a copy of the paragraph to make style changes,
3217 // not updating the buffer directly.
4e09ebe8 3218 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
7fe8059f 3219
5d7836c4
JS
3220 if (haveControl && withUndo)
3221 {
3222 newPara = new wxRichTextParagraph(*para);
3223 action->GetNewParagraphs().AppendChild(newPara);
3224
3225 // Also store the old ones for Undo
3226 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
3227 }
3228 else
3229 newPara = para;
41a85215 3230
a7ed48a5
JS
3231 // If we're specifying paragraphs only, then we really mean character formatting
3232 // to be included in the paragraph style
3233 if ((paragraphStyle || parasOnly) && !charactersOnly)
59509217 3234 {
aeb6ebe2
JS
3235 if (removeStyle)
3236 {
3237 // Removes the given style from the paragraph
3238 wxRichTextRemoveStyle(newPara->GetAttributes(), style);
3239 }
3240 else if (resetExistingStyle)
523d2f14
JS
3241 newPara->GetAttributes() = wholeStyle;
3242 else
59509217 3243 {
523d2f14
JS
3244 if (applyMinimal)
3245 {
3246 // Only apply attributes that will make a difference to the combined
3247 // style as seen on the display
603f702b 3248 wxRichTextAttr combinedAttr(para->GetCombinedAttributes(true));
523d2f14
JS
3249 wxRichTextApplyStyle(newPara->GetAttributes(), wholeStyle, & combinedAttr);
3250 }
3251 else
3252 wxRichTextApplyStyle(newPara->GetAttributes(), wholeStyle);
59509217 3253 }
59509217 3254 }
5d7836c4 3255
5912d19e 3256 // When applying paragraph styles dynamically, don't change the text objects' attributes
fe5aa22c
JS
3257 // since they will computed as needed. Only apply the character styling if it's _only_
3258 // character styling. This policy is subject to change and might be put under user control.
3259
59509217
JS
3260 // Hm. we might well be applying a mix of paragraph and character styles, in which
3261 // case we _do_ want to apply character styles regardless of what para styles are set.
3262 // But if we're applying a paragraph style, which has some character attributes, but
3263 // we only want the paragraphs to hold this character style, then we _don't_ want to
3264 // apply the character style. So we need to be able to choose.
3265
f1d800d9 3266 if (!parasOnly && (characterStyle|charactersOnly) && range.GetStart() != newPara->GetRange().GetEnd())
5d7836c4
JS
3267 {
3268 wxRichTextRange childRange(range);
3269 childRange.LimitTo(newPara->GetRange());
7fe8059f 3270
5d7836c4
JS
3271 // Find the starting position and if necessary split it so
3272 // we can start applying a different style.
3273 // TODO: check that the style actually changes or is different
3274 // from style outside of range
4e09ebe8
JS
3275 wxRichTextObject* firstObject wxDUMMY_INITIALIZE(NULL);
3276 wxRichTextObject* lastObject wxDUMMY_INITIALIZE(NULL);
7fe8059f 3277
5d7836c4
JS
3278 if (childRange.GetStart() == newPara->GetRange().GetStart())
3279 firstObject = newPara->GetChildren().GetFirst()->GetData();
3280 else
3281 firstObject = newPara->SplitAt(range.GetStart());
7fe8059f 3282
5d7836c4
JS
3283 // Increment by 1 because we're apply the style one _after_ the split point
3284 long splitPoint = childRange.GetEnd();
3285 if (splitPoint != newPara->GetRange().GetEnd())
3286 splitPoint ++;
7fe8059f 3287
5d7836c4 3288 // Find last object
4b3483e7 3289 if (splitPoint == newPara->GetRange().GetEnd())
5d7836c4
JS
3290 lastObject = newPara->GetChildren().GetLast()->GetData();
3291 else
3292 // lastObject is set as a side-effect of splitting. It's
3293 // returned as the object before the new object.
3294 (void) newPara->SplitAt(splitPoint, & lastObject);
7fe8059f 3295
5d7836c4
JS
3296 wxASSERT(firstObject != NULL);
3297 wxASSERT(lastObject != NULL);
7fe8059f 3298
5d7836c4
JS
3299 if (!firstObject || !lastObject)
3300 continue;
7fe8059f 3301
5d7836c4
JS
3302 wxRichTextObjectList::compatibility_iterator firstNode = newPara->GetChildren().Find(firstObject);
3303 wxRichTextObjectList::compatibility_iterator lastNode = newPara->GetChildren().Find(lastObject);
7fe8059f 3304
4c9847e1
MW
3305 wxASSERT(firstNode);
3306 wxASSERT(lastNode);
7fe8059f 3307
5d7836c4 3308 wxRichTextObjectList::compatibility_iterator node2 = firstNode;
7fe8059f 3309
5d7836c4
JS
3310 while (node2)
3311 {
3312 wxRichTextObject* child = node2->GetData();
7fe8059f 3313
aeb6ebe2
JS
3314 if (removeStyle)
3315 {
3316 // Removes the given style from the paragraph
3317 wxRichTextRemoveStyle(child->GetAttributes(), style);
3318 }
3319 else if (resetExistingStyle)
5fe7fce4
JS
3320 {
3321 // Preserve the URL as it's not really a formatting style but a property of the object
3322 wxString url;
3323 if (child->GetAttributes().HasURL() && !characterAttributes.HasURL())
3324 url = child->GetAttributes().GetURL();
3325
523d2f14 3326 child->GetAttributes() = characterAttributes;
5fe7fce4
JS
3327
3328 if (!url.IsEmpty())
3329 child->GetAttributes().SetURL(url);
3330 }
523d2f14 3331 else
59509217 3332 {
523d2f14
JS
3333 if (applyMinimal)
3334 {
3335 // Only apply attributes that will make a difference to the combined
3336 // style as seen on the display
603f702b 3337 wxRichTextAttr combinedAttr(newPara->GetCombinedAttributes(child->GetAttributes(), true));
523d2f14
JS
3338 wxRichTextApplyStyle(child->GetAttributes(), characterAttributes, & combinedAttr);
3339 }
3340 else
3341 wxRichTextApplyStyle(child->GetAttributes(), characterAttributes);
59509217 3342 }
59509217 3343
5d7836c4
JS
3344 if (node2 == lastNode)
3345 break;
7fe8059f 3346
5d7836c4
JS
3347 node2 = node2->GetNext();
3348 }
3349 }
3350 }
3351 }
3352
3353 node = node->GetNext();
3354 }
3355
3356 // Do action, or delay it until end of batch.
3357 if (haveControl && withUndo)
603f702b 3358 buffer->SubmitAction(action);
5d7836c4
JS
3359
3360 return true;
3361}
3362
603f702b
JS
3363// Just change the attributes for this single object.
3364void wxRichTextParagraphLayoutBox::SetStyle(wxRichTextObject* obj, const wxRichTextAttr& textAttr, int flags)
cdaed652 3365{
603f702b 3366 wxRichTextBuffer* buffer = GetBuffer();
cdaed652 3367 bool withUndo = flags & wxRICHTEXT_SETSTYLE_WITH_UNDO;
603f702b
JS
3368 bool resetExistingStyle = ((flags & wxRICHTEXT_SETSTYLE_RESET) != 0);
3369 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
3370
cdaed652 3371 wxRichTextAction *action = NULL;
603f702b
JS
3372 wxRichTextAttr newAttr = obj->GetAttributes();
3373 if (resetExistingStyle)
3374 newAttr = textAttr;
3375 else
3376 newAttr.Apply(textAttr);
cdaed652
VZ
3377
3378 if (haveControl && withUndo)
3379 {
603f702b
JS
3380 action = new wxRichTextAction(NULL, _("Change Object Style"), wxRICHTEXT_CHANGE_ATTRIBUTES, buffer, obj->GetContainer(), buffer->GetRichTextCtrl());
3381 action->SetRange(obj->GetRange().FromInternal());
3382 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
3383 action->MakeObject(obj);
bec80f4f 3384
603f702b 3385 action->GetAttributes() = newAttr;
cdaed652
VZ
3386 }
3387 else
603f702b 3388 obj->GetAttributes() = newAttr;
cdaed652
VZ
3389
3390 if (haveControl && withUndo)
603f702b 3391 buffer->SubmitAction(action);
cdaed652
VZ
3392}
3393
5d7836c4 3394/// Get the text attributes for this position.
24777478 3395bool wxRichTextParagraphLayoutBox::GetStyle(long position, wxRichTextAttr& style)
5d7836c4 3396{
fe5aa22c
JS
3397 return DoGetStyle(position, style, true);
3398}
e191ee87 3399
24777478 3400bool wxRichTextParagraphLayoutBox::GetUncombinedStyle(long position, wxRichTextAttr& style)
fe5aa22c
JS
3401{
3402 return DoGetStyle(position, style, false);
3403}
3404
fe5aa22c
JS
3405/// Implementation helper for GetStyle. If combineStyles is true, combine base, paragraph and
3406/// context attributes.
24777478 3407bool wxRichTextParagraphLayoutBox::DoGetStyle(long position, wxRichTextAttr& style, bool combineStyles)
5d7836c4 3408{
4e09ebe8 3409 wxRichTextObject* obj wxDUMMY_INITIALIZE(NULL);
e191ee87 3410
5d7836c4 3411 if (style.IsParagraphStyle())
fe5aa22c 3412 {
5d7836c4 3413 obj = GetParagraphAtPosition(position);
fe5aa22c
JS
3414 if (obj)
3415 {
fe5aa22c
JS
3416 if (combineStyles)
3417 {
3418 // Start with the base style
3419 style = GetAttributes();
32423dd8 3420 style.GetTextBoxAttr().Reset();
e191ee87 3421
fe5aa22c
JS
3422 // Apply the paragraph style
3423 wxRichTextApplyStyle(style, obj->GetAttributes());
3424 }
3425 else
3426 style = obj->GetAttributes();
5912d19e 3427
fe5aa22c
JS
3428 return true;
3429 }
5d7836c4
JS
3430 }
3431 else
fe5aa22c
JS
3432 {
3433 obj = GetLeafObjectAtPosition(position);
3434 if (obj)
3435 {
fe5aa22c
JS
3436 if (combineStyles)
3437 {
3438 wxRichTextParagraph* para = wxDynamicCast(obj->GetParent(), wxRichTextParagraph);
3439 style = para ? para->GetCombinedAttributes(obj->GetAttributes()) : obj->GetAttributes();
3440 }
3441 else
3442 style = obj->GetAttributes();
5912d19e 3443
fe5aa22c
JS
3444 return true;
3445 }
fe5aa22c
JS
3446 }
3447 return false;
5d7836c4
JS
3448}
3449
59509217
JS
3450static bool wxHasStyle(long flags, long style)
3451{
3452 return (flags & style) != 0;
3453}
3454
3455/// Combines 'style' with 'currentStyle' for the purpose of summarising the attributes of a range of
3456/// content.
24777478
JS
3457bool wxRichTextParagraphLayoutBox::CollectStyle(wxRichTextAttr& currentStyle, const wxRichTextAttr& style, wxRichTextAttr& clashingAttr, wxRichTextAttr& absentAttr)
3458{
3459 currentStyle.CollectCommonAttributes(style, clashingAttr, absentAttr);
3460
3461 return true;
3462}
3463
3464/// Get the combined style for a range - if any attribute is different within the range,
3465/// that attribute is not present within the flags.
3466/// *** Note that this is not recursive, and so assumes that content inside a paragraph is not itself
3467/// nested.
3468bool wxRichTextParagraphLayoutBox::GetStyleForRange(const wxRichTextRange& range, wxRichTextAttr& style)
59509217 3469{
24777478
JS
3470 style = wxRichTextAttr();
3471
c4168888 3472 wxRichTextAttr clashingAttrPara, clashingAttrChar;
24777478 3473 wxRichTextAttr absentAttrPara, absentAttrChar;
d1e5be0e 3474
24777478
JS
3475 wxRichTextObjectList::compatibility_iterator node = GetChildren().GetFirst();
3476 while (node)
59509217 3477 {
603f702b
JS
3478 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
3479 if (para && !(para->GetRange().GetStart() > range.GetEnd() || para->GetRange().GetEnd() < range.GetStart()))
59509217 3480 {
24777478 3481 if (para->GetChildren().GetCount() == 0)
59509217 3482 {
603f702b 3483 wxRichTextAttr paraStyle = para->GetCombinedAttributes(true /* use box attributes */);
59509217 3484
c4168888 3485 CollectStyle(style, paraStyle, clashingAttrPara, absentAttrPara);
59509217
JS
3486 }
3487 else
3488 {
24777478
JS
3489 wxRichTextRange paraRange(para->GetRange());
3490 paraRange.LimitTo(range);
59509217 3491
24777478
JS
3492 // First collect paragraph attributes only
3493 wxRichTextAttr paraStyle = para->GetCombinedAttributes();
3494 paraStyle.SetFlags(paraStyle.GetFlags() & wxTEXT_ATTR_PARAGRAPH);
c4168888 3495 CollectStyle(style, paraStyle, clashingAttrPara, absentAttrPara);
9c4cb611 3496
24777478
JS
3497 wxRichTextObjectList::compatibility_iterator childNode = para->GetChildren().GetFirst();
3498
3499 while (childNode)
59509217 3500 {
24777478
JS
3501 wxRichTextObject* child = childNode->GetData();
3502 if (!(child->GetRange().GetStart() > range.GetEnd() || child->GetRange().GetEnd() < range.GetStart()))
3503 {
603f702b 3504 wxRichTextAttr childStyle = para->GetCombinedAttributes(child->GetAttributes(), true /* include box attributes */);
59509217 3505
24777478
JS
3506 // Now collect character attributes only
3507 childStyle.SetFlags(childStyle.GetFlags() & wxTEXT_ATTR_CHARACTER);
59509217 3508
c4168888 3509 CollectStyle(style, childStyle, clashingAttrChar, absentAttrChar);
24777478 3510 }
59509217 3511
24777478 3512 childNode = childNode->GetNext();
59509217
JS
3513 }
3514 }
59509217 3515 }
24777478 3516 node = node->GetNext();
59509217 3517 }
24777478
JS
3518 return true;
3519}
59509217 3520
24777478
JS
3521/// Set default style
3522bool wxRichTextParagraphLayoutBox::SetDefaultStyle(const wxRichTextAttr& style)
3523{
3524 m_defaultAttributes = style;
3525 return true;
3526}
59509217 3527
24777478
JS
3528/// Test if this whole range has character attributes of the specified kind. If any
3529/// of the attributes are different within the range, the test fails. You
3530/// can use this to implement, for example, bold button updating. style must have
3531/// flags indicating which attributes are of interest.
3532bool wxRichTextParagraphLayoutBox::HasCharacterAttributes(const wxRichTextRange& range, const wxRichTextAttr& style) const
3533{
3534 int foundCount = 0;
3535 int matchingCount = 0;
59509217 3536
24777478
JS
3537 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3538 while (node)
59509217 3539 {
24777478 3540 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 3541 // wxASSERT (para != NULL);
59509217 3542
24777478 3543 if (para)
59509217 3544 {
24777478
JS
3545 // Stop searching if we're beyond the range of interest
3546 if (para->GetRange().GetStart() > range.GetEnd())
3547 return foundCount == matchingCount && foundCount != 0;
59509217 3548
24777478 3549 if (!para->GetRange().IsOutside(range))
59509217 3550 {
24777478 3551 wxRichTextObjectList::compatibility_iterator node2 = para->GetChildren().GetFirst();
59509217 3552
24777478
JS
3553 while (node2)
3554 {
3555 wxRichTextObject* child = node2->GetData();
3556 // Allow for empty string if no buffer
3557 wxRichTextRange childRange = child->GetRange();
3558 if (childRange.GetLength() == 0 && GetRange().GetLength() == 1)
3559 childRange.SetEnd(childRange.GetEnd()+1);
59509217 3560
345c78ca 3561 if (!childRange.IsOutside(range) && wxDynamicCast(child, wxRichTextPlainText))
24777478
JS
3562 {
3563 foundCount ++;
3564 wxRichTextAttr textAttr = para->GetCombinedAttributes(child->GetAttributes());
59509217 3565
32423dd8 3566 if (textAttr.EqPartial(style, false /* strong test - attributes must be valid in both objects */))
24777478
JS
3567 matchingCount ++;
3568 }
59509217 3569
24777478
JS
3570 node2 = node2->GetNext();
3571 }
59509217
JS
3572 }
3573 }
59509217 3574
24777478 3575 node = node->GetNext();
59509217
JS
3576 }
3577
24777478
JS
3578 return foundCount == matchingCount && foundCount != 0;
3579}
59509217 3580
24777478
JS
3581/// Test if this whole range has paragraph attributes of the specified kind. If any
3582/// of the attributes are different within the range, the test fails. You
3583/// can use this to implement, for example, centering button updating. style must have
3584/// flags indicating which attributes are of interest.
3585bool wxRichTextParagraphLayoutBox::HasParagraphAttributes(const wxRichTextRange& range, const wxRichTextAttr& style) const
3586{
3587 int foundCount = 0;
3588 int matchingCount = 0;
3589
3590 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3591 while (node)
38f833b1 3592 {
24777478 3593 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 3594 // wxASSERT (para != NULL);
24777478
JS
3595
3596 if (para)
38f833b1 3597 {
24777478
JS
3598 // Stop searching if we're beyond the range of interest
3599 if (para->GetRange().GetStart() > range.GetEnd())
3600 return foundCount == matchingCount && foundCount != 0;
3601
3602 if (!para->GetRange().IsOutside(range))
38f833b1 3603 {
24777478
JS
3604 wxRichTextAttr textAttr = GetAttributes();
3605 // Apply the paragraph style
3606 wxRichTextApplyStyle(textAttr, para->GetAttributes());
3607
3608 foundCount ++;
32423dd8 3609 if (textAttr.EqPartial(style, false /* strong test */))
24777478 3610 matchingCount ++;
38f833b1
JS
3611 }
3612 }
24777478
JS
3613
3614 node = node->GetNext();
38f833b1 3615 }
24777478
JS
3616 return foundCount == matchingCount && foundCount != 0;
3617}
5d7836c4 3618
cc2aecde
JS
3619void wxRichTextParagraphLayoutBox::PrepareContent(wxRichTextParagraphLayoutBox& container)
3620{
3621 wxRichTextBuffer* buffer = GetBuffer();
3622 if (buffer && buffer->GetRichTextCtrl())
3623 buffer->GetRichTextCtrl()->PrepareContent(container);
3624}
3625
590a0f8b
JS
3626/// Set character or paragraph properties
3627bool wxRichTextParagraphLayoutBox::SetProperties(const wxRichTextRange& range, const wxRichTextProperties& properties, int flags)
3628{
3629 wxRichTextBuffer* buffer = GetBuffer();
3630
3631 bool withUndo = ((flags & wxRICHTEXT_SETPROPERTIES_WITH_UNDO) != 0);
3632 bool parasOnly = ((flags & wxRICHTEXT_SETPROPERTIES_PARAGRAPHS_ONLY) != 0);
3633 bool charactersOnly = ((flags & wxRICHTEXT_SETPROPERTIES_CHARACTERS_ONLY) != 0);
3634 bool resetExistingProperties = ((flags & wxRICHTEXT_SETPROPERTIES_RESET) != 0);
3635 bool removeProperties = ((flags & wxRICHTEXT_SETPROPERTIES_REMOVE) != 0);
3636
3637 // If we are associated with a control, make undoable; otherwise, apply immediately
3638 // to the data.
3639
3640 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
3641
3642 wxRichTextAction* action = NULL;
3643
3644 if (haveControl && withUndo)
3645 {
3646 action = new wxRichTextAction(NULL, _("Change Properties"), wxRICHTEXT_CHANGE_PROPERTIES, buffer, this, buffer->GetRichTextCtrl());
3647 action->SetRange(range);
3648 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
3649 }
3650
3651 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3652 while (node)
3653 {
3654 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
3655 // wxASSERT (para != NULL);
3656
3657 if (para && para->GetChildCount() > 0)
3658 {
3659 // Stop searching if we're beyond the range of interest
3660 if (para->GetRange().GetStart() > range.GetEnd())
3661 break;
3662
3663 if (!para->GetRange().IsOutside(range))
3664 {
3665 // We'll be using a copy of the paragraph to make style changes,
3666 // not updating the buffer directly.
3667 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
3668
3669 if (haveControl && withUndo)
3670 {
3671 newPara = new wxRichTextParagraph(*para);
3672 action->GetNewParagraphs().AppendChild(newPara);
3673
3674 // Also store the old ones for Undo
3675 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
3676 }
3677 else
3678 newPara = para;
3679
3680 if (parasOnly)
3681 {
3682 if (removeProperties)
3683 {
3684 // Removes the given style from the paragraph
3685 // TODO
3686 newPara->GetProperties().RemoveProperties(properties);
3687 }
3688 else if (resetExistingProperties)
3689 newPara->GetProperties() = properties;
3690 else
3691 newPara->GetProperties().MergeProperties(properties);
3692 }
3693
3694 // When applying paragraph styles dynamically, don't change the text objects' attributes
3695 // since they will computed as needed. Only apply the character styling if it's _only_
3696 // character styling. This policy is subject to change and might be put under user control.
3697
3698 // Hm. we might well be applying a mix of paragraph and character styles, in which
3699 // case we _do_ want to apply character styles regardless of what para styles are set.
3700 // But if we're applying a paragraph style, which has some character attributes, but
3701 // we only want the paragraphs to hold this character style, then we _don't_ want to
3702 // apply the character style. So we need to be able to choose.
3703
3704 if (!parasOnly && charactersOnly && range.GetStart() != newPara->GetRange().GetEnd())
3705 {
3706 wxRichTextRange childRange(range);
3707 childRange.LimitTo(newPara->GetRange());
3708
3709 // Find the starting position and if necessary split it so
3710 // we can start applying different properties.
3711 // TODO: check that the properties actually change or are different
3712 // from properties outside of range
3713 wxRichTextObject* firstObject wxDUMMY_INITIALIZE(NULL);
3714 wxRichTextObject* lastObject wxDUMMY_INITIALIZE(NULL);
3715
3716 if (childRange.GetStart() == newPara->GetRange().GetStart())
3717 firstObject = newPara->GetChildren().GetFirst()->GetData();
3718 else
3719 firstObject = newPara->SplitAt(range.GetStart());
3720
3721 // Increment by 1 because we're apply the style one _after_ the split point
3722 long splitPoint = childRange.GetEnd();
3723 if (splitPoint != newPara->GetRange().GetEnd())
3724 splitPoint ++;
3725
3726 // Find last object
3727 if (splitPoint == newPara->GetRange().GetEnd())
3728 lastObject = newPara->GetChildren().GetLast()->GetData();
3729 else
3730 // lastObject is set as a side-effect of splitting. It's
3731 // returned as the object before the new object.
3732 (void) newPara->SplitAt(splitPoint, & lastObject);
3733
3734 wxASSERT(firstObject != NULL);
3735 wxASSERT(lastObject != NULL);
3736
3737 if (!firstObject || !lastObject)
3738 continue;
3739
3740 wxRichTextObjectList::compatibility_iterator firstNode = newPara->GetChildren().Find(firstObject);
3741 wxRichTextObjectList::compatibility_iterator lastNode = newPara->GetChildren().Find(lastObject);
3742
3743 wxASSERT(firstNode);
3744 wxASSERT(lastNode);
3745
3746 wxRichTextObjectList::compatibility_iterator node2 = firstNode;
3747
3748 while (node2)
3749 {
3750 wxRichTextObject* child = node2->GetData();
3751
3752 if (removeProperties)
3753 {
3754 // Removes the given properties from the paragraph
3755 child->GetProperties().RemoveProperties(properties);
3756 }
3757 else if (resetExistingProperties)
3758 child->GetProperties() = properties;
3759 else
3760 {
3761 child->GetProperties().MergeProperties(properties);
3762 }
3763
3764 if (node2 == lastNode)
3765 break;
3766
3767 node2 = node2->GetNext();
3768 }
3769 }
3770 }
3771 }
3772
3773 node = node->GetNext();
3774 }
3775
3776 // Do action, or delay it until end of batch.
3777 if (haveControl && withUndo)
3778 buffer->SubmitAction(action);
3779
3780 return true;
3781}
3782
5d7836c4
JS
3783void wxRichTextParagraphLayoutBox::Reset()
3784{
3785 Clear();
3786
603f702b
JS
3787 wxRichTextBuffer* buffer = GetBuffer();
3788 if (buffer && buffer->GetRichTextCtrl())
cd8ba0d9 3789 {
ce7fe42e 3790 wxRichTextEvent event(wxEVT_RICHTEXT_BUFFER_RESET, buffer->GetRichTextCtrl()->GetId());
603f702b
JS
3791 event.SetEventObject(buffer->GetRichTextCtrl());
3792 event.SetContainer(this);
cd8ba0d9
JS
3793
3794 buffer->SendEvent(event, true);
3795 }
3796
7fe8059f 3797 AddParagraph(wxEmptyString);
3e541562 3798
cc2aecde
JS
3799 PrepareContent(*this);
3800
603f702b 3801 InvalidateHierarchy(wxRICHTEXT_ALL);
5d7836c4
JS
3802}
3803
38113684
JS
3804/// Invalidate the buffer. With no argument, invalidates whole buffer.
3805void wxRichTextParagraphLayoutBox::Invalidate(const wxRichTextRange& invalidRange)
3806{
603f702b 3807 wxRichTextCompositeObject::Invalidate(invalidRange);
39a1c2f2 3808
603f702b
JS
3809 DoInvalidate(invalidRange);
3810}
3811
3812// Do the (in)validation for this object only
3813void wxRichTextParagraphLayoutBox::DoInvalidate(const wxRichTextRange& invalidRange)
3814{
1e967276 3815 if (invalidRange == wxRICHTEXT_ALL)
38113684 3816 {
1e967276 3817 m_invalidRange = wxRICHTEXT_ALL;
38113684 3818 }
1e967276 3819 // Already invalidating everything
603f702b
JS
3820 else if (m_invalidRange == wxRICHTEXT_ALL)
3821 {
3822 }
3823 else
3824 {
3825 if ((invalidRange.GetStart() < m_invalidRange.GetStart()) || m_invalidRange.GetStart() == -1)
3826 m_invalidRange.SetStart(invalidRange.GetStart());
3827 if (invalidRange.GetEnd() > m_invalidRange.GetEnd())
3828 m_invalidRange.SetEnd(invalidRange.GetEnd());
3829 }
3830}
39a1c2f2 3831
603f702b
JS
3832// Do the (in)validation both up and down the hierarchy
3833void wxRichTextParagraphLayoutBox::InvalidateHierarchy(const wxRichTextRange& invalidRange)
3834{
3835 Invalidate(invalidRange);
3836
3837 if (invalidRange != wxRICHTEXT_NONE)
3838 {
3839 // Now go up the hierarchy
3840 wxRichTextObject* thisObj = this;
3841 wxRichTextObject* p = GetParent();
3842 while (p)
3843 {
3844 wxRichTextParagraphLayoutBox* l = wxDynamicCast(p, wxRichTextParagraphLayoutBox);
3845 if (l)
3846 l->DoInvalidate(thisObj->GetRange());
3847
3848 thisObj = p;
3849 p = p->GetParent();
3850 }
3851 }
38113684
JS
3852}
3853
3854/// Get invalid range, rounding to entire paragraphs if argument is true.
3855wxRichTextRange wxRichTextParagraphLayoutBox::GetInvalidRange(bool wholeParagraphs) const
3856{
1e967276 3857 if (m_invalidRange == wxRICHTEXT_ALL || m_invalidRange == wxRICHTEXT_NONE)
38113684 3858 return m_invalidRange;
39a1c2f2 3859
38113684 3860 wxRichTextRange range = m_invalidRange;
39a1c2f2 3861
38113684
JS
3862 if (wholeParagraphs)
3863 {
3864 wxRichTextParagraph* para1 = GetParagraphAtPosition(range.GetStart());
38113684
JS
3865 if (para1)
3866 range.SetStart(para1->GetRange().GetStart());
f7667b84
JS
3867
3868 // FIXME: be more intelligent about this. Check if we have floating objects
3869 // before the end of the range. But it's not clear how we can in general
3870 // tell where it's safe to stop laying out.
3871 // Anyway, this code is central to efficiency when laying in floating mode.
3872 if (!wxRichTextBuffer::GetFloatingLayoutMode())
3873 {
3874 wxRichTextParagraph* para2 = GetParagraphAtPosition(range.GetEnd());
3875 if (para2)
3876 range.SetEnd(para2->GetRange().GetEnd());
3877 }
3878 else
3879 // Floating layout means that all children should be laid out,
3880 // because we can't tell how the whole buffer will be affected.
3881 range.SetEnd(GetOwnRange().GetEnd());
38113684
JS
3882 }
3883 return range;
3884}
3885
fe5aa22c
JS
3886/// Apply the style sheet to the buffer, for example if the styles have changed.
3887bool wxRichTextParagraphLayoutBox::ApplyStyleSheet(wxRichTextStyleSheet* styleSheet)
3888{
3889 wxASSERT(styleSheet != NULL);
3890 if (!styleSheet)
3891 return false;
3892
3893 int foundCount = 0;
3894
44580804
JS
3895 wxRichTextAttr attr(GetBasicStyle());
3896 if (GetBasicStyle().HasParagraphStyleName())
3897 {
3898 wxRichTextParagraphStyleDefinition* paraDef = styleSheet->FindParagraphStyle(GetBasicStyle().GetParagraphStyleName());
3899 if (paraDef)
3900 {
3901 attr.Apply(paraDef->GetStyleMergedWithBase(styleSheet));
3902 SetBasicStyle(attr);
3903 foundCount ++;
3904 }
3905 }
3906
3907 if (GetBasicStyle().HasCharacterStyleName())
3908 {
3909 wxRichTextCharacterStyleDefinition* charDef = styleSheet->FindCharacterStyle(GetBasicStyle().GetCharacterStyleName());
3910 if (charDef)
3911 {
3912 attr.Apply(charDef->GetStyleMergedWithBase(styleSheet));
3913 SetBasicStyle(attr);
3914 foundCount ++;
3915 }
3916 }
3917
fe5aa22c
JS
3918 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3919 while (node)
3920 {
3921 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 3922 // wxASSERT (para != NULL);
fe5aa22c
JS
3923
3924 if (para)
3925 {
38f833b1
JS
3926 // Combine paragraph and list styles. If there is a list style in the original attributes,
3927 // the current indentation overrides anything else and is used to find the item indentation.
3928 // Also, for applying paragraph styles, consider having 2 modes: (1) we merge with what we have,
3929 // thereby taking into account all user changes, (2) reset the style completely (except for indentation/list
3930 // exception as above).
3931 // Problem: when changing from one list style to another, there's a danger that the level info will get lost.
3932 // So when changing a list style interactively, could retrieve level based on current style, then
3933 // set appropriate indent and apply new style.
41a85215 3934
bbd55ff9
JS
3935 int outline = -1;
3936 int num = -1;
3937 if (para->GetAttributes().HasOutlineLevel())
3938 outline = para->GetAttributes().GetOutlineLevel();
3939 if (para->GetAttributes().HasBulletNumber())
3940 num = para->GetAttributes().GetBulletNumber();
3941
38f833b1
JS
3942 if (!para->GetAttributes().GetParagraphStyleName().IsEmpty() && !para->GetAttributes().GetListStyleName().IsEmpty())
3943 {
3944 int currentIndent = para->GetAttributes().GetLeftIndent();
3945
3946 wxRichTextParagraphStyleDefinition* paraDef = styleSheet->FindParagraphStyle(para->GetAttributes().GetParagraphStyleName());
3947 wxRichTextListStyleDefinition* listDef = styleSheet->FindListStyle(para->GetAttributes().GetListStyleName());
3948 if (paraDef && !listDef)
3949 {
336d8ae9 3950 para->GetAttributes() = paraDef->GetStyleMergedWithBase(styleSheet);
38f833b1
JS
3951 foundCount ++;
3952 }
3953 else if (listDef && !paraDef)
3954 {
3955 // Set overall style defined for the list style definition
336d8ae9 3956 para->GetAttributes() = listDef->GetStyleMergedWithBase(styleSheet);
38f833b1
JS
3957
3958 // Apply the style for this level
3959 wxRichTextApplyStyle(para->GetAttributes(), * listDef->GetLevelAttributes(listDef->FindLevelForIndent(currentIndent)));
3960 foundCount ++;
3961 }
3962 else if (listDef && paraDef)
3963 {
3964 // Combines overall list style, style for level, and paragraph style
336d8ae9 3965 para->GetAttributes() = listDef->CombineWithParagraphStyle(currentIndent, paraDef->GetStyleMergedWithBase(styleSheet));
38f833b1
JS
3966 foundCount ++;
3967 }
3968 }
3969 else if (para->GetAttributes().GetParagraphStyleName().IsEmpty() && !para->GetAttributes().GetListStyleName().IsEmpty())
3970 {
3971 int currentIndent = para->GetAttributes().GetLeftIndent();
3972
3973 wxRichTextListStyleDefinition* listDef = styleSheet->FindListStyle(para->GetAttributes().GetListStyleName());
3974
41a85215 3975 // Overall list definition style
336d8ae9 3976 para->GetAttributes() = listDef->GetStyleMergedWithBase(styleSheet);
41a85215 3977
38f833b1
JS
3978 // Style for this level
3979 wxRichTextApplyStyle(para->GetAttributes(), * listDef->GetLevelAttributes(listDef->FindLevelForIndent(currentIndent)));
3980
3981 foundCount ++;
3982 }
3983 else if (!para->GetAttributes().GetParagraphStyleName().IsEmpty() && para->GetAttributes().GetListStyleName().IsEmpty())
fe5aa22c
JS
3984 {
3985 wxRichTextParagraphStyleDefinition* def = styleSheet->FindParagraphStyle(para->GetAttributes().GetParagraphStyleName());
3986 if (def)
3987 {
336d8ae9 3988 para->GetAttributes() = def->GetStyleMergedWithBase(styleSheet);
fe5aa22c
JS
3989 foundCount ++;
3990 }
3991 }
bbd55ff9
JS
3992
3993 if (outline != -1)
3994 para->GetAttributes().SetOutlineLevel(outline);
3995 if (num != -1)
3996 para->GetAttributes().SetBulletNumber(num);
fe5aa22c
JS
3997 }
3998
3999 node = node->GetNext();
4000 }
4001 return foundCount != 0;
4002}
4003
38f833b1
JS
4004/// Set list style
4005bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
4006{
603f702b
JS
4007 wxRichTextBuffer* buffer = GetBuffer();
4008 wxRichTextStyleSheet* styleSheet = buffer->GetStyleSheet();
3e541562 4009
38f833b1
JS
4010 bool withUndo = ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
4011 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
4012 bool specifyLevel = ((flags & wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL) != 0);
4013 bool renumber = ((flags & wxRICHTEXT_SETSTYLE_RENUMBER) != 0);
41a85215 4014
38f833b1
JS
4015 // Current number, if numbering
4016 int n = startFrom;
41a85215 4017
38f833b1
JS
4018 wxASSERT (!specifyLevel || (specifyLevel && (specifiedLevel >= 0)));
4019
4020 // If we are associated with a control, make undoable; otherwise, apply immediately
4021 // to the data.
4022
603f702b 4023 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
38f833b1
JS
4024
4025 wxRichTextAction* action = NULL;
4026
4027 if (haveControl && withUndo)
4028 {
603f702b 4029 action = new wxRichTextAction(NULL, _("Change List Style"), wxRICHTEXT_CHANGE_STYLE, buffer, this, buffer->GetRichTextCtrl());
38f833b1 4030 action->SetRange(range);
603f702b 4031 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
38f833b1
JS
4032 }
4033
4034 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
4035 while (node)
4036 {
4037 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 4038 // wxASSERT (para != NULL);
38f833b1
JS
4039
4040 if (para && para->GetChildCount() > 0)
4041 {
4042 // Stop searching if we're beyond the range of interest
4043 if (para->GetRange().GetStart() > range.GetEnd())
4044 break;
4045
4046 if (!para->GetRange().IsOutside(range))
4047 {
4048 // We'll be using a copy of the paragraph to make style changes,
4049 // not updating the buffer directly.
4050 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
4051
4052 if (haveControl && withUndo)
4053 {
4054 newPara = new wxRichTextParagraph(*para);
4055 action->GetNewParagraphs().AppendChild(newPara);
4056
4057 // Also store the old ones for Undo
4058 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
4059 }
4060 else
4061 newPara = para;
41a85215 4062
38f833b1
JS
4063 if (def)
4064 {
4065 int thisIndent = newPara->GetAttributes().GetLeftIndent();
4066 int thisLevel = specifyLevel ? specifiedLevel : def->FindLevelForIndent(thisIndent);
41a85215 4067
38f833b1
JS
4068 // How is numbering going to work?
4069 // If we are renumbering, or numbering for the first time, we need to keep
4070 // track of the number for each level. But we might be simply applying a different
4071 // list style.
4072 // In Word, applying a style to several paragraphs, even if at different levels,
4073 // reverts the level back to the same one. So we could do the same here.
4074 // Renumbering will need to be done when we promote/demote a paragraph.
4075
4076 // Apply the overall list style, and item style for this level
24777478 4077 wxRichTextAttr listStyle(def->GetCombinedStyleForLevel(thisLevel, styleSheet));
38f833b1 4078 wxRichTextApplyStyle(newPara->GetAttributes(), listStyle);
41a85215 4079
d2d0adc7 4080 // Now we need to do numbering
4ce3ebd3
JS
4081 // Preserve the existing list item continuation bullet style, if any
4082 if (para->GetAttributes().HasBulletStyle() && (para->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION))
4083 newPara->GetAttributes().SetBulletStyle(newPara->GetAttributes().GetBulletStyle()|wxTEXT_ATTR_BULLET_STYLE_CONTINUATION);
4084 else
38f833b1 4085 {
4ce3ebd3
JS
4086 if (renumber)
4087 {
4088 newPara->GetAttributes().SetBulletNumber(n);
4089 }
41a85215 4090
4ce3ebd3
JS
4091 n ++;
4092 }
38f833b1
JS
4093 }
4094 else if (!newPara->GetAttributes().GetListStyleName().IsEmpty())
4095 {
4096 // if def is NULL, remove list style, applying any associated paragraph style
4097 // to restore the attributes
4098
4099 newPara->GetAttributes().SetListStyleName(wxEmptyString);
4100 newPara->GetAttributes().SetLeftIndent(0, 0);
d2d0adc7 4101 newPara->GetAttributes().SetBulletText(wxEmptyString);
c4168888 4102 newPara->GetAttributes().SetBulletStyle(0);
41a85215 4103
38f833b1 4104 // Eliminate the main list-related attributes
d2d0adc7 4105 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);
41a85215 4106
38f833b1
JS
4107 if (styleSheet && !newPara->GetAttributes().GetParagraphStyleName().IsEmpty())
4108 {
4109 wxRichTextParagraphStyleDefinition* def = styleSheet->FindParagraphStyle(newPara->GetAttributes().GetParagraphStyleName());
4110 if (def)
4111 {
336d8ae9 4112 newPara->GetAttributes() = def->GetStyleMergedWithBase(styleSheet);
38f833b1
JS
4113 }
4114 }
4115 }
4116 }
4117 }
4118
4119 node = node->GetNext();
4120 }
4121
4122 // Do action, or delay it until end of batch.
4123 if (haveControl && withUndo)
603f702b 4124 buffer->SubmitAction(action);
38f833b1
JS
4125
4126 return true;
4127}
4128
4129bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange& range, const wxString& defName, int flags, int startFrom, int specifiedLevel)
4130{
603f702b
JS
4131 wxRichTextBuffer* buffer = GetBuffer();
4132 if (buffer && buffer->GetStyleSheet())
38f833b1 4133 {
603f702b 4134 wxRichTextListStyleDefinition* def = buffer->GetStyleSheet()->FindListStyle(defName);
38f833b1
JS
4135 if (def)
4136 return SetListStyle(range, def, flags, startFrom, specifiedLevel);
4137 }
4138 return false;
4139}
4140
4141/// Clear list for given range
4142bool wxRichTextParagraphLayoutBox::ClearListStyle(const wxRichTextRange& range, int flags)
4143{
4144 return SetListStyle(range, NULL, flags);
4145}
4146
4147/// Number/renumber any list elements in the given range
4148bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
4149{
4150 return DoNumberList(range, range, 0, def, flags, startFrom, specifiedLevel);
4151}
4152
4153/// Number/renumber any list elements in the given range. Also do promotion or demotion of items, if specified
4154bool wxRichTextParagraphLayoutBox::DoNumberList(const wxRichTextRange& range, const wxRichTextRange& promotionRange, int promoteBy,
4155 wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
4156{
603f702b
JS
4157 wxRichTextBuffer* buffer = GetBuffer();
4158 wxRichTextStyleSheet* styleSheet = buffer->GetStyleSheet();
336d8ae9 4159
38f833b1
JS
4160 bool withUndo = ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
4161 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
4b6a582b 4162#if wxDEBUG_LEVEL
38f833b1 4163 bool specifyLevel = ((flags & wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL) != 0);
3c738608 4164#endif
38f833b1
JS
4165
4166 bool renumber = ((flags & wxRICHTEXT_SETSTYLE_RENUMBER) != 0);
41a85215 4167
38f833b1
JS
4168 // Max number of levels
4169 const int maxLevels = 10;
41a85215 4170
38f833b1
JS
4171 // The level we're looking at now
4172 int currentLevel = -1;
41a85215 4173
38f833b1
JS
4174 // The item number for each level
4175 int levels[maxLevels];
4176 int i;
41a85215 4177
38f833b1
JS
4178 // Reset all numbering
4179 for (i = 0; i < maxLevels; i++)
4180 {
4181 if (startFrom != -1)
d2d0adc7 4182 levels[i] = startFrom-1;
38f833b1 4183 else if (renumber) // start again
d2d0adc7 4184 levels[i] = 0;
38f833b1
JS
4185 else
4186 levels[i] = -1; // start from the number we found, if any
4187 }
41a85215 4188
bb7bbd12 4189#if wxDEBUG_LEVEL
38f833b1 4190 wxASSERT(!specifyLevel || (specifyLevel && (specifiedLevel >= 0)));
bb7bbd12 4191#endif
38f833b1
JS
4192
4193 // If we are associated with a control, make undoable; otherwise, apply immediately
4194 // to the data.
4195
603f702b 4196 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
38f833b1
JS
4197
4198 wxRichTextAction* action = NULL;
4199
4200 if (haveControl && withUndo)
4201 {
603f702b 4202 action = new wxRichTextAction(NULL, _("Renumber List"), wxRICHTEXT_CHANGE_STYLE, buffer, this, buffer->GetRichTextCtrl());
38f833b1 4203 action->SetRange(range);
603f702b 4204 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
38f833b1
JS
4205 }
4206
4207 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
4208 while (node)
4209 {
4210 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 4211 // wxASSERT (para != NULL);
38f833b1
JS
4212
4213 if (para && para->GetChildCount() > 0)
4214 {
4215 // Stop searching if we're beyond the range of interest
4216 if (para->GetRange().GetStart() > range.GetEnd())
4217 break;
4218
4219 if (!para->GetRange().IsOutside(range))
4220 {
4221 // We'll be using a copy of the paragraph to make style changes,
4222 // not updating the buffer directly.
4223 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
4224
4225 if (haveControl && withUndo)
4226 {
4227 newPara = new wxRichTextParagraph(*para);
4228 action->GetNewParagraphs().AppendChild(newPara);
4229
4230 // Also store the old ones for Undo
4231 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
4232 }
4233 else
4234 newPara = para;
41a85215 4235
38f833b1
JS
4236 wxRichTextListStyleDefinition* defToUse = def;
4237 if (!defToUse)
4238 {
336d8ae9
VZ
4239 if (styleSheet && !newPara->GetAttributes().GetListStyleName().IsEmpty())
4240 defToUse = styleSheet->FindListStyle(newPara->GetAttributes().GetListStyleName());
38f833b1 4241 }
41a85215 4242
38f833b1
JS
4243 if (defToUse)
4244 {
4245 int thisIndent = newPara->GetAttributes().GetLeftIndent();
4246 int thisLevel = defToUse->FindLevelForIndent(thisIndent);
4247
d2d0adc7
JS
4248 // If we've specified a level to apply to all, change the level.
4249 if (specifiedLevel != -1)
38f833b1 4250 thisLevel = specifiedLevel;
41a85215 4251
38f833b1
JS
4252 // Do promotion if specified
4253 if ((promoteBy != 0) && !para->GetRange().IsOutside(promotionRange))
4254 {
4255 thisLevel = thisLevel - promoteBy;
4256 if (thisLevel < 0)
4257 thisLevel = 0;
4258 if (thisLevel > 9)
4259 thisLevel = 9;
4260 }
41a85215 4261
38f833b1 4262 // Apply the overall list style, and item style for this level
24777478 4263 wxRichTextAttr listStyle(defToUse->GetCombinedStyleForLevel(thisLevel, styleSheet));
38f833b1 4264 wxRichTextApplyStyle(newPara->GetAttributes(), listStyle);
41a85215 4265
4ce3ebd3
JS
4266 // Preserve the existing list item continuation bullet style, if any
4267 if (para->GetAttributes().HasBulletStyle() && (para->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION))
4268 newPara->GetAttributes().SetBulletStyle(newPara->GetAttributes().GetBulletStyle()|wxTEXT_ATTR_BULLET_STYLE_CONTINUATION);
4269
38f833b1 4270 // OK, we've (re)applied the style, now let's get the numbering right.
41a85215 4271
38f833b1
JS
4272 if (currentLevel == -1)
4273 currentLevel = thisLevel;
41a85215 4274
38f833b1
JS
4275 // Same level as before, do nothing except increment level's number afterwards
4276 if (currentLevel == thisLevel)
4277 {
4278 }
4279 // A deeper level: start renumbering all levels after current level
4280 else if (thisLevel > currentLevel)
4281 {
4282 for (i = currentLevel+1; i <= thisLevel; i++)
4283 {
d2d0adc7 4284 levels[i] = 0;
38f833b1
JS
4285 }
4286 currentLevel = thisLevel;
4287 }
4288 else if (thisLevel < currentLevel)
4289 {
4290 currentLevel = thisLevel;
41a85215 4291 }
38f833b1
JS
4292
4293 // Use the current numbering if -1 and we have a bullet number already
4294 if (levels[currentLevel] == -1)
4295 {
4296 if (newPara->GetAttributes().HasBulletNumber())
4297 levels[currentLevel] = newPara->GetAttributes().GetBulletNumber();
4298 else
4299 levels[currentLevel] = 1;
4300 }
d2d0adc7
JS
4301 else
4302 {
4ce3ebd3
JS
4303 if (!(para->GetAttributes().HasBulletStyle() && (para->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION)))
4304 levels[currentLevel] ++;
d2d0adc7 4305 }
41a85215 4306
38f833b1
JS
4307 newPara->GetAttributes().SetBulletNumber(levels[currentLevel]);
4308
d2d0adc7
JS
4309 // Create the bullet text if an outline list
4310 if (listStyle.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE)
4311 {
4312 wxString text;
4313 for (i = 0; i <= currentLevel; i++)
4314 {
4315 if (!text.IsEmpty())
4316 text += wxT(".");
4317 text += wxString::Format(wxT("%d"), levels[i]);
4318 }
4319 newPara->GetAttributes().SetBulletText(text);
4320 }
38f833b1
JS
4321 }
4322 }
4323 }
4324
4325 node = node->GetNext();
4326 }
4327
4328 // Do action, or delay it until end of batch.
4329 if (haveControl && withUndo)
603f702b 4330 buffer->SubmitAction(action);
38f833b1
JS
4331
4332 return true;
4333}
4334
4335bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange& range, const wxString& defName, int flags, int startFrom, int specifiedLevel)
4336{
603f702b
JS
4337 wxRichTextBuffer* buffer = GetBuffer();
4338 if (buffer->GetStyleSheet())
38f833b1
JS
4339 {
4340 wxRichTextListStyleDefinition* def = NULL;
4341 if (!defName.IsEmpty())
603f702b 4342 def = buffer->GetStyleSheet()->FindListStyle(defName);
38f833b1
JS
4343 return NumberList(range, def, flags, startFrom, specifiedLevel);
4344 }
4345 return false;
4346}
4347
4348/// Promote the list items within the given range. promoteBy can be a positive or negative number, e.g. 1 or -1
4349bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy, const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int specifiedLevel)
4350{
4351 // TODO
4352 // One strategy is to first work out the range within which renumbering must occur. Then could pass these two ranges
4353 // to NumberList with a flag indicating promotion is required within one of the ranges.
4354 // Find first and last paragraphs in range. Then for first, calculate new indentation and look back until we find
4355 // a paragraph that either has no list style, or has one that is different or whose indentation is less.
4356 // We start renumbering from the para after that different para we found. We specify that the numbering of that
4357 // list position will start from 1.
4358 // Similarly, we look after the last para in the promote range for an indentation that is less (or no list style).
4359 // We can end the renumbering at this point.
41a85215 4360
38f833b1 4361 // For now, only renumber within the promotion range.
41a85215 4362
38f833b1
JS
4363 return DoNumberList(range, range, promoteBy, def, flags, 1, specifiedLevel);
4364}
4365
4366bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy, const wxRichTextRange& range, const wxString& defName, int flags, int specifiedLevel)
4367{
603f702b
JS
4368 wxRichTextBuffer* buffer = GetBuffer();
4369 if (buffer->GetStyleSheet())
38f833b1
JS
4370 {
4371 wxRichTextListStyleDefinition* def = NULL;
4372 if (!defName.IsEmpty())
603f702b 4373 def = buffer->GetStyleSheet()->FindListStyle(defName);
38f833b1
JS
4374 return PromoteList(promoteBy, range, def, flags, specifiedLevel);
4375 }
4376 return false;
4377}
4378
d2d0adc7
JS
4379/// Fills in the attributes for numbering a paragraph after previousParagraph. It also finds the
4380/// position of the paragraph that it had to start looking from.
24777478 4381bool wxRichTextParagraphLayoutBox::FindNextParagraphNumber(wxRichTextParagraph* previousParagraph, wxRichTextAttr& attr) const
d2d0adc7 4382{
c4168888 4383 // TODO: add GetNextChild/GetPreviousChild to composite
4ce3ebd3
JS
4384 // Search for a paragraph that isn't a continuation paragraph (no bullet)
4385 while (previousParagraph && previousParagraph->GetAttributes().HasBulletStyle() && previousParagraph->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION)
4386 {
4387 wxRichTextObjectList::compatibility_iterator node = ((wxRichTextCompositeObject*) previousParagraph->GetParent())->GetChildren().Find(previousParagraph);
4388 if (node)
4389 {
4390 node = node->GetPrevious();
4391 if (node)
4392 previousParagraph = wxDynamicCast(node->GetData(), wxRichTextParagraph);
4393 else
4394 previousParagraph = NULL;
4395 }
4396 else
4397 previousParagraph = NULL;
4398 }
4399
c4168888 4400 if (!previousParagraph || !previousParagraph->GetAttributes().HasFlag(wxTEXT_ATTR_BULLET_STYLE) || previousParagraph->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE)
d2d0adc7 4401 return false;
3e541562 4402
603f702b
JS
4403 wxRichTextBuffer* buffer = GetBuffer();
4404 wxRichTextStyleSheet* styleSheet = buffer->GetStyleSheet();
336d8ae9 4405 if (styleSheet && !previousParagraph->GetAttributes().GetListStyleName().IsEmpty())
d2d0adc7 4406 {
336d8ae9 4407 wxRichTextListStyleDefinition* def = styleSheet->FindListStyle(previousParagraph->GetAttributes().GetListStyleName());
d2d0adc7
JS
4408 if (def)
4409 {
4410 // int thisIndent = previousParagraph->GetAttributes().GetLeftIndent();
4411 // int thisLevel = def->FindLevelForIndent(thisIndent);
3e541562 4412
d2d0adc7
JS
4413 bool isOutline = (previousParagraph->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE) != 0;
4414
4415 attr.SetFlags(previousParagraph->GetAttributes().GetFlags() & (wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_BULLET_NUMBER|wxTEXT_ATTR_BULLET_TEXT|wxTEXT_ATTR_BULLET_NAME));
4416 if (previousParagraph->GetAttributes().HasBulletName())
4417 attr.SetBulletName(previousParagraph->GetAttributes().GetBulletName());
4418 attr.SetBulletStyle(previousParagraph->GetAttributes().GetBulletStyle());
4419 attr.SetListStyleName(previousParagraph->GetAttributes().GetListStyleName());
3e541562 4420
d2d0adc7
JS
4421 int nextNumber = previousParagraph->GetAttributes().GetBulletNumber() + 1;
4422 attr.SetBulletNumber(nextNumber);
3e541562 4423
d2d0adc7
JS
4424 if (isOutline)
4425 {
4426 wxString text = previousParagraph->GetAttributes().GetBulletText();
4427 if (!text.IsEmpty())
4428 {
4429 int pos = text.Find(wxT('.'), true);
4430 if (pos != wxNOT_FOUND)
4431 {
4432 text = text.Mid(0, text.Length() - pos - 1);
4433 }
4434 else
4435 text = wxEmptyString;
4436 if (!text.IsEmpty())
4437 text += wxT(".");
4438 text += wxString::Format(wxT("%d"), nextNumber);
4439 attr.SetBulletText(text);
4440 }
4441 }
3e541562 4442
d2d0adc7
JS
4443 return true;
4444 }
4445 else
4446 return false;
4447 }
4448 else
4449 return false;
4450}
4451
5d7836c4
JS
4452/*!
4453 * wxRichTextParagraph
4454 * This object represents a single paragraph (or in a straight text editor, a line).
4455 */
4456
603f702b 4457IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraph, wxRichTextCompositeObject)
5d7836c4 4458
cfa3b256
JS
4459wxArrayInt wxRichTextParagraph::sm_defaultTabs;
4460
24777478 4461wxRichTextParagraph::wxRichTextParagraph(wxRichTextObject* parent, wxRichTextAttr* style):
603f702b 4462 wxRichTextCompositeObject(parent)
5d7836c4 4463{
5d7836c4
JS
4464 if (style)
4465 SetAttributes(*style);
4466}
4467
24777478 4468wxRichTextParagraph::wxRichTextParagraph(const wxString& text, wxRichTextObject* parent, wxRichTextAttr* paraStyle, wxRichTextAttr* charStyle):
603f702b 4469 wxRichTextCompositeObject(parent)
5d7836c4 4470{
4f32b3cf
JS
4471 if (paraStyle)
4472 SetAttributes(*paraStyle);
5d7836c4 4473
4f32b3cf 4474 AppendChild(new wxRichTextPlainText(text, this, charStyle));
5d7836c4
JS
4475}
4476
4477wxRichTextParagraph::~wxRichTextParagraph()
4478{
4479 ClearLines();
4480}
4481
4482/// Draw the item
8db2e3ef 4483bool wxRichTextParagraph::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int WXUNUSED(descent), int style)
5d7836c4 4484{
603f702b
JS
4485 if (!IsShown())
4486 return true;
4487
4488 // Currently we don't merge these attributes with the parent, but we
4489 // should consider whether we should (e.g. if we set a border colour
4490 // for all paragraphs). But generally box attributes are likely to be
4491 // different for different objects.
4492 wxRect paraRect = GetRect();
24777478 4493 wxRichTextAttr attr = GetCombinedAttributes();
8db2e3ef
JS
4494 context.ApplyVirtualAttributes(attr, this);
4495
4496 DrawBoxAttributes(dc, GetBuffer(), attr, paraRect);
fe5aa22c 4497
5d7836c4 4498 // Draw the bullet, if any
4ce3ebd3 4499 if ((attr.GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE) == 0 && (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION) == 0)
5d7836c4 4500 {
fe5aa22c 4501 if (attr.GetLeftSubIndent() != 0)
5d7836c4 4502 {
fe5aa22c 4503 int spaceBeforePara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingBefore());
fe5aa22c 4504 int leftIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftIndent());
5d7836c4 4505
8db2e3ef 4506 wxRichTextAttr bulletAttr(attr);
d2d0adc7 4507
e3eac0ff
JS
4508 // Combine with the font of the first piece of content, if one is specified
4509 if (GetChildren().GetCount() > 0)
4510 {
4511 wxRichTextObject* firstObj = (wxRichTextObject*) GetChildren().GetFirst()->GetData();
cdaed652 4512 if (!firstObj->IsFloatable() && firstObj->GetAttributes().HasFont())
e3eac0ff
JS
4513 {
4514 wxRichTextApplyStyle(bulletAttr, firstObj->GetAttributes());
4515 }
4516 }
4517
d2d0adc7 4518 // Get line height from first line, if any
d3b9f782 4519 wxRichTextLine* line = m_cachedLines.GetFirst() ? (wxRichTextLine* ) m_cachedLines.GetFirst()->GetData() : NULL;
d2d0adc7
JS
4520
4521 wxPoint linePos;
4522 int lineHeight wxDUMMY_INITIALIZE(0);
4523 if (line)
5d7836c4 4524 {
d2d0adc7
JS
4525 lineHeight = line->GetSize().y;
4526 linePos = line->GetPosition() + GetPosition();
5d7836c4 4527 }
d2d0adc7 4528 else
f089713f 4529 {
f089713f 4530 wxFont font;
44cc96a8
JS
4531 if (bulletAttr.HasFont() && GetBuffer())
4532 font = GetBuffer()->GetFontTable().FindFont(bulletAttr);
f089713f
JS
4533 else
4534 font = (*wxNORMAL_FONT);
4535
ecb5fbf1 4536 wxCheckSetFont(dc, font);
f089713f 4537
d2d0adc7
JS
4538 lineHeight = dc.GetCharHeight();
4539 linePos = GetPosition();
4540 linePos.y += spaceBeforePara;
4541 }
f089713f 4542
d2d0adc7 4543 wxRect bulletRect(GetPosition().x + leftIndent, linePos.y, linePos.x - (GetPosition().x + leftIndent), lineHeight);
f089713f 4544
d2d0adc7
JS
4545 if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP)
4546 {
4547 if (wxRichTextBuffer::GetRenderer())
4548 wxRichTextBuffer::GetRenderer()->DrawBitmapBullet(this, dc, bulletAttr, bulletRect);
4549 }
3e541562
JS
4550 else if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_STANDARD)
4551 {
d2d0adc7
JS
4552 if (wxRichTextBuffer::GetRenderer())
4553 wxRichTextBuffer::GetRenderer()->DrawStandardBullet(this, dc, bulletAttr, bulletRect);
f089713f 4554 }
5d7836c4
JS
4555 else
4556 {
4557 wxString bulletText = GetBulletText();
3e541562 4558
d2d0adc7
JS
4559 if (!bulletText.empty() && wxRichTextBuffer::GetRenderer())
4560 wxRichTextBuffer::GetRenderer()->DrawTextBullet(this, dc, bulletAttr, bulletRect, bulletText);
5d7836c4
JS
4561 }
4562 }
4563 }
7fe8059f 4564
5d7836c4
JS
4565 // Draw the range for each line, one object at a time.
4566
4567 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
4568 while (node)
4569 {
4570 wxRichTextLine* line = node->GetData();
1e967276 4571 wxRichTextRange lineRange = line->GetAbsoluteRange();
5d7836c4 4572
5d7836c4
JS
4573 // Lines are specified relative to the paragraph
4574
4575 wxPoint linePosition = line->GetPosition() + GetPosition();
5d7836c4 4576
7051fa41
JS
4577 // Don't draw if off the screen
4578 if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) != 0) || ((linePosition.y + line->GetSize().y) >= rect.y && linePosition.y <= rect.y + rect.height))
5d7836c4 4579 {
7051fa41
JS
4580 wxPoint objectPosition = linePosition;
4581 int maxDescent = line->GetDescent();
4582
4583 // Loop through objects until we get to the one within range
4584 wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
3e541562 4585
7051fa41
JS
4586 int i = 0;
4587 while (node2)
5d7836c4 4588 {
7051fa41 4589 wxRichTextObject* child = node2->GetData();
5d7836c4 4590
e12b91a3 4591 if ((!child->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && child->GetRange().GetLength() > 0 && !child->GetRange().IsOutside(lineRange) && !lineRange.IsOutside(range))
2f45f554 4592 {
7051fa41
JS
4593 // Draw this part of the line at the correct position
4594 wxRichTextRange objectRange(child->GetRange());
4595 objectRange.LimitTo(lineRange);
4596
4597 wxSize objectSize;
603f702b 4598 if (child->IsTopLevel())
7051fa41 4599 {
603f702b
JS
4600 objectSize = child->GetCachedSize();
4601 objectRange = child->GetOwnRange();
7051fa41
JS
4602 }
4603 else
7051fa41 4604 {
603f702b
JS
4605#if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING && wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4606 if (i < (int) line->GetObjectSizes().GetCount())
4607 {
4608 objectSize.x = line->GetObjectSizes()[(size_t) i];
4609 }
4610 else
4611#endif
4612 {
4613 int descent = 0;
8db2e3ef 4614 child->GetRangeSize(objectRange, objectSize, descent, dc, context, wxRICHTEXT_UNFORMATTED, objectPosition);
603f702b 4615 }
7051fa41 4616 }
5d7836c4 4617
7051fa41
JS
4618 // Use the child object's width, but the whole line's height
4619 wxRect childRect(objectPosition, wxSize(objectSize.x, line->GetSize().y));
8db2e3ef 4620 child->Draw(dc, context, objectRange, selection, childRect, maxDescent, style);
5d7836c4 4621
7051fa41
JS
4622 objectPosition.x += objectSize.x;
4623 i ++;
4624 }
4625 else if (child->GetRange().GetStart() > lineRange.GetEnd())
4626 // Can break out of inner loop now since we've passed this line's range
4627 break;
5d7836c4 4628
7051fa41
JS
4629 node2 = node2->GetNext();
4630 }
5d7836c4
JS
4631 }
4632
4633 node = node->GetNext();
7fe8059f 4634 }
5d7836c4
JS
4635
4636 return true;
4637}
4638
4f3d5bc0
JS
4639// Get the range width using partial extents calculated for the whole paragraph.
4640static int wxRichTextGetRangeWidth(const wxRichTextParagraph& para, const wxRichTextRange& range, const wxArrayInt& partialExtents)
4641{
4642 wxASSERT(partialExtents.GetCount() >= (size_t) range.GetLength());
4643
affbfa1f
JS
4644 if (partialExtents.GetCount() < (size_t) range.GetLength())
4645 return 0;
4646
4f3d5bc0
JS
4647 int leftMostPos = 0;
4648 if (range.GetStart() - para.GetRange().GetStart() > 0)
4649 leftMostPos = partialExtents[range.GetStart() - para.GetRange().GetStart() - 1];
4650
4651 int rightMostPos = partialExtents[range.GetEnd() - para.GetRange().GetStart()];
4652
4653 int w = rightMostPos - leftMostPos;
4654
4655 return w;
4656}
4657
5d7836c4 4658/// Lay the item out
8db2e3ef 4659bool wxRichTextParagraph::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style)
5d7836c4 4660{
cdaed652
VZ
4661 // Deal with floating objects firstly before the normal layout
4662 wxRichTextBuffer* buffer = GetBuffer();
4663 wxASSERT(buffer);
e12b91a3 4664
07d4142f 4665 wxRichTextFloatCollector* collector = GetContainer()->GetFloatCollector();
e12b91a3
JS
4666
4667 if (wxRichTextBuffer::GetFloatingLayoutMode())
4668 {
4669 wxASSERT(collector != NULL);
4670 if (collector)
4671 LayoutFloat(dc, context, rect, parentRect, style, collector);
4672 }
cdaed652 4673
24777478 4674 wxRichTextAttr attr = GetCombinedAttributes();
8db2e3ef 4675 context.ApplyVirtualAttributes(attr, this);
fe5aa22c 4676
169adfa9
JS
4677 // ClearLines();
4678
5d7836c4 4679 // Increase the size of the paragraph due to spacing
fe5aa22c
JS
4680 int spaceBeforePara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingBefore());
4681 int spaceAfterPara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingAfter());
4682 int leftIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftIndent());
4683 int leftSubIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftSubIndent());
4684 int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
5d7836c4
JS
4685
4686 int lineSpacing = 0;
4687
4688 // Let's assume line spacing of 10 is normal, 15 is 1.5, 20 is 2, etc.
77120d82 4689 if (attr.HasLineSpacing() && attr.GetLineSpacing() > 0 && attr.HasFont())
5d7836c4 4690 {
77120d82
JS
4691 wxFont font(buffer->GetFontTable().FindFont(attr));
4692 if (font.IsOk())
4693 {
4694 wxCheckSetFont(dc, font);
4695 lineSpacing = (int) (double(dc.GetCharHeight()) * (double(attr.GetLineSpacing())/10.0 - 1.0));
4696 }
5d7836c4
JS
4697 }
4698
5d7836c4
JS
4699 // Start position for each line relative to the paragraph
4700 int startPositionFirstLine = leftIndent;
4701 int startPositionSubsequentLines = leftIndent + leftSubIndent;
4702
4703 // If we have a bullet in this paragraph, the start position for the first line's text
4704 // is actually leftIndent + leftSubIndent.
fe5aa22c 4705 if (attr.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE)
5d7836c4
JS
4706 startPositionFirstLine = startPositionSubsequentLines;
4707
5d7836c4
JS
4708 long lastEndPos = GetRange().GetStart()-1;
4709 long lastCompletedEndPos = lastEndPos;
4710
4711 int currentWidth = 0;
4712 SetPosition(rect.GetPosition());
4713
4714 wxPoint currentPosition(0, spaceBeforePara); // We will calculate lines relative to paragraph
4715 int lineHeight = 0;
4716 int maxWidth = 0;
603f702b 4717 int maxHeight = currentPosition.y;
476a319a 4718 int maxAscent = 0;
5d7836c4 4719 int maxDescent = 0;
5d7836c4 4720 int lineCount = 0;
cdaed652
VZ
4721 int lineAscent = 0;
4722 int lineDescent = 0;
5d7836c4 4723
2f45f554
JS
4724 wxRichTextObjectList::compatibility_iterator node;
4725
4726#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4727 wxUnusedVar(style);
4728 wxArrayInt partialExtents;
4729
4730 wxSize paraSize;
8aab23a1 4731 int paraDescent = 0;
2f45f554
JS
4732
4733 // This calculates the partial text extents
914a4e23 4734 GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, rect.GetPosition(), parentRect.GetSize(), & partialExtents);
2f45f554
JS
4735#else
4736 node = m_children.GetFirst();
ecb5fbf1
JS
4737 while (node)
4738 {
4739 wxRichTextObject* child = node->GetData();
4740
603f702b 4741 //child->SetCachedSize(wxDefaultSize);
8db2e3ef 4742 child->Layout(dc, context, rect, style);
ecb5fbf1
JS
4743
4744 node = node->GetNext();
4745 }
31778480
JS
4746#endif
4747
5d7836c4
JS
4748 // Split up lines
4749
4750 // We may need to go back to a previous child, in which case create the new line,
4751 // find the child corresponding to the start position of the string, and
4752 // continue.
4753
603f702b
JS
4754 wxRect availableRect;
4755
ecb5fbf1 4756 node = m_children.GetFirst();
5d7836c4
JS
4757 while (node)
4758 {
4759 wxRichTextObject* child = node->GetData();
4760
cdaed652 4761 // If floating, ignore. We already laid out floats.
603f702b
JS
4762 // Also ignore if empty object, except if we haven't got any
4763 // size yet.
e12b91a3
JS
4764 if ((child->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode())
4765 || !child->IsShown() || (child->GetRange().GetLength() == 0 && maxHeight > spaceBeforePara)
603f702b 4766 )
affbfa1f
JS
4767 {
4768 node = node->GetNext();
4769 continue;
4770 }
4771
5d7836c4
JS
4772 // If this is e.g. a composite text box, it will need to be laid out itself.
4773 // But if just a text fragment or image, for example, this will
4774 // do nothing. NB: won't we need to set the position after layout?
4775 // since for example if position is dependent on vertical line size, we
4776 // can't tell the position until the size is determined. So possibly introduce
4777 // another layout phase.
4778
5d7836c4
JS
4779 // We may only be looking at part of a child, if we searched back for wrapping
4780 // and found a suitable point some way into the child. So get the size for the fragment
4781 // if necessary.
3e541562 4782
ff76711f
JS
4783 long nextBreakPos = GetFirstLineBreakPosition(lastEndPos+1);
4784 long lastPosToUse = child->GetRange().GetEnd();
4785 bool lineBreakInThisObject = (nextBreakPos > -1 && nextBreakPos <= child->GetRange().GetEnd());
3e541562 4786
ff76711f
JS
4787 if (lineBreakInThisObject)
4788 lastPosToUse = nextBreakPos;
5d7836c4
JS
4789
4790 wxSize childSize;
4791 int childDescent = 0;
3e541562 4792
603f702b
JS
4793 int startOffset = (lineCount == 0 ? startPositionFirstLine : startPositionSubsequentLines);
4794 availableRect = wxRect(rect.x + startOffset, rect.y + currentPosition.y,
4795 rect.width - startOffset - rightIndent, rect.height);
4796
4797 if (child->IsTopLevel())
4798 {
4799 wxSize oldSize = child->GetCachedSize();
4800
4801 child->Invalidate(wxRICHTEXT_ALL);
4802 child->SetPosition(wxPoint(0, 0));
4803
4804 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4805 // lays out the object again using the minimum size
4806 // The position will be determined by its location in its line,
4807 // and not by the child's actual position.
8db2e3ef
JS
4808 child->LayoutToBestSize(dc, context, buffer,
4809 attr, child->GetAttributes(), availableRect, parentRect, style);
603f702b
JS
4810
4811 if (oldSize != child->GetCachedSize())
4812 {
4813 partialExtents.Clear();
4814
4815 // Recalculate the partial text extents since the child object changed size
914a4e23 4816 GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, wxPoint(0,0), parentRect.GetSize(), & partialExtents);
603f702b
JS
4817 }
4818 }
4819
4820 // Problem: we need to layout composites here for which we need the available width,
4821 // but we can't get the available width without using the float collector which
4822 // needs to know the object height.
4823
ff76711f 4824 if ((nextBreakPos == -1) && (lastEndPos == child->GetRange().GetStart() - 1)) // i.e. we want to get the whole thing
5d7836c4
JS
4825 {
4826 childSize = child->GetCachedSize();
4827 childDescent = child->GetDescent();
4828 }
4829 else
4f3d5bc0
JS
4830 {
4831#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4832 // Get height only, then the width using the partial extents
914a4e23 4833 GetRangeSize(wxRichTextRange(lastEndPos+1, lastPosToUse), childSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_HEIGHT_ONLY, wxPoint(0,0), parentRect.GetSize());
4f3d5bc0
JS
4834 childSize.x = wxRichTextGetRangeWidth(*this, wxRichTextRange(lastEndPos+1, lastPosToUse), partialExtents);
4835#else
914a4e23 4836 GetRangeSize(wxRichTextRange(lastEndPos+1, lastPosToUse), childSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED, rect.GetPosition(), parentRect.GetSize());
4f3d5bc0
JS
4837#endif
4838 }
ff76711f 4839
603f702b
JS
4840 bool doLoop = true;
4841 int loopIterations = 0;
4842
4843 // If there are nested objects that need to lay themselves out, we have to do this in a
4844 // loop because the height of the object may well depend on the available width.
4845 // And because of floating object positioning, the available width depends on the
4846 // height of the object and whether it will clash with the floating objects.
4847 // So, we see whether the available width changes due to the presence of floating images.
4848 // If it does, then we'll use the new restricted width to find the object height again.
4849 // If this causes another restriction in the available width, we'll try again, until
4850 // either we lose patience or the available width settles down.
4851 do
4852 {
4853 loopIterations ++;
4854
4855 wxRect oldAvailableRect = availableRect;
4856
4857 // Available width depends on the floating objects and the line height.
7c9fdebe 4858 // Note: the floating objects may be placed vertically along the two sides of
603f702b
JS
4859 // buffer, so we may have different available line widths with different
4860 // [startY, endY]. So, we can't determine how wide the available
4861 // space is until we know the exact line height.
a70eb13e
JS
4862 if (childDescent == 0)
4863 {
4864 lineHeight = wxMax(lineHeight, childSize.y);
4865 lineDescent = maxDescent;
4866 lineAscent = maxAscent;
4867 }
4868 else
4869 {
4870 lineDescent = wxMax(childDescent, maxDescent);
4871 lineAscent = wxMax(childSize.y-childDescent, maxAscent);
4872 }
4873 lineHeight = wxMax(lineHeight, (lineDescent + lineAscent));
603f702b 4874
e12b91a3 4875 if (wxRichTextBuffer::GetFloatingLayoutMode() && collector)
603f702b 4876 {
e12b91a3
JS
4877 wxRect floatAvailableRect = collector->GetAvailableRect(rect.y + currentPosition.y, rect.y + currentPosition.y + lineHeight);
4878
4879 // Adjust availableRect to the space that is available when taking floating objects into account.
4880
4881 if (floatAvailableRect.x + startOffset > availableRect.x)
4882 {
4883 int newX = floatAvailableRect.x + startOffset;
4884 int newW = availableRect.width - (newX - availableRect.x);
4885 availableRect.x = newX;
4886 availableRect.width = newW;
4887 }
603f702b 4888
e12b91a3
JS
4889 if (floatAvailableRect.width < availableRect.width)
4890 availableRect.width = floatAvailableRect.width;
4891 }
603f702b
JS
4892
4893 currentPosition.x = availableRect.x - rect.x;
4894
4895 if (child->IsTopLevel() && loopIterations <= 20)
4896 {
4897 if (availableRect != oldAvailableRect)
4898 {
4899 wxSize oldSize = child->GetCachedSize();
4900
603f702b
JS
4901 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4902 // lays out the object again using the minimum size
4903 child->Invalidate(wxRICHTEXT_ALL);
8db2e3ef 4904 child->LayoutToBestSize(dc, context, buffer,
914a4e23 4905 attr, child->GetAttributes(), availableRect, parentRect.GetSize(), style);
603f702b
JS
4906 childSize = child->GetCachedSize();
4907 childDescent = child->GetDescent();
603f702b
JS
4908
4909 if (oldSize != child->GetCachedSize())
4910 {
4911 partialExtents.Clear();
4912
4913 // Recalculate the partial text extents since the child object changed size
914a4e23 4914 GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, wxPoint(0,0), parentRect.GetSize(), & partialExtents);
603f702b 4915 }
cdaed652 4916
603f702b
JS
4917 // Go around the loop finding the available rect for the given floating objects
4918 }
4919 else
4920 doLoop = false;
4921 }
4922 else
4923 doLoop = false;
4924 }
4925 while (doLoop);
cdaed652 4926
20d09da5
JS
4927 if (child->IsTopLevel())
4928 {
4929 // We can move it to the correct position at this point
32423dd8
JS
4930 // TODO: probably need to add margin
4931 child->Move(GetPosition() + wxPoint(currentWidth + (wxMax(leftIndent, leftIndent + leftSubIndent)), currentPosition.y));
20d09da5
JS
4932 }
4933
ff76711f
JS
4934 // Cases:
4935 // 1) There was a line break BEFORE the natural break
4936 // 2) There was a line break AFTER the natural break
603f702b
JS
4937 // 3) It's the last line
4938 // 4) The child still fits (carry on) - 'else' clause
5d7836c4 4939
603f702b
JS
4940 if ((lineBreakInThisObject && (childSize.x + currentWidth <= availableRect.width))
4941 ||
4942 (childSize.x + currentWidth > availableRect.width)
914a4e23 4943#if 0
603f702b
JS
4944 ||
4945 ((childSize.x + currentWidth <= availableRect.width) && !node->GetNext())
914a4e23 4946#endif
603f702b 4947 )
5d7836c4
JS
4948 {
4949 long wrapPosition = 0;
603f702b
JS
4950 if ((childSize.x + currentWidth <= availableRect.width) && !node->GetNext() && !lineBreakInThisObject)
4951 wrapPosition = child->GetRange().GetEnd();
4952 else
5d7836c4
JS
4953
4954 // Find a place to wrap. This may walk back to previous children,
4955 // for example if a word spans several objects.
cdaed652
VZ
4956 // Note: one object must contains only one wxTextAtrr, so the line height will not
4957 // change inside one object. Thus, we can pass the remain line width to the
4958 // FindWrapPosition function.
8db2e3ef 4959 if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos+1, child->GetRange().GetEnd()), dc, context, availableRect.width, wrapPosition, & partialExtents))
5d7836c4
JS
4960 {
4961 // If the function failed, just cut it off at the end of this child.
4962 wrapPosition = child->GetRange().GetEnd();
4963 }
4964
4965 // FindWrapPosition can still return a value that will put us in an endless wrapping loop
4966 if (wrapPosition <= lastCompletedEndPos)
4967 wrapPosition = wxMax(lastCompletedEndPos+1,child->GetRange().GetEnd());
4968
603f702b
JS
4969 // Line end position shouldn't be the same as the end, or greater.
4970 if (wrapPosition >= GetRange().GetEnd())
a8a15de6 4971 wrapPosition = wxMax(0, GetRange().GetEnd()-1);
603f702b 4972
5d7836c4 4973 // wxLogDebug(wxT("Split at %ld"), wrapPosition);
7fe8059f 4974
5d7836c4
JS
4975 // Let's find the actual size of the current line now
4976 wxSize actualSize;
4977 wxRichTextRange actualRange(lastCompletedEndPos+1, wrapPosition);
4f3d5bc0 4978
a70eb13e 4979 childDescent = 0;
4ab8a5e2 4980
4f3d5bc0 4981#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
603f702b
JS
4982 if (!child->IsEmpty())
4983 {
4984 // Get height only, then the width using the partial extents
914a4e23 4985 GetRangeSize(actualRange, actualSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_HEIGHT_ONLY, wxPoint(0,0), parentRect.GetSize());
603f702b
JS
4986 actualSize.x = wxRichTextGetRangeWidth(*this, actualRange, partialExtents);
4987 }
4988 else
4f3d5bc0 4989#endif
914a4e23 4990 GetRangeSize(actualRange, actualSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED, wxPoint(0,0), parentRect.GetSize());
4f3d5bc0 4991
5d7836c4 4992 currentWidth = actualSize.x;
a70eb13e
JS
4993
4994 // The descent for the whole line at this point, is the correct max descent
4995 maxDescent = childDescent;
4996 // Maximum ascent
4997 maxAscent = actualSize.y-childDescent;
4998
4999 // lineHeight is given by the height for the whole line, since it will
5000 // take into account ascend/descend.
5001 lineHeight = actualSize.y;
7fe8059f 5002
07d4142f 5003 if (lineHeight == 0 && buffer)
603f702b 5004 {
07d4142f 5005 wxFont font(buffer->GetFontTable().FindFont(attr));
603f702b
JS
5006 wxCheckSetFont(dc, font);
5007 lineHeight = dc.GetCharHeight();
5008 }
5009
5010 if (maxDescent == 0)
5011 {
5012 int w, h;
5013 dc.GetTextExtent(wxT("X"), & w, &h, & maxDescent);
5014 }
5015
5d7836c4 5016 // Add a new line
1e967276 5017 wxRichTextLine* line = AllocateLine(lineCount);
39a1c2f2 5018
1e967276
JS
5019 // Set relative range so we won't have to change line ranges when paragraphs are moved
5020 line->SetRange(wxRichTextRange(actualRange.GetStart() - GetRange().GetStart(), actualRange.GetEnd() - GetRange().GetStart()));
5d7836c4
JS
5021 line->SetPosition(currentPosition);
5022 line->SetSize(wxSize(currentWidth, lineHeight));
5023 line->SetDescent(maxDescent);
5024
603f702b
JS
5025 maxHeight = currentPosition.y + lineHeight;
5026
5d7836c4
JS
5027 // Now move down a line. TODO: add margins, spacing
5028 currentPosition.y += lineHeight;
5029 currentPosition.y += lineSpacing;
5d7836c4 5030 maxDescent = 0;
476a319a 5031 maxAscent = 0;
603f702b
JS
5032 maxWidth = wxMax(maxWidth, currentWidth+startOffset);
5033 currentWidth = 0;
7fe8059f 5034
5d7836c4
JS
5035 lineCount ++;
5036
a70eb13e 5037 // TODO: account for zero-length objects
603f702b 5038 // wxASSERT(wrapPosition > lastCompletedEndPos);
7fe8059f 5039
5d7836c4
JS
5040 lastEndPos = wrapPosition;
5041 lastCompletedEndPos = lastEndPos;
5042
5043 lineHeight = 0;
5044
603f702b
JS
5045 if (wrapPosition < GetRange().GetEnd()-1)
5046 {
5047 // May need to set the node back to a previous one, due to searching back in wrapping
5048 wxRichTextObject* childAfterWrapPosition = FindObjectAtPosition(wrapPosition+1);
5049 if (childAfterWrapPosition)
5050 node = m_children.Find(childAfterWrapPosition);
5051 else
5052 node = node->GetNext();
5053 }
5d7836c4
JS
5054 else
5055 node = node->GetNext();
603f702b
JS
5056
5057 // Apply paragraph styles such as alignment to the wrapped line
5058 ApplyParagraphStyle(line, attr, availableRect, dc);
5d7836c4
JS
5059 }
5060 else
5061 {
5062 // We still fit, so don't add a line, and keep going
5063 currentWidth += childSize.x;
a70eb13e
JS
5064
5065 if (childDescent == 0)
5066 {
5067 // An object with a zero descend value wants to take up the whole
5068 // height regardless of baseline
5069 lineHeight = wxMax(lineHeight, childSize.y);
5070 }
5071 else
5072 {
5073 maxDescent = wxMax(childDescent, maxDescent);
5074 maxAscent = wxMax(childSize.y-childDescent, maxAscent);
5075 }
5076
5077 lineHeight = wxMax(lineHeight, (maxDescent + maxAscent));
5d7836c4 5078
603f702b 5079 maxWidth = wxMax(maxWidth, currentWidth+startOffset);
5d7836c4
JS
5080 lastEndPos = child->GetRange().GetEnd();
5081
5082 node = node->GetNext();
5083 }
5084 }
5085
07d4142f 5086 //wxASSERT(!(lastCompletedEndPos != -1 && lastCompletedEndPos < GetRange().GetEnd()-1));
5d7836c4 5087
914a4e23
JS
5088 // Add the last line - it's the current pos -> last para pos
5089 // Substract -1 because the last position is always the end-paragraph position.
5090 if (lastCompletedEndPos <= GetRange().GetEnd()-1)
5091 {
5092 currentPosition.x = (lineCount == 0 ? startPositionFirstLine : startPositionSubsequentLines);
5093
5094 wxRichTextLine* line = AllocateLine(lineCount);
5095
5096 wxRichTextRange actualRange(lastCompletedEndPos+1, GetRange().GetEnd()-1);
5097
5098 // Set relative range so we won't have to change line ranges when paragraphs are moved
5099 line->SetRange(wxRichTextRange(actualRange.GetStart() - GetRange().GetStart(), actualRange.GetEnd() - GetRange().GetStart()));
5100
5101 line->SetPosition(currentPosition);
5102
5103 if (lineHeight == 0 && buffer)
5104 {
5105 wxFont font(buffer->GetFontTable().FindFont(attr));
5106 wxCheckSetFont(dc, font);
5107 lineHeight = dc.GetCharHeight();
5108 }
5109
5110 if (maxDescent == 0)
5111 {
5112 int w, h;
5113 dc.GetTextExtent(wxT("X"), & w, &h, & maxDescent);
5114 }
5115
5116 line->SetSize(wxSize(currentWidth, lineHeight));
5117 line->SetDescent(maxDescent);
5118 currentPosition.y += lineHeight;
5119 currentPosition.y += lineSpacing;
5120 lineCount ++;
1bdb8131
JS
5121
5122 // Apply paragraph styles such as alignment to the wrapped line
5123 ApplyParagraphStyle(line, attr, availableRect, dc);
914a4e23
JS
5124 }
5125
1e967276
JS
5126 // Remove remaining unused line objects, if any
5127 ClearUnusedLines(lineCount);
5128
603f702b
JS
5129 // We need to add back the margins etc.
5130 {
5131 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
5132 contentRect = wxRect(wxPoint(0, 0), wxSize(maxWidth, currentPosition.y + spaceAfterPara));
8db2e3ef 5133 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
5134 SetCachedSize(marginRect.GetSize());
5135 }
5136
5137 // The maximum size is the length of the paragraph stretched out into a line.
5138 // So if there were a single word, or an image, or a fixed-size text box, the object could be shrunk around
5139 // this size. TODO: take into account line breaks.
5140 {
5141 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
031b5b0c 5142 contentRect = wxRect(wxPoint(0, 0), wxSize(paraSize.x + wxMax(leftIndent, leftIndent + leftSubIndent) + rightIndent, currentPosition.y + spaceAfterPara));
8db2e3ef 5143 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
5144 SetMaxSize(marginRect.GetSize());
5145 }
5146
5147 // Find the greatest minimum size. Currently we only look at non-text objects,
5148 // which isn't ideal but it would be slow to find the maximum word width to
5149 // use as the minimum.
5150 {
5151 int minWidth = 0;
5152 node = m_children.GetFirst();
5153 while (node)
5154 {
5155 wxRichTextObject* child = node->GetData();
5156
5157 // If floating, ignore. We already laid out floats.
5158 // Also ignore if empty object, except if we haven't got any
5159 // size yet.
e12b91a3 5160 if ((!child->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && child->GetRange().GetLength() != 0 && !wxDynamicCast(child, wxRichTextPlainText))
603f702b
JS
5161 {
5162 if (child->GetCachedSize().x > minWidth)
5163 minWidth = child->GetMinSize().x;
5164 }
5165 node = node->GetNext();
5166 }
5d7836c4 5167
603f702b
JS
5168 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
5169 contentRect = wxRect(wxPoint(0, 0), wxSize(minWidth, currentPosition.y + spaceAfterPara));
8db2e3ef 5170 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
5171 SetMinSize(marginRect.GetSize());
5172 }
5d7836c4 5173
2f45f554
JS
5174#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5175#if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
5176 // Use the text extents to calculate the size of each fragment in each line
5177 wxRichTextLineList::compatibility_iterator lineNode = m_cachedLines.GetFirst();
5178 while (lineNode)
5179 {
5180 wxRichTextLine* line = lineNode->GetData();
5181 wxRichTextRange lineRange = line->GetAbsoluteRange();
5182
5183 // Loop through objects until we get to the one within range
5184 wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
5185
5186 while (node2)
5187 {
5188 wxRichTextObject* child = node2->GetData();
5189
affbfa1f 5190 if (child->GetRange().GetLength() > 0 && !child->GetRange().IsOutside(lineRange))
2f45f554
JS
5191 {
5192 wxRichTextRange rangeToUse = lineRange;
5193 rangeToUse.LimitTo(child->GetRange());
5194
5195 // Find the size of the child from the text extents, and store in an array
5196 // for drawing later
5197 int left = 0;
5198 if (rangeToUse.GetStart() > GetRange().GetStart())
5199 left = partialExtents[(rangeToUse.GetStart()-1) - GetRange().GetStart()];
5200 int right = partialExtents[rangeToUse.GetEnd() - GetRange().GetStart()];
5201 int sz = right - left;
5202 line->GetObjectSizes().Add(sz);
5203 }
5204 else if (child->GetRange().GetStart() > lineRange.GetEnd())
5205 // Can break out of inner loop now since we've passed this line's range
5206 break;
5207
5208 node2 = node2->GetNext();
5209 }
5210
5211 lineNode = lineNode->GetNext();
5212 }
5213#endif
5214#endif
5215
5d7836c4
JS
5216 return true;
5217}
5218
603f702b
JS
5219/// Apply paragraph styles, such as centering, to wrapped lines
5220/// TODO: take into account box attributes, possibly
5221void wxRichTextParagraph::ApplyParagraphStyle(wxRichTextLine* line, const wxRichTextAttr& attr, const wxRect& rect, wxDC& dc)
5222{
5223 if (!attr.HasAlignment())
5224 return;
5225
5226 wxPoint pos = line->GetPosition();
32423dd8 5227 wxPoint originalPos = pos;
603f702b
JS
5228 wxSize size = line->GetSize();
5229
5230 // centering, right-justification
8db2e3ef 5231 if (attr.HasAlignment() && attr.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE)
603f702b
JS
5232 {
5233 int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
5234 pos.x = (rect.GetWidth() - rightIndent - size.x)/2 + pos.x;
5235 line->SetPosition(pos);
5236 }
8db2e3ef 5237 else if (attr.HasAlignment() && attr.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT)
603f702b
JS
5238 {
5239 int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
5240 pos.x = pos.x + rect.GetWidth() - size.x - rightIndent;
5241 line->SetPosition(pos);
5242 }
32423dd8
JS
5243
5244 if (pos != originalPos)
5245 {
5246 wxPoint inc = pos - originalPos;
5247
5248 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5249
5250 while (node)
5251 {
5252 wxRichTextObject* child = node->GetData();
5253 if (child->IsTopLevel() && !child->GetRange().IsOutside(line->GetAbsoluteRange()))
5254 child->Move(child->GetPosition() + inc);
5255
5256 node = node->GetNext();
5257 }
5258 }
603f702b 5259}
5d7836c4
JS
5260
5261/// Insert text at the given position
5262bool wxRichTextParagraph::InsertText(long pos, const wxString& text)
5263{
5264 wxRichTextObject* childToUse = NULL;
09f14108 5265 wxRichTextObjectList::compatibility_iterator nodeToUse = wxRichTextObjectList::compatibility_iterator();
5d7836c4
JS
5266
5267 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5268 while (node)
5269 {
5270 wxRichTextObject* child = node->GetData();
5271 if (child->GetRange().Contains(pos) && child->GetRange().GetLength() > 0)
5272 {
5273 childToUse = child;
5274 nodeToUse = node;
5275 break;
5276 }
5277
5278 node = node->GetNext();
5279 }
5280
5281 if (childToUse)
5282 {
5283 wxRichTextPlainText* textObject = wxDynamicCast(childToUse, wxRichTextPlainText);
5284 if (textObject)
5285 {
5286 int posInString = pos - textObject->GetRange().GetStart();
5287
5288 wxString newText = textObject->GetText().Mid(0, posInString) +
5289 text + textObject->GetText().Mid(posInString);
5290 textObject->SetText(newText);
5291
28f92d74 5292 int textLength = text.length();
5d7836c4
JS
5293
5294 textObject->SetRange(wxRichTextRange(textObject->GetRange().GetStart(),
5295 textObject->GetRange().GetEnd() + textLength));
5296
5297 // Increment the end range of subsequent fragments in this paragraph.
5298 // We'll set the paragraph range itself at a higher level.
5299
5300 wxRichTextObjectList::compatibility_iterator node = nodeToUse->GetNext();
5301 while (node)
5302 {
5303 wxRichTextObject* child = node->GetData();
5304 child->SetRange(wxRichTextRange(textObject->GetRange().GetStart() + textLength,
5305 textObject->GetRange().GetEnd() + textLength));
7fe8059f 5306
5d7836c4
JS
5307 node = node->GetNext();
5308 }
5309
5310 return true;
5311 }
5312 else
5313 {
5314 // TODO: if not a text object, insert at closest position, e.g. in front of it
5315 }
5316 }
5317 else
5318 {
5319 // Add at end.
5320 // Don't pass parent initially to suppress auto-setting of parent range.
5321 // We'll do that at a higher level.
5322 wxRichTextPlainText* textObject = new wxRichTextPlainText(text, this);
5323
5324 AppendChild(textObject);
5325 return true;
5326 }
5327
5328 return false;
5329}
5330
5331void wxRichTextParagraph::Copy(const wxRichTextParagraph& obj)
5332{
bec80f4f 5333 wxRichTextCompositeObject::Copy(obj);
5d7836c4
JS
5334}
5335
5336/// Clear the cached lines
5337void wxRichTextParagraph::ClearLines()
5338{
5339 WX_CLEAR_LIST(wxRichTextLineList, m_cachedLines);
5340}
5341
5342/// Get/set the object size for the given range. Returns false if the range
5343/// is invalid for this object.
914a4e23 5344bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, const wxPoint& position, const wxSize& parentSize, wxArrayInt* partialExtents) const
5d7836c4
JS
5345{
5346 if (!range.IsWithin(GetRange()))
5347 return false;
5348
5349 if (flags & wxRICHTEXT_UNFORMATTED)
5350 {
5351 // Just use unformatted data, assume no line breaks
5d7836c4
JS
5352 wxSize sz;
5353
31778480
JS
5354 wxArrayInt childExtents;
5355 wxArrayInt* p;
5356 if (partialExtents)
5357 p = & childExtents;
5358 else
5359 p = NULL;
5360
a70eb13e
JS
5361 int maxDescent = 0;
5362 int maxAscent = 0;
5363 int maxLineHeight = 0;
5364
5d7836c4
JS
5365 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5366 while (node)
5367 {
5368 wxRichTextObject* child = node->GetData();
5369 if (!child->GetRange().IsOutside(range))
5370 {
cdaed652 5371 // Floating objects have a zero size within the paragraph.
e12b91a3 5372 if (child->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode())
cdaed652
VZ
5373 {
5374 if (partialExtents)
5375 {
5376 int lastSize;
5377 if (partialExtents->GetCount() > 0)
5378 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
5379 else
5380 lastSize = 0;
5381
5382 partialExtents->Add(0 /* zero size */ + lastSize);
5383 }
5384 }
5385 else
5386 {
603f702b 5387 wxSize childSize;
4f3d5bc0 5388
603f702b
JS
5389 wxRichTextRange rangeToUse = range;
5390 rangeToUse.LimitTo(child->GetRange());
603f702b 5391 int childDescent = 0;
31778480 5392
7c9fdebe 5393 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we've already cached the size,
603f702b
JS
5394 // but it's only going to be used after caching has taken place.
5395 if ((flags & wxRICHTEXT_HEIGHT_ONLY) && child->GetCachedSize().y != 0)
2f45f554 5396 {
603f702b
JS
5397 childDescent = child->GetDescent();
5398 childSize = child->GetCachedSize();
2f45f554 5399
a70eb13e
JS
5400 if (childDescent == 0)
5401 {
5402 maxLineHeight = wxMax(maxLineHeight, childSize.y);
5403 }
5404 else
5405 {
5406 maxDescent = wxMax(maxDescent, childDescent);
5407 maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5408 }
5409
5410 maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5411
5412 sz.y = wxMax(sz.y, maxLineHeight);
603f702b 5413 sz.x += childSize.x;
a70eb13e 5414 descent = maxDescent;
603f702b
JS
5415 }
5416 else if (child->IsTopLevel())
31778480 5417 {
603f702b
JS
5418 childDescent = child->GetDescent();
5419 childSize = child->GetCachedSize();
31778480 5420
a70eb13e
JS
5421 if (childDescent == 0)
5422 {
5423 maxLineHeight = wxMax(maxLineHeight, childSize.y);
5424 }
5425 else
5426 {
5427 maxDescent = wxMax(maxDescent, childDescent);
5428 maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5429 }
5430
5431 maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5432
5433 sz.y = wxMax(sz.y, maxLineHeight);
603f702b 5434 sz.x += childSize.x;
a70eb13e
JS
5435 descent = maxDescent;
5436
5437 // FIXME: this won't change the original values.
5438 // Should we be calling GetRangeSize above instead of using cached values?
5439#if 0
603f702b 5440 if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange()))
31778480 5441 {
603f702b
JS
5442 child->SetCachedSize(childSize);
5443 child->SetDescent(childDescent);
31778480 5444 }
a70eb13e 5445#endif
31778480 5446
603f702b
JS
5447 if (partialExtents)
5448 {
5449 int lastSize;
5450 if (partialExtents->GetCount() > 0)
5451 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
5452 else
5453 lastSize = 0;
5454
5455 partialExtents->Add(childSize.x + lastSize);
5456 }
5457 }
914a4e23 5458 else if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y), parentSize, p))
603f702b 5459 {
a70eb13e
JS
5460 if (childDescent == 0)
5461 {
5462 maxLineHeight = wxMax(maxLineHeight, childSize.y);
5463 }
5464 else
5465 {
5466 maxDescent = wxMax(maxDescent, childDescent);
5467 maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5468 }
5469
5470 maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5471
5472 sz.y = wxMax(sz.y, maxLineHeight);
603f702b 5473 sz.x += childSize.x;
a70eb13e 5474 descent = maxDescent;
603f702b
JS
5475
5476 if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange()))
5477 {
5478 child->SetCachedSize(childSize);
5479 child->SetDescent(childDescent);
5480 }
5481
5482 if (partialExtents)
5483 {
5484 int lastSize;
5485 if (partialExtents->GetCount() > 0)
5486 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
5487 else
5488 lastSize = 0;
5489
5490 size_t i;
5491 for (i = 0; i < childExtents.GetCount(); i++)
5492 {
5493 partialExtents->Add(childExtents[i] + lastSize);
5494 }
5495 }
5496 }
5497 }
5498
5499 if (p)
5500 p->Clear();
5d7836c4
JS
5501 }
5502
5503 node = node->GetNext();
5504 }
5505 size = sz;
5506 }
5507 else
5508 {
5509 // Use formatted data, with line breaks
5510 wxSize sz;
5511
5512 // We're going to loop through each line, and then for each line,
5513 // call GetRangeSize for the fragment that comprises that line.
5514 // Only we have to do that multiple times within the line, because
5515 // the line may be broken into pieces. For now ignore line break commands
5516 // (so we can assume that getting the unformatted size for a fragment
5517 // within a line is the actual size)
5518
5519 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
5520 while (node)
5521 {
5522 wxRichTextLine* line = node->GetData();
1e967276
JS
5523 wxRichTextRange lineRange = line->GetAbsoluteRange();
5524 if (!lineRange.IsOutside(range))
5d7836c4 5525 {
a70eb13e
JS
5526 int maxDescent = 0;
5527 int maxAscent = 0;
5528 int maxLineHeight = 0;
5529 int maxLineWidth = 0;
7fe8059f 5530
5d7836c4
JS
5531 wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
5532 while (node2)
5533 {
5534 wxRichTextObject* child = node2->GetData();
7fe8059f 5535
e12b91a3 5536 if ((!child->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && !child->GetRange().IsOutside(lineRange))
5d7836c4 5537 {
1e967276 5538 wxRichTextRange rangeToUse = lineRange;
5d7836c4 5539 rangeToUse.LimitTo(child->GetRange());
603f702b
JS
5540 if (child->IsTopLevel())
5541 rangeToUse = child->GetOwnRange();
7fe8059f 5542
5d7836c4
JS
5543 wxSize childSize;
5544 int childDescent = 0;
914a4e23 5545 if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y), parentSize))
5d7836c4 5546 {
a70eb13e
JS
5547 if (childDescent == 0)
5548 {
5549 // Assume that if descent is zero, this child can occupy the full line height
5550 // and does not need space for the line's maximum descent. So we influence
5551 // the overall max line height only.
5552 maxLineHeight = wxMax(maxLineHeight, childSize.y);
5553 }
5554 else
5555 {
5556 maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5557 maxDescent = wxMax(maxAscent, childDescent);
5558 }
5559 maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5560 maxLineWidth += childSize.x;
5d7836c4 5561 }
5d7836c4 5562 }
7fe8059f 5563
5d7836c4
JS
5564 node2 = node2->GetNext();
5565 }
5566
a70eb13e
JS
5567 descent = wxMax(descent, maxDescent);
5568
5d7836c4 5569 // Increase size by a line (TODO: paragraph spacing)
a70eb13e
JS
5570 sz.y += maxLineHeight;
5571 sz.x = wxMax(sz.x, maxLineWidth);
5d7836c4
JS
5572 }
5573 node = node->GetNext();
5574 }
5575 size = sz;
5576 }
5577 return true;
5578}
5579
5580/// Finds the absolute position and row height for the given character position
8db2e3ef 5581bool wxRichTextParagraph::FindPosition(wxDC& dc, wxRichTextDrawingContext& context, long index, wxPoint& pt, int* height, bool forceLineStart)
5d7836c4
JS
5582{
5583 if (index == -1)
5584 {
5585 wxRichTextLine* line = ((wxRichTextParagraphLayoutBox*)GetParent())->GetLineAtPosition(0);
5586 if (line)
5587 *height = line->GetSize().y;
5588 else
5589 *height = dc.GetCharHeight();
5590
5591 // -1 means 'the start of the buffer'.
5592 pt = GetPosition();
5593 if (line)
5594 pt = pt + line->GetPosition();
5595
5d7836c4
JS
5596 return true;
5597 }
5598
5599 // The final position in a paragraph is taken to mean the position
5600 // at the start of the next paragraph.
5601 if (index == GetRange().GetEnd())
5602 {
5603 wxRichTextParagraphLayoutBox* parent = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
5604 wxASSERT( parent != NULL );
5605
5606 // Find the height at the next paragraph, if any
5607 wxRichTextLine* line = parent->GetLineAtPosition(index + 1);
5608 if (line)
5609 {
5610 *height = line->GetSize().y;
5611 pt = line->GetAbsolutePosition();
5612 }
5613 else
5614 {
5615 *height = dc.GetCharHeight();
5616 int indent = ConvertTenthsMMToPixels(dc, m_attributes.GetLeftIndent());
5617 pt = wxPoint(indent, GetCachedSize().y);
5618 }
5619
5620 return true;
5621 }
5622
5623 if (index < GetRange().GetStart() || index > GetRange().GetEnd())
5624 return false;
5625
5626 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
5627 while (node)
5628 {
5629 wxRichTextLine* line = node->GetData();
1e967276
JS
5630 wxRichTextRange lineRange = line->GetAbsoluteRange();
5631 if (index >= lineRange.GetStart() && index <= lineRange.GetEnd())
5d7836c4
JS
5632 {
5633 // If this is the last point in the line, and we're forcing the
5634 // returned value to be the start of the next line, do the required
5635 // thing.
1e967276 5636 if (index == lineRange.GetEnd() && forceLineStart)
5d7836c4
JS
5637 {
5638 if (node->GetNext())
5639 {
5640 wxRichTextLine* nextLine = node->GetNext()->GetData();
5641 *height = nextLine->GetSize().y;
5642 pt = nextLine->GetAbsolutePosition();
5643 return true;
5644 }
5645 }
5646
5647 pt.y = line->GetPosition().y + GetPosition().y;
5648
1e967276 5649 wxRichTextRange r(lineRange.GetStart(), index);
5d7836c4
JS
5650 wxSize rangeSize;
5651 int descent = 0;
5652
5653 // We find the size of the line up to this point,
5654 // then we can add this size to the line start position and
5655 // paragraph start position to find the actual position.
5656
8db2e3ef 5657 if (GetRangeSize(r, rangeSize, descent, dc, context, wxRICHTEXT_UNFORMATTED, line->GetPosition()+ GetPosition()))
5d7836c4
JS
5658 {
5659 pt.x = line->GetPosition().x + GetPosition().x + rangeSize.x;
5660 *height = line->GetSize().y;
5661
5662 return true;
5663 }
5664
5665 }
5666
5667 node = node->GetNext();
5668 }
5669
5670 return false;
5671}
5672
5673/// Hit-testing: returns a flag indicating hit test details, plus
5674/// information about position
8db2e3ef 5675int wxRichTextParagraph::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
5d7836c4 5676{
603f702b
JS
5677 if (!IsShown())
5678 return wxRICHTEXT_HITTEST_NONE;
5679
5680 // If we're in the top-level container, then we can return
5681 // a suitable hit test code even if the point is outside the container area,
5682 // so that we can position the caret sensibly even if we don't
5683 // click on valid content. If we're not at the top-level, and the point
5684 // is not within this paragraph object, then we don't want to stop more
5685 // precise hit-testing from working prematurely, so return immediately.
5686 // NEW STRATEGY: use the parent boundary to test whether we're in the
5687 // right region, not the paragraph, since the paragraph may be positioned
5688 // some way in from where the user clicks.
5689 {
5690 long tmpPos;
5691 wxRichTextObject* tempObj, *tempContextObj;
8db2e3ef 5692 if (GetParent() && GetParent()->wxRichTextObject::HitTest(dc, context, pt, tmpPos, & tempObj, & tempContextObj, flags) == wxRICHTEXT_HITTEST_NONE)
603f702b
JS
5693 return wxRICHTEXT_HITTEST_NONE;
5694 }
5695
5696 wxRichTextObjectList::compatibility_iterator objNode = m_children.GetFirst();
5697 while (objNode)
5698 {
5699 wxRichTextObject* child = objNode->GetData();
7c9fdebe
JS
5700 // Don't recurse if we have wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS,
5701 // and also, if this seems composite but actually is marked as atomic,
5702 // don't recurse.
5703 if (child->IsTopLevel() && ((flags & wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS) == 0) &&
5704 (! (((flags & wxRICHTEXT_HITTEST_HONOUR_ATOMIC) != 0) && child->IsAtomic())))
603f702b
JS
5705 {
5706 {
8db2e3ef 5707 int hitTest = child->HitTest(dc, context, pt, textPosition, obj, contextObj);
603f702b
JS
5708 if (hitTest != wxRICHTEXT_HITTEST_NONE)
5709 return hitTest;
5710 }
5711 }
5712
5713 objNode = objNode->GetNext();
5714 }
5715
5d7836c4
JS
5716 wxPoint paraPos = GetPosition();
5717
5718 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
5719 while (node)
5720 {
5721 wxRichTextLine* line = node->GetData();
5722 wxPoint linePos = paraPos + line->GetPosition();
5723 wxSize lineSize = line->GetSize();
1e967276 5724 wxRichTextRange lineRange = line->GetAbsoluteRange();
5d7836c4 5725
62381daa 5726 if (pt.y <= linePos.y + lineSize.y)
5d7836c4
JS
5727 {
5728 if (pt.x < linePos.x)
5729 {
1e967276 5730 textPosition = lineRange.GetStart();
603f702b
JS
5731 *obj = FindObjectAtPosition(textPosition);
5732 *contextObj = GetContainer();
f262b25c 5733 return wxRICHTEXT_HITTEST_BEFORE|wxRICHTEXT_HITTEST_OUTSIDE;
5d7836c4
JS
5734 }
5735 else if (pt.x >= (linePos.x + lineSize.x))
5736 {
1e967276 5737 textPosition = lineRange.GetEnd();
603f702b
JS
5738 *obj = FindObjectAtPosition(textPosition);
5739 *contextObj = GetContainer();
f262b25c 5740 return wxRICHTEXT_HITTEST_AFTER|wxRICHTEXT_HITTEST_OUTSIDE;
5d7836c4
JS
5741 }
5742 else
5743 {
2f45f554
JS
5744#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5745 wxArrayInt partialExtents;
5746
5747 wxSize paraSize;
5748 int paraDescent;
5749
5750 // This calculates the partial text extents
914a4e23 5751 GetRangeSize(lineRange, paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED, linePos, wxDefaultSize, & partialExtents);
2f45f554
JS
5752
5753 int lastX = linePos.x;
5754 size_t i;
5755 for (i = 0; i < partialExtents.GetCount(); i++)
5756 {
5757 int nextX = partialExtents[i] + linePos.x;
5758
5759 if (pt.x >= lastX && pt.x <= nextX)
5760 {
5761 textPosition = i + lineRange.GetStart(); // minus 1?
5762
603f702b
JS
5763 *obj = FindObjectAtPosition(textPosition);
5764 *contextObj = GetContainer();
5765
2f45f554
JS
5766 // So now we know it's between i-1 and i.
5767 // Let's see if we can be more precise about
5768 // which side of the position it's on.
5769
cdaed652 5770 int midPoint = (nextX + lastX)/2;
2f45f554
JS
5771 if (pt.x >= midPoint)
5772 return wxRICHTEXT_HITTEST_AFTER;
5773 else
5774 return wxRICHTEXT_HITTEST_BEFORE;
5775 }
5776
5777 lastX = nextX;
5778 }
5779#else
5d7836c4
JS
5780 long i;
5781 int lastX = linePos.x;
1e967276 5782 for (i = lineRange.GetStart(); i <= lineRange.GetEnd(); i++)
5d7836c4
JS
5783 {
5784 wxSize childSize;
5785 int descent = 0;
7fe8059f 5786
1e967276 5787 wxRichTextRange rangeToUse(lineRange.GetStart(), i);
7fe8059f 5788
8db2e3ef 5789 GetRangeSize(rangeToUse, childSize, descent, dc, context, wxRICHTEXT_UNFORMATTED, linePos);
5d7836c4
JS
5790
5791 int nextX = childSize.x + linePos.x;
5792
5793 if (pt.x >= lastX && pt.x <= nextX)
5794 {
5795 textPosition = i;
5796
603f702b
JS
5797 *obj = FindObjectAtPosition(textPosition);
5798 *contextObj = GetContainer();
5799
5d7836c4
JS
5800 // So now we know it's between i-1 and i.
5801 // Let's see if we can be more precise about
5802 // which side of the position it's on.
5803
cdaed652 5804 int midPoint = (nextX + lastX)/2;
5d7836c4
JS
5805 if (pt.x >= midPoint)
5806 return wxRICHTEXT_HITTEST_AFTER;
5807 else
5808 return wxRICHTEXT_HITTEST_BEFORE;
5809 }
5810 else
5811 {
5812 lastX = nextX;
5813 }
5814 }
2f45f554 5815#endif
5d7836c4
JS
5816 }
5817 }
7fe8059f 5818
5d7836c4
JS
5819 node = node->GetNext();
5820 }
5821
5822 return wxRICHTEXT_HITTEST_NONE;
5823}
5824
5825/// Split an object at this position if necessary, and return
5826/// the previous object, or NULL if inserting at beginning.
5827wxRichTextObject* wxRichTextParagraph::SplitAt(long pos, wxRichTextObject** previousObject)
5828{
5829 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5830 while (node)
5831 {
5832 wxRichTextObject* child = node->GetData();
5833
5834 if (pos == child->GetRange().GetStart())
5835 {
5836 if (previousObject)
4d551ad5
JS
5837 {
5838 if (node->GetPrevious())
5839 *previousObject = node->GetPrevious()->GetData();
5840 else
5841 *previousObject = NULL;
5842 }
5d7836c4
JS
5843
5844 return child;
5845 }
5846
5847 if (child->GetRange().Contains(pos))
5848 {
5849 // This should create a new object, transferring part of
5850 // the content to the old object and the rest to the new object.
5851 wxRichTextObject* newObject = child->DoSplit(pos);
5852
5853 // If we couldn't split this object, just insert in front of it.
5854 if (!newObject)
5855 {
5856 // Maybe this is an empty string, try the next one
5857 // return child;
5858 }
5859 else
5860 {
5861 // Insert the new object after 'child'
5862 if (node->GetNext())
5863 m_children.Insert(node->GetNext(), newObject);
5864 else
5865 m_children.Append(newObject);
5866 newObject->SetParent(this);
5867
5868 if (previousObject)
5869 *previousObject = child;
5870
5871 return newObject;
5872 }
5873 }
5874
5875 node = node->GetNext();
5876 }
5877 if (previousObject)
5878 *previousObject = NULL;
5879 return NULL;
5880}
5881
5882/// Move content to a list from obj on
5883void wxRichTextParagraph::MoveToList(wxRichTextObject* obj, wxList& list)
5884{
5885 wxRichTextObjectList::compatibility_iterator node = m_children.Find(obj);
5886 while (node)
5887 {
5888 wxRichTextObject* child = node->GetData();
5889 list.Append(child);
5890
5891 wxRichTextObjectList::compatibility_iterator oldNode = node;
5892
5893 node = node->GetNext();
5894
5895 m_children.DeleteNode(oldNode);
5896 }
5897}
5898
5899/// Add content back from list
5900void wxRichTextParagraph::MoveFromList(wxList& list)
5901{
09f14108 5902 for (wxList::compatibility_iterator node = list.GetFirst(); node; node = node->GetNext())
5d7836c4
JS
5903 {
5904 AppendChild((wxRichTextObject*) node->GetData());
5905 }
5906}
5907
5908/// Calculate range
5909void wxRichTextParagraph::CalculateRange(long start, long& end)
5910{
5911 wxRichTextCompositeObject::CalculateRange(start, end);
5912
5913 // Add one for end of paragraph
5914 end ++;
5915
5916 m_range.SetRange(start, end);
5917}
5918
5919/// Find the object at the given position
5920wxRichTextObject* wxRichTextParagraph::FindObjectAtPosition(long position)
5921{
5922 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5923 while (node)
5924 {
5925 wxRichTextObject* obj = node->GetData();
603f702b
JS
5926 if (obj->GetRange().Contains(position) ||
5927 obj->GetRange().GetStart() == position ||
5928 obj->GetRange().GetEnd() == position)
5d7836c4 5929 return obj;
7fe8059f 5930
5d7836c4
JS
5931 node = node->GetNext();
5932 }
5933 return NULL;
5934}
5935
5936/// Get the plain text searching from the start or end of the range.
5937/// The resulting string may be shorter than the range given.
5938bool wxRichTextParagraph::GetContiguousPlainText(wxString& text, const wxRichTextRange& range, bool fromStart)
5939{
5940 text = wxEmptyString;
5941
5942 if (fromStart)
5943 {
5944 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5945 while (node)
5946 {
5947 wxRichTextObject* obj = node->GetData();
5948 if (!obj->GetRange().IsOutside(range))
5949 {
5950 wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
5951 if (textObj)
5952 {
5953 text += textObj->GetTextForRange(range);
5954 }
5955 else
043c0d58
JS
5956 {
5957 text += wxT(" ");
5958 }
5d7836c4
JS
5959 }
5960
5961 node = node->GetNext();
5962 }
5963 }
5964 else
5965 {
5966 wxRichTextObjectList::compatibility_iterator node = m_children.GetLast();
5967 while (node)
5968 {
5969 wxRichTextObject* obj = node->GetData();
5970 if (!obj->GetRange().IsOutside(range))
5971 {
5972 wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
5973 if (textObj)
5974 {
5975 text = textObj->GetTextForRange(range) + text;
5976 }
5977 else
043c0d58
JS
5978 {
5979 text = wxT(" ") + text;
5980 }
5d7836c4
JS
5981 }
5982
5983 node = node->GetPrevious();
5984 }
5985 }
5986
5987 return true;
5988}
5989
5990/// Find a suitable wrap position.
8db2e3ef 5991bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange& range, wxDC& dc, wxRichTextDrawingContext& context, int availableSpace, long& wrapPosition, wxArrayInt* partialExtents)
5d7836c4 5992{
72945e24
JS
5993 if (range.GetLength() <= 0)
5994 return false;
5995
5d7836c4
JS
5996 // Find the first position where the line exceeds the available space.
5997 wxSize sz;
5d7836c4 5998 long breakPosition = range.GetEnd();
ecb5fbf1 5999
31778480
JS
6000#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
6001 if (partialExtents && partialExtents->GetCount() >= (size_t) (GetRange().GetLength()-1)) // the final position in a paragraph is the newline
5d7836c4 6002 {
31778480 6003 int widthBefore;
5d7836c4 6004
31778480
JS
6005 if (range.GetStart() > GetRange().GetStart())
6006 widthBefore = (*partialExtents)[range.GetStart() - GetRange().GetStart() - 1];
6007 else
6008 widthBefore = 0;
6009
6010 size_t i;
43a0d1e1 6011 for (i = (size_t) range.GetStart(); i <= (size_t) range.GetEnd(); i++)
5d7836c4 6012 {
31778480 6013 int widthFromStartOfThisRange = (*partialExtents)[i - GetRange().GetStart()] - widthBefore;
ecb5fbf1 6014
72945e24 6015 if (widthFromStartOfThisRange > availableSpace)
ecb5fbf1 6016 {
31778480
JS
6017 breakPosition = i-1;
6018 break;
ecb5fbf1 6019 }
5d7836c4 6020 }
31778480
JS
6021 }
6022 else
6023#endif
6024 {
6025 // Binary chop for speed
6026 long minPos = range.GetStart();
6027 long maxPos = range.GetEnd();
6028 while (true)
ecb5fbf1 6029 {
31778480
JS
6030 if (minPos == maxPos)
6031 {
6032 int descent = 0;
8db2e3ef 6033 GetRangeSize(wxRichTextRange(range.GetStart(), minPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
ecb5fbf1 6034
31778480
JS
6035 if (sz.x > availableSpace)
6036 breakPosition = minPos - 1;
6037 break;
6038 }
6039 else if ((maxPos - minPos) == 1)
ecb5fbf1 6040 {
31778480 6041 int descent = 0;
8db2e3ef 6042 GetRangeSize(wxRichTextRange(range.GetStart(), minPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
31778480
JS
6043
6044 if (sz.x > availableSpace)
6045 breakPosition = minPos - 1;
6046 else
6047 {
8db2e3ef 6048 GetRangeSize(wxRichTextRange(range.GetStart(), maxPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
31778480
JS
6049 if (sz.x > availableSpace)
6050 breakPosition = maxPos-1;
6051 }
6052 break;
ecb5fbf1
JS
6053 }
6054 else
6055 {
31778480
JS
6056 long nextPos = minPos + ((maxPos - minPos) / 2);
6057
6058 int descent = 0;
8db2e3ef 6059 GetRangeSize(wxRichTextRange(range.GetStart(), nextPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
31778480
JS
6060
6061 if (sz.x > availableSpace)
6062 {
6063 maxPos = nextPos;
6064 }
6065 else
6066 {
6067 minPos = nextPos;
6068 }
ecb5fbf1
JS
6069 }
6070 }
5d7836c4
JS
6071 }
6072
6073 // Now we know the last position on the line.
6074 // Let's try to find a word break.
6075
6076 wxString plainText;
6077 if (GetContiguousPlainText(plainText, wxRichTextRange(range.GetStart(), breakPosition), false))
6078 {
ff76711f
JS
6079 int newLinePos = plainText.Find(wxRichTextLineBreakChar);
6080 if (newLinePos != wxNOT_FOUND)
5d7836c4 6081 {
ff76711f
JS
6082 breakPosition = wxMax(0, range.GetStart() + newLinePos);
6083 }
6084 else
6085 {
6086 int spacePos = plainText.Find(wxT(' '), true);
31002e44
JS
6087 int tabPos = plainText.Find(wxT('\t'), true);
6088 int pos = wxMax(spacePos, tabPos);
6089 if (pos != wxNOT_FOUND)
ff76711f 6090 {
31002e44 6091 int positionsFromEndOfString = plainText.length() - pos - 1;
ff76711f
JS
6092 breakPosition = breakPosition - positionsFromEndOfString;
6093 }
5d7836c4
JS
6094 }
6095 }
6096
6097 wrapPosition = breakPosition;
6098
6099 return true;
6100}
6101
6102/// Get the bullet text for this paragraph.
6103wxString wxRichTextParagraph::GetBulletText()
6104{
6105 if (GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE ||
6106 (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP))
6107 return wxEmptyString;
6108
6109 int number = GetAttributes().GetBulletNumber();
6110
6111 wxString text;
d2d0adc7 6112 if ((GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ARABIC) || (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE))
5d7836c4
JS
6113 {
6114 text.Printf(wxT("%d"), number);
6115 }
6116 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_UPPER)
6117 {
6118 // TODO: Unicode, and also check if number > 26
6119 text.Printf(wxT("%c"), (wxChar) (number+64));
6120 }
6121 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_LOWER)
6122 {
6123 // TODO: Unicode, and also check if number > 26
6124 text.Printf(wxT("%c"), (wxChar) (number+96));
6125 }
6126 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER)
6127 {
59509217 6128 text = wxRichTextDecimalToRoman(number);
5d7836c4
JS
6129 }
6130 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER)
6131 {
59509217
JS
6132 text = wxRichTextDecimalToRoman(number);
6133 text.MakeLower();
5d7836c4
JS
6134 }
6135 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL)
6136 {
d2d0adc7
JS
6137 text = GetAttributes().GetBulletText();
6138 }
3e541562 6139
d2d0adc7
JS
6140 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE)
6141 {
6142 // The outline style relies on the text being computed statically,
6143 // since it depends on other levels points (e.g. 1.2.1.1). So normally the bullet text
6144 // should be stored in the attributes; if not, just use the number for this
6145 // level, as previously computed.
6146 if (!GetAttributes().GetBulletText().IsEmpty())
6147 text = GetAttributes().GetBulletText();
5d7836c4
JS
6148 }
6149
6150 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PARENTHESES)
6151 {
6152 text = wxT("(") + text + wxT(")");
6153 }
d2d0adc7
JS
6154 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_RIGHT_PARENTHESIS)
6155 {
6156 text = text + wxT(")");
6157 }
6158
5d7836c4
JS
6159 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PERIOD)
6160 {
6161 text += wxT(".");
6162 }
6163
6164 return text;
6165}
6166
1e967276
JS
6167/// Allocate or reuse a line object
6168wxRichTextLine* wxRichTextParagraph::AllocateLine(int pos)
6169{
6170 if (pos < (int) m_cachedLines.GetCount())
6171 {
6172 wxRichTextLine* line = m_cachedLines.Item(pos)->GetData();
6173 line->Init(this);
6174 return line;
6175 }
6176 else
6177 {
6178 wxRichTextLine* line = new wxRichTextLine(this);
6179 m_cachedLines.Append(line);
6180 return line;
6181 }
6182}
6183
6184/// Clear remaining unused line objects, if any
6185bool wxRichTextParagraph::ClearUnusedLines(int lineCount)
6186{
6187 int cachedLineCount = m_cachedLines.GetCount();
6188 if ((int) cachedLineCount > lineCount)
6189 {
6190 for (int i = 0; i < (int) (cachedLineCount - lineCount); i ++)
6191 {
6192 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetLast();
6193 wxRichTextLine* line = node->GetData();
6194 m_cachedLines.Erase(node);
6195 delete line;
6196 }
6197 }
6198 return true;
6199}
6200
fe5aa22c
JS
6201/// Get combined attributes of the base style, paragraph style and character style. We use this to dynamically
6202/// retrieve the actual style.
603f702b 6203wxRichTextAttr wxRichTextParagraph::GetCombinedAttributes(const wxRichTextAttr& contentStyle, bool includingBoxAttr) const
fe5aa22c 6204{
24777478 6205 wxRichTextAttr attr;
603f702b 6206 wxRichTextParagraphLayoutBox* buf = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
fe5aa22c
JS
6207 if (buf)
6208 {
6209 attr = buf->GetBasicStyle();
603f702b
JS
6210 if (!includingBoxAttr)
6211 {
6212 attr.GetTextBoxAttr().Reset();
6213 // The background colour will be painted by the container, and we don't
6214 // want to unnecessarily overwrite the background when we're drawing text
6215 // because this may erase the guideline (which appears just under the text
6216 // if there's no padding).
6217 attr.SetFlags(attr.GetFlags() & ~wxTEXT_ATTR_BACKGROUND_COLOUR);
6218 }
fe5aa22c
JS
6219 wxRichTextApplyStyle(attr, GetAttributes());
6220 }
6221 else
6222 attr = GetAttributes();
6223
6224 wxRichTextApplyStyle(attr, contentStyle);
6225 return attr;
6226}
6227
6228/// Get combined attributes of the base style and paragraph style.
603f702b 6229wxRichTextAttr wxRichTextParagraph::GetCombinedAttributes(bool includingBoxAttr) const
fe5aa22c 6230{
24777478 6231 wxRichTextAttr attr;
603f702b 6232 wxRichTextParagraphLayoutBox* buf = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
fe5aa22c
JS
6233 if (buf)
6234 {
6235 attr = buf->GetBasicStyle();
603f702b
JS
6236 if (!includingBoxAttr)
6237 attr.GetTextBoxAttr().Reset();
fe5aa22c
JS
6238 wxRichTextApplyStyle(attr, GetAttributes());
6239 }
6240 else
6241 attr = GetAttributes();
6242
6243 return attr;
6244}
5d7836c4 6245
603f702b 6246// Create default tabstop array
cfa3b256
JS
6247void wxRichTextParagraph::InitDefaultTabs()
6248{
6249 // create a default tab list at 10 mm each.
6250 for (int i = 0; i < 20; ++i)
6251 {
6252 sm_defaultTabs.Add(i*100);
6253 }
6254}
6255
603f702b 6256// Clear default tabstop array
cfa3b256
JS
6257void wxRichTextParagraph::ClearDefaultTabs()
6258{
6259 sm_defaultTabs.Clear();
6260}
6261
c4168888 6262void wxRichTextParagraph::LayoutFloat(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style, wxRichTextFloatCollector* floatCollector)
cdaed652
VZ
6263{
6264 wxRichTextObjectList::compatibility_iterator node = GetChildren().GetFirst();
6265 while (node)
6266 {
bec80f4f 6267 wxRichTextObject* anchored = node->GetData();
07d4142f 6268 if (anchored && anchored->IsFloating() && !floatCollector->HasFloat(anchored))
cdaed652 6269 {
c4168888
JS
6270 int x = 0;
6271 wxRichTextAttr parentAttr(GetAttributes());
6272 context.ApplyVirtualAttributes(parentAttr, this);
6273#if 1
6274 // 27-09-2012
6275 wxRect availableSpace = GetParent()->GetAvailableContentArea(dc, context, rect);
6276
6277 anchored->LayoutToBestSize(dc, context, GetBuffer(),
6278 parentAttr, anchored->GetAttributes(),
6279 parentRect, availableSpace,
6280 style);
6281 wxSize size = anchored->GetCachedSize();
6282#else
cdaed652 6283 wxSize size;
c4168888 6284 int descent = 0;
8db2e3ef 6285 anchored->GetRangeSize(anchored->GetRange(), size, descent, dc, context, style);
c4168888 6286#endif
bec80f4f 6287
24777478 6288 int offsetY = 0;
603f702b 6289 if (anchored->GetAttributes().GetTextBoxAttr().GetTop().IsValid())
24777478
JS
6290 {
6291 offsetY = anchored->GetAttributes().GetTextBoxAttr().GetTop().GetValue();
6292 if (anchored->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
6293 {
6294 offsetY = ConvertTenthsMMToPixels(dc, offsetY);
6295 }
6296 }
bec80f4f 6297
24777478 6298 int pos = floatCollector->GetFitPosition(anchored->GetAttributes().GetTextBoxAttr().GetFloatMode(), rect.y + offsetY, size.y);
ce00f59b 6299
cdaed652 6300 /* Update the offset */
24777478
JS
6301 int newOffsetY = pos - rect.y;
6302 if (newOffsetY != offsetY)
6303 {
6304 if (anchored->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
6305 newOffsetY = ConvertPixelsToTenthsMM(dc, newOffsetY);
6306 anchored->GetAttributes().GetTextBoxAttr().GetTop().SetValue(newOffsetY);
6307 }
cdaed652 6308
24777478 6309 if (anchored->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT)
603f702b 6310 x = rect.x;
24777478 6311 else if (anchored->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT)
603f702b 6312 x = rect.x + rect.width - size.x;
24777478 6313
c4168888
JS
6314 //anchored->SetPosition(wxPoint(x, pos));
6315 anchored->Move(wxPoint(x, pos)); // should move children
cdaed652
VZ
6316 anchored->SetCachedSize(size);
6317 floatCollector->CollectFloat(this, anchored);
6318 }
6319
6320 node = node->GetNext();
6321 }
6322}
6323
603f702b 6324// Get the first position from pos that has a line break character.
ff76711f
JS
6325long wxRichTextParagraph::GetFirstLineBreakPosition(long pos)
6326{
6327 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
6328 while (node)
6329 {
6330 wxRichTextObject* obj = node->GetData();
6331 if (pos >= obj->GetRange().GetStart() && pos <= obj->GetRange().GetEnd())
6332 {
6333 wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
6334 if (textObj)
6335 {
6336 long breakPos = textObj->GetFirstLineBreakPosition(pos);
6337 if (breakPos > -1)
6338 return breakPos;
6339 }
6340 }
6341 node = node->GetNext();
6342 }
6343 return -1;
6344}
cfa3b256 6345
5d7836c4
JS
6346/*!
6347 * wxRichTextLine
6348 * This object represents a line in a paragraph, and stores
6349 * offsets from the start of the paragraph representing the
6350 * start and end positions of the line.
6351 */
6352
6353wxRichTextLine::wxRichTextLine(wxRichTextParagraph* parent)
6354{
1e967276 6355 Init(parent);
5d7836c4
JS
6356}
6357
6358/// Initialisation
1e967276 6359void wxRichTextLine::Init(wxRichTextParagraph* parent)
5d7836c4 6360{
1e967276
JS
6361 m_parent = parent;
6362 m_range.SetRange(-1, -1);
6363 m_pos = wxPoint(0, 0);
6364 m_size = wxSize(0, 0);
5d7836c4 6365 m_descent = 0;
2f45f554
JS
6366#if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6367 m_objectSizes.Clear();
6368#endif
5d7836c4
JS
6369}
6370
6371/// Copy
6372void wxRichTextLine::Copy(const wxRichTextLine& obj)
6373{
6374 m_range = obj.m_range;
2f45f554
JS
6375#if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6376 m_objectSizes = obj.m_objectSizes;
6377#endif
5d7836c4
JS
6378}
6379
6380/// Get the absolute object position
6381wxPoint wxRichTextLine::GetAbsolutePosition() const
6382{
6383 return m_parent->GetPosition() + m_pos;
6384}
6385
1e967276
JS
6386/// Get the absolute range
6387wxRichTextRange wxRichTextLine::GetAbsoluteRange() const
6388{
6389 wxRichTextRange range(m_range.GetStart() + m_parent->GetRange().GetStart(), 0);
6390 range.SetEnd(range.GetStart() + m_range.GetLength()-1);
6391 return range;
6392}
6393
5d7836c4
JS
6394/*!
6395 * wxRichTextPlainText
6396 * This object represents a single piece of text.
6397 */
6398
6399IMPLEMENT_DYNAMIC_CLASS(wxRichTextPlainText, wxRichTextObject)
6400
24777478 6401wxRichTextPlainText::wxRichTextPlainText(const wxString& text, wxRichTextObject* parent, wxRichTextAttr* style):
5d7836c4
JS
6402 wxRichTextObject(parent)
6403{
5d7836c4
JS
6404 if (style)
6405 SetAttributes(*style);
6406
6407 m_text = text;
6408}
6409
cfa3b256
JS
6410#define USE_KERNING_FIX 1
6411
4794d69c
JS
6412// If insufficient tabs are defined, this is the tab width used
6413#define WIDTH_FOR_DEFAULT_TABS 50
6414
5d7836c4 6415/// Draw the item
8db2e3ef 6416bool wxRichTextPlainText::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int WXUNUSED(style))
5d7836c4 6417{
fe5aa22c
JS
6418 wxRichTextParagraph* para = wxDynamicCast(GetParent(), wxRichTextParagraph);
6419 wxASSERT (para != NULL);
6420
603f702b 6421 wxRichTextAttr textAttr(para ? para->GetCombinedAttributes(GetAttributes(), false /* no box attributes */) : GetAttributes());
8db2e3ef 6422 context.ApplyVirtualAttributes(textAttr, this);
603f702b
JS
6423
6424 // Let's make the assumption for now that for content in a paragraph, including
6425 // text, we never have a discontinuous selection. So we only deal with a
6426 // single range.
6427 wxRichTextRange selectionRange;
6428 if (selection.IsValid())
6429 {
6430 wxRichTextRangeArray selectionRanges = selection.GetSelectionForObject(this);
6431 if (selectionRanges.GetCount() > 0)
6432 selectionRange = selectionRanges[0];
6433 else
6434 selectionRange = wxRICHTEXT_NO_SELECTION;
6435 }
6436 else
6437 selectionRange = wxRICHTEXT_NO_SELECTION;
fe5aa22c 6438
5d7836c4
JS
6439 int offset = GetRange().GetStart();
6440
ff76711f 6441 wxString str = m_text;
f7667b84
JS
6442 if (context.HasVirtualText(this))
6443 {
6444 if (!context.GetVirtualText(this, str) || str.Length() != m_text.Length())
6445 str = m_text;
6446 }
6447
6448 // Replace line break characters with spaces
ff76711f
JS
6449 wxString toRemove = wxRichTextLineBreakChar;
6450 str.Replace(toRemove, wxT(" "));
d07f2e19 6451 if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS)))
c025e094 6452 str.MakeUpper();
3e541562 6453
5d7836c4 6454 long len = range.GetLength();
ff76711f 6455 wxString stringChunk = str.Mid(range.GetStart() - offset, (size_t) len);
5d7836c4 6456
5d7836c4
JS
6457 // Test for the optimized situations where all is selected, or none
6458 // is selected.
6459
30bf7630
JS
6460 wxFont textFont(GetBuffer()->GetFontTable().FindFont(textAttr));
6461 wxCheckSetFont(dc, textFont);
6462 int charHeight = dc.GetCharHeight();
6463
6464 int x, y;
a1b806b9 6465 if ( textFont.IsOk() )
30bf7630 6466 {
d07f2e19
JS
6467 if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SMALL_CAPITALS))
6468 {
6469 textFont.SetPointSize((int) (textFont.GetPointSize()*0.75));
6470 wxCheckSetFont(dc, textFont);
6471 charHeight = dc.GetCharHeight();
6472 }
6473
30bf7630
JS
6474 if ( textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT) )
6475 {
32423dd8
JS
6476 if (textFont.IsUsingSizeInPixels())
6477 {
6478 double size = static_cast<double>(textFont.GetPixelSize().y) / wxSCRIPT_MUL_FACTOR;
4ba36292 6479 textFont.SetPixelSize(wxSize(0, static_cast<int>(size)));
32423dd8
JS
6480 x = rect.x;
6481 y = rect.y;
6482 }
6483 else
6484 {
6485 double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
4ba36292 6486 textFont.SetPointSize(static_cast<int>(size));
32423dd8
JS
6487 x = rect.x;
6488 y = rect.y;
6489 }
30bf7630
JS
6490 wxCheckSetFont(dc, textFont);
6491 }
6492 else if ( textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT) )
6493 {
32423dd8
JS
6494 if (textFont.IsUsingSizeInPixels())
6495 {
6496 double size = static_cast<double>(textFont.GetPixelSize().y) / wxSCRIPT_MUL_FACTOR;
6497 textFont.SetPixelSize(wxSize(0, static_cast<int>(size)));
6498 x = rect.x;
4ba36292 6499 int sub_height = static_cast<int>(static_cast<double>(charHeight) / wxSCRIPT_MUL_FACTOR);
32423dd8
JS
6500 y = rect.y + (rect.height - sub_height + (descent - m_descent));
6501 }
6502 else
6503 {
6504 double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
4ba36292 6505 textFont.SetPointSize(static_cast<int>(size));
32423dd8 6506 x = rect.x;
4ba36292 6507 int sub_height = static_cast<int>(static_cast<double>(charHeight) / wxSCRIPT_MUL_FACTOR);
32423dd8
JS
6508 y = rect.y + (rect.height - sub_height + (descent - m_descent));
6509 }
30bf7630
JS
6510 wxCheckSetFont(dc, textFont);
6511 }
6512 else
6513 {
6514 x = rect.x;
6515 y = rect.y + (rect.height - charHeight - (descent - m_descent));
6516 }
6517 }
6518 else
6519 {
6520 x = rect.x;
6521 y = rect.y + (rect.height - charHeight - (descent - m_descent));
6522 }
5d7836c4 6523
603f702b
JS
6524 // TODO: new selection code
6525
5d7836c4
JS
6526 // (a) All selected.
6527 if (selectionRange.GetStart() <= range.GetStart() && selectionRange.GetEnd() >= range.GetEnd())
ab14c7aa 6528 {
fe5aa22c 6529 DrawTabbedString(dc, textAttr, rect, stringChunk, x, y, true);
5d7836c4
JS
6530 }
6531 // (b) None selected.
6532 else if (selectionRange.GetEnd() < range.GetStart() || selectionRange.GetStart() > range.GetEnd())
6533 {
6534 // Draw all unselected
fe5aa22c 6535 DrawTabbedString(dc, textAttr, rect, stringChunk, x, y, false);
5d7836c4
JS
6536 }
6537 else
6538 {
6539 // (c) Part selected, part not
6540 // Let's draw unselected chunk, selected chunk, then unselected chunk.
6541
04ee05f9 6542 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
7fe8059f 6543
5d7836c4
JS
6544 // 1. Initial unselected chunk, if any, up until start of selection.
6545 if (selectionRange.GetStart() > range.GetStart() && selectionRange.GetStart() <= range.GetEnd())
6546 {
6547 int r1 = range.GetStart();
6548 int s1 = selectionRange.GetStart()-1;
6549 int fragmentLen = s1 - r1 + 1;
6550 if (fragmentLen < 0)
af588446 6551 {
5d7836c4 6552 wxLogDebug(wxT("Mid(%d, %d"), (int)(r1 - offset), (int)fragmentLen);
af588446 6553 }
ff76711f 6554 wxString stringFragment = str.Mid(r1 - offset, fragmentLen);
5d7836c4 6555
fe5aa22c 6556 DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, false);
cfa3b256
JS
6557
6558#if USE_KERNING_FIX
6559 if (stringChunk.Find(wxT("\t")) == wxNOT_FOUND)
6560 {
6561 // Compensate for kerning difference
ff76711f
JS
6562 wxString stringFragment2(str.Mid(r1 - offset, fragmentLen+1));
6563 wxString stringFragment3(str.Mid(r1 - offset + fragmentLen, 1));
41a85215 6564
cfa3b256
JS
6565 wxCoord w1, h1, w2, h2, w3, h3;
6566 dc.GetTextExtent(stringFragment, & w1, & h1);
6567 dc.GetTextExtent(stringFragment2, & w2, & h2);
6568 dc.GetTextExtent(stringFragment3, & w3, & h3);
41a85215 6569
cfa3b256
JS
6570 int kerningDiff = (w1 + w3) - w2;
6571 x = x - kerningDiff;
6572 }
6573#endif
5d7836c4
JS
6574 }
6575
6576 // 2. Selected chunk, if any.
6577 if (selectionRange.GetEnd() >= range.GetStart())
6578 {
6579 int s1 = wxMax(selectionRange.GetStart(), range.GetStart());
6580 int s2 = wxMin(selectionRange.GetEnd(), range.GetEnd());
6581
6582 int fragmentLen = s2 - s1 + 1;
6583 if (fragmentLen < 0)
af588446 6584 {
5d7836c4 6585 wxLogDebug(wxT("Mid(%d, %d"), (int)(s1 - offset), (int)fragmentLen);
af588446 6586 }
ff76711f 6587 wxString stringFragment = str.Mid(s1 - offset, fragmentLen);
5d7836c4 6588
fe5aa22c 6589 DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, true);
cfa3b256
JS
6590
6591#if USE_KERNING_FIX
6592 if (stringChunk.Find(wxT("\t")) == wxNOT_FOUND)
6593 {
6594 // Compensate for kerning difference
ff76711f
JS
6595 wxString stringFragment2(str.Mid(s1 - offset, fragmentLen+1));
6596 wxString stringFragment3(str.Mid(s1 - offset + fragmentLen, 1));
41a85215 6597
cfa3b256
JS
6598 wxCoord w1, h1, w2, h2, w3, h3;
6599 dc.GetTextExtent(stringFragment, & w1, & h1);
6600 dc.GetTextExtent(stringFragment2, & w2, & h2);
6601 dc.GetTextExtent(stringFragment3, & w3, & h3);
41a85215 6602
cfa3b256
JS
6603 int kerningDiff = (w1 + w3) - w2;
6604 x = x - kerningDiff;
6605 }
6606#endif
5d7836c4
JS
6607 }
6608
6609 // 3. Remaining unselected chunk, if any
6610 if (selectionRange.GetEnd() < range.GetEnd())
6611 {
6612 int s2 = wxMin(selectionRange.GetEnd()+1, range.GetEnd());
6613 int r2 = range.GetEnd();
6614
6615 int fragmentLen = r2 - s2 + 1;
6616 if (fragmentLen < 0)
af588446 6617 {
5d7836c4 6618 wxLogDebug(wxT("Mid(%d, %d"), (int)(s2 - offset), (int)fragmentLen);
af588446 6619 }
ff76711f 6620 wxString stringFragment = str.Mid(s2 - offset, fragmentLen);
ab14c7aa 6621
fe5aa22c 6622 DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, false);
7fe8059f 6623 }
5d7836c4
JS
6624 }
6625
6626 return true;
6627}
61399247 6628
24777478 6629bool wxRichTextPlainText::DrawTabbedString(wxDC& dc, const wxRichTextAttr& attr, const wxRect& rect,wxString& str, wxCoord& x, wxCoord& y, bool selected)
7f0d9d71 6630{
cfa3b256
JS
6631 bool hasTabs = (str.Find(wxT('\t')) != wxNOT_FOUND);
6632
6633 wxArrayInt tabArray;
6634 int tabCount;
6635 if (hasTabs)
ab14c7aa 6636 {
cfa3b256
JS
6637 if (attr.GetTabs().IsEmpty())
6638 tabArray = wxRichTextParagraph::GetDefaultTabs();
6639 else
6640 tabArray = attr.GetTabs();
6641 tabCount = tabArray.GetCount();
6642
6643 for (int i = 0; i < tabCount; ++i)
ab14c7aa 6644 {
cfa3b256
JS
6645 int pos = tabArray[i];
6646 pos = ConvertTenthsMMToPixels(dc, pos);
6647 tabArray[i] = pos;
7f0d9d71
JS
6648 }
6649 }
cfa3b256
JS
6650 else
6651 tabCount = 0;
ab14c7aa 6652
cfa3b256
JS
6653 int nextTabPos = -1;
6654 int tabPos = -1;
7f0d9d71 6655 wxCoord w, h;
ab14c7aa 6656
cfa3b256 6657 if (selected)
ab14c7aa 6658 {
0ec6da02
JS
6659 wxColour highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
6660 wxColour highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
6661
ecb5fbf1
JS
6662 wxCheckSetBrush(dc, wxBrush(highlightColour));
6663 wxCheckSetPen(dc, wxPen(highlightColour));
0ec6da02 6664 dc.SetTextForeground(highlightTextColour);
04ee05f9 6665 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
7f0d9d71 6666 }
ab14c7aa
JS
6667 else
6668 {
fe5aa22c 6669 dc.SetTextForeground(attr.GetTextColour());
ab14c7aa 6670
f0e9eda2
JS
6671 if (attr.HasFlag(wxTEXT_ATTR_BACKGROUND_COLOUR) && attr.GetBackgroundColour().IsOk())
6672 {
04ee05f9 6673 dc.SetBackgroundMode(wxBRUSHSTYLE_SOLID);
f0e9eda2
JS
6674 dc.SetTextBackground(attr.GetBackgroundColour());
6675 }
6676 else
04ee05f9 6677 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
3e541562 6678 }
3e541562 6679
925a662a 6680 wxCoord x_orig = GetParent()->GetPosition().x;
cfa3b256 6681 while (hasTabs)
ab14c7aa
JS
6682 {
6683 // the string has a tab
7f0d9d71
JS
6684 // break up the string at the Tab
6685 wxString stringChunk = str.BeforeFirst(wxT('\t'));
6686 str = str.AfterFirst(wxT('\t'));
6687 dc.GetTextExtent(stringChunk, & w, & h);
cfa3b256 6688 tabPos = x + w;
7f0d9d71 6689 bool not_found = true;
cfa3b256 6690 for (int i = 0; i < tabCount && not_found; ++i)
ab14c7aa 6691 {
015d0446 6692 nextTabPos = tabArray.Item(i) + x_orig;
4794d69c
JS
6693
6694 // Find the next tab position.
6695 // Even if we're at the end of the tab array, we must still draw the chunk.
6696
6697 if (nextTabPos > tabPos || (i == (tabCount - 1)))
ab14c7aa 6698 {
4794d69c
JS
6699 if (nextTabPos <= tabPos)
6700 {
6701 int defaultTabWidth = ConvertTenthsMMToPixels(dc, WIDTH_FOR_DEFAULT_TABS);
6702 nextTabPos = tabPos + defaultTabWidth;
6703 }
6704
7f0d9d71 6705 not_found = false;
ab14c7aa
JS
6706 if (selected)
6707 {
cfa3b256 6708 w = nextTabPos - x;
7f0d9d71 6709 wxRect selRect(x, rect.y, w, rect.GetHeight());
61399247 6710 dc.DrawRectangle(selRect);
7f0d9d71
JS
6711 }
6712 dc.DrawText(stringChunk, x, y);
42688aea
JS
6713
6714 if (attr.HasTextEffects() && (attr.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH))
6715 {
6716 wxPen oldPen = dc.GetPen();
ecb5fbf1 6717 wxCheckSetPen(dc, wxPen(attr.GetTextColour(), 1));
42688aea 6718 dc.DrawLine(x, (int) (y+(h/2)+0.5), x+w, (int) (y+(h/2)+0.5));
ecb5fbf1 6719 wxCheckSetPen(dc, oldPen);
42688aea
JS
6720 }
6721
cfa3b256 6722 x = nextTabPos;
7f0d9d71
JS
6723 }
6724 }
cfa3b256 6725 hasTabs = (str.Find(wxT('\t')) != wxNOT_FOUND);
7f0d9d71 6726 }
61399247 6727
cfa3b256 6728 if (!str.IsEmpty())
ab14c7aa 6729 {
cfa3b256
JS
6730 dc.GetTextExtent(str, & w, & h);
6731 if (selected)
6732 {
6733 wxRect selRect(x, rect.y, w, rect.GetHeight());
6734 dc.DrawRectangle(selRect);
6735 }
6736 dc.DrawText(str, x, y);
42688aea
JS
6737
6738 if (attr.HasTextEffects() && (attr.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH))
6739 {
6740 wxPen oldPen = dc.GetPen();
ecb5fbf1 6741 wxCheckSetPen(dc, wxPen(attr.GetTextColour(), 1));
42688aea 6742 dc.DrawLine(x, (int) (y+(h/2)+0.5), x+w, (int) (y+(h/2)+0.5));
ecb5fbf1 6743 wxCheckSetPen(dc, oldPen);
42688aea
JS
6744 }
6745
cfa3b256 6746 x += w;
7f0d9d71 6747 }
5d7836c4 6748
7c9fdebe 6749 return true;
7f0d9d71 6750}
fe5aa22c 6751
5d7836c4 6752/// Lay the item out
8db2e3ef 6753bool wxRichTextPlainText::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& WXUNUSED(rect), const wxRect& WXUNUSED(parentRect), int WXUNUSED(style))
5d7836c4 6754{
ecb5fbf1
JS
6755 // Only lay out if we haven't already cached the size
6756 if (m_size.x == -1)
8db2e3ef 6757 GetRangeSize(GetRange(), m_size, m_descent, dc, context, 0, wxPoint(0, 0));
603f702b
JS
6758 m_maxSize = m_size;
6759 // Eventually we want to have a reasonable estimate of minimum size.
6760 m_minSize = wxSize(0, 0);
5d7836c4
JS
6761 return true;
6762}
6763
6764/// Copy
6765void wxRichTextPlainText::Copy(const wxRichTextPlainText& obj)
6766{
6767 wxRichTextObject::Copy(obj);
6768
6769 m_text = obj.m_text;
6770}
6771
6772/// Get/set the object size for the given range. Returns false if the range
6773/// is invalid for this object.
914a4e23 6774bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int WXUNUSED(flags), const wxPoint& position, const wxSize& WXUNUSED(parentSize), wxArrayInt* partialExtents) const
5d7836c4
JS
6775{
6776 if (!range.IsWithin(GetRange()))
6777 return false;
6778
fe5aa22c
JS
6779 wxRichTextParagraph* para = wxDynamicCast(GetParent(), wxRichTextParagraph);
6780 wxASSERT (para != NULL);
603f702b 6781
925a662a 6782 int relativeX = position.x - GetParent()->GetPosition().x;
fe5aa22c 6783
24777478 6784 wxRichTextAttr textAttr(para ? para->GetCombinedAttributes(GetAttributes()) : GetAttributes());
8db2e3ef 6785 context.ApplyVirtualAttributes(textAttr, (wxRichTextObject*) this);
fe5aa22c 6786
5d7836c4
JS
6787 // Always assume unformatted text, since at this level we have no knowledge
6788 // of line breaks - and we don't need it, since we'll calculate size within
6789 // formatted text by doing it in chunks according to the line ranges
6790
30bf7630 6791 bool bScript(false);
44cc96a8 6792 wxFont font(GetBuffer()->GetFontTable().FindFont(textAttr));
a1b806b9 6793 if (font.IsOk())
30bf7630
JS
6794 {
6795 if ( textAttr.HasTextEffects() && ( (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT)
6796 || (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT) ) )
6797 {
6798 wxFont textFont = font;
32423dd8
JS
6799 if (textFont.IsUsingSizeInPixels())
6800 {
6801 double size = static_cast<double>(textFont.GetPixelSize().y) / wxSCRIPT_MUL_FACTOR;
6802 textFont.SetPixelSize(wxSize(0, static_cast<int>(size)));
6803 }
6804 else
6805 {
6806 double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
6807 textFont.SetPointSize(static_cast<int>(size));
6808 }
30bf7630
JS
6809 wxCheckSetFont(dc, textFont);
6810 bScript = true;
6811 }
d07f2e19
JS
6812 else if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SMALL_CAPITALS))
6813 {
6814 wxFont textFont = font;
6815 textFont.SetPointSize((int) (textFont.GetPointSize()*0.75));
6816 wxCheckSetFont(dc, textFont);
6817 bScript = true;
6818 }
30bf7630
JS
6819 else
6820 {
6821 wxCheckSetFont(dc, font);
6822 }
6823 }
5d7836c4 6824
109bfc88 6825 bool haveDescent = false;
5d7836c4
JS
6826 int startPos = range.GetStart() - GetRange().GetStart();
6827 long len = range.GetLength();
3e541562 6828
ff76711f 6829 wxString str(m_text);
f7667b84
JS
6830 if (context.HasVirtualText(this))
6831 {
6832 if (!context.GetVirtualText(this, str) || str.Length() != m_text.Length())
6833 str = m_text;
6834 }
6835
ff76711f
JS
6836 wxString toReplace = wxRichTextLineBreakChar;
6837 str.Replace(toReplace, wxT(" "));
6838
6839 wxString stringChunk = str.Mid(startPos, (size_t) len);
42688aea 6840
d07f2e19 6841 if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS)))
42688aea
JS
6842 stringChunk.MakeUpper();
6843
5d7836c4 6844 wxCoord w, h;
7f0d9d71 6845 int width = 0;
cfa3b256 6846 if (stringChunk.Find(wxT('\t')) != wxNOT_FOUND)
ab14c7aa
JS
6847 {
6848 // the string has a tab
cfa3b256
JS
6849 wxArrayInt tabArray;
6850 if (textAttr.GetTabs().IsEmpty())
6851 tabArray = wxRichTextParagraph::GetDefaultTabs();
6852 else
6853 tabArray = textAttr.GetTabs();
ab14c7aa 6854
cfa3b256 6855 int tabCount = tabArray.GetCount();
41a85215 6856
cfa3b256 6857 for (int i = 0; i < tabCount; ++i)
61399247 6858 {
cfa3b256
JS
6859 int pos = tabArray[i];
6860 pos = ((wxRichTextPlainText*) this)->ConvertTenthsMMToPixels(dc, pos);
6861 tabArray[i] = pos;
7f0d9d71 6862 }
41a85215 6863
cfa3b256 6864 int nextTabPos = -1;
61399247 6865
ab14c7aa
JS
6866 while (stringChunk.Find(wxT('\t')) >= 0)
6867 {
109bfc88
JS
6868 int absoluteWidth = 0;
6869
ab14c7aa 6870 // the string has a tab
7f0d9d71
JS
6871 // break up the string at the Tab
6872 wxString stringFragment = stringChunk.BeforeFirst(wxT('\t'));
6873 stringChunk = stringChunk.AfterFirst(wxT('\t'));
4794d69c 6874
31778480
JS
6875 if (partialExtents)
6876 {
109bfc88
JS
6877 int oldWidth;
6878 if (partialExtents->GetCount() > 0)
6879 oldWidth = (*partialExtents)[partialExtents->GetCount()-1];
6880 else
6881 oldWidth = 0;
6882
31778480
JS
6883 // Add these partial extents
6884 wxArrayInt p;
6885 dc.GetPartialTextExtents(stringFragment, p);
6886 size_t j;
6887 for (j = 0; j < p.GetCount(); j++)
6888 partialExtents->Add(oldWidth + p[j]);
109bfc88
JS
6889
6890 if (partialExtents->GetCount() > 0)
925a662a 6891 absoluteWidth = (*partialExtents)[(*partialExtents).GetCount()-1] + relativeX;
109bfc88 6892 else
925a662a 6893 absoluteWidth = relativeX;
109bfc88
JS
6894 }
6895 else
6896 {
6897 dc.GetTextExtent(stringFragment, & w, & h);
6898 width += w;
603f702b 6899 absoluteWidth = width + relativeX;
109bfc88 6900 haveDescent = true;
31778480
JS
6901 }
6902
cfa3b256
JS
6903 bool notFound = true;
6904 for (int i = 0; i < tabCount && notFound; ++i)
ab14c7aa 6905 {
cfa3b256 6906 nextTabPos = tabArray.Item(i);
4794d69c
JS
6907
6908 // Find the next tab position.
6909 // Even if we're at the end of the tab array, we must still process the chunk.
6910
6911 if (nextTabPos > absoluteWidth || (i == (tabCount - 1)))
ab14c7aa 6912 {
4794d69c
JS
6913 if (nextTabPos <= absoluteWidth)
6914 {
6915 int defaultTabWidth = ((wxRichTextPlainText*) this)->ConvertTenthsMMToPixels(dc, WIDTH_FOR_DEFAULT_TABS);
6916 nextTabPos = absoluteWidth + defaultTabWidth;
6917 }
6918
cfa3b256 6919 notFound = false;
925a662a 6920 width = nextTabPos - relativeX;
31778480
JS
6921
6922 if (partialExtents)
6923 partialExtents->Add(width);
7f0d9d71
JS
6924 }
6925 }
6926 }
6927 }
30bf7630 6928
31778480
JS
6929 if (!stringChunk.IsEmpty())
6930 {
31778480
JS
6931 if (partialExtents)
6932 {
109bfc88
JS
6933 int oldWidth;
6934 if (partialExtents->GetCount() > 0)
6935 oldWidth = (*partialExtents)[partialExtents->GetCount()-1];
6936 else
6937 oldWidth = 0;
6938
31778480
JS
6939 // Add these partial extents
6940 wxArrayInt p;
6941 dc.GetPartialTextExtents(stringChunk, p);
6942 size_t j;
6943 for (j = 0; j < p.GetCount(); j++)
6944 partialExtents->Add(oldWidth + p[j]);
6945 }
109bfc88
JS
6946 else
6947 {
6948 dc.GetTextExtent(stringChunk, & w, & h, & descent);
6949 width += w;
6950 haveDescent = true;
6951 }
6952 }
6953
6954 if (partialExtents)
6955 {
6956 int charHeight = dc.GetCharHeight();
6957 if ((*partialExtents).GetCount() > 0)
6958 w = (*partialExtents)[partialExtents->GetCount()-1];
6959 else
6960 w = 0;
6961 size = wxSize(w, charHeight);
6962 }
6963 else
6964 {
6965 size = wxSize(width, dc.GetCharHeight());
31778480 6966 }
30bf7630 6967
109bfc88
JS
6968 if (!haveDescent)
6969 dc.GetTextExtent(wxT("X"), & w, & h, & descent);
6970
30bf7630
JS
6971 if ( bScript )
6972 dc.SetFont(font);
6973
5d7836c4
JS
6974 return true;
6975}
6976
6977/// Do a split, returning an object containing the second part, and setting
6978/// the first part in 'this'.
6979wxRichTextObject* wxRichTextPlainText::DoSplit(long pos)
6980{
ff76711f 6981 long index = pos - GetRange().GetStart();
3e541562 6982
28f92d74 6983 if (index < 0 || index >= (int) m_text.length())
5d7836c4
JS
6984 return NULL;
6985
6986 wxString firstPart = m_text.Mid(0, index);
6987 wxString secondPart = m_text.Mid(index);
6988
6989 m_text = firstPart;
6990
6991 wxRichTextPlainText* newObject = new wxRichTextPlainText(secondPart);
6992 newObject->SetAttributes(GetAttributes());
8db2e3ef 6993 newObject->SetProperties(GetProperties());
5d7836c4
JS
6994
6995 newObject->SetRange(wxRichTextRange(pos, GetRange().GetEnd()));
6996 GetRange().SetEnd(pos-1);
3e541562 6997
5d7836c4
JS
6998 return newObject;
6999}
7000
7001/// Calculate range
7002void wxRichTextPlainText::CalculateRange(long start, long& end)
7003{
28f92d74 7004 end = start + m_text.length() - 1;
5d7836c4
JS
7005 m_range.SetRange(start, end);
7006}
7007
7008/// Delete range
7009bool wxRichTextPlainText::DeleteRange(const wxRichTextRange& range)
7010{
7011 wxRichTextRange r = range;
7012
7013 r.LimitTo(GetRange());
7014
7015 if (r.GetStart() == GetRange().GetStart() && r.GetEnd() == GetRange().GetEnd())
7016 {
7017 m_text.Empty();
7018 return true;
7019 }
7020
7021 long startIndex = r.GetStart() - GetRange().GetStart();
7022 long len = r.GetLength();
7023
7024 m_text = m_text.Mid(0, startIndex) + m_text.Mid(startIndex+len);
7025 return true;
7026}
7027
7028/// Get text for the given range.
7029wxString wxRichTextPlainText::GetTextForRange(const wxRichTextRange& range) const
7030{
7031 wxRichTextRange r = range;
7032
7033 r.LimitTo(GetRange());
7034
7035 long startIndex = r.GetStart() - GetRange().GetStart();
7036 long len = r.GetLength();
7037
7038 return m_text.Mid(startIndex, len);
7039}
7040
7041/// Returns true if this object can merge itself with the given one.
f7667b84 7042bool wxRichTextPlainText::CanMerge(wxRichTextObject* object, wxRichTextDrawingContext& context) const
5d7836c4 7043{
f7667b84
JS
7044 // JACS 2013-01-27
7045 if (!context.GetVirtualAttributesEnabled())
7046 {
7047 return object->GetClassInfo() == wxCLASSINFO(wxRichTextPlainText) &&
7048 (m_text.empty() || (wxTextAttrEq(GetAttributes(), object->GetAttributes()) && m_properties == object->GetProperties()));
7049 }
7050 else
7051 {
7052 wxRichTextPlainText* otherObj = wxDynamicCast(object, wxRichTextPlainText);
7053 if (!otherObj || m_text.empty())
7054 return false;
7055
7056 if (!wxTextAttrEq(GetAttributes(), object->GetAttributes()) || !(m_properties == object->GetProperties()))
7057 return false;
7058
7059 // Check if differing virtual attributes makes it impossible to merge
7060 // these strings.
7061
7062 bool hasVirtualAttr1 = context.HasVirtualAttributes((wxRichTextObject*) this);
7063 bool hasVirtualAttr2 = context.HasVirtualAttributes((wxRichTextObject*) object);
7064 if (!hasVirtualAttr1 && !hasVirtualAttr2)
7065 return true;
7066 else if (hasVirtualAttr1 != hasVirtualAttr2)
7067 return false;
7068 else
7069 {
7070 wxRichTextAttr virtualAttr1 = context.GetVirtualAttributes((wxRichTextObject*) this);
7071 wxRichTextAttr virtualAttr2 = context.GetVirtualAttributes((wxRichTextObject*) object);
7072 return virtualAttr1 == virtualAttr2;
7073 }
7074 }
5d7836c4
JS
7075}
7076
7077/// Returns true if this object merged itself with the given one.
7078/// The calling code will then delete the given object.
f7667b84 7079bool wxRichTextPlainText::Merge(wxRichTextObject* object, wxRichTextDrawingContext& WXUNUSED(context))
5d7836c4
JS
7080{
7081 wxRichTextPlainText* textObject = wxDynamicCast(object, wxRichTextPlainText);
7082 wxASSERT( textObject != NULL );
7083
7084 if (textObject)
7085 {
7086 m_text += textObject->GetText();
99404ab0 7087 wxRichTextApplyStyle(m_attributes, textObject->GetAttributes());
5d7836c4
JS
7088 return true;
7089 }
7090 else
7091 return false;
7092}
7093
f7667b84
JS
7094bool wxRichTextPlainText::CanSplit(wxRichTextDrawingContext& context) const
7095{
7096 // If this object has any virtual attributes at all, whether for the whole object
7097 // or individual ones, we should try splitting it by calling Split.
7098 // Must be more than one character in order to be able to split.
7099 return m_text.Length() > 1 && context.HasVirtualAttributes((wxRichTextObject*) this);
7100}
7101
7102wxRichTextObject* wxRichTextPlainText::Split(wxRichTextDrawingContext& context)
7103{
7104 int count = context.GetVirtualSubobjectAttributesCount(this);
7105 if (count > 0 && GetParent())
7106 {
7107 wxRichTextCompositeObject* parent = wxDynamicCast(GetParent(), wxRichTextCompositeObject);
7108 wxRichTextObjectList::compatibility_iterator node = parent->GetChildren().Find(this);
7109 if (node)
7110 {
7111 const wxRichTextAttr emptyAttr;
7112 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
7113
7114 wxArrayInt positions;
7115 wxRichTextAttrArray attributes;
7116 if (context.GetVirtualSubobjectAttributes(this, positions, attributes) && positions.GetCount() > 0)
7117 {
7118 wxASSERT(positions.GetCount() == attributes.GetCount());
7119
7120 // We will gather up runs of text with the same virtual attributes
7121
7122 int len = m_text.Length();
7123 int i = 0;
7124
7125 // runStart and runEnd represent the accumulated run with a consistent attribute
7126 // that hasn't yet been appended
7127 int runStart = -1;
7128 int runEnd = -1;
7129 wxRichTextAttr currentAttr;
7130 wxString text = m_text;
7131 wxRichTextPlainText* lastPlainText = this;
7132
7133 for (i = 0; i < (int) positions.GetCount(); i++)
7134 {
7135 int pos = positions[i];
7136 wxASSERT(pos >= 0 && pos < len);
7137 if (pos >= 0 && pos < len)
7138 {
7139 const wxRichTextAttr& attr = attributes[i];
7140
7141 if (pos == 0)
7142 {
7143 runStart = 0;
7144 currentAttr = attr;
7145 }
7146 // Check if there was a gap from the last known attribute and this.
7147 // In that case, we need to do something with the span of non-attributed text.
7148 else if ((pos-1) > runEnd)
7149 {
7150 if (runEnd == -1)
7151 {
7152 // We hadn't processed anything previously, so the previous run is from the text start
7153 // to just before this position. The current attribute remains empty.
7154 runStart = 0;
7155 runEnd = pos-1;
7156 }
7157 else
7158 {
7159 // If the previous attribute matches the gap's attribute (i.e., no attributes)
7160 // then just extend the run.
7161 if (currentAttr.IsDefault())
7162 {
7163 runEnd = pos-1;
7164 }
7165 else
7166 {
7167 // We need to add an object, or reuse the existing one.
7168 if (runStart == 0)
7169 {
7170 lastPlainText = this;
7171 SetText(text.Mid(runStart, runEnd - runStart + 1));
7172 }
7173 else
7174 {
7175 wxRichTextPlainText* obj = new wxRichTextPlainText;
7176 lastPlainText = obj;
7177 obj->SetAttributes(GetAttributes());
7178 obj->SetProperties(GetProperties());
7179 obj->SetParent(parent);
7180
7181 obj->SetText(text.Mid(runStart, runEnd - runStart + 1));
7182 if (next)
7183 parent->GetChildren().Insert(next, obj);
7184 else
7185 parent->GetChildren().Append(obj);
7186 }
7187
7188 runStart = runEnd+1;
7189 runEnd = pos-1;
7190
7191 currentAttr = emptyAttr;
7192 }
7193 }
7194 }
7195
7196 wxASSERT(runEnd == pos-1);
7197
7198 // Now we only have to deal with the previous run
7199 if (currentAttr == attr)
7200 {
7201 // If we still have the same attributes, then we
7202 // simply increase the run size.
7203 runEnd = pos;
7204 }
7205 else
7206 {
7207 if (runEnd >= 0)
7208 {
7209 // We need to add an object, or reuse the existing one.
7210 if (runStart == 0)
7211 {
7212 lastPlainText = this;
7213 SetText(text.Mid(runStart, runEnd - runStart + 1));
7214 }
7215 else
7216 {
7217 wxRichTextPlainText* obj = new wxRichTextPlainText;
7218 lastPlainText = obj;
7219 obj->SetAttributes(GetAttributes());
7220 obj->SetProperties(GetProperties());
7221 obj->SetParent(parent);
7222
7223 obj->SetText(text.Mid(runStart, runEnd - runStart + 1));
7224 if (next)
7225 parent->GetChildren().Insert(next, obj);
7226 else
7227 parent->GetChildren().Append(obj);
7228 }
7229 }
7230
7231 runStart = pos;
7232 runEnd = pos;
7233
7234 currentAttr = attr;
7235 }
7236 }
7237 }
7238
7239 // We may still have a run to add, and possibly a no-attribute text fragment after that.
7240 // If the whole string was already a single attribute (the run covers the whole string), don't split.
7241 if ((runStart != -1) && !(runStart == 0 && runEnd == (len-1)))
7242 {
7243 // If the current attribute is empty, merge the run with the next fragment
7244 // which by definition (because it's not specified) has empty attributes.
7245 if (currentAttr.IsDefault())
7246 runEnd = (len-1);
7247
7248 if (runEnd < (len-1))
7249 {
7250 // We need to add an object, or reuse the existing one.
7251 if (runStart == 0)
7252 {
7253 lastPlainText = this;
7254 SetText(text.Mid(runStart, runEnd - runStart + 1));
7255 }
7256 else
7257 {
7258 wxRichTextPlainText* obj = new wxRichTextPlainText;
7259 lastPlainText = obj;
7260 obj->SetAttributes(GetAttributes());
7261 obj->SetProperties(GetProperties());
7262 obj->SetParent(parent);
7263
7264 obj->SetText(text.Mid(runStart, runEnd - runStart + 1));
7265 if (next)
7266 parent->GetChildren().Insert(next, obj);
7267 else
7268 parent->GetChildren().Append(obj);
7269 }
7270
7271 runStart = runEnd+1;
7272 runEnd = (len-1);
7273 }
7274
7275 // Now the last, non-attributed fragment at the end, if any
7276 if ((runStart < len) && !(runStart == 0 && runEnd == (len-1)))
7277 {
7278 wxASSERT(runStart != 0);
7279
7280 wxRichTextPlainText* obj = new wxRichTextPlainText;
7281 obj->SetAttributes(GetAttributes());
7282 obj->SetProperties(GetProperties());
7283 obj->SetParent(parent);
7284
7285 obj->SetText(text.Mid(runStart, runEnd - runStart + 1));
7286 if (next)
7287 parent->GetChildren().Insert(next, obj);
7288 else
7289 parent->GetChildren().Append(obj);
7290
7291 lastPlainText = obj;
7292 }
7293 }
7294
7295 return lastPlainText;
7296 }
7297 }
7298 }
7299 return this;
7300}
7301
5d7836c4
JS
7302/// Dump to output stream for debugging
7303void wxRichTextPlainText::Dump(wxTextOutputStream& stream)
7304{
7305 wxRichTextObject::Dump(stream);
7306 stream << m_text << wxT("\n");
7307}
7308
ff76711f
JS
7309/// Get the first position from pos that has a line break character.
7310long wxRichTextPlainText::GetFirstLineBreakPosition(long pos)
7311{
7312 int i;
7313 int len = m_text.length();
7314 int startPos = pos - m_range.GetStart();
7315 for (i = startPos; i < len; i++)
7316 {
7317 wxChar ch = m_text[i];
7318 if (ch == wxRichTextLineBreakChar)
7319 {
7320 return i + m_range.GetStart();
7321 }
7322 }
7323 return -1;
7324}
7325
5d7836c4
JS
7326/*!
7327 * wxRichTextBuffer
7328 * This is a kind of box, used to represent the whole buffer
7329 */
7330
7331IMPLEMENT_DYNAMIC_CLASS(wxRichTextBuffer, wxRichTextParagraphLayoutBox)
7332
7c9fdebe
JS
7333wxList wxRichTextBuffer::sm_handlers;
7334wxList wxRichTextBuffer::sm_drawingHandlers;
7335wxRichTextFieldTypeHashMap wxRichTextBuffer::sm_fieldTypes;
7336wxRichTextRenderer* wxRichTextBuffer::sm_renderer = NULL;
7337int wxRichTextBuffer::sm_bulletRightMargin = 20;
7338float wxRichTextBuffer::sm_bulletProportion = (float) 0.3;
e12b91a3 7339bool wxRichTextBuffer::sm_floatingLayoutMode = true;
5d7836c4
JS
7340
7341/// Initialisation
7342void wxRichTextBuffer::Init()
7343{
7344 m_commandProcessor = new wxCommandProcessor;
7345 m_styleSheet = NULL;
7346 m_modified = false;
7347 m_batchedCommandDepth = 0;
7348 m_batchedCommand = NULL;
7349 m_suppressUndo = 0;
d2d0adc7 7350 m_handlerFlags = 0;
44219ff0 7351 m_scale = 1.0;
32423dd8
JS
7352 m_dimensionScale = 1.0;
7353 m_fontScale = 1.0;
f819ed5d 7354 SetMargins(4);
5d7836c4
JS
7355}
7356
7357/// Initialisation
7358wxRichTextBuffer::~wxRichTextBuffer()
7359{
7360 delete m_commandProcessor;
7361 delete m_batchedCommand;
7362
7363 ClearStyleStack();
d2d0adc7 7364 ClearEventHandlers();
5d7836c4
JS
7365}
7366
85d8909b 7367void wxRichTextBuffer::ResetAndClearCommands()
5d7836c4 7368{
85d8909b 7369 Reset();
3e541562 7370
5d7836c4 7371 GetCommandProcessor()->ClearCommands();
5d7836c4 7372
5d7836c4 7373 Modify(false);
1e967276 7374 Invalidate(wxRICHTEXT_ALL);
5d7836c4
JS
7375}
7376
0ca07313
JS
7377void wxRichTextBuffer::Copy(const wxRichTextBuffer& obj)
7378{
7379 wxRichTextParagraphLayoutBox::Copy(obj);
7380
7381 m_styleSheet = obj.m_styleSheet;
7382 m_modified = obj.m_modified;
bec80f4f
JS
7383 m_batchedCommandDepth = 0;
7384 if (m_batchedCommand)
7385 delete m_batchedCommand;
7386 m_batchedCommand = NULL;
0ca07313 7387 m_suppressUndo = obj.m_suppressUndo;
603f702b 7388 m_invalidRange = obj.m_invalidRange;
32423dd8
JS
7389 m_dimensionScale = obj.m_dimensionScale;
7390 m_fontScale = obj.m_fontScale;
0ca07313
JS
7391}
7392
38f833b1
JS
7393/// Push style sheet to top of stack
7394bool wxRichTextBuffer::PushStyleSheet(wxRichTextStyleSheet* styleSheet)
7395{
7396 if (m_styleSheet)
7397 styleSheet->InsertSheet(m_styleSheet);
7398
7399 SetStyleSheet(styleSheet);
41a85215 7400
38f833b1
JS
7401 return true;
7402}
7403
7404/// Pop style sheet from top of stack
7405wxRichTextStyleSheet* wxRichTextBuffer::PopStyleSheet()
7406{
7407 if (m_styleSheet)
7408 {
7409 wxRichTextStyleSheet* oldSheet = m_styleSheet;
7410 m_styleSheet = oldSheet->GetNextSheet();
7411 oldSheet->Unlink();
41a85215 7412
38f833b1
JS
7413 return oldSheet;
7414 }
7415 else
7416 return NULL;
7417}
7418
0ca07313
JS
7419/// Submit command to insert paragraphs
7420bool wxRichTextBuffer::InsertParagraphsWithUndo(long pos, const wxRichTextParagraphLayoutBox& paragraphs, wxRichTextCtrl* ctrl, int flags)
7421{
4e63bfb9 7422 return ctrl->GetFocusObject()->InsertParagraphsWithUndo(this, pos, paragraphs, ctrl, flags);
603f702b
JS
7423}
7424
7425/// Submit command to insert paragraphs
4e63bfb9 7426bool wxRichTextParagraphLayoutBox::InsertParagraphsWithUndo(wxRichTextBuffer* buffer, long pos, const wxRichTextParagraphLayoutBox& paragraphs, wxRichTextCtrl* ctrl, int WXUNUSED(flags))
603f702b
JS
7427{
7428 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
0ca07313 7429
0ca07313 7430 action->GetNewParagraphs() = paragraphs;
59509217 7431
0ca07313
JS
7432 action->SetPosition(pos);
7433
603f702b 7434 wxRichTextRange range = wxRichTextRange(pos, pos + paragraphs.GetOwnRange().GetEnd() - 1);
99404ab0
JS
7435 if (!paragraphs.GetPartialParagraph())
7436 range.SetEnd(range.GetEnd()+1);
7437
0ca07313 7438 // Set the range we'll need to delete in Undo
99404ab0 7439 action->SetRange(range);
0ca07313 7440
603f702b 7441 buffer->SubmitAction(action);
0ca07313
JS
7442
7443 return true;
7444}
7445
5d7836c4 7446/// Submit command to insert the given text
fe5aa22c 7447bool wxRichTextBuffer::InsertTextWithUndo(long pos, const wxString& text, wxRichTextCtrl* ctrl, int flags)
5d7836c4 7448{
fca66f59
JS
7449 if (ctrl)
7450 return ctrl->GetFocusObject()->InsertTextWithUndo(this, pos, text, ctrl, flags);
7451 else
7452 return wxRichTextParagraphLayoutBox::InsertTextWithUndo(this, pos, text, ctrl, flags);
603f702b
JS
7453}
7454
7455/// Submit command to insert the given text
4e63bfb9 7456bool wxRichTextParagraphLayoutBox::InsertTextWithUndo(wxRichTextBuffer* buffer, long pos, const wxString& text, wxRichTextCtrl* ctrl, int flags)
603f702b
JS
7457{
7458 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
5d7836c4 7459
24777478
JS
7460 wxRichTextAttr* p = NULL;
7461 wxRichTextAttr paraAttr;
fe5aa22c
JS
7462 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7463 {
7c081bd2 7464 // Get appropriate paragraph style
603f702b 7465 paraAttr = GetStyleForNewParagraph(buffer, pos, false, false);
fe5aa22c
JS
7466 if (!paraAttr.IsDefault())
7467 p = & paraAttr;
7468 }
7469
fe5aa22c 7470 action->GetNewParagraphs().AddParagraphs(text, p);
0ca07313 7471
603f702b 7472 int length = action->GetNewParagraphs().GetOwnRange().GetLength();
0ca07313 7473
6636ef8d 7474 if (!text.empty() && text.Last() != wxT('\n'))
0ca07313
JS
7475 {
7476 // Don't count the newline when undoing
7477 length --;
5d7836c4 7478 action->GetNewParagraphs().SetPartialParagraph(true);
0ca07313 7479 }
6636ef8d 7480 else if (!text.empty() && text.Last() == wxT('\n'))
46ee0e5b 7481 length --;
5d7836c4
JS
7482
7483 action->SetPosition(pos);
7484
7485 // Set the range we'll need to delete in Undo
0ca07313 7486 action->SetRange(wxRichTextRange(pos, pos + length - 1));
7fe8059f 7487
603f702b 7488 buffer->SubmitAction(action);
7fe8059f 7489
5d7836c4
JS
7490 return true;
7491}
7492
7493/// Submit command to insert the given text
fe5aa22c 7494bool wxRichTextBuffer::InsertNewlineWithUndo(long pos, wxRichTextCtrl* ctrl, int flags)
5d7836c4 7495{
4e63bfb9 7496 return ctrl->GetFocusObject()->InsertNewlineWithUndo(this, pos, ctrl, flags);
603f702b
JS
7497}
7498
7499/// Submit command to insert the given text
4e63bfb9 7500bool wxRichTextParagraphLayoutBox::InsertNewlineWithUndo(wxRichTextBuffer* buffer, long pos, wxRichTextCtrl* ctrl, int flags)
603f702b
JS
7501{
7502 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
5d7836c4 7503
24777478
JS
7504 wxRichTextAttr* p = NULL;
7505 wxRichTextAttr paraAttr;
fe5aa22c
JS
7506 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7507 {
603f702b 7508 paraAttr = GetStyleForNewParagraph(buffer, pos, false, true /* look for next paragraph style */);
fe5aa22c
JS
7509 if (!paraAttr.IsDefault())
7510 p = & paraAttr;
7511 }
7512
603f702b 7513 wxRichTextAttr attr(buffer->GetDefaultStyle());
32423dd8
JS
7514 // Don't include box attributes such as margins
7515 attr.GetTextBoxAttr().Reset();
7fe8059f
WS
7516
7517 wxRichTextParagraph* newPara = new wxRichTextParagraph(wxEmptyString, this, & attr);
5d7836c4
JS
7518 action->GetNewParagraphs().AppendChild(newPara);
7519 action->GetNewParagraphs().UpdateRanges();
7520 action->GetNewParagraphs().SetPartialParagraph(false);
c025e094
JS
7521 wxRichTextParagraph* para = GetParagraphAtPosition(pos, false);
7522 long pos1 = pos;
7523
6c0ea513
JS
7524 if (p)
7525 newPara->SetAttributes(*p);
7526
c025e094
JS
7527 if (flags & wxRICHTEXT_INSERT_INTERACTIVE)
7528 {
7529 if (para && para->GetRange().GetEnd() == pos)
7530 pos1 ++;
e2d0875a
JS
7531
7532 // Now see if we need to number the paragraph.
6c0ea513 7533 if (newPara->GetAttributes().HasBulletNumber())
e2d0875a
JS
7534 {
7535 wxRichTextAttr numberingAttr;
7536 if (FindNextParagraphNumber(para, numberingAttr))
7537 wxRichTextApplyStyle(newPara->GetAttributes(), (const wxRichTextAttr&) numberingAttr);
7538 }
c025e094
JS
7539 }
7540
5d7836c4
JS
7541 action->SetPosition(pos);
7542
99404ab0 7543 // Use the default character style
603f702b 7544 if (!buffer->GetDefaultStyle().IsDefault() && newPara->GetChildren().GetFirst())
c025e094
JS
7545 {
7546 // Check whether the default style merely reflects the paragraph/basic style,
7547 // in which case don't apply it.
603f702b 7548 wxRichTextAttr defaultStyle(buffer->GetDefaultStyle());
32423dd8 7549 defaultStyle.GetTextBoxAttr().Reset();
24777478 7550 wxRichTextAttr toApply;
c025e094
JS
7551 if (para)
7552 {
603f702b 7553 wxRichTextAttr combinedAttr = para->GetCombinedAttributes(true /* include box attributes */);
24777478 7554 wxRichTextAttr newAttr;
c025e094
JS
7555 // This filters out attributes that are accounted for by the current
7556 // paragraph/basic style
7557 wxRichTextApplyStyle(toApply, defaultStyle, & combinedAttr);
7558 }
7559 else
7560 toApply = defaultStyle;
7561
7562 if (!toApply.IsDefault())
7563 newPara->GetChildren().GetFirst()->GetData()->SetAttributes(toApply);
7564 }
99404ab0 7565
5d7836c4 7566 // Set the range we'll need to delete in Undo
c025e094 7567 action->SetRange(wxRichTextRange(pos1, pos1));
7fe8059f 7568
603f702b 7569 buffer->SubmitAction(action);
7fe8059f 7570
5d7836c4
JS
7571 return true;
7572}
7573
7574/// Submit command to insert the given image
24777478
JS
7575bool wxRichTextBuffer::InsertImageWithUndo(long pos, const wxRichTextImageBlock& imageBlock, wxRichTextCtrl* ctrl, int flags,
7576 const wxRichTextAttr& textAttr)
5d7836c4 7577{
4e63bfb9 7578 return ctrl->GetFocusObject()->InsertImageWithUndo(this, pos, imageBlock, ctrl, flags, textAttr);
603f702b
JS
7579}
7580
7581/// Submit command to insert the given image
4e63bfb9
JS
7582bool wxRichTextParagraphLayoutBox::InsertImageWithUndo(wxRichTextBuffer* buffer, long pos, const wxRichTextImageBlock& imageBlock,
7583 wxRichTextCtrl* ctrl, int flags,
603f702b
JS
7584 const wxRichTextAttr& textAttr)
7585{
7586 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Image"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
5d7836c4 7587
24777478
JS
7588 wxRichTextAttr* p = NULL;
7589 wxRichTextAttr paraAttr;
fe5aa22c
JS
7590 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7591 {
603f702b 7592 paraAttr = GetStyleForNewParagraph(buffer, pos);
fe5aa22c
JS
7593 if (!paraAttr.IsDefault())
7594 p = & paraAttr;
7595 }
7596
603f702b 7597 wxRichTextAttr attr(buffer->GetDefaultStyle());
7fe8059f 7598
32423dd8
JS
7599 // Don't include box attributes such as margins
7600 attr.GetTextBoxAttr().Reset();
7601
5d7836c4 7602 wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
fe5aa22c
JS
7603 if (p)
7604 newPara->SetAttributes(*p);
7605
5d7836c4
JS
7606 wxRichTextImage* imageObject = new wxRichTextImage(imageBlock, newPara);
7607 newPara->AppendChild(imageObject);
24777478 7608 imageObject->SetAttributes(textAttr);
5d7836c4
JS
7609 action->GetNewParagraphs().AppendChild(newPara);
7610 action->GetNewParagraphs().UpdateRanges();
7611
7612 action->GetNewParagraphs().SetPartialParagraph(true);
7613
7614 action->SetPosition(pos);
7615
7616 // Set the range we'll need to delete in Undo
7617 action->SetRange(wxRichTextRange(pos, pos));
7fe8059f 7618
603f702b 7619 buffer->SubmitAction(action);
7fe8059f 7620
5d7836c4
JS
7621 return true;
7622}
7623
cdaed652 7624// Insert an object with no change of it
603f702b
JS
7625wxRichTextObject* wxRichTextBuffer::InsertObjectWithUndo(long pos, wxRichTextObject *object, wxRichTextCtrl* ctrl, int flags)
7626{
4e63bfb9 7627 return ctrl->GetFocusObject()->InsertObjectWithUndo(this, pos, object, ctrl, flags);
603f702b
JS
7628}
7629
7630// Insert an object with no change of it
4e63bfb9 7631wxRichTextObject* wxRichTextParagraphLayoutBox::InsertObjectWithUndo(wxRichTextBuffer* buffer, long pos, wxRichTextObject *object, wxRichTextCtrl* ctrl, int flags)
cdaed652 7632{
603f702b 7633 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Object"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
cdaed652 7634
24777478
JS
7635 wxRichTextAttr* p = NULL;
7636 wxRichTextAttr paraAttr;
cdaed652
VZ
7637 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7638 {
603f702b 7639 paraAttr = GetStyleForNewParagraph(buffer, pos);
cdaed652
VZ
7640 if (!paraAttr.IsDefault())
7641 p = & paraAttr;
7642 }
7643
603f702b 7644 wxRichTextAttr attr(buffer->GetDefaultStyle());
cdaed652 7645
32423dd8
JS
7646 // Don't include box attributes such as margins
7647 attr.GetTextBoxAttr().Reset();
7648
cdaed652
VZ
7649 wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
7650 if (p)
7651 newPara->SetAttributes(*p);
7652
7653 newPara->AppendChild(object);
7654 action->GetNewParagraphs().AppendChild(newPara);
7655 action->GetNewParagraphs().UpdateRanges();
7656
7657 action->GetNewParagraphs().SetPartialParagraph(true);
7658
7659 action->SetPosition(pos);
7660
7661 // Set the range we'll need to delete in Undo
7662 action->SetRange(wxRichTextRange(pos, pos));
7663
603f702b 7664 buffer->SubmitAction(action);
cdaed652 7665
603f702b
JS
7666 wxRichTextObject* obj = GetLeafObjectAtPosition(pos);
7667 return obj;
cdaed652 7668}
603f702b 7669
7c9fdebe
JS
7670wxRichTextField* wxRichTextParagraphLayoutBox::InsertFieldWithUndo(wxRichTextBuffer* buffer, long pos, const wxString& fieldType,
7671 const wxRichTextProperties& properties,
7672 wxRichTextCtrl* ctrl, int flags,
7673 const wxRichTextAttr& textAttr)
7674{
7675 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Field"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
7676
7677 wxRichTextAttr* p = NULL;
7678 wxRichTextAttr paraAttr;
7679 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7680 {
7681 paraAttr = GetStyleForNewParagraph(buffer, pos);
7682 if (!paraAttr.IsDefault())
7683 p = & paraAttr;
7684 }
7685
7686 wxRichTextAttr attr(buffer->GetDefaultStyle());
7687
32423dd8
JS
7688 // Don't include box attributes such as margins
7689 attr.GetTextBoxAttr().Reset();
7690
7c9fdebe
JS
7691 wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
7692 if (p)
7693 newPara->SetAttributes(*p);
7694
7695 wxRichTextField* fieldObject = new wxRichTextField();
7696 fieldObject->wxRichTextObject::SetProperties(properties);
7697 fieldObject->SetFieldType(fieldType);
7698 fieldObject->SetAttributes(textAttr);
7699 newPara->AppendChild(fieldObject);
7700 action->GetNewParagraphs().AppendChild(newPara);
7701 action->GetNewParagraphs().UpdateRanges();
7702 action->GetNewParagraphs().SetPartialParagraph(true);
7703 action->SetPosition(pos);
7704
7705 // Set the range we'll need to delete in Undo
7706 action->SetRange(wxRichTextRange(pos, pos));
7707
7708 buffer->SubmitAction(action);
7709
7710 wxRichTextField* obj = wxDynamicCast(GetLeafObjectAtPosition(pos), wxRichTextField);
7711 return obj;
7712}
7713
fe5aa22c
JS
7714/// Get the style that is appropriate for a new paragraph at this position.
7715/// If the previous paragraph has a paragraph style name, look up the next-paragraph
7716/// style.
603f702b 7717wxRichTextAttr wxRichTextParagraphLayoutBox::GetStyleForNewParagraph(wxRichTextBuffer* buffer, long pos, bool caretPosition, bool lookUpNewParaStyle) const
fe5aa22c
JS
7718{
7719 wxRichTextParagraph* para = GetParagraphAtPosition(pos, caretPosition);
7720 if (para)
7721 {
24777478 7722 wxRichTextAttr attr;
d2d0adc7 7723 bool foundAttributes = false;
3e541562 7724
d2d0adc7 7725 // Look for a matching paragraph style
603f702b 7726 if (lookUpNewParaStyle && !para->GetAttributes().GetParagraphStyleName().IsEmpty() && buffer->GetStyleSheet())
fe5aa22c 7727 {
603f702b 7728 wxRichTextParagraphStyleDefinition* paraDef = buffer->GetStyleSheet()->FindParagraphStyle(para->GetAttributes().GetParagraphStyleName());
d2d0adc7 7729 if (paraDef)
fe5aa22c 7730 {
caad0109
JS
7731 // If we're not at the end of the paragraph, then we apply THIS style, and not the designated next style.
7732 if (para->GetRange().GetEnd() == pos && !paraDef->GetNextStyle().IsEmpty())
d2d0adc7 7733 {
603f702b 7734 wxRichTextParagraphStyleDefinition* nextParaDef = buffer->GetStyleSheet()->FindParagraphStyle(paraDef->GetNextStyle());
d2d0adc7
JS
7735 if (nextParaDef)
7736 {
7737 foundAttributes = true;
603f702b 7738 attr = nextParaDef->GetStyleMergedWithBase(buffer->GetStyleSheet());
d2d0adc7
JS
7739 }
7740 }
3e541562 7741
d2d0adc7
JS
7742 // If we didn't find the 'next style', use this style instead.
7743 if (!foundAttributes)
7744 {
7745 foundAttributes = true;
603f702b 7746 attr = paraDef->GetStyleMergedWithBase(buffer->GetStyleSheet());
d2d0adc7 7747 }
fe5aa22c
JS
7748 }
7749 }
e2d0875a
JS
7750
7751 // Also apply list style if present
603f702b 7752 if (lookUpNewParaStyle && !para->GetAttributes().GetListStyleName().IsEmpty() && buffer->GetStyleSheet())
e2d0875a 7753 {
603f702b 7754 wxRichTextListStyleDefinition* listDef = buffer->GetStyleSheet()->FindListStyle(para->GetAttributes().GetListStyleName());
e2d0875a
JS
7755 if (listDef)
7756 {
7757 int thisIndent = para->GetAttributes().GetLeftIndent();
7758 int thisLevel = para->GetAttributes().HasOutlineLevel() ? para->GetAttributes().GetOutlineLevel() : listDef->FindLevelForIndent(thisIndent);
7759
7760 // Apply the overall list style, and item style for this level
603f702b 7761 wxRichTextAttr listStyle(listDef->GetCombinedStyleForLevel(thisLevel, buffer->GetStyleSheet()));
e2d0875a
JS
7762 wxRichTextApplyStyle(attr, listStyle);
7763 attr.SetOutlineLevel(thisLevel);
7764 if (para->GetAttributes().HasBulletNumber())
7765 attr.SetBulletNumber(para->GetAttributes().GetBulletNumber());
7766 }
34b4899d 7767 }
e2d0875a 7768
d2d0adc7
JS
7769 if (!foundAttributes)
7770 {
7771 attr = para->GetAttributes();
7772 int flags = attr.GetFlags();
fe5aa22c 7773
d2d0adc7
JS
7774 // Eliminate character styles
7775 flags &= ( (~ wxTEXT_ATTR_FONT) |
fe5aa22c
JS
7776 (~ wxTEXT_ATTR_TEXT_COLOUR) |
7777 (~ wxTEXT_ATTR_BACKGROUND_COLOUR) );
d2d0adc7
JS
7778 attr.SetFlags(flags);
7779 }
3e541562 7780
fe5aa22c
JS
7781 return attr;
7782 }
7783 else
24777478 7784 return wxRichTextAttr();
fe5aa22c
JS
7785}
7786
5d7836c4 7787/// Submit command to delete this range
12cc29c5 7788bool wxRichTextBuffer::DeleteRangeWithUndo(const wxRichTextRange& range, wxRichTextCtrl* ctrl)
5d7836c4 7789{
603f702b
JS
7790 return ctrl->GetFocusObject()->DeleteRangeWithUndo(range, ctrl, this);
7791}
7792
7793/// Submit command to delete this range
7794bool wxRichTextParagraphLayoutBox::DeleteRangeWithUndo(const wxRichTextRange& range, wxRichTextCtrl* ctrl, wxRichTextBuffer* buffer)
7795{
7796 wxRichTextAction* action = new wxRichTextAction(NULL, _("Delete"), wxRICHTEXT_DELETE, buffer, this, ctrl);
7fe8059f 7797
12cc29c5 7798 action->SetPosition(ctrl->GetCaretPosition());
5d7836c4
JS
7799
7800 // Set the range to delete
7801 action->SetRange(range);
7fe8059f 7802
5d7836c4
JS
7803 // Copy the fragment that we'll need to restore in Undo
7804 CopyFragment(range, action->GetOldParagraphs());
7805
6c0ea513
JS
7806 // See if we're deleting a paragraph marker, in which case we need to
7807 // make a note not to copy the attributes from the 2nd paragraph to the 1st.
7808 if (range.GetStart() == range.GetEnd())
5d7836c4 7809 {
6c0ea513
JS
7810 wxRichTextParagraph* para = GetParagraphAtPosition(range.GetStart());
7811 if (para && para->GetRange().GetEnd() == range.GetEnd())
5d7836c4 7812 {
6c0ea513
JS
7813 wxRichTextParagraph* nextPara = GetParagraphAtPosition(range.GetStart()+1);
7814 if (nextPara && nextPara != para)
5d7836c4 7815 {
6c0ea513
JS
7816 action->GetOldParagraphs().GetChildren().GetFirst()->GetData()->SetAttributes(nextPara->GetAttributes());
7817 action->GetOldParagraphs().GetAttributes().SetFlags(action->GetOldParagraphs().GetAttributes().GetFlags() | wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE);
5d7836c4
JS
7818 }
7819 }
7820 }
7821
603f702b 7822 buffer->SubmitAction(action);
7fe8059f 7823
5d7836c4
JS
7824 return true;
7825}
7826
7827/// Collapse undo/redo commands
7828bool wxRichTextBuffer::BeginBatchUndo(const wxString& cmdName)
7829{
7830 if (m_batchedCommandDepth == 0)
7831 {
7832 wxASSERT(m_batchedCommand == NULL);
7833 if (m_batchedCommand)
7834 {
0745f364 7835 GetCommandProcessor()->Store(m_batchedCommand);
5d7836c4
JS
7836 }
7837 m_batchedCommand = new wxRichTextCommand(cmdName);
7838 }
7839
7fe8059f 7840 m_batchedCommandDepth ++;
5d7836c4
JS
7841
7842 return true;
7843}
7844
7845/// Collapse undo/redo commands
7846bool wxRichTextBuffer::EndBatchUndo()
7847{
7848 m_batchedCommandDepth --;
7849
7850 wxASSERT(m_batchedCommandDepth >= 0);
7851 wxASSERT(m_batchedCommand != NULL);
7852
7853 if (m_batchedCommandDepth == 0)
7854 {
0745f364 7855 GetCommandProcessor()->Store(m_batchedCommand);
5d7836c4
JS
7856 m_batchedCommand = NULL;
7857 }
7858
7859 return true;
7860}
7861
7862/// Submit immediately, or delay according to whether collapsing is on
7863bool wxRichTextBuffer::SubmitAction(wxRichTextAction* action)
7864{
cc2aecde
JS
7865 if (action && !action->GetNewParagraphs().IsEmpty())
7866 PrepareContent(action->GetNewParagraphs());
7867
5d7836c4 7868 if (BatchingUndo() && m_batchedCommand && !SuppressingUndo())
0745f364
JS
7869 {
7870 wxRichTextCommand* cmd = new wxRichTextCommand(action->GetName());
7871 cmd->AddAction(action);
7872 cmd->Do();
7873 cmd->GetActions().Clear();
7874 delete cmd;
7875
5d7836c4 7876 m_batchedCommand->AddAction(action);
0745f364 7877 }
5d7836c4
JS
7878 else
7879 {
7880 wxRichTextCommand* cmd = new wxRichTextCommand(action->GetName());
7881 cmd->AddAction(action);
7882
7883 // Only store it if we're not suppressing undo.
7884 return GetCommandProcessor()->Submit(cmd, !SuppressingUndo());
7885 }
7886
7887 return true;
7888}
7889
7890/// Begin suppressing undo/redo commands.
7891bool wxRichTextBuffer::BeginSuppressUndo()
7892{
7fe8059f 7893 m_suppressUndo ++;
5d7836c4
JS
7894
7895 return true;
7896}
7897
7898/// End suppressing undo/redo commands.
7899bool wxRichTextBuffer::EndSuppressUndo()
7900{
7fe8059f 7901 m_suppressUndo --;
5d7836c4
JS
7902
7903 return true;
7904}
7905
7906/// Begin using a style
24777478 7907bool wxRichTextBuffer::BeginStyle(const wxRichTextAttr& style)
5d7836c4 7908{
24777478 7909 wxRichTextAttr newStyle(GetDefaultStyle());
32423dd8 7910 newStyle.GetTextBoxAttr().Reset();
5d7836c4
JS
7911
7912 // Save the old default style
32423dd8 7913 m_attributeStack.Append((wxObject*) new wxRichTextAttr(newStyle));
5d7836c4
JS
7914
7915 wxRichTextApplyStyle(newStyle, style);
7916 newStyle.SetFlags(style.GetFlags()|newStyle.GetFlags());
7917
7918 SetDefaultStyle(newStyle);
7919
5d7836c4
JS
7920 return true;
7921}
7922
7923/// End the style
7924bool wxRichTextBuffer::EndStyle()
7925{
63886f6d 7926 if (!m_attributeStack.GetFirst())
5d7836c4
JS
7927 {
7928 wxLogDebug(_("Too many EndStyle calls!"));
7929 return false;
7930 }
7931
09f14108 7932 wxList::compatibility_iterator node = m_attributeStack.GetLast();
24777478 7933 wxRichTextAttr* attr = (wxRichTextAttr*)node->GetData();
9e31a660 7934 m_attributeStack.Erase(node);
5d7836c4
JS
7935
7936 SetDefaultStyle(*attr);
7937
7938 delete attr;
7939 return true;
7940}
7941
7942/// End all styles
7943bool wxRichTextBuffer::EndAllStyles()
7944{
7945 while (m_attributeStack.GetCount() != 0)
7946 EndStyle();
7947 return true;
7948}
7949
7950/// Clear the style stack
7951void wxRichTextBuffer::ClearStyleStack()
7952{
09f14108 7953 for (wxList::compatibility_iterator node = m_attributeStack.GetFirst(); node; node = node->GetNext())
24777478 7954 delete (wxRichTextAttr*) node->GetData();
5d7836c4
JS
7955 m_attributeStack.Clear();
7956}
7957
7958/// Begin using bold
7959bool wxRichTextBuffer::BeginBold()
7960{
24777478 7961 wxRichTextAttr attr;
7d76fbd5 7962 attr.SetFontWeight(wxFONTWEIGHT_BOLD);
7fe8059f 7963
5d7836c4
JS
7964 return BeginStyle(attr);
7965}
7966
7967/// Begin using italic
7968bool wxRichTextBuffer::BeginItalic()
7969{
24777478 7970 wxRichTextAttr attr;
7d76fbd5 7971 attr.SetFontStyle(wxFONTSTYLE_ITALIC);
7fe8059f 7972
5d7836c4
JS
7973 return BeginStyle(attr);
7974}
7975
7976/// Begin using underline
7977bool wxRichTextBuffer::BeginUnderline()
7978{
24777478 7979 wxRichTextAttr attr;
44cc96a8 7980 attr.SetFontUnderlined(true);
7fe8059f 7981
5d7836c4
JS
7982 return BeginStyle(attr);
7983}
7984
7985/// Begin using point size
7986bool wxRichTextBuffer::BeginFontSize(int pointSize)
7987{
24777478 7988 wxRichTextAttr attr;
44cc96a8 7989 attr.SetFontSize(pointSize);
7fe8059f 7990
5d7836c4
JS
7991 return BeginStyle(attr);
7992}
7993
7994/// Begin using this font
7995bool wxRichTextBuffer::BeginFont(const wxFont& font)
7996{
24777478 7997 wxRichTextAttr attr;
5d7836c4 7998 attr.SetFont(font);
7fe8059f 7999
5d7836c4
JS
8000 return BeginStyle(attr);
8001}
8002
8003/// Begin using this colour
8004bool wxRichTextBuffer::BeginTextColour(const wxColour& colour)
8005{
24777478 8006 wxRichTextAttr attr;
5d7836c4
JS
8007 attr.SetFlags(wxTEXT_ATTR_TEXT_COLOUR);
8008 attr.SetTextColour(colour);
7fe8059f 8009
5d7836c4
JS
8010 return BeginStyle(attr);
8011}
8012
8013/// Begin using alignment
8014bool wxRichTextBuffer::BeginAlignment(wxTextAttrAlignment alignment)
8015{
24777478 8016 wxRichTextAttr attr;
5d7836c4
JS
8017 attr.SetFlags(wxTEXT_ATTR_ALIGNMENT);
8018 attr.SetAlignment(alignment);
7fe8059f 8019
5d7836c4
JS
8020 return BeginStyle(attr);
8021}
8022
8023/// Begin left indent
8024bool wxRichTextBuffer::BeginLeftIndent(int leftIndent, int leftSubIndent)
8025{
24777478 8026 wxRichTextAttr attr;
5d7836c4
JS
8027 attr.SetFlags(wxTEXT_ATTR_LEFT_INDENT);
8028 attr.SetLeftIndent(leftIndent, leftSubIndent);
7fe8059f 8029
5d7836c4
JS
8030 return BeginStyle(attr);
8031}
8032
8033/// Begin right indent
8034bool wxRichTextBuffer::BeginRightIndent(int rightIndent)
8035{
24777478 8036 wxRichTextAttr attr;
5d7836c4
JS
8037 attr.SetFlags(wxTEXT_ATTR_RIGHT_INDENT);
8038 attr.SetRightIndent(rightIndent);
7fe8059f 8039
5d7836c4
JS
8040 return BeginStyle(attr);
8041}
8042
8043/// Begin paragraph spacing
8044bool wxRichTextBuffer::BeginParagraphSpacing(int before, int after)
8045{
8046 long flags = 0;
8047 if (before != 0)
8048 flags |= wxTEXT_ATTR_PARA_SPACING_BEFORE;
8049 if (after != 0)
8050 flags |= wxTEXT_ATTR_PARA_SPACING_AFTER;
8051
24777478 8052 wxRichTextAttr attr;
5d7836c4
JS
8053 attr.SetFlags(flags);
8054 attr.SetParagraphSpacingBefore(before);
8055 attr.SetParagraphSpacingAfter(after);
7fe8059f 8056
5d7836c4
JS
8057 return BeginStyle(attr);
8058}
8059
8060/// Begin line spacing
8061bool wxRichTextBuffer::BeginLineSpacing(int lineSpacing)
8062{
24777478 8063 wxRichTextAttr attr;
5d7836c4
JS
8064 attr.SetFlags(wxTEXT_ATTR_LINE_SPACING);
8065 attr.SetLineSpacing(lineSpacing);
7fe8059f 8066
5d7836c4
JS
8067 return BeginStyle(attr);
8068}
8069
8070/// Begin numbered bullet
8071bool wxRichTextBuffer::BeginNumberedBullet(int bulletNumber, int leftIndent, int leftSubIndent, int bulletStyle)
8072{
24777478 8073 wxRichTextAttr attr;
f089713f 8074 attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
5d7836c4
JS
8075 attr.SetBulletStyle(bulletStyle);
8076 attr.SetBulletNumber(bulletNumber);
8077 attr.SetLeftIndent(leftIndent, leftSubIndent);
7fe8059f 8078
5d7836c4
JS
8079 return BeginStyle(attr);
8080}
8081
8082/// Begin symbol bullet
d2d0adc7 8083bool wxRichTextBuffer::BeginSymbolBullet(const wxString& symbol, int leftIndent, int leftSubIndent, int bulletStyle)
5d7836c4 8084{
24777478 8085 wxRichTextAttr attr;
f089713f 8086 attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
5d7836c4
JS
8087 attr.SetBulletStyle(bulletStyle);
8088 attr.SetLeftIndent(leftIndent, leftSubIndent);
d2d0adc7 8089 attr.SetBulletText(symbol);
7fe8059f 8090
5d7836c4
JS
8091 return BeginStyle(attr);
8092}
8093
f089713f
JS
8094/// Begin standard bullet
8095bool wxRichTextBuffer::BeginStandardBullet(const wxString& bulletName, int leftIndent, int leftSubIndent, int bulletStyle)
8096{
24777478 8097 wxRichTextAttr attr;
f089713f
JS
8098 attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
8099 attr.SetBulletStyle(bulletStyle);
8100 attr.SetLeftIndent(leftIndent, leftSubIndent);
8101 attr.SetBulletName(bulletName);
8102
8103 return BeginStyle(attr);
8104}
8105
5d7836c4
JS
8106/// Begin named character style
8107bool wxRichTextBuffer::BeginCharacterStyle(const wxString& characterStyle)
8108{
8109 if (GetStyleSheet())
8110 {
8111 wxRichTextCharacterStyleDefinition* def = GetStyleSheet()->FindCharacterStyle(characterStyle);
8112 if (def)
8113 {
24777478 8114 wxRichTextAttr attr = def->GetStyleMergedWithBase(GetStyleSheet());
5d7836c4
JS
8115 return BeginStyle(attr);
8116 }
8117 }
8118 return false;
8119}
8120
8121/// Begin named paragraph style
8122bool wxRichTextBuffer::BeginParagraphStyle(const wxString& paragraphStyle)
8123{
8124 if (GetStyleSheet())
8125 {
8126 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(paragraphStyle);
8127 if (def)
8128 {
24777478 8129 wxRichTextAttr attr = def->GetStyleMergedWithBase(GetStyleSheet());
5d7836c4
JS
8130 return BeginStyle(attr);
8131 }
8132 }
8133 return false;
8134}
8135
f089713f
JS
8136/// Begin named list style
8137bool wxRichTextBuffer::BeginListStyle(const wxString& listStyle, int level, int number)
8138{
8139 if (GetStyleSheet())
8140 {
8141 wxRichTextListStyleDefinition* def = GetStyleSheet()->FindListStyle(listStyle);
8142 if (def)
8143 {
24777478 8144 wxRichTextAttr attr(def->GetCombinedStyleForLevel(level));
f089713f
JS
8145
8146 attr.SetBulletNumber(number);
8147
8148 return BeginStyle(attr);
8149 }
8150 }
8151 return false;
8152}
8153
d2d0adc7
JS
8154/// Begin URL
8155bool wxRichTextBuffer::BeginURL(const wxString& url, const wxString& characterStyle)
8156{
24777478 8157 wxRichTextAttr attr;
d2d0adc7
JS
8158
8159 if (!characterStyle.IsEmpty() && GetStyleSheet())
8160 {
8161 wxRichTextCharacterStyleDefinition* def = GetStyleSheet()->FindCharacterStyle(characterStyle);
8162 if (def)
8163 {
336d8ae9 8164 attr = def->GetStyleMergedWithBase(GetStyleSheet());
d2d0adc7
JS
8165 }
8166 }
8167 attr.SetURL(url);
8168
8169 return BeginStyle(attr);
8170}
8171
5d7836c4
JS
8172/// Adds a handler to the end
8173void wxRichTextBuffer::AddHandler(wxRichTextFileHandler *handler)
8174{
8175 sm_handlers.Append(handler);
8176}
8177
8178/// Inserts a handler at the front
8179void wxRichTextBuffer::InsertHandler(wxRichTextFileHandler *handler)
8180{
8181 sm_handlers.Insert( handler );
8182}
8183
8184/// Removes a handler
8185bool wxRichTextBuffer::RemoveHandler(const wxString& name)
8186{
8187 wxRichTextFileHandler *handler = FindHandler(name);
8188 if (handler)
8189 {
8190 sm_handlers.DeleteObject(handler);
8191 delete handler;
8192 return true;
8193 }
8194 else
8195 return false;
8196}
8197
8198/// Finds a handler by filename or, if supplied, type
d75a69e8
FM
8199wxRichTextFileHandler *wxRichTextBuffer::FindHandlerFilenameOrType(const wxString& filename,
8200 wxRichTextFileType imageType)
5d7836c4
JS
8201{
8202 if (imageType != wxRICHTEXT_TYPE_ANY)
8203 return FindHandler(imageType);
0ca07313 8204 else if (!filename.IsEmpty())
5d7836c4
JS
8205 {
8206 wxString path, file, ext;
a51e601e 8207 wxFileName::SplitPath(filename, & path, & file, & ext);
5d7836c4
JS
8208 return FindHandler(ext, imageType);
8209 }
0ca07313
JS
8210 else
8211 return NULL;
5d7836c4
JS
8212}
8213
8214
8215/// Finds a handler by name
8216wxRichTextFileHandler* wxRichTextBuffer::FindHandler(const wxString& name)
8217{
8218 wxList::compatibility_iterator node = sm_handlers.GetFirst();
8219 while (node)
8220 {
8221 wxRichTextFileHandler *handler = (wxRichTextFileHandler*)node->GetData();
8222 if (handler->GetName().Lower() == name.Lower()) return handler;
8223
8224 node = node->GetNext();
8225 }
8226 return NULL;
8227}
8228
8229/// Finds a handler by extension and type
d75a69e8 8230wxRichTextFileHandler* wxRichTextBuffer::FindHandler(const wxString& extension, wxRichTextFileType type)
5d7836c4
JS
8231{
8232 wxList::compatibility_iterator node = sm_handlers.GetFirst();
8233 while (node)
8234 {
8235 wxRichTextFileHandler *handler = (wxRichTextFileHandler*)node->GetData();
8236 if ( handler->GetExtension().Lower() == extension.Lower() &&
8237 (type == wxRICHTEXT_TYPE_ANY || handler->GetType() == type) )
8238 return handler;
8239 node = node->GetNext();
8240 }
8241 return 0;
8242}
8243
8244/// Finds a handler by type
d75a69e8 8245wxRichTextFileHandler* wxRichTextBuffer::FindHandler(wxRichTextFileType type)
5d7836c4
JS
8246{
8247 wxList::compatibility_iterator node = sm_handlers.GetFirst();
8248 while (node)
8249 {
8250 wxRichTextFileHandler *handler = (wxRichTextFileHandler *)node->GetData();
8251 if (handler->GetType() == type) return handler;
8252 node = node->GetNext();
8253 }
8254 return NULL;
8255}
8256
8257void wxRichTextBuffer::InitStandardHandlers()
8258{
8259 if (!FindHandler(wxRICHTEXT_TYPE_TEXT))
8260 AddHandler(new wxRichTextPlainTextHandler);
8261}
8262
8263void wxRichTextBuffer::CleanUpHandlers()
8264{
8265 wxList::compatibility_iterator node = sm_handlers.GetFirst();
8266 while (node)
8267 {
8268 wxRichTextFileHandler* handler = (wxRichTextFileHandler*)node->GetData();
8269 wxList::compatibility_iterator next = node->GetNext();
8270 delete handler;
8271 node = next;
8272 }
8273
8274 sm_handlers.Clear();
8275}
8276
1e967276 8277wxString wxRichTextBuffer::GetExtWildcard(bool combine, bool save, wxArrayInt* types)
5d7836c4 8278{
1e967276
JS
8279 if (types)
8280 types->Clear();
8281
5d7836c4
JS
8282 wxString wildcard;
8283
8284 wxList::compatibility_iterator node = GetHandlers().GetFirst();
8285 int count = 0;
8286 while (node)
8287 {
8288 wxRichTextFileHandler* handler = (wxRichTextFileHandler*) node->GetData();
2a230426 8289 if (handler->IsVisible() && ((save && handler->CanSave()) || (!save && handler->CanLoad())))
5d7836c4
JS
8290 {
8291 if (combine)
8292 {
8293 if (count > 0)
8294 wildcard += wxT(";");
8295 wildcard += wxT("*.") + handler->GetExtension();
8296 }
8297 else
8298 {
8299 if (count > 0)
8300 wildcard += wxT("|");
8301 wildcard += handler->GetName();
8302 wildcard += wxT(" ");
8303 wildcard += _("files");
8304 wildcard += wxT(" (*.");
8305 wildcard += handler->GetExtension();
8306 wildcard += wxT(")|*.");
8307 wildcard += handler->GetExtension();
1e967276
JS
8308 if (types)
8309 types->Add(handler->GetType());
5d7836c4
JS
8310 }
8311 count ++;
8312 }
8313
8314 node = node->GetNext();
8315 }
8316
8317 if (combine)
8318 wildcard = wxT("(") + wildcard + wxT(")|") + wildcard;
8319 return wildcard;
8320}
8321
7e81e3a7 8322#if wxUSE_FFILE && wxUSE_STREAMS
5d7836c4 8323/// Load a file
d75a69e8 8324bool wxRichTextBuffer::LoadFile(const wxString& filename, wxRichTextFileType type)
5d7836c4
JS
8325{
8326 wxRichTextFileHandler* handler = FindHandlerFilenameOrType(filename, type);
8327 if (handler)
1e967276 8328 {
24777478 8329 SetDefaultStyle(wxRichTextAttr());
d2d0adc7 8330 handler->SetFlags(GetHandlerFlags());
1e967276
JS
8331 bool success = handler->LoadFile(this, filename);
8332 Invalidate(wxRICHTEXT_ALL);
8333 return success;
8334 }
5d7836c4
JS
8335 else
8336 return false;
8337}
8338
8339/// Save a file
d75a69e8 8340bool wxRichTextBuffer::SaveFile(const wxString& filename, wxRichTextFileType type)
5d7836c4
JS
8341{
8342 wxRichTextFileHandler* handler = FindHandlerFilenameOrType(filename, type);
8343 if (handler)
d2d0adc7
JS
8344 {
8345 handler->SetFlags(GetHandlerFlags());
5d7836c4 8346 return handler->SaveFile(this, filename);
d2d0adc7 8347 }
5d7836c4
JS
8348 else
8349 return false;
8350}
7e81e3a7 8351#endif // wxUSE_FFILE && wxUSE_STREAMS
5d7836c4 8352
7e81e3a7 8353#if wxUSE_STREAMS
5d7836c4 8354/// Load from a stream
d75a69e8 8355bool wxRichTextBuffer::LoadFile(wxInputStream& stream, wxRichTextFileType type)
5d7836c4
JS
8356{
8357 wxRichTextFileHandler* handler = FindHandler(type);
8358 if (handler)
1e967276 8359 {
24777478 8360 SetDefaultStyle(wxRichTextAttr());
d2d0adc7 8361 handler->SetFlags(GetHandlerFlags());
1e967276
JS
8362 bool success = handler->LoadFile(this, stream);
8363 Invalidate(wxRICHTEXT_ALL);
8364 return success;
8365 }
5d7836c4
JS
8366 else
8367 return false;
8368}
8369
8370/// Save to a stream
d75a69e8 8371bool wxRichTextBuffer::SaveFile(wxOutputStream& stream, wxRichTextFileType type)
5d7836c4
JS
8372{
8373 wxRichTextFileHandler* handler = FindHandler(type);
8374 if (handler)
d2d0adc7
JS
8375 {
8376 handler->SetFlags(GetHandlerFlags());
5d7836c4 8377 return handler->SaveFile(this, stream);
d2d0adc7 8378 }
5d7836c4
JS
8379 else
8380 return false;
8381}
7e81e3a7 8382#endif // wxUSE_STREAMS
5d7836c4
JS
8383
8384/// Copy the range to the clipboard
8385bool wxRichTextBuffer::CopyToClipboard(const wxRichTextRange& range)
8386{
8387 bool success = false;
603f702b
JS
8388 wxRichTextParagraphLayoutBox* container = this;
8389 if (GetRichTextCtrl())
8390 container = GetRichTextCtrl()->GetFocusObject();
8391
11ef729d 8392#if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
0ca07313 8393
d2142335 8394 if (!wxTheClipboard->IsOpened() && wxTheClipboard->Open())
7fe8059f 8395 {
0ca07313
JS
8396 wxTheClipboard->Clear();
8397
8398 // Add composite object
8399
8400 wxDataObjectComposite* compositeObject = new wxDataObjectComposite();
8401
8402 {
603f702b 8403 wxString text = container->GetTextForRange(range);
0ca07313
JS
8404
8405#ifdef __WXMSW__
8406 text = wxTextFile::Translate(text, wxTextFileType_Dos);
8407#endif
8408
8409 compositeObject->Add(new wxTextDataObject(text), false /* not preferred */);
8410 }
8411
8412 // Add rich text buffer data object. This needs the XML handler to be present.
8413
8414 if (FindHandler(wxRICHTEXT_TYPE_XML))
8415 {
8416 wxRichTextBuffer* richTextBuf = new wxRichTextBuffer;
603f702b 8417 container->CopyFragment(range, *richTextBuf);
0ca07313
JS
8418
8419 compositeObject->Add(new wxRichTextBufferDataObject(richTextBuf), true /* preferred */);
8420 }
8421
8422 if (wxTheClipboard->SetData(compositeObject))
8423 success = true;
8424
5d7836c4
JS
8425 wxTheClipboard->Close();
8426 }
0ca07313 8427
39a1c2f2
WS
8428#else
8429 wxUnusedVar(range);
8430#endif
5d7836c4
JS
8431 return success;
8432}
8433
8434/// Paste the clipboard content to the buffer
8435bool wxRichTextBuffer::PasteFromClipboard(long position)
8436{
8437 bool success = false;
603f702b
JS
8438 wxRichTextParagraphLayoutBox* container = this;
8439 if (GetRichTextCtrl())
8440 container = GetRichTextCtrl()->GetFocusObject();
8441
11ef729d 8442#if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
5d7836c4
JS
8443 if (CanPasteFromClipboard())
8444 {
8445 if (wxTheClipboard->Open())
8446 {
0ca07313
JS
8447 if (wxTheClipboard->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())))
8448 {
8449 wxRichTextBufferDataObject data;
8450 wxTheClipboard->GetData(data);
8451 wxRichTextBuffer* richTextBuffer = data.GetRichTextBuffer();
8452 if (richTextBuffer)
8453 {
4e63bfb9 8454 container->InsertParagraphsWithUndo(this, position+1, *richTextBuffer, GetRichTextCtrl(), 0);
62381daa 8455 if (GetRichTextCtrl())
603f702b 8456 GetRichTextCtrl()->ShowPosition(position + richTextBuffer->GetOwnRange().GetEnd());
0ca07313
JS
8457 delete richTextBuffer;
8458 }
8459 }
f7d83f24 8460 else if (wxTheClipboard->IsSupported(wxDF_TEXT)
603f702b
JS
8461 #if wxUSE_UNICODE
8462 || wxTheClipboard->IsSupported(wxDF_UNICODETEXT)
8463 #endif
f7d83f24 8464 )
5d7836c4
JS
8465 {
8466 wxTextDataObject data;
8467 wxTheClipboard->GetData(data);
8468 wxString text(data.GetText());
c21f3211
JS
8469#ifdef __WXMSW__
8470 wxString text2;
8471 text2.Alloc(text.Length()+1);
8472 size_t i;
8473 for (i = 0; i < text.Length(); i++)
8474 {
8475 wxChar ch = text[i];
8476 if (ch != wxT('\r'))
8477 text2 += ch;
8478 }
8479#else
8480 wxString text2 = text;
8481#endif
4e63bfb9 8482 container->InsertTextWithUndo(this, position+1, text2, GetRichTextCtrl(), wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE);
7fe8059f 8483
62381daa
JS
8484 if (GetRichTextCtrl())
8485 GetRichTextCtrl()->ShowPosition(position + text2.Length());
8486
5d7836c4
JS
8487 success = true;
8488 }
8489 else if (wxTheClipboard->IsSupported(wxDF_BITMAP))
8490 {
8491 wxBitmapDataObject data;
8492 wxTheClipboard->GetData(data);
8493 wxBitmap bitmap(data.GetBitmap());
8494 wxImage image(bitmap.ConvertToImage());
8495
603f702b 8496 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Image"), wxRICHTEXT_INSERT, this, container, GetRichTextCtrl(), false);
7fe8059f 8497
5d7836c4
JS
8498 action->GetNewParagraphs().AddImage(image);
8499
8500 if (action->GetNewParagraphs().GetChildCount() == 1)
8501 action->GetNewParagraphs().SetPartialParagraph(true);
7fe8059f 8502
9c8e10ad 8503 action->SetPosition(position+1);
7fe8059f 8504
5d7836c4 8505 // Set the range we'll need to delete in Undo
9c8e10ad 8506 action->SetRange(wxRichTextRange(position+1, position+1));
7fe8059f 8507
5d7836c4
JS
8508 SubmitAction(action);
8509
8510 success = true;
8511 }
8512 wxTheClipboard->Close();
8513 }
8514 }
39a1c2f2
WS
8515#else
8516 wxUnusedVar(position);
8517#endif
5d7836c4
JS
8518 return success;
8519}
8520
8521/// Can we paste from the clipboard?
8522bool wxRichTextBuffer::CanPasteFromClipboard() const
8523{
7fe8059f 8524 bool canPaste = false;
11ef729d 8525#if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
d2142335 8526 if (!wxTheClipboard->IsOpened() && wxTheClipboard->Open())
5d7836c4 8527 {
f7d83f24
VZ
8528 if (wxTheClipboard->IsSupported(wxDF_TEXT)
8529#if wxUSE_UNICODE
603f702b
JS
8530 || wxTheClipboard->IsSupported(wxDF_UNICODETEXT)
8531#endif
8532 || wxTheClipboard->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())) ||
8533 wxTheClipboard->IsSupported(wxDF_BITMAP))
5d7836c4 8534 {
7fe8059f 8535 canPaste = true;
5d7836c4
JS
8536 }
8537 wxTheClipboard->Close();
8538 }
39a1c2f2 8539#endif
5d7836c4
JS
8540 return canPaste;
8541}
8542
8543/// Dumps contents of buffer for debugging purposes
8544void wxRichTextBuffer::Dump()
8545{
8546 wxString text;
8547 {
8548 wxStringOutputStream stream(& text);
8549 wxTextOutputStream textStream(stream);
8550 Dump(textStream);
8551 }
8552
8553 wxLogDebug(text);
8554}
8555
d2d0adc7
JS
8556/// Add an event handler
8557bool wxRichTextBuffer::AddEventHandler(wxEvtHandler* handler)
8558{
8559 m_eventHandlers.Append(handler);
8560 return true;
8561}
8562
8563/// Remove an event handler
8564bool wxRichTextBuffer::RemoveEventHandler(wxEvtHandler* handler, bool deleteHandler)
8565{
8566 wxList::compatibility_iterator node = m_eventHandlers.Find(handler);
8567 if (node)
8568 {
8569 m_eventHandlers.Erase(node);
8570 if (deleteHandler)
8571 delete handler;
3e541562 8572
d2d0adc7
JS
8573 return true;
8574 }
8575 else
8576 return false;
8577}
8578
8579/// Clear event handlers
8580void wxRichTextBuffer::ClearEventHandlers()
8581{
8582 m_eventHandlers.Clear();
8583}
8584
8585/// Send event to event handlers. If sendToAll is true, will send to all event handlers,
8586/// otherwise will stop at the first successful one.
8587bool wxRichTextBuffer::SendEvent(wxEvent& event, bool sendToAll)
8588{
8589 bool success = false;
8590 for (wxList::compatibility_iterator node = m_eventHandlers.GetFirst(); node; node = node->GetNext())
8591 {
8592 wxEvtHandler* handler = (wxEvtHandler*) node->GetData();
8593 if (handler->ProcessEvent(event))
8594 {
8595 success = true;
8596 if (!sendToAll)
8597 return true;
8598 }
8599 }
8600 return success;
8601}
8602
8603/// Set style sheet and notify of the change
8604bool wxRichTextBuffer::SetStyleSheetAndNotify(wxRichTextStyleSheet* sheet)
8605{
8606 wxRichTextStyleSheet* oldSheet = GetStyleSheet();
3e541562 8607
616c7cbd 8608 wxWindowID winid = wxID_ANY;
d2d0adc7 8609 if (GetRichTextCtrl())
616c7cbd 8610 winid = GetRichTextCtrl()->GetId();
3e541562 8611
ce7fe42e 8612 wxRichTextEvent event(wxEVT_RICHTEXT_STYLESHEET_REPLACING, winid);
d2d0adc7 8613 event.SetEventObject(GetRichTextCtrl());
603f702b 8614 event.SetContainer(GetRichTextCtrl()->GetFocusObject());
d2d0adc7
JS
8615 event.SetOldStyleSheet(oldSheet);
8616 event.SetNewStyleSheet(sheet);
8617 event.Allow();
3e541562 8618
d2d0adc7
JS
8619 if (SendEvent(event) && !event.IsAllowed())
8620 {
8621 if (sheet != oldSheet)
8622 delete sheet;
8623
8624 return false;
8625 }
8626
8627 if (oldSheet && oldSheet != sheet)
8628 delete oldSheet;
8629
8630 SetStyleSheet(sheet);
8631
ce7fe42e 8632 event.SetEventType(wxEVT_RICHTEXT_STYLESHEET_REPLACED);
d2d0adc7
JS
8633 event.SetOldStyleSheet(NULL);
8634 event.Allow();
8635
8636 return SendEvent(event);
8637}
8638
8639/// Set renderer, deleting old one
8640void wxRichTextBuffer::SetRenderer(wxRichTextRenderer* renderer)
8641{
8642 if (sm_renderer)
8643 delete sm_renderer;
8644 sm_renderer = renderer;
8645}
8646
603f702b
JS
8647/// Hit-testing: returns a flag indicating hit test details, plus
8648/// information about position
8db2e3ef 8649int wxRichTextBuffer::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
603f702b 8650{
8db2e3ef 8651 int ret = wxRichTextParagraphLayoutBox::HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
603f702b
JS
8652 if (ret != wxRICHTEXT_HITTEST_NONE)
8653 {
8654 return ret;
8655 }
8656 else
8657 {
8658 textPosition = m_ownRange.GetEnd()-1;
8659 *obj = this;
8660 *contextObj = this;
8661 return wxRICHTEXT_HITTEST_AFTER|wxRICHTEXT_HITTEST_OUTSIDE;
8662 }
8663}
8664
32423dd8
JS
8665void wxRichTextBuffer::SetFontScale(double fontScale)
8666{
8667 m_fontScale = fontScale;
8668 m_fontTable.SetFontScale(fontScale);
8669}
8670
8671void wxRichTextBuffer::SetDimensionScale(double dimScale)
8672{
8673 m_dimensionScale = dimScale;
8674}
8675
24777478 8676bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph* paragraph, wxDC& dc, const wxRichTextAttr& bulletAttr, const wxRect& rect)
d2d0adc7 8677{
a1b806b9 8678 if (bulletAttr.GetTextColour().IsOk())
d2d0adc7 8679 {
ecb5fbf1
JS
8680 wxCheckSetPen(dc, wxPen(bulletAttr.GetTextColour()));
8681 wxCheckSetBrush(dc, wxBrush(bulletAttr.GetTextColour()));
d2d0adc7
JS
8682 }
8683 else
8684 {
ecb5fbf1
JS
8685 wxCheckSetPen(dc, *wxBLACK_PEN);
8686 wxCheckSetBrush(dc, *wxBLACK_BRUSH);
d2d0adc7
JS
8687 }
8688
8689 wxFont font;
44cc96a8
JS
8690 if (bulletAttr.HasFont())
8691 {
8692 font = paragraph->GetBuffer()->GetFontTable().FindFont(bulletAttr);
8693 }
d2d0adc7
JS
8694 else
8695 font = (*wxNORMAL_FONT);
8696
ecb5fbf1 8697 wxCheckSetFont(dc, font);
d2d0adc7
JS
8698
8699 int charHeight = dc.GetCharHeight();
3e541562 8700
d2d0adc7
JS
8701 int bulletWidth = (int) (((float) charHeight) * wxRichTextBuffer::GetBulletProportion());
8702 int bulletHeight = bulletWidth;
8703
8704 int x = rect.x;
3e541562 8705
d2d0adc7
JS
8706 // Calculate the top position of the character (as opposed to the whole line height)
8707 int y = rect.y + (rect.height - charHeight);
3e541562 8708
d2d0adc7
JS
8709 // Calculate where the bullet should be positioned
8710 y = y + (charHeight+1)/2 - (bulletHeight+1)/2;
3e541562 8711
d2d0adc7 8712 // The margin between a bullet and text.
44219ff0 8713 int margin = paragraph->ConvertTenthsMMToPixels(dc, wxRichTextBuffer::GetBulletRightMargin());
3e541562 8714
d2d0adc7
JS
8715 if (bulletAttr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT)
8716 x = rect.x + rect.width - bulletWidth - margin;
8717 else if (bulletAttr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE)
8718 x = x + (rect.width)/2 - bulletWidth/2;
3e541562 8719
d2d0adc7
JS
8720 if (bulletAttr.GetBulletName() == wxT("standard/square"))
8721 {
8722 dc.DrawRectangle(x, y, bulletWidth, bulletHeight);
8723 }
8724 else if (bulletAttr.GetBulletName() == wxT("standard/diamond"))
8725 {
8726 wxPoint pts[5];
8727 pts[0].x = x; pts[0].y = y + bulletHeight/2;
8728 pts[1].x = x + bulletWidth/2; pts[1].y = y;
8729 pts[2].x = x + bulletWidth; pts[2].y = y + bulletHeight/2;
8730 pts[3].x = x + bulletWidth/2; pts[3].y = y + bulletHeight;
3e541562 8731
d2d0adc7
JS
8732 dc.DrawPolygon(4, pts);
8733 }
8734 else if (bulletAttr.GetBulletName() == wxT("standard/triangle"))
8735 {
8736 wxPoint pts[3];
8737 pts[0].x = x; pts[0].y = y;
8738 pts[1].x = x + bulletWidth; pts[1].y = y + bulletHeight/2;
8739 pts[2].x = x; pts[2].y = y + bulletHeight;
3e541562 8740
d2d0adc7
JS
8741 dc.DrawPolygon(3, pts);
8742 }
603f702b
JS
8743 else if (bulletAttr.GetBulletName() == wxT("standard/circle-outline"))
8744 {
8745 wxCheckSetBrush(dc, *wxTRANSPARENT_BRUSH);
8746 dc.DrawEllipse(x, y, bulletWidth, bulletHeight);
8747 }
d2d0adc7
JS
8748 else // "standard/circle", and catch-all
8749 {
8750 dc.DrawEllipse(x, y, bulletWidth, bulletHeight);
3e541562
JS
8751 }
8752
d2d0adc7
JS
8753 return true;
8754}
8755
24777478 8756bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph* paragraph, wxDC& dc, const wxRichTextAttr& attr, const wxRect& rect, const wxString& text)
d2d0adc7
JS
8757{
8758 if (!text.empty())
8759 {
8760 wxFont font;
44cc96a8
JS
8761 if ((attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL) && !attr.GetBulletFont().IsEmpty() && attr.HasFont())
8762 {
24777478 8763 wxRichTextAttr fontAttr;
32423dd8
JS
8764 if (attr.HasFontPixelSize())
8765 fontAttr.SetFontPixelSize(attr.GetFontSize());
8766 else
8767 fontAttr.SetFontPointSize(attr.GetFontSize());
44cc96a8
JS
8768 fontAttr.SetFontStyle(attr.GetFontStyle());
8769 fontAttr.SetFontWeight(attr.GetFontWeight());
8770 fontAttr.SetFontUnderlined(attr.GetFontUnderlined());
8771 fontAttr.SetFontFaceName(attr.GetBulletFont());
8772 font = paragraph->GetBuffer()->GetFontTable().FindFont(fontAttr);
8773 }
8774 else if (attr.HasFont())
8775 font = paragraph->GetBuffer()->GetFontTable().FindFont(attr);
d2d0adc7
JS
8776 else
8777 font = (*wxNORMAL_FONT);
8778
ecb5fbf1 8779 wxCheckSetFont(dc, font);
d2d0adc7 8780
a1b806b9 8781 if (attr.GetTextColour().IsOk())
d2d0adc7
JS
8782 dc.SetTextForeground(attr.GetTextColour());
8783
04ee05f9 8784 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
d2d0adc7
JS
8785
8786 int charHeight = dc.GetCharHeight();
8787 wxCoord tw, th;
8788 dc.GetTextExtent(text, & tw, & th);
8789
8790 int x = rect.x;
8791
8792 // Calculate the top position of the character (as opposed to the whole line height)
3e541562 8793 int y = rect.y + (rect.height - charHeight);
d2d0adc7
JS
8794
8795 // The margin between a bullet and text.
44219ff0 8796 int margin = paragraph->ConvertTenthsMMToPixels(dc, wxRichTextBuffer::GetBulletRightMargin());
3e541562 8797
d2d0adc7
JS
8798 if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT)
8799 x = (rect.x + rect.width) - tw - margin;
8800 else if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE)
8801 x = x + (rect.width)/2 - tw/2;
8802
8803 dc.DrawText(text, x, y);
3e541562 8804
d2d0adc7
JS
8805 return true;
8806 }
8807 else
8808 return false;
8809}
8810
24777478 8811bool wxRichTextStdRenderer::DrawBitmapBullet(wxRichTextParagraph* WXUNUSED(paragraph), wxDC& WXUNUSED(dc), const wxRichTextAttr& WXUNUSED(attr), const wxRect& WXUNUSED(rect))
d2d0adc7
JS
8812{
8813 // Currently unimplemented. The intention is to store bitmaps by name in a media store associated
8814 // with the buffer. The store will allow retrieval from memory, disk or other means.
8815 return false;
8816}
8817
8818/// Enumerate the standard bullet names currently supported
8819bool wxRichTextStdRenderer::EnumerateStandardBulletNames(wxArrayString& bulletNames)
8820{
04529b2a 8821 bulletNames.Add(wxTRANSLATE("standard/circle"));
603f702b 8822 bulletNames.Add(wxTRANSLATE("standard/circle-outline"));
04529b2a
JS
8823 bulletNames.Add(wxTRANSLATE("standard/square"));
8824 bulletNames.Add(wxTRANSLATE("standard/diamond"));
8825 bulletNames.Add(wxTRANSLATE("standard/triangle"));
d2d0adc7
JS
8826
8827 return true;
8828}
5d7836c4 8829
bec80f4f
JS
8830/*!
8831 * wxRichTextBox
8832 */
8833
603f702b 8834IMPLEMENT_DYNAMIC_CLASS(wxRichTextBox, wxRichTextParagraphLayoutBox)
bec80f4f
JS
8835
8836wxRichTextBox::wxRichTextBox(wxRichTextObject* parent):
603f702b 8837 wxRichTextParagraphLayoutBox(parent)
bec80f4f
JS
8838{
8839}
8840
8841/// Draw the item
8db2e3ef 8842bool wxRichTextBox::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
bec80f4f 8843{
603f702b
JS
8844 if (!IsShown())
8845 return true;
5ad9ae3a 8846
603f702b
JS
8847 // TODO: if the active object in the control, draw an indication.
8848 // We need to add the concept of active object, and not just focus object,
8849 // so we can apply commands (properties, delete, ...) to objects such as text boxes and images.
8850 // Ultimately we would like to be able to interactively resize an active object
8851 // using drag handles.
8db2e3ef 8852 return wxRichTextParagraphLayoutBox::Draw(dc, context, range, selection, rect, descent, style);
603f702b 8853}
5ad9ae3a 8854
603f702b
JS
8855/// Copy
8856void wxRichTextBox::Copy(const wxRichTextBox& obj)
8857{
8858 wxRichTextParagraphLayoutBox::Copy(obj);
8859}
8860
8861// Edit properties via a GUI
8862bool wxRichTextBox::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
8863{
8864 wxRichTextObjectPropertiesDialog boxDlg(this, wxGetTopLevelParent(parent), wxID_ANY, _("Box Properties"));
8865 boxDlg.SetAttributes(GetAttributes());
8866
8867 if (boxDlg.ShowModal() == wxID_OK)
8868 {
8869 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
8870 // indeterminate in the object.
8871 boxDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
8872 return true;
5ad9ae3a 8873 }
603f702b
JS
8874 else
8875 return false;
bec80f4f
JS
8876}
8877
7c9fdebe
JS
8878/*!
8879 * wxRichTextField
8880 */
8881
8882IMPLEMENT_DYNAMIC_CLASS(wxRichTextField, wxRichTextParagraphLayoutBox)
8883
8884wxRichTextField::wxRichTextField(const wxString& fieldType, wxRichTextObject* parent):
8885 wxRichTextParagraphLayoutBox(parent)
8886{
8887 SetFieldType(fieldType);
8888}
8889
8890/// Draw the item
8891bool wxRichTextField::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
8892{
8893 if (!IsShown())
8894 return true;
8895
8896 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8897 if (fieldType && fieldType->Draw(this, dc, context, range, selection, rect, descent, style))
8898 return true;
8899
8900 // Fallback; but don't draw guidelines.
8901 style &= ~wxRICHTEXT_DRAW_GUIDELINES;
8902 return wxRichTextParagraphLayoutBox::Draw(dc, context, range, selection, rect, descent, style);
8903}
8904
8905bool wxRichTextField::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style)
8906{
8907 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8908 if (fieldType && fieldType->Layout(this, dc, context, rect, parentRect, style))
8909 return true;
8910
8911 // Fallback
8912 return wxRichTextParagraphLayoutBox::Layout(dc, context, rect, parentRect, style);
8913}
8914
914a4e23 8915bool wxRichTextField::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, const wxPoint& position, const wxSize& parentSize, wxArrayInt* partialExtents) const
7c9fdebe
JS
8916{
8917 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8918 if (fieldType)
914a4e23 8919 return fieldType->GetRangeSize((wxRichTextField*) this, range, size, descent, dc, context, flags, position, parentSize, partialExtents);
7c9fdebe 8920
914a4e23 8921 return wxRichTextParagraphLayoutBox::GetRangeSize(range, size, descent, dc, context, flags, position, parentSize, partialExtents);
7c9fdebe
JS
8922}
8923
8924/// Calculate range
8925void wxRichTextField::CalculateRange(long start, long& end)
8926{
8927 if (IsTopLevel())
8928 wxRichTextParagraphLayoutBox::CalculateRange(start, end);
8929 else
8930 wxRichTextObject::CalculateRange(start, end);
8931}
8932
8933/// Copy
8934void wxRichTextField::Copy(const wxRichTextField& obj)
8935{
8936 wxRichTextParagraphLayoutBox::Copy(obj);
8937
32423dd8 8938 UpdateField(GetBuffer());
7c9fdebe
JS
8939}
8940
8941// Edit properties via a GUI
8942bool wxRichTextField::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
8943{
8944 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8945 if (fieldType)
8946 return fieldType->EditProperties(this, parent, buffer);
8947
8948 return false;
8949}
8950
8951bool wxRichTextField::CanEditProperties() const
8952{
8953 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8954 if (fieldType)
8955 return fieldType->CanEditProperties((wxRichTextField*) this);
8956
8957 return false;
8958}
8959
8960wxString wxRichTextField::GetPropertiesMenuLabel() const
8961{
8962 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8963 if (fieldType)
8964 return fieldType->GetPropertiesMenuLabel((wxRichTextField*) this);
8965
8966 return wxEmptyString;
8967}
8968
32423dd8 8969bool wxRichTextField::UpdateField(wxRichTextBuffer* buffer)
7c9fdebe
JS
8970{
8971 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8972 if (fieldType)
32423dd8 8973 return fieldType->UpdateField(buffer, (wxRichTextField*) this);
7c9fdebe
JS
8974
8975 return false;
8976}
8977
8978bool wxRichTextField::IsTopLevel() const
8979{
8980 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8981 if (fieldType)
8982 return fieldType->IsTopLevel((wxRichTextField*) this);
8983
8984 return true;
8985}
8986
8987IMPLEMENT_CLASS(wxRichTextFieldType, wxObject)
8988
8989IMPLEMENT_CLASS(wxRichTextFieldTypeStandard, wxRichTextFieldType)
8990
8991wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString& name, const wxString& label, int displayStyle)
8992{
8993 Init();
8994
8995 SetName(name);
8996 SetLabel(label);
8997 SetDisplayStyle(displayStyle);
8998}
8999
9000wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString& name, const wxBitmap& bitmap, int displayStyle)
9001{
9002 Init();
9003
9004 SetName(name);
9005 SetBitmap(bitmap);
9006 SetDisplayStyle(displayStyle);
9007}
9008
9009void wxRichTextFieldTypeStandard::Init()
9010{
9011 m_displayStyle = wxRICHTEXT_FIELD_STYLE_RECTANGLE;
9012 m_font = wxFont(6, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
9013 m_textColour = *wxWHITE;
9014 m_borderColour = *wxBLACK;
9015 m_backgroundColour = *wxBLACK;
9016 m_verticalPadding = 1;
9017 m_horizontalPadding = 3;
9018 m_horizontalMargin = 2;
9019 m_verticalMargin = 0;
9020}
9021
9022void wxRichTextFieldTypeStandard::Copy(const wxRichTextFieldTypeStandard& field)
9023{
9024 wxRichTextFieldType::Copy(field);
9025
9026 m_label = field.m_label;
9027 m_displayStyle = field.m_displayStyle;
9028 m_font = field.m_font;
9029 m_textColour = field.m_textColour;
9030 m_borderColour = field.m_borderColour;
9031 m_backgroundColour = field.m_backgroundColour;
9032 m_verticalPadding = field.m_verticalPadding;
9033 m_horizontalPadding = field.m_horizontalPadding;
9034 m_horizontalMargin = field.m_horizontalMargin;
9035 m_bitmap = field.m_bitmap;
9036}
9037
9038bool wxRichTextFieldTypeStandard::Draw(wxRichTextField* obj, wxDC& dc, wxRichTextDrawingContext& WXUNUSED(context), const wxRichTextRange& WXUNUSED(range), const wxRichTextSelection& selection, const wxRect& rect, int descent, int WXUNUSED(style))
9039{
9040 if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_COMPOSITE)
9041 return false; // USe default composite drawing
9042 else // if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_RECTANGLE || m_displayStyle == wxRICHTEXT_FIELD_STYLE_NOBORDER)
9043 {
9044 int borderSize = 1;
9045
9046 wxPen borderPen(m_borderColour, 1, wxSOLID);
9047 wxBrush backgroundBrush(m_backgroundColour);
9048 wxColour textColour(m_textColour);
9049
9050 if (selection.WithinSelection(obj->GetRange().GetStart(), obj))
9051 {
9052 wxColour highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
9053 wxColour highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
9054
9055 borderPen = wxPen(highlightTextColour, 1, wxSOLID);
9056 backgroundBrush = wxBrush(highlightColour);
9057
9058 wxCheckSetBrush(dc, backgroundBrush);
9059 wxCheckSetPen(dc, wxPen(highlightColour, 1, wxSOLID));
9060 dc.DrawRectangle(rect);
9061 }
9062
9063 if (m_displayStyle != wxRICHTEXT_FIELD_STYLE_NO_BORDER)
9064 borderSize = 0;
9065
9066 // objectRect is the area where the content is drawn, after margins around it have been taken into account
9067 wxRect objectRect = wxRect(wxPoint(rect.x + m_horizontalMargin, rect.y + wxMax(0, rect.height - descent - obj->GetCachedSize().y)),
9068 wxSize(obj->GetCachedSize().x - 2*m_horizontalMargin - borderSize, obj->GetCachedSize().y));
9069
9070 // clientArea is where the text is actually written
9071 wxRect clientArea = objectRect;
9072
9073 if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_RECTANGLE)
9074 {
9075 dc.SetPen(borderPen);
9076 dc.SetBrush(backgroundBrush);
9077 dc.DrawRoundedRectangle(objectRect, 4.0);
9078 }
9079 else if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_START_TAG)
9080 {
9081 int arrowLength = objectRect.height/2;
9082 clientArea.width -= (arrowLength - m_horizontalPadding);
9083
9084 wxPoint pts[5];
9085 pts[0].x = objectRect.x; pts[0].y = objectRect.y;
9086 pts[1].x = objectRect.x + objectRect.width - arrowLength; pts[1].y = objectRect.y;
9087 pts[2].x = objectRect.x + objectRect.width; pts[2].y = objectRect.y + (objectRect.height/2);
9088 pts[3].x = objectRect.x + objectRect.width - arrowLength; pts[3].y = objectRect.y + objectRect.height;
9089 pts[4].x = objectRect.x; pts[4].y = objectRect.y + objectRect.height;
9090 dc.SetPen(borderPen);
9091 dc.SetBrush(backgroundBrush);
9092 dc.DrawPolygon(5, pts);
9093 }
9094 else if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_END_TAG)
9095 {
9096 int arrowLength = objectRect.height/2;
9097 clientArea.width -= (arrowLength - m_horizontalPadding);
9098 clientArea.x += (arrowLength - m_horizontalPadding);
9099
9100 wxPoint pts[5];
9101 pts[0].x = objectRect.x + objectRect.width; pts[0].y = objectRect.y;
9102 pts[1].x = objectRect.x + arrowLength; pts[1].y = objectRect.y;
9103 pts[2].x = objectRect.x; pts[2].y = objectRect.y + (objectRect.height/2);
9104 pts[3].x = objectRect.x + arrowLength; pts[3].y = objectRect.y + objectRect.height;
9105 pts[4].x = objectRect.x + objectRect.width; pts[4].y = objectRect.y + objectRect.height;
9106 dc.SetPen(borderPen);
9107 dc.SetBrush(backgroundBrush);
9108 dc.DrawPolygon(5, pts);
9109 }
9110
9111 if (m_bitmap.IsOk())
9112 {
9113 int x = clientArea.x + (clientArea.width - m_bitmap.GetWidth())/2;
9114 int y = clientArea.y + m_verticalPadding;
9115 dc.DrawBitmap(m_bitmap, x, y, true);
9116
9117 if (selection.WithinSelection(obj->GetRange().GetStart(), obj))
9118 {
9119 wxCheckSetBrush(dc, *wxBLACK_BRUSH);
9120 wxCheckSetPen(dc, *wxBLACK_PEN);
9121 dc.SetLogicalFunction(wxINVERT);
9122 dc.DrawRectangle(wxRect(x, y, m_bitmap.GetWidth(), m_bitmap.GetHeight()));
9123 dc.SetLogicalFunction(wxCOPY);
9124 }
9125 }
9126 else
9127 {
9128 wxString label(m_label);
9129 if (label.IsEmpty())
9130 label = wxT("??");
9131 int w, h, maxDescent;
9132 dc.SetFont(m_font);
9133 dc.GetTextExtent(m_label, & w, &h, & maxDescent);
9134 dc.SetTextForeground(textColour);
9135
9136 int x = clientArea.x + (clientArea.width - w)/2;
9137 int y = clientArea.y + (clientArea.height - (h - maxDescent))/2;
9138 dc.DrawText(m_label, x, y);
9139 }
9140 }
9141
9142 return true;
9143}
9144
9145bool wxRichTextFieldTypeStandard::Layout(wxRichTextField* obj, wxDC& dc, wxRichTextDrawingContext& context, const wxRect& WXUNUSED(rect), const wxRect& WXUNUSED(parentRect), int style)
9146{
9147 if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_COMPOSITE)
9148 return false; // USe default composite layout
9149
9150 wxSize size = GetSize(obj, dc, context, style);
9151 obj->SetCachedSize(size);
9152 obj->SetMinSize(size);
9153 obj->SetMaxSize(size);
9154 return true;
9155}
9156
914a4e23 9157bool wxRichTextFieldTypeStandard::GetRangeSize(wxRichTextField* obj, const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, const wxPoint& position, const wxSize& parentSize, wxArrayInt* partialExtents) const
7c9fdebe
JS
9158{
9159 if (IsTopLevel(obj))
914a4e23 9160 return obj->wxRichTextParagraphLayoutBox::GetRangeSize(range, size, descent, dc, context, flags, position, parentSize);
7c9fdebe
JS
9161 else
9162 {
9163 wxSize sz = GetSize(obj, dc, context, 0);
9164 if (partialExtents)
9165 {
9166 int lastSize;
9167 if (partialExtents->GetCount() > 0)
9168 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
9169 else
9170 lastSize = 0;
9171 partialExtents->Add(lastSize + sz.x);
9172 }
9173 size = sz;
9174 return true;
9175 }
9176}
9177
9178wxSize wxRichTextFieldTypeStandard::GetSize(wxRichTextField* WXUNUSED(obj), wxDC& dc, wxRichTextDrawingContext& WXUNUSED(context), int WXUNUSED(style)) const
9179{
9180 int borderSize = 1;
9181 int w = 0, h = 0, maxDescent = 0;
9182
9183 wxSize sz;
9184 if (m_bitmap.IsOk())
9185 {
9186 w = m_bitmap.GetWidth();
9187 h = m_bitmap.GetHeight();
9188
9189 sz = wxSize(w + m_horizontalMargin*2, h + m_verticalMargin*2);
9190 }
9191 else
9192 {
9193 wxString label(m_label);
9194 if (label.IsEmpty())
9195 label = wxT("??");
9196 dc.SetFont(m_font);
9197 dc.GetTextExtent(label, & w, &h, & maxDescent);
9198
9199 sz = wxSize(w + m_horizontalPadding*2 + m_horizontalMargin*2, h + m_verticalPadding *2 + m_verticalMargin*2);
9200 }
9201
9202 if (m_displayStyle != wxRICHTEXT_FIELD_STYLE_NO_BORDER)
9203 {
9204 sz.x += borderSize*2;
9205 sz.y += borderSize*2;
9206 }
9207
9208 if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_START_TAG || m_displayStyle == wxRICHTEXT_FIELD_STYLE_END_TAG)
9209 {
9210 // Add space for the arrow
9211 sz.x += (sz.y/2 - m_horizontalPadding);
9212 }
9213
9214 return sz;
9215}
9216
603f702b
JS
9217IMPLEMENT_DYNAMIC_CLASS(wxRichTextCell, wxRichTextBox)
9218
9219wxRichTextCell::wxRichTextCell(wxRichTextObject* parent):
9220 wxRichTextBox(parent)
bec80f4f 9221{
603f702b
JS
9222}
9223
9224/// Draw the item
8db2e3ef 9225bool wxRichTextCell::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
603f702b 9226{
8db2e3ef 9227 return wxRichTextBox::Draw(dc, context, range, selection, rect, descent, style);
603f702b
JS
9228}
9229
9230/// Copy
9231void wxRichTextCell::Copy(const wxRichTextCell& obj)
9232{
9233 wxRichTextBox::Copy(obj);
9234}
9235
9236// Edit properties via a GUI
9237bool wxRichTextCell::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
9238{
9239 // We need to gather common attributes for all selected cells.
9240
9241 wxRichTextTable* table = wxDynamicCast(GetParent(), wxRichTextTable);
9242 bool multipleCells = false;
9243 wxRichTextAttr attr;
9244
9245 if (table && buffer && buffer->GetRichTextCtrl() && buffer->GetRichTextCtrl()->GetSelection().IsValid() &&
9246 buffer->GetRichTextCtrl()->GetSelection().GetContainer() == GetParent())
5ad9ae3a 9247 {
603f702b
JS
9248 wxRichTextAttr clashingAttr, absentAttr;
9249 const wxRichTextSelection& sel = buffer->GetRichTextCtrl()->GetSelection();
9250 size_t i;
9251 int selectedCellCount = 0;
9252 for (i = 0; i < sel.GetCount(); i++)
9253 {
9254 const wxRichTextRange& range = sel[i];
9255 wxRichTextCell* cell = table->GetCell(range.GetStart());
9256 if (cell)
9257 {
9258 wxRichTextAttr cellStyle = cell->GetAttributes();
5ad9ae3a 9259
603f702b
JS
9260 CollectStyle(attr, cellStyle, clashingAttr, absentAttr);
9261
9262 selectedCellCount ++;
9263 }
9264 }
9265 multipleCells = selectedCellCount > 1;
5ad9ae3a 9266 }
603f702b
JS
9267 else
9268 {
9269 attr = GetAttributes();
9270 }
9271
9272 wxString caption;
9273 if (multipleCells)
9274 caption = _("Multiple Cell Properties");
9275 else
9276 caption = _("Cell Properties");
9277
9278 wxRichTextObjectPropertiesDialog cellDlg(this, wxGetTopLevelParent(parent), wxID_ANY, caption);
9279 cellDlg.SetAttributes(attr);
9280
80a46597 9281 wxRichTextSizePage* sizePage = wxDynamicCast(cellDlg.FindPage(wxCLASSINFO(wxRichTextSizePage)), wxRichTextSizePage);
603f702b
JS
9282 if (sizePage)
9283 {
9284 // We don't want position and floating controls for a cell.
9285 sizePage->ShowPositionControls(false);
9286 sizePage->ShowFloatingControls(false);
9287 }
9288
9289 if (cellDlg.ShowModal() == wxID_OK)
9290 {
9291 if (multipleCells)
9292 {
9293 const wxRichTextSelection& sel = buffer->GetRichTextCtrl()->GetSelection();
9294 // Apply the style; we interpret indeterminate attributes as 'don't touch this attribute'
9295 // since it may represent clashing attributes across multiple objects.
9296 table->SetCellStyle(sel, attr);
9297 }
9298 else
9299 // For a single object, indeterminate attributes set by the user should be reflected in the
9300 // actual object style, so pass the wxRICHTEXT_SETSTYLE_RESET flag to assign
9301 // the style directly instead of applying (which ignores indeterminate attributes,
9302 // leaving them as they were).
9303 cellDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
9304 return true;
9305 }
9306 else
9307 return false;
9308}
9309
8a28cd66
JS
9310// The next 2 methods return span values. Note that the default is 1, not 0
9311int wxRichTextCell::GetColspan() const
9312{
9313 int span = 1;
9314 if (GetProperties().HasProperty(wxT("colspan")))
9315 {
9316 span = GetProperties().GetPropertyLong(wxT("colspan"));
9317 }
9318
9319 return span;
9320}
9321
9322int wxRichTextCell::GetRowspan() const
9323{
9324 int span = 1;
9325 if (GetProperties().HasProperty(wxT("rowspan")))
9326 {
9327 span = GetProperties().GetPropertyLong(wxT("rowspan"));
9328 }
9329
9330 return span;
9331}
9332
603f702b
JS
9333WX_DEFINE_OBJARRAY(wxRichTextObjectPtrArrayArray)
9334
9335IMPLEMENT_DYNAMIC_CLASS(wxRichTextTable, wxRichTextBox)
9336
9337wxRichTextTable::wxRichTextTable(wxRichTextObject* parent): wxRichTextBox(parent)
9338{
9339 m_rowCount = 0;
9340 m_colCount = 0;
9341}
5ad9ae3a 9342
603f702b 9343// Draws the object.
8db2e3ef 9344bool wxRichTextTable::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
603f702b 9345{
8db2e3ef 9346 return wxRichTextBox::Draw(dc, context, range, selection, rect, descent, style);
bec80f4f
JS
9347}
9348
603f702b
JS
9349WX_DECLARE_OBJARRAY(wxRect, wxRichTextRectArray);
9350WX_DEFINE_OBJARRAY(wxRichTextRectArray);
9351
8e77fd8b
JS
9352
9353 // Helper function for Layout() that clears the space needed by a cell with rowspan > 1
9354int GetRowspanDisplacement(const wxRichTextTable* table, int row, int col, int paddingX, const wxArrayInt& colWidths)
9355{
9356 // If one or more cells above-left of this one has rowspan > 1, the affected cells below it
9357 // will have been hidden and have width 0. As a result they are ignored by the layout algorithm,
9358 // and all cells to their right are effectively shifted left. As a result there's no hole for
9359 // the spanning cell to fill.
9360 // So search back along the current row for hidden cells. However there's also the annoying issue of a
9361 // rowspanning cell that also has colspam. So we can't rely on the rowspanning cell being directly above
9362 // the first hidden one we come to. We also can't rely on a cell being hidden only by one type of span;
9363 // there's nothing to stop a cell being hidden by colspan, and then again hidden from above by rowspan.
9364 // The answer is to look above each hidden cell in turn, which I think covers all bases.
9365 int deltaX = 0;
9366 for (int prevcol = 0; prevcol < col; ++prevcol)
9367 {
9368 if (!table->GetCell(row, prevcol)->IsShown())
9369 {
9370 // We've found a hidden cell. If it's hidden because of colspan to its left, it's
9371 // already been taken into account; but not if there's a rowspanning cell above
9372 for (int prevrow = row-1; prevrow >= 0; --prevrow)
9373 {
9374 wxRichTextCell* cell = table->GetCell(prevrow, prevcol);
9375 if (cell && cell->IsShown())
9376 {
9377 int rowSpan = cell->GetRowspan();
9378 if (rowSpan > 1 && rowSpan > (row-prevrow))
9379 {
9380 // There is a rowspanning cell above above the hidden one, so we need
9381 // to right-shift the index cell by this column's width. Furthermore,
9382 // if the cell also colspans, we need to shift by all affected columns
9383 for (int colSpan = 0; colSpan < cell->GetColspan(); ++colSpan)
9384 deltaX += (colWidths[prevcol+colSpan] + paddingX);
9385 break;
9386 }
9387 }
9388 }
9389 }
9390 }
9391 return deltaX;
9392}
9393
9394 // Helper function for Layout() that expands any cell with rowspan > 1
9395void ExpandCellsWithRowspan(const wxRichTextTable* table, int paddingY, int& bottomY, wxDC& dc, wxRichTextDrawingContext& context, const wxRect& availableSpace, int style)
9396{
9397 // This is called when the table's cell layout is otherwise complete.
9398 // For any cell with rowspan > 1, expand downwards into the row(s) below.
9399
9400 // Start by finding the current 'y' of the top of each row, plus the bottom of the available area for cells.
9401 // Deduce this from the top of a visible cell in the row below. (If none are visible, the row will be invisible anyway and can be ignored.)
9402 const int rowCount = table->GetRowCount();
9403 const int colCount = table->GetColumnCount();
9404 wxArrayInt rowTops;
9405 rowTops.Add(0, rowCount+1);
5d98e603
VZ
9406 int row;
9407 for (row = 0; row < rowCount; ++row)
8e77fd8b
JS
9408 {
9409 for (int column = 0; column < colCount; ++column)
9410 {
9411 wxRichTextCell* cell = table->GetCell(row, column);
9412 if (cell && cell->IsShown())
9413 {
9414 rowTops[row] = cell->GetPosition().y;
9415 break;
9416 }
9417 }
9418 }
9419 rowTops[rowCount] = bottomY + paddingY; // The table bottom, which was passed to us
9420
9421 bool needsRelay = false;
9422
8e77fd8b
JS
9423 for (row = 0; row < rowCount-1; ++row) // -1 as the bottom row can't rowspan
9424 {
5d98e603 9425 for (int col = 0; col < colCount; ++col)
8e77fd8b
JS
9426 {
9427 wxRichTextCell* cell = table->GetCell(row, col);
9428 if (cell && cell->IsShown())
9429 {
9430 int span = cell->GetRowspan();
9431 if (span > 1)
9432 {
9433 span = wxMin(span, rowCount-row); // Don't try to span below the table!
9434 if (span < 2)
9435 continue;
9436
9437 int availableHeight = rowTops[row+span] - rowTops[row] - paddingY;
9438 wxSize newSize = wxSize(cell->GetCachedSize().GetWidth(), availableHeight);
9439 wxRect availableCellSpace = wxRect(cell->GetPosition(), newSize);
9440 cell->Invalidate(wxRICHTEXT_ALL);
9441 cell->Layout(dc, context, availableCellSpace, availableSpace, style);
9442 // Ensure there's room in the span to display its contents, else it'll overwrite lower rows
9443 int overhang = cell->GetCachedSize().GetHeight() - availableHeight;
9444 cell->SetCachedSize(newSize);
9445
9446 if (overhang > 0)
9447 {
9448 // There are 3 things to get right:
9449 // 1) The easiest is the rows below the span: they need to be downshifted by the overhang, and so does the table bottom itself
9450 // 2) The rows within the span, including the one holding this cell, need to be deepened by their share of the overhang
9451 // e.g. if rowspan == 3, each row should increase in depth by 1/3rd of the overhang.
9452 // 3) The cell with the rowspan shouldn't be touched in 2); its height will be set to the whole span later.
9453 int deltaY = overhang / span;
9454 int spare = overhang % span;
9455
9456 // Each row in the span needs to by deepened by its share of the overhang (give the first row any spare).
9457 // This is achieved by increasing the value stored in the following row's rowTops
9458 for (int spannedRows = 0; spannedRows < span; ++spannedRows)
9459 {
9460 rowTops[row+spannedRows+1] += ((deltaY * (spannedRows+1)) + (spannedRows == 0 ? spare:0));
9461 }
9462
9463 // Any rows below the span need shifting down
9464 for (int rowsBelow = row + span+1; rowsBelow <= rowCount; ++rowsBelow)
9465 {
9466 rowTops[rowsBelow] += overhang;
9467 }
9468
9469 needsRelay = true;
9470 }
9471 }
9472 }
9473 }
9474 }
9475
9476 if (!needsRelay)
9477 return;
9478
9479 // There were overflowing rowspanning cells, so layout yet again to make the increased row depths show
9480 for (row = 0; row < rowCount; ++row)
9481 {
5d98e603 9482 for (int col = 0; col < colCount; ++col)
8e77fd8b
JS
9483 {
9484 wxRichTextCell* cell = table->GetCell(row, col);
9485 if (cell && cell->IsShown())
9486 {
9487 wxPoint position(cell->GetPosition().x, rowTops[row]);
9488
9489 // GetRowspan() will usually return 1, but may be greater
9490 wxSize size(cell->GetCachedSize().GetWidth(), rowTops[row + cell->GetRowspan()] - rowTops[row] - paddingY);
9491
9492 wxRect availableCellSpace = wxRect(position, size);
9493 cell->Invalidate(wxRICHTEXT_ALL);
9494 cell->Layout(dc, context, availableCellSpace, availableSpace, style);
9495 cell->SetCachedSize(size);
9496 }
9497 }
9498
9499 bottomY = rowTops[rowCount] - paddingY;
9500 }
9501}
9502
603f702b
JS
9503// Lays the object out. rect is the space available for layout. Often it will
9504// be the specified overall space for this object, if trying to constrain
9505// layout to a particular size, or it could be the total space available in the
9506// parent. rect is the overall size, so we must subtract margins and padding.
9507// to get the actual available space.
8db2e3ef 9508bool wxRichTextTable::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& WXUNUSED(parentRect), int style)
bec80f4f 9509{
603f702b
JS
9510 SetPosition(rect.GetPosition());
9511
8a28cd66 9512 // The meaty bit. Calculate sizes of all cells and rows. Try to use
603f702b
JS
9513 // minimum size if within alloted size, then divide up remaining size
9514 // between rows/cols.
9515
9516 double scale = 1.0;
9517 wxRichTextBuffer* buffer = GetBuffer();
9518 if (buffer) scale = buffer->GetScale();
9519
8db2e3ef 9520 wxRect availableSpace = GetAvailableContentArea(dc, context, rect);
603f702b
JS
9521 wxTextAttrDimensionConverter converter(dc, scale, availableSpace.GetSize());
9522
8db2e3ef
JS
9523 wxRichTextAttr attr(GetAttributes());
9524 context.ApplyVirtualAttributes(attr, this);
9525
fc63fb9a 9526 bool tableHasPercentWidth = (attr.GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE);
603f702b
JS
9527 // If we have no fixed table size, and assuming we're not pushed for
9528 // space, then we don't have to try to stretch the table to fit the contents.
fc63fb9a
JS
9529 bool stretchToFitTableWidth = tableHasPercentWidth;
9530
603f702b 9531 int tableWidth = rect.width;
fc63fb9a 9532 if (attr.GetTextBoxAttr().GetWidth().IsValid() && !tableHasPercentWidth)
603f702b 9533 {
8db2e3ef 9534 tableWidth = converter.GetPixels(attr.GetTextBoxAttr().GetWidth());
603f702b
JS
9535
9536 // Fixed table width, so we do want to stretch columns out if necessary.
9537 stretchToFitTableWidth = true;
9538
9539 // Shouldn't be able to exceed the size passed to this function
9540 tableWidth = wxMin(rect.width, tableWidth);
9541 }
9542
9543 // Get internal padding
36307fdf 9544 int paddingLeft = 0, paddingTop = 0;
8db2e3ef
JS
9545 if (attr.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
9546 paddingLeft = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetLeft());
9547 if (attr.GetTextBoxAttr().GetPadding().GetTop().IsValid())
9548 paddingTop = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetTop());
603f702b
JS
9549
9550 // Assume that left and top padding are also used for inter-cell padding.
9551 int paddingX = paddingLeft;
9552 int paddingY = paddingTop;
9553
9554 int totalLeftMargin = 0, totalRightMargin = 0, totalTopMargin = 0, totalBottomMargin = 0;
8db2e3ef 9555 GetTotalMargin(dc, buffer, attr, totalLeftMargin, totalRightMargin, totalTopMargin, totalBottomMargin);
603f702b
JS
9556
9557 // Internal table width - the area for content
9558 int internalTableWidth = tableWidth - totalLeftMargin - totalRightMargin;
9559
9560 int rowCount = m_cells.GetCount();
9561 if (m_colCount == 0 || rowCount == 0)
9562 {
9563 wxRect overallRect(rect.x, rect.y, totalLeftMargin + totalRightMargin, totalTopMargin + totalBottomMargin);
9564 SetCachedSize(overallRect.GetSize());
9565
9566 // Zero content size
9567 SetMinSize(overallRect.GetSize());
9568 SetMaxSize(GetMinSize());
9569 return true;
9570 }
9571
9572 // The final calculated widths
bb7bbd12
JS
9573 wxArrayInt colWidths;
9574 colWidths.Add(0, m_colCount);
603f702b 9575
bb7bbd12
JS
9576 wxArrayInt absoluteColWidths;
9577 absoluteColWidths.Add(0, m_colCount);
7c9fdebe 9578
bb7bbd12
JS
9579 wxArrayInt percentageColWidths;
9580 percentageColWidths.Add(0, m_colCount);
603f702b
JS
9581 // wxArrayInt percentageColWidthsSpanning(m_colCount);
9582 // These are only relevant when the first column contains spanning information.
9583 // wxArrayInt columnSpans(m_colCount); // Each contains 1 for non-spanning cell, > 1 for spanning cell.
bb7bbd12
JS
9584 wxArrayInt maxColWidths;
9585 maxColWidths.Add(0, m_colCount);
9586 wxArrayInt minColWidths;
9587 minColWidths.Add(0, m_colCount);
603f702b
JS
9588
9589 wxSize tableSize(tableWidth, 0);
9590
9591 int i, j, k;
9592
9593 for (i = 0; i < m_colCount; i++)
9594 {
9595 absoluteColWidths[i] = 0;
9596 // absoluteColWidthsSpanning[i] = 0;
9597 percentageColWidths[i] = -1;
9598 // percentageColWidthsSpanning[i] = -1;
9599 colWidths[i] = 0;
9600 maxColWidths[i] = 0;
9601 minColWidths[i] = 0;
9602 // columnSpans[i] = 1;
9603 }
9604
9605 // (0) Determine which cells are visible according to spans
9606 // 1 2 3 4 5
9607 // __________________
9608 // | | | | | 1
9609 // |------| |----|
9610 // |------| | | 2
9611 // |------| | | 3
9612 // |------------------|
9613 // |__________________| 4
9614
9615 // To calculate cell visibility:
9616 // First find all spanning cells. Build an array of span records with start x, y and end x, y.
9617 // Then for each cell, test whether we're within one of those cells, and unless we're at the start of
9618 // that cell, hide the cell.
9619
9620 // We can also use this array to match the size of spanning cells to the grid. Or just do
9621 // this when we iterate through all cells.
9622
9623 // 0.1: add spanning cells to an array
9624 wxRichTextRectArray rectArray;
9625 for (j = 0; j < m_rowCount; j++)
9626 {
9627 for (i = 0; i < m_colCount; i++)
9628 {
8a28cd66
JS
9629 wxRichTextCell* cell = GetCell(j, i);
9630 int colSpan = cell->GetColspan();
9631 int rowSpan = cell->GetRowspan();
603f702b
JS
9632 if (colSpan > 1 || rowSpan > 1)
9633 {
9634 rectArray.Add(wxRect(i, j, colSpan, rowSpan));
9635 }
9636 }
9637 }
9638 // 0.2: find which cells are subsumed by a spanning cell
9639 for (j = 0; j < m_rowCount; j++)
9640 {
9641 for (i = 0; i < m_colCount; i++)
9642 {
8a28cd66 9643 wxRichTextCell* cell = GetCell(j, i);
603f702b
JS
9644 if (rectArray.GetCount() == 0)
9645 {
9646 cell->Show(true);
9647 }
9648 else
9649 {
8a28cd66
JS
9650 int colSpan = cell->GetColspan();
9651 int rowSpan = cell->GetRowspan();
9652
603f702b
JS
9653 if (colSpan > 1 || rowSpan > 1)
9654 {
9655 // Assume all spanning cells are shown
9656 cell->Show(true);
9657 }
9658 else
9659 {
9660 bool shown = true;
9661 for (k = 0; k < (int) rectArray.GetCount(); k++)
9662 {
9663 if (rectArray[k].Contains(wxPoint(i, j)))
9664 {
9665 shown = false;
9666 break;
9667 }
9668 }
9669 cell->Show(shown);
9670 }
9671 }
9672 }
9673 }
9674
8a28cd66 9675 // Find the first spanned cell in each row that spans the most columns and doesn't
603f702b
JS
9676 // overlap with a spanned cell starting at a previous column position.
9677 // This means we need to keep an array of rects so we can check. However
9678 // it does also mean that some spans simply may not be taken into account
9679 // where there are different spans happening on different rows. In these cases,
9680 // they will simply be as wide as their constituent columns.
9681
9682 // (1) Do an initial layout for all cells to get minimum and maximum size, and get
9683 // the absolute or percentage width of each column.
9684
9685 for (j = 0; j < m_rowCount; j++)
9686 {
9687 // First get the overall margins so we can calculate percentage widths based on
9688 // the available content space for all cells on the row
9689
9690 int overallRowContentMargin = 0;
9691 int visibleCellCount = 0;
9692
9693 for (i = 0; i < m_colCount; i++)
9694 {
9695 wxRichTextBox* cell = GetCell(j, i);
9696 if (cell->IsShown())
9697 {
9698 int cellTotalLeftMargin = 0, cellTotalRightMargin = 0, cellTotalTopMargin = 0, cellTotalBottomMargin = 0;
9699 GetTotalMargin(dc, buffer, cell->GetAttributes(), cellTotalLeftMargin, cellTotalRightMargin, cellTotalTopMargin, cellTotalBottomMargin);
9700
9701 overallRowContentMargin += (cellTotalLeftMargin + cellTotalRightMargin);
9702 visibleCellCount ++;
9703 }
9704 }
9705
9706 // Add in inter-cell padding
9707 overallRowContentMargin += ((visibleCellCount-1) * paddingX);
9708
9709 int rowContentWidth = internalTableWidth - overallRowContentMargin;
9710 wxSize rowTableSize(rowContentWidth, 0);
9711 wxTextAttrDimensionConverter converter(dc, scale, rowTableSize);
9712
9713 for (i = 0; i < m_colCount; i++)
9714 {
8a28cd66 9715 wxRichTextCell* cell = GetCell(j, i);
603f702b
JS
9716 if (cell->IsShown())
9717 {
8a28cd66 9718 int colSpan = cell->GetColspan();
603f702b
JS
9719
9720 // Lay out cell to find min/max widths
9721 cell->Invalidate(wxRICHTEXT_ALL);
8db2e3ef 9722 cell->Layout(dc, context, availableSpace, availableSpace, style);
603f702b
JS
9723
9724 if (colSpan == 1)
9725 {
9726 int absoluteCellWidth = -1;
9727 int percentageCellWidth = -1;
9728
9729 // I think we need to calculate percentages from the internal table size,
9730 // minus the padding between cells which we'll need to calculate from the
9731 // (number of VISIBLE cells - 1)*paddingX. Then percentages that add up to 100%
9732 // will add up to 100%. In CSS, the width specifies the cell's content rect width,
9733 // so if we want to conform to that we'll need to add in the overall cell margins.
9734 // However, this will make it difficult to specify percentages that add up to
9735 // 100% and still fit within the table width.
9736 // Let's say two cells have 50% width. They have 10 pixels of overall margin each.
9737 // The table content rect is 500 pixels and the inter-cell padding is 20 pixels.
9738 // If we're using internal content size for the width, we would calculate the
9739 // the overall cell width for n cells as:
9740 // (500 - 20*(n-1) - overallCellMargin1 - overallCellMargin2 - ...) * percentage / 100
9741 // + thisOverallCellMargin
9742 // = 500 - 20 - 10 - 10) * 0.5 + 10 = 240 pixels overall cell width.
9743 // Adding this back, we get 240 + 240 + 20 = 500 pixels.
9744
9745 if (cell->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9746 {
9747 int w = converter.GetPixels(cell->GetAttributes().GetTextBoxAttr().GetWidth());
9748 if (cell->GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
9749 {
9750 percentageCellWidth = w;
9751 }
9752 else
9753 {
9754 absoluteCellWidth = w;
9755 }
9756 // Override absolute width with minimum width if necessary
9757 if (cell->GetMinSize().x > 0 && absoluteCellWidth !=1 && cell->GetMinSize().x > absoluteCellWidth)
9758 absoluteCellWidth = cell->GetMinSize().x;
9759 }
9760
9761 if (absoluteCellWidth != -1)
9762 {
9763 if (absoluteCellWidth > absoluteColWidths[i])
9764 absoluteColWidths[i] = absoluteCellWidth;
9765 }
9766
9767 if (percentageCellWidth != -1)
9768 {
9769 if (percentageCellWidth > percentageColWidths[i])
9770 percentageColWidths[i] = percentageCellWidth;
9771 }
9772
9773 if (colSpan == 1 && cell->GetMinSize().x && cell->GetMinSize().x > minColWidths[i])
9774 minColWidths[i] = cell->GetMinSize().x;
9775 if (colSpan == 1 && cell->GetMaxSize().x && cell->GetMaxSize().x > maxColWidths[i])
9776 maxColWidths[i] = cell->GetMaxSize().x;
9777 }
9778 }
9779 }
9780 }
9781
9782 // (2) Allocate initial column widths from minimum widths, absolute values and proportions
9783 // TODO: simply merge this into (1).
9784 for (i = 0; i < m_colCount; i++)
9785 {
9786 if (absoluteColWidths[i] > 0)
9787 {
9788 colWidths[i] = absoluteColWidths[i];
9789 }
9790 else if (percentageColWidths[i] > 0)
9791 {
9792 colWidths[i] = percentageColWidths[i];
9793
9794 // This is rubbish - we calculated the absolute widths from percentages, so
9795 // we can't do it again here.
9796 //colWidths[i] = (int) (double(percentageColWidths[i]) * double(tableWidth) / 100.0 + 0.5);
9797 }
9798 }
9799
9800 // (3) Process absolute or proportional widths of spanning columns,
9801 // now that we know what our fixed column widths are going to be.
9802 // Spanned cells will try to adjust columns so the span will fit.
9803 // Even existing fixed column widths can be expanded if necessary.
9804 // Actually, currently fixed columns widths aren't adjusted; instead,
9805 // the algorithm favours earlier rows and adjusts unspecified column widths
9806 // the first time only. After that, we can't know whether the column has been
9807 // specified explicitly or not. (We could make a note if necessary.)
9808 for (j = 0; j < m_rowCount; j++)
9809 {
9810 // First get the overall margins so we can calculate percentage widths based on
9811 // the available content space for all cells on the row
9812
9813 int overallRowContentMargin = 0;
9814 int visibleCellCount = 0;
9815
9816 for (i = 0; i < m_colCount; i++)
9817 {
9818 wxRichTextBox* cell = GetCell(j, i);
9819 if (cell->IsShown())
9820 {
9821 int cellTotalLeftMargin = 0, cellTotalRightMargin = 0, cellTotalTopMargin = 0, cellTotalBottomMargin = 0;
9822 GetTotalMargin(dc, buffer, cell->GetAttributes(), cellTotalLeftMargin, cellTotalRightMargin, cellTotalTopMargin, cellTotalBottomMargin);
9823
9824 overallRowContentMargin += (cellTotalLeftMargin + cellTotalRightMargin);
9825 visibleCellCount ++;
9826 }
9827 }
9828
9829 // Add in inter-cell padding
9830 overallRowContentMargin += ((visibleCellCount-1) * paddingX);
9831
9832 int rowContentWidth = internalTableWidth - overallRowContentMargin;
9833 wxSize rowTableSize(rowContentWidth, 0);
9834 wxTextAttrDimensionConverter converter(dc, scale, rowTableSize);
9835
9836 for (i = 0; i < m_colCount; i++)
9837 {
8a28cd66 9838 wxRichTextCell* cell = GetCell(j, i);
603f702b
JS
9839 if (cell->IsShown())
9840 {
8a28cd66 9841 int colSpan = cell->GetColspan();
603f702b
JS
9842 if (colSpan > 1)
9843 {
9844 int spans = wxMin(colSpan, m_colCount - i);
9845 int cellWidth = 0;
9846 if (spans > 0)
9847 {
9848 if (cell->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9849 {
9850 cellWidth = converter.GetPixels(cell->GetAttributes().GetTextBoxAttr().GetWidth());
9851 // Override absolute width with minimum width if necessary
9852 if (cell->GetMinSize().x > 0 && cellWidth !=1 && cell->GetMinSize().x > cellWidth)
9853 cellWidth = cell->GetMinSize().x;
9854 }
9855 else
9856 {
9857 // Do we want to do this? It's the only chance we get to
9858 // use the cell's min/max sizes, so we need to work out
9859 // how we're going to balance the unspecified spanning cell
9860 // width with the possibility more-constrained constituent cell widths.
9861 // Say there's a tiny bitmap giving it a max width of 10 pixels. We
9862 // don't want to constraint all the spanned columns to fit into this cell.
9863 // OK, let's say that if any of the constituent columns don't fit,
9864 // then we simply stop constraining the columns; instead, we'll just fit the spanning
9865 // cells to the columns later.
9866 cellWidth = cell->GetMinSize().x;
9867 if (cell->GetMaxSize().x > cellWidth)
9868 cellWidth = cell->GetMaxSize().x;
9869 }
9870
9871 // Subtract the padding between cells
9872 int spanningWidth = cellWidth;
9873 spanningWidth -= paddingX * (spans-1);
9874
9875 if (spanningWidth > 0)
9876 {
9877 // Now share the spanning width between columns within that span
9878 // TODO: take into account min widths of columns within the span
9879 int spanningWidthLeft = spanningWidth;
9880 int stretchColCount = 0;
9881 for (k = i; k < (i+spans); k++)
9882 {
9883 if (colWidths[k] > 0) // absolute or proportional width has been specified
9884 spanningWidthLeft -= colWidths[k];
9885 else
9886 stretchColCount ++;
9887 }
9888 // Now divide what's left between the remaining columns
9889 int colShare = 0;
9890 if (stretchColCount > 0)
9891 colShare = spanningWidthLeft / stretchColCount;
9892 int colShareRemainder = spanningWidthLeft - (colShare * stretchColCount);
9893
9894 // If fixed-width columns are currently too big, then we'll later
9895 // stretch the spanned cell to fit.
9896
9897 if (spanningWidthLeft > 0)
9898 {
9899 for (k = i; k < (i+spans); k++)
9900 {
9901 if (colWidths[k] <= 0) // absolute or proportional width has not been specified
9902 {
9903 int newWidth = colShare;
9904 if (k == (i+spans-1))
9905 newWidth += colShareRemainder; // ensure all pixels are filled
9906 colWidths[k] = newWidth;
9907 }
9908 }
9909 }
9910 }
9911 }
9912 }
9913 }
9914 }
9915 }
9916
9917 // (4) Next, share any remaining space out between columns that have not yet been calculated.
9918 // TODO: take into account min widths of columns within the span
9919 int tableWidthMinusPadding = internalTableWidth - (m_colCount-1)*paddingX;
9920 int widthLeft = tableWidthMinusPadding;
9921 int stretchColCount = 0;
9922 for (i = 0; i < m_colCount; i++)
9923 {
603f702b
JS
9924 // Subtract min width from width left, then
9925 // add the colShare to the min width
9926 if (colWidths[i] > 0) // absolute or proportional width has been specified
9927 widthLeft -= colWidths[i];
9928 else
9929 {
9930 if (minColWidths[i] > 0)
9931 widthLeft -= minColWidths[i];
9932
9933 stretchColCount ++;
9934 }
9935 }
9936
9937 // Now divide what's left between the remaining columns
9938 int colShare = 0;
9939 if (stretchColCount > 0)
9940 colShare = widthLeft / stretchColCount;
9941 int colShareRemainder = widthLeft - (colShare * stretchColCount);
9942
9943 // Check we don't have enough space, in which case shrink all columns, overriding
9944 // any absolute/proportional widths
9945 // TODO: actually we would like to divide up the shrinkage according to size.
9946 // How do we calculate the proportions that will achieve this?
9947 // Could first choose an arbitrary value for stretching cells, and then calculate
9948 // factors to multiply each width by.
9949 // TODO: want to record this fact and pass to an iteration that tries e.g. min widths
9950 if (widthLeft < 0 || (stretchToFitTableWidth && (stretchColCount == 0)))
9951 {
9952 colShare = tableWidthMinusPadding / m_colCount;
9953 colShareRemainder = tableWidthMinusPadding - (colShare * m_colCount);
9954 for (i = 0; i < m_colCount; i++)
9955 {
9956 colWidths[i] = 0;
9957 minColWidths[i] = 0;
9958 }
9959 }
9960
9961 // We have to adjust the columns if either we need to shrink the
9962 // table to fit the parent/table width, or we explicitly set the
9963 // table width and need to stretch out the table.
9964 if (widthLeft < 0 || stretchToFitTableWidth)
9965 {
9966 for (i = 0; i < m_colCount; i++)
9967 {
9968 if (colWidths[i] <= 0) // absolute or proportional width has not been specified
9969 {
9970 if (minColWidths[i] > 0)
9971 colWidths[i] = minColWidths[i] + colShare;
9972 else
9973 colWidths[i] = colShare;
9974 if (i == (m_colCount-1))
9975 colWidths[i] += colShareRemainder; // ensure all pixels are filled
9976 }
9977 }
9978 }
9979
9980 // TODO: if spanned cells have no specified or max width, make them the
9981 // as big as the columns they span. Do this for all spanned cells in all
9982 // rows, of course. Size any spanned cells left over at the end - even if they
9983 // have width > 0, make sure they're limited to the appropriate column edge.
9984
9985
9986/*
9987 Sort out confusion between content width
9988 and overall width later. For now, assume we specify overall width.
9989
9990 So, now we've laid out the table to fit into the given space
9991 and have used specified widths and minimum widths.
9992
9993 Now we need to consider how we will try to take maximum width into account.
9994
9995*/
9996
9997 // (??) TODO: take max width into account
9998
9999 // (6) Lay out all cells again with the current values
10000
10001 int maxRight = 0;
10002 int y = availableSpace.y;
10003 for (j = 0; j < m_rowCount; j++)
10004 {
10005 int x = availableSpace.x; // TODO: take into account centering etc.
10006 int maxCellHeight = 0;
10007 int maxSpecifiedCellHeight = 0;
10008
bb7bbd12
JS
10009 wxArrayInt actualWidths;
10010 actualWidths.Add(0, m_colCount);
603f702b
JS
10011
10012 wxTextAttrDimensionConverter converter(dc, scale);
10013 for (i = 0; i < m_colCount; i++)
10014 {
10015 wxRichTextCell* cell = GetCell(j, i);
10016 if (cell->IsShown())
10017 {
603f702b
JS
10018 // Get max specified cell height
10019 // Don't handle percentages for height
10020 if (cell->GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && cell->GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() != wxTEXT_ATTR_UNITS_PERCENTAGE)
10021 {
10022 int h = converter.GetPixels(cell->GetAttributes().GetTextBoxAttr().GetHeight());
10023 if (h > maxSpecifiedCellHeight)
10024 maxSpecifiedCellHeight = h;
10025 }
10026
10027 if (colWidths[i] > 0) // absolute or proportional width has been specified
10028 {
8a28cd66 10029 int colSpan = cell->GetColspan();
603f702b
JS
10030 wxRect availableCellSpace;
10031
8a28cd66 10032 // Take into account spans
603f702b
JS
10033 if (colSpan > 1)
10034 {
10035 // Calculate the size of this spanning cell from its constituent columns
8a28cd66 10036 int xx = 0;
603f702b 10037 int spans = wxMin(colSpan, m_colCount - i);
8a28cd66 10038 for (k = i; k < (i+spans); k++)
603f702b
JS
10039 {
10040 if (k != i)
10041 xx += paddingX;
10042 xx += colWidths[k];
10043 }
10044 availableCellSpace = wxRect(x, y, xx, -1);
10045 }
10046 else
10047 availableCellSpace = wxRect(x, y, colWidths[i], -1);
10048
10049 // Store actual width so we can force cell to be the appropriate width on the final loop
10050 actualWidths[i] = availableCellSpace.GetWidth();
10051
8e77fd8b
JS
10052 // We now need to shift right by the width of any rowspanning cells above-left of us
10053 int deltaX = GetRowspanDisplacement(this, j, i, paddingX, colWidths);
10054 availableCellSpace.SetX(availableCellSpace.GetX() + deltaX);
10055
603f702b
JS
10056 // Lay out cell
10057 cell->Invalidate(wxRICHTEXT_ALL);
8db2e3ef 10058 cell->Layout(dc, context, availableCellSpace, availableSpace, style);
603f702b
JS
10059
10060 // TODO: use GetCachedSize().x to compute 'natural' size
10061
10062 x += (availableCellSpace.GetWidth() + paddingX);
8e77fd8b 10063 if ((cell->GetCachedSize().y > maxCellHeight) && (cell->GetRowspan() < 2))
603f702b
JS
10064 maxCellHeight = cell->GetCachedSize().y;
10065 }
10066 }
10067 }
10068
10069 maxCellHeight = wxMax(maxCellHeight, maxSpecifiedCellHeight);
10070
10071 for (i = 0; i < m_colCount; i++)
10072 {
10073 wxRichTextCell* cell = GetCell(j, i);
10074 if (cell->IsShown())
10075 {
10076 wxRect availableCellSpace = wxRect(cell->GetPosition(), wxSize(actualWidths[i], maxCellHeight));
10077 // Lay out cell with new height
10078 cell->Invalidate(wxRICHTEXT_ALL);
8db2e3ef 10079 cell->Layout(dc, context, availableCellSpace, availableSpace, style);
603f702b
JS
10080
10081 // Make sure the cell size really is the appropriate size,
10082 // not the calculated box size
10083 cell->SetCachedSize(wxSize(actualWidths[i], maxCellHeight));
10084
10085 maxRight = wxMax(maxRight, cell->GetPosition().x + cell->GetCachedSize().x);
10086 }
10087 }
10088
10089 y += maxCellHeight;
10090 if (j < (m_rowCount-1))
10091 y += paddingY;
10092 }
8e77fd8b
JS
10093
10094 // Finally we need to expand any cell with rowspan > 1. We couldn't earlier; lower rows' heights weren't known
10095 ExpandCellsWithRowspan(this, paddingY, y, dc, context, availableSpace, style);
603f702b
JS
10096
10097 // We need to add back the margins etc.
10098 {
10099 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
10100 contentRect = wxRect(wxPoint(0, 0), wxSize(maxRight - availableSpace.x, y - availableSpace.y));
8db2e3ef 10101 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
10102 SetCachedSize(marginRect.GetSize());
10103 }
10104
10105 // TODO: calculate max size
10106 {
10107 SetMaxSize(GetCachedSize());
10108 }
10109
10110 // TODO: calculate min size
10111 {
10112 SetMinSize(GetCachedSize());
10113 }
10114
10115 // TODO: currently we use either a fixed table width or the parent's size.
10116 // We also want to be able to calculate the table width from its content,
10117 // whether using fixed column widths or cell content min/max width.
10118 // Probably need a boolean flag to say whether we need to stretch cells
10119 // to fit the table width, or to simply use min/max cell widths. The
10120 // trouble with this is that if cell widths are not specified, they
10121 // will be tiny; we could use arbitrary defaults but this seems unsatisfactory.
10122 // Anyway, ignoring that problem, we probably need to factor layout into a function
10123 // that can can calculate the maximum unconstrained layout in case table size is
10124 // not specified. Then LayoutToBestSize() can choose to use either parent size to
10125 // constrain Layout(), or the previously-calculated max size to constraint layout.
10126
10127 return true;
10128}
10129
10130// Finds the absolute position and row height for the given character position
8db2e3ef 10131bool wxRichTextTable::FindPosition(wxDC& dc, wxRichTextDrawingContext& context, long index, wxPoint& pt, int* height, bool forceLineStart)
603f702b
JS
10132{
10133 wxRichTextCell* child = GetCell(index+1);
10134 if (child)
10135 {
10136 // Find the position at the start of the child cell, since the table doesn't
10137 // have any caret position of its own.
8db2e3ef 10138 return child->FindPosition(dc, context, -1, pt, height, forceLineStart);
603f702b
JS
10139 }
10140 else
10141 return false;
10142}
10143
10144// Get the cell at the given character position (in the range of the table).
10145wxRichTextCell* wxRichTextTable::GetCell(long pos) const
10146{
10147 int row = 0, col = 0;
10148 if (GetCellRowColumnPosition(pos, row, col))
10149 {
10150 return GetCell(row, col);
10151 }
10152 else
10153 return NULL;
10154}
10155
10156// Get the row/column for a given character position
10157bool wxRichTextTable::GetCellRowColumnPosition(long pos, int& row, int& col) const
10158{
10159 if (m_colCount == 0 || m_rowCount == 0)
10160 return false;
10161
10162 row = (int) (pos / m_colCount);
10163 col = pos - (row * m_colCount);
10164
10165 wxASSERT(row < m_rowCount && col < m_colCount);
10166
10167 if (row < m_rowCount && col < m_colCount)
10168 return true;
10169 else
10170 return false;
10171}
10172
10173// Calculate range, taking row/cell ordering into account instead of relying
10174// on list ordering.
10175void wxRichTextTable::CalculateRange(long start, long& end)
10176{
10177 long current = start;
10178 long lastEnd = current;
10179
10180 if (IsTopLevel())
10181 {
10182 current = 0;
10183 lastEnd = 0;
10184 }
10185
10186 int i, j;
10187 for (i = 0; i < m_rowCount; i++)
10188 {
10189 for (j = 0; j < m_colCount; j++)
10190 {
10191 wxRichTextCell* child = GetCell(i, j);
10192 if (child)
10193 {
10194 long childEnd = 0;
10195
10196 child->CalculateRange(current, childEnd);
10197
10198 lastEnd = childEnd;
10199 current = childEnd + 1;
10200 }
10201 }
10202 }
10203
10204 // A top-level object always has a range of size 1,
10205 // because its children don't count at this level.
10206 end = start;
10207 m_range.SetRange(start, start);
10208
10209 // An object with no children has zero length
10210 if (m_children.GetCount() == 0)
10211 lastEnd --;
10212 m_ownRange.SetRange(0, lastEnd);
10213}
10214
10215// Gets the range size.
914a4e23 10216bool wxRichTextTable::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, const wxPoint& position, const wxSize& parentSize, wxArrayInt* partialExtents) const
603f702b 10217{
914a4e23 10218 return wxRichTextBox::GetRangeSize(range, size, descent, dc, context, flags, position, parentSize, partialExtents);
603f702b
JS
10219}
10220
10221// Deletes content in the given range.
10222bool wxRichTextTable::DeleteRange(const wxRichTextRange& WXUNUSED(range))
10223{
10224 // TODO: implement deletion of cells
10225 return true;
10226}
10227
10228// Gets any text in this object for the given range.
10229wxString wxRichTextTable::GetTextForRange(const wxRichTextRange& range) const
10230{
10231 return wxRichTextBox::GetTextForRange(range);
10232}
10233
10234// Copies this object.
10235void wxRichTextTable::Copy(const wxRichTextTable& obj)
10236{
10237 wxRichTextBox::Copy(obj);
10238
10239 ClearTable();
10240
10241 m_rowCount = obj.m_rowCount;
10242 m_colCount = obj.m_colCount;
10243
10244 m_cells.Add(wxRichTextObjectPtrArray(), m_rowCount);
10245
10246 int i, j;
10247 for (i = 0; i < m_rowCount; i++)
10248 {
10249 wxRichTextObjectPtrArray& colArray = m_cells[i];
10250 for (j = 0; j < m_colCount; j++)
10251 {
10252 wxRichTextCell* cell = wxDynamicCast(obj.GetCell(i, j)->Clone(), wxRichTextCell);
10253 AppendChild(cell);
10254
10255 colArray.Add(cell);
10256 }
10257 }
10258}
10259
10260void wxRichTextTable::ClearTable()
10261{
10262 m_cells.Clear();
10263 DeleteChildren();
0a6ec346
VZ
10264 m_rowCount = 0;
10265 m_colCount = 0;
603f702b
JS
10266}
10267
10268bool wxRichTextTable::CreateTable(int rows, int cols)
10269{
10270 ClearTable();
10271
10272 m_rowCount = rows;
10273 m_colCount = cols;
10274
10275 m_cells.Add(wxRichTextObjectPtrArray(), rows);
10276
10277 int i, j;
10278 for (i = 0; i < rows; i++)
10279 {
10280 wxRichTextObjectPtrArray& colArray = m_cells[i];
10281 for (j = 0; j < cols; j++)
10282 {
10283 wxRichTextCell* cell = new wxRichTextCell;
10284 AppendChild(cell);
10285 cell->AddParagraph(wxEmptyString);
10286
10287 colArray.Add(cell);
10288 }
10289 }
10290
10291 return true;
10292}
10293
10294wxRichTextCell* wxRichTextTable::GetCell(int row, int col) const
10295{
10296 wxASSERT(row < m_rowCount);
10297 wxASSERT(col < m_colCount);
10298
10299 if (row < m_rowCount && col < m_colCount)
10300 {
10301 wxRichTextObjectPtrArray& colArray = m_cells[row];
10302 wxRichTextObject* obj = colArray[col];
10303 return wxDynamicCast(obj, wxRichTextCell);
10304 }
10305 else
d67faa04 10306 return NULL;
603f702b
JS
10307}
10308
10309// Returns a selection object specifying the selections between start and end character positions.
10310// For example, a table would deduce what cells (of range length 1) are selected when dragging across the table.
10311wxRichTextSelection wxRichTextTable::GetSelection(long start, long end) const
10312{
10313 wxRichTextSelection selection;
10314 selection.SetContainer((wxRichTextTable*) this);
10315
10316 if (start > end)
10317 {
10318 long tmp = end;
10319 end = start;
10320 start = tmp;
10321 }
10322
10323 wxASSERT( start >= 0 && end < (m_colCount * m_rowCount));
10324
10325 if (end >= (m_colCount * m_rowCount))
10326 return selection;
10327
10328 // We need to find the rectangle of cells that is described by the rectangle
10329 // with start, end as the diagonal. Make sure we don't add cells that are
10330 // not currenty visible because they are overlapped by spanning cells.
10331/*
10332 --------------------------
10333 | 0 | 1 | 2 | 3 | 4 |
10334 --------------------------
10335 | 5 | 6 | 7 | 8 | 9 |
10336 --------------------------
10337 | 10 | 11 | 12 | 13 | 14 |
10338 --------------------------
10339 | 15 | 16 | 17 | 18 | 19 |
10340 --------------------------
10341
10342 Let's say we select 6 -> 18.
10343
10344 Left and right edge cols of rectangle are 1 and 3 inclusive. Find least/greatest to find
10345 which is left and which is right.
10346
10347 Top and bottom edge rows are 1 and 3 inclusive. Again, find least/greatest to find top and bottom.
10348
10349 Now go through rows from 1 to 3 and only add cells that are (a) within above column range
10350 and (b) shown.
10351
10352
10353*/
10354
10355 int leftCol = start - m_colCount * int(start/m_colCount);
10356 int rightCol = end - m_colCount * int(end/m_colCount);
10357
10358 int topRow = int(start/m_colCount);
10359 int bottomRow = int(end/m_colCount);
10360
10361 if (leftCol > rightCol)
10362 {
10363 int tmp = rightCol;
10364 rightCol = leftCol;
10365 leftCol = tmp;
10366 }
10367
10368 if (topRow > bottomRow)
10369 {
10370 int tmp = bottomRow;
10371 bottomRow = topRow;
10372 topRow = tmp;
10373 }
10374
10375 int i, j;
10376 for (i = topRow; i <= bottomRow; i++)
10377 {
10378 for (j = leftCol; j <= rightCol; j++)
10379 {
10380 wxRichTextCell* cell = GetCell(i, j);
10381 if (cell && cell->IsShown())
10382 selection.Add(cell->GetRange());
10383 }
10384 }
10385
10386 return selection;
10387}
10388
10389// Sets the attributes for the cells specified by the selection.
10390bool wxRichTextTable::SetCellStyle(const wxRichTextSelection& selection, const wxRichTextAttr& style, int flags)
10391{
10392 if (selection.GetContainer() != this)
10393 return false;
10394
10395 wxRichTextBuffer* buffer = GetBuffer();
10396 bool haveControl = (buffer && buffer->GetRichTextCtrl() != NULL);
10397 bool withUndo = haveControl && ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
10398
10399 if (withUndo)
10400 buffer->BeginBatchUndo(_("Set Cell Style"));
10401
10402 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
10403 while (node)
10404 {
10405 wxRichTextCell* cell = wxDynamicCast(node->GetData(), wxRichTextCell);
10406 if (cell && selection.WithinSelection(cell->GetRange().GetStart()))
10407 SetStyle(cell, style, flags);
10408 node = node->GetNext();
10409 }
10410
10411 // Do action, or delay it until end of batch.
10412 if (withUndo)
10413 buffer->EndBatchUndo();
10414
10415 return true;
10416}
10417
4c86168d
JS
10418wxPosition wxRichTextTable::GetFocusedCell() const
10419{
10420 wxPosition position(-1, -1);
10421 const wxRichTextObject* focus = GetBuffer()->GetRichTextCtrl()->GetFocusObject();
10422
10423 for (int row = 0; row < GetRowCount(); ++row)
10424 {
10425 for (int col = 0; col < GetColumnCount(); ++col)
10426 {
10427 if (GetCell(row, col) == focus)
10428 {
10429 position.SetRow(row);
10430 position.SetCol(col);
10431 return position;
10432 }
10433 }
10434 }
10435
10436 return position;
10437}
10438
603f702b
JS
10439bool wxRichTextTable::DeleteRows(int startRow, int noRows)
10440{
ef3f0679
JS
10441 wxASSERT((startRow + noRows) <= m_rowCount);
10442 if ((startRow + noRows) > m_rowCount)
603f702b
JS
10443 return false;
10444
4c86168d
JS
10445 wxCHECK_MSG(noRows != m_rowCount, false, "Trying to delete all the cells in a table");
10446
31be8400 10447 wxRichTextBuffer* buffer = GetBuffer();
4c86168d
JS
10448 wxRichTextCtrl* rtc = buffer->GetRichTextCtrl();
10449
10450 wxPosition position = GetFocusedCell();
10451 int focusCol = position.GetCol();
10452 int focusRow = position.GetRow();
10453 if (focusRow >= startRow && focusRow < (startRow+noRows))
10454 {
10455 // Deleting a focused cell causes a segfault later when laying out, due to GetFocusedObject() returning an invalid object
10456 if ((startRow + noRows) < m_rowCount)
10457 {
10458 // There are more rows after the one(s) to be deleted, so set focus in the first of them
10459 rtc->SetFocusObject(GetCell(startRow + noRows, focusCol));
10460 }
10461 else
10462 {
10463 // Otherwise set focus in the preceding row
10464 rtc->SetFocusObject(GetCell(startRow - 1, focusCol));
10465 }
10466 }
10467
31be8400
JS
10468 wxRichTextAction* action = NULL;
10469 wxRichTextTable* clone = NULL;
4c86168d 10470 if (!rtc->SuppressingUndo())
31be8400
JS
10471 {
10472 // Create a clone containing the current state of the table. It will be used to Undo the action
10473 clone = wxStaticCast(this->Clone(), wxRichTextTable);
10474 clone->SetParent(GetParent());
4c86168d 10475 action = new wxRichTextAction(NULL, _("Delete row"), wxRICHTEXT_CHANGE_OBJECT, buffer, this, rtc);
31be8400
JS
10476 action->SetObject(this);
10477 action->SetPosition(GetRange().GetStart());
10478 }
10479
603f702b
JS
10480 int i, j;
10481 for (i = startRow; i < (startRow+noRows); i++)
10482 {
10483 wxRichTextObjectPtrArray& colArray = m_cells[startRow];
10484 for (j = 0; j < (int) colArray.GetCount(); j++)
10485 {
10486 wxRichTextObject* cell = colArray[j];
10487 RemoveChild(cell, true);
10488 }
10489
10490 // Keep deleting at the same position, since we move all
10491 // the others up
10492 m_cells.RemoveAt(startRow);
10493 }
10494
10495 m_rowCount = m_rowCount - noRows;
10496
4c86168d 10497 if (!rtc->SuppressingUndo())
31be8400
JS
10498 {
10499 buffer->SubmitAction(action);
10500 // Finally store the original-state clone; doing so earlier would cause various failures
10501 action->StoreObject(clone);
10502 }
10503
603f702b
JS
10504 return true;
10505}
10506
10507bool wxRichTextTable::DeleteColumns(int startCol, int noCols)
10508{
ef3f0679
JS
10509 wxASSERT((startCol + noCols) <= m_colCount);
10510 if ((startCol + noCols) > m_colCount)
603f702b
JS
10511 return false;
10512
4c86168d
JS
10513 wxCHECK_MSG(noCols != m_colCount, false, "Trying to delete all the cells in a table");
10514
31be8400 10515 wxRichTextBuffer* buffer = GetBuffer();
4c86168d
JS
10516 wxRichTextCtrl* rtc = buffer->GetRichTextCtrl();
10517
10518 wxPosition position = GetFocusedCell();
10519 int focusCol = position.GetCol();
10520 int focusRow = position.GetRow();
10521 if (focusCol >= startCol && focusCol < (startCol+noCols))
10522 {
10523 // Deleting a focused cell causes a segfault later when laying out, due to GetFocusedObject() returning an invalid object
10524 if ((startCol + noCols) < m_colCount)
10525 {
10526 // There are more columns after the one(s) to be deleted, so set focus in the first of them
10527 rtc->SetFocusObject(GetCell(focusRow, startCol + noCols));
10528 }
10529 else
10530 {
10531 // Otherwise set focus in the preceding column
10532 rtc->SetFocusObject(GetCell(focusRow, startCol - 1));
10533 }
10534 }
10535
31be8400
JS
10536 wxRichTextAction* action = NULL;
10537 wxRichTextTable* clone = NULL;
4c86168d 10538 if (!rtc->SuppressingUndo())
31be8400
JS
10539 {
10540 // Create a clone containing the current state of the table. It will be used to Undo the action
10541 clone = wxStaticCast(this->Clone(), wxRichTextTable);
10542 clone->SetParent(GetParent());
4c86168d 10543 action = new wxRichTextAction(NULL, _("Delete column"), wxRICHTEXT_CHANGE_OBJECT, buffer, this, rtc);
31be8400
JS
10544 action->SetObject(this);
10545 action->SetPosition(GetRange().GetStart());
10546 }
10547
603f702b
JS
10548 bool deleteRows = (noCols == m_colCount);
10549
10550 int i, j;
10551 for (i = 0; i < m_rowCount; i++)
10552 {
10553 wxRichTextObjectPtrArray& colArray = m_cells[deleteRows ? 0 : i];
ef3f0679 10554 for (j = 0; j < noCols; j++)
603f702b 10555 {
ef3f0679 10556 wxRichTextObject* cell = colArray[startCol];
603f702b 10557 RemoveChild(cell, true);
ef3f0679 10558 colArray.RemoveAt(startCol);
603f702b
JS
10559 }
10560
10561 if (deleteRows)
10562 m_cells.RemoveAt(0);
10563 }
10564
10565 if (deleteRows)
10566 m_rowCount = 0;
10567 m_colCount = m_colCount - noCols;
10568
4c86168d 10569 if (!rtc->SuppressingUndo())
31be8400
JS
10570 {
10571 buffer->SubmitAction(action);
10572 // Finally store the original-state clone; doing so earlier would cause various failures
10573 action->StoreObject(clone);
10574 }
10575
603f702b
JS
10576 return true;
10577}
10578
10579bool wxRichTextTable::AddRows(int startRow, int noRows, const wxRichTextAttr& attr)
10580{
10581 wxASSERT(startRow <= m_rowCount);
10582 if (startRow > m_rowCount)
10583 return false;
10584
31be8400
JS
10585 wxRichTextBuffer* buffer = GetBuffer();
10586 wxRichTextAction* action = NULL;
10587 wxRichTextTable* clone = NULL;
10588 if (!buffer->GetRichTextCtrl()->SuppressingUndo())
10589 {
10590 // Create a clone containing the current state of the table. It will be used to Undo the action
10591 clone = wxStaticCast(this->Clone(), wxRichTextTable);
10592 clone->SetParent(GetParent());
10593 action = new wxRichTextAction(NULL, _("Add row"), wxRICHTEXT_CHANGE_OBJECT, buffer, this, buffer->GetRichTextCtrl());
10594 action->SetObject(this);
10595 action->SetPosition(GetRange().GetStart());
10596 }
10597
603f702b
JS
10598 int i, j;
10599 for (i = 0; i < noRows; i++)
10600 {
10601 int idx;
10602 if (startRow == m_rowCount)
10603 {
10604 m_cells.Add(wxRichTextObjectPtrArray());
10605 idx = m_cells.GetCount() - 1;
10606 }
10607 else
10608 {
10609 m_cells.Insert(wxRichTextObjectPtrArray(), startRow+i);
10610 idx = startRow+i;
10611 }
10612
10613 wxRichTextObjectPtrArray& colArray = m_cells[idx];
10614 for (j = 0; j < m_colCount; j++)
10615 {
10616 wxRichTextCell* cell = new wxRichTextCell;
10617 cell->GetAttributes() = attr;
10618
10619 AppendChild(cell);
a9fd42cc 10620 cell->AddParagraph(wxEmptyString);
603f702b
JS
10621 colArray.Add(cell);
10622 }
10623 }
10624
10625 m_rowCount = m_rowCount + noRows;
31be8400
JS
10626
10627 if (!buffer->GetRichTextCtrl()->SuppressingUndo())
10628 {
10629 buffer->SubmitAction(action);
10630 // Finally store the original-state clone; doing so earlier would cause various failures
10631 action->StoreObject(clone);
10632 }
10633
603f702b
JS
10634 return true;
10635}
10636
10637bool wxRichTextTable::AddColumns(int startCol, int noCols, const wxRichTextAttr& attr)
10638{
10639 wxASSERT(startCol <= m_colCount);
10640 if (startCol > m_colCount)
10641 return false;
10642
31be8400
JS
10643 wxRichTextBuffer* buffer = GetBuffer();
10644 wxRichTextAction* action = NULL;
10645 wxRichTextTable* clone = NULL;
10646 if (!buffer->GetRichTextCtrl()->SuppressingUndo())
10647 {
10648 // Create a clone containing the current state of the table. It will be used to Undo the action
10649 clone = wxStaticCast(this->Clone(), wxRichTextTable);
10650 clone->SetParent(GetParent());
10651 action = new wxRichTextAction(NULL, _("Add column"), wxRICHTEXT_CHANGE_OBJECT, buffer, this, buffer->GetRichTextCtrl());
10652 action->SetObject(this);
10653 action->SetPosition(GetRange().GetStart());
10654 }
10655
603f702b
JS
10656 int i, j;
10657 for (i = 0; i < m_rowCount; i++)
10658 {
10659 wxRichTextObjectPtrArray& colArray = m_cells[i];
10660 for (j = 0; j < noCols; j++)
10661 {
10662 wxRichTextCell* cell = new wxRichTextCell;
10663 cell->GetAttributes() = attr;
10664
10665 AppendChild(cell);
a9fd42cc 10666 cell->AddParagraph(wxEmptyString);
603f702b
JS
10667
10668 if (startCol == m_colCount)
10669 colArray.Add(cell);
10670 else
10671 colArray.Insert(cell, startCol+j);
10672 }
10673 }
10674
10675 m_colCount = m_colCount + noCols;
10676
31be8400
JS
10677 if (!buffer->GetRichTextCtrl()->SuppressingUndo())
10678 {
10679 buffer->SubmitAction(action);
10680 // Finally store the original-state clone; doing so earlier would cause various failures
10681 action->StoreObject(clone);
10682 }
10683
603f702b 10684 return true;
5ad9ae3a
JS
10685}
10686
603f702b
JS
10687// Edit properties via a GUI
10688bool wxRichTextTable::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
5ad9ae3a 10689{
603f702b
JS
10690 wxRichTextObjectPropertiesDialog boxDlg(this, wxGetTopLevelParent(parent), wxID_ANY, _("Table Properties"));
10691 boxDlg.SetAttributes(GetAttributes());
10692
10693 if (boxDlg.ShowModal() == wxID_OK)
5ad9ae3a 10694 {
603f702b
JS
10695 boxDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
10696 return true;
5ad9ae3a
JS
10697 }
10698 else
10699 return false;
bec80f4f
JS
10700}
10701
5d7836c4
JS
10702/*
10703 * Module to initialise and clean up handlers
10704 */
10705
10706class wxRichTextModule: public wxModule
10707{
10708DECLARE_DYNAMIC_CLASS(wxRichTextModule)
10709public:
10710 wxRichTextModule() {}
cfa3b256
JS
10711 bool OnInit()
10712 {
d2d0adc7 10713 wxRichTextBuffer::SetRenderer(new wxRichTextStdRenderer);
cfa3b256
JS
10714 wxRichTextBuffer::InitStandardHandlers();
10715 wxRichTextParagraph::InitDefaultTabs();
1aca9fcd
JS
10716
10717 wxRichTextXMLHandler::RegisterNodeName(wxT("text"), wxT("wxRichTextPlainText"));
10718 wxRichTextXMLHandler::RegisterNodeName(wxT("symbol"), wxT("wxRichTextPlainText"));
10719 wxRichTextXMLHandler::RegisterNodeName(wxT("image"), wxT("wxRichTextImage"));
10720 wxRichTextXMLHandler::RegisterNodeName(wxT("paragraph"), wxT("wxRichTextParagraph"));
10721 wxRichTextXMLHandler::RegisterNodeName(wxT("paragraphlayout"), wxT("wxRichTextParagraphLayoutBox"));
10722 wxRichTextXMLHandler::RegisterNodeName(wxT("textbox"), wxT("wxRichTextBox"));
10723 wxRichTextXMLHandler::RegisterNodeName(wxT("cell"), wxT("wxRichTextCell"));
10724 wxRichTextXMLHandler::RegisterNodeName(wxT("table"), wxT("wxRichTextTable"));
10725 wxRichTextXMLHandler::RegisterNodeName(wxT("field"), wxT("wxRichTextField"));
10726
cfa3b256 10727 return true;
47b378bd 10728 }
cfa3b256
JS
10729 void OnExit()
10730 {
10731 wxRichTextBuffer::CleanUpHandlers();
8db2e3ef 10732 wxRichTextBuffer::CleanUpDrawingHandlers();
7c9fdebe 10733 wxRichTextBuffer::CleanUpFieldTypes();
1aca9fcd 10734 wxRichTextXMLHandler::ClearNodeToClassMap();
cfa3b256
JS
10735 wxRichTextDecimalToRoman(-1);
10736 wxRichTextParagraph::ClearDefaultTabs();
dadd4f55 10737 wxRichTextCtrl::ClearAvailableFontNames();
d2d0adc7 10738 wxRichTextBuffer::SetRenderer(NULL);
47b378bd 10739 }
5d7836c4
JS
10740};
10741
10742IMPLEMENT_DYNAMIC_CLASS(wxRichTextModule, wxModule)
10743
10744
f1d6804f
RD
10745// If the richtext lib is dynamically loaded after the app has already started
10746// (such as from wxPython) then the built-in module system will not init this
10747// module. Provide this function to do it manually.
10748void wxRichTextModuleInit()
10749{
10750 wxModule* module = new wxRichTextModule;
f1d6804f 10751 wxModule::RegisterModule(module);
58d1949f 10752 wxModule::InitializeModules();
f1d6804f
RD
10753}
10754
10755
5d7836c4
JS
10756/*!
10757 * Commands for undo/redo
10758 *
10759 */
10760
10761wxRichTextCommand::wxRichTextCommand(const wxString& name, wxRichTextCommandId id, wxRichTextBuffer* buffer,
603f702b 10762 wxRichTextParagraphLayoutBox* container, wxRichTextCtrl* ctrl, bool ignoreFirstTime): wxCommand(true, name)
5d7836c4 10763{
603f702b 10764 /* wxRichTextAction* action = */ new wxRichTextAction(this, name, id, buffer, container, ctrl, ignoreFirstTime);
5d7836c4
JS
10765}
10766
7fe8059f 10767wxRichTextCommand::wxRichTextCommand(const wxString& name): wxCommand(true, name)
5d7836c4
JS
10768{
10769}
10770
10771wxRichTextCommand::~wxRichTextCommand()
10772{
10773 ClearActions();
10774}
10775
10776void wxRichTextCommand::AddAction(wxRichTextAction* action)
10777{
10778 if (!m_actions.Member(action))
10779 m_actions.Append(action);
10780}
10781
10782bool wxRichTextCommand::Do()
10783{
09f14108 10784 for (wxList::compatibility_iterator node = m_actions.GetFirst(); node; node = node->GetNext())
5d7836c4
JS
10785 {
10786 wxRichTextAction* action = (wxRichTextAction*) node->GetData();
10787 action->Do();
10788 }
10789
10790 return true;
10791}
10792
10793bool wxRichTextCommand::Undo()
10794{
09f14108 10795 for (wxList::compatibility_iterator node = m_actions.GetLast(); node; node = node->GetPrevious())
5d7836c4
JS
10796 {
10797 wxRichTextAction* action = (wxRichTextAction*) node->GetData();
10798 action->Undo();
10799 }
10800
10801 return true;
10802}
10803
10804void wxRichTextCommand::ClearActions()
10805{
10806 WX_CLEAR_LIST(wxList, m_actions);
10807}
10808
10809/*!
10810 * Individual action
10811 *
10812 */
10813
603f702b
JS
10814wxRichTextAction::wxRichTextAction(wxRichTextCommand* cmd, const wxString& name, wxRichTextCommandId id,
10815 wxRichTextBuffer* buffer, wxRichTextParagraphLayoutBox* container,
10816 wxRichTextCtrl* ctrl, bool ignoreFirstTime)
5d7836c4
JS
10817{
10818 m_buffer = buffer;
603f702b
JS
10819 m_object = NULL;
10820 m_containerAddress.Create(buffer, container);
5d7836c4
JS
10821 m_ignoreThis = ignoreFirstTime;
10822 m_cmdId = id;
10823 m_position = -1;
10824 m_ctrl = ctrl;
10825 m_name = name;
10826 m_newParagraphs.SetDefaultStyle(buffer->GetDefaultStyle());
10827 m_newParagraphs.SetBasicStyle(buffer->GetBasicStyle());
10828 if (cmd)
10829 cmd->AddAction(this);
10830}
10831
10832wxRichTextAction::~wxRichTextAction()
10833{
603f702b
JS
10834 if (m_object)
10835 delete m_object;
10836}
10837
10838// Returns the container that this action refers to, using the container address and top-level buffer.
10839wxRichTextParagraphLayoutBox* wxRichTextAction::GetContainer() const
10840{
10841 wxRichTextParagraphLayoutBox* container = wxDynamicCast(GetContainerAddress().GetObject(m_buffer), wxRichTextParagraphLayoutBox);
10842 return container;
5d7836c4
JS
10843}
10844
603f702b 10845
7051fa41
JS
10846void wxRichTextAction::CalculateRefreshOptimizations(wxArrayInt& optimizationLineCharPositions, wxArrayInt& optimizationLineYPositions)
10847{
10848 // Store a list of line start character and y positions so we can figure out which area
10849 // we need to refresh
10850
10851#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
603f702b
JS
10852 wxRichTextParagraphLayoutBox* container = GetContainer();
10853 wxASSERT(container != NULL);
10854 if (!container)
10855 return;
10856
7051fa41
JS
10857 // NOTE: we're assuming that the buffer is laid out correctly at this point.
10858 // If we had several actions, which only invalidate and leave layout until the
10859 // paint handler is called, then this might not be true. So we may need to switch
10860 // optimisation on only when we're simply adding text and not simultaneously
10861 // deleting a selection, for example. Or, we make sure the buffer is laid out correctly
10862 // first, but of course this means we'll be doing it twice.
603f702b 10863 if (!m_buffer->IsDirty() && m_ctrl) // can only do optimisation if the buffer is already laid out correctly
7051fa41 10864 {
4ba36292
JS
10865 wxSize clientSize = m_ctrl->GetUnscaledSize(m_ctrl->GetClientSize());
10866 wxPoint firstVisiblePt = m_ctrl->GetUnscaledPoint(m_ctrl->GetFirstVisiblePoint());
7051fa41
JS
10867 int lastY = firstVisiblePt.y + clientSize.y;
10868
603f702b
JS
10869 wxRichTextParagraph* para = container->GetParagraphAtPosition(GetRange().GetStart());
10870 wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(para);
7051fa41
JS
10871 while (node)
10872 {
10873 wxRichTextParagraph* child = (wxRichTextParagraph*) node->GetData();
10874 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
10875 while (node2)
10876 {
10877 wxRichTextLine* line = node2->GetData();
10878 wxPoint pt = line->GetAbsolutePosition();
10879 wxRichTextRange range = line->GetAbsoluteRange();
10880
10881 if (pt.y > lastY)
10882 {
10883 node2 = wxRichTextLineList::compatibility_iterator();
10884 node = wxRichTextObjectList::compatibility_iterator();
10885 }
10886 else if (range.GetStart() > GetPosition() && pt.y >= firstVisiblePt.y)
10887 {
10888 optimizationLineCharPositions.Add(range.GetStart());
10889 optimizationLineYPositions.Add(pt.y);
10890 }
10891
10892 if (node2)
10893 node2 = node2->GetNext();
10894 }
10895
10896 if (node)
10897 node = node->GetNext();
10898 }
10899 }
10900#endif
10901}
10902
5d7836c4
JS
10903bool wxRichTextAction::Do()
10904{
10905 m_buffer->Modify(true);
10906
603f702b
JS
10907 wxRichTextParagraphLayoutBox* container = GetContainer();
10908 wxASSERT(container != NULL);
10909 if (!container)
10910 return false;
10911
5d7836c4
JS
10912 switch (m_cmdId)
10913 {
10914 case wxRICHTEXT_INSERT:
10915 {
ea160b2e
JS
10916 // Store a list of line start character and y positions so we can figure out which area
10917 // we need to refresh
10918 wxArrayInt optimizationLineCharPositions;
10919 wxArrayInt optimizationLineYPositions;
10920
10921#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
7051fa41 10922 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
ea160b2e
JS
10923#endif
10924
603f702b
JS
10925 container->InsertFragment(GetRange().GetStart(), m_newParagraphs);
10926 container->UpdateRanges();
10927
10928 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10929 // Layout() would stop prematurely at the top level.
10930 container->InvalidateHierarchy(wxRichTextRange(wxMax(0, GetRange().GetStart()-1), GetRange().GetEnd()));
5d7836c4 10931
603f702b 10932 long newCaretPosition = GetPosition() + m_newParagraphs.GetOwnRange().GetLength();
0ca07313
JS
10933
10934 // Character position to caret position
10935 newCaretPosition --;
10936
10937 // Don't take into account the last newline
5d7836c4
JS
10938 if (m_newParagraphs.GetPartialParagraph())
10939 newCaretPosition --;
46ee0e5b 10940 else
7c081bd2 10941 if (m_newParagraphs.GetChildren().GetCount() > 1)
46ee0e5b
JS
10942 {
10943 wxRichTextObject* p = (wxRichTextObject*) m_newParagraphs.GetChildren().GetLast()->GetData();
10944 if (p->GetRange().GetLength() == 1)
10945 newCaretPosition --;
10946 }
5d7836c4 10947
603f702b 10948 newCaretPosition = wxMin(newCaretPosition, (container->GetOwnRange().GetEnd()-1));
0ca07313 10949
7051fa41 10950 UpdateAppearance(newCaretPosition, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions, true /* do */);
3e541562 10951
5912d19e 10952 wxRichTextEvent cmdEvent(
ce7fe42e 10953 wxEVT_RICHTEXT_CONTENT_INSERTED,
5912d19e
JS
10954 m_ctrl ? m_ctrl->GetId() : -1);
10955 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10956 cmdEvent.SetRange(GetRange());
10957 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 10958 cmdEvent.SetContainer(container);
3e541562 10959
5912d19e 10960 m_buffer->SendEvent(cmdEvent);
5d7836c4
JS
10961
10962 break;
10963 }
10964 case wxRICHTEXT_DELETE:
10965 {
7051fa41
JS
10966 wxArrayInt optimizationLineCharPositions;
10967 wxArrayInt optimizationLineYPositions;
10968
10969#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10970 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
10971#endif
10972
603f702b
JS
10973 container->DeleteRange(GetRange());
10974 container->UpdateRanges();
10975 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10976 // Layout() would stop prematurely at the top level.
10977 container->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
5d7836c4 10978
6ccbca24 10979 long caretPos = GetRange().GetStart()-1;
603f702b 10980 if (caretPos >= container->GetOwnRange().GetEnd())
6ccbca24
JS
10981 caretPos --;
10982
7051fa41 10983 UpdateAppearance(caretPos, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions, true /* do */);
5d7836c4 10984
5912d19e 10985 wxRichTextEvent cmdEvent(
ce7fe42e 10986 wxEVT_RICHTEXT_CONTENT_DELETED,
5912d19e
JS
10987 m_ctrl ? m_ctrl->GetId() : -1);
10988 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10989 cmdEvent.SetRange(GetRange());
10990 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 10991 cmdEvent.SetContainer(container);
3e541562 10992
5912d19e
JS
10993 m_buffer->SendEvent(cmdEvent);
10994
5d7836c4
JS
10995 break;
10996 }
10997 case wxRICHTEXT_CHANGE_STYLE:
590a0f8b 10998 case wxRICHTEXT_CHANGE_PROPERTIES:
5d7836c4
JS
10999 {
11000 ApplyParagraphs(GetNewParagraphs());
603f702b 11001
c4168888 11002 // Invalidate the whole buffer if there were floating objects
e12b91a3 11003 if (wxRichTextBuffer::GetFloatingLayoutMode() && container->GetFloatingObjectCount() > 0)
c4168888
JS
11004 m_buffer->InvalidateHierarchy(wxRICHTEXT_ALL);
11005 else
11006 {
11007 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
11008 // Layout() would stop prematurely at the top level.
11009 container->InvalidateHierarchy(GetRange());
11010 }
603f702b
JS
11011
11012 UpdateAppearance(GetPosition());
11013
11014 wxRichTextEvent cmdEvent(
ce7fe42e 11015 m_cmdId == wxRICHTEXT_CHANGE_STYLE ? wxEVT_RICHTEXT_STYLE_CHANGED : wxEVT_RICHTEXT_PROPERTIES_CHANGED,
603f702b
JS
11016 m_ctrl ? m_ctrl->GetId() : -1);
11017 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
11018 cmdEvent.SetRange(GetRange());
11019 cmdEvent.SetPosition(GetRange().GetStart());
11020 cmdEvent.SetContainer(container);
11021
11022 m_buffer->SendEvent(cmdEvent);
11023
11024 break;
11025 }
11026 case wxRICHTEXT_CHANGE_ATTRIBUTES:
11027 {
11028 wxRichTextObject* obj = m_objectAddress.GetObject(m_buffer); // container->GetChildAtPosition(GetRange().GetStart());
11029 if (obj)
11030 {
11031 wxRichTextAttr oldAttr = obj->GetAttributes();
11032 obj->GetAttributes() = m_attributes;
11033 m_attributes = oldAttr;
11034 }
11035
11036 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
11037 // Layout() would stop prematurely at the top level.
c4168888 11038 // Invalidate the whole buffer if there were floating objects
e12b91a3 11039 if (wxRichTextBuffer::GetFloatingLayoutMode() && container->GetFloatingObjectCount() > 0)
c4168888
JS
11040 m_buffer->InvalidateHierarchy(wxRICHTEXT_ALL);
11041 else
11042 container->InvalidateHierarchy(GetRange());
5d7836c4
JS
11043
11044 UpdateAppearance(GetPosition());
11045
5912d19e 11046 wxRichTextEvent cmdEvent(
ce7fe42e 11047 wxEVT_RICHTEXT_STYLE_CHANGED,
5912d19e
JS
11048 m_ctrl ? m_ctrl->GetId() : -1);
11049 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
11050 cmdEvent.SetRange(GetRange());
11051 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 11052 cmdEvent.SetContainer(container);
3e541562 11053
5912d19e
JS
11054 m_buffer->SendEvent(cmdEvent);
11055
603f702b
JS
11056 break;
11057 }
11058 case wxRICHTEXT_CHANGE_OBJECT:
11059 {
11060 wxRichTextObject* obj = m_objectAddress.GetObject(m_buffer);
31be8400 11061 if (obj && m_object && m_ctrl)
603f702b 11062 {
31be8400
JS
11063 // The plan is to swap the current object with the stored, previous-state, clone
11064 // We can't get 'node' from the containing buffer (as it doesn't directly store objects)
11065 // so use the parent paragraph
11066 wxRichTextParagraph* para = wxDynamicCast(obj->GetParent(), wxRichTextParagraph);
11067 wxCHECK_MSG(para, false, "Invalid parent paragraph");
bf5e92db
VZ
11068
11069 // The stored object, m_object, may have a stale parent paragraph. This would cause
11070 // a crash during layout, so use obj's parent para, which should be the correct one.
11071 // (An alternative would be to return the parent too from m_objectAddress.GetObject(),
11072 // or to set obj's parent there before returning)
11073 m_object->SetParent(para);
11074
31be8400 11075 wxRichTextObjectList::compatibility_iterator node = para->GetChildren().Find(obj);
603f702b
JS
11076 if (node)
11077 {
11078 wxRichTextObject* obj = node->GetData();
11079 node->SetData(m_object);
11080 m_object = obj;
11081 }
11082 }
11083
11084 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
11085 // Layout() would stop prematurely at the top level.
c4168888 11086 // Invalidate the whole buffer if there were floating objects
e12b91a3 11087 if (wxRichTextBuffer::GetFloatingLayoutMode() && container->GetFloatingObjectCount() > 0)
c4168888
JS
11088 m_buffer->InvalidateHierarchy(wxRICHTEXT_ALL);
11089 else
11090 container->InvalidateHierarchy(GetRange());
603f702b
JS
11091
11092 UpdateAppearance(GetPosition());
11093
11094 // TODO: send new kind of modification event
11095
5d7836c4
JS
11096 break;
11097 }
11098 default:
11099 break;
11100 }
11101
11102 return true;
11103}
11104
11105bool wxRichTextAction::Undo()
11106{
11107 m_buffer->Modify(true);
11108
603f702b
JS
11109 wxRichTextParagraphLayoutBox* container = GetContainer();
11110 wxASSERT(container != NULL);
11111 if (!container)
11112 return false;
11113
5d7836c4
JS
11114 switch (m_cmdId)
11115 {
11116 case wxRICHTEXT_INSERT:
11117 {
7051fa41
JS
11118 wxArrayInt optimizationLineCharPositions;
11119 wxArrayInt optimizationLineYPositions;
11120
11121#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
11122 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
11123#endif
11124
603f702b
JS
11125 container->DeleteRange(GetRange());
11126 container->UpdateRanges();
7c9fdebe 11127
603f702b
JS
11128 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
11129 // Layout() would stop prematurely at the top level.
11130 container->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
5d7836c4
JS
11131
11132 long newCaretPosition = GetPosition() - 1;
3e541562 11133
7051fa41 11134 UpdateAppearance(newCaretPosition, true, /* send update event */ & optimizationLineCharPositions, & optimizationLineYPositions, false /* undo */);
5d7836c4 11135
5912d19e 11136 wxRichTextEvent cmdEvent(
ce7fe42e 11137 wxEVT_RICHTEXT_CONTENT_DELETED,
5912d19e
JS
11138 m_ctrl ? m_ctrl->GetId() : -1);
11139 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
11140 cmdEvent.SetRange(GetRange());
11141 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 11142 cmdEvent.SetContainer(container);
3e541562 11143
5912d19e
JS
11144 m_buffer->SendEvent(cmdEvent);
11145
5d7836c4
JS
11146 break;
11147 }
11148 case wxRICHTEXT_DELETE:
11149 {
7051fa41
JS
11150 wxArrayInt optimizationLineCharPositions;
11151 wxArrayInt optimizationLineYPositions;
11152
11153#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
11154 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
11155#endif
11156
603f702b
JS
11157 container->InsertFragment(GetRange().GetStart(), m_oldParagraphs);
11158 container->UpdateRanges();
7c9fdebe 11159
603f702b
JS
11160 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
11161 // Layout() would stop prematurely at the top level.
11162 container->InvalidateHierarchy(GetRange());
5d7836c4 11163
7051fa41 11164 UpdateAppearance(GetPosition(), true, /* send update event */ & optimizationLineCharPositions, & optimizationLineYPositions, false /* undo */);
5d7836c4 11165
5912d19e 11166 wxRichTextEvent cmdEvent(
ce7fe42e 11167 wxEVT_RICHTEXT_CONTENT_INSERTED,
5912d19e
JS
11168 m_ctrl ? m_ctrl->GetId() : -1);
11169 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
11170 cmdEvent.SetRange(GetRange());
11171 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 11172 cmdEvent.SetContainer(container);
3e541562 11173
5912d19e
JS
11174 m_buffer->SendEvent(cmdEvent);
11175
5d7836c4
JS
11176 break;
11177 }
11178 case wxRICHTEXT_CHANGE_STYLE:
590a0f8b 11179 case wxRICHTEXT_CHANGE_PROPERTIES:
5d7836c4
JS
11180 {
11181 ApplyParagraphs(GetOldParagraphs());
603f702b
JS
11182 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
11183 // Layout() would stop prematurely at the top level.
11184 container->InvalidateHierarchy(GetRange());
5d7836c4
JS
11185
11186 UpdateAppearance(GetPosition());
11187
5912d19e 11188 wxRichTextEvent cmdEvent(
ce7fe42e 11189 m_cmdId == wxRICHTEXT_CHANGE_STYLE ? wxEVT_RICHTEXT_STYLE_CHANGED : wxEVT_RICHTEXT_PROPERTIES_CHANGED,
5912d19e
JS
11190 m_ctrl ? m_ctrl->GetId() : -1);
11191 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
11192 cmdEvent.SetRange(GetRange());
11193 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 11194 cmdEvent.SetContainer(container);
3e541562 11195
5912d19e
JS
11196 m_buffer->SendEvent(cmdEvent);
11197
5d7836c4
JS
11198 break;
11199 }
603f702b
JS
11200 case wxRICHTEXT_CHANGE_ATTRIBUTES:
11201 case wxRICHTEXT_CHANGE_OBJECT:
11202 {
11203 return Do();
11204 }
5d7836c4
JS
11205 default:
11206 break;
11207 }
11208
11209 return true;
11210}
11211
11212/// Update the control appearance
603f702b 11213void wxRichTextAction::UpdateAppearance(long caretPosition, bool sendUpdateEvent, wxArrayInt* optimizationLineCharPositions, wxArrayInt* optimizationLineYPositions, bool isDoCmd)
5d7836c4 11214{
603f702b
JS
11215 wxRichTextParagraphLayoutBox* container = GetContainer();
11216 wxASSERT(container != NULL);
11217 if (!container)
11218 return;
11219
5d7836c4
JS
11220 if (m_ctrl)
11221 {
603f702b 11222 m_ctrl->SetFocusObject(container);
5d7836c4 11223 m_ctrl->SetCaretPosition(caretPosition);
603f702b 11224
5d7836c4
JS
11225 if (!m_ctrl->IsFrozen())
11226 {
603f702b
JS
11227 wxRect containerRect = container->GetRect();
11228
2f36e8dc 11229 m_ctrl->LayoutContent();
5d7836c4 11230
603f702b
JS
11231 // Refresh everything if there were floating objects or the container changed size
11232 // (we can't yet optimize in these cases, since more complex interaction with other content occurs)
e12b91a3 11233 if ((wxRichTextBuffer::GetFloatingLayoutMode() && container->GetFloatingObjectCount() > 0) || (container->GetParent() && containerRect != container->GetRect()))
603f702b
JS
11234 {
11235 m_ctrl->Refresh(false);
11236 }
11237 else
11238
11239#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
11240 // Find refresh rectangle if we are in a position to optimise refresh
11241 if ((m_cmdId == wxRICHTEXT_INSERT || m_cmdId == wxRICHTEXT_DELETE) && optimizationLineCharPositions)
11242 {
11243 size_t i;
11244
4ba36292
JS
11245 wxSize clientSize = m_ctrl->GetUnscaledSize(m_ctrl->GetClientSize());
11246 wxPoint firstVisiblePt = m_ctrl->GetUnscaledPoint(m_ctrl->GetFirstVisiblePoint());
603f702b
JS
11247
11248 // Start/end positions
11249 int firstY = 0;
11250 int lastY = firstVisiblePt.y + clientSize.y;
11251
11252 bool foundEnd = false;
11253
11254 // position offset - how many characters were inserted
11255 int positionOffset = GetRange().GetLength();
11256
11257 // Determine whether this is Do or Undo, and adjust positionOffset accordingly
11258 if ((m_cmdId == wxRICHTEXT_DELETE && isDoCmd) || (m_cmdId == wxRICHTEXT_INSERT && !isDoCmd))
11259 positionOffset = - positionOffset;
11260
11261 // find the first line which is being drawn at the same position as it was
11262 // before. Since we're talking about a simple insertion, we can assume
11263 // that the rest of the window does not need to be redrawn.
93a16a7d 11264 long pos = GetRange().GetStart();
603f702b 11265
93a16a7d 11266 wxRichTextParagraph* para = container->GetParagraphAtPosition(pos, false /* is not caret pos */);
603f702b
JS
11267 // Since we support floating layout, we should redraw the whole para instead of just
11268 // the first line touching the invalid range.
11269 if (para)
11270 {
93a16a7d
JS
11271 // In case something was drawn above the paragraph,
11272 // such as a line break, allow a little extra.
11273 firstY = para->GetPosition().y - 4;
603f702b
JS
11274 }
11275
11276 wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(para);
11277 while (node)
11278 {
11279 wxRichTextParagraph* child = (wxRichTextParagraph*) node->GetData();
11280 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
11281 while (node2)
11282 {
11283 wxRichTextLine* line = node2->GetData();
11284 wxPoint pt = line->GetAbsolutePosition();
11285 wxRichTextRange range = line->GetAbsoluteRange();
11286
11287 // we want to find the first line that is in the same position
11288 // as before. This will mean we're at the end of the changed text.
11289
11290 if (pt.y > lastY) // going past the end of the window, no more info
11291 {
11292 node2 = wxRichTextLineList::compatibility_iterator();
11293 node = wxRichTextObjectList::compatibility_iterator();
11294 }
11295 // Detect last line in the buffer
11296 else if (!node2->GetNext() && para->GetRange().Contains(container->GetOwnRange().GetEnd()))
11297 {
11298 // If deleting text, make sure we refresh below as well as above
11299 if (positionOffset >= 0)
11300 {
11301 foundEnd = true;
11302 lastY = pt.y + line->GetSize().y;
11303 }
11304
11305 node2 = wxRichTextLineList::compatibility_iterator();
11306 node = wxRichTextObjectList::compatibility_iterator();
11307
11308 break;
11309 }
11310 else
11311 {
11312 // search for this line being at the same position as before
11313 for (i = 0; i < optimizationLineCharPositions->GetCount(); i++)
11314 {
11315 if (((*optimizationLineCharPositions)[i] + positionOffset == range.GetStart()) &&
11316 ((*optimizationLineYPositions)[i] == pt.y))
11317 {
11318 // Stop, we're now the same as we were
11319 foundEnd = true;
11320
93a16a7d 11321 lastY = pt.y + line->GetSize().y;
603f702b
JS
11322
11323 node2 = wxRichTextLineList::compatibility_iterator();
11324 node = wxRichTextObjectList::compatibility_iterator();
11325
11326 break;
11327 }
11328 }
11329 }
11330
11331 if (node2)
11332 node2 = node2->GetNext();
11333 }
11334
11335 if (node)
11336 node = node->GetNext();
11337 }
11338
11339 firstY = wxMax(firstVisiblePt.y, firstY);
11340 if (!foundEnd)
11341 lastY = firstVisiblePt.y + clientSize.y;
11342
11343 // Convert to device coordinates
4ba36292 11344 wxRect rect(m_ctrl->GetPhysicalPoint(m_ctrl->GetScaledPoint(wxPoint(firstVisiblePt.x, firstY))), m_ctrl->GetScaledSize(wxSize(clientSize.x, lastY - firstY)));
603f702b
JS
11345 m_ctrl->RefreshRect(rect);
11346 }
11347 else
1c13f06e 11348#endif
603f702b
JS
11349 m_ctrl->Refresh(false);
11350
11351 m_ctrl->PositionCaret();
4fe83b93
JS
11352
11353 // This causes styles to persist when doing programmatic
11354 // content creation except when Freeze/Thaw is used, so
11355 // disable this and check for the consequences.
11356 // m_ctrl->SetDefaultStyleToCursorStyle();
603f702b 11357
5d7836c4 11358 if (sendUpdateEvent)
0ec1179b 11359 wxTextCtrl::SendTextUpdatedEvent(m_ctrl);
5d7836c4 11360 }
7fe8059f 11361 }
5d7836c4
JS
11362}
11363
11364/// Replace the buffer paragraphs with the new ones.
0ca07313 11365void wxRichTextAction::ApplyParagraphs(const wxRichTextParagraphLayoutBox& fragment)
5d7836c4 11366{
603f702b
JS
11367 wxRichTextParagraphLayoutBox* container = GetContainer();
11368 wxASSERT(container != NULL);
11369 if (!container)
11370 return;
11371
5d7836c4
JS
11372 wxRichTextObjectList::compatibility_iterator node = fragment.GetChildren().GetFirst();
11373 while (node)
11374 {
11375 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
11376 wxASSERT (para != NULL);
11377
11378 // We'll replace the existing paragraph by finding the paragraph at this position,
11379 // delete its node data, and setting a copy as the new node data.
11380 // TODO: make more efficient by simply swapping old and new paragraph objects.
11381
603f702b 11382 wxRichTextParagraph* existingPara = container->GetParagraphAtPosition(para->GetRange().GetStart());
5d7836c4
JS
11383 if (existingPara)
11384 {
603f702b 11385 wxRichTextObjectList::compatibility_iterator bufferParaNode = container->GetChildren().Find(existingPara);
5d7836c4
JS
11386 if (bufferParaNode)
11387 {
11388 wxRichTextParagraph* newPara = new wxRichTextParagraph(*para);
603f702b 11389 newPara->SetParent(container);
5d7836c4
JS
11390
11391 bufferParaNode->SetData(newPara);
11392
11393 delete existingPara;
11394 }
11395 }
11396
11397 node = node->GetNext();
11398 }
11399}
11400
11401
11402/*!
11403 * wxRichTextRange
11404 * This stores beginning and end positions for a range of data.
11405 */
11406
603f702b
JS
11407WX_DEFINE_OBJARRAY(wxRichTextRangeArray);
11408
5d7836c4
JS
11409/// Limit this range to be within 'range'
11410bool wxRichTextRange::LimitTo(const wxRichTextRange& range)
11411{
11412 if (m_start < range.m_start)
11413 m_start = range.m_start;
11414
11415 if (m_end > range.m_end)
11416 m_end = range.m_end;
11417
11418 return true;
11419}
11420
11421/*!
11422 * wxRichTextImage implementation
11423 * This object represents an image.
11424 */
11425
bec80f4f 11426IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage, wxRichTextObject)
5d7836c4 11427
24777478 11428wxRichTextImage::wxRichTextImage(const wxImage& image, wxRichTextObject* parent, wxRichTextAttr* charStyle):
bec80f4f 11429 wxRichTextObject(parent)
5d7836c4 11430{
23698b12 11431 Init();
cdaed652 11432 m_imageBlock.MakeImageBlockDefaultQuality(image, wxBITMAP_TYPE_PNG);
4f32b3cf
JS
11433 if (charStyle)
11434 SetAttributes(*charStyle);
5d7836c4
JS
11435}
11436
24777478 11437wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock& imageBlock, wxRichTextObject* parent, wxRichTextAttr* charStyle):
bec80f4f 11438 wxRichTextObject(parent)
5d7836c4 11439{
23698b12 11440 Init();
5d7836c4 11441 m_imageBlock = imageBlock;
4f32b3cf
JS
11442 if (charStyle)
11443 SetAttributes(*charStyle);
5d7836c4
JS
11444}
11445
914a4e23
JS
11446wxRichTextImage::~wxRichTextImage()
11447{
11448}
11449
23698b12
JS
11450void wxRichTextImage::Init()
11451{
11452 m_originalImageSize = wxSize(-1, -1);
11453}
11454
cdaed652 11455/// Create a cached image at the required size
914a4e23 11456bool wxRichTextImage::LoadImageCache(wxDC& dc, bool resetCache, const wxSize& parentSize)
5d7836c4 11457{
23698b12
JS
11458 if (!m_imageBlock.IsOk())
11459 return false;
11460
11461 // If we have an original image size, use that to compute the cached bitmap size
11462 // instead of loading the image each time. This way we can avoid loading
11463 // the image so long as the new cached bitmap size hasn't changed.
11464
11465 wxImage image;
2798df59 11466 if (resetCache || m_originalImageSize.GetWidth() <= 0 || m_originalImageSize.GetHeight() <= 0)
cdaed652 11467 {
23698b12 11468 m_imageCache = wxNullBitmap;
ce00f59b 11469
cdaed652
VZ
11470 m_imageBlock.Load(image);
11471 if (!image.IsOk())
11472 return false;
ce00f59b 11473
23698b12
JS
11474 m_originalImageSize = wxSize(image.GetWidth(), image.GetHeight());
11475 }
11476
11477 int width = m_originalImageSize.GetWidth();
11478 int height = m_originalImageSize.GetHeight();
11479
11480 int parentWidth = 0;
11481 int parentHeight = 0;
bec80f4f 11482
23698b12
JS
11483 int maxWidth = -1;
11484 int maxHeight = -1;
11485
914a4e23
JS
11486 wxSize sz = parentSize;
11487 if (sz == wxDefaultSize)
23698b12 11488 {
914a4e23
JS
11489 if (GetParent() && GetParent()->GetParent())
11490 sz = GetParent()->GetParent()->GetCachedSize();
11491 }
ab6b1860 11492
914a4e23
JS
11493 if (sz != wxDefaultSize)
11494 {
11495 wxRichTextBuffer* buffer = GetBuffer();
11496 if (buffer)
11497 {
11498 // Find the actual space available when margin is taken into account
23698b12
JS
11499 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
11500 marginRect = wxRect(0, 0, sz.x, sz.y);
914a4e23
JS
11501 if (GetParent() && GetParent()->GetParent())
11502 {
11503 buffer->GetBoxRects(dc, buffer, GetParent()->GetParent()->GetAttributes(), marginRect, borderRect, contentRect, paddingRect, outlineRect);
11504 sz = contentRect.GetSize();
11505 }
23698b12 11506
914a4e23
JS
11507 // Use a minimum size to stop images becoming very small
11508 parentWidth = wxMax(100, sz.GetWidth());
11509 parentHeight = wxMax(100, sz.GetHeight());
23698b12 11510
914a4e23
JS
11511 if (buffer->GetRichTextCtrl())
11512 // Start with a maximum width of the control size, even if not specified by the content,
11513 // to minimize the amount of picture overlapping the right-hand side
11514 maxWidth = parentWidth;
cdaed652 11515 }
23698b12
JS
11516 }
11517
11518 if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetWidth().GetValue() > 0)
11519 {
11520 if (parentWidth > 0 && GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
11521 width = (int) ((GetAttributes().GetTextBoxAttr().GetWidth().GetValue() * parentWidth)/100.0);
11522 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
11523 width = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetWidth().GetValue());
11524 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
11525 width = GetAttributes().GetTextBoxAttr().GetWidth().GetValue();
11526 }
11527
11528 // Limit to max width
11529
11530 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() > 0)
11531 {
11532 int mw = -1;
11533
11534 if (parentWidth > 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
11535 mw = (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() * parentWidth)/100.0);
11536 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
11537 mw = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue());
11538 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
11539 mw = GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue();
11540
11541 // If we already have a smaller max width due to the constraints of the control size,
11542 // don't use the larger max width.
11543 if (mw != -1 && ((maxWidth == -1) || (mw < maxWidth)))
11544 maxWidth = mw;
11545 }
11546
11547 if (maxWidth > 0 && width > maxWidth)
11548 width = maxWidth;
11549
11550 // Preserve the aspect ratio
11551 if (width != m_originalImageSize.GetWidth())
11552 height = (int) (float(m_originalImageSize.GetHeight()) * (float(width)/float(m_originalImageSize.GetWidth())));
11553
11554 if (GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetHeight().GetValue() > 0)
11555 {
11556 if (parentHeight > 0 && GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
11557 height = (int) ((GetAttributes().GetTextBoxAttr().GetHeight().GetValue() * parentHeight)/100.0);
11558 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
11559 height = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetHeight().GetValue());
11560 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
11561 height = GetAttributes().GetTextBoxAttr().GetHeight().GetValue();
11562
11563 // Preserve the aspect ratio
11564 if (height != m_originalImageSize.GetHeight())
11565 width = (int) (float(m_originalImageSize.GetWidth()) * (float(height)/float(m_originalImageSize.GetHeight())));
11566 }
11567
11568 // Limit to max height
11569
11570 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() > 0)
11571 {
11572 if (parentHeight > 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
11573 maxHeight = (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() * parentHeight)/100.0);
11574 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
11575 maxHeight = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue());
11576 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
11577 maxHeight = GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue();
11578 }
11579
11580 if (maxHeight > 0 && height > maxHeight)
11581 {
11582 height = maxHeight;
11583
11584 // Preserve the aspect ratio
11585 if (height != m_originalImageSize.GetHeight())
11586 width = (int) (float(m_originalImageSize.GetWidth()) * (float(height)/float(m_originalImageSize.GetHeight())));
11587 }
11588
ab6b1860
JS
11589 // Prevent the use of zero size
11590 width = wxMax(1, width);
11591 height = wxMax(1, height);
11592
23698b12
JS
11593 if (m_imageCache.IsOk() && m_imageCache.GetWidth() == width && m_imageCache.GetHeight() == height)
11594 {
11595 // Do nothing, we didn't need to change the image cache
11596 }
11597 else
11598 {
11599 if (!image.IsOk())
cdaed652 11600 {
23698b12
JS
11601 m_imageBlock.Load(image);
11602 if (!image.IsOk())
11603 return false;
cdaed652 11604 }
5d7836c4 11605
cdaed652
VZ
11606 if (image.GetWidth() == width && image.GetHeight() == height)
11607 m_imageCache = wxBitmap(image);
11608 else
11609 {
11610 // If the original width and height is small, e.g. 400 or below,
11611 // scale up and then down to improve image quality. This can make
11612 // a big difference, with not much performance hit.
11613 int upscaleThreshold = 400;
11614 wxImage img;
11615 if (image.GetWidth() <= upscaleThreshold || image.GetHeight() <= upscaleThreshold)
11616 {
11617 img = image.Scale(image.GetWidth()*2, image.GetHeight()*2);
11618 img.Rescale(width, height, wxIMAGE_QUALITY_HIGH);
11619 }
11620 else
11621 img = image.Scale(width, height, wxIMAGE_QUALITY_HIGH);
11622 m_imageCache = wxBitmap(img);
11623 }
11624 }
ce00f59b 11625
cdaed652 11626 return m_imageCache.IsOk();
5d7836c4
JS
11627}
11628
5d7836c4 11629/// Draw the item
20d09da5 11630bool wxRichTextImage::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& WXUNUSED(range), const wxRichTextSelection& selection, const wxRect& rect, int WXUNUSED(descent), int WXUNUSED(style))
5d7836c4 11631{
603f702b
JS
11632 if (!IsShown())
11633 return true;
11634
cdaed652
VZ
11635 // Don't need cached size AFAIK
11636 // wxSize size = GetCachedSize();
11637 if (!LoadImageCache(dc))
5d7836c4 11638 return false;
ce00f59b 11639
8db2e3ef
JS
11640 wxRichTextAttr attr(GetAttributes());
11641 context.ApplyVirtualAttributes(attr, this);
11642
11643 DrawBoxAttributes(dc, GetBuffer(), attr, wxRect(rect.GetPosition(), GetCachedSize()));
603f702b 11644
603f702b
JS
11645 wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
11646 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
11647 marginRect = rect; // outer rectangle, will calculate contentRect
8db2e3ef 11648 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
11649
11650 dc.DrawBitmap(m_imageCache, contentRect.x, contentRect.y, true);
5d7836c4 11651
a70eb13e 11652 if (selection.WithinSelection(GetRange().GetStart(), this))
5d7836c4 11653 {
ecb5fbf1
JS
11654 wxCheckSetBrush(dc, *wxBLACK_BRUSH);
11655 wxCheckSetPen(dc, *wxBLACK_PEN);
5d7836c4 11656 dc.SetLogicalFunction(wxINVERT);
603f702b 11657 dc.DrawRectangle(contentRect);
5d7836c4
JS
11658 dc.SetLogicalFunction(wxCOPY);
11659 }
11660
11661 return true;
11662}
11663
11664/// Lay the item out
8db2e3ef 11665bool wxRichTextImage::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& WXUNUSED(parentRect), int WXUNUSED(style))
5d7836c4 11666{
cdaed652
VZ
11667 if (!LoadImageCache(dc))
11668 return false;
5d7836c4 11669
603f702b
JS
11670 wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
11671 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
11672 contentRect = wxRect(wxPoint(0,0), imageSize);
8db2e3ef
JS
11673
11674 wxRichTextAttr attr(GetAttributes());
11675 context.ApplyVirtualAttributes(attr, this);
11676
11677 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
11678
11679 wxSize overallSize = marginRect.GetSize();
11680
11681 SetCachedSize(overallSize);
11682 SetMaxSize(overallSize);
11683 SetMinSize(overallSize);
cdaed652 11684 SetPosition(rect.GetPosition());
5d7836c4
JS
11685
11686 return true;
11687}
11688
11689/// Get/set the object size for the given range. Returns false if the range
11690/// is invalid for this object.
914a4e23 11691bool wxRichTextImage::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& WXUNUSED(descent), wxDC& dc, wxRichTextDrawingContext& context, int WXUNUSED(flags), const wxPoint& WXUNUSED(position), const wxSize& parentSize, wxArrayInt* partialExtents) const
5d7836c4
JS
11692{
11693 if (!range.IsWithin(GetRange()))
11694 return false;
11695
914a4e23 11696 if (!((wxRichTextImage*)this)->LoadImageCache(dc, false, parentSize))
31778480 11697 {
cdaed652
VZ
11698 size.x = 0; size.y = 0;
11699 if (partialExtents)
31778480 11700 partialExtents->Add(0);
cdaed652 11701 return false;
31778480 11702 }
ce00f59b 11703
8db2e3ef
JS
11704 wxRichTextAttr attr(GetAttributes());
11705 context.ApplyVirtualAttributes(attr, (wxRichTextObject*) this);
11706
603f702b
JS
11707 wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
11708 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
11709 contentRect = wxRect(wxPoint(0,0), imageSize);
8db2e3ef 11710 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
11711
11712 wxSize overallSize = marginRect.GetSize();
31778480 11713
cdaed652 11714 if (partialExtents)
603f702b 11715 partialExtents->Add(overallSize.x);
5d7836c4 11716
603f702b 11717 size = overallSize;
5d7836c4
JS
11718
11719 return true;
11720}
11721
603f702b
JS
11722// Get the 'natural' size for an object. For an image, it would be the
11723// image size.
11724wxTextAttrSize wxRichTextImage::GetNaturalSize() const
11725{
11726 wxTextAttrSize size;
11727 if (GetImageCache().IsOk())
11728 {
11729 size.SetWidth(GetImageCache().GetWidth(), wxTEXT_ATTR_UNITS_PIXELS);
11730 size.SetHeight(GetImageCache().GetHeight(), wxTEXT_ATTR_UNITS_PIXELS);
11731 }
11732 return size;
11733}
11734
11735
5d7836c4
JS
11736/// Copy
11737void wxRichTextImage::Copy(const wxRichTextImage& obj)
11738{
bec80f4f 11739 wxRichTextObject::Copy(obj);
59509217 11740
5d7836c4 11741 m_imageBlock = obj.m_imageBlock;
23698b12 11742 m_originalImageSize = obj.m_originalImageSize;
5d7836c4
JS
11743}
11744
cdaed652
VZ
11745/// Edit properties via a GUI
11746bool wxRichTextImage::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
11747{
603f702b
JS
11748 wxRichTextObjectPropertiesDialog imageDlg(this, wxGetTopLevelParent(parent), wxID_ANY, _("Picture Properties"));
11749 imageDlg.SetAttributes(GetAttributes());
cdaed652
VZ
11750
11751 if (imageDlg.ShowModal() == wxID_OK)
11752 {
603f702b
JS
11753 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
11754 // indeterminate in the object.
11755 imageDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
cdaed652
VZ
11756 return true;
11757 }
11758 else
11759 return false;
11760}
11761
5d7836c4
JS
11762/*!
11763 * Utilities
11764 *
11765 */
11766
11767/// Compare two attribute objects
24777478 11768bool wxTextAttrEq(const wxRichTextAttr& attr1, const wxRichTextAttr& attr2)
5d7836c4 11769{
38f833b1 11770 return (attr1 == attr2);
5d7836c4
JS
11771}
11772
44cc96a8
JS
11773/// Compare tabs
11774bool wxRichTextTabsEq(const wxArrayInt& tabs1, const wxArrayInt& tabs2)
11775{
11776 if (tabs1.GetCount() != tabs2.GetCount())
5d7836c4
JS
11777 return false;
11778
44cc96a8
JS
11779 size_t i;
11780 for (i = 0; i < tabs1.GetCount(); i++)
11781 {
11782 if (tabs1[i] != tabs2[i])
11783 return false;
11784 }
11785 return true;
11786}
5d7836c4 11787
24777478 11788bool wxRichTextApplyStyle(wxRichTextAttr& destStyle, const wxRichTextAttr& style, wxRichTextAttr* compareWith)
44cc96a8
JS
11789{
11790 return destStyle.Apply(style, compareWith);
11791}
5d7836c4 11792
44cc96a8 11793// Remove attributes
24777478 11794bool wxRichTextRemoveStyle(wxRichTextAttr& destStyle, const wxRichTextAttr& style)
44cc96a8 11795{
24777478 11796 return destStyle.RemoveStyle(style);
44cc96a8 11797}
5d7836c4 11798
44cc96a8
JS
11799/// Combine two bitlists, specifying the bits of interest with separate flags.
11800bool wxRichTextCombineBitlists(int& valueA, int valueB, int& flagsA, int flagsB)
11801{
24777478 11802 return wxRichTextAttr::CombineBitlists(valueA, valueB, flagsA, flagsB);
44cc96a8 11803}
5d7836c4 11804
44cc96a8
JS
11805/// Compare two bitlists
11806bool wxRichTextBitlistsEqPartial(int valueA, int valueB, int flags)
11807{
24777478 11808 return wxRichTextAttr::BitlistsEqPartial(valueA, valueB, flags);
44cc96a8 11809}
38f833b1 11810
44cc96a8 11811/// Split into paragraph and character styles
24777478 11812bool wxRichTextSplitParaCharStyles(const wxRichTextAttr& style, wxRichTextAttr& parStyle, wxRichTextAttr& charStyle)
44cc96a8 11813{
24777478 11814 return wxRichTextAttr::SplitParaCharStyles(style, parStyle, charStyle);
44cc96a8 11815}
5d7836c4 11816
44cc96a8
JS
11817/// Convert a decimal to Roman numerals
11818wxString wxRichTextDecimalToRoman(long n)
11819{
11820 static wxArrayInt decimalNumbers;
11821 static wxArrayString romanNumbers;
5d7836c4 11822
44cc96a8
JS
11823 // Clean up arrays
11824 if (n == -1)
11825 {
11826 decimalNumbers.Clear();
11827 romanNumbers.Clear();
11828 return wxEmptyString;
11829 }
5d7836c4 11830
44cc96a8
JS
11831 if (decimalNumbers.GetCount() == 0)
11832 {
11833 #define wxRichTextAddDecRom(n, r) decimalNumbers.Add(n); romanNumbers.Add(r);
59509217 11834
44cc96a8
JS
11835 wxRichTextAddDecRom(1000, wxT("M"));
11836 wxRichTextAddDecRom(900, wxT("CM"));
11837 wxRichTextAddDecRom(500, wxT("D"));
11838 wxRichTextAddDecRom(400, wxT("CD"));
11839 wxRichTextAddDecRom(100, wxT("C"));
11840 wxRichTextAddDecRom(90, wxT("XC"));
11841 wxRichTextAddDecRom(50, wxT("L"));
11842 wxRichTextAddDecRom(40, wxT("XL"));
11843 wxRichTextAddDecRom(10, wxT("X"));
11844 wxRichTextAddDecRom(9, wxT("IX"));
11845 wxRichTextAddDecRom(5, wxT("V"));
11846 wxRichTextAddDecRom(4, wxT("IV"));
11847 wxRichTextAddDecRom(1, wxT("I"));
11848 }
5d7836c4 11849
44cc96a8
JS
11850 int i = 0;
11851 wxString roman;
ea160b2e 11852
44cc96a8 11853 while (n > 0 && i < 13)
42688aea 11854 {
44cc96a8
JS
11855 if (n >= decimalNumbers[i])
11856 {
11857 n -= decimalNumbers[i];
11858 roman += romanNumbers[i];
11859 }
11860 else
11861 {
11862 i ++;
11863 }
42688aea 11864 }
44cc96a8
JS
11865 if (roman.IsEmpty())
11866 roman = wxT("0");
11867 return roman;
11868}
42688aea 11869
44cc96a8
JS
11870/*!
11871 * wxRichTextFileHandler
11872 * Base class for file handlers
11873 */
4d6d8bf4 11874
44cc96a8 11875IMPLEMENT_CLASS(wxRichTextFileHandler, wxObject)
5d7836c4 11876
44cc96a8
JS
11877#if wxUSE_FFILE && wxUSE_STREAMS
11878bool wxRichTextFileHandler::LoadFile(wxRichTextBuffer *buffer, const wxString& filename)
5d7836c4 11879{
44cc96a8 11880 wxFFileInputStream stream(filename);
a1b806b9 11881 if (stream.IsOk())
44cc96a8 11882 return LoadFile(buffer, stream);
5d7836c4 11883
44cc96a8
JS
11884 return false;
11885}
5d7836c4 11886
44cc96a8
JS
11887bool wxRichTextFileHandler::SaveFile(wxRichTextBuffer *buffer, const wxString& filename)
11888{
11889 wxFFileOutputStream stream(filename);
a1b806b9 11890 if (stream.IsOk())
44cc96a8 11891 return SaveFile(buffer, stream);
5d7836c4 11892
44cc96a8
JS
11893 return false;
11894}
11895#endif // wxUSE_FFILE && wxUSE_STREAMS
5d7836c4 11896
44cc96a8
JS
11897/// Can we handle this filename (if using files)? By default, checks the extension.
11898bool wxRichTextFileHandler::CanHandle(const wxString& filename) const
11899{
11900 wxString path, file, ext;
a51e601e 11901 wxFileName::SplitPath(filename, & path, & file, & ext);
5d7836c4 11902
44cc96a8
JS
11903 return (ext.Lower() == GetExtension());
11904}
5d7836c4 11905
44cc96a8
JS
11906/*!
11907 * wxRichTextTextHandler
11908 * Plain text handler
11909 */
5d7836c4 11910
44cc96a8 11911IMPLEMENT_CLASS(wxRichTextPlainTextHandler, wxRichTextFileHandler)
5d7836c4 11912
44cc96a8
JS
11913#if wxUSE_STREAMS
11914bool wxRichTextPlainTextHandler::DoLoadFile(wxRichTextBuffer *buffer, wxInputStream& stream)
11915{
11916 if (!stream.IsOk())
797e38dd
JS
11917 return false;
11918
44cc96a8
JS
11919 wxString str;
11920 int lastCh = 0;
5d7836c4 11921
44cc96a8
JS
11922 while (!stream.Eof())
11923 {
11924 int ch = stream.GetC();
5d7836c4 11925
44cc96a8
JS
11926 if (!stream.Eof())
11927 {
11928 if (ch == 10 && lastCh != 13)
11929 str += wxT('\n');
5d7836c4 11930
44cc96a8
JS
11931 if (ch > 0 && ch != 10)
11932 str += wxChar(ch);
5d7836c4 11933
44cc96a8
JS
11934 lastCh = ch;
11935 }
11936 }
5d7836c4 11937
44cc96a8
JS
11938 buffer->ResetAndClearCommands();
11939 buffer->Clear();
11940 buffer->AddParagraphs(str);
11941 buffer->UpdateRanges();
5d7836c4 11942
44cc96a8
JS
11943 return true;
11944}
5d7836c4 11945
44cc96a8
JS
11946bool wxRichTextPlainTextHandler::DoSaveFile(wxRichTextBuffer *buffer, wxOutputStream& stream)
11947{
11948 if (!stream.IsOk())
5d7836c4
JS
11949 return false;
11950
44cc96a8 11951 wxString text = buffer->GetText();
38f833b1 11952
44cc96a8
JS
11953 wxString newLine = wxRichTextLineBreakChar;
11954 text.Replace(newLine, wxT("\n"));
5d7836c4 11955
44cc96a8 11956 wxCharBuffer buf = text.ToAscii();
5d7836c4 11957
44cc96a8
JS
11958 stream.Write((const char*) buf, text.length());
11959 return true;
11960}
11961#endif // wxUSE_STREAMS
5d7836c4 11962
44cc96a8
JS
11963/*
11964 * Stores information about an image, in binary in-memory form
11965 */
59509217 11966
44cc96a8
JS
11967wxRichTextImageBlock::wxRichTextImageBlock()
11968{
11969 Init();
11970}
5d7836c4 11971
44cc96a8
JS
11972wxRichTextImageBlock::wxRichTextImageBlock(const wxRichTextImageBlock& block):wxObject()
11973{
11974 Init();
11975 Copy(block);
11976}
ea160b2e 11977
44cc96a8
JS
11978wxRichTextImageBlock::~wxRichTextImageBlock()
11979{
5276b0a5 11980 wxDELETEA(m_data);
5d7836c4
JS
11981}
11982
44cc96a8 11983void wxRichTextImageBlock::Init()
5d7836c4
JS
11984{
11985 m_data = NULL;
11986 m_dataSize = 0;
d75a69e8 11987 m_imageType = wxBITMAP_TYPE_INVALID;
5d7836c4
JS
11988}
11989
11990void wxRichTextImageBlock::Clear()
11991{
5276b0a5 11992 wxDELETEA(m_data);
5d7836c4 11993 m_dataSize = 0;
d75a69e8 11994 m_imageType = wxBITMAP_TYPE_INVALID;
5d7836c4
JS
11995}
11996
11997
11998// Load the original image into a memory block.
11999// If the image is not a JPEG, we must convert it into a JPEG
12000// to conserve space.
12001// If it's not a JPEG we can make use of 'image', already scaled, so we don't have to
12002// load the image a 2nd time.
12003
d75a69e8
FM
12004bool wxRichTextImageBlock::MakeImageBlock(const wxString& filename, wxBitmapType imageType,
12005 wxImage& image, bool convertToJPEG)
5d7836c4
JS
12006{
12007 m_imageType = imageType;
12008
12009 wxString filenameToRead(filename);
7fe8059f 12010 bool removeFile = false;
5d7836c4 12011
62891c87 12012 if (imageType == wxBITMAP_TYPE_INVALID)
7fe8059f 12013 return false; // Could not determine image type
5d7836c4
JS
12014
12015 if ((imageType != wxBITMAP_TYPE_JPEG) && convertToJPEG)
12016 {
a51e601e
FM
12017 wxString tempFile =
12018 wxFileName::CreateTempFileName(_("image"));
5d7836c4 12019
a51e601e 12020 wxASSERT(!tempFile.IsEmpty());
5d7836c4
JS
12021
12022 image.SaveFile(tempFile, wxBITMAP_TYPE_JPEG);
12023 filenameToRead = tempFile;
7fe8059f 12024 removeFile = true;
5d7836c4
JS
12025
12026 m_imageType = wxBITMAP_TYPE_JPEG;
12027 }
12028 wxFile file;
12029 if (!file.Open(filenameToRead))
7fe8059f 12030 return false;
5d7836c4
JS
12031
12032 m_dataSize = (size_t) file.Length();
12033 file.Close();
12034
12035 if (m_data)
12036 delete[] m_data;
12037 m_data = ReadBlock(filenameToRead, m_dataSize);
12038
12039 if (removeFile)
12040 wxRemoveFile(filenameToRead);
12041
12042 return (m_data != NULL);
12043}
12044
12045// Make an image block from the wxImage in the given
12046// format.
d75a69e8 12047bool wxRichTextImageBlock::MakeImageBlock(wxImage& image, wxBitmapType imageType, int quality)
5d7836c4 12048{
5d7836c4
JS
12049 image.SetOption(wxT("quality"), quality);
12050
62891c87 12051 if (imageType == wxBITMAP_TYPE_INVALID)
7fe8059f 12052 return false; // Could not determine image type
5d7836c4 12053
cdaed652
VZ
12054 return DoMakeImageBlock(image, imageType);
12055}
12056
12057// Uses a const wxImage for efficiency, but can't set quality (only relevant for JPEG)
12058bool wxRichTextImageBlock::MakeImageBlockDefaultQuality(const wxImage& image, wxBitmapType imageType)
12059{
12060 if (imageType == wxBITMAP_TYPE_INVALID)
12061 return false; // Could not determine image type
ce00f59b 12062
cdaed652
VZ
12063 return DoMakeImageBlock(image, imageType);
12064}
7fe8059f 12065
cdaed652
VZ
12066// Makes the image block
12067bool wxRichTextImageBlock::DoMakeImageBlock(const wxImage& image, wxBitmapType imageType)
12068{
12069 wxMemoryOutputStream memStream;
12070 if (!image.SaveFile(memStream, imageType))
5d7836c4 12071 {
7fe8059f 12072 return false;
5d7836c4 12073 }
ce00f59b 12074
cdaed652
VZ
12075 unsigned char* block = new unsigned char[memStream.GetSize()];
12076 if (!block)
377c1ba4 12077 return false;
ce00f59b 12078
5d7836c4
JS
12079 if (m_data)
12080 delete[] m_data;
cdaed652 12081 m_data = block;
ce00f59b
VZ
12082
12083 m_imageType = imageType;
cdaed652 12084 m_dataSize = memStream.GetSize();
5d7836c4 12085
cdaed652 12086 memStream.CopyTo(m_data, m_dataSize);
5d7836c4
JS
12087
12088 return (m_data != NULL);
12089}
12090
5d7836c4
JS
12091// Write to a file
12092bool wxRichTextImageBlock::Write(const wxString& filename)
12093{
12094 return WriteBlock(filename, m_data, m_dataSize);
12095}
12096
12097void wxRichTextImageBlock::Copy(const wxRichTextImageBlock& block)
12098{
12099 m_imageType = block.m_imageType;
5276b0a5 12100 wxDELETEA(m_data);
5d7836c4
JS
12101 m_dataSize = block.m_dataSize;
12102 if (m_dataSize == 0)
12103 return;
12104
12105 m_data = new unsigned char[m_dataSize];
12106 unsigned int i;
12107 for (i = 0; i < m_dataSize; i++)
12108 m_data[i] = block.m_data[i];
12109}
12110
12111//// Operators
12112void wxRichTextImageBlock::operator=(const wxRichTextImageBlock& block)
12113{
12114 Copy(block);
12115}
12116
12117// Load a wxImage from the block
12118bool wxRichTextImageBlock::Load(wxImage& image)
12119{
12120 if (!m_data)
7fe8059f 12121 return false;
5d7836c4
JS
12122
12123 // Read in the image.
0ca07313 12124#if wxUSE_STREAMS
5d7836c4
JS
12125 wxMemoryInputStream mstream(m_data, m_dataSize);
12126 bool success = image.LoadFile(mstream, GetImageType());
12127#else
a51e601e
FM
12128 wxString tempFile = wxFileName::CreateTempFileName(_("image"));
12129 wxASSERT(!tempFile.IsEmpty());
5d7836c4
JS
12130
12131 if (!WriteBlock(tempFile, m_data, m_dataSize))
12132 {
7fe8059f 12133 return false;
5d7836c4
JS
12134 }
12135 success = image.LoadFile(tempFile, GetImageType());
12136 wxRemoveFile(tempFile);
12137#endif
12138
12139 return success;
12140}
12141
12142// Write data in hex to a stream
12143bool wxRichTextImageBlock::WriteHex(wxOutputStream& stream)
12144{
4dc7ae1a
JS
12145 if (m_dataSize == 0)
12146 return true;
12147
12148 int bufSize = 100000;
a3c12576
JS
12149 if (int(2*m_dataSize) < bufSize)
12150 bufSize = 2*m_dataSize;
4dc7ae1a 12151 char* buf = new char[bufSize+1];
351c0647
JS
12152
12153 int left = m_dataSize;
12154 int n, i, j;
12155 j = 0;
12156 while (left > 0)
5d7836c4 12157 {
351c0647
JS
12158 if (left*2 > bufSize)
12159 {
12160 n = bufSize; left -= (bufSize/2);
12161 }
12162 else
12163 {
12164 n = left*2; left = 0;
12165 }
7fe8059f 12166
351c0647
JS
12167 char* b = buf;
12168 for (i = 0; i < (n/2); i++)
12169 {
f728025e 12170 wxDecToHex(m_data[j], b, b+1);
351c0647
JS
12171 b += 2; j ++;
12172 }
5d7836c4 12173
351c0647
JS
12174 buf[n] = 0;
12175 stream.Write((const char*) buf, n);
12176 }
4dc7ae1a 12177 delete[] buf;
5d7836c4
JS
12178 return true;
12179}
12180
12181// Read data in hex from a stream
d75a69e8 12182bool wxRichTextImageBlock::ReadHex(wxInputStream& stream, int length, wxBitmapType imageType)
5d7836c4
JS
12183{
12184 int dataSize = length/2;
12185
12186 if (m_data)
12187 delete[] m_data;
12188
046fce47
FM
12189 // create a null terminated temporary string:
12190 char str[3];
12191 str[2] = '\0';
12192
5d7836c4
JS
12193 m_data = new unsigned char[dataSize];
12194 int i;
12195 for (i = 0; i < dataSize; i ++)
12196 {
c9f78968
VS
12197 str[0] = (char)stream.GetC();
12198 str[1] = (char)stream.GetC();
5d7836c4 12199
a9465653 12200 m_data[i] = (unsigned char)wxHexToDec(str);
5d7836c4
JS
12201 }
12202
12203 m_dataSize = dataSize;
12204 m_imageType = imageType;
12205
12206 return true;
12207}
12208
5d7836c4
JS
12209// Allocate and read from stream as a block of memory
12210unsigned char* wxRichTextImageBlock::ReadBlock(wxInputStream& stream, size_t size)
12211{
12212 unsigned char* block = new unsigned char[size];
12213 if (!block)
12214 return NULL;
12215
12216 stream.Read(block, size);
12217
12218 return block;
12219}
12220
12221unsigned char* wxRichTextImageBlock::ReadBlock(const wxString& filename, size_t size)
12222{
12223 wxFileInputStream stream(filename);
a1b806b9 12224 if (!stream.IsOk())
5d7836c4
JS
12225 return NULL;
12226
12227 return ReadBlock(stream, size);
12228}
12229
12230// Write memory block to stream
12231bool wxRichTextImageBlock::WriteBlock(wxOutputStream& stream, unsigned char* block, size_t size)
12232{
12233 stream.Write((void*) block, size);
12234 return stream.IsOk();
12235
12236}
12237
12238// Write memory block to file
12239bool wxRichTextImageBlock::WriteBlock(const wxString& filename, unsigned char* block, size_t size)
12240{
12241 wxFileOutputStream outStream(filename);
a1b806b9 12242 if (!outStream.IsOk())
7fe8059f 12243 return false;
5d7836c4
JS
12244
12245 return WriteBlock(outStream, block, size);
12246}
12247
d2d0adc7
JS
12248// Gets the extension for the block's type
12249wxString wxRichTextImageBlock::GetExtension() const
12250{
12251 wxImageHandler* handler = wxImage::FindHandler(GetImageType());
12252 if (handler)
12253 return handler->GetExtension();
12254 else
12255 return wxEmptyString;
12256}
12257
0ca07313
JS
12258#if wxUSE_DATAOBJ
12259
12260/*!
12261 * The data object for a wxRichTextBuffer
12262 */
12263
bafc4eac 12264const wxChar *wxRichTextBufferDataObject::ms_richTextBufferFormatId = wxT("wxRichText");
0ca07313
JS
12265
12266wxRichTextBufferDataObject::wxRichTextBufferDataObject(wxRichTextBuffer* richTextBuffer)
12267{
12268 m_richTextBuffer = richTextBuffer;
12269
12270 // this string should uniquely identify our format, but is otherwise
12271 // arbitrary
12272 m_formatRichTextBuffer.SetId(GetRichTextBufferFormatId());
12273
12274 SetFormat(m_formatRichTextBuffer);
12275}
12276
12277wxRichTextBufferDataObject::~wxRichTextBufferDataObject()
12278{
12279 delete m_richTextBuffer;
12280}
12281
12282// after a call to this function, the richTextBuffer is owned by the caller and it
12283// is responsible for deleting it!
12284wxRichTextBuffer* wxRichTextBufferDataObject::GetRichTextBuffer()
12285{
12286 wxRichTextBuffer* richTextBuffer = m_richTextBuffer;
12287 m_richTextBuffer = NULL;
12288
12289 return richTextBuffer;
12290}
12291
12292wxDataFormat wxRichTextBufferDataObject::GetPreferredFormat(Direction WXUNUSED(dir)) const
12293{
12294 return m_formatRichTextBuffer;
12295}
12296
12297size_t wxRichTextBufferDataObject::GetDataSize() const
12298{
12299 if (!m_richTextBuffer)
12300 return 0;
12301
12302 wxString bufXML;
12303
12304 {
12305 wxStringOutputStream stream(& bufXML);
12306 if (!m_richTextBuffer->SaveFile(stream, wxRICHTEXT_TYPE_XML))
12307 {
12308 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
12309 return 0;
12310 }
12311 }
12312
12313#if wxUSE_UNICODE
12314 wxCharBuffer buffer = bufXML.mb_str(wxConvUTF8);
12315 return strlen(buffer) + 1;
12316#else
12317 return bufXML.Length()+1;
12318#endif
12319}
12320
12321bool wxRichTextBufferDataObject::GetDataHere(void *pBuf) const
12322{
12323 if (!pBuf || !m_richTextBuffer)
12324 return false;
12325
12326 wxString bufXML;
12327
12328 {
12329 wxStringOutputStream stream(& bufXML);
12330 if (!m_richTextBuffer->SaveFile(stream, wxRICHTEXT_TYPE_XML))
12331 {
12332 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
12333 return 0;
12334 }
12335 }
12336
12337#if wxUSE_UNICODE
12338 wxCharBuffer buffer = bufXML.mb_str(wxConvUTF8);
12339 size_t len = strlen(buffer);
12340 memcpy((char*) pBuf, (const char*) buffer, len);
12341 ((char*) pBuf)[len] = 0;
12342#else
12343 size_t len = bufXML.Length();
12344 memcpy((char*) pBuf, (const char*) bufXML.c_str(), len);
12345 ((char*) pBuf)[len] = 0;
12346#endif
12347
12348 return true;
12349}
12350
12351bool wxRichTextBufferDataObject::SetData(size_t WXUNUSED(len), const void *buf)
12352{
5276b0a5 12353 wxDELETE(m_richTextBuffer);
0ca07313
JS
12354
12355 wxString bufXML((const char*) buf, wxConvUTF8);
12356
12357 m_richTextBuffer = new wxRichTextBuffer;
12358
12359 wxStringInputStream stream(bufXML);
12360 if (!m_richTextBuffer->LoadFile(stream, wxRICHTEXT_TYPE_XML))
12361 {
12362 wxLogError(wxT("Could not read the buffer from an XML stream.\nYou may have forgotten to add the XML file handler."));
12363
5276b0a5 12364 wxDELETE(m_richTextBuffer);
0ca07313
JS
12365
12366 return false;
12367 }
12368 return true;
12369}
12370
12371#endif
12372 // wxUSE_DATAOBJ
12373
44cc96a8
JS
12374
12375/*
12376 * wxRichTextFontTable
12377 * Manages quick access to a pool of fonts for rendering rich text
12378 */
12379
d65381ac 12380WX_DECLARE_STRING_HASH_MAP_WITH_DECL(wxFont, wxRichTextFontTableHashMap, class WXDLLIMPEXP_RICHTEXT);
44cc96a8
JS
12381
12382class wxRichTextFontTableData: public wxObjectRefData
12383{
12384public:
12385 wxRichTextFontTableData() {}
12386
32423dd8 12387 wxFont FindFont(const wxRichTextAttr& fontSpec, double fontScale);
44cc96a8
JS
12388
12389 wxRichTextFontTableHashMap m_hashMap;
12390};
12391
32423dd8 12392wxFont wxRichTextFontTableData::FindFont(const wxRichTextAttr& fontSpec, double fontScale)
44cc96a8
JS
12393{
12394 wxString facename(fontSpec.GetFontFaceName());
44cc96a8 12395
32423dd8
JS
12396 int fontSize = fontSpec.GetFontSize();
12397 if (fontScale != 1.0)
12398 fontSize = (int) ((double(fontSize) * fontScale) + 0.5);
12399
12400 wxString units;
12401 if (fontSpec.HasFontPixelSize() && !fontSpec.HasFontPointSize())
12402 units = wxT("px");
12403 else
12404 units = wxT("pt");
12405 wxString spec = wxString::Format(wxT("%d-%s-%d-%d-%d-%d-%s-%d"),
12406 fontSize, units.c_str(), fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), (int) fontSpec.GetFontUnderlined(), (int) fontSpec.GetFontStrikethrough(),
12407 facename.c_str(), (int) fontSpec.GetFontEncoding());
12408
12409 wxRichTextFontTableHashMap::iterator entry = m_hashMap.find(spec);
44cc96a8
JS
12410 if ( entry == m_hashMap.end() )
12411 {
32423dd8
JS
12412 if (fontSpec.HasFontPixelSize() && !fontSpec.HasFontPointSize())
12413 {
b42e4b3e 12414 wxFont font(wxSize(0, fontSize), wxFONTFAMILY_DEFAULT, fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), fontSpec.GetFontUnderlined(), facename);
32423dd8
JS
12415 if (fontSpec.HasFontStrikethrough() && fontSpec.GetFontStrikethrough())
12416 font.SetStrikethrough(true);
12417 m_hashMap[spec] = font;
12418 return font;
12419 }
12420 else
12421 {
5a0e33af 12422 wxFont font(fontSize, wxFONTFAMILY_DEFAULT, fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), fontSpec.GetFontUnderlined(), facename.c_str());
32423dd8
JS
12423 if (fontSpec.HasFontStrikethrough() && fontSpec.GetFontStrikethrough())
12424 font.SetStrikethrough(true);
12425
12426 m_hashMap[spec] = font;
12427 return font;
12428 }
44cc96a8
JS
12429 }
12430 else
12431 {
12432 return entry->second;
12433 }
12434}
12435
12436IMPLEMENT_DYNAMIC_CLASS(wxRichTextFontTable, wxObject)
12437
12438wxRichTextFontTable::wxRichTextFontTable()
12439{
12440 m_refData = new wxRichTextFontTableData;
32423dd8 12441 m_fontScale = 1.0;
44cc96a8
JS
12442}
12443
12444wxRichTextFontTable::wxRichTextFontTable(const wxRichTextFontTable& table)
2a230426 12445 : wxObject()
44cc96a8
JS
12446{
12447 (*this) = table;
12448}
12449
12450wxRichTextFontTable::~wxRichTextFontTable()
12451{
12452 UnRef();
12453}
12454
12455bool wxRichTextFontTable::operator == (const wxRichTextFontTable& table) const
12456{
12457 return (m_refData == table.m_refData);
12458}
12459
12460void wxRichTextFontTable::operator= (const wxRichTextFontTable& table)
12461{
12462 Ref(table);
32423dd8 12463 m_fontScale = table.m_fontScale;
44cc96a8
JS
12464}
12465
24777478 12466wxFont wxRichTextFontTable::FindFont(const wxRichTextAttr& fontSpec)
44cc96a8
JS
12467{
12468 wxRichTextFontTableData* data = (wxRichTextFontTableData*) m_refData;
12469 if (data)
32423dd8 12470 return data->FindFont(fontSpec, m_fontScale);
44cc96a8
JS
12471 else
12472 return wxFont();
12473}
12474
12475void wxRichTextFontTable::Clear()
12476{
12477 wxRichTextFontTableData* data = (wxRichTextFontTableData*) m_refData;
12478 if (data)
12479 data->m_hashMap.clear();
12480}
12481
32423dd8
JS
12482void wxRichTextFontTable::SetFontScale(double fontScale)
12483{
12484 if (fontScale != m_fontScale)
12485 Clear();
12486 m_fontScale = fontScale;
12487}
12488
24777478
JS
12489// wxTextBoxAttr
12490
24777478
JS
12491void wxTextBoxAttr::Reset()
12492{
12493 m_flags = 0;
603f702b
JS
12494 m_floatMode = wxTEXT_BOX_ATTR_FLOAT_NONE;
12495 m_clearMode = wxTEXT_BOX_ATTR_CLEAR_NONE;
12496 m_collapseMode = wxTEXT_BOX_ATTR_COLLAPSE_NONE;
12497 m_verticalAlignment = wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_NONE;
2f987d83 12498 m_boxStyleName = wxEmptyString;
bec80f4f 12499
24777478
JS
12500 m_margins.Reset();
12501 m_padding.Reset();
12502 m_position.Reset();
12503
603f702b 12504 m_size.Reset();
303f0be7
JS
12505 m_minSize.Reset();
12506 m_maxSize.Reset();
24777478
JS
12507
12508 m_border.Reset();
12509 m_outline.Reset();
12510}
12511
12512// Equality test
12513bool wxTextBoxAttr::operator== (const wxTextBoxAttr& attr) const
12514{
12515 return (
12516 m_flags == attr.m_flags &&
12517 m_floatMode == attr.m_floatMode &&
12518 m_clearMode == attr.m_clearMode &&
12519 m_collapseMode == attr.m_collapseMode &&
603f702b 12520 m_verticalAlignment == attr.m_verticalAlignment &&
bec80f4f 12521
24777478
JS
12522 m_margins == attr.m_margins &&
12523 m_padding == attr.m_padding &&
12524 m_position == attr.m_position &&
12525
603f702b 12526 m_size == attr.m_size &&
303f0be7
JS
12527 m_minSize == attr.m_minSize &&
12528 m_maxSize == attr.m_maxSize &&
24777478
JS
12529
12530 m_border == attr.m_border &&
2f987d83
JS
12531 m_outline == attr.m_outline &&
12532
12533 m_boxStyleName == attr.m_boxStyleName
24777478
JS
12534 );
12535}
12536
12537// Partial equality test
32423dd8 12538bool wxTextBoxAttr::EqPartial(const wxTextBoxAttr& attr, bool weakTest) const
24777478 12539{
32423dd8
JS
12540 if (!weakTest &&
12541 ((!HasFloatMode() && attr.HasFloatMode()) ||
12542 (!HasClearMode() && attr.HasClearMode()) ||
12543 (!HasCollapseBorders() && attr.HasCollapseBorders()) ||
12544 (!HasVerticalAlignment() && attr.HasVerticalAlignment()) ||
12545 (!HasBoxStyleName() && attr.HasBoxStyleName())))
12546 {
12547 return false;
12548 }
24777478
JS
12549 if (attr.HasFloatMode() && HasFloatMode() && (GetFloatMode() != attr.GetFloatMode()))
12550 return false;
12551
12552 if (attr.HasClearMode() && HasClearMode() && (GetClearMode() != attr.GetClearMode()))
12553 return false;
12554
12555 if (attr.HasCollapseBorders() && HasCollapseBorders() && (attr.GetCollapseBorders() != GetCollapseBorders()))
12556 return false;
12557
603f702b
JS
12558 if (attr.HasVerticalAlignment() && HasVerticalAlignment() && (attr.GetVerticalAlignment() != GetVerticalAlignment()))
12559 return false;
12560
2f987d83
JS
12561 if (attr.HasBoxStyleName() && HasBoxStyleName() && (attr.GetBoxStyleName() != GetBoxStyleName()))
12562 return false;
12563
24777478
JS
12564 // Position
12565
32423dd8 12566 if (!m_position.EqPartial(attr.m_position, weakTest))
24777478
JS
12567 return false;
12568
303f0be7
JS
12569 // Size
12570
32423dd8 12571 if (!m_size.EqPartial(attr.m_size, weakTest))
303f0be7 12572 return false;
32423dd8 12573 if (!m_minSize.EqPartial(attr.m_minSize, weakTest))
303f0be7 12574 return false;
32423dd8 12575 if (!m_maxSize.EqPartial(attr.m_maxSize, weakTest))
303f0be7
JS
12576 return false;
12577
24777478
JS
12578 // Margins
12579
32423dd8 12580 if (!m_margins.EqPartial(attr.m_margins, weakTest))
24777478
JS
12581 return false;
12582
12583 // Padding
12584
32423dd8 12585 if (!m_padding.EqPartial(attr.m_padding, weakTest))
24777478
JS
12586 return false;
12587
12588 // Border
12589
32423dd8 12590 if (!GetBorder().EqPartial(attr.GetBorder(), weakTest))
24777478
JS
12591 return false;
12592
12593 // Outline
12594
32423dd8 12595 if (!GetOutline().EqPartial(attr.GetOutline(), weakTest))
24777478
JS
12596 return false;
12597
12598 return true;
12599}
12600
12601// Merges the given attributes. If compareWith
12602// is non-NULL, then it will be used to mask out those attributes that are the same in style
12603// and compareWith, for situations where we don't want to explicitly set inherited attributes.
12604bool wxTextBoxAttr::Apply(const wxTextBoxAttr& attr, const wxTextBoxAttr* compareWith)
12605{
12606 if (attr.HasFloatMode())
12607 {
12608 if (!(compareWith && compareWith->HasFloatMode() && compareWith->GetFloatMode() == attr.GetFloatMode()))
12609 SetFloatMode(attr.GetFloatMode());
12610 }
12611
12612 if (attr.HasClearMode())
12613 {
12614 if (!(compareWith && compareWith->HasClearMode() && compareWith->GetClearMode() == attr.GetClearMode()))
12615 SetClearMode(attr.GetClearMode());
12616 }
12617
12618 if (attr.HasCollapseBorders())
12619 {
12620 if (!(compareWith && compareWith->HasCollapseBorders() && compareWith->GetCollapseBorders() == attr.GetCollapseBorders()))
603f702b
JS
12621 SetCollapseBorders(attr.GetCollapseBorders());
12622 }
12623
12624 if (attr.HasVerticalAlignment())
12625 {
12626 if (!(compareWith && compareWith->HasVerticalAlignment() && compareWith->GetVerticalAlignment() == attr.GetVerticalAlignment()))
12627 SetVerticalAlignment(attr.GetVerticalAlignment());
24777478 12628 }
bec80f4f 12629
2f987d83
JS
12630 if (attr.HasBoxStyleName())
12631 {
12632 if (!(compareWith && compareWith->HasBoxStyleName() && compareWith->GetBoxStyleName() == attr.GetBoxStyleName()))
12633 SetBoxStyleName(attr.GetBoxStyleName());
12634 }
12635
bec80f4f
JS
12636 m_margins.Apply(attr.m_margins, compareWith ? (& attr.m_margins) : (const wxTextAttrDimensions*) NULL);
12637 m_padding.Apply(attr.m_padding, compareWith ? (& attr.m_padding) : (const wxTextAttrDimensions*) NULL);
12638 m_position.Apply(attr.m_position, compareWith ? (& attr.m_position) : (const wxTextAttrDimensions*) NULL);
24777478 12639
603f702b 12640 m_size.Apply(attr.m_size, compareWith ? (& attr.m_size) : (const wxTextAttrSize*) NULL);
303f0be7
JS
12641 m_minSize.Apply(attr.m_minSize, compareWith ? (& attr.m_minSize) : (const wxTextAttrSize*) NULL);
12642 m_maxSize.Apply(attr.m_maxSize, compareWith ? (& attr.m_maxSize) : (const wxTextAttrSize*) NULL);
24777478 12643
bec80f4f
JS
12644 m_border.Apply(attr.m_border, compareWith ? (& attr.m_border) : (const wxTextAttrBorders*) NULL);
12645 m_outline.Apply(attr.m_outline, compareWith ? (& attr.m_outline) : (const wxTextAttrBorders*) NULL);
24777478
JS
12646
12647 return true;
12648}
12649
12650// Remove specified attributes from this object
12651bool wxTextBoxAttr::RemoveStyle(const wxTextBoxAttr& attr)
12652{
12653 if (attr.HasFloatMode())
12654 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT);
12655
12656 if (attr.HasClearMode())
12657 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR);
12658
12659 if (attr.HasCollapseBorders())
12660 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
12661
603f702b
JS
12662 if (attr.HasVerticalAlignment())
12663 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
12664
2f987d83
JS
12665 if (attr.HasBoxStyleName())
12666 {
12667 SetBoxStyleName(wxEmptyString);
12668 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
12669 }
12670
24777478
JS
12671 m_margins.RemoveStyle(attr.m_margins);
12672 m_padding.RemoveStyle(attr.m_padding);
12673 m_position.RemoveStyle(attr.m_position);
12674
603f702b 12675 m_size.RemoveStyle(attr.m_size);
303f0be7
JS
12676 m_minSize.RemoveStyle(attr.m_minSize);
12677 m_maxSize.RemoveStyle(attr.m_maxSize);
24777478
JS
12678
12679 m_border.RemoveStyle(attr.m_border);
12680 m_outline.RemoveStyle(attr.m_outline);
12681
12682 return true;
12683}
12684
12685// Collects the attributes that are common to a range of content, building up a note of
12686// which attributes are absent in some objects and which clash in some objects.
12687void wxTextBoxAttr::CollectCommonAttributes(const wxTextBoxAttr& attr, wxTextBoxAttr& clashingAttr, wxTextBoxAttr& absentAttr)
12688{
12689 if (attr.HasFloatMode())
12690 {
12691 if (!clashingAttr.HasFloatMode() && !absentAttr.HasFloatMode())
12692 {
12693 if (HasFloatMode())
12694 {
12695 if (GetFloatMode() != attr.GetFloatMode())
12696 {
12697 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_FLOAT);
12698 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT);
12699 }
12700 }
12701 else
12702 SetFloatMode(attr.GetFloatMode());
12703 }
12704 }
12705 else
12706 absentAttr.AddFlag(wxTEXT_BOX_ATTR_FLOAT);
bec80f4f 12707
24777478
JS
12708 if (attr.HasClearMode())
12709 {
12710 if (!clashingAttr.HasClearMode() && !absentAttr.HasClearMode())
12711 {
12712 if (HasClearMode())
12713 {
12714 if (GetClearMode() != attr.GetClearMode())
12715 {
12716 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_CLEAR);
12717 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR);
12718 }
12719 }
12720 else
12721 SetClearMode(attr.GetClearMode());
12722 }
12723 }
12724 else
12725 absentAttr.AddFlag(wxTEXT_BOX_ATTR_CLEAR);
12726
12727 if (attr.HasCollapseBorders())
12728 {
12729 if (!clashingAttr.HasCollapseBorders() && !absentAttr.HasCollapseBorders())
12730 {
12731 if (HasCollapseBorders())
12732 {
12733 if (GetCollapseBorders() != attr.GetCollapseBorders())
12734 {
12735 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
12736 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
12737 }
12738 }
12739 else
12740 SetCollapseBorders(attr.GetCollapseBorders());
12741 }
12742 }
12743 else
12744 absentAttr.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
bec80f4f 12745
603f702b
JS
12746 if (attr.HasVerticalAlignment())
12747 {
12748 if (!clashingAttr.HasVerticalAlignment() && !absentAttr.HasVerticalAlignment())
12749 {
12750 if (HasVerticalAlignment())
12751 {
12752 if (GetVerticalAlignment() != attr.GetVerticalAlignment())
12753 {
12754 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
12755 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
12756 }
12757 }
12758 else
12759 SetVerticalAlignment(attr.GetVerticalAlignment());
12760 }
12761 }
12762 else
12763 absentAttr.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
12764
2f987d83
JS
12765 if (attr.HasBoxStyleName())
12766 {
12767 if (!clashingAttr.HasBoxStyleName() && !absentAttr.HasBoxStyleName())
12768 {
12769 if (HasBoxStyleName())
12770 {
12771 if (GetBoxStyleName() != attr.GetBoxStyleName())
12772 {
12773 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
12774 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
12775 }
12776 }
12777 else
12778 SetBoxStyleName(attr.GetBoxStyleName());
12779 }
12780 }
12781 else
12782 absentAttr.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
12783
24777478
JS
12784 m_margins.CollectCommonAttributes(attr.m_margins, clashingAttr.m_margins, absentAttr.m_margins);
12785 m_padding.CollectCommonAttributes(attr.m_padding, clashingAttr.m_padding, absentAttr.m_padding);
12786 m_position.CollectCommonAttributes(attr.m_position, clashingAttr.m_position, absentAttr.m_position);
12787
603f702b 12788 m_size.CollectCommonAttributes(attr.m_size, clashingAttr.m_size, absentAttr.m_size);
303f0be7
JS
12789 m_minSize.CollectCommonAttributes(attr.m_minSize, clashingAttr.m_minSize, absentAttr.m_minSize);
12790 m_maxSize.CollectCommonAttributes(attr.m_maxSize, clashingAttr.m_maxSize, absentAttr.m_maxSize);
24777478
JS
12791
12792 m_border.CollectCommonAttributes(attr.m_border, clashingAttr.m_border, absentAttr.m_border);
12793 m_outline.CollectCommonAttributes(attr.m_outline, clashingAttr.m_outline, absentAttr.m_outline);
12794}
12795
eb3d8a33
JS
12796bool wxTextBoxAttr::IsDefault() const
12797{
12798 return GetFlags() == 0 && !m_border.IsValid() && !m_outline.IsValid() &&
303f0be7 12799 !m_size.IsValid() && !m_minSize.IsValid() && !m_maxSize.IsValid() &&
eb3d8a33
JS
12800 !m_position.IsValid() && !m_padding.IsValid() && !m_margins.IsValid();
12801}
12802
24777478
JS
12803// wxRichTextAttr
12804
12805void wxRichTextAttr::Copy(const wxRichTextAttr& attr)
12806{
bec80f4f
JS
12807 wxTextAttr::Copy(attr);
12808
24777478
JS
12809 m_textBoxAttr = attr.m_textBoxAttr;
12810}
12811
12812bool wxRichTextAttr::operator==(const wxRichTextAttr& attr) const
12813{
12814 if (!(wxTextAttr::operator==(attr)))
12815 return false;
bec80f4f 12816
24777478
JS
12817 return (m_textBoxAttr == attr.m_textBoxAttr);
12818}
12819
32423dd8
JS
12820// Partial equality test
12821bool wxRichTextAttr::EqPartial(const wxRichTextAttr& attr, bool weakTest) const
24777478 12822{
32423dd8 12823 if (!(wxTextAttr::EqPartial(attr, weakTest)))
24777478 12824 return false;
bec80f4f 12825
32423dd8 12826 return m_textBoxAttr.EqPartial(attr.m_textBoxAttr, weakTest);
24777478
JS
12827}
12828
12829// Merges the given attributes. If compareWith
12830// is non-NULL, then it will be used to mask out those attributes that are the same in style
12831// and compareWith, for situations where we don't want to explicitly set inherited attributes.
12832bool wxRichTextAttr::Apply(const wxRichTextAttr& style, const wxRichTextAttr* compareWith)
12833{
12834 wxTextAttr::Apply(style, compareWith);
12835
12836 return m_textBoxAttr.Apply(style.m_textBoxAttr, compareWith ? (& compareWith->m_textBoxAttr) : (const wxTextBoxAttr*) NULL);
12837}
12838
12839// Remove specified attributes from this object
12840bool wxRichTextAttr::RemoveStyle(const wxRichTextAttr& attr)
12841{
12842 wxTextAttr::RemoveStyle(*this, attr);
12843
12844 return m_textBoxAttr.RemoveStyle(attr.m_textBoxAttr);
12845}
12846
12847// Collects the attributes that are common to a range of content, building up a note of
12848// which attributes are absent in some objects and which clash in some objects.
12849void wxRichTextAttr::CollectCommonAttributes(const wxRichTextAttr& attr, wxRichTextAttr& clashingAttr, wxRichTextAttr& absentAttr)
12850{
12851 wxTextAttrCollectCommonAttributes(*this, attr, clashingAttr, absentAttr);
bec80f4f 12852
24777478
JS
12853 m_textBoxAttr.CollectCommonAttributes(attr.m_textBoxAttr, clashingAttr.m_textBoxAttr, absentAttr.m_textBoxAttr);
12854}
12855
12856// Partial equality test
32423dd8 12857bool wxTextAttrBorder::EqPartial(const wxTextAttrBorder& border, bool weakTest) const
24777478 12858{
32423dd8
JS
12859 if (!weakTest &&
12860 ((!HasStyle() && border.HasStyle()) ||
12861 (!HasColour() && border.HasColour()) ||
12862 (!HasWidth() && border.HasWidth())))
12863 {
12864 return false;
12865 }
12866
12867 if (border.HasStyle() && HasStyle() && (border.GetStyle() != GetStyle()))
24777478
JS
12868 return false;
12869
32423dd8 12870 if (border.HasColour() && HasColour() && (border.GetColourLong() != GetColourLong()))
24777478
JS
12871 return false;
12872
32423dd8 12873 if (border.HasWidth() && HasWidth() && !(border.GetWidth() == GetWidth()))
24777478
JS
12874 return false;
12875
12876 return true;
12877}
12878
12879// Apply border to 'this', but not if the same as compareWith
bec80f4f 12880bool wxTextAttrBorder::Apply(const wxTextAttrBorder& border, const wxTextAttrBorder* compareWith)
24777478
JS
12881{
12882 if (border.HasStyle())
12883 {
12884 if (!(compareWith && (border.GetStyle() == compareWith->GetStyle())))
12885 SetStyle(border.GetStyle());
12886 }
12887 if (border.HasColour())
12888 {
12889 if (!(compareWith && (border.GetColourLong() == compareWith->GetColourLong())))
12890 SetColour(border.GetColourLong());
12891 }
12892 if (border.HasWidth())
12893 {
12894 if (!(compareWith && (border.GetWidth() == compareWith->GetWidth())))
12895 SetWidth(border.GetWidth());
12896 }
12897
12898 return true;
12899}
12900
12901// Remove specified attributes from this object
bec80f4f 12902bool wxTextAttrBorder::RemoveStyle(const wxTextAttrBorder& attr)
24777478
JS
12903{
12904 if (attr.HasStyle() && HasStyle())
12905 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_STYLE);
12906 if (attr.HasColour() && HasColour())
12907 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_COLOUR);
12908 if (attr.HasWidth() && HasWidth())
12909 m_borderWidth.Reset();
12910
12911 return true;
12912}
12913
12914// Collects the attributes that are common to a range of content, building up a note of
12915// which attributes are absent in some objects and which clash in some objects.
bec80f4f 12916void wxTextAttrBorder::CollectCommonAttributes(const wxTextAttrBorder& attr, wxTextAttrBorder& clashingAttr, wxTextAttrBorder& absentAttr)
24777478
JS
12917{
12918 if (attr.HasStyle())
12919 {
12920 if (!clashingAttr.HasStyle() && !absentAttr.HasStyle())
12921 {
12922 if (HasStyle())
12923 {
12924 if (GetStyle() != attr.GetStyle())
12925 {
12926 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE);
12927 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_STYLE);
12928 }
12929 }
12930 else
12931 SetStyle(attr.GetStyle());
12932 }
12933 }
12934 else
12935 absentAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE);
12936
12937 if (attr.HasColour())
12938 {
12939 if (!clashingAttr.HasColour() && !absentAttr.HasColour())
12940 {
12941 if (HasColour())
12942 {
12943 if (GetColour() != attr.GetColour())
12944 {
12945 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
12946 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
12947 }
12948 }
12949 else
12950 SetColour(attr.GetColourLong());
12951 }
12952 }
12953 else
12954 absentAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
bec80f4f 12955
24777478
JS
12956 m_borderWidth.CollectCommonAttributes(attr.m_borderWidth, clashingAttr.m_borderWidth, absentAttr.m_borderWidth);
12957}
12958
12959// Partial equality test
32423dd8 12960bool wxTextAttrBorders::EqPartial(const wxTextAttrBorders& borders, bool weakTest) const
24777478 12961{
32423dd8
JS
12962 return m_left.EqPartial(borders.m_left, weakTest) && m_right.EqPartial(borders.m_right, weakTest) &&
12963 m_top.EqPartial(borders.m_top, weakTest) && m_bottom.EqPartial(borders.m_bottom, weakTest);
24777478
JS
12964}
12965
12966// Apply border to 'this', but not if the same as compareWith
bec80f4f 12967bool wxTextAttrBorders::Apply(const wxTextAttrBorders& borders, const wxTextAttrBorders* compareWith)
24777478 12968{
bec80f4f
JS
12969 m_left.Apply(borders.m_left, compareWith ? (& compareWith->m_left) : (const wxTextAttrBorder*) NULL);
12970 m_right.Apply(borders.m_right, compareWith ? (& compareWith->m_right) : (const wxTextAttrBorder*) NULL);
12971 m_top.Apply(borders.m_top, compareWith ? (& compareWith->m_top) : (const wxTextAttrBorder*) NULL);
12972 m_bottom.Apply(borders.m_bottom, compareWith ? (& compareWith->m_bottom) : (const wxTextAttrBorder*) NULL);
24777478
JS
12973 return true;
12974}
12975
12976// Remove specified attributes from this object
bec80f4f 12977bool wxTextAttrBorders::RemoveStyle(const wxTextAttrBorders& attr)
24777478
JS
12978{
12979 m_left.RemoveStyle(attr.m_left);
12980 m_right.RemoveStyle(attr.m_right);
12981 m_top.RemoveStyle(attr.m_top);
12982 m_bottom.RemoveStyle(attr.m_bottom);
12983 return true;
12984}
12985
12986// Collects the attributes that are common to a range of content, building up a note of
12987// which attributes are absent in some objects and which clash in some objects.
bec80f4f 12988void wxTextAttrBorders::CollectCommonAttributes(const wxTextAttrBorders& attr, wxTextAttrBorders& clashingAttr, wxTextAttrBorders& absentAttr)
24777478
JS
12989{
12990 m_left.CollectCommonAttributes(attr.m_left, clashingAttr.m_left, absentAttr.m_left);
12991 m_right.CollectCommonAttributes(attr.m_right, clashingAttr.m_right, absentAttr.m_right);
12992 m_top.CollectCommonAttributes(attr.m_top, clashingAttr.m_top, absentAttr.m_top);
12993 m_bottom.CollectCommonAttributes(attr.m_bottom, clashingAttr.m_bottom, absentAttr.m_bottom);
12994}
12995
12996// Set style of all borders
bec80f4f 12997void wxTextAttrBorders::SetStyle(int style)
24777478
JS
12998{
12999 m_left.SetStyle(style);
13000 m_right.SetStyle(style);
13001 m_top.SetStyle(style);
13002 m_bottom.SetStyle(style);
13003}
13004
13005// Set colour of all borders
bec80f4f 13006void wxTextAttrBorders::SetColour(unsigned long colour)
24777478
JS
13007{
13008 m_left.SetColour(colour);
13009 m_right.SetColour(colour);
13010 m_top.SetColour(colour);
13011 m_bottom.SetColour(colour);
13012}
13013
bec80f4f 13014void wxTextAttrBorders::SetColour(const wxColour& colour)
24777478
JS
13015{
13016 m_left.SetColour(colour);
13017 m_right.SetColour(colour);
13018 m_top.SetColour(colour);
13019 m_bottom.SetColour(colour);
13020}
13021
13022// Set width of all borders
bec80f4f 13023void wxTextAttrBorders::SetWidth(const wxTextAttrDimension& width)
24777478
JS
13024{
13025 m_left.SetWidth(width);
13026 m_right.SetWidth(width);
13027 m_top.SetWidth(width);
13028 m_bottom.SetWidth(width);
13029}
13030
13031// Partial equality test
32423dd8 13032bool wxTextAttrDimension::EqPartial(const wxTextAttrDimension& dim, bool weakTest) const
24777478 13033{
32423dd8
JS
13034 if (!weakTest && !IsValid() && dim.IsValid())
13035 return false;
13036
603f702b 13037 if (dim.IsValid() && IsValid() && !((*this) == dim))
24777478
JS
13038 return false;
13039 else
13040 return true;
13041}
13042
13043bool wxTextAttrDimension::Apply(const wxTextAttrDimension& dim, const wxTextAttrDimension* compareWith)
13044{
603f702b 13045 if (dim.IsValid())
24777478
JS
13046 {
13047 if (!(compareWith && dim == (*compareWith)))
13048 (*this) = dim;
13049 }
13050
13051 return true;
13052}
13053
13054// Collects the attributes that are common to a range of content, building up a note of
13055// which attributes are absent in some objects and which clash in some objects.
13056void wxTextAttrDimension::CollectCommonAttributes(const wxTextAttrDimension& attr, wxTextAttrDimension& clashingAttr, wxTextAttrDimension& absentAttr)
13057{
603f702b 13058 if (attr.IsValid())
24777478 13059 {
603f702b 13060 if (!clashingAttr.IsValid() && !absentAttr.IsValid())
24777478 13061 {
603f702b 13062 if (IsValid())
24777478
JS
13063 {
13064 if (!((*this) == attr))
13065 {
603f702b
JS
13066 clashingAttr.SetValid(true);
13067 SetValid(false);
24777478
JS
13068 }
13069 }
13070 else
13071 (*this) = attr;
13072 }
13073 }
13074 else
603f702b 13075 absentAttr.SetValid(true);
24777478
JS
13076}
13077
8995db52
JS
13078wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(wxDC& dc, double scale, const wxSize& parentSize)
13079{
13080 m_ppi = dc.GetPPI().x; m_scale = scale; m_parentSize = parentSize;
13081}
13082
13083wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(int ppi, double scale, const wxSize& parentSize)
13084{
13085 m_ppi = ppi; m_scale = scale; m_parentSize = parentSize;
13086}
13087
bec80f4f
JS
13088int wxTextAttrDimensionConverter::ConvertTenthsMMToPixels(int units) const
13089{
13090 return wxRichTextObject::ConvertTenthsMMToPixels(m_ppi, units, m_scale);
13091}
13092
13093int wxTextAttrDimensionConverter::ConvertPixelsToTenthsMM(int pixels) const
13094{
13095 return wxRichTextObject::ConvertPixelsToTenthsMM(m_ppi, pixels, m_scale);
13096}
13097
13098int wxTextAttrDimensionConverter::GetPixels(const wxTextAttrDimension& dim, int direction) const
13099{
13100 if (dim.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
13101 return ConvertTenthsMMToPixels(dim.GetValue());
13102 else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
13103 return dim.GetValue();
13104 else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
13105 {
13106 wxASSERT(m_parentSize != wxDefaultSize);
13107 if (direction == wxHORIZONTAL)
13108 return (int) (double(m_parentSize.x) * double(dim.GetValue()) / 100.0);
13109 else
13110 return (int) (double(m_parentSize.y) * double(dim.GetValue()) / 100.0);
13111 }
13112 else
13113 {
13114 wxASSERT(false);
13115 return 0;
13116 }
13117}
13118
13119int wxTextAttrDimensionConverter::GetTenthsMM(const wxTextAttrDimension& dim) const
13120{
13121 if (dim.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
13122 return dim.GetValue();
13123 else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
13124 return ConvertPixelsToTenthsMM(dim.GetValue());
13125 else
13126 {
13127 wxASSERT(false);
13128 return 0;
13129 }
13130}
13131
24777478 13132// Partial equality test
32423dd8 13133bool wxTextAttrDimensions::EqPartial(const wxTextAttrDimensions& dims, bool weakTest) const
24777478 13134{
32423dd8 13135 if (!m_left.EqPartial(dims.m_left, weakTest))
24777478
JS
13136 return false;
13137
32423dd8 13138 if (!m_right.EqPartial(dims.m_right, weakTest))
24777478
JS
13139 return false;
13140
32423dd8 13141 if (!m_top.EqPartial(dims.m_top, weakTest))
24777478
JS
13142 return false;
13143
32423dd8 13144 if (!m_bottom.EqPartial(dims.m_bottom, weakTest))
24777478
JS
13145 return false;
13146
13147 return true;
13148}
13149
13150// Apply border to 'this', but not if the same as compareWith
bec80f4f 13151bool wxTextAttrDimensions::Apply(const wxTextAttrDimensions& dims, const wxTextAttrDimensions* compareWith)
24777478
JS
13152{
13153 m_left.Apply(dims.m_left, compareWith ? (& compareWith->m_left) : (const wxTextAttrDimension*) NULL);
13154 m_right.Apply(dims.m_right, compareWith ? (& compareWith->m_right): (const wxTextAttrDimension*) NULL);
13155 m_top.Apply(dims.m_top, compareWith ? (& compareWith->m_top): (const wxTextAttrDimension*) NULL);
13156 m_bottom.Apply(dims.m_bottom, compareWith ? (& compareWith->m_bottom): (const wxTextAttrDimension*) NULL);
13157
13158 return true;
13159}
13160
13161// Remove specified attributes from this object
bec80f4f 13162bool wxTextAttrDimensions::RemoveStyle(const wxTextAttrDimensions& attr)
24777478 13163{
603f702b 13164 if (attr.m_left.IsValid())
24777478 13165 m_left.Reset();
603f702b 13166 if (attr.m_right.IsValid())
24777478 13167 m_right.Reset();
603f702b 13168 if (attr.m_top.IsValid())
24777478 13169 m_top.Reset();
603f702b 13170 if (attr.m_bottom.IsValid())
24777478
JS
13171 m_bottom.Reset();
13172
13173 return true;
13174}
13175
13176// Collects the attributes that are common to a range of content, building up a note of
13177// which attributes are absent in some objects and which clash in some objects.
bec80f4f 13178void wxTextAttrDimensions::CollectCommonAttributes(const wxTextAttrDimensions& attr, wxTextAttrDimensions& clashingAttr, wxTextAttrDimensions& absentAttr)
24777478
JS
13179{
13180 m_left.CollectCommonAttributes(attr.m_left, clashingAttr.m_left, absentAttr.m_left);
13181 m_right.CollectCommonAttributes(attr.m_right, clashingAttr.m_right, absentAttr.m_right);
13182 m_top.CollectCommonAttributes(attr.m_top, clashingAttr.m_top, absentAttr.m_top);
13183 m_bottom.CollectCommonAttributes(attr.m_bottom, clashingAttr.m_bottom, absentAttr.m_bottom);
13184}
13185
603f702b 13186// Partial equality test
32423dd8 13187bool wxTextAttrSize::EqPartial(const wxTextAttrSize& size, bool weakTest) const
603f702b 13188{
32423dd8 13189 if (!m_width.EqPartial(size.m_width, weakTest))
603f702b
JS
13190 return false;
13191
32423dd8 13192 if (!m_height.EqPartial(size.m_height, weakTest))
603f702b
JS
13193 return false;
13194
13195 return true;
13196}
13197
13198// Apply border to 'this', but not if the same as compareWith
13199bool wxTextAttrSize::Apply(const wxTextAttrSize& size, const wxTextAttrSize* compareWith)
13200{
13201 m_width.Apply(size.m_width, compareWith ? (& compareWith->m_width) : (const wxTextAttrDimension*) NULL);
13202 m_height.Apply(size.m_height, compareWith ? (& compareWith->m_height): (const wxTextAttrDimension*) NULL);
13203
13204 return true;
13205}
13206
13207// Remove specified attributes from this object
13208bool wxTextAttrSize::RemoveStyle(const wxTextAttrSize& attr)
13209{
13210 if (attr.m_width.IsValid())
13211 m_width.Reset();
13212 if (attr.m_height.IsValid())
13213 m_height.Reset();
13214
13215 return true;
13216}
13217
13218// Collects the attributes that are common to a range of content, building up a note of
13219// which attributes are absent in some objects and which clash in some objects.
13220void wxTextAttrSize::CollectCommonAttributes(const wxTextAttrSize& attr, wxTextAttrSize& clashingAttr, wxTextAttrSize& absentAttr)
13221{
13222 m_width.CollectCommonAttributes(attr.m_width, clashingAttr.m_width, absentAttr.m_width);
13223 m_height.CollectCommonAttributes(attr.m_height, clashingAttr.m_height, absentAttr.m_height);
13224}
13225
24777478
JS
13226// Collects the attributes that are common to a range of content, building up a note of
13227// which attributes are absent in some objects and which clash in some objects.
13228void wxTextAttrCollectCommonAttributes(wxTextAttr& currentStyle, const wxTextAttr& attr, wxTextAttr& clashingAttr, wxTextAttr& absentAttr)
13229{
13230 absentAttr.SetFlags(absentAttr.GetFlags() | (~attr.GetFlags() & wxTEXT_ATTR_ALL));
13231 absentAttr.SetTextEffectFlags(absentAttr.GetTextEffectFlags() | (~attr.GetTextEffectFlags() & 0xFFFF));
13232
13233 long forbiddenFlags = clashingAttr.GetFlags()|absentAttr.GetFlags();
13234
c4168888
JS
13235 // If different font size units are being used, this is a clash.
13236 if (((attr.GetFlags() & wxTEXT_ATTR_FONT_SIZE) | (currentStyle.GetFlags() & wxTEXT_ATTR_FONT_SIZE)) == wxTEXT_ATTR_FONT_SIZE)
24777478 13237 {
c4168888
JS
13238 currentStyle.SetFontSize(0);
13239 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_SIZE);
13240 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_SIZE);
13241 }
13242 else
13243 {
13244 if (attr.HasFontPointSize() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_POINT_SIZE))
340ef5c5 13245 {
c4168888 13246 if (currentStyle.HasFontPointSize())
24777478 13247 {
c4168888 13248 if (currentStyle.GetFontSize() != attr.GetFontSize())
24777478 13249 {
c4168888
JS
13250 // Clash of attr - mark as such
13251 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE);
13252 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE);
32423dd8
JS
13253 }
13254 }
c4168888
JS
13255 else
13256 currentStyle.SetFontSize(attr.GetFontSize());
13257 }
13258 else if (!attr.HasFontPointSize() && currentStyle.HasFontPointSize())
13259 {
13260 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE);
13261 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE);
32423dd8
JS
13262 }
13263
c4168888 13264 if (attr.HasFontPixelSize() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_PIXEL_SIZE))
24777478 13265 {
c4168888 13266 if (currentStyle.HasFontPixelSize())
24777478 13267 {
c4168888 13268 if (currentStyle.GetFontSize() != attr.GetFontSize())
24777478
JS
13269 {
13270 // Clash of attr - mark as such
c4168888
JS
13271 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE);
13272 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE);
24777478
JS
13273 }
13274 }
13275 else
c4168888 13276 currentStyle.SetFontPixelSize(attr.GetFontSize());
24777478 13277 }
c4168888
JS
13278 else if (!attr.HasFontPixelSize() && currentStyle.HasFontPixelSize())
13279 {
13280 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE);
13281 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE);
13282 }
13283 }
24777478 13284
c4168888
JS
13285 if (attr.HasFontItalic() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_ITALIC))
13286 {
13287 if (currentStyle.HasFontItalic())
24777478 13288 {
c4168888 13289 if (currentStyle.GetFontStyle() != attr.GetFontStyle())
24777478 13290 {
c4168888
JS
13291 // Clash of attr - mark as such
13292 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_ITALIC);
13293 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC);
24777478 13294 }
24777478 13295 }
c4168888
JS
13296 else
13297 currentStyle.SetFontStyle(attr.GetFontStyle());
13298 }
13299 else if (!attr.HasFontItalic() && currentStyle.HasFontItalic())
13300 {
13301 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_ITALIC);
13302 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC);
13303 }
24777478 13304
c4168888
JS
13305 if (attr.HasFontFamily() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_FAMILY))
13306 {
13307 if (currentStyle.HasFontFamily())
24777478 13308 {
c4168888 13309 if (currentStyle.GetFontFamily() != attr.GetFontFamily())
24777478 13310 {
c4168888
JS
13311 // Clash of attr - mark as such
13312 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FAMILY);
13313 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY);
24777478 13314 }
24777478 13315 }
c4168888
JS
13316 else
13317 currentStyle.SetFontFamily(attr.GetFontFamily());
13318 }
13319 else if (!attr.HasFontFamily() && currentStyle.HasFontFamily())
13320 {
13321 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FAMILY);
13322 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY);
13323 }
24777478 13324
c4168888
JS
13325 if (attr.HasFontWeight() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_WEIGHT))
13326 {
13327 if (currentStyle.HasFontWeight())
24777478 13328 {
c4168888 13329 if (currentStyle.GetFontWeight() != attr.GetFontWeight())
24777478 13330 {
c4168888
JS
13331 // Clash of attr - mark as such
13332 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_WEIGHT);
13333 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT);
13334 }
13335 }
13336 else
13337 currentStyle.SetFontWeight(attr.GetFontWeight());
13338 }
13339 else if (!attr.HasFontWeight() && currentStyle.HasFontWeight())
13340 {
13341 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_WEIGHT);
13342 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT);
13343 }
24777478 13344
c4168888
JS
13345 if (attr.HasFontFaceName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_FACE))
13346 {
13347 if (currentStyle.HasFontFaceName())
13348 {
13349 wxString faceName1(currentStyle.GetFontFaceName());
13350 wxString faceName2(attr.GetFontFaceName());
13351
13352 if (faceName1 != faceName2)
13353 {
13354 // Clash of attr - mark as such
13355 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FACE);
13356 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FACE);
24777478 13357 }
24777478 13358 }
c4168888
JS
13359 else
13360 currentStyle.SetFontFaceName(attr.GetFontFaceName());
13361 }
13362 else if (!attr.HasFontFaceName() && currentStyle.HasFontFaceName())
13363 {
13364 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FACE);
13365 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FACE);
13366 }
24777478 13367
c4168888
JS
13368 if (attr.HasFontUnderlined() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_UNDERLINE))
13369 {
13370 if (currentStyle.HasFontUnderlined())
24777478 13371 {
c4168888 13372 if (currentStyle.GetFontUnderlined() != attr.GetFontUnderlined())
24777478 13373 {
c4168888
JS
13374 // Clash of attr - mark as such
13375 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE);
13376 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE);
24777478 13377 }
24777478 13378 }
c4168888
JS
13379 else
13380 currentStyle.SetFontUnderlined(attr.GetFontUnderlined());
13381 }
13382 else if (!attr.HasFontUnderlined() && currentStyle.HasFontUnderlined())
13383 {
13384 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE);
13385 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE);
13386 }
32423dd8 13387
c4168888
JS
13388 if (attr.HasFontStrikethrough() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_STRIKETHROUGH))
13389 {
13390 if (currentStyle.HasFontStrikethrough())
32423dd8 13391 {
c4168888 13392 if (currentStyle.GetFontStrikethrough() != attr.GetFontStrikethrough())
32423dd8 13393 {
c4168888
JS
13394 // Clash of attr - mark as such
13395 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH);
13396 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH);
32423dd8 13397 }
32423dd8 13398 }
c4168888
JS
13399 else
13400 currentStyle.SetFontStrikethrough(attr.GetFontStrikethrough());
13401 }
13402 else if (!attr.HasFontStrikethrough() && currentStyle.HasFontStrikethrough())
13403 {
13404 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH);
13405 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH);
24777478
JS
13406 }
13407
13408 if (attr.HasTextColour() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_TEXT_COLOUR))
13409 {
13410 if (currentStyle.HasTextColour())
13411 {
13412 if (currentStyle.GetTextColour() != attr.GetTextColour())
13413 {
13414 // Clash of attr - mark as such
13415 clashingAttr.AddFlag(wxTEXT_ATTR_TEXT_COLOUR);
13416 currentStyle.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR);
13417 }
13418 }
13419 else
13420 currentStyle.SetTextColour(attr.GetTextColour());
13421 }
c4168888
JS
13422 else if (!attr.HasTextColour() && currentStyle.HasTextColour())
13423 {
13424 clashingAttr.AddFlag(wxTEXT_ATTR_TEXT_COLOUR);
13425 currentStyle.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR);
13426 }
24777478
JS
13427
13428 if (attr.HasBackgroundColour() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BACKGROUND_COLOUR))
13429 {
13430 if (currentStyle.HasBackgroundColour())
13431 {
13432 if (currentStyle.GetBackgroundColour() != attr.GetBackgroundColour())
13433 {
13434 // Clash of attr - mark as such
13435 clashingAttr.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
13436 currentStyle.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
13437 }
13438 }
13439 else
13440 currentStyle.SetBackgroundColour(attr.GetBackgroundColour());
13441 }
c4168888
JS
13442 else if (!attr.HasBackgroundColour() && currentStyle.HasBackgroundColour())
13443 {
13444 clashingAttr.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
13445 currentStyle.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
13446 }
24777478
JS
13447
13448 if (attr.HasAlignment() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_ALIGNMENT))
13449 {
13450 if (currentStyle.HasAlignment())
13451 {
13452 if (currentStyle.GetAlignment() != attr.GetAlignment())
13453 {
13454 // Clash of attr - mark as such
13455 clashingAttr.AddFlag(wxTEXT_ATTR_ALIGNMENT);
13456 currentStyle.RemoveFlag(wxTEXT_ATTR_ALIGNMENT);
13457 }
13458 }
13459 else
13460 currentStyle.SetAlignment(attr.GetAlignment());
13461 }
c4168888
JS
13462 else if (!attr.HasAlignment() && currentStyle.HasAlignment())
13463 {
13464 clashingAttr.AddFlag(wxTEXT_ATTR_ALIGNMENT);
13465 currentStyle.RemoveFlag(wxTEXT_ATTR_ALIGNMENT);
13466 }
24777478
JS
13467
13468 if (attr.HasTabs() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_TABS))
13469 {
13470 if (currentStyle.HasTabs())
13471 {
13472 if (!wxRichTextTabsEq(currentStyle.GetTabs(), attr.GetTabs()))
13473 {
13474 // Clash of attr - mark as such
13475 clashingAttr.AddFlag(wxTEXT_ATTR_TABS);
13476 currentStyle.RemoveFlag(wxTEXT_ATTR_TABS);
13477 }
13478 }
13479 else
13480 currentStyle.SetTabs(attr.GetTabs());
13481 }
c4168888
JS
13482 else if (!attr.HasTabs() && currentStyle.HasTabs())
13483 {
13484 clashingAttr.AddFlag(wxTEXT_ATTR_TABS);
13485 currentStyle.RemoveFlag(wxTEXT_ATTR_TABS);
13486 }
24777478
JS
13487
13488 if (attr.HasLeftIndent() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LEFT_INDENT))
13489 {
13490 if (currentStyle.HasLeftIndent())
13491 {
13492 if (currentStyle.GetLeftIndent() != attr.GetLeftIndent() || currentStyle.GetLeftSubIndent() != attr.GetLeftSubIndent())
13493 {
13494 // Clash of attr - mark as such
13495 clashingAttr.AddFlag(wxTEXT_ATTR_LEFT_INDENT);
13496 currentStyle.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT);
13497 }
13498 }
13499 else
13500 currentStyle.SetLeftIndent(attr.GetLeftIndent(), attr.GetLeftSubIndent());
13501 }
c4168888
JS
13502 else if (!attr.HasLeftIndent() && currentStyle.HasLeftIndent())
13503 {
13504 clashingAttr.AddFlag(wxTEXT_ATTR_LEFT_INDENT);
13505 currentStyle.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT);
13506 }
24777478
JS
13507
13508 if (attr.HasRightIndent() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_RIGHT_INDENT))
13509 {
13510 if (currentStyle.HasRightIndent())
13511 {
13512 if (currentStyle.GetRightIndent() != attr.GetRightIndent())
13513 {
13514 // Clash of attr - mark as such
13515 clashingAttr.AddFlag(wxTEXT_ATTR_RIGHT_INDENT);
13516 currentStyle.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT);
13517 }
13518 }
13519 else
13520 currentStyle.SetRightIndent(attr.GetRightIndent());
13521 }
c4168888
JS
13522 else if (!attr.HasRightIndent() && currentStyle.HasRightIndent())
13523 {
13524 clashingAttr.AddFlag(wxTEXT_ATTR_RIGHT_INDENT);
13525 currentStyle.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT);
13526 }
24777478
JS
13527
13528 if (attr.HasParagraphSpacingAfter() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARA_SPACING_AFTER))
13529 {
13530 if (currentStyle.HasParagraphSpacingAfter())
13531 {
13532 if (currentStyle.GetParagraphSpacingAfter() != attr.GetParagraphSpacingAfter())
13533 {
13534 // Clash of attr - mark as such
13535 clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
13536 currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
13537 }
13538 }
13539 else
13540 currentStyle.SetParagraphSpacingAfter(attr.GetParagraphSpacingAfter());
13541 }
c4168888
JS
13542 else if (!attr.HasParagraphSpacingAfter() && currentStyle.HasParagraphSpacingAfter())
13543 {
13544 clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
13545 currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
13546 }
24777478
JS
13547
13548 if (attr.HasParagraphSpacingBefore() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARA_SPACING_BEFORE))
13549 {
13550 if (currentStyle.HasParagraphSpacingBefore())
13551 {
13552 if (currentStyle.GetParagraphSpacingBefore() != attr.GetParagraphSpacingBefore())
13553 {
13554 // Clash of attr - mark as such
13555 clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
13556 currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
13557 }
13558 }
13559 else
13560 currentStyle.SetParagraphSpacingBefore(attr.GetParagraphSpacingBefore());
13561 }
c4168888
JS
13562 else if (!attr.HasParagraphSpacingBefore() && currentStyle.HasParagraphSpacingBefore())
13563 {
13564 clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
13565 currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
13566 }
24777478
JS
13567
13568 if (attr.HasLineSpacing() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LINE_SPACING))
13569 {
13570 if (currentStyle.HasLineSpacing())
13571 {
13572 if (currentStyle.GetLineSpacing() != attr.GetLineSpacing())
13573 {
13574 // Clash of attr - mark as such
13575 clashingAttr.AddFlag(wxTEXT_ATTR_LINE_SPACING);
13576 currentStyle.RemoveFlag(wxTEXT_ATTR_LINE_SPACING);
13577 }
13578 }
13579 else
13580 currentStyle.SetLineSpacing(attr.GetLineSpacing());
13581 }
c4168888
JS
13582 else if (!attr.HasLineSpacing() && currentStyle.HasLineSpacing())
13583 {
13584 clashingAttr.AddFlag(wxTEXT_ATTR_LINE_SPACING);
13585 currentStyle.RemoveFlag(wxTEXT_ATTR_LINE_SPACING);
13586 }
24777478
JS
13587
13588 if (attr.HasCharacterStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_CHARACTER_STYLE_NAME))
13589 {
13590 if (currentStyle.HasCharacterStyleName())
13591 {
13592 if (currentStyle.GetCharacterStyleName() != attr.GetCharacterStyleName())
13593 {
13594 // Clash of attr - mark as such
13595 clashingAttr.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
13596 currentStyle.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
13597 }
13598 }
13599 else
13600 currentStyle.SetCharacterStyleName(attr.GetCharacterStyleName());
13601 }
c4168888
JS
13602 else if (!attr.HasCharacterStyleName() && currentStyle.HasCharacterStyleName())
13603 {
13604 clashingAttr.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
13605 currentStyle.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
13606 }
24777478
JS
13607
13608 if (attr.HasParagraphStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARAGRAPH_STYLE_NAME))
13609 {
13610 if (currentStyle.HasParagraphStyleName())
13611 {
13612 if (currentStyle.GetParagraphStyleName() != attr.GetParagraphStyleName())
13613 {
13614 // Clash of attr - mark as such
13615 clashingAttr.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
13616 currentStyle.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
13617 }
13618 }
13619 else
13620 currentStyle.SetParagraphStyleName(attr.GetParagraphStyleName());
13621 }
c4168888
JS
13622 else if (!attr.HasParagraphStyleName() && currentStyle.HasParagraphStyleName())
13623 {
13624 clashingAttr.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
13625 currentStyle.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
13626 }
24777478
JS
13627
13628 if (attr.HasListStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LIST_STYLE_NAME))
13629 {
13630 if (currentStyle.HasListStyleName())
13631 {
13632 if (currentStyle.GetListStyleName() != attr.GetListStyleName())
13633 {
13634 // Clash of attr - mark as such
13635 clashingAttr.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
13636 currentStyle.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
13637 }
13638 }
13639 else
13640 currentStyle.SetListStyleName(attr.GetListStyleName());
13641 }
c4168888
JS
13642 else if (!attr.HasListStyleName() && currentStyle.HasListStyleName())
13643 {
13644 clashingAttr.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
13645 currentStyle.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
13646 }
24777478
JS
13647
13648 if (attr.HasBulletStyle() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_STYLE))
13649 {
13650 if (currentStyle.HasBulletStyle())
13651 {
13652 if (currentStyle.GetBulletStyle() != attr.GetBulletStyle())
13653 {
13654 // Clash of attr - mark as such
13655 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_STYLE);
13656 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE);
13657 }
13658 }
13659 else
13660 currentStyle.SetBulletStyle(attr.GetBulletStyle());
13661 }
c4168888
JS
13662 else if (!attr.HasBulletStyle() && currentStyle.HasBulletStyle())
13663 {
13664 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_STYLE);
13665 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE);
13666 }
24777478
JS
13667
13668 if (attr.HasBulletNumber() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_NUMBER))
13669 {
13670 if (currentStyle.HasBulletNumber())
13671 {
13672 if (currentStyle.GetBulletNumber() != attr.GetBulletNumber())
13673 {
13674 // Clash of attr - mark as such
13675 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NUMBER);
13676 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER);
13677 }
13678 }
13679 else
13680 currentStyle.SetBulletNumber(attr.GetBulletNumber());
13681 }
c4168888
JS
13682 else if (!attr.HasBulletNumber() && currentStyle.HasBulletNumber())
13683 {
13684 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NUMBER);
13685 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER);
13686 }
24777478
JS
13687
13688 if (attr.HasBulletText() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_TEXT))
13689 {
13690 if (currentStyle.HasBulletText())
13691 {
13692 if (currentStyle.GetBulletText() != attr.GetBulletText())
13693 {
13694 // Clash of attr - mark as such
13695 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_TEXT);
13696 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT);
13697 }
13698 }
13699 else
13700 {
13701 currentStyle.SetBulletText(attr.GetBulletText());
13702 currentStyle.SetBulletFont(attr.GetBulletFont());
13703 }
13704 }
c4168888
JS
13705 else if (!attr.HasBulletText() && currentStyle.HasBulletText())
13706 {
13707 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_TEXT);
13708 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT);
13709 }
24777478
JS
13710
13711 if (attr.HasBulletName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_NAME))
13712 {
13713 if (currentStyle.HasBulletName())
13714 {
13715 if (currentStyle.GetBulletName() != attr.GetBulletName())
13716 {
13717 // Clash of attr - mark as such
13718 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NAME);
13719 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NAME);
13720 }
13721 }
13722 else
13723 {
13724 currentStyle.SetBulletName(attr.GetBulletName());
13725 }
13726 }
c4168888
JS
13727 else if (!attr.HasBulletName() && currentStyle.HasBulletName())
13728 {
13729 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NAME);
13730 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NAME);
13731 }
24777478
JS
13732
13733 if (attr.HasURL() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_URL))
13734 {
13735 if (currentStyle.HasURL())
13736 {
13737 if (currentStyle.GetURL() != attr.GetURL())
13738 {
13739 // Clash of attr - mark as such
13740 clashingAttr.AddFlag(wxTEXT_ATTR_URL);
13741 currentStyle.RemoveFlag(wxTEXT_ATTR_URL);
13742 }
13743 }
13744 else
13745 {
13746 currentStyle.SetURL(attr.GetURL());
13747 }
13748 }
c4168888
JS
13749 else if (!attr.HasURL() && currentStyle.HasURL())
13750 {
13751 clashingAttr.AddFlag(wxTEXT_ATTR_URL);
13752 currentStyle.RemoveFlag(wxTEXT_ATTR_URL);
13753 }
24777478
JS
13754
13755 if (attr.HasTextEffects() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_EFFECTS))
13756 {
13757 if (currentStyle.HasTextEffects())
13758 {
13759 // We need to find the bits in the new attr that are different:
13760 // just look at those bits that are specified by the new attr.
13761
13762 // We need to remove the bits and flags that are not common between current attr
13763 // and new attr. In so doing we need to take account of the styles absent from one or more of the
13764 // previous styles.
13765
13766 int currentRelevantTextEffects = currentStyle.GetTextEffects() & attr.GetTextEffectFlags();
13767 int newRelevantTextEffects = attr.GetTextEffects() & attr.GetTextEffectFlags();
13768
13769 if (currentRelevantTextEffects != newRelevantTextEffects)
13770 {
13771 // Find the text effects that were different, using XOR
13772 int differentEffects = currentRelevantTextEffects ^ newRelevantTextEffects;
13773
13774 // Clash of attr - mark as such
13775 clashingAttr.SetTextEffectFlags(clashingAttr.GetTextEffectFlags() | differentEffects);
13776 currentStyle.SetTextEffectFlags(currentStyle.GetTextEffectFlags() & ~differentEffects);
13777 }
13778 }
13779 else
13780 {
13781 currentStyle.SetTextEffects(attr.GetTextEffects());
13782 currentStyle.SetTextEffectFlags(attr.GetTextEffectFlags());
13783 }
13784
13785 // Mask out the flags and values that cannot be common because they were absent in one or more objecrs
13786 // that we've looked at so far
13787 currentStyle.SetTextEffects(currentStyle.GetTextEffects() & ~absentAttr.GetTextEffectFlags());
13788 currentStyle.SetTextEffectFlags(currentStyle.GetTextEffectFlags() & ~absentAttr.GetTextEffectFlags());
13789
13790 if (currentStyle.GetTextEffectFlags() == 0)
13791 currentStyle.RemoveFlag(wxTEXT_ATTR_EFFECTS);
13792 }
c4168888
JS
13793 else if (!attr.HasTextEffects() && currentStyle.HasTextEffects())
13794 {
13795 clashingAttr.AddFlag(wxTEXT_ATTR_EFFECTS);
13796 currentStyle.RemoveFlag(wxTEXT_ATTR_EFFECTS);
13797 }
24777478
JS
13798
13799 if (attr.HasOutlineLevel() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_OUTLINE_LEVEL))
13800 {
13801 if (currentStyle.HasOutlineLevel())
13802 {
13803 if (currentStyle.GetOutlineLevel() != attr.GetOutlineLevel())
13804 {
13805 // Clash of attr - mark as such
13806 clashingAttr.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
13807 currentStyle.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
13808 }
13809 }
13810 else
13811 currentStyle.SetOutlineLevel(attr.GetOutlineLevel());
13812 }
c4168888
JS
13813 else if (!attr.HasOutlineLevel() && currentStyle.HasOutlineLevel())
13814 {
13815 clashingAttr.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
13816 currentStyle.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
13817 }
24777478
JS
13818}
13819
bec80f4f
JS
13820WX_DEFINE_OBJARRAY(wxRichTextVariantArray);
13821
f7667b84
JS
13822// JACS 2013-01-27
13823WX_DEFINE_OBJARRAY(wxRichTextAttrArray);
13824
bec80f4f
JS
13825IMPLEMENT_DYNAMIC_CLASS(wxRichTextProperties, wxObject)
13826
13827bool wxRichTextProperties::operator==(const wxRichTextProperties& props) const
13828{
13829 if (m_properties.GetCount() != props.GetCount())
13830 return false;
13831
13832 size_t i;
13833 for (i = 0; i < m_properties.GetCount(); i++)
13834 {
13835 const wxVariant& var1 = m_properties[i];
13836 int idx = props.Find(var1.GetName());
13837 if (idx == -1)
13838 return false;
13839 const wxVariant& var2 = props.m_properties[idx];
13840 if (!(var1 == var2))
13841 return false;
13842 }
13843
13844 return true;
13845}
13846
13847wxArrayString wxRichTextProperties::GetPropertyNames() const
13848{
13849 wxArrayString arr;
13850 size_t i;
13851 for (i = 0; i < m_properties.GetCount(); i++)
13852 {
13853 arr.Add(m_properties[i].GetName());
13854 }
13855 return arr;
13856}
13857
13858int wxRichTextProperties::Find(const wxString& name) const
13859{
13860 size_t i;
13861 for (i = 0; i < m_properties.GetCount(); i++)
13862 {
13863 if (m_properties[i].GetName() == name)
13864 return (int) i;
13865 }
13866 return -1;
13867}
13868
590a0f8b
JS
13869bool wxRichTextProperties::Remove(const wxString& name)
13870{
13871 int idx = Find(name);
13872 if (idx != -1)
13873 {
13874 m_properties.RemoveAt(idx);
13875 return true;
13876 }
13877 else
13878 return false;
13879}
13880
bec80f4f
JS
13881wxVariant* wxRichTextProperties::FindOrCreateProperty(const wxString& name)
13882{
13883 int idx = Find(name);
13884 if (idx == wxNOT_FOUND)
13885 SetProperty(name, wxString());
13886 idx = Find(name);
13887 if (idx != wxNOT_FOUND)
13888 {
13889 return & (*this)[idx];
13890 }
13891 else
13892 return NULL;
13893}
13894
13895const wxVariant& wxRichTextProperties::GetProperty(const wxString& name) const
13896{
13897 static const wxVariant nullVariant;
13898 int idx = Find(name);
13899 if (idx != -1)
13900 return m_properties[idx];
13901 else
13902 return nullVariant;
13903}
13904
13905wxString wxRichTextProperties::GetPropertyString(const wxString& name) const
13906{
13907 return GetProperty(name).GetString();
13908}
13909
13910long wxRichTextProperties::GetPropertyLong(const wxString& name) const
13911{
13912 return GetProperty(name).GetLong();
13913}
13914
13915bool wxRichTextProperties::GetPropertyBool(const wxString& name) const
13916{
13917 return GetProperty(name).GetBool();
13918}
13919
13920double wxRichTextProperties::GetPropertyDouble(const wxString& name) const
13921{
13922 return GetProperty(name).GetDouble();
13923}
13924
13925void wxRichTextProperties::SetProperty(const wxVariant& variant)
13926{
13927 wxASSERT(!variant.GetName().IsEmpty());
13928
13929 int idx = Find(variant.GetName());
13930
13931 if (idx == -1)
13932 m_properties.Add(variant);
13933 else
13934 m_properties[idx] = variant;
13935}
13936
13937void wxRichTextProperties::SetProperty(const wxString& name, const wxVariant& variant)
13938{
13939 int idx = Find(name);
13940 wxVariant var(variant);
13941 var.SetName(name);
13942
13943 if (idx == -1)
13944 m_properties.Add(var);
13945 else
13946 m_properties[idx] = var;
13947}
13948
13949void wxRichTextProperties::SetProperty(const wxString& name, const wxString& value)
13950{
13951 SetProperty(name, wxVariant(value, name));
13952}
13953
13954void wxRichTextProperties::SetProperty(const wxString& name, long value)
13955{
13956 SetProperty(name, wxVariant(value, name));
13957}
13958
13959void wxRichTextProperties::SetProperty(const wxString& name, double value)
13960{
13961 SetProperty(name, wxVariant(value, name));
13962}
13963
13964void wxRichTextProperties::SetProperty(const wxString& name, bool value)
13965{
13966 SetProperty(name, wxVariant(value, name));
13967}
24777478 13968
590a0f8b
JS
13969void wxRichTextProperties::RemoveProperties(const wxRichTextProperties& properties)
13970{
13971 size_t i;
13972 for (i = 0; i < properties.GetCount(); i++)
13973 {
13974 wxString name = properties.GetProperties()[i].GetName();
13975 if (HasProperty(name))
13976 Remove(name);
13977 }
13978}
13979
13980void wxRichTextProperties::MergeProperties(const wxRichTextProperties& properties)
13981{
13982 size_t i;
13983 for (i = 0; i < properties.GetCount(); i++)
13984 {
13985 SetProperty(properties.GetProperties()[i]);
13986 }
13987}
13988
603f702b
JS
13989wxRichTextObject* wxRichTextObjectAddress::GetObject(wxRichTextParagraphLayoutBox* topLevelContainer) const
13990{
13991 if (m_address.GetCount() == 0)
13992 return topLevelContainer;
13993
13994 wxRichTextCompositeObject* p = topLevelContainer;
13995 size_t i = 0;
13996 while (p && i < m_address.GetCount())
13997 {
13998 int pos = m_address[i];
13999 wxASSERT(pos >= 0 && pos < (int) p->GetChildren().GetCount());
14000 if (pos < 0 || pos >= (int) p->GetChildren().GetCount())
14001 return NULL;
14002
14003 wxRichTextObject* p1 = p->GetChild(pos);
14004 if (i == (m_address.GetCount()-1))
14005 return p1;
14006
14007 p = wxDynamicCast(p1, wxRichTextCompositeObject);
14008 i ++;
14009 }
14010 return NULL;
14011}
14012
14013bool wxRichTextObjectAddress::Create(wxRichTextParagraphLayoutBox* topLevelContainer, wxRichTextObject* obj)
14014{
14015 m_address.Clear();
14016
14017 if (topLevelContainer == obj)
14018 return true;
14019
14020 wxRichTextObject* o = obj;
14021 while (o)
14022 {
14023 wxRichTextCompositeObject* p = wxDynamicCast(o->GetParent(), wxRichTextCompositeObject);
14024 if (!p)
14025 return false;
14026
14027 int pos = p->GetChildren().IndexOf(o);
14028 if (pos == -1)
14029 return false;
14030
14031 m_address.Insert(pos, 0);
14032
14033 if (p == topLevelContainer)
14034 return true;
14035
14036 o = p;
14037 }
14038 return false;
14039}
14040
14041// Equality test
14042bool wxRichTextSelection::operator==(const wxRichTextSelection& sel) const
14043{
14044 if (m_container != sel.m_container)
14045 return false;
14046 if (m_ranges.GetCount() != sel.m_ranges.GetCount())
14047 return false;
14048 size_t i;
14049 for (i = 0; i < m_ranges.GetCount(); i++)
14050 if (!(m_ranges[i] == sel.m_ranges[i]))
14051 return false;
14052 return true;
14053}
14054
14055// Get the selections appropriate to the specified object, if any; returns wxRICHTEXT_NO_SELECTION if none
14056// or none at the level of the object's container.
14057wxRichTextRangeArray wxRichTextSelection::GetSelectionForObject(wxRichTextObject* obj) const
14058{
14059 if (IsValid())
14060 {
14061 wxRichTextParagraphLayoutBox* container = obj->GetParentContainer();
14062
14063 if (container == m_container)
14064 return m_ranges;
14065
14066 container = obj->GetContainer();
14067 while (container)
14068 {
14069 if (container->GetParent())
14070 {
14071 // If we found that our object's container is within the range of
14072 // a selection higher up, then assume the whole original object
14073 // is also selected.
14074 wxRichTextParagraphLayoutBox* parentContainer = container->GetParentContainer();
14075 if (parentContainer == m_container)
14076 {
14077 if (WithinSelection(container->GetRange().GetStart(), m_ranges))
14078 {
14079 wxRichTextRangeArray ranges;
14080 ranges.Add(obj->GetRange());
14081 return ranges;
14082 }
14083 }
14084
14085 container = parentContainer;
14086 }
14087 else
14088 {
14089 container = NULL;
14090 break;
14091 }
14092 }
14093 }
14094 return wxRichTextRangeArray();
14095}
14096
14097// Is the given position within the selection?
14098bool wxRichTextSelection::WithinSelection(long pos, wxRichTextObject* obj) const
14099{
14100 if (!IsValid())
14101 return false;
14102 else
14103 {
14104 wxRichTextRangeArray selectionRanges = GetSelectionForObject(obj);
14105 return WithinSelection(pos, selectionRanges);
14106 }
14107}
14108
14109// Is the given position within the selection range?
14110bool wxRichTextSelection::WithinSelection(long pos, const wxRichTextRangeArray& ranges)
14111{
14112 size_t i;
14113 for (i = 0; i < ranges.GetCount(); i++)
14114 {
14115 const wxRichTextRange& range = ranges[i];
14116 if (pos >= range.GetStart() && pos <= range.GetEnd())
14117 return true;
14118 }
14119 return false;
14120}
14121
14122// Is the given range completely within the selection range?
14123bool wxRichTextSelection::WithinSelection(const wxRichTextRange& range, const wxRichTextRangeArray& ranges)
14124{
14125 size_t i;
14126 for (i = 0; i < ranges.GetCount(); i++)
14127 {
14128 const wxRichTextRange& eachRange = ranges[i];
14129 if (range.IsWithin(eachRange))
14130 return true;
14131 }
14132 return false;
14133}
14134
8db2e3ef
JS
14135IMPLEMENT_CLASS(wxRichTextDrawingHandler, wxObject)
14136IMPLEMENT_CLASS(wxRichTextDrawingContext, wxObject)
14137
f7667b84
JS
14138wxRichTextDrawingContext::wxRichTextDrawingContext(wxRichTextBuffer* buffer)
14139{
14140 Init();
14141 m_buffer = buffer;
14142 if (m_buffer && m_buffer->GetRichTextCtrl())
14143 EnableVirtualAttributes(m_buffer->GetRichTextCtrl()->GetVirtualAttributesEnabled());
14144}
14145
8db2e3ef
JS
14146bool wxRichTextDrawingContext::HasVirtualAttributes(wxRichTextObject* obj) const
14147{
f7667b84
JS
14148 if (!GetVirtualAttributesEnabled())
14149 return false;
14150
8db2e3ef
JS
14151 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
14152 while (node)
14153 {
14154 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
14155 if (handler->HasVirtualAttributes(obj))
14156 return true;
14157
14158 node = node->GetNext();
14159 }
14160 return false;
14161}
14162
14163wxRichTextAttr wxRichTextDrawingContext::GetVirtualAttributes(wxRichTextObject* obj) const
14164{
14165 wxRichTextAttr attr;
f7667b84
JS
14166 if (!GetVirtualAttributesEnabled())
14167 return attr;
14168
8db2e3ef
JS
14169 // We apply all handlers, so we can may combine several different attributes
14170 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
14171 while (node)
14172 {
14173 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
14174 if (handler->HasVirtualAttributes(obj))
14175 {
14176 bool success = handler->GetVirtualAttributes(attr, obj);
14177 wxASSERT(success);
aa8f57f4 14178 wxUnusedVar(success);
8db2e3ef
JS
14179 }
14180
14181 node = node->GetNext();
14182 }
14183 return attr;
14184}
14185
14186bool wxRichTextDrawingContext::ApplyVirtualAttributes(wxRichTextAttr& attr, wxRichTextObject* obj) const
14187{
f7667b84
JS
14188 if (!GetVirtualAttributesEnabled())
14189 return false;
14190
8db2e3ef
JS
14191 if (HasVirtualAttributes(obj))
14192 {
14193 wxRichTextAttr a(GetVirtualAttributes(obj));
14194 attr.Apply(a);
14195 return true;
14196 }
14197 else
14198 return false;
14199}
14200
f7667b84
JS
14201int wxRichTextDrawingContext::GetVirtualSubobjectAttributesCount(wxRichTextObject* obj) const
14202{
14203 if (!GetVirtualAttributesEnabled())
14204 return 0;
14205
14206 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
14207 while (node)
14208 {
14209 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
14210 int count = handler->GetVirtualSubobjectAttributesCount(obj);
14211 if (count > 0)
14212 return count;
14213
14214 node = node->GetNext();
14215 }
14216 return 0;
14217}
14218
14219int wxRichTextDrawingContext::GetVirtualSubobjectAttributes(wxRichTextObject* obj, wxArrayInt& positions, wxRichTextAttrArray& attributes) const
14220{
14221 if (!GetVirtualAttributesEnabled())
14222 return 0;
14223
14224 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
14225 while (node)
14226 {
14227 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
14228 if (handler->GetVirtualSubobjectAttributes(obj, positions, attributes))
14229 return positions.GetCount();
14230
14231 node = node->GetNext();
14232 }
14233 return 0;
14234}
14235
14236bool wxRichTextDrawingContext::HasVirtualText(const wxRichTextPlainText* obj) const
14237{
14238 if (!GetVirtualAttributesEnabled())
14239 return false;
14240
14241 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
14242 while (node)
14243 {
14244 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
14245 if (handler->HasVirtualText(obj))
14246 return true;
14247
14248 node = node->GetNext();
14249 }
14250 return false;
14251}
14252
14253bool wxRichTextDrawingContext::GetVirtualText(const wxRichTextPlainText* obj, wxString& text) const
14254{
14255 if (!GetVirtualAttributesEnabled())
14256 return false;
14257
14258 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
14259 while (node)
14260 {
14261 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
14262 if (handler->GetVirtualText(obj, text))
14263 return true;
14264
14265 node = node->GetNext();
14266 }
14267 return false;
14268}
14269
8db2e3ef
JS
14270/// Adds a handler to the end
14271void wxRichTextBuffer::AddDrawingHandler(wxRichTextDrawingHandler *handler)
14272{
14273 sm_drawingHandlers.Append(handler);
14274}
14275
14276/// Inserts a handler at the front
14277void wxRichTextBuffer::InsertDrawingHandler(wxRichTextDrawingHandler *handler)
14278{
14279 sm_drawingHandlers.Insert( handler );
14280}
14281
14282/// Removes a handler
14283bool wxRichTextBuffer::RemoveDrawingHandler(const wxString& name)
14284{
14285 wxRichTextDrawingHandler *handler = FindDrawingHandler(name);
14286 if (handler)
14287 {
14288 sm_drawingHandlers.DeleteObject(handler);
14289 delete handler;
14290 return true;
14291 }
14292 else
14293 return false;
14294}
14295
14296wxRichTextDrawingHandler* wxRichTextBuffer::FindDrawingHandler(const wxString& name)
14297{
14298 wxList::compatibility_iterator node = sm_drawingHandlers.GetFirst();
14299 while (node)
14300 {
14301 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
14302 if (handler->GetName().Lower() == name.Lower()) return handler;
14303
14304 node = node->GetNext();
14305 }
14306 return NULL;
14307}
14308
14309void wxRichTextBuffer::CleanUpDrawingHandlers()
14310{
14311 wxList::compatibility_iterator node = sm_drawingHandlers.GetFirst();
14312 while (node)
14313 {
14314 wxRichTextDrawingHandler* handler = (wxRichTextDrawingHandler*)node->GetData();
14315 wxList::compatibility_iterator next = node->GetNext();
14316 delete handler;
14317 node = next;
14318 }
14319
14320 sm_drawingHandlers.Clear();
14321}
603f702b 14322
7c9fdebe
JS
14323void wxRichTextBuffer::AddFieldType(wxRichTextFieldType *fieldType)
14324{
14325 sm_fieldTypes[fieldType->GetName()] = fieldType;
14326}
14327
14328bool wxRichTextBuffer::RemoveFieldType(const wxString& name)
14329{
14330 wxRichTextFieldTypeHashMap::iterator it = sm_fieldTypes.find(name);
14331 if (it == sm_fieldTypes.end())
14332 return false;
14333 else
14334 {
14335 wxRichTextFieldType* fieldType = it->second;
14336 sm_fieldTypes.erase(it);
14337 delete fieldType;
14338 return true;
14339 }
14340}
14341
14342wxRichTextFieldType *wxRichTextBuffer::FindFieldType(const wxString& name)
14343{
14344 wxRichTextFieldTypeHashMap::iterator it = sm_fieldTypes.find(name);
14345 if (it == sm_fieldTypes.end())
14346 return NULL;
14347 else
14348 return it->second;
14349}
14350
14351void wxRichTextBuffer::CleanUpFieldTypes()
14352{
14353 wxRichTextFieldTypeHashMap::iterator it;
14354 for( it = sm_fieldTypes.begin(); it != sm_fieldTypes.end(); ++it )
14355 {
14356 wxRichTextFieldType* fieldType = it->second;
14357 delete fieldType;
14358 }
14359
14360 sm_fieldTypes.clear();
14361}
14362
5d7836c4
JS
14363#endif
14364 // wxUSE_RICHTEXT